]> granicus.if.org Git - sudo/commitdiff
Convert perm setting to push/pop model; still needs some work
authorTodd C. Miller <Todd.Miller@courtesan.com>
Tue, 20 Apr 2010 21:00:31 +0000 (17:00 -0400)
committerTodd C. Miller <Todd.Miller@courtesan.com>
Tue, 20 Apr 2010 21:00:31 +0000 (17:00 -0400)
Use the stashed runas groups instead of using getgrouplist()
Reset perms to the initial value on error

plugins/sudoers/auth/sudo_auth.c
plugins/sudoers/set_perms.c
plugins/sudoers/sudoers.c
plugins/sudoers/sudoers.h
plugins/sudoers/testsudoers.c

index 94ca3bb351f9bee1795f4bcff6012268a28e0cd8..3a7f8372574c4280d179c84eb816f2ff6c026546 100644 (file)
@@ -136,7 +136,7 @@ verify_user(struct passwd *pw, char *prompt)
            }
 
            if (NEEDS_USER(auth))
-               set_perms(PERM_ROOT);
+               restore_perms();
        }
     }
 
@@ -158,7 +158,7 @@ verify_user(struct passwd *pw, char *prompt)
                }
 
                if (NEEDS_USER(auth))
-                   set_perms(PERM_ROOT);
+                   restore_perms();
            }
        }
 
@@ -180,7 +180,7 @@ verify_user(struct passwd *pw, char *prompt)
            success = auth->status = (auth->verify)(pw, (char *)p, auth);
 
            if (NEEDS_USER(auth))
-               set_perms(PERM_ROOT);
+               restore_perms();
 
            if (auth->status != AUTH_FAILURE)
                goto cleanup;
@@ -210,7 +210,7 @@ cleanup:
            }
 
            if (NEEDS_USER(auth))
-               set_perms(PERM_ROOT);
+               restore_perms();
        }
     }
 
index e634895a4ef956957cc5a94e2753714b9d96ebf0..601e0c10517211e570c58089753f9b553a0f4fae 100644 (file)
 static void runas_setup(void);
 #endif
 static void runas_setgroups(void);
-static void restore_groups(void);
 
 /*
  * We keep track of the current permisstions and use a stack to restore
  * the old permissions.  A depth of 16 is overkill.
  */
+struct perm_state {
+    uid_t ruid;
+    uid_t euid;
+#ifdef HAVE_SETRESUID
+    uid_t suid;
+#endif
+    gid_t rgid;
+    gid_t egid;
+#ifdef HAVE_SETRESUID
+    gid_t sgid;
+#endif
+    GETGROUPS_T *groups;
+    int ngroups;
+};
+
 #define PERM_STACK_MAX 16
-static int perm_stack[PERM_STACK_MAX];
+static struct perm_state perm_stack[PERM_STACK_MAX];
 static int perm_stack_depth = 0;
-static int perm_current = PERM_INITIAL;
 
-/*
- * XXX - better to push what we've changed:
- *     ruid, euid, suid, gids group vector.
- */
+/* XXX - make a runas_user struct? */
+int runas_ngroups = -1;
+#ifdef HAVE_GETGROUPS
+GETGROUPS_T *runas_groups;
+#endif
 
-int
-restore_perms(void)
-{
-    int old_perm;
+#undef ID
+#define ID(x) (state->x == ostate->x ? -1 : state->x)
+#undef OID
+#define OID(x) (ostate->x == state->x ? -1 : ostate->x)
 
-    if (!perm_stack_depth) {
-       /* nothing to do */
-       return TRUE;
-    }
-    old_perm = perm_stack[--perm_stack_depth];
-    return set_perms2(old_perm, FALSE);
-}
-
-int
-set_perms(int perm)
+void
+rewind_perms(void)
 {
-    return set_perms2(perm, TRUE);
+    while (perm_stack_depth > 1)
+       restore_perms();
 }
 
 #ifdef HAVE_SETRESUID
+
 /*
  * Set real and effective and saved uids and gids based on perm.
  * We always retain a saved uid of 0 unless we are headed for an exec().
@@ -107,8 +115,9 @@ set_perms(int perm)
  * This version of set_perms() works fine with the "stay_setuid" option.
  */
 int
