1 | /* spawn.c
2 |
3 | Source file for spawn operations for PGPsendmail (wrapper to sendmail).
4 |
5 | Copyright (C) 1994-1998 Richard Gooch
6 |
7 | This program is free software; you can redistribute it and/or modify
8 | it under the terms of the GNU General Public License as published by
9 | the Free Software Foundation; either version 2 of the License, or
10 | (at your option) any later version.
11 |
12 | This program is distributed in the hope that it will be useful,
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | GNU General Public License for more details.
16 |
17 | You should have received a copy of the GNU General Public License
18 | along with this program; if not, write to the Free Software
19 | Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 |
21 | Richard Gooch may be reached by email at rgooch@atnf.csiro.au
22 | The postal address is:
23 | Richard Gooch, c/o ATNF, P. O. Box 76, Epping, N.S.W., 2121, Australia.
24 | */
25 |
26 | /* This programme intercepts messages sent by user mail agents to the
27 | sendmail daemon and checks to see if messages can be encrypted using the
28 | recipient's PGP public keys.
29 |
30 |
31 | Written by Richard Gooch 31-MAY-1994
32 |
33 | Updated by Richard Gooch 31-MAY-1994: Extracted from pgpsendmail.c
34 |
35 | Updated by Richard Gooch 18-JUN-1994: Made error messages more
36 | explicit.
37 |
38 | Updated by Richard Gooch 27-JUN-1994: Copied set_env from
39 | pgpdaemon.c
40 |
41 | Updated by Richard Gooch 5-JUL-1994: Changed to use of m_copy .
42 |
43 | Updated by Richard Gooch 14-JUL-1994: Moved copy_data and set_env
44 | to misc.c
45 |
46 | Updated by Richard Gooch 3-DEC-1994: Fixed bug for externally set
47 | error descriptor.
48 |
49 | Updated by Richard Gooch 25-SEP-1997: Used new ERRSTRING macro.
50 |
51 | Last updated by Richard Gooch 10-JUL-1998: Removed definitions of system
52 | errlist array.
53 |
54 |
55 | */
56 | #include <stdio.h>
57 | #include <stdlib.h>
58 | #include <errno.h>
59 | #include <sys/types.h>
60 | #include <unistd.h>
61 | #include <string.h>
62 | #include <sys/stat.h>
63 | #include <fcntl.h>
64 | #include <time.h>
65 |
66 | /* #include "pgpsendmail.h" */
67 |
68 | #define ERRSTRING strerror(errno)
69 |
70 | #define LINE_LENGTH 1024
71 | #define STRING_LENGTH 255
72 |
73 | enum NFSL {
74 | NFSL_SYSF,
75 | NFSL_SECV,
76 | NFSL_LOCKED,
77 | NFSL_OK,
78 | NFSL_STOLEN,
79 | NFSL_LOST
80 | };
81 |
82 |
83 | int sd1[2] /*, sd2[2] */; /* sd2 is used to have a collaborative
84 | dialogue between processes */
85 | int spawn_job (char *path, char *argv[], int *in_fd, int *out_fd, int *err_fd)
86 | /* This routine will fork(2) and execvp(2) a process.
87 | The file to execute must be pointed to by path .
88 | The NULL terminated list of arguments which will be passed to main must
89 | be pointed to by argv .
90 | The input file descriptor (fd = 0) for the process must be pointed to by
91 | in_fd .If the value here is less than 0, then a pipe to the process is
92 | opened and the writeable end is written to the storage pointed to by in_fd
93 | The standard output file descriptor (fd = 1) for the process must be
94 | pointed to by out_fd .If the value here is less than 0, then a pipe to
95 | the process is opened and the readable end is written to the storage
96 | pointed to by out_fd .
97 | The standard error output file descriptor (fd = 2) for the process must be
98 | pointed to by err_fd .If the value here is less than 0, then a pipe to
99 | the process is opened and the readable end is written to the storage
100 | pointed to by err_fd .
101 | The routine returns the child process ID on success, else it returns -1.
102 | */
103 | {
104 | int child_pid;
105 | /* char txt[LINE_LENGTH]; */
106 |
107 | if (pipe(sd1) == -1)
108 | {
109 | perror("pipe failed");
110 | return(1);
111 | }
112 |
113 | /* Fork and exec */
114 | switch ( child_pid = fork () )
115 | {
116 | case 0:
117 | /* Child: exec */
118 | close(sd1[0]);
119 |
120 | dup2( sd1[1], 1 ); /* stdout */
121 |
122 | /* fprintf (stderr, "dup2 1 result: %s\n", ERRSTRING); */
123 | dup2( sd1[1], 2 ); /* stderr */
124 | /* fprintf (stderr, "dup2 2 result: %s\n", ERRSTRING); */
125 |
126 | execvp (path, argv);
127 |
128 | fprintf (stderr, "Could not exec: \"%s\"\t%s\n", path, ERRSTRING);
129 | exit (1);
130 | break;
131 | case -1:
132 | /* Error */
133 | fprintf (stderr, "Could not fork\t%s\n", ERRSTRING);
134 | return (-1);
135 | break;
136 | default:
137 | /* Parent */
138 | break;
139 | }
140 | /* Parent only */
141 |
142 | close(sd1[1]);
143 |
144 | dup2 (sd1[0], 0);
145 |
146 | /* fprintf (stderr, "dup2 0 result: %s\n", ERRSTRING); */
147 | /* fprintf(stderr, "Reading child output\n");
148 | while (read(0, txt, 1000) != 0)
149 | fprintf(stderr, "child read %s\n", txt);
150 |
151 | fprintf(stderr, "Finished reading child output\n"); */
152 |
153 | return (child_pid);
154 | } /* End Function spawn_job */
155 |
156 |
157 | #define DEFAULT_LOCKTIME 300;
158 |
159 | time_t nfslock(char *path, char *namelock, int max_age, int notify)
160 | {
161 | int tries = 0, oldlck = 0, tmpfd;
162 | struct stat old_stat, our_tmp;
163 | char tmp[32];
164 | char *tpath, *fullpath;
165 |
166 | srandom(getpid()+time(0));
167 | max_age = max_age ? max_age : DEFAULT_LOCKTIME;
168 |
169 | /*
170 | * 1. create a tmp file with a psuedo random file name. we also make
171 | * tpath which is a buffer to store the full pathname of the tmp file.
172 | */
173 |
174 | sprintf(tmp,"slock%d.%d", (int)getpid(), (int)random());
175 |
176 | tpath = malloc(strlen(path) + 1 + strlen(tmp) + 1);
177 | if (tpath == NULL) return(NFSL_SYSF);
178 | sprintf(tpath,"%s/%s", path, tmp);
179 |
180 | tmpfd = open(tpath, O_CREAT|O_WRONLY|O_EXCL, S_IRUSR|S_IWUSR);
181 |
182 | if (tmpfd < 0) { /* open failed */
183 | close(tmpfd);
184 | unlink(tpath);
185 | free(tpath);
186 | return(NFSL_SYSF);
187 | }
188 |
189 | if (getuid()==0) { /* we're root, so be careful! */
190 | if (fstat(tmpfd, &our_tmp) < 0) { /* stat failed... shouldn't happen */
191 | close(tmpfd);
192 | unlink(tpath);
193 | free(tpath);
194 | return(NFSL_SYSF);
195 | }
196 | if (our_tmp.st_nlink != 1) { /* someone's trying to mess with us */
197 | fprintf(stderr,"nfslock: bad link count on %s\n",tpath);
198 | close(tmpfd);
199 | unlink(tpath);
200 | free(tpath);
201 | return(NFSL_SECV);
202 | }
203 | }
204 |
205 | /*
206 | * 2. make fullpath, a buffer for the full pathname of the lock file.
207 | * then start looping trying to lock it
208 | */
209 |
210 | fullpath = malloc(strlen(path) + 1 + strlen(namelock) + 1);
211 | if (fullpath == NULL) {
212 | close(tmpfd);
213 | unlink(tpath);
214 | free(tpath);
215 | return(NFSL_SYSF);
216 | }
217 | sprintf(fullpath,"%s/%s", path, namelock);
218 |
219 | while (tries < 10) {
220 |
221 | /*
222 | * 3. link tmp file to lock file. if it goes, we win and we clean
223 | * up and return the st_ctime of the lock file.
224 | */
225 |
226 | if (link(tpath, fullpath) == 0) {
227 | unlink(tpath); /* got it! */
228 | free(tpath);
229 | close(tmpfd);
230 | if (lstat(fullpath, &our_tmp) < 0) { /* stat failed... shouldn't happen */
231 | unlink(fullpath);
232 | free(fullpath);
233 | return (NFSL_SYSF);
234 | }
235 | free(fullpath);
236 | return(our_tmp.st_ctime);
237 | }
238 |
239 | /*
240 | * 4. the lock failed. check for a stale lock file, being mindful
241 | * of NFS and the fact the time is set from the NFS server. we
242 | * do a write on the tmp file to update its time to the server's
243 | * idea of "now."
244 | */
245 |
246 | oldlck = lstat(fullpath, &old_stat);
247 |
248 | if (write(tmpfd, "\0", 1) != 1 || fstat(tmpfd, &our_tmp) < 0)
249 | break; /* something bogus is going on */
250 |
251 | if (oldlck != -1 && old_stat.st_ctime + max_age < our_tmp.st_ctime) {
252 | unlink(fullpath); /* break the stale lock */
253 | if (notify) fprintf(stderr,"Breaking stale lock on %s.\n",fullpath);
254 | tries++;
255 | sleep(1+(random() % 10)); /* It is CRITICAL that we sleep after breaking
256 | * the lock. Otherwise, we could race with
257 | * another process and unlink it's newly-
258 | * created file.
259 | */
260 | continue;
261 | }
262 |
263 | /*
264 | * 5. try again
265 | */
266 |
267 | if (notify) {
268 | if (tries==0) fprintf(stderr,"Waiting for lock on file %s.\n",fullpath);
269 | else fprintf(stderr,"Still waiting...\n");
270 | }
271 | tries++;
272 | sleep(1+(random() % 10));
273 | }
274 |
275 | /*
276 | * 6. give up, failure.
277 | */
278 |
279 | errno = EEXIST;
280 | unlink(tpath);
281 | free(tpath);
282 | free(fullpath);
283 | close(tmpfd);
284 | return(NFSL_LOCKED);
285 | }
286 |
287 | int nfsunlock(char *path, char *namelock, int max_age, time_t birth)
288 | {
289 | int tmpfd;
290 | struct stat old_stat, our_tmp;
291 | char *tpath, *fullpath;
292 |
293 | srandom(getpid()+time(0));
294 | max_age = max_age ? max_age : DEFAULT_LOCKTIME;
295 |
296 | /*
297 | * 1. Build a temp file and stat that to get an idea of what the server
298 | * thinks the current time is (our_tmp.st_ctime)..
299 | */
300 |
301 | tpath = malloc(strlen(path) + 25); /* small slop factor- 23 s/b enough */
302 | if (tpath == NULL) return(NFSL_SYSF);
303 | sprintf(tpath,"%s/slock%d.%d", path, (int)getpid(), (int)random());
304 |
305 | tmpfd = open(tpath, O_CREAT|O_WRONLY|O_EXCL, S_IRUSR|S_IWUSR);
306 |
307 | if ((tmpfd < 0) || (write(tmpfd, "\0", 1) != 1)
308 | || (fstat(tmpfd, &our_tmp) < 0)) {
309 | /* The open failed, or we can't write the file, or we can't stat it */
310 | close(tmpfd);
311 | unlink(tpath);
312 | free(tpath);
313 | return(NFSL_SYSF);
314 | }
315 |
316 | close(tmpfd); /* We don't need this once we have our_tmp.st_ctime. */
317 | unlink(tpath);
318 | free(tpath);
319 |
320 | /*
321 | * 2. make fullpath, a buffer for the full pathname of the lock file
322 | */
323 |
324 | fullpath = malloc(strlen(path) + 1 + strlen(namelock) + 1);
325 | if (fullpath == NULL)
326 | return(NFSL_SYSF);
327 | sprintf(fullpath,"%s/%s", path, namelock);
328 |
329 | /*
330 | * 3. If the ctime hasn't been modified, unlink the file and return. If the
331 | * lock has expired, sleep the usual random interval before returning.
332 | * If we didn't sleep, there could be a race if the caller immediately
333 | * tries to relock the file.
334 | */
335 |
336 | if ( !lstat(fullpath, &old_stat) && /* stat succeeds so file is there */
337 | (old_stat.st_ctime == birth)) { /* hasn't been modified since birth */
338 | unlink(fullpath); /* so the lock is ours to remove */
339 | if (our_tmp.st_ctime >= birth + max_age) /* the lock has expired */
340 | sleep(1+(random() % 10)); /* so sleep a bit */
341 | free(fullpath);
342 | return(NFSL_OK); /* success */
343 | }
344 |
345 | free(fullpath); /* we don't use fullpath anymore */
346 |
347 | /*
348 | * 4. Either ctime has been modified, or the entire lock file is missing.
349 | * If the lock should still be ours, based on the ctime of the temp
350 | * file, return with NFSL_STOLEN. If not, then our lock is expired and
351 | * someone else has grabbed the file, so return NFSL_LOST.
352 | */
353 |
354 | if (our_tmp.st_ctime < birth + max_age) /* lock was stolen */
355 | return(NFSL_STOLEN);
356 |
357 | return(NFSL_LOST); /* The lock must have expired first. */
358 | }
359 |
360 |
361 |