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 |