-set_perms2(int perm, int push_it)
+set_perms(int perm)
 {
+    struct perm_state *state, *ostate = NULL;
     const char *errstr;
     int noexit;
 
@@ -116,113 +125,171 @@ set_perms2(int perm, int push_it)
     CLR(perm, PERM_MASK);
 
     if (perm_stack_depth == PERM_STACK_MAX) {
+       errstr = "perm stack overflow";
        errno = EINVAL;
        goto bad;
     }
-    if (perm == perm_current)
-       goto done;
+
+    state = &perm_stack[perm_stack_depth];
+    if (perm_stack_depth)
+       ostate = &perm_stack[perm_stack_depth - 1];
+
+    if (perm != PERM_INITIAL && memcmp(state, ostate, sizeof(*state)) == 0)
+       goto done;
 
     switch (perm) {
-       case PERM_INITIAL:
-                               /* Setuid root */
-                               if (setuid(ROOT_UID)) {
-                                   errstr = "setuid(ROOT_UID)";
-                                   goto bad;
-                               }
-                               (void) setresgid(-1, user_gid, -1);
-                               if (perm_current == PERM_RUNAS)
-                                   restore_groups();
-                               (void) setresuid(user_uid, -1, -1);
-                               break;
-       case PERM_ROOT:
-                               if (setresuid(ROOT_UID, ROOT_UID, ROOT_UID)) {
-                                   errstr = "setresuid(ROOT_UID, ROOT_UID, ROOT_UID)";
-                                   goto bad;
-                               }
-                               (void) setresgid(-1, user_gid, -1);
-                               if (perm_current == PERM_RUNAS)
-                                   restore_groups();
-                               break;
-
-       case PERM_USER:
-                               (void) setresgid(-1, user_gid, -1);
-                               if (setresuid(user_uid, user_uid, ROOT_UID)) {
-                                   errstr = "setresuid(user_uid, user_uid, ROOT_UID)";
-                                   goto bad;
-                               }
-                               break;
-                               
-       case PERM_FULL_USER:
-                               /* headed for exec() */
-                               (void) setgid(user_gid);
-                               if (setresuid(user_uid, user_uid, user_uid)) {
-                                   errstr = "setresuid(user_uid, user_uid, user_uid)";
-                                   goto bad;
-                               }
-                               break;
-                               
-       case PERM_RUNAS:
-                               runas_setgroups();
-                               (void) setresgid(-1, runas_gr ?
-                                   runas_gr->gr_gid : runas_pw->pw_gid, -1);
-                               if (setresuid(-1, runas_pw ? runas_pw->pw_uid :
-                                   user_uid, -1)) {
-                                   errstr = "unable to change to runas uid";
-                                   goto bad;
-                               }
-                               break;
+    case PERM_INITIAL:
+       /* Stash initial state */
+       if (getresuid(&state->ruid, &state->euid, &state->suid)) {
+           errstr = "getresuid";
+           goto bad;
 
-#if 0
-       case PERM_FULL_RUNAS:
-                               /* headed for exec(), assume euid == ROOT_UID */
-                               runas_setup();
-                               if (setresuid(def_stay_setuid ?
-                                   user_uid : runas_pw->pw_uid,
-                                   runas_pw->pw_uid, runas_pw->pw_uid)) {
-                                   errstr = "unable to change to runas uid";
-                                   goto bad;
-                               }
-                               break;
-#endif
+       }
+       if (getresgid(&state->rgid, &state->egid, &state->sgid)) {
+           errstr = "getresgid";
+           goto bad;
+
+       }
+       state->groups = user_groups;
+       state->ngroups = user_ngroups;
+       break;
+
+    case PERM_ROOT:
+       state->ruid = ROOT_UID;
+       state->euid = ROOT_UID;
+       state->suid = ROOT_UID;
+       if (setresuid(ID(ruid), ID(euid), ID(suid))) {
+           errstr = "setresuid(ROOT_UID, ROOT_UID, ROOT_UID)";
+           goto bad;
+       }
+       state->rgid = -1;
+       state->egid = -1;
+       state->sgid = -1;
+       state->groups = NULL;
+       state->ngroups = -1;
+       break;
+
+    case PERM_USER:
+       state->groups = user_groups;
+       state->ngroups = user_ngroups;
+       if (state->ngroups != -1 && state->groups != ostate->groups) {
+           if (setgroups(state->ngroups, state->groups)) {
+               errstr = "setgroups()";
+               goto bad;
+           }
+       }
+       state->rgid = -1;
+       state->egid = user_gid;
+       state->sgid = -1;
+       if (setresgid(-1, ID(egid), -1)) {
+           errstr = "setresgid(-1, user_gid, -1)";
+           goto bad;
+       }
+       state->ruid = user_uid;
+       state->euid = user_uid;
+       state->suid = ROOT_UID;
+       if (setresuid(ID(ruid), ID(euid), ID(suid))) {
+           errstr = "setresuid(user_uid, user_uid, ROOT_UID)";
+           goto bad;
+       }
+       break;
+
+    case PERM_FULL_USER:
+       /* headed for exec() */
+       state->groups = user_groups;
+       state->ngroups = user_ngroups;
+       if (state->ngroups != -1 && state->groups != ostate->groups) {
+           if (setgroups(state->ngroups, state->groups)) {
+               errstr = "setgroups()";
+               goto bad;
+           }
+       }
+       state->rgid = user_gid;
+       state->egid = user_gid;
+       state->sgid = user_gid;
+       if (setresgid(ID(rgid), ID(egid), ID(sgid))) {
+           errstr = "setresgid(user_gid, user_gid, user_gid)";
+           goto bad;
+       }
+       state->ruid = user_uid;
+       state->euid = user_uid;
+       state->suid = user_uid;
+       if (setresuid(ID(ruid), ID(euid), ID(suid))) {
+           errstr = "setresuid(user_uid, user_uid, user_uid)";
+           goto bad;
+       }
+       break;
+
+    case PERM_RUNAS:
+       runas_setgroups();
+       state->groups = runas_groups;
+       state->ngroups = runas_ngroups;
+
+       state->rgid = -1;
+       state->egid = runas_gr ? runas_gr->gr_gid : runas_pw->pw_gid;
+       state->sgid = -1;
+       if (setresgid(-1, ID(egid), -1)) {
+           errstr = "unable to change to runas gid";
+           goto bad;
+       }
+       state->ruid = -1;
+       state->euid = runas_pw ? runas_pw->pw_uid : user_uid;
+       state->suid = -1;
+       if (setresuid(-1, ID(euid), -1)) {
+           errstr = "unable to change to runas uid";
+           goto bad;
+       }
+       break;
+
+    case PERM_SUDOERS:
+       state->groups = NULL;
+       state->ngroups = -1;
 
-       case PERM_SUDOERS:
-                               /* assume euid == ROOT_UID, ruid == user */
-                               if (setresgid(-1, SUDOERS_GID, -1))
-                                   error(1, "unable to change to sudoers gid");
-
-                               /*
-                                * If SUDOERS_UID == ROOT_UID and SUDOERS_MODE
-                                * is group readable we use a non-zero
-                                * uid in order to avoid NFS lossage.
-                                * Using uid 1 is a bit bogus but should
-                                * work on all OS's.
-                                */
-                               if (SUDOERS_UID == ROOT_UID) {
-                                   if ((SUDOERS_MODE & 040) && setresuid(ROOT_UID, 1, ROOT_UID)) {
-                                       errstr = "setresuid(ROOT_UID, 1, ROOT_UID)";
-                                       goto bad;
-                                   }
-                               } else {
-                                   if (setresuid(ROOT_UID, SUDOERS_UID, ROOT_UID)) {
-                                       errstr = "setresuid(ROOT_UID, SUDOERS_UID, ROOT_UID)";
-                                       goto bad;
-                                   }
-                               }
-                               break;
-       case PERM_TIMESTAMP:
-                               if (setresuid(ROOT_UID, timestamp_uid, ROOT_UID)) {
-                                   errstr = "setresuid(ROOT_UID, timestamp_uid, ROOT_UID)";
-                                   goto bad;
-                               }
-                               break;
+       /* assumes euid == ROOT_UID, ruid == user */
+       state->rgid = -1;
+       state->egid = SUDOERS_GID;
+       state->sgid = -1;
+       if (setresgid(-1, ID(egid), -1))
+           error(1, "unable to change to sudoers gid");
+
+       state->ruid = ROOT_UID;
+       /*
+        * If SUDOERS_UID == ROOT_UID and SUDOERS_MODE is group readable
+        * we use a non-zero uid in order to avoid NFS lossage.
+        * Using uid 1 is a bit bogus but should work on all OS's.
+        */
+       if (SUDOERS_UID == ROOT_UID && (SUDOERS_MODE & 040))
+           state->euid = 1;
+       else
+           state->euid = SUDOERS_UID;
+       state->suid = ROOT_UID;
+       if (setresuid(ID(ruid), ID(euid), ID(suid))) {
+           errstr = "setresuid(ROOT_UID, SUDOERS_UID, ROOT_UID)";
+           goto bad;
+       }
+       break;
+
+    case PERM_TIMESTAMP:
+       state->groups = NULL;
+       state->ngroups = -1;
+       state->rgid = -1;
+       state->egid = -1;
+       state->sgid = -1;
+       state->ruid = ROOT_UID;
+       state->euid = timestamp_uid;
+       state->suid = ROOT_UID;
+       if (setresuid(ID(ruid), ID(euid), ID(suid))) {
+           errstr = "setresuid(ROOT_UID, timestamp_uid, ROOT_UID)";
+           goto bad;
+       }
+       break;
     }
 
 done:
-    if (push_it)
-       perm_stack[perm_stack_depth++] = perm_current;
-    perm_current = perm;
-    return(1);
+    perm_stack_depth++;
+    return 1;
 bad:
+    /* XXX - better warnings inline */
     warningx("%s: %s", errstr,
        errno == EAGAIN ? "too many processes" : strerror(errno));
     if (noexit)
@@ -230,6 +297,48 @@ bad:
     exit(1);
 }
 
