X-Git-Url: https://git.gag.com/?a=blobdiff_plain;f=set_perms.c;h=244bc40a923e4277752e03a456157d7f01d02aa4;hb=a4d16b7546088ef5bdeadb3a6877bcc1d1530a63;hp=300f5b3f388a951390615f244cdf2220812613e0;hpb=ca3ab12a66fc683cabf546fd405cfbf39ef9fb6f;p=debian%2Fsudo diff --git a/set_perms.c b/set_perms.c index 300f5b3..244bc40 100644 --- a/set_perms.c +++ b/set_perms.c @@ -1,38 +1,24 @@ /* - * Copyright (c) 1994-1996,1998-2001 Todd C. Miller - * All rights reserved. + * Copyright (c) 1994-1996,1998-2009 Todd C. Miller * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: + * 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. * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. + * 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. * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * 3. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * 4. Products derived from this software may not be called "Sudo" nor - * may "Sudo" appear in their names without specific prior written - * permission from the author. - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, - * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY - * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL - * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; - * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Sponsored in part by the Defense Advanced Research Projects + * Agency (DARPA) and Air Force Research Laboratory, Air Force + * Materiel Command, USAF, under agreement number F39502-99-1-0512. */ -#include "config.h" +#include #include #include @@ -65,280 +51,533 @@ #include "sudo.h" -#ifndef lint -static const char rcsid[] = "$Sudo: set_perms.c,v 1.12 2002/01/22 02:00:25 millert Exp $"; -#endif /* lint */ +#ifdef __TANDEM +# define ROOT_UID 65535 +#else +# define ROOT_UID 0 +#endif /* * Prototypes */ static void runas_setup __P((void)); -static void fatal __P((char *, int)); +static void runas_setgroups __P((void)); +static void restore_groups __P((void)); -#if !defined(NO_SAVED_IDS) && defined(_SC_SAVED_IDS) && defined(_SC_VERSION) +static int current_perm = -1; + +#ifdef HAVE_SETRESUID /* - * Set real and effective uids and gids based on perm. - * Since we have POSIX saved IDs we can get away with just - * toggling the effective uid/gid unless we are headed for an exec(). + * 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(). + * We only flip the effective gid since it only changes for PERM_SUDOERS. + * This version of set_perms() works fine with the "stay_setuid" option. */ -void -set_perms_posix(perm, sudo_mode) +int +set_perms(perm) int perm; - int sudo_mode; { - int error; + const char *errstr; + int noexit; + + noexit = ISSET(perm, PERM_NOEXIT); + CLR(perm, PERM_MASK); + + if (perm == current_perm) + return(1); switch (perm) { case PERM_ROOT: - if (seteuid(0)) - fatal("seteuid(0) failed, your operating system may have broken POSIX saved ID support\nTry running configure with --disable-saved-ids", 0); - break; - - case PERM_FULL_ROOT: - /* headed for exec() */ - (void) seteuid(0); - if (setuid(0)) - fatal("setuid(0)", 1); + 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 (current_perm == PERM_RUNAS) + restore_groups(); break; case PERM_USER: - (void) setegid(user_gid); - if (seteuid(user_uid)) - fatal("seteuid(user_uid)", 1); + (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 (setuid(user_uid)) - fatal("setuid(user_uid)", 1); - break; + (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: - /* headed for exec(), assume euid == 0 */ + 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_FULL_RUNAS: + /* headed for exec(), assume euid == ROOT_UID */ runas_setup(); - if (def_flag(I_STAY_SETUID)) - error = seteuid(runas_pw->pw_uid); - else - error = setuid(runas_pw->pw_uid); - if (error) - fatal("unable to change to runas uid", 1); + 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; case PERM_SUDOERS: - /* assume euid == 0, ruid == user */ - if (setegid(SUDOERS_GID)) - fatal("unable to change to sudoers gid", 1); + /* assume euid == ROOT_UID, ruid == user */ + if (setresgid(-1, SUDOERS_GID, -1)) + error(1, "unable to change to sudoers gid"); /* - * If SUDOERS_UID == 0 and SUDOERS_MODE + * 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 == 0) { - if ((SUDOERS_MODE & 040) && seteuid(1)) - fatal("seteuid(1)", 1); + 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 (seteuid(SUDOERS_UID)) - fatal("seteuid(SUDOERS_UID)", 1); + 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; } + + current_perm = perm; + return(1); +bad: + warningx("%s: %s", errstr, + errno == EAGAIN ? "too many processes" : strerror(errno)); + if (noexit) + return(0); + exit(1); } -#endif /* !NO_SAVED_IDS && _SC_SAVED_IDS && _SC_VERSION */ -#ifdef HAVE_SETREUID +#else +# ifdef HAVE_SETREUID + /* * Set real and effective uids and gids based on perm. - * We always retain a real or effective uid of 0 unless + * 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. */ -void -set_perms_fallback(perm, sudo_mode) +int +set_perms(perm) int perm; - int sudo_mode; { - int error; + const char *errstr; + int noexit; + + noexit = ISSET(perm, PERM_NOEXIT); + CLR(perm, PERM_MASK); + + if (perm == current_perm) + return(1); switch (perm) { - case PERM_FULL_ROOT: case PERM_ROOT: - if (setuid(0)) - fatal("setuid(0) failed, your operating system may have broken POSIX saved ID support\nTry running configure with --disable-setreuid", 0); + 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 (current_perm == PERM_RUNAS) + restore_groups(); break; case PERM_USER: - (void) setegid(user_gid); - if (setreuid(0, user_uid)) - fatal("setreuid(0, user_uid)", 1); + (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 (setuid(user_uid)) - fatal("setuid(user_uid)", 1); + if (setreuid(user_uid, user_uid)) { + errstr = "setreuid(user_uid, user_uid)"; + goto bad; + } break; case PERM_RUNAS: - /* headed for exec(), assume euid == 0 */ + 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_FULL_RUNAS: + /* headed for exec(), assume euid == ROOT_UID */ runas_setup(); - if (def_flag(I_STAY_SETUID)) - error = setreuid(user_uid, runas_pw->pw_uid); - else - error = setuid(runas_pw->pw_uid); - if (error) - fatal("unable to change to runas uid", 1); + 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; case PERM_SUDOERS: - /* assume euid == 0, ruid == user */ - if (setegid(SUDOERS_GID)) - fatal("unable to change to sudoers gid", 1); + /* assume euid == ROOT_UID, ruid == user */ + if (setregid(-1, SUDOERS_GID)) + error(1, "unable to change to sudoers gid"); /* - * If SUDOERS_UID == 0 and SUDOERS_MODE + * 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 == 0) { - if ((SUDOERS_MODE & 040) && setreuid(0, 1)) - fatal("setreuid(0, 1)", 1); + if (SUDOERS_UID == ROOT_UID) { + if ((SUDOERS_MODE & 040) && setreuid(ROOT_UID, 1)) { + errstr = "setreuid(ROOT_UID, 1)"; + goto bad; + } } else { - if (setreuid(0, SUDOERS_UID)) - fatal("setreuid(0, SUDOERS_UID)", 1); + 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; } + + current_perm = perm; + return(1); +bad: + warningx("%s: %s", errstr, + errno == EAGAIN ? "too many processes" : strerror(errno)); + if (noexit) + return(0); + exit(1); } -#else +# 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. */ -void -set_perms_fallback(perm, sudo_mode) +int +set_perms(perm) int perm; - int sudo_mode; { + const char *errstr; + int noexit; + + noexit = ISSET(perm, PERM_NOEXIT); + CLR(perm, PERM_MASK); + + if (perm == current_perm) + return(1); /* - * Since we only have setuid() and seteuid() we have to set - * real and effective uidss to 0 initially. + * 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 (setuid(0)) - fatal("setuid(0)", 1); + 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 (current_perm == PERM_RUNAS) + restore_groups(); + break; + case PERM_USER: (void) setegid(user_gid); - if (seteuid(user_uid)) - fatal("seteuid(user_uid)", 1); + 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)) - fatal("setuid(user_uid)", 1); + if (setuid(user_uid)) { + errstr = "setuid(user_uid)"; + goto bad; + } break; case PERM_RUNAS: - /* headed for exec(), assume euid == 0 */ + 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_FULL_RUNAS: + /* headed for exec() */ runas_setup(); - if (setuid(runas_pw->pw_uid)) - fatal("unable to change to runas uid", 1); + if (setuid(runas_pw->pw_uid)) { + errstr = "unable to change to runas uid"; + goto bad; + } break; case PERM_SUDOERS: - /* assume euid == 0, ruid == user */ if (setegid(SUDOERS_GID)) - fatal("unable to change to sudoers gid", 1); + error(1, "unable to change to sudoers gid"); /* - * If SUDOERS_UID == 0 and SUDOERS_MODE + * 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 == 0) { - if ((SUDOERS_MODE & 040) && seteuid(1)) - fatal("seteuid(1)", 1); + if (SUDOERS_UID == ROOT_UID) { + if ((SUDOERS_MODE & 040) && seteuid(1)) { + errstr = "seteuid(1)"; + goto bad; + } } else { - if (seteuid(SUDOERS_UID)) - fatal("seteuid(SUDOERS_UID)", 1); + 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; + } + + current_perm = perm; + return(1); +bad: + warningx("%s: %s", errstr, + errno == EAGAIN ? "too many processes" : strerror(errno)); + if (noexit) + return(0); + exit(1); +} + +# else /* !HAVE_SETRESUID && !HAVE_SETREUID && !HAVE_SETEUID */ + +/* + * Set uids and gids based on perm via setuid() and setgid(). + * NOTE: does not support the "stay_setuid" or timestampowner options. + * Also, SUDOERS_UID and SUDOERS_GID are not used. + */ +int +set_perms(perm) + int perm; +{ + const char *errstr; + int noexit; + + noexit = ISSET(perm, PERM_NOEXIT); + CLR(perm, PERM_MASK); + + if (perm == current_perm) + return(1); + + switch (perm) { + case PERM_ROOT: + if (setuid(ROOT_UID)) { + errstr = "setuid(ROOT_UID)"; + goto bad; + } + if (current_perm == PERM_RUNAS) + restore_groups(); + break; + + case PERM_FULL_USER: + (void) setgid(user_gid); + if (setuid(user_uid)) { + errstr = "setuid(user_uid)"; + goto bad; } break; + + case PERM_FULL_RUNAS: + runas_setup(); + if (setuid(runas_pw->pw_uid)) { + errstr = "unable to change to runas uid"; + goto bad; + } + break; + + case PERM_USER: + case PERM_SUDOERS: + case PERM_RUNAS: + case PERM_TIMESTAMP: + /* Unsupported since we can't set euid. */ + break; } + + current_perm = perm; + return(1); +bad: + warningx("%s: %s", errstr, + errno == EAGAIN ? "too many processes" : strerror(errno)); + if (noexit) + return(0); + exit(1); } -#endif /* HAVE_SETREUID */ +# endif /* HAVE_SETEUID */ +# endif /* HAVE_SETREUID */ +#endif /* HAVE_SETRESUID */ + +#ifdef HAVE_INITGROUPS +static void +runas_setgroups() +{ + static int ngroups = -1; +#ifdef HAVE_GETGROUPS + static GETGROUPS_T *groups; +#endif + struct passwd *pw; + + if (def_preserve_groups) + return; + + /* + * Use stashed copy of runas groups if available, else initgroups and stash. + */ + if (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) + log_error(USE_ERRNO|MSG_ONLY, "can't get runas group vector"); + } + } else { + if (setgroups(ngroups, groups) < 0) + log_error(USE_ERRNO|MSG_ONLY, "can't set runas group vector"); +#endif /* HAVE_GETGROUPS */ + } +} + +static void +restore_groups() +{ + if (setgroups(user_ngroups, user_groups) < 0) + log_error(USE_ERRNO|MSG_ONLY, "can't reset user group vector"); +} + +#else + +static void +runas_setgroups() +{ + /* STUB */ +} + +static void +restore_groups() +{ + /* STUB */ +} + +#endif /* HAVE_INITGROUPS */ static void runas_setup() { + gid_t gid; #ifdef HAVE_LOGIN_CAP_H - int error, flags; + int flags; extern login_cap_t *lc; #endif if (runas_pw->pw_name != NULL) { + gid = runas_gr ? runas_gr->gr_gid : runas_pw->pw_gid; +#ifdef HAVE_GETUSERATTR + aix_setlimits(runas_pw->pw_name); +#endif #ifdef HAVE_PAM pam_prep_user(runas_pw); #endif /* HAVE_PAM */ #ifdef HAVE_LOGIN_CAP_H - if (def_flag(I_USE_LOGINCLASS)) { + if (def_use_loginclass) { /* - * We don't have setusercontext() set the user since we - * may only want to set the effective uid. Depending on - * sudoers and/or command line arguments we may not want - * setusercontext() to call initgroups(). + * We only use setusercontext() to set the nice value and rlimits. */ flags = LOGIN_SETRESOURCES|LOGIN_SETPRIORITY; - if (!def_flag(I_PRESERVE_GROUPS)) - flags |= LOGIN_SETGROUP; - else if (setgid(runas_pw->pw_gid)) - perror("cannot set gid to runas gid"); - error = setusercontext(lc, runas_pw, - runas_pw->pw_uid, flags); - if (error) - perror("unable to set user context"); - } else -#endif /* HAVE_LOGIN_CAP_H */ - { - if (setgid(runas_pw->pw_gid)) - perror("cannot set gid to runas gid"); -#ifdef HAVE_INITGROUPS - /* - * Initialize group vector unless asked not to. - */ - if (!def_flag(I_PRESERVE_GROUPS) && - initgroups(*user_runas, runas_pw->pw_gid) < 0) - perror("cannot set group vector"); -#endif /* HAVE_INITGROUPS */ - } - } -} - -static void -fatal(str, printerr) - char *str; -{ - - if (str) { - if (printerr) - perror(str); - else { - fputs(str, stderr); - fputc('\n', stderr); + if (setusercontext(lc, runas_pw, runas_pw->pw_uid, flags)) { + if (runas_pw->pw_uid != ROOT_UID) + error(1, "unable to set user context"); + else + warning("unable to set user context"); + } } +#endif /* HAVE_LOGIN_CAP_H */ + /* + * Initialize group vector + */ + runas_setgroups(); +#ifdef HAVE_SETEUID + if (setegid(gid)) + warning("cannot set egid to runas gid"); +#endif + if (setgid(gid)) + warning("cannot set gid to runas gid"); } - exit(1); }