Blame src/cmd/ksh93/sh/suid_exec.c

Packit Service a8c26c
/***********************************************************************
Packit Service a8c26c
*                                                                      *
Packit Service a8c26c
*               This software is part of the ast package               *
Packit Service a8c26c
*          Copyright (c) 1982-2011 AT&T Intellectual Property          *
Packit Service a8c26c
*                      and is licensed under the                       *
Packit Service a8c26c
*                 Eclipse Public License, Version 1.0                  *
Packit Service a8c26c
*                    by AT&T Intellectual Property                     *
Packit Service a8c26c
*                                                                      *
Packit Service a8c26c
*                A copy of the License is available at                 *
Packit Service a8c26c
*          http://www.eclipse.org/org/documents/epl-v10.html           *
Packit Service a8c26c
*         (with md5 checksum b35adb5213ca9657e911e9befb180842)         *
Packit Service a8c26c
*                                                                      *
Packit Service a8c26c
*              Information and Software Systems Research               *
Packit Service a8c26c
*                            AT&T Research                             *
Packit Service a8c26c
*                           Florham Park NJ                            *
Packit Service a8c26c
*                                                                      *
Packit Service a8c26c
*                  David Korn <dgk@research.att.com>                   *
Packit Service a8c26c
*                                                                      *
Packit Service a8c26c
***********************************************************************/
Packit Service a8c26c
#pragma prototyped
Packit Service a8c26c
/*
Packit Service a8c26c
 * This is a program to execute 'execute only' and suid/sgid shell scripts.
Packit Service a8c26c
 * This program must be owned by root and must have the set uid bit set.
Packit Service a8c26c
 * It must not have the set group id bit set.  This program must be installed
Packit Service a8c26c
 * where the define parameter THISPROG indicates to work correctly on system V
Packit Service a8c26c
 *
Packit Service a8c26c
 *  Written by David Korn
Packit Service a8c26c
 *  AT&T Labs
Packit Service a8c26c
 *  Enhanced by Rob Stampfli
Packit Service a8c26c
 */
Packit Service a8c26c
Packit Service a8c26c
/* The file name of the script to execute is argv[0]
Packit Service a8c26c
 * Argv[1] is the  program name
Packit Service a8c26c
 * The basic idea is to open the script as standard input, set the effective
Packit Service a8c26c
 *   user and group id correctly, and then exec the shell.
Packit Service a8c26c
 * The complicated part is getting the effective uid of the caller and 
Packit Service a8c26c
 *   setting the effective uid/gid.  The program which execs this program
Packit Service a8c26c
 *   may pass file descriptor FDIN as an open file with mode SPECIAL if
Packit Service a8c26c
 *   the effective user id is not the real user id.  The effective
Packit Service a8c26c
 *   user id for authentication purposes will be the owner of this
Packit Service a8c26c
 *   open file.  On systems without the setreuid() call, e[ug]id is set
Packit Service a8c26c
 *   by copying this program to a /tmp/file, making it a suid and/or sgid
Packit Service a8c26c
 *   program, and then execing this program.
Packit Service a8c26c
 * A forked version of this program waits until it can unlink the /tmp
Packit Service a8c26c
 *   file and then exits.  Actually, we fork() twice so the parent can
Packit Service a8c26c
 *   wait for the child to complete.  A pipe is used to guarantee that we
Packit Service a8c26c
 *   do not remove the /tmp file too soon.
Packit Service a8c26c
 */
Packit Service a8c26c
Packit Service a8c26c
#include	<ast.h>
Packit Service a8c26c
#include	"FEATURE/externs"
Packit Service a8c26c
#include	<ls.h>
Packit Service a8c26c
#include	<sig.h>
Packit Service a8c26c
#include	<error.h>
Packit Service a8c26c
#include	<sys/wait.h>
Packit Service a8c26c
#include	"version.h"
Packit Service a8c26c
Packit Service a8c26c
#define SPECIAL		04100	/* setuid execute only by owner */
Packit Service a8c26c
#define FDIN		10	/* must be same as /dev/fd below */
Packit Service a8c26c
#undef FDSYNC 
Packit Service a8c26c
#define FDSYNC		11	/* used on sys5 to synchronize cleanup */
Packit Service a8c26c
#define FDVERIFY	12	/* used to validate /tmp process */
Packit Service a8c26c
#undef BLKSIZE 
Packit Service a8c26c
#define BLKSIZE		sizeof(char*)*1024
Packit Service a8c26c
#define THISPROG	"/etc/suid_exec"
Packit Service a8c26c
#define DEFSHELL	"/bin/sh"
Packit Service a8c26c
Packit Service a8c26c
static void error_exit(const char*);
Packit Service a8c26c
static int in_dir(const char*, const char*);
Packit Service a8c26c
static int endsh(const char*);
Packit Service a8c26c
#ifndef _lib_setregid
Packit Service a8c26c
#   undef _lib_setreuid
Packit Service a8c26c
#endif
Packit Service a8c26c
#ifndef _lib_setreuid
Packit Service a8c26c
    static void setids(int,uid_t,gid_t);