+void
+restore_perms(void)
+{
+    struct perm_state *state, *ostate;
+
+    if (perm_stack_depth < 2)
+       return;
+
+    state = &perm_stack[perm_stack_depth - 1];
+    ostate = &perm_stack[perm_stack_depth - 2];
+    perm_stack_depth--;
+
+    /* XXX - more cases here where euid != ruid */
+    if (OID(euid) == ROOT_UID && state->euid != ROOT_UID) {
+       if (setresuid(-1, ROOT_UID, -1)) {
+           warning("setresuid() [%d, %d, %d] -> [%d, %d, %d]", state->ruid,
+               state->euid, state->suid, -1, ROOT_UID, -1);
+           goto bad;
+       }
+    }
+    if (setresuid(OID(ruid), OID(euid), OID(suid))) {
+       warning("setresuid() [%d, %d, %d] -> [%d, %d, %d]", state->ruid,
+           state->euid, state->suid, OID(ruid), OID(euid), OID(suid));
+       goto bad;
+    }
+    if (setresgid(OID(rgid), OID(egid), OID(sgid))) {
+       warning("setresgid() [%d, %d, %d] -> [%d, %d, %d]", state->rgid,
+           state->egid, state->sgid, OID(rgid), OID(egid), OID(sgid));
+       goto bad;
+    }
+    if (state->ngroups != -1 && state->groups != ostate->groups) {
+       if (setgroups(ostate->ngroups, ostate->groups)) {
+           warning("setgroups()");
+           goto bad;
+       }
+    }
+    return;
+
+bad:
+    exit(1);
+}
+
 #else
 # ifdef HAVE_SETREUID
 
@@ -240,8 +349,9 @@ bad:
  * This version of set_perms() works fine with the "stay_setuid" option.
  */
 int
