File:  [MW Coherent from dump] / coherent / g / usr / lib / uucp / tay104 / unix / spawn.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs
Wed May 29 04:56:35 2019 UTC (7 years ago) by root
Branches: MarkWilliams, MAIN
CVS tags: relic, HEAD
coherent

/* spawn.c
   Spawn a program securely.

   Copyright (C) 1992 Ian Lance Taylor

   This file is part of the Taylor UUCP package.

   This program is free software; you can redistribute it and/or
   modify it under the terms of the GNU General Public License as
   published by the Free Software Foundation; either version 2 of the
   License, or (at your option) any later version.

   This program is distributed in the hope that it will be useful, but
   WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

   The author of the program may be contacted at [email protected] or
   c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254.
   */

#include "uucp.h"

#include "uudefs.h"
#include "sysdep.h"

#include <errno.h>

#if HAVE_FCNTL_H
#include <fcntl.h>
#else
#if HAVE_SYS_FILE_H
#include <sys/file.h>
#endif
#endif

#ifndef O_RDONLY
#define O_RDONLY 0
#define O_WRONLY 1
#define O_RDWR 2
#endif

#ifndef FD_CLOEXEC
#define FD_CLOEXEC 1
#endif

#ifndef environ
extern char **environ;
#endif

/* Spawn a child in a fairly secure fashion.  This returns the process
   ID of the child or -1 on error.  It takes far too many arguments:

   pazargs -- arguments (element 0 is command)
   aidescs -- file descriptors for stdin, stdout and stderr
   fkeepuid -- TRUE if euid should be left unchanged
   fkeepenv -- TRUE if environment should be left unmodified
   zchdir -- directory to chdir to
   fnosigs -- TRUE if child should ignore SIGHUP, SIGINT and SIGQUIT
   fshell -- TRUE if should try /bin/sh if execve gets ENOEXEC
   zpath -- value for environment variable PATH
   zuu_machine -- value for environment variable UU_MACHINE
   zuu_user -- value for environment variable UU_USER

   The aidescs array is three elements long.  0 is stdin, 1 is stdout
   and 2 is stderr.  The array may contain either file descriptor
   numbers to dup appropriately, or one of the following:

   SPAWN_NULL -- set descriptor to /dev/null
   SPAWN_READ_PIPE -- set aidescs element to pipe for parent to read
   SPAWN_WRITE_PIPE -- set aidescs element to pipe for parent to write

   If fkeepenv is FALSE, a standard environment is created.  The
   environment arguments (zpath, zuu_machine and zuu_user) are only
   used if fkeepenv is FALSE; any of them may be NULL.

   This routine expects that all file descriptors have been set to
   close-on-exec, so it doesn't have to worry about closing them
   explicitly.  It sets the close-on-exec flag for the new pipe
   descriptors it returns.  */

