1da2e3ebdSchin /***********************************************************************
2da2e3ebdSchin *                                                                      *
3da2e3ebdSchin *               This software is part of the ast package               *
4*b30d1939SAndy Fiddaman *          Copyright (c) 1982-2011 AT&T Intellectual Property          *
5da2e3ebdSchin *                      and is licensed under the                       *
6*b30d1939SAndy Fiddaman *                 Eclipse Public License, Version 1.0                  *
77c2fbfb3SApril Chin *                    by AT&T Intellectual Property                     *
8da2e3ebdSchin *                                                                      *
9da2e3ebdSchin *                A copy of the License is available at                 *
10*b30d1939SAndy Fiddaman *          http://www.eclipse.org/org/documents/epl-v10.html           *
11*b30d1939SAndy Fiddaman *         (with md5 checksum b35adb5213ca9657e911e9befb180842)         *
12da2e3ebdSchin *                                                                      *
13da2e3ebdSchin *              Information and Software Systems Research               *
14da2e3ebdSchin *                            AT&T Research                             *
15da2e3ebdSchin *                           Florham Park NJ                            *
16da2e3ebdSchin *                                                                      *
17da2e3ebdSchin *                  David Korn <dgk@research.att.com>                   *
18da2e3ebdSchin *                                                                      *
19da2e3ebdSchin ***********************************************************************/
20da2e3ebdSchin #pragma prototyped
21da2e3ebdSchin /*
22da2e3ebdSchin  * This is a program to execute 'execute only' and suid/sgid shell scripts.
23da2e3ebdSchin  * This program must be owned by root and must have the set uid bit set.
24da2e3ebdSchin  * It must not have the set group id bit set.  This program must be installed
25da2e3ebdSchin  * where the define parameter THISPROG indicates to work correctly on system V
26da2e3ebdSchin  *
27da2e3ebdSchin  *  Written by David Korn
28da2e3ebdSchin  *  AT&T Labs
29da2e3ebdSchin  *  Enhanced by Rob Stampfli
30da2e3ebdSchin  */
31da2e3ebdSchin 
32da2e3ebdSchin /* The file name of the script to execute is argv[0]
33da2e3ebdSchin  * Argv[1] is the  program name
34da2e3ebdSchin  * The basic idea is to open the script as standard input, set the effective
35da2e3ebdSchin  *   user and group id correctly, and then exec the shell.
36da2e3ebdSchin  * The complicated part is getting the effective uid of the caller and
37da2e3ebdSchin  *   setting the effective uid/gid.  The program which execs this program
38da2e3ebdSchin  *   may pass file descriptor FDIN as an open file with mode SPECIAL if
39da2e3ebdSchin  *   the effective user id is not the real user id.  The effective
40da2e3ebdSchin  *   user id for authentication purposes will be the owner of this
41da2e3ebdSchin  *   open file.  On systems without the setreuid() call, e[ug]id is set
42da2e3ebdSchin  *   by copying this program to a /tmp/file, making it a suid and/or sgid
43da2e3ebdSchin  *   program, and then execing this program.
44da2e3ebdSchin  * A forked version of this program waits until it can unlink the /tmp
45da2e3ebdSchin  *   file and then exits.  Actually, we fork() twice so the parent can
46da2e3ebdSchin  *   wait for the child to complete.  A pipe is used to guarantee that we
47da2e3ebdSchin  *   do not remove the /tmp file too soon.
48da2e3ebdSchin  */
49da2e3ebdSchin 
50da2e3ebdSchin #include	<ast.h>
51da2e3ebdSchin #include	"FEATURE/externs"
52da2e3ebdSchin #include	<ls.h>
53da2e3ebdSchin #include	<sig.h>
54da2e3ebdSchin #include	<error.h>
55da2e3ebdSchin #include	<sys/wait.h>
56da2e3ebdSchin #include	"version.h"
57da2e3ebdSchin 
58da2e3ebdSchin #define SPECIAL		04100	/* setuid execute only by owner */
59da2e3ebdSchin #define FDIN		10	/* must be same as /dev/fd below */
60da2e3ebdSchin #undef FDSYNC
61da2e3ebdSchin #define FDSYNC		11	/* used on sys5 to synchronize cleanup */
62da2e3ebdSchin #define FDVERIFY	12	/* used to validate /tmp process */
63da2e3ebdSchin #undef BLKSIZE
64da2e3ebdSchin #define BLKSIZE		sizeof(char*)*1024
65da2e3ebdSchin #define THISPROG	"/etc/suid_exec"
66da2e3ebdSchin #define DEFSHELL	"/bin/sh"
67da2e3ebdSchin 
68da2e3ebdSchin static void error_exit(const char*);
69da2e3ebdSchin static int in_dir(const char*, const char*);
70da2e3ebdSchin static int endsh(const char*);
71da2e3ebdSchin #ifndef _lib_setregid
72da2e3ebdSchin #   undef _lib_setreuid
73da2e3ebdSchin #endif
74da2e3ebdSchin #ifndef _lib_setreuid
75da2e3ebdSchin     static void setids(int,uid_t,gid_t);
76da2e3ebdSchin     static int mycopy(int, int);
77da2e3ebdSchin     static void maketemp(char*);
78da2e3ebdSchin #else
79da2e3ebdSchin     static void setids(int,int,int);
80da2e3ebdSchin #endif /* _lib_setreuid */
81da2e3ebdSchin 
82da2e3ebdSchin static const char version[]	= "\n@(#)$Id: suid_exec "SH_RELEASE" $\n";
83da2e3ebdSchin static const char badopen[]	= "cannot open";
84da2e3ebdSchin static const char badexec[]	= "cannot exec";
85da2e3ebdSchin static const char devfd[]	= "/dev/fd/10";	/* must match FDIN above */
86da2e3ebdSchin static char tmpname[]		= "/tmp/SUIDXXXXXX";
87da2e3ebdSchin static char **arglist;
88da2e3ebdSchin 
89da2e3ebdSchin static char *shell;
90da2e3ebdSchin static char *command;
91da2e3ebdSchin static uid_t ruserid;
92da2e3ebdSchin static uid_t euserid;
93da2e3ebdSchin static gid_t rgroupid;
94da2e3ebdSchin static gid_t egroupid;
95da2e3ebdSchin static struct stat statb;
96da2e3ebdSchin 
main(int argc,char * argv[])97da2e3ebdSchin int main(int argc,char *argv[])
98da2e3ebdSchin {
99da2e3ebdSchin 	register int m,n;
100da2e3ebdSchin 	register char *p;
101da2e3ebdSchin 	struct stat statx;
102da2e3ebdSchin 	int mode;
103da2e3ebdSchin 	uid_t effuid;
104da2e3ebdSchin 	gid_t effgid;
105da2e3ebdSchin 	NOT_USED(argc);
106da2e3ebdSchin 	arglist = argv;
107da2e3ebdSchin 	if((command = argv[1]) == 0)
108da2e3ebdSchin 		error_exit(badexec);
109da2e3ebdSchin 	ruserid = getuid();
110da2e3ebdSchin 	euserid = geteuid();
111da2e3ebdSchin 	rgroupid = getgid();
112da2e3ebdSchin 	egroupid = getegid();
113da2e3ebdSchin 	p = argv[0];
114da2e3ebdSchin #ifndef _lib_setreuid
115da2e3ebdSchin 	maketemp(tmpname);
116da2e3ebdSchin 	if(strcmp(p,tmpname)==0)
117da2e3ebdSchin 	{
118da2e3ebdSchin 		/* At this point, the presumption is that we are the
119da2e3ebdSchin 		 * version of THISPROG copied into /tmp, with the owner,
120da2e3ebdSchin 		 * group, and setuid/gid bits correctly set.  This copy of
121da2e3ebdSchin 		 * the program is executable by anyone, so we must be careful
122da2e3ebdSchin 		 * not to allow just any invocation of it to succeed, since
123da2e3ebdSchin 		 * it is setuid/gid.  Validate the proper execution by
124da2e3ebdSchin 		 * examining the FDVERIFY file descriptor -- if it is owned
125da2e3ebdSchin 		 * by root and is mode SPECIAL, then this is proof that it was
126da2e3ebdSchin 		 * passed by a program with superuser privileges -- hence we
127da2e3ebdSchin 		 * can presume legitimacy.  Otherwise, bail out, as we suspect
128da2e3ebdSchin 		 * an impostor.
129da2e3ebdSchin 		 */
130da2e3ebdSchin 		if(fstat(FDVERIFY,&statb) < 0 || statb.st_uid != 0 ||
131da2e3ebdSchin 		    (statb.st_mode & ~S_IFMT) != SPECIAL || close(FDVERIFY)<0)
132da2e3ebdSchin 			error_exit(badexec);
133da2e3ebdSchin 		/* This enables the grandchild to clean up /tmp file */
134da2e3ebdSchin 		close(FDSYNC);
135da2e3ebdSchin 		/* Make sure that this is a valid invocation of the clone.
136da2e3ebdSchin 		 * Perhaps unnecessary, given FDVERIFY, but what the heck...
137da2e3ebdSchin 		 */
138da2e3ebdSchin 		if(stat(tmpname,&statb) < 0 || statb.st_nlink != 1 ||
139da2e3ebdSchin 		    !S_ISREG(statb.st_mode))
140da2e3ebdSchin 			error_exit(badexec);
141da2e3ebdSchin 		if(ruserid != euserid &&
142da2e3ebdSchin 		  ((statb.st_mode & S_ISUID) == 0 || statb.st_uid != euserid))
143da2e3ebdSchin 			error_exit(badexec);
144da2e3ebdSchin 		goto exec;
145da2e3ebdSchin 	}
146da2e3ebdSchin 	/* Make sure that this is the real setuid program, not the clone.
147da2e3ebdSchin 	 * It is possible by clever hacking to get past this point in the
148da2e3ebdSchin 	 * clone, but it doesn't do the hacker any good that I can see.
149da2e3ebdSchin 	 */
150da2e3ebdSchin 	if(euserid)
151da2e3ebdSchin 		error_exit(badexec);
152da2e3ebdSchin #endif /* _lib_setreuid */
153da2e3ebdSchin 	/* Open the script for reading first and then validate it.  This
154da2e3ebdSchin 	 * prevents someone from pulling a switcheroo while we are validating.
155da2e3ebdSchin 	 */
156da2e3ebdSchin 	n = open(p,0);
157da2e3ebdSchin 	if(n == FDIN)
158da2e3ebdSchin 	{
159da2e3ebdSchin 		n = dup(n);
160da2e3ebdSchin 		close(FDIN);
161da2e3ebdSchin 	}
162da2e3ebdSchin 	if(n < 0)
163da2e3ebdSchin 		error_exit(badopen);
164da2e3ebdSchin 	/* validate execution rights to this script */
165da2e3ebdSchin 	if(fstat(FDIN,&statb) < 0 || (statb.st_mode & ~S_IFMT) != SPECIAL)
166da2e3ebdSchin 		euserid = ruserid;
167da2e3ebdSchin 	else
168da2e3ebdSchin 		euserid = statb.st_uid;
169da2e3ebdSchin 	/* do it the easy way if you can */
170da2e3ebdSchin 	if(euserid == ruserid && egroupid == rgroupid)
171da2e3ebdSchin 	{
172da2e3ebdSchin 		if(access(p,X_OK) < 0)
173da2e3ebdSchin 			error_exit(badexec);
174da2e3ebdSchin 	}
175da2e3ebdSchin 	else
176da2e3ebdSchin 	{
177da2e3ebdSchin 		/* have to check access on each component */
178da2e3ebdSchin 		while(*p++)
179da2e3ebdSchin 		{
180da2e3ebdSchin 			if(*p == '/' || *p == 0)
181da2e3ebdSchin 			{
182da2e3ebdSchin 				m = *p;
183da2e3ebdSchin 				*p = 0;
184da2e3ebdSchin 				if(eaccess(argv[0],X_OK) < 0)
185da2e3ebdSchin 					error_exit(badexec);
186da2e3ebdSchin 				*p = m;
187da2e3ebdSchin 			}
188da2e3ebdSchin 		}
189da2e3ebdSchin 		p = argv[0];
190da2e3ebdSchin 	}
191da2e3ebdSchin 	if(fstat(n, &statb) < 0 || !S_ISREG(statb.st_mode))
192da2e3ebdSchin 		error_exit(badopen);
193da2e3ebdSchin 	if(stat(p, &statx) < 0 ||
194da2e3ebdSchin 	  statb.st_ino != statx.st_ino || statb.st_dev != statx.st_dev)
195da2e3ebdSchin 		error_exit(badexec);
196da2e3ebdSchin 	if(stat(THISPROG, &statx) < 0 ||
197da2e3ebdSchin 	  (statb.st_ino == statx.st_ino && statb.st_dev == statx.st_dev))
198da2e3ebdSchin 		error_exit(badexec);
199da2e3ebdSchin 	close(FDIN);
200da2e3ebdSchin 	if(fcntl(n,F_DUPFD,FDIN) != FDIN)
201da2e3ebdSchin 		error_exit(badexec);
202da2e3ebdSchin 	close(n);
203da2e3ebdSchin 
204da2e3ebdSchin 	/* compute the desired new effective user and group id */
205da2e3ebdSchin 	effuid = euserid;
206da2e3ebdSchin 	effgid = egroupid;
207da2e3ebdSchin 	mode = 0;
208da2e3ebdSchin 	if(statb.st_mode & S_ISUID)
209da2e3ebdSchin 		effuid = statb.st_uid;
210da2e3ebdSchin 	if(statb.st_mode & S_ISGID)
211da2e3ebdSchin 		effgid = statb.st_gid;
212da2e3ebdSchin 
213da2e3ebdSchin 	/* see if group needs setting */
214da2e3ebdSchin 	if(effgid != egroupid)
215da2e3ebdSchin 		if(effgid != rgroupid || setgid(rgroupid) < 0)
216da2e3ebdSchin 			mode = S_ISGID;
217da2e3ebdSchin 
218da2e3ebdSchin 	/* now see if the uid needs setting */
219da2e3ebdSchin 	if(mode)
220da2e3ebdSchin 	{
221da2e3ebdSchin 		if(effuid != ruserid)
222da2e3ebdSchin 			mode |= S_ISUID;
223da2e3ebdSchin 	}
224da2e3ebdSchin 	else if(effuid)
225da2e3ebdSchin 	{
226da2e3ebdSchin 		if(effuid != ruserid || setuid(ruserid) < 0)
227da2e3ebdSchin 			mode = S_ISUID;
228da2e3ebdSchin 	}
229da2e3ebdSchin 
230da2e3ebdSchin 	if(mode)
231da2e3ebdSchin 		setids(mode, effuid, effgid);
232da2e3ebdSchin #ifndef _lib_setreuid
233da2e3ebdSchin exec:
234da2e3ebdSchin #endif /* _lib_setreuid */
235da2e3ebdSchin 	/* only use SHELL if file is in trusted directory and ends in sh */
236da2e3ebdSchin 	shell = getenv("SHELL");
237da2e3ebdSchin 	if(shell == 0 || !endsh(shell) || (
238da2e3ebdSchin 		!in_dir("/bin",shell) &&
239da2e3ebdSchin 		!in_dir("/usr/bin",shell) &&
240da2e3ebdSchin 		!in_dir("/usr/lbin",shell) &&
241da2e3ebdSchin 		!in_dir("/usr/local/bin",shell)))
242da2e3ebdSchin 			shell = DEFSHELL;
243da2e3ebdSchin 	argv[0] = command;
244da2e3ebdSchin 	argv[1] = (char*)devfd;
245da2e3ebdSchin 	execv(shell,argv);
246da2e3ebdSchin 	error_exit(badexec);
247da2e3ebdSchin }
248da2e3ebdSchin 
249da2e3ebdSchin /*
250da2e3ebdSchin  * return true of shell ends in sh of ksh
251da2e3ebdSchin  */
252da2e3ebdSchin 
endsh(register const char * shell)253da2e3ebdSchin static int endsh(register const char *shell)
254da2e3ebdSchin {
255da2e3ebdSchin 	while(*shell)
256da2e3ebdSchin 		shell++;
257da2e3ebdSchin 	if(*--shell != 'h' || *--shell != 's')
258da2e3ebdSchin 		return(0);
259da2e3ebdSchin 	if(*--shell=='/')
260da2e3ebdSchin 		return(1);
261da2e3ebdSchin 	if(*shell=='k' && *--shell=='/')
262da2e3ebdSchin 		return(1);
263da2e3ebdSchin 	return(0);
264da2e3ebdSchin }
265da2e3ebdSchin 
266da2e3ebdSchin 
267da2e3ebdSchin /*
268da2e3ebdSchin  * return true of shell is in <dir> directory
269da2e3ebdSchin  */
270da2e3ebdSchin 
in_dir(register const char * dir,register const char * shell)271da2e3ebdSchin static int in_dir(register const char *dir,register const char *shell)
272da2e3ebdSchin {
273da2e3ebdSchin 	while(*dir)
274da2e3ebdSchin 	{
275da2e3ebdSchin 		if(*dir++ != *shell++)
276da2e3ebdSchin 			return(0);
277da2e3ebdSchin 	}
278da2e3ebdSchin 	/* return true if next character is a '/' */
279da2e3ebdSchin 	return(*shell=='/');
280da2e3ebdSchin }
281da2e3ebdSchin 
error_exit(const char * message)282da2e3ebdSchin static void error_exit(const char *message)
283da2e3ebdSchin {
284da2e3ebdSchin 	sfprintf(sfstdout,"%s: %s\n",command,message);
285da2e3ebdSchin 	exit(126);
286da2e3ebdSchin }
287da2e3ebdSchin 
288da2e3ebdSchin 
289da2e3ebdSchin /*
290da2e3ebdSchin  * This version of access checks against effective uid and effective gid
291da2e3ebdSchin  */
292da2e3ebdSchin 
eaccess(register const char * name,register int mode)293da2e3ebdSchin int eaccess(register const char *name, register int mode)
294da2e3ebdSchin {
295da2e3ebdSchin 	struct stat statb;
296da2e3ebdSchin 	if (stat(name, &statb) == 0)
297da2e3ebdSchin 	{
298da2e3ebdSchin 		if(euserid == 0)
299da2e3ebdSchin 		{
300da2e3ebdSchin 			if(!S_ISREG(statb.st_mode) || mode != 1)
301da2e3ebdSchin 				return(0);
302da2e3ebdSchin 		    	/* root needs execute permission for someone */
303da2e3ebdSchin 			mode = (S_IXUSR|S_IXGRP|S_IXOTH);
304da2e3ebdSchin 		}
305da2e3ebdSchin 		else if(euserid == statb.st_uid)
306da2e3ebdSchin 			mode <<= 6;
307da2e3ebdSchin 		else if(egroupid == statb.st_gid)
308da2e3ebdSchin 			mode <<= 3;
309da2e3ebdSchin #ifdef _lib_getgroups
310da2e3ebdSchin 		/* on some systems you can be in several groups */
311da2e3ebdSchin 		else
312da2e3ebdSchin 		{
313da2e3ebdSchin 			static int maxgroups;
314da2e3ebdSchin 			gid_t *groups=0;
315da2e3ebdSchin 			register int n;
316da2e3ebdSchin 			if(maxgroups==0)
317da2e3ebdSchin 			{
318da2e3ebdSchin 				/* first time */
319da2e3ebdSchin 				if((maxgroups=getgroups(0,groups)) < 0)
320da2e3ebdSchin 				{
321da2e3ebdSchin 					/* pre-POSIX system */
322da2e3ebdSchin 					maxgroups=NGROUPS_MAX;
323da2e3ebdSchin 				}
324da2e3ebdSchin 			}
325da2e3ebdSchin 			groups = (gid_t*)malloc((maxgroups+1)*sizeof(gid_t));
326da2e3ebdSchin 			n = getgroups(maxgroups,groups);
327da2e3ebdSchin 			while(--n >= 0)
328da2e3ebdSchin 			{
329da2e3ebdSchin 				if(groups[n] == statb.st_gid)
330da2e3ebdSchin 				{
331da2e3ebdSchin 					mode <<= 3;
332da2e3ebdSchin 					break;
333da2e3ebdSchin 				}
334da2e3ebdSchin 			}
335da2e3ebdSchin 		}
336da2e3ebdSchin #endif /* _lib_getgroups */
337da2e3ebdSchin 		if(statb.st_mode & mode)
338da2e3ebdSchin 			return(0);
339da2e3ebdSchin 	}
340da2e3ebdSchin 	return(-1);
341da2e3ebdSchin }
342da2e3ebdSchin 
343da2e3ebdSchin #ifdef _lib_setreuid
setids(int mode,int owner,int group)344da2e3ebdSchin static void setids(int mode,int owner,int group)
345da2e3ebdSchin {
346da2e3ebdSchin 	if(mode & S_ISGID)
347da2e3ebdSchin 		setregid(rgroupid,group);
348da2e3ebdSchin 
349da2e3ebdSchin 	/* set effective uid even if S_ISUID is not set.  This is because
350da2e3ebdSchin 	 * we are *really* executing EUID root at this point.  Even if S_ISUID
351da2e3ebdSchin 	 * is not set, the value for owner that is passsed should be correct.
352da2e3ebdSchin 	 */
353da2e3ebdSchin 	setreuid(ruserid,owner);
354da2e3ebdSchin }
355da2e3ebdSchin 
356da2e3ebdSchin #else
357da2e3ebdSchin /*
358da2e3ebdSchin  * This version of setids creats a /tmp file and copies itself into it.
359da2e3ebdSchin  * The "clone" file is made executable with appropriate suid/sgid bits.
360da2e3ebdSchin  * Finally, the clone is exec'ed.  This file is unlinked by a grandchild
361da2e3ebdSchin  * of this program, who waits around until the text is free.
362da2e3ebdSchin  */
363da2e3ebdSchin 
setids(int mode,uid_t owner,gid_t group)364da2e3ebdSchin static void setids(int mode,uid_t owner,gid_t group)
365da2e3ebdSchin {
366da2e3ebdSchin 	register int n,m;
367da2e3ebdSchin 	int pv[2];
368da2e3ebdSchin 
369da2e3ebdSchin 	/*
370da2e3ebdSchin 	 * Create a token to pass to the new program for validation.
371da2e3ebdSchin 	 * This token can only be procured by someone running with an
372da2e3ebdSchin 	 * effective userid of root, and hence gives the clone a way to
373da2e3ebdSchin 	 * certify that it was really invoked by THISPROG.  Someone who
374da2e3ebdSchin 	 * is already root could spoof us, but why would they want to?
375da2e3ebdSchin 	 *
376da2e3ebdSchin 	 * Since we are root here, we must be careful:  What if someone
377da2e3ebdSchin 	 * linked a valuable file to tmpname?
378da2e3ebdSchin 	 */
379da2e3ebdSchin 	unlink(tmpname);	/* should normally fail */
380da2e3ebdSchin #ifdef O_EXCL
381da2e3ebdSchin 	if((n = open(tmpname, O_WRONLY | O_CREAT | O_EXCL, SPECIAL)) < 0 ||
382da2e3ebdSchin 		unlink(tmpname) < 0)
383da2e3ebdSchin #else
384da2e3ebdSchin 	if((n = open(tmpname, O_WRONLY | O_CREAT ,SPECIAL)) < 0 || unlink(tmpname) < 0)
385da2e3ebdSchin #endif
386da2e3ebdSchin 		error_exit(badexec);
387da2e3ebdSchin 	if(n != FDVERIFY)
388da2e3ebdSchin 	{
389da2e3ebdSchin 		close(FDVERIFY);
390da2e3ebdSchin 		if(fcntl(n,F_DUPFD,FDVERIFY) != FDVERIFY)
391da2e3ebdSchin 			error_exit(badexec);
392da2e3ebdSchin 	}
393da2e3ebdSchin 	mode |= S_IEXEC|(S_IEXEC>>3)|(S_IEXEC>>6);
394da2e3ebdSchin 	/* create a pipe for synchronization */
395da2e3ebdSchin 	if(pipe(pv) < 0)
396da2e3ebdSchin 		error_exit(badexec);
397da2e3ebdSchin 	if((n=fork()) == 0)
398da2e3ebdSchin 	{	/* child */
399da2e3ebdSchin 		close(FDVERIFY);
400da2e3ebdSchin 		close(pv[1]);
401da2e3ebdSchin 		if((n=fork()) == 0)
402da2e3ebdSchin 		{	/* grandchild -- cleans up clone file */
403da2e3ebdSchin 			signal(SIGHUP, SIG_IGN);
404da2e3ebdSchin 			signal(SIGINT, SIG_IGN);
405da2e3ebdSchin 			signal(SIGQUIT, SIG_IGN);
406da2e3ebdSchin 			signal(SIGTERM, SIG_IGN);
407da2e3ebdSchin 			read(pv[0],pv,1); /* wait for clone to close pipe */
408da2e3ebdSchin 			while(unlink(tmpname) < 0 && errno == ETXTBSY)
409da2e3ebdSchin 				sleep(1);
410da2e3ebdSchin 			exit(0);
411da2e3ebdSchin 	    	}
412da2e3ebdSchin 		else if(n == -1)
413da2e3ebdSchin 			exit(1);
414da2e3ebdSchin 		else
415da2e3ebdSchin 		{
416da2e3ebdSchin 			/* Create a set[ug]id file that will become the clone.
417da2e3ebdSchin 			 * To make this atomic, without need for chown(), the
418da2e3ebdSchin 			 * child takes on desired user and group.  The only
419da2e3ebdSchin 			 * downsize of this that I can see is that it may
420da2e3ebdSchin 			 * screw up some per- * user accounting.
421da2e3ebdSchin 			 */
422da2e3ebdSchin 			if((m = open(THISPROG, O_RDONLY)) < 0)
423da2e3ebdSchin 				exit(1);
424da2e3ebdSchin 			if((mode & S_ISGID) && setgid(group) < 0)
425da2e3ebdSchin 				exit(1);
426da2e3ebdSchin 			if((mode & S_ISUID) && owner && setuid(owner) < 0)
427da2e3ebdSchin 				exit(1);
428da2e3ebdSchin #ifdef O_EXCL
429da2e3ebdSchin 			if((n = open(tmpname,O_WRONLY|O_CREAT|O_TRUNC|O_EXCL, mode)) < 0)
430da2e3ebdSchin #else
431da2e3ebdSchin 			unlink(tmpname);
432da2e3ebdSchin 			if((n = open(tmpname,O_WRONLY|O_CREAT|O_TRUNC, mode)) < 0)
433da2e3ebdSchin #endif /* O_EXCL */
434da2e3ebdSchin 				exit(1);
435da2e3ebdSchin 			/* populate the clone */
436da2e3ebdSchin 			m = mycopy(m,n);
437da2e3ebdSchin 			if(chmod(tmpname,mode) <0)
438da2e3ebdSchin 				exit(1);
439da2e3ebdSchin 			exit(m);
440da2e3ebdSchin 		}
441da2e3ebdSchin 	}
442da2e3ebdSchin 	else if(n == -1)
443da2e3ebdSchin 		error_exit(badexec);
444da2e3ebdSchin 	else
445da2e3ebdSchin 	{
446da2e3ebdSchin 		arglist[0] = (char*)tmpname;
447da2e3ebdSchin 		close(pv[0]);
448da2e3ebdSchin 		/* move write end of pipe into FDSYNC */
449da2e3ebdSchin 		if(pv[1] != FDSYNC)
450da2e3ebdSchin 		{
451da2e3ebdSchin 			close(FDSYNC);
452da2e3ebdSchin 			if(fcntl(pv[1],F_DUPFD,FDSYNC) != FDSYNC)
453da2e3ebdSchin 				error_exit(badexec);
454da2e3ebdSchin 		}
455da2e3ebdSchin 		/* wait for child to die */
456da2e3ebdSchin 		while((m = wait(0)) != n)
457da2e3ebdSchin 			if(m == -1 && errno != EINTR)
458da2e3ebdSchin 				break;
459da2e3ebdSchin 		/* Kill any setuid status at this point.  That way, if the
460da2e3ebdSchin 		 * clone is not setuid, we won't exec it as root.  Also, don't
461da2e3ebdSchin 		 * neglect to consider that someone could have switched the
462da2e3ebdSchin 		 * clone file on us.
463da2e3ebdSchin 		 */
464da2e3ebdSchin 		if(setuid(ruserid) < 0)
465da2e3ebdSchin 			error_exit(badexec);
466da2e3ebdSchin 		execv(tmpname,arglist);
467da2e3ebdSchin 		error_exit(badexec);
468da2e3ebdSchin 	}
469da2e3ebdSchin }
470da2e3ebdSchin 
471da2e3ebdSchin /*
472da2e3ebdSchin  * create a unique name into the <template>
473da2e3ebdSchin  */
474da2e3ebdSchin 
maketemp(char * template)475da2e3ebdSchin static void maketemp(char *template)
476da2e3ebdSchin {
477da2e3ebdSchin 	register char *cp = template;
478da2e3ebdSchin 	register pid_t n = getpid();
479da2e3ebdSchin 	/* skip to end of string */
480da2e3ebdSchin 	while(*++cp);
481da2e3ebdSchin 	/* convert process id to string */
482da2e3ebdSchin 	while(n > 0)
483da2e3ebdSchin 	{
484da2e3ebdSchin 		*--cp = (n%10) + '0';
485da2e3ebdSchin 		n /= 10;
486da2e3ebdSchin 	}
487da2e3ebdSchin 
488da2e3ebdSchin }
489da2e3ebdSchin 
490da2e3ebdSchin /*
491da2e3ebdSchin  *  copy THISPROG into the open file number <fdo> and close <fdo>
492da2e3ebdSchin  */
493da2e3ebdSchin 
mycopy(int fdi,int fdo)494da2e3ebdSchin static int mycopy(int fdi, int fdo)
495da2e3ebdSchin {
496da2e3ebdSchin 	char buffer[BLKSIZE];
497da2e3ebdSchin 	register int n;
498da2e3ebdSchin 
499da2e3ebdSchin 	while((n = read(fdi,buffer,BLKSIZE)) > 0)
500da2e3ebdSchin 		if(write(fdo,buffer,n) != n)
501da2e3ebdSchin 			break;
502da2e3ebdSchin 	close(fdi);
503da2e3ebdSchin 	close(fdo);
504da2e3ebdSchin 	return n;
505da2e3ebdSchin }
506da2e3ebdSchin 
507da2e3ebdSchin #endif /* _lib_setreuid */
508da2e3ebdSchin 
509da2e3ebdSchin 
510