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 |
57 | #include "rip.h"
58 |
59 | #include <stdio.h>
60 | #include <stdlib.h>
61 | #include <errno.h>
62 | #include <sys/types.h>
63 | #include <unistd.h>
64 | #include <string.h>
65 | #include <sys/stat.h>
66 | #include <fcntl.h>
67 | #include <time.h>
68 |
69 | /* #include "pgpsendmail.h" */
70 |
71 | #ifndef ERRSTRING
72 | #define ERRSTRING strerror(errno)
73 | #endif /* ERRSTRING */
74 |
75 | #define LINE_LENGTH 1024
76 | #define STRING_LENGTH 255
77 |
78 | enum NFSL {
79 | NFSL_SYSF,
80 | NFSL_SECV,
81 | NFSL_LOCKED,
82 | NFSL_OK,
83 | NFSL_STOLEN,
84 | NFSL_LOST
85 | };
86 |
87 |
88 | int sd1[2] /*, sd2[2] */; /* sd2 is used to have a collaborative
89 | dialogue between processes */
90 | int spawn_job (char *path, char *argv[], int *in_fd, int *out_fd, int *err_fd)
91 | /* This routine will fork(2) and execvp(2) a process.
92 | The file to execute must be pointed to by path .
93 | The NULL terminated list of arguments which will be passed to main must
94 | be pointed to by argv .
95 | The input file descriptor (fd = 0) for the process must be pointed to by
96 | in_fd .If the value here is less than 0, then a pipe to the process is
97 | opened and the writeable end is written to the storage pointed to by in_fd
98 | The standard output file descriptor (fd = 1) for the process must be
99 | pointed to by out_fd .If the value here is less than 0, then a pipe to
100 | the process is opened and the readable end is written to the storage
101 | pointed to by out_fd .
102 | The standard error output file descriptor (fd = 2) for the process must be
103 | pointed to by err_fd .If the value here is less than 0, then a pipe to
104 | the process is opened and the readable end is written to the storage
105 | pointed to by err_fd .
106 | The routine returns the child process ID on success, else it returns -1.
107 | */
108 | {
109 | int child_pid;
110 | /* char txt[LINE_LENGTH]; */
111 |
112 | if (pipe(sd1) == -1)
113 | {
114 | perror("pipe failed");
115 | return(1);
116 | }
117 |
118 | /* Fork and exec */
119 | switch ( child_pid = fork () )
120 | {
121 | case 0:
122 | /* Child: exec */
123 | close(sd1[0]);
124 |
125 | dup2( sd1[1], 1 ); /* stdout */
126 |
127 | /* fprintf (stderr, "dup2 1 result: %s\n", ERRSTRING); */
128 | dup2( sd1[1], 2 ); /* stderr */
129 | /* fprintf (stderr, "dup2 2 result: %s\n", ERRSTRING); */
130 |
131 | execvp (path, argv);
132 |
133 | fprintf (stderr, "Could not exec: \"%s\"\t%s\n", path, ERRSTRING);
134 | exit (1);
135 | break;
136 | case -1:
137 | /* Error */
138 | fprintf (stderr, "Could not fork\t%s\n", ERRSTRING);
139 | return (-1);
140 | break;
141 | default:
142 | /* Parent */
143 | break;
144 | }
145 | /* Parent only */
146 |
147 | close(sd1[1]);
148 |
149 | dup2 (sd1[0], 0);
150 |
151 | /* fprintf (stderr, "dup2 0 result: %s\n", ERRSTRING); */
152 | /* fprintf(stderr, "Reading child output\n");
153 | while (read(0, txt, 1000) != 0)
154 | fprintf(stderr, "child read %s\n", txt);
155 |
156 | fprintf(stderr, "Finished reading child output\n"); */
157 |
158 | return (child_pid);
159 | } /* End Function spawn_job */
160 |
161 |
162 | #define DEFAULT_LOCKTIME 300;
163 |
164 | time_t nfslock(char *path, char *namelock, int max_age, int notify)
165 | {
166 | int tries = 0, oldlck = 0, tmpfd;
167 | struct stat old_stat, our_tmp;
168 | char tmp[32];
169 | char *tpath, *fullpath;
170 |
171 | srandom(getpid()+time(0));
172 | max_age = max_age ? max_age : DEFAULT_LOCKTIME;
173 |
174 | /*
175 | * 1. create a tmp file with a psuedo random file name. we also make
176 | * tpath which is a buffer to store the full pathname of the tmp file.
177 | */
178 |
179 | sprintf(tmp,"slock%d.%d", (int)getpid(), (int)random());
180 |
181 | tpath = UT_malloc(strlen(path) + 1 + strlen(tmp) + 1);
182 | if (tpath == NULL) return(NFSL_SYSF);
183 | sprintf(tpath,"%s/%s", path, tmp);
184 |
185 | tmpfd = open(tpath, O_CREAT|O_WRONLY|O_EXCL, S_IRUSR|S_IWUSR);
186 |
187 | if (tmpfd < 0) { /* open failed */
188 | close(tmpfd);
189 | unlink(tpath);
190 | UT_free(tpath);
191 | return(NFSL_SYSF);
192 | }
193 |
194 | if (getuid()==0) { /* we're root, so be careful! */
195 | if (fstat(tmpfd, &our_tmp) < 0) { /* stat failed... shouldn't happen */
196 | close(tmpfd);
197 | unlink(tpath);
198 | UT_free(tpath);
199 | return(NFSL_SYSF);
200 | }
201 | if (our_tmp.st_nlink != 1) { /* someone's trying to mess with us */
202 | fprintf(stderr,"nfslock: bad link count on %s\n",tpath);
203 | close(tmpfd);
204 | unlink(tpath);
205 | UT_free(tpath);
206 | return(NFSL_SECV);
207 | }
208 | }
209 |
210 | /*
211 | * 2. make fullpath, a buffer for the full pathname of the lock file.
212 | * then start looping trying to lock it
213 | */
214 |
215 | fullpath = UT_malloc(strlen(path) + 1 + strlen(namelock) + 1);
216 | if (fullpath == NULL) {
217 | close(tmpfd);
218 | unlink(tpath);
219 | UT_free(tpath);
220 | return(NFSL_SYSF);
221 | }
222 | sprintf(fullpath,"%s/%s", path, namelock);
223 |
224 | while (tries < 10) {
225 |
226 | /*
227 | * 3. link tmp file to lock file. if it goes, we win and we clean
228 | * up and return the st_ctime of the lock file.
229 | */
230 |
231 | if (link(tpath, fullpath) == 0) {
232 | unlink(tpath); /* got it! */
233 | UT_free(tpath);
234 | close(tmpfd);
235 | if (lstat(fullpath, &our_tmp) < 0) { /* stat failed... shouldn't happen */
236 | unlink(fullpath);
237 | UT_free(fullpath);
238 | return (NFSL_SYSF);
239 | }
240 | UT_free(fullpath);
241 | return(our_tmp.st_ctime);
242 | }
243 |
244 | /*
245 | * 4. the lock failed. check for a stale lock file, being mindful
246 | * of NFS and the fact the time is set from the NFS server. we
247 | * do a write on the tmp file to update its time to the server's
248 | * idea of "now."
249 | */
250 |
251 | oldlck = lstat(fullpath, &old_stat);
252 |
253 | if (write(tmpfd, "\0", 1) != 1 || fstat(tmpfd, &our_tmp) < 0)
254 | break; /* something bogus is going on */
255 |
256 | if (oldlck != -1 && old_stat.st_ctime + max_age < our_tmp.st_ctime) {
257 | unlink(fullpath); /* break the stale lock */
258 | if (notify) fprintf(stderr,"Breaking stale lock on %s.\n",fullpath);
259 | tries++;
260 | sleep(1+(random() % 10)); /* It is CRITICAL that we sleep after breaking
261 | * the lock. Otherwise, we could race with
262 | * another process and unlink it's newly-
263 | * created file.
264 | */
265 | continue;
266 | }
267 |
268 | /*
269 | * 5. try again
270 | */
271 |
272 | if (notify) {
273 | if (tries==0) fprintf(stderr,"Waiting for lock on file %s.\n",fullpath);
274 | else fprintf(stderr,"Still waiting...\n");
275 | }
276 | tries++;
277 | sleep(1+(random() % 10));
278 | }
279 |
280 | /*
281 | * 6. give up, failure.
282 | */
283 |
284 | errno = EEXIST;
285 | unlink(tpath);
286 | UT_free(tpath);
287 | UT_free(fullpath);
288 | close(tmpfd);
289 | return(NFSL_LOCKED);
290 | }
291 |
292 | int nfsunlock(char *path, char *namelock, int max_age, time_t birth)
293 | {
294 | int tmpfd;
295 | struct stat old_stat, our_tmp;
296 | char *tpath, *fullpath;
297 |
298 | srandom(getpid()+time(0));
299 | max_age = max_age ? max_age : DEFAULT_LOCKTIME;
300 |
301 | /*
302 | * 1. Build a temp file and stat that to get an idea of what the server
303 | * thinks the current time is (our_tmp.st_ctime)..
304 | */
305 |
306 | tpath = UT_malloc(strlen(path) + 25); /* small slop factor- 23 s/b enough */
307 | if (tpath == NULL) return(NFSL_SYSF);
308 | sprintf(tpath,"%s/slock%d.%d", path, (int)getpid(), (int)random());
309 |
310 | tmpfd = open(tpath, O_CREAT|O_WRONLY|O_EXCL, S_IRUSR|S_IWUSR);
311 |
312 | if ((tmpfd < 0) || (write(tmpfd, "\0", 1) != 1)
313 | || (fstat(tmpfd, &our_tmp) < 0)) {
314 | /* The open failed, or we can't write the file, or we can't stat it */
315 | close(tmpfd);
316 | unlink(tpath);
317 | UT_free(tpath);
318 | return(NFSL_SYSF);
319 | }
320 |
321 | close(tmpfd); /* We don't need this once we have our_tmp.st_ctime. */
322 | unlink(tpath);
323 | UT_free(tpath);
324 |
325 | /*
326 | * 2. make fullpath, a buffer for the full pathname of the lock file
327 | */
328 |
329 | fullpath = UT_malloc(strlen(path) + 1 + strlen(namelock) + 1);
330 | if (fullpath == NULL)
331 | return(NFSL_SYSF);
332 | sprintf(fullpath,"%s/%s", path, namelock);
333 |
334 | /*
335 | * 3. If the ctime hasn't been modified, unlink the file and return. If the
336 | * lock has expired, sleep the usual random interval before returning.
337 | * If we didn't sleep, there could be a race if the caller immediately
338 | * tries to relock the file.
339 | */
340 |
341 | if ( !lstat(fullpath, &old_stat) && /* stat succeeds so file is there */
342 | (old_stat.st_ctime == birth)) { /* hasn't been modified since birth */
343 | unlink(fullpath); /* so the lock is ours to remove */
344 | if (our_tmp.st_ctime >= birth + max_age) /* the lock has expired */
345 | sleep(1+(random() % 10)); /* so sleep a bit */
346 | UT_free(fullpath);
347 | return(NFSL_OK); /* success */
348 | }
349 |
350 | UT_free(fullpath); /* we don't use fullpath anymore */
351 |
352 | /*
353 | * 4. Either ctime has been modified, or the entire lock file is missing.
354 | * If the lock should still be ours, based on the ctime of the temp
355 | * file, return with NFSL_STOLEN. If not, then our lock is expired and
356 | * someone else has grabbed the file, so return NFSL_LOST.
357 | */
358 |
359 | if (our_tmp.st_ctime < birth + max_age) /* lock was stolen */
360 | return(NFSL_STOLEN);
361 |
362 | return(NFSL_LOST); /* The lock must have expired first. */
363 | }
364 |
365 |
366 |