| /* |
| * Copyright (c) 1994-1996,1998-2010 Todd C. Miller <Todd.Miller@courtesan.com> |
| * |
| * 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. |
| * |
| * 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 <sys/types.h> |
| #include <sys/param.h> |
| #include <sys/stat.h> |
| #include <stdio.h> |
| #ifdef STDC_HEADERS |
| # include <stdlib.h> |
| # include <stddef.h> |
| #else |
| # ifdef HAVE_STDLIB_H |
| # include <stdlib.h> |
| # endif |
| #endif /* STDC_HEADERS */ |
| #ifdef HAVE_STRING_H |
| # include <string.h> |
| #endif /* HAVE_STRING_H */ |
| #ifdef HAVE_STRINGS_H |
| # include <strings.h> |
| #endif /* HAVE_STRINGS_H */ |
| #ifdef HAVE_UNISTD_H |
| # include <unistd.h> |
| #endif /* HAVE_UNISTD_H */ |
| #include <pwd.h> |
| #include <errno.h> |
| #include <grp.h> |
| #ifdef HAVE_LOGIN_CAP_H |
| # include <login_cap.h> |
| #endif |
| |
| #include "sudo.h" |
| |
| #ifdef __TANDEM |
| # define ROOT_UID 65535 |
| #else |
| # define ROOT_UID 0 |
| #endif |
| |
| /* |
| * Prototypes |
| */ |
| static void runas_setup __P((void)); |
| static void runas_setgroups __P((void)); |
| static void restore_groups __P((void)); |
| |
| static int current_perm = -1; |
| |
| #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(). |
| * 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. |
| */ |
| 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 (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) 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_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; |
| |
| 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; |
| } |
| |
| current_perm = perm; |
| return(1); |
| bad: |
| warningx("%s: %s", errstr, |
| errno == EAGAIN ? "too many processes" : strerror(errno)); |
| if (noexit) |
| return(0); |
| exit(1); |
| } |
| |
| #else |
| # ifdef HAVE_SETREUID |
| |
| /* |
| * Set real and effective uids and gids based on perm. |
| * 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_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 (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) 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_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; |
| |
| 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; |
| } |
| |
| 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 */ |
| # ifdef HAVE_SETEUID |
| |
| /* |
| * Set real and effective uids and gids based on perm. |
| * NOTE: does not support the "stay_setuid" option. |
| */ |
| 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); |
| |
| /* |
| * 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; |
| } |
| |
| 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)) { |
| 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_FULL_RUNAS: |
| /* headed for exec() */ |
| runas_setup(); |
| if (setuid(runas_pw->pw_uid)) { |
| errstr = "unable to change to runas uid"; |
| goto bad; |
| } |
| break; |
| |
| 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; |
| } |
| |
| 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_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 |
| static struct passwd *pw; |
| struct passwd *opw = pw; |
| |
| if (def_preserve_groups) |
| return; |
| |
| /* |
| * Use stashed copy of runas groups if available, else initgroups and stash. |
| */ |
| pw = runas_pw ? runas_pw : sudo_user.pw; |
| if (pw != opw) { |
| # ifdef HAVE_SETAUTHDB |
| aix_setauthdb(pw->pw_name); |
| # endif |
| 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 (groups) { |
| efree(groups); |
| groups = NULL; |
| } |
| 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"); |
| } |
| # ifdef HAVE_SETAUTHDB |
| aix_restoreauthdb(); |
| # endif |
| } 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 (user_ngroups >= 0 && 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 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_prep_user(runas_pw->pw_name, user_ttypath); |
| #endif |
| #ifdef HAVE_PAM |
| pam_begin_session(runas_pw); |
| #endif /* HAVE_PAM */ |
| |
| #ifdef HAVE_LOGIN_CAP_H |
| if (def_use_loginclass) { |
| /* |
| * We only use setusercontext() to set the nice value and rlimits. |
| */ |
| flags = LOGIN_SETRESOURCES|LOGIN_SETPRIORITY; |
| 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"); |
| } |
| } |