pid_t
ixsspawn (pazargs, aidescs, fkeepuid, fkeepenv, zchdir, fnosigs, fshell,
	  zpath, zuu_machine, zuu_user)
     const char **pazargs;
     int aidescs[3];
     boolean fkeepuid;
     boolean fkeepenv;
     const char *zchdir;
     boolean fnosigs;
     boolean fshell;
     const char *zpath;
     const char *zuu_machine;
     const char *zuu_user;
{
  char *zshcmd;
  int i;
  char *azenv[9];
  char **pazenv;
  boolean ferr;
  int ierr = 0;
  int onull;
  int aichild_descs[3];
  int cpar_close;
  int aipar_close[4];
  int cchild_close;
  int aichild_close[3];
  pid_t iret = 0;
  const char *zcmd;

  /* If we might have to use the shell, allocate enough space for the
     quoted command before forking.  Otherwise the allocation would
     modify the data segment and we could not safely use vfork.  */
  zshcmd = NULL;
  if (fshell)
    {
      size_t clen;

      clen = 0;
      for (i = 0; pazargs[i] != NULL; i++)
	clen += strlen (pazargs[i]);
      zshcmd = zbufalc (2 * clen + i);
    }

  /* Set up a standard environment.  This is again done before forking
     because it will modify the data segment.  */
  if (fkeepenv)
    pazenv = environ;
  else
    {
      const char *zterm, *ztz;
      char *zspace;
      int ienv;

      if (zpath == NULL)
	zpath = CMDPATH;

      azenv[0] = zbufalc (sizeof "PATH=" + strlen (zpath));
      sprintf (azenv[0], "PATH=%s", zpath);
      zspace = azenv[0] + sizeof "PATH=" - 1;
      while ((zspace = strchr (zspace, ' ')) != NULL)
	*zspace = ':';
    
      azenv[1] = zbufalc (sizeof "HOME=" + strlen (zSspooldir));
      sprintf (azenv[1], "HOME=%s", zSspooldir);

      zterm = getenv ("TERM");
      if (zterm == NULL)
	zterm = "unknown";
      azenv[2] = zbufalc (sizeof "TERM=" + strlen (zterm));
      sprintf (azenv[2], "TERM=%s", zterm);

      azenv[3] = zbufcpy ("SHELL=/bin/sh");
  
      azenv[4] = zbufalc (sizeof "USER=" + strlen (OWNER));
      sprintf (azenv[4], "USER=%s", OWNER);

      ienv = 5;

      ztz = getenv ("TZ");
      if (ztz != NULL)
	{
	  azenv[ienv] = zbufalc (sizeof "TZ=" + strlen (ztz));
	  sprintf (azenv[ienv], "TZ=%s", ztz);
	  ++ienv;
	}

      if (zuu_machine != NULL)
	{
	  azenv[ienv] = zbufalc (sizeof "UU_MACHINE="
				 + strlen (zuu_machine));
	  sprintf (azenv[ienv], "UU_MACHINE=%s", zuu_machine);
	  ++ienv;
	}

      if (zuu_user != NULL)
	{
	  azenv[ienv] = zbufalc (sizeof "UU_USER="
				 + strlen (zuu_user));
	  sprintf (azenv[ienv], "UU_USER=%s", zuu_user);
	  ++ienv;
	}

      azenv[ienv] = NULL;
      pazenv = azenv;
    }

  /* Set up any needed pipes.  */

  ferr = FALSE;
  onull = -1;
  cpar_close = 0;
  cchild_close = 0;

  for (i = 0; i < 3; i++)
    {
      if (aidescs[i] == SPAWN_NULL)
	{
	  if (onull < 0)
	    {
	      onull = open ((char *) "/dev/null", O_RDWR);
	      if (onull < 0
		  || fcntl (onull, F_SETFD,
			    fcntl (onull, F_GETFD, 0) | FD_CLOEXEC) < 0)
		{
		  ierr = errno;
		  (void) close (onull);
		  ferr = TRUE;
		  break;
		}
	      aipar_close[cpar_close] = onull;
	      ++cpar_close;
	    }
	  aichild_descs[i] = onull;
	}
      else if (aidescs[i] != SPAWN_READ_PIPE
	       && aidescs[i] != SPAWN_WRITE_PIPE)
	aichild_descs[i] = aidescs[i];
      else
	{
	  int aipipe[2];

	  if (pipe (aipipe) < 0)
	    {
	      ierr = errno;
	      ferr = TRUE;
	      break;
	    }

	  if (aidescs[i] == SPAWN_READ_PIPE)
	    {
	      aidescs[i] = aipipe[0];
	      aichild_close[cchild_close] = aipipe[0];
	      aichild_descs[i] = aipipe[1];
	      aipar_close[cpar_close] = aipipe[1];
	    }
	  else
	    {
	      aidescs[i] = aipipe[1];
	      aichild_close[cchild_close] = aipipe[1];
	      aichild_descs[i] = aipipe[0];
	      aipar_close[cpar_close] = aipipe[0];
	    }

	  ++cpar_close;
	  ++cchild_close;

	  if (fcntl (aidescs[i], F_SETFD,
		     fcntl (aidescs[i], F_GETFD, 0) | FD_CLOEXEC) < 0)
	    {
	      ierr = errno;
	      ferr = TRUE;
	      break;
	    }	      
	}
    }

#if DEBUG > 1
  if (! ferr && FDEBUGGING (DEBUG_EXECUTE))
    {
      ulog (LOG_DEBUG_START, "Forking %s", pazargs[0]);
      for (i = 1; pazargs[i] != NULL; i++)
	ulog (LOG_DEBUG_CONTINUE, " %s", pazargs[i]);
      ulog (LOG_DEBUG_END, "%s", "");
    }
#endif

  if (! ferr)
    {
      /* This should really be vfork if available.  */
      iret = ixsfork ();
      if (iret < 0)
	{
	  ferr = TRUE;
	  ierr = errno;
	}
    }

  if (ferr)
    {
      for (i = 0; i < cchild_close; i++)
	(void) close (aichild_close[i]);
      iret = -1;
    }

  if (iret != 0)
    {
      /* The parent.  Close the child's ends of the pipes and return
	 the process ID, or an error.  */
      for (i = 0; i < cpar_close; i++)
	(void) close (aipar_close[i]);
      ubuffree (zshcmd);
      if (! fkeepenv)
	{
	  char **pz;

	  for (pz = azenv; *pz != NULL; pz++)
	    ubuffree (*pz);
	}
      errno = ierr;
      return iret;
    }

  /* The child.  */

#ifdef STDIN_FILENO
#if STDIN_FILENO != 0 || STDOUT_FILENO != 1 || STDERR_FILENO != 2
 #error The following code makes invalid assumptions
#endif
#endif

  for (i = 0; i < 3; i++)
    {
      if (aichild_descs[i] != i)
	(void) dup2 (aichild_descs[i], i);
      /* This should only be necessary if aichild_descs[i] == i, but
	 some systems copy the close-on-exec flag for a dupped
	 descriptor, which is wrong according to POSIX.  */
      (void) fcntl (i, F_SETFD, fcntl (i, F_GETFD, 0) &~ FD_CLOEXEC);
    }

  zcmd = pazargs[0];
  pazargs[0] = strrchr (zcmd, '/');
  if (pazargs[0] == NULL)
    pazargs[0] = zcmd;
  else
    ++pazargs[0];

  if (! fkeepuid)
    {
      (void) setuid (getuid ());
      (void) setgid (getgid ());
    }

  if (zchdir != NULL)
    (void) chdir (zchdir);

  if (fnosigs)
    {
#ifdef SIGHUP
      (void) signal (SIGHUP, SIG_IGN);
#endif
#ifdef SIGINT
      (void) signal (SIGINT, SIG_IGN);
#endif
#ifdef SIGQUIT
      (void) signal (SIGQUIT, SIG_IGN);
#endif
    }

  (void) execve ((char *) zcmd, (char **) pazargs, pazenv);

  /* The exec failed.  If permitted, try using /bin/sh to execute a
     shell script.  */

  if (errno == ENOEXEC && fshell)
    {
      char *zto;
      const char *azshargs[4];
      
      pazargs[0] = zcmd;
      zto = zshcmd;
      for (i = 0; pazargs[i] != NULL; i++)
	{
	  const char *zfrom;

	  for (zfrom = pazargs[i]; *zfrom != '\0'; zfrom++)
	    {
	      /* Some versions of /bin/sh appear to have a bug such
		 that quoting a '/' sometimes causes an error.  I
		 don't know exactly when this happens (I can recreate
		 it on Ultrix 4.0), but in any case it is harmless to
		 not quote a '/'.  */
	      if (*zfrom != '/')
		*zto++ = '\\';
	      *zto++ = *zfrom;
	    }
	  *zto++ = ' ';
	}
      *(zto - 1) = '\0';

      azshargs[0] = "sh";
      azshargs[1] = "-c";
      azshargs[2] = zshcmd;
      azshargs[3] = NULL;

      (void) execve ((char *) "/bin/sh", (char **) azshargs, pazenv);
    }

  _exit (EXIT_FAILURE);

  /* Avoid compiler warning.  */
  return -1;
}

unix.superglobalmegacorp.com

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