-set_perms2(int perm, int push_it)
+set_perms(int perm)
 {
+    struct perm_state *state, *ostate = NULL;
     const char *errstr;
     int noexit;
 
@@ -249,105 +359,162 @@ set_perms2(int perm, int push_it)
     CLR(perm, PERM_MASK);
 
     if (perm_stack_depth == PERM_STACK_MAX) {
+       errstr = "perm stack overflow";
        errno = EINVAL;
        goto bad;
     }
-    if (perm == perm_current)
+
+    state = &perm_stack[perm_stack_depth];
+    if (perm_stack_depth)
+       ostate = &perm_stack[perm_stack_depth - 1];
+
+    if (perm != PERM_INITIAL && memcmp(state, ostate, sizeof(*state)) == 0)
        goto done;
 
     switch (perm) {
-       case PERM_ROOT:
-                               if (setreuid(-1, ROOT_UID)) {
-                                   errstr = "setreuid(-1, ROOT_UID)";
-                                   goto bad;
-                               }
-                               if (setuid(ROOT_UID)) {
-                                   errstr = "setuid(ROOT_UID)";
-                                   goto bad;
-                               }
-                               (void) setregid(-1, user_gid);
-                               if (perm_current == PERM_RUNAS)
-                                   restore_groups();
-                               break;
-
-       case PERM_USER:
-                               (void) setregid(-1, user_gid);
-                               if (setreuid(ROOT_UID, user_uid)) {
-                                   errstr = "setreuid(ROOT_UID, user_uid)";
-                                   goto bad;
-                               }
-                               break;
-                               
-       case PERM_FULL_USER:
-                               /* headed for exec() */
-                               (void) setgid(user_gid);
-                               if (setreuid(user_uid, user_uid)) {
-                                   errstr = "setreuid(user_uid, user_uid)";
-                                   goto bad;
-                               }
-                               break;
-                               
-       case PERM_RUNAS:
-                               runas_setgroups();
-                               (void) setregid(-1, runas_gr ?
-                                   runas_gr->gr_gid : runas_pw->pw_gid);
-                               if (setreuid(-1,
-                                   runas_pw ? runas_pw->pw_uid : user_uid)) {
-                                   errstr = "unable to change to runas uid";
-                                   goto bad;
-                               }
-                               break;
+    case PERM_INITIAL:
+       /* Stash initial state */
+       state->ruid = getuid();
+       state->euid = geteuid();
+       state->rgid = getgid();
+       state->egid = getegid();
+       state->groups = user_groups;
+       state->ngroups = user_ngroups;
+       break;
+
+    case PERM_ROOT:
+       /*
+        * setreuid(0, 0) may fail on some systems
+        * when the euid is not already 0.
+        */
+       state->ruid = -1;
+       state->euid = ROOT_UID;
+       if (setreuid(ID(ruid), ID(euid))) {
+           errstr = "setreuid(-1, ROOT_UID)";
+           goto bad;
+       }
+       if (setuid(ROOT_UID)) {
+           errstr = "setuid(ROOT_UID)";
+           goto bad;
+       }
+       state->ruid = ROOT_UID;
+       state->rgid = -1;
+       state->egid = -1;
+       state->groups = NULL;
+       state->ngroups = -1;
+       break;
+
+    case PERM_USER:
+       state->groups = user_groups;
+       state->ngroups = user_ngroups;
+       if (state->ngroups != -1 && state->groups != ostate->groups) {
+           if (setgroups(state->ngroups, state->groups)) {
+               errstr = "setgroups()";
+               goto bad;
+           }
+       }
+       state->rgid = -1;
+       state->egid = user_gid;
+       if (setregid(-1, ID(egid))) {
+           errstr = "setregid(-1, user_gid)";
+           goto bad;
+       }
+       state->ruid = ROOT_UID;
+       state->euid = user_uid;
+       if (setreuid(ID(ruid), ID(euid))) {
+           errstr = "setreuid(ROOT_UID, user_uid)";
+           goto bad;
+       }
+       break;
+
+    case PERM_FULL_USER:
+       /* headed for exec() */
+       state->groups = user_groups;
+       state->ngroups = user_ngroups;
+       if (state->ngroups != -1 && state->groups != ostate->groups) {
+           if (setgroups(state->ngroups, state->groups)) {
+               errstr = "setgroups()";
+               goto bad;
+           }
+       }
+       state->rgid = user_gid;
+       state->egid = user_gid;
+       if (setregid(ID(rgid), ID(egid))) {
+           errstr = "setregid(user_gid, user_gid)";
+           goto bad;
+       }
+       state->ruid = user_uid;
+       state->euid = user_uid;
+       if (setreuid(ID(ruid), ID(euid))) {
+           errstr = "setreuid(user_uid, user_uid)";
+           goto bad;
+       }
+       break;
 
-#if 0
-       case PERM_FULL_RUNAS:
-                               /* headed for exec(), assume euid == ROOT_UID */
-                               runas_setup();
-                               if (setreuid(def_stay_setuid ? user_uid :
-                                   runas_pw->pw_uid, runas_pw->pw_uid)) {
-                                   errstr = "unable to change to runas uid";
-                                   goto bad;
-                               }
-                               break;
-#endif
+    case PERM_RUNAS:
+       runas_setgroups();
+       state->groups = runas_groups;
+       state->ngroups = runas_ngroups;
+
+       state->rgid = -1;
+       state->egid = runas_gr ? runas_gr->gr_gid : runas_pw->pw_gid;
+       if (setregid(-1, ID(egid))) {
+           errstr = "unable to change to runas gid";
+           goto bad;
+       }
+       state->ruid = -1;
+       state->euid = runas_pw ? runas_pw->pw_uid : user_uid;
+       if (setreuid(-1, ID(euid))) {
+           errstr = "unable to change to runas uid";
+           goto bad;
+       }
+       break;
 
-       case PERM_SUDOERS:
-                               /* assume euid == ROOT_UID, ruid == user */
-                               if (setregid(-1, SUDOERS_GID))
-                                   error(1, "unable to change to sudoers gid");
-
-                               /*
-                                * If SUDOERS_UID == ROOT_UID and SUDOERS_MODE
-                                * is group readable we use a non-zero
-                                * uid in order to avoid NFS lossage.
-                                * Using uid 1 is a bit bogus but should
-                                * work on all OS's.
-                                */
-                               if (SUDOERS_UID == ROOT_UID) {
-                                   if ((SUDOERS_MODE & 040) && setreuid(ROOT_UID, 1)) {
-                                       errstr = "setreuid(ROOT_UID, 1)";
-                                       goto bad;
-                                   }
-                               } else {
-                                   if (setreuid(ROOT_UID, SUDOERS_UID)) {
-                                       errstr = "setreuid(ROOT_UID, SUDOERS_UID)";
-                                       goto bad;
-                                   }
-                               }
-                               break;
-       case PERM_TIMESTAMP:
-                               if (setreuid(ROOT_UID, timestamp_uid)) {
-                                   errstr = "setreuid(ROOT_UID, timestamp_uid)";
-                                   goto bad;
-                               }
-                               break;
+    case PERM_SUDOERS:
+       state->groups = NULL;
+       state->ngroups = -1;
+
+       /* assume euid == ROOT_UID, ruid == user */
+       state->rgid = -1;
+       state->egid = SUDOERS_GID;
+       if (setregid(-1, ID(egid)))
+           error(1, "unable to change to sudoers gid");
+
+       state->ruid = ROOT_UID;
+       /*
+        * If SUDOERS_UID == ROOT_UID and SUDOERS_MODE is group readable
+        * we use a non-zero uid in order to avoid NFS lossage.
+        * Using uid 1 is a bit bogus but should work on all OS's.
+        */
+       if (SUDOERS_UID == ROOT_UID && (SUDOERS_MODE & 040))
+           state->euid = 1;
+       else
+           state->euid = SUDOERS_UID;
+       if (setreuid(ID(ruid), ID(euid))) {
+           errstr = "setreuid(ROOT_UID, SUDOERS_UID)";
+           goto bad;
+       }
+       break;
+
+    case PERM_TIMESTAMP:
+       state->groups = NULL;
+       state->ngroups = -1;
+       state->rgid = -1;
+       state->egid = -1;
+       state->ruid = ROOT_UID;
+       state->euid = timestamp_uid;
+       if (setreuid(ID(ruid), ID(euid))) {
+           errstr = "setreuid(ROOT_UID, timestamp_uid)";
+           goto bad;
+       }
+       break;
     }
 
 done:
-    if (push_it)
-       perm_stack[perm_stack_depth++] = perm_current;
-    perm_current = perm;
-    return(1);
+    perm_stack_depth++;
+    return 1;
 bad:
+    /* XXX - better warnings inline */
     warningx("%s: %s", errstr,
        errno == EAGAIN ? "too many processes" : strerror(errno));
     if (noexit)
@@ -355,16 +522,63 @@ bad:
     exit(1);
 }
 
