From b4fb06180253da4c62985b69062c12615fb8dfe5 Mon Sep 17 00:00:00 2001 From: Thomas Veerman Date: Mon, 28 Nov 2011 10:03:43 +0000 Subject: [PATCH] Implement issetugid syscall Implement issetugid syscall and provide a test. This gets rid of the scary "Unsecure. Implement me" warning during compilation. --- common/include/minix/callnr.h | 2 +- common/include/minix/type.h | 1 + include/unistd.h | 1 + lib/libc/other/Makefile.inc | 1 + lib/libc/other/issetugid.c | 14 ++ lib/nbsd_libc/sys-minix/issetugid.c | 8 +- servers/avfs/exec.c | 30 +++- servers/avfs/exec.h | 2 +- servers/pm/exec.c | 20 ++- servers/pm/forkexit.c | 2 +- servers/pm/getset.c | 7 +- servers/pm/mproc.h | 1 + servers/pm/table.c | 2 +- servers/vfs/exec.c | 30 +++- servers/vfs/exec.h | 2 +- test/Makefile | 11 +- test/run | 1 + test/t60a.c | 18 ++ test/t60b.c | 23 +++ test/test60.c | 265 ++++++++++++++++++++++++++++ 20 files changed, 409 insertions(+), 32 deletions(-) create mode 100644 lib/libc/other/issetugid.c create mode 100644 test/t60a.c create mode 100644 test/t60b.c create mode 100644 test/test60.c diff --git a/common/include/minix/callnr.h b/common/include/minix/callnr.h index 4960a3610..10cff2dbd 100644 --- a/common/include/minix/callnr.h +++ b/common/include/minix/callnr.h @@ -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 diff --git a/common/include/minix/type.h b/common/include/minix/type.h index 2237a5416..2c8c74cdc 100644 --- a/common/include/minix/type.h +++ b/common/include/minix/type.h @@ -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 */ }; diff --git a/include/unistd.h b/include/unistd.h index 87902b148..822c30657 100644 --- a/include/unistd.h +++ b/include/unistd.h @@ -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) ); diff --git a/lib/libc/other/Makefile.inc b/lib/libc/other/Makefile.inc index a35894d16..69fe50893 100644 --- a/lib/libc/other/Makefile.inc +++ b/lib/libc/other/Makefile.inc @@ -69,6 +69,7 @@ SRCS+= \ hypot.c \ index.c \ initgroups.c \ + issetugid.c \ itoa.c \ loadname.c \ lock.c \ diff --git a/lib/libc/other/issetugid.c b/lib/libc/other/issetugid.c new file mode 100644 index 000000000..bb40379d4 --- /dev/null +++ b/lib/libc/other/issetugid.c @@ -0,0 +1,14 @@ +#include +#include + +#include + +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); +} diff --git a/lib/nbsd_libc/sys-minix/issetugid.c b/lib/nbsd_libc/sys-minix/issetugid.c index 4472f2830..499331f15 100644 --- a/lib/nbsd_libc/sys-minix/issetugid.c +++ b/lib/nbsd_libc/sys-minix/issetugid.c @@ -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); } diff --git a/servers/avfs/exec.c b/servers/avfs/exec.c index eb91fc96b..45a1b9321 100644 --- a/servers/avfs/exec.c +++ b/servers/avfs/exec.c @@ -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); } diff --git a/servers/avfs/exec.h b/servers/avfs/exec.h index 32114d6ab..ddf6e5a5f 100644 --- a/servers/avfs/exec.h +++ b/servers/avfs/exec.h @@ -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 */ diff --git a/servers/pm/exec.c b/servers/pm/exec.c index 6b781419c..46f133eda 100644 --- a/servers/pm/exec.c +++ b/servers/pm/exec.c @@ -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'; diff --git a/servers/pm/forkexit.c b/servers/pm/forkexit.c index 9b1124ad9..04886dd56 100644 --- a/servers/pm/forkexit.c +++ b/servers/pm/forkexit.c @@ -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; diff --git a/servers/pm/getset.c b/servers/pm/getset.c index a2e3e7f3a..3a1f24763 100644 --- a/servers/pm/getset.c +++ b/servers/pm/getset.c @@ -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; diff --git a/servers/pm/mproc.h b/servers/pm/mproc.h index e841f0152..80afb4eba 100644 --- a/servers/pm/mproc.h +++ b/servers/pm/mproc.h @@ -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 diff --git a/servers/pm/table.c b/servers/pm/table.c index 364fbb039..cd8c6d88c 100644 --- a/servers/pm/table.c +++ b/servers/pm/table.c @@ -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 */ diff --git a/servers/vfs/exec.c b/servers/vfs/exec.c index 00fe3ee72..d05fc3b37 100644 --- a/servers/vfs/exec.c +++ b/servers/vfs/exec.c @@ -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); } diff --git a/servers/vfs/exec.h b/servers/vfs/exec.h index 32114d6ab..ddf6e5a5f 100644 --- a/servers/vfs/exec.h +++ b/servers/vfs/exec.h @@ -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 */ diff --git a/test/Makefile b/test/Makefile index 1721030b2..0e02e9bf0 100644 --- a/test/Makefile +++ b/test/Makefile @@ -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 diff --git a/test/run b/test/run index 332336eb7..fdf893be4 100755 --- a/test/run +++ b/test/run @@ -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` diff --git a/test/t60a.c b/test/t60a.c new file mode 100644 index 000000000..812996b87 --- /dev/null +++ b/test/t60a.c @@ -0,0 +1,18 @@ +#include +#include +#include + +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()); +} diff --git a/test/t60b.c b/test/t60b.c new file mode 100644 index 000000000..637517f31 --- /dev/null +++ b/test/t60b.c @@ -0,0 +1,23 @@ +#include +#include +#include +#include +#include + +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)); +} + diff --git a/test/test60.c b/test/test60.c new file mode 100644 index 000000000..3375a6065 --- /dev/null +++ b/test/test60.c @@ -0,0 +1,265 @@ +#include +#include +#include +#include + +#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 */ +} -- 2.44.0