From 1bb9f6214314f28136df2987c773181b2f3a12b0 Mon Sep 17 00:00:00 2001 From: "Todd C. Miller" Date: Tue, 20 Apr 2010 17:00:31 -0400 Subject: [PATCH] Convert perm setting to push/pop model; still needs some work Use the stashed runas groups instead of using getgrouplist() Reset perms to the initial value on error --- plugins/sudoers/auth/sudo_auth.c | 8 +- plugins/sudoers/set_perms.c | 1057 ++++++++++++++++++++---------- plugins/sudoers/sudoers.c | 34 +- plugins/sudoers/sudoers.h | 3 +- plugins/sudoers/testsudoers.c | 10 +- 5 files changed, 735 insertions(+), 377 deletions(-) diff --git a/plugins/sudoers/auth/sudo_auth.c b/plugins/sudoers/auth/sudo_auth.c index 94ca3bb35..3a7f83725 100644 --- a/plugins/sudoers/auth/sudo_auth.c +++ b/plugins/sudoers/auth/sudo_auth.c @@ -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(); } } diff --git a/plugins/sudoers/set_perms.c b/plugins/sudoers/set_perms.c index e634895a4..601e0c105 100644 --- a/plugins/sudoers/set_perms.c +++ b/plugins/sudoers/set_perms.c @@ -64,42 +64,50 @@ 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 */ diff --git a/plugins/sudoers/sudoers.c b/plugins/sudoers/sudoers.c index 5bb593e5e..8c1516594 100644 --- a/plugins/sudoers/sudoers.c +++ b/plugins/sudoers/sudoers.c @@ -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; } diff --git a/plugins/sudoers/sudoers.h b/plugins/sudoers/sudoers.h index 494a15fd2..dce4f0d59 100644 --- a/plugins/sudoers/sudoers.h +++ b/plugins/sudoers/sudoers.h @@ -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); diff --git a/plugins/sudoers/testsudoers.c b/plugins/sudoers/testsudoers.c index 2c4346426..5ddd4045b 100644 --- a/plugins/sudoers/testsudoers.c +++ b/plugins/sudoers/testsudoers.c @@ -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 -- 2.40.0