+void
+restore_perms(void)
+{
+    struct perm_state *state, *ostate;
+
+    if (perm_stack_depth < 2)
+       return;
+
+    state = &perm_stack[perm_stack_depth - 1];
+    ostate = &perm_stack[perm_stack_depth - 2];
+    perm_stack_depth--;
+
+    /*
+     * When changing euid to ROOT_UID, setreuid() may fail even if
+     * the ruid is ROOT_UID so call setuid() first.
+     */
+    if (OID(euid) == ROOT_UID) {
+       if (setuid(ROOT_UID)) {
+           warning("setuid()");
+           goto bad;
+       }
+    }
+    if (setreuid(OID(ruid), OID(euid))) {
+       warning("setreuid() [%d, %d] -> [%d, %d]", state->ruid,
+           state->euid, OID(ruid), OID(euid));
+       goto bad;
+    }
+    if (setregid(OID(rgid), OID(egid))) {
+       warning("setregid() [%d, %d] -> [%d, %d]", state->rgid,
+           state->egid, OID(rgid), OID(egid));
+       goto bad;
+    }
+    if (state->ngroups != -1 && state->groups != ostate->groups) {
+       if (setgroups(ostate->ngroups, ostate->groups)) {
+           warning("setgroups()");
+           goto bad;
+       }
+    }
+    return;
+
+bad:
+    exit(1);
+}
+
 # else /* !HAVE_SETRESUID && !HAVE_SETREUID */
 # ifdef HAVE_SETEUID
 
 /*
  * Set real and effective uids and gids based on perm.
- * NOTE: does not support the "stay_setuid" option.
+ * We always retain a real or effective uid of ROOT_UID unless
+ * we are headed for an exec().
+ * This version of set_perms() works fine with the "stay_setuid" option.
  */
 int