Packit Service a8c26c
    static int mycopy(int, int);
Packit Service a8c26c
    static void maketemp(char*);
Packit Service a8c26c
#else
Packit Service a8c26c
    static void setids(int,int,int);
Packit Service a8c26c
#endif /* _lib_setreuid */
Packit Service a8c26c
Packit Service a8c26c
static const char version[]	= "\n@(#)$Id: suid_exec "SH_RELEASE" $\n";
Packit Service a8c26c
static const char badopen[]	= "cannot open";
Packit Service a8c26c
static const char badexec[]	= "cannot exec";
Packit Service a8c26c
static const char devfd[]	= "/dev/fd/10";	/* must match FDIN above */
Packit Service a8c26c
static char tmpname[]		= "/tmp/SUIDXXXXXX";
Packit Service a8c26c
static char **arglist;
Packit Service a8c26c
Packit Service a8c26c
static char *shell;
Packit Service a8c26c
static char *command;
Packit Service a8c26c
static uid_t ruserid;
Packit Service a8c26c
static uid_t euserid;
Packit Service a8c26c
static gid_t rgroupid;
Packit Service a8c26c
static gid_t egroupid;
Packit Service a8c26c
static struct stat statb;
Packit Service a8c26c
Packit Service a8c26c
int main(int argc,char *argv[])
Packit Service a8c26c
{
Packit Service a8c26c
	register int m,n;
Packit Service a8c26c
	register char *p;
Packit Service a8c26c
	struct stat statx;
Packit Service a8c26c
	int mode;
Packit Service a8c26c
	uid_t effuid;
Packit Service a8c26c
	gid_t effgid;
Packit Service a8c26c
	NOT_USED(argc);
Packit Service a8c26c
	arglist = argv;
Packit Service a8c26c
	if((command = argv[1]) == 0)
Packit Service a8c26c
		error_exit(badexec);
Packit Service a8c26c
	ruserid = getuid();
Packit Service a8c26c
	euserid = geteuid();
Packit Service a8c26c
	rgroupid = getgid();
Packit Service a8c26c
	egroupid = getegid();
Packit Service a8c26c
	p = argv[0];
Packit Service a8c26c
#ifndef _lib_setreuid
Packit Service a8c26c
	maketemp(tmpname);
Packit Service a8c26c
	if(strcmp(p,tmpname)==0)
Packit Service a8c26c
	{
Packit Service a8c26c
		/* At this point, the presumption is that we are the
Packit Service a8c26c
		 * version of THISPROG copied into /tmp, with the owner,
Packit Service a8c26c
		 * group, and setuid/gid bits correctly set.  This copy of
Packit Service a8c26c
		 * the program is executable by anyone, so we must be careful
Packit Service a8c26c
		 * not to allow just any invocation of it to succeed, since
Packit Service a8c26c
		 * it is setuid/gid.  Validate the proper execution by
Packit Service a8c26c
		 * examining the FDVERIFY file descriptor -- if it is owned
Packit Service a8c26c
		 * by root and is mode SPECIAL, then this is proof that it was
Packit Service a8c26c
		 * passed by a program with superuser privileges -- hence we
Packit Service a8c26c
		 * can presume legitimacy.  Otherwise, bail out, as we suspect
Packit Service a8c26c
		 * an impostor.
Packit Service a8c26c
		 */
Packit Service a8c26c
		if(fstat(FDVERIFY,&statb) < 0 || statb.st_uid != 0 ||
Packit Service a8c26c
		    (statb.st_mode & ~S_IFMT) != SPECIAL || close(FDVERIFY)<0)
Packit Service a8c26c
			error_exit(badexec);
Packit Service a8c26c
		/* This enables the grandchild to clean up /tmp file */
Packit Service a8c26c
		close(FDSYNC);
Packit Service a8c26c
		/* Make sure that this is a valid invocation of the clone.
Packit Service a8c26c
		 * Perhaps unnecessary, given FDVERIFY, but what the heck...
Packit Service a8c26c
		 */
Packit Service a8c26c
		if(stat(tmpname,&statb) < 0 || statb.st_nlink != 1 ||
Packit Service a8c26c
		    !S_ISREG(statb.st_mode))
Packit Service a8c26c
			error_exit(badexec);
Packit Service a8c26c
		if(ruserid != euserid &&
Packit Service a8c26c
		  ((statb.st_mode & S_ISUID) == 0 || statb.st_uid != euserid))
Packit Service a8c26c
			error_exit(badexec);
Packit Service a8c26c
		goto exec;
Packit Service a8c26c
	}
Packit Service a8c26c
	/* Make sure that this is the real setuid program, not the clone.
Packit Service a8c26c
	 * It is possible by clever hacking to get past this point in the
Packit Service a8c26c
	 * clone, but it doesn't do the hacker any good that I can see.
Packit Service a8c26c
	 */
Packit Service a8c26c
	if(euserid)
Packit Service a8c26c
		error_exit(badexec);
Packit Service a8c26c
#endif /* _lib_setreuid */
Packit Service a8c26c
	/* Open the script for reading first and then validate it.  This
Packit Service a8c26c
	 * prevents someone from pulling a switcheroo while we are validating.
Packit Service a8c26c
	 */
Packit Service a8c26c
	n = open(p,0);
Packit Service a8c26c
	if(n == FDIN)
Packit Service a8c26c
	{
Packit Service a8c26c
		n = dup(n);
Packit Service a8c26c
		close(FDIN);
Packit Service a8c26c
	}
Packit Service a8c26c
	if(n < 0)
Packit Service a8c26c
		error_exit(badopen);
Packit Service a8c26c
	/* validate execution rights to this script */
Packit Service a8c26c
	if(fstat(FDIN,&statb) < 0 || (statb.st_mode & ~S_IFMT) != SPECIAL)
Packit Service a8c26c
		euserid = ruserid;
Packit Service a8c26c
	else
Packit Service a8c26c
		euserid = statb.st_uid;
Packit Service a8c26c
	/* do it the easy way if you can */
Packit Service a8c26c
	if(euserid == ruserid && egroupid == rgroupid)
Packit Service a8c26c
	{
Packit Service a8c26c
		if(access(p,X_OK) < 0)
Packit Service a8c26c
			error_exit(badexec);
Packit Service a8c26c
	}
Packit Service a8c26c
	else
Packit Service a8c26c
	{
Packit Service a8c26c
		/* have to check access on each component */
Packit Service a8c26c
		while(*p++)
Packit Service a8c26c
		{
Packit Service a8c26c
			if(*p == '/' || *p == 0)
Packit Service a8c26c
			{
Packit Service a8c26c
				m = *p;
Packit Service a8c26c
				*p = 0;
Packit Service a8c26c
				if(eaccess(argv[0],X_OK) < 0)
Packit Service a8c26c
					error_exit(badexec);
Packit Service a8c26c
				*p = m;
Packit Service a8c26c
			}
Packit Service a8c26c
		}
Packit Service a8c26c
		p = argv[0];
Packit Service a8c26c
	}
Packit Service a8c26c
	if(fstat(n, &statb) < 0 || !S_ISREG(statb.st_mode))
Packit Service a8c26c
		error_exit(badopen);
Packit Service a8c26c
	if(stat(p, &statx) < 0 ||
Packit Service a8c26c
	  statb.st_ino != statx.st_ino || statb.st_dev != statx.st_dev)
Packit Service a8c26c
		error_exit(badexec);
Packit Service a8c26c
	if(stat(THISPROG, &statx) < 0 ||
Packit Service a8c26c
	  (statb.st_ino == statx.st_ino && statb.st_dev == statx.st_dev))
Packit Service a8c26c
		error_exit(badexec);
Packit Service a8c26c
	close(FDIN);
Packit Service a8c26c
	if(fcntl(n,F_DUPFD,FDIN) != FDIN)
Packit Service a8c26c
		error_exit(badexec);
Packit Service a8c26c
	close(n);
Packit Service a8c26c
Packit Service a8c26c
	/* compute the desired new effective user and group id */
Packit Service a8c26c
	effuid = euserid;
Packit Service a8c26c
	effgid = egroupid;
Packit Service a8c26c
	mode = 0;
Packit Service a8c26c
	if(statb.st_mode & S_ISUID)
Packit Service a8c26c
		effuid = statb.st_uid;
Packit Service a8c26c
	if(statb.st_mode & S_ISGID)
Packit Service a8c26c
		effgid = statb.st_gid;
Packit Service a8c26c
Packit Service a8c26c
	/* see if group needs setting */
Packit Service a8c26c
	if(effgid != egroupid)
Packit Service a8c26c
		if(effgid != rgroupid || setgid(rgroupid) < 0)
Packit Service a8c26c
			mode = S_ISGID;
Packit Service a8c26c
		
Packit Service a8c26c
	/* now see if the uid needs setting */
Packit Service a8c26c
	if(mode)
Packit Service a8c26c
	{
Packit Service a8c26c
		if(effuid != ruserid)
Packit Service a8c26c
			mode |= S_ISUID;
Packit Service a8c26c
	}
Packit Service a8c26c
	else if(effuid)
Packit Service a8c26c
	{
Packit Service a8c26c
		if(effuid != ruserid || setuid(ruserid) < 0)
Packit Service a8c26c
			mode = S_ISUID;
Packit Service a8c26c
	}
Packit Service a8c26c
		
Packit Service a8c26c
	if(mode)
Packit Service a8c26c
		setids(mode, effuid, effgid);
Packit Service a8c26c
#ifndef _lib_setreuid
Packit Service a8c26c
exec:
Packit Service a8c26c
#endif /* _lib_setreuid */
Packit Service a8c26c
	/* only use SHELL if file is in trusted directory and ends in sh */
Packit Service a8c26c
	shell = getenv("SHELL");
Packit Service a8c26c
	if(shell == 0 || !endsh(shell) || (
Packit Service a8c26c
		!in_dir("/bin",shell) &&
Packit Service a8c26c
		!in_dir("/usr/bin",shell) &&
Packit Service a8c26c
		!in_dir("/usr/lbin",shell) &&
Packit Service a8c26c
		!in_dir("/usr/local/bin",shell)))
Packit Service a8c26c
			shell = DEFSHELL;
Packit Service a8c26c
	argv[0] = command;
Packit Service a8c26c
	argv[1] = (char*)devfd;
Packit Service a8c26c
	execv(shell,argv);
Packit Service a8c26c
	error_exit(badexec);
Packit Service a8c26c
}
Packit Service a8c26c
Packit Service a8c26c
/*
Packit Service a8c26c
 * return true of shell ends in sh of ksh
Packit Service a8c26c
 */
