diff --git a/MANIFEST b/MANIFEST index a2ed2a957..805a86241 100644 --- a/MANIFEST +++ b/MANIFEST @@ -15,6 +15,7 @@ common/fileops.c common/fmt_string.c common/lbuf.c common/list.c +common/setgroups.c common/term.c common/zero_bytes.c compat/Makefile.in diff --git a/common/Makefile.in b/common/Makefile.in index 6486e1844..a1319eff6 100644 --- a/common/Makefile.in +++ b/common/Makefile.in @@ -43,7 +43,7 @@ DEFS = @OSDEFS@ SHELL = @SHELL@ LTOBJS = alloc.lo atobool.lo fileops.lo fmt_string.lo \ - lbuf.lo list.lo term.lo zero_bytes.lo @COMMON_OBJS@ + lbuf.lo list.lo setgroups.lo term.lo zero_bytes.lo @COMMON_OBJS@ all: libcommon.la @@ -113,6 +113,8 @@ lbuf.lo: $(srcdir)/lbuf.c $(top_builddir)/config.h $(incdir)/missing.h \ list.lo: $(srcdir)/list.c $(top_builddir)/config.h $(incdir)/missing.h \ $(incdir)/list.h $(incdir)/error.h $(LIBTOOL) --mode=compile $(CC) -c -o $@ $(CPPFLAGS) $(CFLAGS) $(DEFS) $(srcdir)/list.c +setgroups.lo: $(srcdir)/setgroups.c $(top_builddir)/config.h $(incdir)/missing.h + $(LIBTOOL) --mode=compile $(CC) -c -o $@ $(CPPFLAGS) $(CFLAGS) $(DEFS) $(srcdir)/setgroups.c term.lo: $(srcdir)/term.c $(top_builddir)/config.h $(incdir)/missing.h $(LIBTOOL) --mode=compile $(CC) -c -o $@ $(CPPFLAGS) $(CFLAGS) $(DEFS) $(srcdir)/term.c zero_bytes.lo: $(srcdir)/zero_bytes.c $(top_builddir)/config.h \ diff --git a/common/setgroups.c b/common/setgroups.c new file mode 100644 index 000000000..19feb53ac --- /dev/null +++ b/common/setgroups.c @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2011 Todd C. Miller + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include +#include +#ifdef STDC_HEADERS +# include +# include +#else +# ifdef HAVE_STDLIB_H +# include +# endif +#endif /* STDC_HEADERS */ +#ifdef HAVE_UNISTD_H +# include +#endif /* HAVE_UNISTD_H */ +#include +#include + +#include "missing.h" + +int +sudo_setgroups(int ngids, const GETGROUPS_T *gids) +{ + int maxgids, rval; + + rval = setgroups(ngids, gids); + if (rval == -1 && errno == EINVAL) { + /* Too many groups, try again with fewer. */ +#if defined(HAVE_SYSCONF) && defined(_SC_NGROUPS_MAX) + maxgids = sysconf(_SC_NGROUPS_MAX); + if (maxgids == -1) +#endif + maxgids = NGROUPS_MAX; + if (maxgids > ngids) { + /* Skip base gid. */ + if (getegid() == gids[0]) + rval = setgroups(maxgids, gids + 1); + } + } + return rval; +} diff --git a/plugins/sudoers/set_perms.c b/plugins/sudoers/set_perms.c index 4ad5ef7a3..1fb261888 100644 --- a/plugins/sudoers/set_perms.c +++ b/plugins/sudoers/set_perms.c @@ -141,7 +141,7 @@ set_perms(int perm) state->sgid = state->egid; /* in case we are setgid */ #endif state->grlist = user_group_list; - grlist_addref(user_group_list); + grlist_addref(state->grlist); break; case PERM_ROOT: @@ -160,14 +160,6 @@ set_perms(int perm) break; case PERM_USER: - state->grlist = user_group_list; - grlist_addref(user_group_list); - if (state->grlist != ostate->grlist) { - if (setgroups(state->grlist->ngids, state->grlist->gids)) { - errstr = "setgroups()"; - goto bad; - } - } state->rgid = -1; state->egid = user_gid; state->sgid = -1; @@ -175,6 +167,14 @@ set_perms(int perm) errstr = "setresgid(-1, user_gid, -1)"; goto bad; } + state->grlist = user_group_list; + grlist_addref(state->grlist); + if (state->grlist != ostate->grlist) { + if (sudo_setgroups(state->grlist->ngids, state->grlist->gids)) { + errstr = "setgroups()"; + goto bad; + } + } state->ruid = user_uid; state->euid = user_uid; state->suid = ROOT_UID; @@ -186,14 +186,6 @@ set_perms(int perm) case PERM_FULL_USER: /* headed for exec() */ - state->grlist = user_group_list; - grlist_addref(user_group_list); - if (state->grlist != ostate->grlist) { - if (setgroups(state->grlist->ngids, state->grlist->gids)) { - errstr = "setgroups()"; - goto bad; - } - } state->rgid = user_gid; state->egid = user_gid; state->sgid = user_gid; @@ -201,6 +193,14 @@ set_perms(int perm) errstr = "setresgid(user_gid, user_gid, user_gid)"; goto bad; } + state->grlist = user_group_list; + grlist_addref(state->grlist); + if (state->grlist != ostate->grlist) { + if (sudo_setgroups(state->grlist->ngids, state->grlist->gids)) { + errstr = "setgroups()"; + goto bad; + } + } state->ruid = user_uid; state->euid = user_uid; state->suid = user_uid; @@ -211,7 +211,6 @@ set_perms(int perm) break; case PERM_RUNAS: - state->grlist = runas_setgroups(); state->rgid = -1; state->egid = runas_gr ? runas_gr->gr_gid : runas_pw->pw_gid; state->sgid = -1; @@ -219,6 +218,7 @@ set_perms(int perm) errstr = _("unable to change to runas gid"); goto bad; } + state->grlist = runas_setgroups(); state->ruid = -1; state->euid = runas_pw ? runas_pw->pw_uid : user_uid; state->suid = -1; @@ -315,7 +315,7 @@ restore_perms(void) goto bad; } if (state->grlist != ostate->grlist) { - if (setgroups(ostate->grlist->ngids, ostate->grlist->gids)) { + if (sudo_setgroups(ostate->grlist->ngids, ostate->grlist->gids)) { warning("setgroups()"); goto bad; } @@ -392,19 +392,20 @@ set_perms(int perm) break; case PERM_USER: - state->grlist = user_group_list; - if (state->grlist != ostate->grlist) { - if (setgroups(state->grlist->ngids, state->grlist->gids)) { - errstr = "setgroups()"; - goto bad; - } - } state->rgid = -1; state->egid = user_gid; if (setregid(-1, ID(egid))) { errstr = "setregid(-1, user_gid)"; goto bad; } + state->grlist = user_group_list; + grlist_addref(state->grlist); + if (state->grlist != ostate->grlist) { + if (sudo_setgroups(state->grlist->ngids, state->grlist->gids)) { + errstr = "setgroups()"; + goto bad; + } + } state->ruid = ROOT_UID; state->euid = user_uid; if (setreuid(ID(ruid), ID(euid))) { @@ -415,19 +416,20 @@ set_perms(int perm) case PERM_FULL_USER: /* headed for exec() */ - state->grlist = user_group_list; - if (state->grlist != ostate->grlist) { - if (setgroups(state->grlist->ngids, state->grlist->gids)) { - 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->grlist = user_group_list; + grlist_addref(state->grlist); + if (state->grlist != ostate->grlist) { + if (sudo_setgroups(state->grlist->ngids, state->grlist->gids)) { + errstr = "setgroups()"; + goto bad; + } + } state->ruid = user_uid; state->euid = user_uid; if (setreuid(ID(ruid), ID(euid))) { @@ -437,13 +439,13 @@ set_perms(int perm) break; case PERM_RUNAS: - state->grlist = runas_setgroups(); state->rgid = -1; state->egid = runas_gr ? runas_gr->gr_gid : runas_pw->pw_gid; if (setregid(ID(rgid), ID(egid))) { errstr = _("unable to change to runas gid"); goto bad; } + state->grlist = runas_setgroups(); state->ruid = ROOT_UID; state->euid = runas_pw ? runas_pw->pw_uid : user_uid; if (setreuid(ID(ruid), ID(euid))) { @@ -540,7 +542,7 @@ restore_perms(void) goto bad; } if (state->grlist != ostate->grlist) { - if (setgroups(ostate->grlist->ngids, ostate->grlist->gids)) { + if (sudo_setgroups(ostate->grlist->ngids, ostate->grlist->gids)) { warning("setgroups()"); goto bad; } @@ -622,19 +624,20 @@ set_perms(int perm) break; case PERM_USER: - state->grlist = user_group_list; - if (state->grlist != ostate->grlist) { - if (setgroups(state->grlist->ngids, state->grlist->gids)) { - errstr = "setgroups()"; - goto bad; - } - } - state->rgid = -1; state->egid = user_gid; if (setegid(ID(egid))) { errstr = "setegid(user_gid)"; goto bad; } + state->grlist = user_group_list; + grlist_addref(state->grlist); + if (state->grlist != ostate->grlist) { + if (sudo_setgroups(state->grlist->ngids, state->grlist->gids)) { + errstr = "setgroups()"; + goto bad; + } + } + state->rgid = -1; state->ruid = ROOT_UID; state->euid = user_uid; if (seteuid(ID(euid))) { @@ -645,19 +648,20 @@ set_perms(int perm) case PERM_FULL_USER: /* headed for exec() */ - state->grlist = user_group_list; - if (state->grlist != ostate->grlist) { - if (setgroups(state->grlist->ngids, state->grlist->gids)) { - errstr = "setgroups()"; - goto bad; - } - } state->rgid = user_gid; state->egid = user_gid; if (setgid(user_gid)) { errstr = "setgid(user_gid)"; goto bad; } + state->grlist = user_group_list; + grlist_addref(state->grlist); + if (state->grlist != ostate->grlist) { + if (sudo_setgroups(state->grlist->ngids, state->grlist->gids)) { + errstr = "setgroups()"; + goto bad; + } + } state->ruid = user_uid; state->euid = user_uid; if (setuid(user_uid)) { @@ -667,13 +671,13 @@ set_perms(int perm) break; case PERM_RUNAS: - state->grlist = runas_setgroups(); 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->grlist = runas_setgroups(); state->ruid = -1; state->euid = runas_pw ? runas_pw->pw_uid : user_uid; if (seteuid(ID(euid))) { @@ -765,7 +769,7 @@ restore_perms(void) goto bad; } if (state->grlist != ostate->grlist) { - if (setgroups(ostate->grlist->ngids, ostate->grlist->gids)) { + if (sudo_setgroups(ostate->grlist->ngids, ostate->grlist->gids)) { warning("setgroups()"); goto bad; } @@ -832,15 +836,16 @@ set_perms(int perm) break; case PERM_FULL_USER: + state->rgid = user_gid; + (void) setgid(user_gid); state->grlist = user_group_list; + grlist_addref(state->grlist); if (state->grlist != ostate->grlist) { - if (setgroups(state->grlist->ngids, state->grlist->gids)) { + if (sudo_setgroups(state->grlist->ngids, state->grlist->gids)) { errstr = "setgroups()"; goto bad; } } - state->rgid = user_gid; - (void) setgid(user_gid); state->ruid = user_uid; if (setuid(user_uid)) { errstr = "setuid(user_uid)"; @@ -880,17 +885,17 @@ restore_perms(void) ostate = &perm_stack[perm_stack_depth - 2]; perm_stack_depth--; + if (OID(rgid) != -1 && setgid(ostate->rgid)) { + warning("setgid(%d)", ostate->rgid); + goto bad; + } if (state->grlist != ostate->grlist) { - if (setgroups(ostate->grlist->ngids, ostate->grlist->gids)) { + if (sudo_setgroups(ostate->grlist->ngids, ostate->grlist->gids)) { warning("setgroups()"); goto bad; } } grlist_delref(state->grlist); - 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; @@ -923,7 +928,7 @@ runas_setgroups(void) #ifdef HAVE_SETAUTHDB aix_restoreauthdb(); #endif - if (setgroups(grlist->ngids, grlist->gids) < 0) + if (sudo_setgroups(grlist->ngids, grlist->gids) < 0) log_error(USE_ERRNO|MSG_ONLY, _("unable to set runas group vector")); return grlist; } diff --git a/plugins/sudoers/sudoers.h b/plugins/sudoers/sudoers.h index 6ec375bf8..2e4745ee9 100644 --- a/plugins/sudoers/sudoers.h +++ b/plugins/sudoers/sudoers.h @@ -333,6 +333,9 @@ void group_plugin_unload(void); int group_plugin_query(const char *user, const char *group, const struct passwd *pwd); +/* setgroups.c */ +int sudo_setgroups(int ngids, const GETGROUPS_T *gids); + #ifndef _SUDO_MAIN extern struct sudo_user sudo_user; extern struct passwd *list_pw; diff --git a/src/sudo.c b/src/sudo.c index 1fc471b53..6fccf46d6 100644 --- a/src/sudo.c +++ b/src/sudo.c @@ -928,7 +928,7 @@ exec_setup(struct command_details *details, const char *ptyname, int ptyfd) if (!ISSET(details->flags, CD_PRESERVE_GROUPS)) { if (details->ngroups >= 0) { - if (setgroups(details->ngroups, details->groups) < 0) { + if (sudo_setgroups(details->ngroups, details->groups) < 0) { warning(_("unable to set supplementary group IDs")); goto done; } diff --git a/src/sudo.h b/src/sudo.h index 64022c460..cbf4860d3 100644 --- a/src/sudo.h +++ b/src/sudo.h @@ -227,6 +227,9 @@ void aix_setauthdb(char *user); /* interfaces.c */ int get_net_ifs(char **addrinfo); +/* setgroups.c */ +int sudo_setgroups(int ngids, const GETGROUPS_T *gids); + #ifndef errno extern int errno; #endif diff --git a/src/sudo_edit.c b/src/sudo_edit.c index 99393cf7a..0a4d511f8 100644 --- a/src/sudo_edit.c +++ b/src/sudo_edit.c @@ -64,12 +64,12 @@ switch_user(uid_t euid, gid_t egid, int ngroups, GETGROUPS_T *groups) if (seteuid(ROOT_UID) != 0) error(1, "seteuid(ROOT_UID)"); } - if (ngroups != -1) { - if (setgroups(ngroups, groups) != 0) - error(1, "setgroups"); - } if (setegid(egid) != 0) error(1, "setegid(%d)", (int)egid); + if (ngroups != -1) { + if (sudo_setgroups(ngroups, groups) != 0) + error(1, "setgroups"); + } if (euid != ROOT_UID) { if (seteuid(euid) != 0) error(1, "seteuid(%d)", (int)euid);