|
|
1.1 ! root 1: /* spawn.c ! 2: Spawn a program securely. ! 3: ! 4: Copyright (C) 1992 Ian Lance Taylor ! 5: ! 6: This file is part of the Taylor UUCP package. ! 7: ! 8: This program is free software; you can redistribute it and/or ! 9: modify it under the terms of the GNU General Public License as ! 10: published by the Free Software Foundation; either version 2 of the ! 11: License, or (at your option) any later version. ! 12: ! 13: This program is distributed in the hope that it will be useful, but ! 14: WITHOUT ANY WARRANTY; without even the implied warranty of ! 15: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ! 16: General Public License for more details. ! 17: ! 18: You should have received a copy of the GNU General Public License ! 19: along with this program; if not, write to the Free Software ! 20: Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ! 21: ! 22: The author of the program may be contacted at [email protected] or ! 23: c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. ! 24: */ ! 25: ! 26: #include "uucp.h" ! 27: ! 28: #include "uudefs.h" ! 29: #include "sysdep.h" ! 30: ! 31: #include <errno.h> ! 32: ! 33: #if HAVE_FCNTL_H ! 34: #include <fcntl.h> ! 35: #else ! 36: #if HAVE_SYS_FILE_H ! 37: #include <sys/file.h> ! 38: #endif ! 39: #endif ! 40: ! 41: #ifndef O_RDONLY ! 42: #define O_RDONLY 0 ! 43: #define O_WRONLY 1 ! 44: #define O_RDWR 2 ! 45: #endif ! 46: ! 47: #ifndef FD_CLOEXEC ! 48: #define FD_CLOEXEC 1 ! 49: #endif ! 50: ! 51: #ifndef environ ! 52: extern char **environ; ! 53: #endif ! 54: ! 55: /* Spawn a child in a fairly secure fashion. This returns the process ! 56: ID of the child or -1 on error. It takes far too many arguments: ! 57: ! 58: pazargs -- arguments (element 0 is command) ! 59: aidescs -- file descriptors for stdin, stdout and stderr ! 60: fkeepuid -- TRUE if euid should be left unchanged ! 61: fkeepenv -- TRUE if environment should be left unmodified ! 62: zchdir -- directory to chdir to ! 63: fnosigs -- TRUE if child should ignore SIGHUP, SIGINT and SIGQUIT ! 64: fshell -- TRUE if should try /bin/sh if execve gets ENOEXEC ! 65: zpath -- value for environment variable PATH ! 66: zuu_machine -- value for environment variable UU_MACHINE ! 67: zuu_user -- value for environment variable UU_USER ! 68: ! 69: The aidescs array is three elements long. 0 is stdin, 1 is stdout ! 70: and 2 is stderr. The array may contain either file descriptor ! 71: numbers to dup appropriately, or one of the following: ! 72: ! 73: SPAWN_NULL -- set descriptor to /dev/null ! 74: SPAWN_READ_PIPE -- set aidescs element to pipe for parent to read ! 75: SPAWN_WRITE_PIPE -- set aidescs element to pipe for parent to write ! 76: ! 77: If fkeepenv is FALSE, a standard environment is created. The ! 78: environment arguments (zpath, zuu_machine and zuu_user) are only ! 79: used if fkeepenv is FALSE; any of them may be NULL. ! 80: ! 81: This routine expects that all file descriptors have been set to ! 82: close-on-exec, so it doesn't have to worry about closing them ! 83: explicitly. It sets the close-on-exec flag for the new pipe ! 84: descriptors it returns. */ ! 85: ! 86: pid_t ! 87: ixsspawn (pazargs, aidescs, fkeepuid, fkeepenv, zchdir, fnosigs, fshell, ! 88: zpath, zuu_machine, zuu_user) ! 89: const char **pazargs; ! 90: int aidescs[3]; ! 91: boolean fkeepuid; ! 92: boolean fkeepenv; ! 93: const char *zchdir; ! 94: boolean fnosigs; ! 95: boolean fshell; ! 96: const char *zpath; ! 97: const char *zuu_machine; ! 98: const char *zuu_user; ! 99: { ! 100: char *zshcmd; ! 101: int i; ! 102: char *azenv[9]; ! 103: char **pazenv; ! 104: boolean ferr; ! 105: int ierr = 0; ! 106: int onull; ! 107: int aichild_descs[3]; ! 108: int cpar_close; ! 109: int aipar_close[4]; ! 110: int cchild_close; ! 111: int aichild_close[3]; ! 112: pid_t iret = 0; ! 113: const char *zcmd; ! 114: ! 115: /* If we might have to use the shell, allocate enough space for the ! 116: quoted command before forking. Otherwise the allocation would ! 117: modify the data segment and we could not safely use vfork. */ ! 118: zshcmd = NULL; ! 119: if (fshell) ! 120: { ! 121: size_t clen; ! 122: ! 123: clen = 0; ! 124: for (i = 0; pazargs[i] != NULL; i++) ! 125: clen += strlen (pazargs[i]); ! 126: zshcmd = zbufalc (2 * clen + i); ! 127: } ! 128: ! 129: /* Set up a standard environment. This is again done before forking ! 130: because it will modify the data segment. */ ! 131: if (fkeepenv) ! 132: pazenv = environ; ! 133: else ! 134: { ! 135: const char *zterm, *ztz; ! 136: char *zspace; ! 137: int ienv; ! 138: ! 139: if (zpath == NULL) ! 140: zpath = CMDPATH; ! 141: ! 142: azenv[0] = zbufalc (sizeof "PATH=" + strlen (zpath)); ! 143: sprintf (azenv[0], "PATH=%s", zpath); ! 144: zspace = azenv[0] + sizeof "PATH=" - 1; ! 145: while ((zspace = strchr (zspace, ' ')) != NULL) ! 146: *zspace = ':'; ! 147: ! 148: azenv[1] = zbufalc (sizeof "HOME=" + strlen (zSspooldir)); ! 149: sprintf (azenv[1], "HOME=%s", zSspooldir); ! 150: ! 151: zterm = getenv ("TERM"); ! 152: if (zterm == NULL) ! 153: zterm = "unknown"; ! 154: azenv[2] = zbufalc (sizeof "TERM=" + strlen (zterm)); ! 155: sprintf (azenv[2], "TERM=%s", zterm); ! 156: ! 157: azenv[3] = zbufcpy ("SHELL=/bin/sh"); ! 158: ! 159: azenv[4] = zbufalc (sizeof "USER=" + strlen (OWNER)); ! 160: sprintf (azenv[4], "USER=%s", OWNER); ! 161: ! 162: ienv = 5; ! 163: ! 164: ztz = getenv ("TZ"); ! 165: if (ztz != NULL) ! 166: { ! 167: azenv[ienv] = zbufalc (sizeof "TZ=" + strlen (ztz)); ! 168: sprintf (azenv[ienv], "TZ=%s", ztz); ! 169: ++ienv; ! 170: } ! 171: ! 172: if (zuu_machine != NULL) ! 173: { ! 174: azenv[ienv] = zbufalc (sizeof "UU_MACHINE=" ! 175: + strlen (zuu_machine)); ! 176: sprintf (azenv[ienv], "UU_MACHINE=%s", zuu_machine); ! 177: ++ienv; ! 178: } ! 179: ! 180: if (zuu_user != NULL) ! 181: { ! 182: azenv[ienv] = zbufalc (sizeof "UU_USER=" ! 183: + strlen (zuu_user)); ! 184: sprintf (azenv[ienv], "UU_USER=%s", zuu_user); ! 185: ++ienv; ! 186: } ! 187: ! 188: azenv[ienv] = NULL; ! 189: pazenv = azenv; ! 190: } ! 191: ! 192: /* Set up any needed pipes. */ ! 193: ! 194: ferr = FALSE; ! 195: onull = -1; ! 196: cpar_close = 0; ! 197: cchild_close = 0; ! 198: ! 199: for (i = 0; i < 3; i++) ! 200: { ! 201: if (aidescs[i] == SPAWN_NULL) ! 202: { ! 203: if (onull < 0) ! 204: { ! 205: onull = open ((char *) "/dev/null", O_RDWR); ! 206: if (onull < 0 ! 207: || fcntl (onull, F_SETFD, ! 208: fcntl (onull, F_GETFD, 0) | FD_CLOEXEC) < 0) ! 209: { ! 210: ierr = errno; ! 211: (void) close (onull); ! 212: ferr = TRUE; ! 213: break; ! 214: } ! 215: aipar_close[cpar_close] = onull; ! 216: ++cpar_close; ! 217: } ! 218: aichild_descs[i] = onull; ! 219: } ! 220: else if (aidescs[i] != SPAWN_READ_PIPE ! 221: && aidescs[i] != SPAWN_WRITE_PIPE) ! 222: aichild_descs[i] = aidescs[i]; ! 223: else ! 224: { ! 225: int aipipe[2]; ! 226: ! 227: if (pipe (aipipe) < 0) ! 228: { ! 229: ierr = errno; ! 230: ferr = TRUE; ! 231: break; ! 232: } ! 233: ! 234: if (aidescs[i] == SPAWN_READ_PIPE) ! 235: { ! 236: aidescs[i] = aipipe[0]; ! 237: aichild_close[cchild_close] = aipipe[0]; ! 238: aichild_descs[i] = aipipe[1]; ! 239: aipar_close[cpar_close] = aipipe[1]; ! 240: } ! 241: else ! 242: { ! 243: aidescs[i] = aipipe[1]; ! 244: aichild_close[cchild_close] = aipipe[1]; ! 245: aichild_descs[i] = aipipe[0]; ! 246: aipar_close[cpar_close] = aipipe[0]; ! 247: } ! 248: ! 249: ++cpar_close; ! 250: ++cchild_close; ! 251: ! 252: if (fcntl (aidescs[i], F_SETFD, ! 253: fcntl (aidescs[i], F_GETFD, 0) | FD_CLOEXEC) < 0) ! 254: { ! 255: ierr = errno; ! 256: ferr = TRUE; ! 257: break; ! 258: } ! 259: } ! 260: } ! 261: ! 262: #if DEBUG > 1 ! 263: if (! ferr && FDEBUGGING (DEBUG_EXECUTE)) ! 264: { ! 265: ulog (LOG_DEBUG_START, "Forking %s", pazargs[0]); ! 266: for (i = 1; pazargs[i] != NULL; i++) ! 267: ulog (LOG_DEBUG_CONTINUE, " %s", pazargs[i]); ! 268: ulog (LOG_DEBUG_END, "%s", ""); ! 269: } ! 270: #endif ! 271: ! 272: if (! ferr) ! 273: { ! 274: /* This should really be vfork if available. */ ! 275: iret = ixsfork (); ! 276: if (iret < 0) ! 277: { ! 278: ferr = TRUE; ! 279: ierr = errno; ! 280: } ! 281: } ! 282: ! 283: if (ferr) ! 284: { ! 285: for (i = 0; i < cchild_close; i++) ! 286: (void) close (aichild_close[i]); ! 287: iret = -1; ! 288: } ! 289: ! 290: if (iret != 0) ! 291: { ! 292: /* The parent. Close the child's ends of the pipes and return ! 293: the process ID, or an error. */ ! 294: for (i = 0; i < cpar_close; i++) ! 295: (void) close (aipar_close[i]); ! 296: ubuffree (zshcmd); ! 297: if (! fkeepenv) ! 298: { ! 299: char **pz; ! 300: ! 301: for (pz = azenv; *pz != NULL; pz++) ! 302: ubuffree (*pz); ! 303: } ! 304: errno = ierr; ! 305: return iret; ! 306: } ! 307: ! 308: /* The child. */ ! 309: ! 310: #ifdef STDIN_FILENO ! 311: #if STDIN_FILENO != 0 || STDOUT_FILENO != 1 || STDERR_FILENO != 2 ! 312: #error The following code makes invalid assumptions ! 313: #endif ! 314: #endif ! 315: ! 316: for (i = 0; i < 3; i++) ! 317: { ! 318: if (aichild_descs[i] != i) ! 319: (void) dup2 (aichild_descs[i], i); ! 320: /* This should only be necessary if aichild_descs[i] == i, but ! 321: some systems copy the close-on-exec flag for a dupped ! 322: descriptor, which is wrong according to POSIX. */ ! 323: (void) fcntl (i, F_SETFD, fcntl (i, F_GETFD, 0) &~ FD_CLOEXEC); ! 324: } ! 325: ! 326: zcmd = pazargs[0]; ! 327: pazargs[0] = strrchr (zcmd, '/'); ! 328: if (pazargs[0] == NULL) ! 329: pazargs[0] = zcmd; ! 330: else ! 331: ++pazargs[0]; ! 332: ! 333: if (! fkeepuid) ! 334: { ! 335: (void) setuid (getuid ()); ! 336: (void) setgid (getgid ()); ! 337: } ! 338: ! 339: if (zchdir != NULL) ! 340: (void) chdir (zchdir); ! 341: ! 342: if (fnosigs) ! 343: { ! 344: #ifdef SIGHUP ! 345: (void) signal (SIGHUP, SIG_IGN); ! 346: #endif ! 347: #ifdef SIGINT ! 348: (void) signal (SIGINT, SIG_IGN); ! 349: #endif ! 350: #ifdef SIGQUIT ! 351: (void) signal (SIGQUIT, SIG_IGN); ! 352: #endif ! 353: } ! 354: ! 355: (void) execve ((char *) zcmd, (char **) pazargs, pazenv); ! 356: ! 357: /* The exec failed. If permitted, try using /bin/sh to execute a ! 358: shell script. */ ! 359: ! 360: if (errno == ENOEXEC && fshell) ! 361: { ! 362: char *zto; ! 363: const char *azshargs[4]; ! 364: ! 365: pazargs[0] = zcmd; ! 366: zto = zshcmd; ! 367: for (i = 0; pazargs[i] != NULL; i++) ! 368: { ! 369: const char *zfrom; ! 370: ! 371: for (zfrom = pazargs[i]; *zfrom != '\0'; zfrom++) ! 372: { ! 373: /* Some versions of /bin/sh appear to have a bug such ! 374: that quoting a '/' sometimes causes an error. I ! 375: don't know exactly when this happens (I can recreate ! 376: it on Ultrix 4.0), but in any case it is harmless to ! 377: not quote a '/'. */ ! 378: if (*zfrom != '/') ! 379: *zto++ = '\\'; ! 380: *zto++ = *zfrom; ! 381: } ! 382: *zto++ = ' '; ! 383: } ! 384: *(zto - 1) = '\0'; ! 385: ! 386: azshargs[0] = "sh"; ! 387: azshargs[1] = "-c"; ! 388: azshargs[2] = zshcmd; ! 389: azshargs[3] = NULL; ! 390: ! 391: (void) execve ((char *) "/bin/sh", (char **) azshargs, pazenv); ! 392: } ! 393: ! 394: _exit (EXIT_FAILURE); ! 395: ! 396: /* Avoid compiler warning. */ ! 397: return -1; ! 398: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.