Packit Service a8c26c
Packit Service a8c26c
static int endsh(register const char *shell)
Packit Service a8c26c
{
Packit Service a8c26c
	while(*shell)
Packit Service a8c26c
		shell++;
Packit Service a8c26c
	if(*--shell != 'h' || *--shell != 's')
Packit Service a8c26c
		return(0);
Packit Service a8c26c
	if(*--shell=='/')
Packit Service a8c26c
		return(1);
Packit Service a8c26c
	if(*shell=='k' && *--shell=='/')
Packit Service a8c26c
		return(1);
Packit Service a8c26c
	return(0);
Packit Service a8c26c
}
Packit Service a8c26c
Packit Service a8c26c
Packit Service a8c26c
/*
Packit Service a8c26c
 * return true of shell is in <dir> directory
Packit Service a8c26c
 */
Packit Service a8c26c
Packit Service a8c26c
static int in_dir(register const char *dir,register const char *shell)
Packit Service a8c26c
{
Packit Service a8c26c
	while(*dir)
Packit Service a8c26c
	{
Packit Service a8c26c
		if(*dir++ != *shell++)
Packit Service a8c26c
			return(0);
Packit Service a8c26c
	}
Packit Service a8c26c
	/* return true if next character is a '/' */
Packit Service a8c26c
	return(*shell=='/');
Packit Service a8c26c
}
Packit Service a8c26c
Packit Service a8c26c
static void error_exit(const char *message)
Packit Service a8c26c
{
Packit Service a8c26c
	sfprintf(sfstdout,"%s: %s\n",command,message);
Packit Service a8c26c
	exit(126);
Packit Service a8c26c
}
Packit Service a8c26c
Packit Service a8c26c
Packit Service a8c26c
/*
Packit Service a8c26c
 * This version of access checks against effective uid and effective gid
Packit Service a8c26c
 */
