home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Source Code 1993 July / THE_SOURCE_CODE_CD_ROM.iso / gnu / uucp-1.04 / unix / spawn.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-02-13  |  9.5 KB  |  399 lines

  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 ian@airs.com 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. }
  399.