Annotation of coherent/g/usr/lib/uucp/tay104/unix/spawn.c, revision 1.1.1.1

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: }

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.