Packit Service a8c26c
Packit Service a8c26c
int eaccess(register const char *name, register int mode)
Packit Service a8c26c
{	
Packit Service a8c26c
	struct stat statb;
Packit Service a8c26c
	if (stat(name, &statb) == 0)
Packit Service a8c26c
	{
Packit Service a8c26c
		if(euserid == 0)
Packit Service a8c26c
		{
Packit Service a8c26c
			if(!S_ISREG(statb.st_mode) || mode != 1)
Packit Service a8c26c
				return(0);
Packit Service a8c26c
		    	/* root needs execute permission for someone */
Packit Service a8c26c
			mode = (S_IXUSR|S_IXGRP|S_IXOTH);
Packit Service a8c26c
		}
Packit Service a8c26c
		else if(euserid == statb.st_uid)
Packit Service a8c26c
			mode <<= 6;
Packit Service a8c26c
		else if(egroupid == statb.st_gid)
Packit Service a8c26c
			mode <<= 3;
Packit Service a8c26c
#ifdef _lib_getgroups
Packit Service a8c26c
		/* on some systems you can be in several groups */
Packit Service a8c26c
		else
Packit Service a8c26c
		{
Packit Service a8c26c
			static int maxgroups;
Packit Service a8c26c
			gid_t *groups=0; 
Packit Service a8c26c
			register int n;
Packit Service a8c26c
			if(maxgroups==0)
Packit Service a8c26c
			{
Packit Service a8c26c
				/* first time */
Packit Service a8c26c
				if((maxgroups=getgroups(0,groups)) < 0)
Packit Service a8c26c
				{
Packit Service a8c26c
					/* pre-POSIX system */
Packit Service a8c26c
					maxgroups=NGROUPS_MAX;
Packit Service a8c26c
				}
Packit Service a8c26c
			}
Packit Service a8c26c
			groups = (gid_t*)malloc((maxgroups+1)*sizeof(gid_t));
Packit Service a8c26c
			n = getgroups(maxgroups,groups);
Packit Service a8c26c
			while(--n >= 0)
Packit Service a8c26c
			{
Packit Service a8c26c
				if(groups[n] == statb.st_gid)
Packit Service a8c26c
				{
Packit Service a8c26c
					mode <<= 3;
Packit Service a8c26c
					break;
Packit Service a8c26c
				}
Packit Service a8c26c
			}
Packit Service a8c26c
		}
Packit Service a8c26c
#endif /* _lib_getgroups */
Packit Service a8c26c
		if(statb.st_mode & mode)
Packit Service a8c26c
			return(0);
Packit Service a8c26c
	}
Packit Service a8c26c
	return(-1);
Packit Service a8c26c
}
Packit Service a8c26c
Packit Service a8c26c
#ifdef _lib_setreuid
Packit Service a8c26c
static void setids(int mode,int owner,int group)
Packit Service a8c26c
{
Packit Service a8c26c
	if(mode & S_ISGID)
Packit Service a8c26c
		setregid(rgroupid,group);
Packit Service a8c26c
Packit Service a8c26c
	/* set effective uid even if S_ISUID is not set.  This is because
Packit Service a8c26c
	 * we are *really* executing EUID root at this point.  Even if S_ISUID
Packit Service a8c26c
	 * is not set, the value for owner that is passsed should be correct.
Packit Service a8c26c
	 */
Packit Service a8c26c
	setreuid(ruserid,owner);
Packit Service a8c26c
}
Packit Service a8c26c
Packit Service a8c26c
#else
Packit Service a8c26c
/*
Packit Service a8c26c
 * This version of setids creats a /tmp file and copies itself into it.
Packit Service a8c26c
 * The "clone" file is made executable with appropriate suid/sgid bits.
Packit Service a8c26c
 * Finally, the clone is exec'ed.  This file is unlinked by a grandchild
Packit Service a8c26c
 * of this program, who waits around until the text is free.
Packit Service a8c26c
 */
