]> Zhao Yanbai Git Server - minix.git/commitdiff
Implement issetugid syscall
authorThomas Veerman <thomas@minix3.org>
Mon, 28 Nov 2011 10:03:43 +0000 (10:03 +0000)
committerThomas Veerman <thomas@minix3.org>
Mon, 28 Nov 2011 10:03:43 +0000 (10:03 +0000)
Implement issetugid syscall and provide a test. This gets rid of the
scary "Unsecure. Implement me" warning during compilation.

20 files changed:
common/include/minix/callnr.h
common/include/minix/type.h
include/unistd.h
lib/libc/other/Makefile.inc
lib/libc/other/issetugid.c [new file with mode: 0644]
lib/nbsd_libc/sys-minix/issetugid.c
servers/avfs/exec.c
servers/avfs/exec.h
servers/pm/exec.c
servers/pm/forkexit.c
servers/pm/getset.c
servers/pm/mproc.h
servers/pm/table.c
servers/vfs/exec.c
servers/vfs/exec.h
test/Makefile
test/run
test/t60a.c [new file with mode: 0644]
test/t60b.c [new file with mode: 0644]
test/test60.c [new file with mode: 0644]

index 4960a361059022678b8bcde4f6b9172ed347d697..10cff2dbd12855703ef3d1b33c59cc28391c0ff2 100644 (file)
 #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
index 2237a5416c51ccee0baa31e1e067c92c2841604f..2c8c74cdcc33c1034b59e3cfe8937ee99d2c9811 100644 (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 */
 };
 
index 87902b148f40f8da1df7a142ff3f21d5871483a2..822c3065714ad33f285bec28a2e007d7fbbbf79e 100644 (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)              );
index a35894d169b127a0a1f050e0bba6771c33881c21..69fe5089399b72299851c0136483ed5c4275d381 100644 (file)
@@ -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 (file)
index 0000000..bb40379
--- /dev/null
@@ -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);
+}
index 4472f283068d7bb52e9b2f2f595a91fa7ba6ccf3..499331f150d740bd28f1523c2bd15e858fa27236 100644 (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);
 }
index eb91fc96b20e61ff69126d191fc3b36347df507d..45a1b932182f820bb1558f1578a53a765469c582 100644 (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);
 }
index 32114d6abdac915ac6a188392fe4c88f8e232af2..ddf6e5a5fbb1bc0f29127b73ff843bec67a27cb2 100644 (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 */
index 6b781419cf4a8275376d3f72db6b1ab9f4e874ea..46f133eda0127d596d9d7cfb9b10e4c0532d63df 100644 (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';
index 9b1124ad9d437b6ce6b3ee0c59025e9b18737397..04886dd56b94fc736ee63ec8ebd954489dbe9b26 100644 (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;
index a2e3e7f3a597203f461c6536e481752dfe22bb0b..3a1f247636a856e87fcceaf219acb887ff2fceda 100644 (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;  
index e841f0152f778b8e21835df7a6e5244766e4d90c..80afb4eba537215becf74039c9d69367422e5c48 100644 (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
index 364fbb0394df0919a60be5f79771ae8191440830..cd8c6d88ca90a9b780ec9ef91f70cff15c616d22 100644 (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 */
index 00fe3ee729cefaf089e9f7e38b03421109f4d71e..d05fc3b37092658e3c8f22ca2d3ae468a041d886 100644 (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);
 }
index 32114d6abdac915ac6a188392fe4c88f8e232af2..ddf6e5a5fbb1bc0f29127b73ff843bec67a27cb2 100644 (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 */
index 1721030b2efd10f24f6c67ab59ca6e4e87ff1497..0e02e9bf0dc6f8ae84a8d18d629bcdd8c963d886 100644 (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
index 332336eb7a31e49661b18ab31a9e472c60bf3253..fdf893be4cce2606945b017257ef90a65ed6195c 100755 (executable)
--- 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 (file)
index 0000000..812996b
--- /dev/null
@@ -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());
+}
diff --git a/test/t60b.c b/test/t60b.c
new file mode 100644 (file)
index 0000000..637517f
--- /dev/null
@@ -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));
+}
+
diff --git a/test/test60.c b/test/test60.c
new file mode 100644 (file)
index 0000000..3375a60
--- /dev/null
@@ -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 */
+}