Implement issetugid syscall

Implement issetugid syscall and provide a test. This gets rid of the
scary "Unsecure. Implement me" warning during compilation.
This commit is contained in:
Thomas Veerman 2011-11-28 10:03:43 +00:00
parent ef99a7a3dc
commit b4fb061802
20 changed files with 409 additions and 32 deletions

View file

@ -100,7 +100,7 @@
#define EXEC_RESTART 102 /* to PM: final part of exec for RS */
#define PROCSTAT 103 /* to PM */
#define GETPROCNR 104 /* to PM */
#define ISSETUGID 106 /* to PM: ask if process is tainted */
#define GETEPINFO_O 107 /* to PM: get pid/uid/gid of an endpoint */
#define ADDDMA 108 /* to PM: inform PM about a region of memory
* that is used for bus-master DMA

View file

@ -165,6 +165,7 @@ struct exec_newmem
time_t enst_ctime; /* Last changed time of executable file */
uid_t new_uid; /* Process UID after exec */
gid_t new_gid; /* Process GID after exec */
int setugid; /* Process is setuid or setgid */
char progname[16]; /* Should be at least PROC_NAME_LEN */
};

View file

@ -116,6 +116,7 @@ _PROTOTYPE( pid_t getpid, (void) );
_PROTOTYPE( pid_t getppid, (void) );
_PROTOTYPE( uid_t getuid, (void) );
_PROTOTYPE( int isatty, (int _fd) );
_PROTOTYPE( int issetugid, (void) );
_PROTOTYPE( int link, (const char *_existing, const char *_new) );
_PROTOTYPE( off_t lseek, (int _fd, off_t _offset, int _whence) );
_PROTOTYPE( long pathconf, (const char *_path, int _name) );

View file

@ -69,6 +69,7 @@ SRCS+= \
hypot.c \
index.c \
initgroups.c \
issetugid.c \
itoa.c \
loadname.c \
lock.c \

View file

@ -0,0 +1,14 @@
#include <sys/cdefs.h>
#include <lib.h>
#include <unistd.h>
int issetugid(void)
{
int r;
message m;
r = _syscall(PM_PROC_NR, ISSETUGID, &m);
if (r == -1) return 0; /* Default to old behavior */
return(r);
}

View file

@ -6,6 +6,10 @@
int issetugid(void)
{
#warning Unsecure. Implement me.
return 0;
int r;
message m;
r = _syscall(PM_PROC_NR, ISSETUGID, &m);
if (r == -1) return 0; /* Default to old behavior */
return(r);
}

View file