Packit Service a8c26c
Packit Service a8c26c
static void setids(int mode,uid_t owner,gid_t group)
Packit Service a8c26c
{
Packit Service a8c26c
	register int n,m;
Packit Service a8c26c
	int pv[2];
Packit Service a8c26c
Packit Service a8c26c
	/*
Packit Service a8c26c
	 * Create a token to pass to the new program for validation.
Packit Service a8c26c
	 * This token can only be procured by someone running with an
Packit Service a8c26c
	 * effective userid of root, and hence gives the clone a way to
Packit Service a8c26c
	 * certify that it was really invoked by THISPROG.  Someone who
Packit Service a8c26c
	 * is already root could spoof us, but why would they want to?
Packit Service a8c26c
	 *
Packit Service a8c26c
	 * Since we are root here, we must be careful:  What if someone
Packit Service a8c26c
	 * linked a valuable file to tmpname?
Packit Service a8c26c
	 */
Packit Service a8c26c
	unlink(tmpname);	/* should normally fail */
Packit Service a8c26c
#ifdef O_EXCL
Packit Service a8c26c
	if((n = open(tmpname, O_WRONLY | O_CREAT | O_EXCL, SPECIAL)) < 0 ||
Packit Service a8c26c
		unlink(tmpname) < 0)
Packit Service a8c26c
#else
Packit Service a8c26c
	if((n = open(tmpname, O_WRONLY | O_CREAT ,SPECIAL)) < 0 || unlink(tmpname) < 0)
Packit Service a8c26c
#endif
Packit Service a8c26c
		error_exit(badexec);
Packit Service a8c26c
	if(n != FDVERIFY)
Packit Service a8c26c
	{
Packit Service a8c26c
		close(FDVERIFY);
Packit Service a8c26c
		if(fcntl(n,F_DUPFD,FDVERIFY) != FDVERIFY)
Packit Service a8c26c
			error_exit(badexec);
Packit Service a8c26c
	}
Packit Service a8c26c
	mode |= S_IEXEC|(S_IEXEC>>3)|(S_IEXEC>>6);
Packit Service a8c26c
	/* create a pipe for synchronization */
Packit Service a8c26c
	if(pipe(pv) < 0)
Packit Service a8c26c
		error_exit(badexec);
Packit Service a8c26c
	if((n=fork()) == 0)
Packit Service a8c26c
	{	/* child */
Packit Service a8c26c
		close(FDVERIFY);
Packit Service a8c26c
		close(pv[1]);
Packit Service a8c26c
		if((n=fork()) == 0)
Packit Service a8c26c
		{	/* grandchild -- cleans up clone file */
Packit Service a8c26c
			signal(SIGHUP, SIG_IGN);
Packit Service a8c26c
			signal(SIGINT, SIG_IGN);
Packit Service a8c26c
			signal(SIGQUIT, SIG_IGN);
Packit Service a8c26c
			signal(SIGTERM, SIG_IGN);
Packit Service a8c26c
			read(pv[0],pv,1); /* wait for clone to close pipe */
Packit Service a8c26c
			while(unlink(tmpname) < 0 && errno == ETXTBSY)
Packit Service a8c26c
				sleep(1);
Packit Service a8c26c
			exit(0);
Packit Service a8c26c
	    	}
Packit Service a8c26c
		else if(n == -1)
Packit Service a8c26c
			exit(1);
Packit Service a8c26c
		else
Packit Service a8c26c
		{
Packit Service a8c26c
			/* Create a set[ug]id file that will become the clone. 
Packit Service a8c26c
			 * To make this atomic, without need for chown(), the
Packit Service a8c26c
			 * child takes on desired user and group.  The only
Packit Service a8c26c
			 * downsize of this that I can see is that it may
Packit Service a8c26c
			 * screw up some per- * user accounting.
Packit Service a8c26c
			 */
Packit Service a8c26c
			if((m = open(THISPROG, O_RDONLY)) < 0)
Packit Service a8c26c
				exit(1);
Packit Service a8c26c
			if((mode & S_ISGID) && setgid(group) < 0)
Packit Service a8c26c
				exit(1);
Packit Service a8c26c
			if((mode & S_ISUID) && owner && setuid(owner) < 0)
Packit Service a8c26c
				exit(1);
Packit Service a8c26c
#ifdef O_EXCL
Packit Service a8c26c
			if((n = open(tmpname,O_WRONLY|O_CREAT|O_TRUNC|O_EXCL, mode)) < 0)
Packit Service a8c26c
#else
Packit Service a8c26c
			unlink(tmpname);
Packit Service a8c26c
			if((n = open(tmpname,O_WRONLY|O_CREAT|O_TRUNC, mode)) < 0)
Packit Service a8c26c
#endif /* O_EXCL */
Packit Service a8c26c
				exit(1);
Packit Service a8c26c
			/* populate the clone */
Packit Service a8c26c
			m = mycopy(m,n);
Packit Service a8c26c
			if(chmod(tmpname,mode) <0)
Packit Service a8c26c
				exit(1);
Packit Service a8c26c
			exit(m);
Packit Service a8c26c
		}
Packit Service a8c26c
	}
Packit Service a8c26c
	else if(n == -1)
Packit Service a8c26c
		error_exit(badexec);
Packit Service a8c26c
	else
Packit Service a8c26c
	{
Packit Service a8c26c
		arglist[0] = (char*)tmpname;
Packit Service a8c26c
		close(pv[0]);
Packit Service a8c26c
		/* move write end of pipe into FDSYNC */
Packit Service a8c26c
		if(pv[1] != FDSYNC)
Packit Service a8c26c
		{
Packit Service a8c26c
			close(FDSYNC);
Packit Service a8c26c
			if(fcntl(pv[1],F_DUPFD,FDSYNC) != FDSYNC)
Packit Service a8c26c
				error_exit(badexec);
Packit Service a8c26c
		}
Packit Service a8c26c
		/* wait for child to die */
Packit Service a8c26c
		while((m = wait(0)) != n)
Packit Service a8c26c
			if(m == -1 && errno != EINTR)
Packit Service a8c26c
				break;
Packit Service a8c26c
		/* Kill any setuid status at this point.  That way, if the
Packit Service a8c26c
		 * clone is not setuid, we won't exec it as root.  Also, don't
Packit Service a8c26c
		 * neglect to consider that someone could have switched the
Packit Service a8c26c
		 * clone file on us.
Packit Service a8c26c
		 */
Packit Service a8c26c
		if(setuid(ruserid) < 0)
Packit Service a8c26c
			error_exit(badexec);
Packit Service a8c26c
		execv(tmpname,arglist);
Packit Service a8c26c
		error_exit(badexec);
Packit Service a8c26c
	}
Packit Service a8c26c
}
Packit Service a8c26c
Packit Service a8c26c
/*
Packit Service a8c26c
 * create a unique name into the <template>
Packit Service a8c26c
 */
