|
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 |
|