@ -43,7 +43,7 @@ FORWARD _PROTOTYPE( int exec_newmem, (int proc_e, vir_bytes text_addr, vir_bytes
int is_elf, dev_t st_dev, ino_t st_ino, time_t ctime,
char *progname, int new_uid, int new_gid,
vir_bytes *stack_topp, int *load_textp,
int *allow_setuidp) );
int *setugidp) );
FORWARD _PROTOTYPE( int is_script, (const char *exec_hdr, size_t exec_len));
FORWARD _PROTOTYPE( int patch_stack, (struct vnode *vp, char stack[ARG_MAX],
vir_bytes *stk_bytes, char path[PATH_MAX]) );
@ -168,6 +168,7 @@ PUBLIC int pm_exec(int proc_e, char *path, vir_bytes path_len, char *frame,
strncpy(execi.progname, cp, PROC_NAME_LEN-1);
execi.progname[PROC_NAME_LEN-1] = '\0';
execi.setugid = 0;
/* Open executable */
if ((vp = eat_path(&resolve, fp)) == NULL) {
@ -187,9 +188,15 @@ PUBLIC int pm_exec(int proc_e, char *path, vir_bytes path_len, char *frame,
if (r != OK) goto pm_execfinal;
if (round == 0) {
/* Deal with setuid/setgid executables */
if (vp->v_mode & I_SET_UID_BIT) execi.new_uid = vp->v_uid;
if (vp->v_mode & I_SET_GID_BIT) execi.new_gid = vp->v_gid;
/* Deal with setuid/setgid executables */
if (vp->v_mode & I_SET_UID_BIT) {
execi.new_uid = vp->v_uid;
execi.setugid = 1;
}
if (vp->v_mode & I_SET_GID_BIT) {
execi.new_gid = vp->v_gid;
execi.setugid = 1;
}
}
r = map_header(&execi.hdr, execi.vp);
@ -240,7 +247,9 @@ PUBLIC int pm_exec(int proc_e, char *path, vir_bytes path_len, char *frame,
if (r != OK) goto pm_execfinal;
clo_exec(rfp);
if (execi.allow_setuid) {
if (execi.setugid) {
/* If after loading the image we're still allowed to run with
* setuid or setgid, change credentials now */
rfp->fp_effuid = execi.new_uid;
rfp->fp_effgid = execi.new_gid;
}
@ -286,7 +295,7 @@ PRIVATE int load_aout(struct exec_info *execi)
execi->frame_len, sep_id, 0 /* is_elf */, vp->v_dev, vp->v_inode_nr,
execi->sb.st_ctime,
execi->progname, execi->new_uid, execi->new_gid,
&execi->stack_top, &execi->load_text, &execi->allow_setuid);
&execi->stack_top, &execi->load_text, &execi->setugid);
if (r != OK) {
printf("VFS: load_aout: exec_newmem failed: %d\n", r);
@ -343,7 +352,7 @@ PRIVATE int load_elf(struct exec_info *execi)
tot_bytes, execi->frame_len, sep_id, is_elf,
vp->v_dev, vp->v_inode_nr, execi->sb.st_ctime,
execi->progname, execi->new_uid, execi->new_gid,
&execi->stack_top, &execi->load_text, &execi->allow_setuid);
&execi->stack_top, &execi->load_text, &execi->setugid);
if (r != OK) {
printf("VFS: load_elf: exec_newmem failed: %d\n", r);
@ -381,7 +390,7 @@ PRIVATE int exec_newmem(
int new_gid,
vir_bytes *stack_topp,
int *load_textp,
int *allow_setuidp
int *setugidp
)
{
/* Allocate a new memory map for a process that tries to exec */
@ -389,6 +398,8 @@ PRIVATE int exec_newmem(
struct exec_newmem e;
message m;
assert(setugidp != NULL);
e.text_addr = text_addr;
e.text_bytes = text_bytes;
e.data_addr = data_addr;
@ -402,6 +413,7 @@ PRIVATE int exec_newmem(
e.enst_ctime = ctime;
e.new_uid = new_uid;
e.new_gid = new_gid;
e.setugid = *setugidp;
strncpy(e.progname, progname, sizeof(e.progname)-1);
e.progname[sizeof(e.progname)-1] = '\0';
@ -412,7 +424,7 @@ PRIVATE int exec_newmem(
*stack_topp = m.m1_i1;
*load_textp = !!(m.m1_i2 & EXC_NM_RF_LOAD_TEXT);
*allow_setuidp = !!(m.m1_i2 & EXC_NM_RF_ALLOW_SETUID);
*setugidp = !!(m.m1_i2 & EXC_NM_RF_ALLOW_SETUID);
return(m.m_type);
}

View file

@ -10,7 +10,7 @@ struct exec_info {
uid_t new_uid; /* Process UID after exec */
gid_t new_gid; /* Process GID after exec */
int load_text; /* Load text section? */
int allow_setuid; /* Allow setuid execution? */
int setugid; /* Allow set{u,g}id execution? */
struct vnode *vp; /* Exec file's vnode */
struct stat sb; /* Exec file's stat structure */
char progname[PROC_NAME_LEN]; /* Program name */

View file

@ -81,16 +81,30 @@ PUBLIC int do_exec_newmem()
if (r != OK)
panic("do_exec_newmem: sys_datacopy failed: %d", r);
if((r=vm_exec_newmem(proc_e, &args, sizeof(args), &stack_top, &flags)) == OK) {
allow_setuid= 0; /* Do not allow setuid execution */
if ((r = vm_exec_newmem(proc_e, &args, sizeof(args), &stack_top,
&flags)) == OK) {
allow_setuid = 0; /* Do not allow setuid execution */
rmp->mp_flags &= ~TAINTED; /* By default not tainted */
if (rmp->mp_tracer == NO_TRACER) {
/* Okay, setuid execution is allowed */
allow_setuid= 1;
allow_setuid = 1;
rmp->mp_effuid = args.new_uid;
rmp->mp_effgid = args.new_gid;
}
/* A process is considered 'tainted' when it's executing with
* setuid or setgid bit set, or when the real{u,g}id doesn't
* match the eff{u,g}id, respectively. */
if (allow_setuid && args.setugid) {
/* Program has setuid and/or setgid bits set */
rmp->mp_flags |= TAINTED;
} else if (rmp->mp_effuid != rmp->mp_realuid ||
rmp->mp_effgid != rmp->mp_realgid) {
rmp->mp_flags |= TAINTED;
}
/* System will save command line for debugging, ps(1) output, etc. */
strncpy(rmp->mp_name, args.progname, PROC_NAME_LEN-1);
rmp->mp_name[PROC_NAME_LEN-1] = '\0';

View file

@ -103,7 +103,7 @@ PUBLIC int do_fork()
}
/* Inherit only these flags. In normal fork(), PRIV_PROC is not inherited. */
rmc->mp_flags &= (IN_USE|DELAY_CALL);
rmc->mp_flags &= (IN_USE|DELAY_CALL|TAINTED);
rmc->mp_child_utime = 0; /* reset administration */
rmc->mp_child_stime = 0; /* reset administration */
rmc->mp_exitstatus = 0;

View file

@ -18,7 +18,8 @@
*===========================================================================*/
PUBLIC int do_get()
{
/* Handle GETUID, GETGID, GETGROUPS, GETGROUPS_O, GETPID, GETPGRP, GETSID.
/* Handle GETUID, GETGID, GETGROUPS, GETGROUPS_O, GETPID, GETPGRP, GETSID,
ISSETUGID.
*/
register struct mproc *rmp = mp;
@ -103,6 +104,10 @@ PUBLIC int do_get()
r = target->mp_procgrp;
break;
}
case ISSETUGID:
r = !!(rmp->mp_flags & TAINTED);
break;
default:
r = EINVAL;
break;

View file

@ -91,5 +91,6 @@ EXTERN struct mproc {
#define TRACE_EXIT 0x08000 /* tracer is forcing this process to exit */
#define TRACE_ZOMBIE 0x10000 /* waiting for tracer to issue WAIT call */
#define DELAY_CALL 0x20000 /* waiting for call before sending signal */
#define TAINTED 0x40000 /* process is 'tainted' */
#define MP_MAGIC 0xC0FFEE0

View file

@ -117,7 +117,7 @@ _PROTOTYPE (int (*call_vec[]), (void) ) = {
do_procstat, /* 103 = procstat */
do_getprocnr, /* 104 = getprocnr */
no_sys, /* 105 = unused */
no_sys, /* 106 = unused */
do_get, /* 106 = issetugid */
do_getepinfo_o, /* 107 = getepinfo XXX: old implementation*/
do_adddma, /* 108 = adddma */
do_deldma, /* 109 = deldma */

View file

@ -41,7 +41,7 @@ static int exec_newmem(int proc_e, vir_bytes text_addr, vir_bytes text_bytes,
int is_elf, dev_t st_dev, ino_t st_ino, time_t ctime,
char *progname, int new_uid, int new_gid,
vir_bytes *stack_topp, int *load_textp,
int *allow_setuidp);
int *setugidp);
static int is_script(const char *exec_hdr, size_t exec_len);
static int patch_stack(struct vnode *vp, char stack[ARG_MAX],
vir_bytes *stk_bytes);
@ -118,6 +118,7 @@ PUBLIC int pm_exec(int proc_e, char *path, vir_bytes path_len, char *frame,
strncpy(execi.progname, cp, PROC_NAME_LEN-1);
execi.progname[PROC_NAME_LEN-1] = '\0';
execi.setugid = 0;
/* Open executable */
if ((vp = eat_path(PATH_NOFLAGS, fp)) == NULL) return(err_code);
@ -136,9 +137,15 @@ PUBLIC int pm_exec(int proc_e, char *path, vir_bytes path_len, char *frame,
}
if (round == 0) {
/* Deal with setuid/setgid executables */
if (vp->v_mode & I_SET_UID_BIT) execi.new_uid = vp->v_uid;
if (vp->v_mode & I_SET_GID_BIT) execi.new_gid = vp->v_gid;
/* Deal with setuid/setgid executables */
if (vp->v_mode & I_SET_UID_BIT) {
execi.new_uid = vp->v_uid;
execi.setugid = 1;
}
if (vp->v_mode & I_SET_GID_BIT) {
execi.new_gid = vp->v_gid;
execi.setugid = 1;
}
}
r = map_header(&execi.hdr, execi.vp);
@ -190,7 +197,9 @@ PUBLIC int pm_exec(int proc_e, char *path, vir_bytes path_len, char *frame,
if (r != OK) return(r);
clo_exec(rfp);
if (execi.allow_setuid) {
if (execi.setugid) {
/* If after loading the image we're still allowed to run with
* setuid or setgid, change the credentials now */
rfp->fp_effuid = execi.new_uid;
rfp->fp_effgid = execi.new_gid;
}
@ -230,7 +239,7 @@ static int load_aout(struct exec_info *execi)
execi->frame_len, sep_id, 0 /* is_elf */, vp->v_dev, vp->v_inode_nr,
execi->sb.st_ctime,
execi->progname, execi->new_uid, execi->new_gid,
&execi->stack_top, &execi->load_text, &execi->allow_setuid);
&execi->stack_top, &execi->load_text, &execi->setugid);
if (r != OK) {
printf("VFS: load_aout: exec_newmem failed: %d\n", r);
@ -282,7 +291,7 @@ static int load_elf(struct exec_info *execi)
tot_bytes, execi->frame_len, sep_id, is_elf,
vp->v_dev, vp->v_inode_nr, execi->sb.st_ctime,
execi->progname, execi->new_uid, execi->new_gid,
&execi->stack_top, &execi->load_text, &execi->allow_setuid);
&execi->stack_top, &execi->load_text, &execi->setugid);
if (r != OK) {
printf("VFS: load_elf: exec_newmem failed: %d\n", r);
@ -320,13 +329,15 @@ static int exec_newmem(
int new_gid,
vir_bytes *stack_topp,
int *load_textp,
int *allow_setuidp
int *setugidp
)
{
int r;
struct exec_newmem e;
message m;
assert(setugidp != NULL);
e.text_addr = text_addr;
e.text_bytes = text_bytes;
e.data_addr = data_addr;
@ -340,6 +351,7 @@ static int exec_newmem(
e.enst_ctime = ctime;
e.new_uid = new_uid;
e.new_gid = new_gid;
e.setugid = *setugidp;
strncpy(e.progname, progname, sizeof(e.progname)-1);
e.progname[sizeof(e.progname)-1] = '\0';
@ -350,7 +362,7 @@ static int exec_newmem(
*stack_topp = m.m1_i1;
*load_textp = !!(m.m1_i2 & EXC_NM_RF_LOAD_TEXT);
*allow_setuidp = !!(m.m1_i2 & EXC_NM_RF_ALLOW_SETUID);
*setugidp = !!(m.m1_i2 & EXC_NM_RF_ALLOW_SETUID);
return(m.m_type);
}

View file

@ -10,7 +10,7 @@ struct exec_info {
uid_t new_uid; /* Process UID after exec */
gid_t new_gid; /* Process GID after exec */
int load_text; /* Load text section? */
int allow_setuid; /* Allow setuid execution? */
int setugid; /* Allow set{u,g}id execution? */
struct vnode *vp; /* Exec file's vnode */
struct stat sb; /* Exec file's stat structure */
char progname[PROC_NAME_LEN]; /* Program name */

View file

@ -16,10 +16,10 @@ OBJ= test1 test2 test3 test4 test5 test6 test7 test8 test9 \
test30 test31 test32 test34 test35 test36 test37 test38 \
test39 t10a t11a t11b test40 t40a t40b t40c t40d t40e t40f test41 \
test42 test45 test47 test49 test50 test51 test52 test53 \
test54 test56 test58
test54 test56 test58 t60a t60b
BIGOBJ= test20 test24
ROOTOBJ= test11 test33 test43 test44 test46
ROOTOBJ= test11 test33 test43 test44 test46 test60
GCCOBJ= test45-gcc test48 test49-gcc test55
GCCFPUOBJ= test51-gcc test52-gcc
OTHEROBJ= test57 test59
@ -53,7 +53,9 @@ depend: .gitignore
clean:
$(MAKE) -C select clean
-rm -rf *.o *.s *.bak test? test?? test??-gcc t10a t11a t11b \
t40a t40b t40c t40d t40e t40f t43 DIR*
t40a t40b t40c t40d t40e t40f t43 \
t60a t60b \
DIR*
test1: test1.c
test2: test2.c
@ -128,3 +130,6 @@ test57: test57.c test57loop.S
test58: test58.c
test59: test59.c
$(CC) $(CFLAGS) -o $@ $@.c -lmthread
test60: test60.c
t60a: t60a.c
t60b: t60b.c

View file

@ -15,6 +15,7 @@ tests=" 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 \
21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 \
41 42 43 44 45 45-gcc 46 47 48 49 49-gcc 50 \
51 51-gcc 52 52-gcc 53 54 55 56 57 58 59\
60 \
sh1.sh sh2.sh"
tests_no=`expr 0`

18
test/t60a.c Normal file
View file

@ -0,0 +1,18 @@
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
int main(int argc, char *argv[])
{
/* Return our tainted state to the parent */
int newmode;
char cmd[30];
if (argc < 2) return(-2);
if ((newmode = atoi(argv[1])) > 0) {
snprintf(cmd, sizeof(cmd), "chmod %o %s", newmode, argv[0]);
system(cmd);
}
return(issetugid());
}

23
test/t60b.c Normal file
View file

@ -0,0 +1,23 @@
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
int main(int argc, char **argv)
{
/* Return the tainted state of our child to our parent */
pid_t childpid;
int status;
childpid = fork();
if (childpid == (pid_t) -1) exit(-2);
else if (childpid == 0) {
exit(issetugid());
} else {
wait(&status);
}
return(WEXITSTATUS(status));
}

265
test/test60.c Normal file
View file

@ -0,0 +1,265 @@
#include <sys/types.h>
#include <sys/wait.h>
#include <stdio.h>
#include <unistd.h>
#define MAX_ERROR 5
#include "common.c"
int subtest = -1;
void test_self(void);
void test_setnone(void);
void test_setuid(void);
void test_setgid(void);
void test_effugid(void);
int execute(const char *prog, const char *arg);
int execute(const char *prog, const char *arg)
{
pid_t childpid;
int status;
char cmd[30];
snprintf(cmd, sizeof(cmd), "./%s", prog);
childpid = fork();
if (childpid == (pid_t) -1) {
return(-2);
} else if (childpid == 0) {
if (execl(cmd, prog, arg, NULL) == -1) {
exit(-2);
}
return(-2); /* Never reached */
} else {
wait(&status);
}
return(WEXITSTATUS(status));
}
void test_setgid(void)
{
/* Execve a new process that has setgid bits set */
subtest = 3;
/* When we exec a new process which has setgid set, that process should
* be tainted.
*/
system("chmod 2755 setgid");
if (execute("setgid", "0000") != 1) e(2);
/* When we exec a new process which has setgid set, but unsets that bit
* before calling issetugid() should still be tainted
*/
system("chmod 2755 setgid");
if (execute("setgid", "0755") != 1) e(3);
/* When we exec a new process which has setgid set, and then also sets
* setuid before calling issetugid() should still be tainted
*/
system("chmod 2755 setgid");
if (execute("setgid", "06755") != 1) e(4);
/* When we exec a new process that has setgid set, and which upon
* execution forks, the forked child should also be tainted */
system("chmod 2755 setgidfork");
if (execute("setgidfork", "0000") != 1) e(5);
}
void test_setuid(void)
{
/* Execve a new process that has setuid bits set */
subtest = 4;
/* When we exec a new process which has setuid set, that process should
* be tainted.
*/
system("chmod 4755 setuid");
if (execute("setuid", "0000") != 1) e(1);
/* When we exec a new process which has setuid set, but unsets that bit
* before calling issetugid() should still be tainted
*/
system("chmod 4755 setuid");
if (execute("setuid", "0755") != 1) e(2);
/* When we exec a new process which has setuid set, and then also sets
* setgid before calling issetugid() should still be tainted
*/
system("chmod 4755 setuid");
if (execute("setuid", "06755") != 1) e(3);
/* When we exec a new process that has setgid set, and which upon
* execution forks, the forked child should also be tainted */
system("chmod 4755 setuidfork");
if (execute("setuidfork", "0000") != 1) e(4);
}
void test_setugid(void)
{
/* Execve a new process that has setuid and setgid bits set */
subtest = 5;
/* When we exec a new process which has setugid set, that
* process should be tainted.
*/
system("chmod 6755 setugid");
if (execute("setugid", "0000") != 1) e(1);
/* When we exec a new process which has setugid set, but unsets those bits
* before calling issetugid() should still be tainted
*/
system("chmod 6755 setugid");
if (execute("setugid", "0755") != 1) e(2);
/* When we exec a new process that has setugid set, and which upon
* execution forks, the forked child should also be tainted */
system("chmod 6755 setugidfork");
if (execute("setugidfork", "0000") != 1) e(4);
}
void test_effugid(void)
{
/* Test taint status with different effective uid and gid */
pid_t childpid;
int status;
subtest = 6;
/* Start with effective uid */
childpid = fork();
if (childpid == (pid_t) -1) e(1);
else if (childpid == 0) {
/* We're the child */
/* We should be tainted */
if (issetugid() != 1) e(2);
/* Now execute a program without set{u,g}id; should not be tainted */
system("chmod 755 nobits");
if (execute("nobits", "0000") != 0) e(3);
/* Change effective uid into current+42 and try nobits again. This time
* it should be tainted */
if (seteuid(geteuid() + 42) != 0) e(4);
if (execute("nobits", "0000") != 1) e(5);
exit(EXIT_SUCCESS);
} else {
/* We're the parent, wait for the child to finish */
wait(&status);
}
/* Now test effective gid */
childpid = fork();
if (childpid == (pid_t) -1) e(1);
else if (childpid == 0) {
/* We're the child */
/* We should be tainted */
if (issetugid() != 1) e(2);
/* Now execute a program without set{u,g}id; should not be tainted */
system("chmod 755 nobits");
if (execute("nobits", "0000") != 0) e(3);
/* Change effective gid into current+42 and try nobits again. This time
* it should be tainted */
if (seteuid(getegid() + 42) != 0) e(4);
if (execute("nobits", "0000") != 1) e(5);
exit(EXIT_SUCCESS);
} else {
/* We're the parent, wait for the child to finish */
wait(&status);
}
}
void test_setnone(void)
{
/* Execve a new process that does not have setuid or setgid bits set */
subtest = 2;
/* When we exec a new process which doesn't have set{u,g}id set, that
* process should not be tainted */
system("chmod 755 nobits");
if (execute("nobits", "0000") != 0) e(2);
/* When we exec a new process which doesn't have set{u,g}id set, but
* sets them after execution, the process should still not be tainted
*/
system("chmod 755 nobits");
if (execute("nobits", "02755") != 0) e(4);
system("chmod 755 nobits");
if (execute("nobits", "04755") != 0) e(3);
system("chmod 755 nobits");
if (execute("nobits", "06755") != 0) e(5);
/* When we exec a new process that doesn't have setugid set, and which upon
* execution forks, the forked child should not be tainted either */
system("chmod 755 nobitsfork");
if (execute("nobitsfork", "0000") != 0) e(6);
}
void test_self(void)
{
/* We're supposed to be setuid. Verify. */
int status;
pid_t childpid;
subtest = 1;
if (issetugid() != 1) e(1);
childpid = fork();
if (childpid == -1) e(2);
else if (childpid == 0) {
/* We're the child and should inherit the tainted status of the parent
*/
if (issetugid() != 1) e(3);
/* Let's change to the bin user */
if (setuid((uid_t) 2) != 0) e(4);
if (getuid() != (uid_t) 2) e(5);
/* At this point, taint status should not have changed. */
if (issetugid() != 1) e(6);
exit(EXIT_SUCCESS);
} else {
/* We're the parent. Wait for the child to finish */
wait(&status);
}
}
void switch_to_su(void)
{
subtest = 0;
if (setuid(0) != 0) e(1);
}
int main(int argc, char **argv)
{
start(60);
system("cp ../t60a nobits");
system("cp ../t60a setgid");
system("cp ../t60a setuid");
system("cp ../t60a setugid");
system("cp ../t60b nobitsfork");
system("cp ../t60b setuidfork");
system("cp ../t60b setgidfork");
system("cp ../t60b setugidfork");
switch_to_su(); /* We have to be root to perform this test */
test_self();
test_setnone();
test_setuid();
test_setgid();
test_setugid();
test_effugid();
quit();
return(-1); /* Never reached */
}