-set_perms2(int perm, int push_it)
+set_perms(int perm)
 {
+    struct perm_state *state, *ostate = NULL;
     const char *errstr;
     int noexit;
 
@@ -372,10 +586,16 @@ set_perms2(int perm, int push_it)
     CLR(perm, PERM_MASK);
 
     if (perm_stack_depth == PERM_STACK_MAX) {
+       errstr = "perm stack overflow";
        errno = EINVAL;
        goto bad;
     }
-    if (perm == perm_current)
+
+    state = &perm_stack[perm_stack_depth];
+    if (perm_stack_depth)
+       ostate = &perm_stack[perm_stack_depth - 1];
+
+    if (perm != PERM_INITIAL && memcmp(state, ostate, sizeof(*state)) == 0)
        goto done;
 
     /*
@@ -383,98 +603,149 @@ set_perms2(int perm, int push_it)
      * for these calls differ on various systems, we set
      * real and effective uids to ROOT_UID initially to be safe.
      */
-    if (seteuid(ROOT_UID)) {
-       errstr = "seteuid(ROOT_UID)";
-       goto bad;
-    }
-    if (setuid(ROOT_UID)) {
-       errstr = "setuid(ROOT_UID)";
-       goto bad;
+    if (perm != PERM_INITIAL) {
+       if (seteuid(ROOT_UID)) {
+           errstr = "seteuid(ROOT_UID)";
+           goto bad;
+       }
+       if (setuid(ROOT_UID)) {
+           errstr = "setuid(ROOT_UID)";
+           goto bad;
+       }
     }
 
     switch (perm) {
-       case PERM_ROOT:
-                               /* uid set above */
-                               (void) setegid(user_gid);
-                               if (perm_current == PERM_RUNAS)
-                                   restore_groups();
-                               break;
-
-       case PERM_USER:
-                               (void) setegid(user_gid);
-                               if (seteuid(user_uid)) {
-                                   errstr = "seteuid(user_uid)";
-                                   goto bad;
-                               }
-                               break;
-                               
-       case PERM_FULL_USER:
-                               /* headed for exec() */
-                               (void) setgid(user_gid);
-                               if (setuid(user_uid)) {
-                                   errstr = "setuid(user_uid)";
-                                   goto bad;
-                               }
-                               break;
-                               
-       case PERM_RUNAS:
-                               runas_setgroups();
-                               (void) setegid(runas_gr ?
-                                   runas_gr->gr_gid : runas_pw->pw_gid);
-                               if (seteuid(runas_pw ? runas_pw->pw_uid : user_uid)) {
-                                   errstr = "unable to change to runas uid";
-                                   goto bad;
-                               }
-                               break;
+    case PERM_INITIAL:
+       /* Stash initial state */
+       state->ruid = getuid();
+       state->euid = geteuid();
+       state->rgid = getgid();
+       state->egid = getegid();
+       state->groups = user_groups;
+       state->ngroups = user_ngroups;
+       break;
+
+    case PERM_ROOT:
+       /* We already set ruid/euid above. */
+       state->ruid = ROOT_UID;
+       state->euid = ROOT_UID;
+       state->rgid = -1;
+       state->egid = -1;
+       state->groups = NULL;
+       state->ngroups = -1;
+       break;
+
+    case PERM_USER:
+       state->groups = user_groups;
+       state->ngroups = user_ngroups;
+       if (state->ngroups != -1 && state->groups != ostate->groups) {
+           if (setgroups(state->ngroups, state->groups)) {
+               errstr = "setgroups()";
+               goto bad;
+           }
+       }
+       state->rgid = -1;
+       state->egid = user_gid;
+       if (setegid(ID(egid))) {
+           errstr = "setegid(user_gid)";
+           goto bad;
+       }
+       state->ruid = ROOT_UID;
+       state->euid = user_uid;
+       if (seteuid(ID(euid))) {
+           errstr = "seteuid(user_uid)";
+           goto bad;
+       }
+       break;
+
+    case PERM_FULL_USER:
+       /* headed for exec() */
+       state->groups = user_groups;
+       state->ngroups = user_ngroups;
+       if (state->ngroups != -1 && state->groups != ostate->groups) {
+           if (setgroups(state->ngroups, state->groups)) {
+               errstr = "setgroups()";
+               goto bad;
+           }
+       }
+       state->rgid = user_gid;
+       state->egid = user_gid;
+       if (setgid(user_gid)) {
+           errstr = "setgid(user_gid)";
+           goto bad;
+       }
+       state->ruid = user_uid;
+       state->euid = user_uid;
+       if (setuid(user_uid)) {
+           errstr = "setuid(user_uid)";
+           goto bad;
+       }
+       break;
 
-#if 0
-       case PERM_FULL_RUNAS:
-                               /* headed for exec() */
-                               runas_setup();
-                               if (setuid(runas_pw->pw_uid)) {
-                                   errstr = "unable to change to runas uid";
-                                   goto bad;
-                               }
-                               break;
-#endif
+    case PERM_RUNAS:
+       runas_setgroups();
+       state->groups = runas_groups;
+       state->ngroups = runas_ngroups;
+
+       state->rgid = -1;
+       state->egid = runas_gr ? runas_gr->gr_gid : runas_pw->pw_gid;
+       if (setegid(ID(egid))) {
+           errstr = "unable to change to runas gid";
+           goto bad;
+       }
+       state->ruid = -1;
+       state->euid = runas_pw ? runas_pw->pw_uid : user_uid;
+       if (seteuid(ID(euid))) {
+           errstr = "unable to change to runas uid";
+           goto bad;
+       }
+       break;
+
+    case PERM_SUDOERS:
+       state->groups = NULL;
+       state->ngroups = -1;
+
+       /* assume euid == ROOT_UID, ruid == user */
+       state->rgid = -1;
+       state->egid = SUDOERS_GID;
+       if (setegid(ID(egid)))
+           error(1, "unable to change to sudoers gid");
 
-       case PERM_SUDOERS:
-                               if (setegid(SUDOERS_GID))
-                                   error(1, "unable to change to sudoers gid");
-
-                               /*
-                                * If SUDOERS_UID == ROOT_UID and SUDOERS_MODE
-                                * is group readable we use a non-zero
-                                * uid in order to avoid NFS lossage.
-                                * Using uid 1 is a bit bogus but should
-                                * work on all OS's.
-                                */
-                               if (SUDOERS_UID == ROOT_UID) {
-                                   if ((SUDOERS_MODE & 040) && seteuid(1)) {
-                                       errstr = "seteuid(1)";
-                                       goto bad;
-                                   }
-                               } else {
-                                   if (seteuid(SUDOERS_UID)) {
-                                       errstr = "seteuid(SUDOERS_UID)";
-                                       goto bad;
-                                   }
-                               }
-                               break;
-       case PERM_TIMESTAMP:
-                               if (seteuid(timestamp_uid)) {
-                                   errstr = "seteuid(timestamp_uid)";
-                                   goto bad;
-                               }
-                               break;
+       state->ruid = ROOT_UID;
+       /*
+        * If SUDOERS_UID == ROOT_UID and SUDOERS_MODE is group readable
+        * we use a non-zero uid in order to avoid NFS lossage.
+        * Using uid 1 is a bit bogus but should work on all OS's.
+        */
+       if (SUDOERS_UID == ROOT_UID && (SUDOERS_MODE & 040))
+           state->euid = 1;
+       else
+           state->euid = SUDOERS_UID;
+       if (seteuid(ID(euid))) {
+           errstr = "seteuid(SUDOERS_UID)";
+           goto bad;
+       }
+       break;
+
+    case PERM_TIMESTAMP:
+       state->groups = NULL;
+       state->ngroups = -1;
+       state->rgid = -1;
+       state->egid = -1;
+       state->ruid = ROOT_UID;
+       state->euid = timestamp_uid;
+       if (seteuid(ID(euid))) {
+           errstr = "seteuid(timestamp_uid)";
+           goto bad;
+       }
+       break;
     }
 
 done:
-    if (push_it)
-       perm_stack[perm_stack_depth++] = perm_current;
-    perm_current = perm;
-    return(1);
+    perm_stack_depth++;
+    return 1;
 bad:
+    /* XXX - better warnings inline */
     warningx("%s: %s", errstr,
        errno == EAGAIN ? "too many processes" : strerror(errno));
     if (noexit)
@@ -482,6 +753,52 @@ bad:
     exit(1);
 }
 
+void
+restore_perms(void)
+{
+    struct perm_state *state, *ostate;
+
+    if (perm_stack_depth < 2)
+       return;
+
+    state = &perm_stack[perm_stack_depth - 1];
+    ostate = &perm_stack[perm_stack_depth - 2];
+    perm_stack_depth--;
+
+    /*
+     * Since we only have setuid() and seteuid() and semantics
+     * for these calls differ on various systems, we set
+     * real and effective uids to ROOT_UID initially to be safe.
+     */
+    if (seteuid(ROOT_UID)) {
+       errstr = "seteuid(ROOT_UID)";
+       goto bad;
+    }
+    if (setuid(ROOT_UID)) {
+       errstr = "setuid(ROOT_UID)";
+       goto bad;
+    }
+
+    if (setegid(OID(egid))) {
+       warning("setegid(%d)", OID(egid));
+       goto bad;
+    }
+    if (state->ngroups != -1 && state->groups != ostate->groups) {
+       if (setgroups(ostate->ngroups, ostate->groups)) {
+           warning("setgroups()");
+           goto bad;
+       }
+    }
+    if (seteuid(OID(euid))) {
+       warning("seteuid(%d)", OID(euid));
+       goto bad;
+    }
+    return;
+
+bad:
+    exit(1);
+}
+
 # else /* !HAVE_SETRESUID && !HAVE_SETREUID && !HAVE_SETEUID */
 
 /*
@@ -490,8 +807,9 @@ bad:
  *       Also, SUDOERS_UID and SUDOERS_GID are not used.
  */
 int
-set_perms2(int perm, int push_it)
+set_perms(int perm)
 {
+    struct perm_state *state, *ostate = NULL;
     const char *errstr;
     int noexit;
 
@@ -499,60 +817,107 @@ set_perms2(int perm, int push_it)
     CLR(perm, PERM_MASK);
 
     if (perm_stack_depth == PERM_STACK_MAX) {
+       errstr = "perm stack overflow";
        errno = EINVAL;
        goto bad;
     }
-    if (perm == perm_current)
+
+    state = &perm_stack[perm_stack_depth];
+    if (perm_stack_depth)
+       ostate = &perm_stack[perm_stack_depth - 1];
+
+    if (perm != PERM_INITIAL && memcmp(state, ostate, sizeof(*state)) == 0)
        goto done;
 
     switch (perm) {
-       case PERM_ROOT:
-                               if (setuid(ROOT_UID)) {
-                                   errstr = "setuid(ROOT_UID)";
-                                   goto bad;
-                               }
-                               if (perm_current == PERM_RUNAS)
-                                   restore_groups();
-                               break;
-
-       case PERM_FULL_USER:
-                               (void) setgid(user_gid);
-                               if (setuid(user_uid)) {
-                                   errstr = "setuid(user_uid)";
-                                   goto bad;
-                               }
-                               break;
-                               
-#if 0
-       case PERM_FULL_RUNAS:
-                               runas_setup();
-                               if (setuid(runas_pw->pw_uid)) {
-                                   errstr = "unable to change to runas uid";
-                                   goto bad;
-                               }
-                               break;
-#endif
-
-       case PERM_USER:
-       case PERM_SUDOERS:
-       case PERM_RUNAS:
-       case PERM_TIMESTAMP:
-                               /* Unsupported since we can't set euid. */
-                               break;
+    case PERM_INITIAL:
+       /* Stash initial state */
+       state->ruid = getuid();
+       state->rgid = getgid();
+       state->groups = user_groups;
+       state->ngroups = user_ngroups;
+       break;
+
+    case PERM_ROOT:
+       state->ruid = ROOT_UID;
+       state->rgid = -1;
+       state->groups = NULL;
+       state->ngroups = -1;
+       if (setuid(ROOT_UID)) {
+           errstr = "setuid(ROOT_UID)";
+           goto bad;
+       }
+       break;
+
+    case PERM_FULL_USER:
+       state->groups = user_groups;
+       state->ngroups = user_ngroups;
+       if (state->ngroups != -1 && state->groups != ostate->groups) {
+           if (setgroups(state->ngroups, state->groups)) {
+               errstr = "setgroups()";
+               goto bad;
+           }
+       }
+       state->rgid = user_gid;
+       (void) setgid(user_gid);
+       state->ruid = user_uid;
+       if (setuid(user_uid)) {
+           errstr = "setuid(user_uid)";
+           goto bad;
+       }
+       break;
+
+    case PERM_USER:
+    case PERM_SUDOERS:
+    case PERM_RUNAS:
+    case PERM_TIMESTAMP:
+       /* Unsupported since we can't set euid. */
+       break;
     }
 
 done:
-    if (push_it)
-       perm_stack[perm_stack_depth++] = perm_current;
-    perm_current = perm;
-    return(1);
+    perm_stack_depth++;
+    return 1;
 bad:
+    /* XXX - better warnings inline */
     warningx("%s: %s", errstr,
        errno == EAGAIN ? "too many processes" : strerror(errno));
     if (noexit)
        return(0);
     exit(1);
 }
+
+void
+restore_perms(void)
+{
+    struct perm_state *state, *ostate;
+
+    if (perm_stack_depth < 2)
+       return;
+
+    state = &perm_stack[perm_stack_depth - 1];
+    ostate = &perm_stack[perm_stack_depth - 2];
+    perm_stack_depth--;
+
+    if (state->ngroups != -1 && state->groups != ostate->groups) {
+       if (setgroups(ostate->ngroups, ostate->groups)) {
+           warning("setgroups()");
+           goto bad;
+       }
+    }
+    if (OID(rgid) != -1 && setgid(ostate->rgid)) {
+       warning("setgid(%d)", ostate->rgid);
+       goto bad;
+    }
+    if (OID(ruid) != -1 && setuid(ostate->ruid)) {
+       warning("setuid(%d)", ostate->ruid);
+       goto bad;
+    }
+    return;
+
+bad:
+    exit(1);
+}
 #  endif /* HAVE_SETEUID */
 # endif /* HAVE_SETREUID */
 #endif /* HAVE_SETRESUID */
@@ -561,10 +926,6 @@ bad:
 static void
 runas_setgroups()
 {
-    static int ngroups = -1;
-#ifdef HAVE_GETGROUPS
-    static GETGROUPS_T *groups;
-#endif
     struct passwd *pw;
 
     if (def_preserve_groups)
@@ -573,30 +934,23 @@ runas_setgroups()
     /*
      * Use stashed copy of runas groups if available, else initgroups and stash.
      */
-    if (ngroups == -1) {
+    if (runas_ngroups == -1) {
        pw = runas_pw ? runas_pw : sudo_user.pw;
        if (initgroups(pw->pw_name, pw->pw_gid) < 0)
            log_error(USE_ERRNO|MSG_ONLY, "can't set runas group vector");
 #ifdef HAVE_GETGROUPS
-       if ((ngroups = getgroups(0, NULL)) > 0) {
-           groups = emalloc2(ngroups, sizeof(GETGROUPS_T));
-           if (getgroups(ngroups, groups) < 0)
+       if ((runas_ngroups = getgroups(0, NULL)) > 0) {
+           runas_groups = emalloc2(runas_ngroups, sizeof(GETGROUPS_T));
+           if (getgroups(runas_ngroups, runas_groups) < 0)
                log_error(USE_ERRNO|MSG_ONLY, "can't get runas group vector");
        }
     } else {
-       if (setgroups(ngroups, groups) < 0)
+       if (setgroups(runas_ngroups, runas_groups) < 0)
            log_error(USE_ERRNO|MSG_ONLY, "can't set runas group vector");
 #endif /* HAVE_GETGROUPS */
     }
 }
 
-static void
-restore_groups()
-{
-    if (user_groups >= 0 && setgroups(user_ngroups, user_groups) < 0)
-       log_error(USE_ERRNO|MSG_ONLY, "can't reset user group vector");
-}
-
 #else
 
 static void
@@ -605,12 +959,6 @@ runas_setgroups()
     /* STUB */
 }
 
-static void
-restore_groups()
-{
-    /* STUB */
-}
-
 #endif /* HAVE_INITGROUPS */
 
 #if 0
@@ -629,6 +977,7 @@ runas_setup()
        aix_setlimits(runas_pw->pw_name);
 #endif
 #ifdef HAVE_PAM
+       /* XXX - move this */
        pam_prep_user(runas_pw);
 #endif /* HAVE_PAM */
 
index 5bb593e5e8d3a7700bda27da1128600e1b5229ff..8c1516594c999d1856b1c656fb3eba7f3ed31428 100644 (file)
@@ -126,6 +126,10 @@ extern int sudo_edit(int, char **, char **);
 void validate_env_vars(struct list_member *);
 void insert_env_vars(struct list_member *);
 
+/* XXX */
+extern int runas_ngroups;
+extern GETGROUPS_T *runas_groups;
+
 /*
  * Globals
  */
@@ -182,6 +186,7 @@ sudoers_policy_open(unsigned int version, sudo_conv_t conversation,
 
     if (sigsetjmp(error_jmp, 1)) {
        /* called via error(), errorx() or log_error() */
+       rewind_perms();
        return -1;
     }
 
@@ -220,6 +225,8 @@ sudoers_policy_open(unsigned int version, sudo_conv_t conversation,
     /* Parse nsswitch.conf for sudoers order. */
     snl = sudo_read_nss();
 
+    set_perms(PERM_INITIAL);
+
     /* Open and parse sudoers, set global defaults */
     tq_foreach_fwd(snl, nss) {
        if (nss->open(nss) == 0 && nss->parse(nss) == 0) {
@@ -255,6 +262,8 @@ sudoers_policy_open(unsigned int version, sudo_conv_t conversation,
     /* Initialize environment functions (including replacements). */
     env_init(envp);
 
+    restore_perms();
+
     return TRUE;
 }
 
@@ -286,9 +295,12 @@ sudoers_policy_main(int argc, char * const argv[], char *env_add[],
 
     if (sigsetjmp(error_jmp, 1)) {
        /* error recovery via error(), errorx() or log_error() */
+       rewind_perms();
        return -1;
     }
 
+    set_perms(PERM_INITIAL);
+
     /*
      * Make a local copy of argc/argv, with special handling
      * for the '-e', '-i' or '-s' options.
@@ -530,32 +542,22 @@ sudoers_policy_main(int argc, char * const argv[], char *env_add[],
     }
     if (def_preserve_groups) {
        command_info[info_len++] = "preserve_groups=true";
-    } else {
-       /* XXX - what about when runas user has no passwd entry? */
-#ifdef HAVE_GETGRSET
-       char *gid_list = getgrset(runas_pw->pw_name);
-       easprintf(&command_info[info_len++], "runas_groups=%s", gid_list);
-       efree(gid_list);
-#else
-       gid_t groups[NGROUPS_MAX * 2]; /* should use sysconf */
-       int i, len, ngroups = NGROUPS_MAX * 2;
+    } else if (runas_ngroups != -1) {
+       int i, len;
        size_t glsize;
        char *cp, *gid_list;
 
-       /* XXX - rval */
-       getgrouplist(runas_pw->pw_name, runas_pw->pw_gid, groups, &ngroups);
-       glsize = sizeof("runas_groups=") - 1 + (user_ngroups * (MAX_UID_T_LEN + 1));
+       glsize = sizeof("runas_groups=") - 1 + (runas_ngroups * (MAX_UID_T_LEN + 1));
        gid_list = emalloc(glsize);
        memcpy(gid_list, "runas_groups=", sizeof("runas_groups=") - 1);
        cp = gid_list + sizeof("runas_groups=") - 1;
-       for (i = 0; i < ngroups; i++) {
+       for (i = 0; i < runas_ngroups; i++) {
            /* XXX - check rval */
            len = snprintf(cp, glsize - (cp - gid_list), "%s%lu",
-                i ? "," : "", (unsigned long)groups[i]);
+                i ? "," : "", (unsigned long)runas_groups[i]);
            cp += len;
        }
        command_info[info_len++] = gid_list;
-#endif
     }
 
     /* Must audit before uid change. */
@@ -568,6 +570,8 @@ sudoers_policy_main(int argc, char * const argv[], char *env_add[],
 
     rval = TRUE;
 
+    restore_perms();
+
 done:
     return rval;
 }
index 494a15fd2bcc6a721e21189fe5e920c665c3eade..dce4f0d5935e47ec263fe97aa537210c19f6dd62 100644 (file)
@@ -236,8 +236,9 @@ int sudo_file_display_cmnd(struct sudo_nss *, struct passwd *);
 int sudo_file_display_defaults(struct sudo_nss *, struct passwd *, struct lbuf *);
 int sudo_file_display_bound_defaults(struct sudo_nss *, struct passwd *, struct lbuf *);
 int sudo_file_display_privs(struct sudo_nss *, struct passwd *, struct lbuf *);
+void rewind_perms(void);
 int set_perms(int);
-int restore_perms(void);
+void restore_perms(void);
 void remove_timestamp(int);
 int check_secureware(char *);
 void sia_attempt_auth(void);
index 2c4346426a09fdf1c0df4dd1a3632da2e42e51f5..5ddd4045b5d4a739f085e5cc2b62f25fbe351a92 100644 (file)
@@ -363,10 +363,14 @@ init_envtables()
 }
 
 int
-set_perms(perm)
-    int perm;
+set_perms(int perm)
+{
+    return 1;
+}
+
+void
+restore_perms(void)
 {
-    return(1);
 }
 
 void