Packit Service a8c26c
Packit Service a8c26c
static void maketemp(char *template)
Packit Service a8c26c
{
Packit Service a8c26c
	register char *cp = template;
Packit Service a8c26c
	register pid_t n = getpid();
Packit Service a8c26c
	/* skip to end of string */
Packit Service a8c26c
	while(*++cp);
Packit Service a8c26c
	/* convert process id to string */
Packit Service a8c26c
	while(n > 0)
Packit Service a8c26c
	{
Packit Service a8c26c
		*--cp = (n%10) + '0';
Packit Service a8c26c
		n /= 10;
Packit Service a8c26c
	}
Packit Service a8c26c
	
Packit Service a8c26c
}
Packit Service a8c26c
Packit Service a8c26c
/*
Packit Service a8c26c
 *  copy THISPROG into the open file number <fdo> and close <fdo>
Packit Service a8c26c
 */
Packit Service a8c26c
Packit Service a8c26c
static int mycopy(int fdi, int fdo)
Packit Service a8c26c
{
Packit Service a8c26c
	char buffer[BLKSIZE];
Packit Service a8c26c
	register int n;
Packit Service a8c26c
Packit Service a8c26c
	while((n = read(fdi,buffer,BLKSIZE)) > 0)
Packit Service a8c26c
		if(write(fdo,buffer,n) != n)
Packit Service a8c26c
			break;
Packit Service a8c26c
	close(fdi);
Packit Service a8c26c
	close(fdo);
Packit Service a8c26c
	return n;
Packit Service a8c26c
}
Packit Service a8c26c
Packit Service a8c26c
#endif /* _lib_setreuid */
Packit Service a8c26c
Packit Service a8c26c