/***********************************************************************
* *
* This software is part of the ast package *
* Copyright (c) 1982-2011 AT&T Intellectual Property *
* and is licensed under the *
* Eclipse Public License, Version 1.0 *
* by AT&T Intellectual Property *
* *
* A copy of the License is available at *
* (with md5 checksum b35adb5213ca9657e911e9befb180842) *
* *
* Information and Software Systems Research *
* AT&T Research *
* Florham Park NJ *
* *
* David Korn <dgk@research.att.com> *
* *
***********************************************************************/
#pragma prototyped
/*
* This program must be owned by root and must have the set uid bit set.
* It must not have the set group id bit set. This program must be installed
* where the define parameter THISPROG indicates to work correctly on system V
*
* Written by David Korn
* AT&T Labs
* Enhanced by Rob Stampfli
*/
/* The file name of the script to execute is argv[0]
* Argv[1] is the program name
* The basic idea is to open the script as standard input, set the effective
* user and group id correctly, and then exec the shell.
* The complicated part is getting the effective uid of the caller and
* may pass file descriptor FDIN as an open file with mode SPECIAL if
* the effective user id is not the real user id. The effective
* user id for authentication purposes will be the owner of this
* open file. On systems without the setreuid() call, e[ug]id is set
* program, and then execing this program.
* A forked version of this program waits until it can unlink the /tmp
* file and then exits. Actually, we fork() twice so the parent can
* wait for the child to complete. A pipe is used to guarantee that we
* do not remove the /tmp file too soon.
*/
#include <ast.h>
#include <ls.h>
#include <sig.h>
#include <error.h>
#include "version.h"
static void error_exit(const char*);
static int in_dir(const char*, const char*);
static int endsh(const char*);
#ifndef _lib_setregid
#endif
#ifndef _lib_setreuid
static int mycopy(int, int);
static void maketemp(char*);
#else
static void setids(int,int,int);
#endif /* _lib_setreuid */
static char **arglist;
static char *shell;
static char *command;
{
register int m,n;
register char *p;
int mode;
p = argv[0];
#ifndef _lib_setreuid
{
/* At this point, the presumption is that we are the
* version of THISPROG copied into /tmp, with the owner,
* the program is executable by anyone, so we must be careful
* not to allow just any invocation of it to succeed, since
* examining the FDVERIFY file descriptor -- if it is owned
* by root and is mode SPECIAL, then this is proof that it was
* passed by a program with superuser privileges -- hence we
* can presume legitimacy. Otherwise, bail out, as we suspect
* an impostor.
*/
/* This enables the grandchild to clean up /tmp file */
/* Make sure that this is a valid invocation of the clone.
* Perhaps unnecessary, given FDVERIFY, but what the heck...
*/
goto exec;
}
/* Make sure that this is the real setuid program, not the clone.
* It is possible by clever hacking to get past this point in the
* clone, but it doesn't do the hacker any good that I can see.
*/
if(euserid)
#endif /* _lib_setreuid */
/* Open the script for reading first and then validate it. This
* prevents someone from pulling a switcheroo while we are validating.
*/
n = open(p,0);
if(n == FDIN)
{
n = dup(n);
}
if(n < 0)
/* validate execution rights to this script */
else
/* do it the easy way if you can */
{
}
else
{
/* have to check access on each component */
while(*p++)
{
if(*p == '/' || *p == 0)
{
m = *p;
*p = 0;
*p = m;
}
}
p = argv[0];
}
close(n);
/* compute the desired new effective user and group id */
mode = 0;
/* see if group needs setting */
/* now see if the uid needs setting */
if(mode)
{
}
else if(effuid)
{
}
if(mode)
#ifndef _lib_setreuid
exec:
#endif /* _lib_setreuid */
/* only use SHELL if file is in trusted directory and ends in sh */
}
/*
* return true of shell ends in sh of ksh
*/
{
while(*shell)
shell++;
return(0);
if(*--shell=='/')
return(1);
return(1);
return(0);
}
/*
* return true of shell is in <dir> directory
*/
{
while(*dir)
{
return(0);
}
/* return true if next character is a '/' */
return(*shell=='/');
}
{
exit(126);
}
/*
* This version of access checks against effective uid and effective gid
*/
{
{
if(euserid == 0)
{
return(0);
/* root needs execute permission for someone */
}
mode <<= 6;
mode <<= 3;
#ifdef _lib_getgroups
/* on some systems you can be in several groups */
else
{
static int maxgroups;
register int n;
if(maxgroups==0)
{
/* first time */
{
/* pre-POSIX system */
}
}
while(--n >= 0)
{
{
mode <<= 3;
break;
}
}
}
#endif /* _lib_getgroups */
return(0);
}
return(-1);
}
#ifdef _lib_setreuid
{
/* set effective uid even if S_ISUID is not set. This is because
* we are *really* executing EUID root at this point. Even if S_ISUID
* is not set, the value for owner that is passsed should be correct.
*/
}
#else
/*
* This version of setids creats a /tmp file and copies itself into it.
* Finally, the clone is exec'ed. This file is unlinked by a grandchild
* of this program, who waits around until the text is free.
*/
{
register int n,m;
/*
* Create a token to pass to the new program for validation.
* This token can only be procured by someone running with an
* effective userid of root, and hence gives the clone a way to
* certify that it was really invoked by THISPROG. Someone who
* is already root could spoof us, but why would they want to?
*
* Since we are root here, we must be careful: What if someone
* linked a valuable file to tmpname?
*/
#ifdef O_EXCL
#else
#endif
if(n != FDVERIFY)
{
}
/* create a pipe for synchronization */
if((n=fork()) == 0)
{ /* child */
if((n=fork()) == 0)
{ /* grandchild -- cleans up clone file */
sleep(1);
exit(0);
}
else if(n == -1)
exit(1);
else
{
/* Create a set[ug]id file that will become the clone.
* To make this atomic, without need for chown(), the
* child takes on desired user and group. The only
* downsize of this that I can see is that it may
* screw up some per- * user accounting.
*/
exit(1);
exit(1);
exit(1);
#ifdef O_EXCL
#else
#endif /* O_EXCL */
exit(1);
/* populate the clone */
m = mycopy(m,n);
exit(1);
exit(m);
}
}
else if(n == -1)
else
{
/* move write end of pipe into FDSYNC */
{
}
/* wait for child to die */
while((m = wait(0)) != n)
break;
/* Kill any setuid status at this point. That way, if the
* clone is not setuid, we won't exec it as root. Also, don't
* neglect to consider that someone could have switched the
* clone file on us.
*/
}
}
/*
* create a unique name into the <template>
*/
{
/* skip to end of string */
while(*++cp);
/* convert process id to string */
while(n > 0)
{
n /= 10;
}
}
/*
* copy THISPROG into the open file number <fdo> and close <fdo>
*/
{
register int n;
break;
return n;
}
#endif /* _lib_setreuid */