Annotation of embedaddon/sudo/src/sudo.c, revision 1.1.1.6

1.1       misho       1: /*
1.1.1.4   misho       2:  * Copyright (c) 2009-2013 Todd C. Miller <Todd.Miller@courtesan.com>
1.1       misho       3:  *
                      4:  * Permission to use, copy, modify, and distribute this software for any
                      5:  * purpose with or without fee is hereby granted, provided that the above
                      6:  * copyright notice and this permission notice appear in all copies.
                      7:  *
                      8:  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
                      9:  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
                     10:  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
                     11:  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
                     12:  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
                     13:  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
                     14:  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
                     15:  */
                     16: 
                     17: #ifdef __TANDEM
                     18: # include <floss.h>
                     19: #endif
                     20: 
                     21: #include <config.h>
                     22: 
                     23: #include <sys/types.h>
                     24: #include <sys/stat.h>
                     25: #include <sys/wait.h>
                     26: #include <sys/socket.h>
                     27: #include <sys/time.h>
                     28: #include <sys/resource.h>
                     29: #include <stdio.h>
                     30: #ifdef STDC_HEADERS
                     31: # include <stdlib.h>
                     32: # include <stddef.h>
                     33: #else
                     34: # ifdef HAVE_STDLIB_H
                     35: #  include <stdlib.h>
                     36: # endif
                     37: #endif /* STDC_HEADERS */
                     38: #ifdef HAVE_STRING_H
                     39: # if defined(HAVE_MEMORY_H) && !defined(STDC_HEADERS)
                     40: #  include <memory.h>
                     41: # endif
                     42: # include <string.h>
                     43: #endif /* HAVE_STRING_H */
                     44: #ifdef HAVE_STRINGS_H
                     45: # include <strings.h>
                     46: #endif /* HAVE_STRINGS_H */
                     47: #ifdef HAVE_UNISTD_H
                     48: # include <unistd.h>
                     49: #endif /* HAVE_UNISTD_H */
                     50: #include <ctype.h>
                     51: #include <errno.h>
                     52: #include <fcntl.h>
                     53: #include <limits.h>
                     54: #include <signal.h>
                     55: #include <grp.h>
                     56: #include <pwd.h>
1.1.1.6 ! misho      57: #ifdef TIME_WITH_SYS_TIME
1.1       misho      58: # include <time.h>
                     59: #endif
                     60: #ifdef HAVE_LOGIN_CAP_H
                     61: # include <login_cap.h>
1.1.1.2   misho      62: # ifndef LOGIN_SETENV
                     63: #  define LOGIN_SETENV 0
                     64: # endif
1.1       misho      65: #endif
                     66: #ifdef HAVE_PROJECT_H
                     67: # include <project.h>
                     68: # include <sys/task.h>
                     69: #endif
                     70: #ifdef HAVE_SELINUX
                     71: # include <selinux/selinux.h>
                     72: #endif
                     73: #ifdef HAVE_SETAUTHDB
                     74: # include <usersec.h>
                     75: #endif /* HAVE_SETAUTHDB */
                     76: #if defined(HAVE_GETPRPWNAM) && defined(HAVE_SET_AUTH_PARAMETERS)
                     77: # ifdef __hpux
                     78: #  undef MAXINT
                     79: #  include <hpsecurity.h>
                     80: # else
                     81: #  include <sys/security.h>
                     82: # endif /* __hpux */
                     83: # include <prot.h>
                     84: #endif /* HAVE_GETPRPWNAM && HAVE_SET_AUTH_PARAMETERS */
                     85: 
1.1.1.5   misho      86: #include <sudo_usage.h>
1.1       misho      87: #include "sudo.h"
                     88: #include "sudo_plugin.h"
                     89: #include "sudo_plugin_int.h"
                     90: 
                     91: /*
                     92:  * Local variables
                     93:  */
                     94: struct plugin_container policy_plugin;
1.1.1.6 ! misho      95: struct plugin_container_list io_plugins = TAILQ_HEAD_INITIALIZER(io_plugins);
1.1       misho      96: struct user_details user_details;
1.1.1.5   misho      97: const char *list_user; /* extern for parse_args.c */
1.1.1.4   misho      98: static struct command_details command_details;
1.1.1.2   misho      99: static int sudo_mode;
1.1       misho     100: 
                    101: /*
                    102:  * Local functions
                    103:  */
                    104: static void fix_fds(void);
                    105: static void disable_coredumps(void);
1.1.1.2   misho     106: static void sudo_check_suid(const char *path);
1.1       misho     107: static char **get_user_info(struct user_details *);
                    108: static void command_info_to_details(char * const info[],
                    109:     struct command_details *details);
                    110: 
                    111: /* Policy plugin convenience functions. */
                    112: static int policy_open(struct plugin_container *plugin, char * const settings[],
                    113:     char * const user_info[], char * const user_env[]);
                    114: static void policy_close(struct plugin_container *plugin, int exit_status,
                    115:     int error);
                    116: static int policy_show_version(struct plugin_container *plugin, int verbose);
                    117: static int policy_check(struct plugin_container *plugin, int argc,
                    118:     char * const argv[], char *env_add[], char **command_info[],
                    119:     char **argv_out[], char **user_env_out[]);
                    120: static int policy_list(struct plugin_container *plugin, int argc,
                    121:     char * const argv[], int verbose, const char *list_user);
                    122: static int policy_validate(struct plugin_container *plugin);
                    123: static void policy_invalidate(struct plugin_container *plugin, int remove);
                    124: 
                    125: /* I/O log plugin convenience functions. */
                    126: static int iolog_open(struct plugin_container *plugin, char * const settings[],
                    127:     char * const user_info[], char * const command_details[],
                    128:     int argc, char * const argv[], char * const user_env[]);
                    129: static void iolog_close(struct plugin_container *plugin, int exit_status,
                    130:     int error);
                    131: static int iolog_show_version(struct plugin_container *plugin, int verbose);
1.1.1.2   misho     132: static void iolog_unlink(struct plugin_container *plugin);
1.1       misho     133: 
1.1.1.2   misho     134: #ifdef RLIMIT_CORE
1.1       misho     135: static struct rlimit corelimit;
1.1.1.5   misho     136: #endif
                    137: #ifdef __linux__
1.1       misho     138: static struct rlimit nproclimit;
                    139: #endif
                    140: 
1.1.1.4   misho     141: __dso_public int main(int argc, char *argv[], char *envp[]);
                    142: 
1.1       misho     143: int
                    144: main(int argc, char *argv[], char *envp[])
                    145: {
1.1.1.2   misho     146:     int nargc, ok, exitcode = 0;
1.1       misho     147:     char **nargv, **settings, **env_add;
                    148:     char **user_info, **command_info, **argv_out, **user_env_out;
                    149:     struct plugin_container *plugin, *next;
                    150:     sigset_t mask;
1.1.1.2   misho     151:     debug_decl(main, SUDO_DEBUG_MAIN)
                    152: 
1.1.1.4   misho     153:     os_init(argc, argv, envp);
1.1       misho     154: 
                    155:     setlocale(LC_ALL, "");
                    156:     bindtextdomain(PACKAGE_NAME, LOCALEDIR);
                    157:     textdomain(PACKAGE_NAME);
                    158: 
1.1.1.4   misho     159: #ifdef HAVE_TZSET
                    160:     (void) tzset();
                    161: #endif /* HAVE_TZSET */
                    162: 
1.1       misho     163:     /* Must be done before we do any password lookups */
                    164: #if defined(HAVE_GETPRPWNAM) && defined(HAVE_SET_AUTH_PARAMETERS)
                    165:     (void) set_auth_parameters(argc, argv);
                    166: # ifdef HAVE_INITPRIVS
                    167:     initprivs();
                    168: # endif
                    169: #endif /* HAVE_GETPRPWNAM && HAVE_SET_AUTH_PARAMETERS */
                    170: 
1.1.1.2   misho     171:     /* Make sure we are setuid root. */
                    172:     sudo_check_suid(argv[0]);
1.1       misho     173: 
1.1.1.4   misho     174:     /* Reset signal mask, save signal state and make sure fds 0-2 are open. */
1.1       misho     175:     (void) sigemptyset(&mask);
                    176:     (void) sigprocmask(SIG_SETMASK, &mask, NULL);
1.1.1.4   misho     177:     save_signals();
1.1       misho     178:     fix_fds();
                    179: 
1.1.1.3   misho     180:     /* Read sudo.conf. */
1.1.1.4   misho     181:     sudo_conf_read(NULL);
1.1.1.3   misho     182: 
1.1       misho     183:     /* Fill in user_info with user name, uid, cwd, etc. */
                    184:     memset(&user_details, 0, sizeof(user_details));
                    185:     user_info = get_user_info(&user_details);
                    186: 
1.1.1.2   misho     187:     /* Disable core dumps if not enabled in sudo.conf. */
                    188:     disable_coredumps();
                    189: 
1.1       misho     190:     /* Parse command line arguments. */
                    191:     sudo_mode = parse_args(argc, argv, &nargc, &nargv, &settings, &env_add);
1.1.1.2   misho     192:     sudo_debug_printf(SUDO_DEBUG_DEBUG, "sudo_mode %d", sudo_mode);
1.1       misho     193: 
                    194:     /* Print sudo version early, in case of plugin init failure. */
                    195:     if (ISSET(sudo_mode, MODE_VERSION)) {
                    196:        printf(_("Sudo version %s\n"), PACKAGE_VERSION);
                    197:        if (user_details.uid == ROOT_UID)
                    198:            (void) printf(_("Configure options: %s\n"), CONFIGURE_ARGS);
                    199:     }
                    200: 
1.1.1.2   misho     201:     /* Load plugins. */
                    202:     if (!sudo_load_plugins(&policy_plugin, &io_plugins))
1.1.1.6 ! misho     203:        fatalx(U_("fatal error, unable to load plugins"));
1.1       misho     204: 
                    205:     /* Open policy plugin. */
                    206:     ok = policy_open(&policy_plugin, settings, user_info, envp);
1.1.1.2   misho     207:     if (ok != 1) {
1.1       misho     208:        if (ok == -2)
                    209:            usage(1);
                    210:        else
1.1.1.6 ! misho     211:            fatalx(U_("unable to initialize policy plugin"));
1.1       misho     212:     }
                    213: 
1.1.1.4   misho     214:     init_signals();
                    215: 
1.1       misho     216:     switch (sudo_mode & MODE_MASK) {
                    217:        case MODE_VERSION:
                    218:            policy_show_version(&policy_plugin, !user_details.uid);
1.1.1.6 ! misho     219:            TAILQ_FOREACH(plugin, &io_plugins, entries) {
1.1       misho     220:                ok = iolog_open(plugin, settings, user_info, NULL,
                    221:                    nargc, nargv, envp);
1.1.1.4   misho     222:                if (ok != -1)
1.1       misho     223:                    iolog_show_version(plugin, !user_details.uid);
                    224:            }
                    225:            break;
                    226:        case MODE_VALIDATE:
                    227:        case MODE_VALIDATE|MODE_INVALIDATE:
                    228:            ok = policy_validate(&policy_plugin);
1.1.1.2   misho     229:            exit(ok != 1);
1.1       misho     230:        case MODE_KILL:
                    231:        case MODE_INVALIDATE:
                    232:            policy_invalidate(&policy_plugin, sudo_mode == MODE_KILL);
                    233:            exit(0);
                    234:            break;
                    235:        case MODE_CHECK:
                    236:        case MODE_CHECK|MODE_INVALIDATE:
                    237:        case MODE_LIST:
                    238:        case MODE_LIST|MODE_INVALIDATE:
                    239:            ok = policy_list(&policy_plugin, nargc, nargv,
                    240:                ISSET(sudo_mode, MODE_LONG_LIST), list_user);
1.1.1.2   misho     241:            exit(ok != 1);
1.1       misho     242:        case MODE_EDIT:
                    243:        case MODE_RUN:
                    244:            ok = policy_check(&policy_plugin, nargc, nargv, env_add,
                    245:                &command_info, &argv_out, &user_env_out);
1.1.1.2   misho     246:            sudo_debug_printf(SUDO_DEBUG_INFO, "policy plugin returns %d", ok);
                    247:            if (ok != 1) {
1.1       misho     248:                if (ok == -2)
                    249:                    usage(1);
                    250:                exit(1); /* plugin printed error message */
                    251:            }
                    252:            /* Open I/O plugins once policy plugin succeeds. */
1.1.1.6 ! misho     253:            TAILQ_FOREACH_SAFE(plugin, &io_plugins, entries, next) {
1.1       misho     254:                ok = iolog_open(plugin, settings, user_info,
                    255:                    command_info, nargc, nargv, envp);
                    256:                switch (ok) {
1.1.1.2   misho     257:                case 1:
1.1       misho     258:                    break;
1.1.1.2   misho     259:                case 0:
                    260:                    /* I/O plugin asked to be disabled, remove and free. */
                    261:                    iolog_unlink(plugin);
1.1       misho     262:                    break;
                    263:                case -2:
                    264:                    usage(1);
                    265:                    break;
                    266:                default:
1.1.1.6 ! misho     267:                    fatalx(U_("error initializing I/O plugin %s"),
1.1       misho     268:                        plugin->name);
                    269:                }
                    270:            }
1.1.1.2   misho     271:            /* Setup command details and run command/edit. */
1.1       misho     272:            command_info_to_details(command_info, &command_details);
                    273:            command_details.argv = argv_out;
                    274:            command_details.envp = user_env_out;
                    275:            if (ISSET(sudo_mode, MODE_BACKGROUND))
                    276:                SET(command_details.flags, CD_BACKGROUND);
1.1.1.2   misho     277:            /* Become full root (not just setuid) so user cannot kill us. */
1.1.1.4   misho     278:            if (setuid(ROOT_UID) == -1)
                    279:                warning("setuid(%d)", ROOT_UID);
1.1       misho     280:            /* Restore coredumpsize resource limit before running. */
1.1.1.2   misho     281: #ifdef RLIMIT_CORE
                    282:            if (sudo_conf_disable_coredump())
                    283:                (void) setrlimit(RLIMIT_CORE, &corelimit);
                    284: #endif /* RLIMIT_CORE */
1.1       misho     285:            if (ISSET(command_details.flags, CD_SUDOEDIT)) {
                    286:                exitcode = sudo_edit(&command_details);
                    287:            } else {
                    288:                exitcode = run_command(&command_details);
                    289:            }
                    290:            /* The close method was called by sudo_edit/run_command. */
                    291:            break;
                    292:        default:
1.1.1.6 ! misho     293:            fatalx(U_("unexpected sudo mode 0x%x"), sudo_mode);
1.1       misho     294:     }
1.1.1.2   misho     295:     sudo_debug_exit_int(__func__, __FILE__, __LINE__, sudo_debug_subsys, exitcode);                
1.1       misho     296:     exit(exitcode);
                    297: }
                    298: 
1.1.1.4   misho     299: int
                    300: os_init_common(int argc, char *argv[], char *envp[])
                    301: {
1.1.1.6 ! misho     302:     initprogname(argc > 0 ? argv[0] : "sudo");
        !           303: #ifdef STATIC_SUDOERS_PLUGIN
        !           304:     preload_static_symbols();
1.1.1.4   misho     305: #endif
                    306:     return 0;
                    307: }
                    308: 
1.1       misho     309: /*
                    310:  * Ensure that stdin, stdout and stderr are open; set to /dev/null if not.
                    311:  * Some operating systems do this automatically in the kernel or libc.
                    312:  */
                    313: static void
                    314: fix_fds(void)
                    315: {
                    316:     int miss[3], devnull = -1;
1.1.1.2   misho     317:     debug_decl(fix_fds, SUDO_DEBUG_UTIL)
1.1       misho     318: 
                    319:     /*
                    320:      * stdin, stdout and stderr must be open; set them to /dev/null
                    321:      * if they are closed.
                    322:      */
                    323:     miss[STDIN_FILENO] = fcntl(STDIN_FILENO, F_GETFL, 0) == -1;
                    324:     miss[STDOUT_FILENO] = fcntl(STDOUT_FILENO, F_GETFL, 0) == -1;
                    325:     miss[STDERR_FILENO] = fcntl(STDERR_FILENO, F_GETFL, 0) == -1;
                    326:     if (miss[STDIN_FILENO] || miss[STDOUT_FILENO] || miss[STDERR_FILENO]) {
                    327:        if ((devnull = open(_PATH_DEVNULL, O_RDWR, 0644)) == -1)
1.1.1.6 ! misho     328:            fatal(U_("unable to open %s"), _PATH_DEVNULL);
1.1       misho     329:        if (miss[STDIN_FILENO] && dup2(devnull, STDIN_FILENO) == -1)
1.1.1.4   misho     330:            fatal("dup2");
1.1       misho     331:        if (miss[STDOUT_FILENO] && dup2(devnull, STDOUT_FILENO) == -1)
1.1.1.4   misho     332:            fatal("dup2");
1.1       misho     333:        if (miss[STDERR_FILENO] && dup2(devnull, STDERR_FILENO) == -1)
1.1.1.4   misho     334:            fatal("dup2");
1.1       misho     335:        if (devnull > STDERR_FILENO)
                    336:            close(devnull);
                    337:     }
1.1.1.2   misho     338:     debug_return;
1.1       misho     339: }
                    340: 
                    341: /*
                    342:  * Allocate space for groups and fill in using getgrouplist()
1.1.1.4   misho     343:  * for when we cannot (or don't want to) use getgroups().
1.1       misho     344:  */
                    345: static int
1.1.1.4   misho     346: fill_group_list(struct user_details *ud, int system_maxgroups)
1.1       misho     347: {
1.1.1.4   misho     348:     int tries, rval = -1;
1.1.1.2   misho     349:     debug_decl(fill_group_list, SUDO_DEBUG_UTIL)
1.1       misho     350: 
                    351:     /*
1.1.1.4   misho     352:      * If user specified a max number of groups, use it, otherwise keep
                    353:      * trying getgrouplist() until we have enough room in the array.
1.1       misho     354:      */
1.1.1.4   misho     355:     ud->ngroups = sudo_conf_max_groups();
1.1.1.5   misho     356:     if (ud->ngroups > 0) {
1.1       misho     357:        ud->groups = emalloc2(ud->ngroups, sizeof(GETGROUPS_T));
1.1.1.4   misho     358:        /* No error on insufficient space if user specified max_groups. */
                    359:        (void)getgrouplist(ud->username, ud->gid, ud->groups, &ud->ngroups);
                    360:        rval = 0;
                    361:     } else {
                    362:        /*
                    363:         * It is possible to belong to more groups in the group database
                    364:         * than NGROUPS_MAX.  We start off with NGROUPS_MAX * 4 entries
                    365:         * and double this as needed.
                    366:         */
                    367:        ud->groups = NULL;
                    368:        ud->ngroups = system_maxgroups << 1;
                    369:        for (tries = 0; tries < 10 && rval == -1; tries++) {
                    370:            ud->ngroups <<= 1;
                    371:            efree(ud->groups);
                    372:            ud->groups = emalloc2(ud->ngroups, sizeof(GETGROUPS_T));
                    373:            rval = getgrouplist(ud->username, ud->gid, ud->groups, &ud->ngroups);
                    374:        }
1.1       misho     375:     }
1.1.1.2   misho     376:     debug_return_int(rval);
1.1       misho     377: }
                    378: 
                    379: static char *
                    380: get_user_groups(struct user_details *ud)
                    381: {
                    382:     char *cp, *gid_list = NULL;
                    383:     size_t glsize;
1.1.1.4   misho     384:     int i, len, maxgroups, group_source;
1.1.1.2   misho     385:     debug_decl(get_user_groups, SUDO_DEBUG_UTIL)
1.1       misho     386: 
1.1.1.4   misho     387: #if defined(HAVE_SYSCONF) && defined(_SC_NGROUPS_MAX)
                    388:     maxgroups = (int)sysconf(_SC_NGROUPS_MAX);
                    389:     if (maxgroups < 0)
                    390: #endif
                    391:        maxgroups = NGROUPS_MAX;
                    392: 
1.1       misho     393:     ud->groups = NULL;
1.1.1.4   misho     394:     group_source = sudo_conf_group_source();
                    395:     if (group_source != GROUP_SOURCE_DYNAMIC) {
                    396:        if ((ud->ngroups = getgroups(0, NULL)) > 0) {
                    397:            /* Use groups from kernel if not too many or source is static. */
                    398:            if (ud->ngroups < maxgroups || group_source == GROUP_SOURCE_STATIC) {
                    399:                ud->groups = emalloc2(ud->ngroups, sizeof(GETGROUPS_T));
                    400:                if (getgroups(ud->ngroups, ud->groups) < 0) {
                    401:                    efree(ud->groups);
                    402:                    ud->groups = NULL;
                    403:                }
                    404:            }
1.1       misho     405:        }
                    406:     }
                    407:     if (ud->groups == NULL) {
1.1.1.4   misho     408:        /*
                    409:         * Query group database if kernel list is too small or disabled.
                    410:         * Typically, this is because NFS can only support up to 16 groups.
                    411:         */
                    412:        if (fill_group_list(ud, maxgroups) == -1)
1.1.1.6 ! misho     413:            fatal(U_("unable to get group vector"));
1.1       misho     414:     }
                    415: 
                    416:     /*
                    417:      * Format group list as a comma-separated string of gids.
                    418:      */
                    419:     glsize = sizeof("groups=") - 1 + (ud->ngroups * (MAX_UID_T_LEN + 1));
                    420:     gid_list = emalloc(glsize);
                    421:     memcpy(gid_list, "groups=", sizeof("groups=") - 1);
                    422:     cp = gid_list + sizeof("groups=") - 1;
                    423:     for (i = 0; i < ud->ngroups; i++) {
                    424:        /* XXX - check rval */
                    425:        len = snprintf(cp, glsize - (cp - gid_list), "%s%u",
                    426:            i ? "," : "", (unsigned int)ud->groups[i]);
                    427:        cp += len;
                    428:     }
1.1.1.2   misho     429:     debug_return_str(gid_list);
1.1       misho     430: }
                    431: 
                    432: /*
                    433:  * Return user information as an array of name=value pairs.
                    434:  * and fill in struct user_details (which shares the same strings).
                    435:  */
                    436: static char **
                    437: get_user_info(struct user_details *ud)
                    438: {
1.1.1.4   misho     439:     char *cp, **user_info, cwd[PATH_MAX], host[HOST_NAME_MAX + 1];
1.1       misho     440:     struct passwd *pw;
1.1.1.2   misho     441:     int fd, i = 0;
                    442:     debug_decl(get_user_info, SUDO_DEBUG_UTIL)
1.1       misho     443: 
                    444:     /* XXX - bound check number of entries */
                    445:     user_info = emalloc2(32, sizeof(char *));
                    446: 
1.1.1.2   misho     447:     ud->pid = getpid();
                    448:     ud->ppid = getppid();
                    449:     ud->pgid = getpgid(0);
                    450:     ud->tcpgid = (pid_t)-1;
                    451:     fd = open(_PATH_TTY, O_RDWR|O_NOCTTY|O_NONBLOCK, 0);
                    452:     if (fd != -1) {
                    453:        ud->tcpgid = tcgetpgrp(fd);
                    454:        close(fd);
                    455:     }
                    456:     ud->sid = getsid(0);
                    457: 
1.1       misho     458:     ud->uid = getuid();
                    459:     ud->euid = geteuid();
                    460:     ud->gid = getgid();
                    461:     ud->egid = getegid();
                    462: 
                    463:     pw = getpwuid(ud->uid);
                    464:     if (pw == NULL)
1.1.1.6 ! misho     465:        fatalx(U_("unknown uid %u: who are you?"), (unsigned int)ud->uid);
1.1       misho     466: 
                    467:     user_info[i] = fmt_string("user", pw->pw_name);
                    468:     if (user_info[i] == NULL)
1.1.1.5   misho     469:        fatal(NULL);
1.1       misho     470:     ud->username = user_info[i] + sizeof("user=") - 1;
                    471: 
                    472:     /* Stash user's shell for use with the -s flag; don't pass to plugin. */
                    473:     if ((ud->shell = getenv("SHELL")) == NULL || ud->shell[0] == '\0') {
                    474:        ud->shell = pw->pw_shell[0] ? pw->pw_shell : _PATH_BSHELL;
                    475:     }
                    476:     ud->shell = estrdup(ud->shell);
                    477: 
1.1.1.2   misho     478:     easprintf(&user_info[++i], "pid=%d", (int)ud->pid);
                    479:     easprintf(&user_info[++i], "ppid=%d", (int)ud->ppid);
                    480:     easprintf(&user_info[++i], "pgid=%d", (int)ud->pgid);
                    481:     easprintf(&user_info[++i], "tcpgid=%d", (int)ud->tcpgid);
                    482:     easprintf(&user_info[++i], "sid=%d", (int)ud->sid);
                    483: 
1.1       misho     484:     easprintf(&user_info[++i], "uid=%u", (unsigned int)ud->uid);
                    485:     easprintf(&user_info[++i], "euid=%u", (unsigned int)ud->euid);
                    486:     easprintf(&user_info[++i], "gid=%u", (unsigned int)ud->gid);
                    487:     easprintf(&user_info[++i], "egid=%u", (unsigned int)ud->egid);
                    488: 
                    489:     if ((cp = get_user_groups(ud)) != NULL)
                    490:        user_info[++i] = cp;
                    491: 
                    492:     if (getcwd(cwd, sizeof(cwd)) != NULL) {
                    493:        user_info[++i] = fmt_string("cwd", cwd);
                    494:        if (user_info[i] == NULL)
1.1.1.5   misho     495:            fatal(NULL);
1.1       misho     496:        ud->cwd = user_info[i] + sizeof("cwd=") - 1;
                    497:     }
                    498: 
1.1.1.2   misho     499:     if ((cp = get_process_ttyname()) != NULL) {
1.1       misho     500:        user_info[++i] = fmt_string("tty", cp);
                    501:        if (user_info[i] == NULL)
1.1.1.5   misho     502:            fatal(NULL);
1.1       misho     503:        ud->tty = user_info[i] + sizeof("tty=") - 1;
1.1.1.2   misho     504:        efree(cp);
1.1       misho     505:     }
                    506: 
                    507:     if (gethostname(host, sizeof(host)) == 0)
                    508:        host[sizeof(host) - 1] = '\0';
                    509:     else
                    510:        strlcpy(host, "localhost", sizeof(host));
                    511:     user_info[++i] = fmt_string("host", host);
                    512:     if (user_info[i] == NULL)
1.1.1.5   misho     513:        fatal(NULL);
1.1       misho     514:     ud->host = user_info[i] + sizeof("host=") - 1;
                    515: 
                    516:     get_ttysize(&ud->ts_lines, &ud->ts_cols);
                    517:     easprintf(&user_info[++i], "lines=%d", ud->ts_lines);
                    518:     easprintf(&user_info[++i], "cols=%d", ud->ts_cols);
                    519: 
                    520:     user_info[++i] = NULL;
                    521: 
1.1.1.2   misho     522:     debug_return_ptr(user_info);
1.1       misho     523: }
                    524: 
                    525: /*
                    526:  * Convert a command_info array into a command_details structure.
                    527:  */
                    528: static void
                    529: command_info_to_details(char * const info[], struct command_details *details)
                    530: {
                    531:     int i;
1.1.1.5   misho     532:     id_t id;
1.1.1.6 ! misho     533:     char *cp;
1.1.1.5   misho     534:     const char *errstr;
1.1.1.2   misho     535:     debug_decl(command_info_to_details, SUDO_DEBUG_PCOMM)
1.1       misho     536: 
                    537:     memset(details, 0, sizeof(*details));
                    538:     details->closefrom = -1;
1.1.1.6 ! misho     539:     TAILQ_INIT(&details->preserved_fds);
1.1       misho     540: 
                    541: #define SET_STRING(s, n) \
                    542:     if (strncmp(s, info[i], sizeof(s) - 1) == 0 && info[i][sizeof(s) - 1]) { \
                    543:        details->n = info[i] + sizeof(s) - 1; \
                    544:        break; \
                    545:     }
                    546: 
1.1.1.2   misho     547:     sudo_debug_printf(SUDO_DEBUG_INFO, "command info from plugin:");
1.1       misho     548:     for (i = 0; info[i] != NULL; i++) {
1.1.1.2   misho     549:        sudo_debug_printf(SUDO_DEBUG_INFO, "    %d: %s", i, info[i]);
1.1       misho     550:        switch (info[i][0]) {
                    551:            case 'c':
                    552:                SET_STRING("chroot=", chroot)
                    553:                SET_STRING("command=", command)
                    554:                SET_STRING("cwd=", cwd)
                    555:                if (strncmp("closefrom=", info[i], sizeof("closefrom=") - 1) == 0) {
1.1.1.5   misho     556:                    cp = info[i] + sizeof("closefrom=") - 1;
1.1.1.6 ! misho     557:                    details->closefrom = strtonum(cp, 0, INT_MAX, &errstr);
        !           558:                    if (errstr != NULL)
        !           559:                        fatalx(U_("%s: %s"), info[i], U_(errstr));
1.1       misho     560:                    break;
                    561:                }
                    562:                break;
1.1.1.4   misho     563:            case 'e':
                    564:                if (strncmp("exec_background=", info[i], sizeof("exec_background=") - 1) == 0) {
                    565:                    if (atobool(info[i] + sizeof("exec_background=") - 1) == true)
                    566:                        SET(details->flags, CD_EXEC_BG);
                    567:                    break;
                    568:                }
                    569:                break;
1.1       misho     570:            case 'l':
                    571:                SET_STRING("login_class=", login_class)
                    572:                break;
                    573:            case 'n':
                    574:                if (strncmp("nice=", info[i], sizeof("nice=") - 1) == 0) {
1.1.1.5   misho     575:                    cp = info[i] + sizeof("nice=") - 1;
1.1.1.6 ! misho     576:                    details->priority = strtonum(cp, INT_MIN, INT_MAX, &errstr);
        !           577:                    if (errstr != NULL)
        !           578:                        fatalx(U_("%s: %s"), info[i], U_(errstr));
1.1.1.5   misho     579:                    SET(details->flags, CD_SET_PRIORITY);
1.1       misho     580:                    break;
                    581:                }
                    582:                if (strncmp("noexec=", info[i], sizeof("noexec=") - 1) == 0) {
1.1.1.2   misho     583:                    if (atobool(info[i] + sizeof("noexec=") - 1) == true)
1.1       misho     584:                        SET(details->flags, CD_NOEXEC);
                    585:                    break;
                    586:                }
                    587:                break;
                    588:            case 'p':
                    589:                if (strncmp("preserve_groups=", info[i], sizeof("preserve_groups=") - 1) == 0) {
1.1.1.2   misho     590:                    if (atobool(info[i] + sizeof("preserve_groups=") - 1) == true)
1.1       misho     591:                        SET(details->flags, CD_PRESERVE_GROUPS);
                    592:                    break;
                    593:                }
1.1.1.6 ! misho     594:                if (strncmp("preserve_fds=", info[i], sizeof("preserve_fds=") - 1) == 0) {
        !           595:                    parse_preserved_fds(&details->preserved_fds,
        !           596:                        info[i] + sizeof("preserve_fds=") - 1);
        !           597:                    break;
        !           598:                }
1.1       misho     599:                break;
                    600:            case 'r':
                    601:                if (strncmp("runas_egid=", info[i], sizeof("runas_egid=") - 1) == 0) {
                    602:                    cp = info[i] + sizeof("runas_egid=") - 1;
1.1.1.5   misho     603:                    id = atoid(cp, NULL, NULL, &errstr);
                    604:                    if (errstr != NULL)
1.1.1.6 ! misho     605:                        fatalx(U_("%s: %s"), info[i], U_(errstr));
1.1.1.5   misho     606:                    details->egid = (gid_t)id;
                    607:                    SET(details->flags, CD_SET_EGID);
1.1       misho     608:                    break;
                    609:                }
                    610:                if (strncmp("runas_euid=", info[i], sizeof("runas_euid=") - 1) == 0) {
                    611:                    cp = info[i] + sizeof("runas_euid=") - 1;
1.1.1.5   misho     612:                    id = atoid(cp, NULL, NULL, &errstr);
                    613:                    if (errstr != NULL)
1.1.1.6 ! misho     614:                        fatalx(U_("%s: %s"), info[i], U_(errstr));
1.1.1.5   misho     615:                    details->euid = (uid_t)id;
                    616:                    SET(details->flags, CD_SET_EUID);
1.1       misho     617:                    break;
                    618:                }
                    619:                if (strncmp("runas_gid=", info[i], sizeof("runas_gid=") - 1) == 0) {
                    620:                    cp = info[i] + sizeof("runas_gid=") - 1;
1.1.1.5   misho     621:                    id = atoid(cp, NULL, NULL, &errstr);
                    622:                    if (errstr != NULL)
1.1.1.6 ! misho     623:                        fatalx(U_("%s: %s"), info[i], U_(errstr));
1.1.1.5   misho     624:                    details->gid = (gid_t)id;
                    625:                    SET(details->flags, CD_SET_GID);
1.1       misho     626:                    break;
                    627:                }
                    628:                if (strncmp("runas_groups=", info[i], sizeof("runas_groups=") - 1) == 0) {
1.1.1.5   misho     629:                    /* parse_gid_list() will call fatalx() on error. */
1.1       misho     630:                    cp = info[i] + sizeof("runas_groups=") - 1;
1.1.1.5   misho     631:                    details->ngroups = parse_gid_list(cp, NULL, &details->groups);
1.1       misho     632:                    break;
                    633:                }
                    634:                if (strncmp("runas_uid=", info[i], sizeof("runas_uid=") - 1) == 0) {
                    635:                    cp = info[i] + sizeof("runas_uid=") - 1;
1.1.1.5   misho     636:                    id = atoid(cp, NULL, NULL, &errstr);
                    637:                    if (errstr != NULL)
1.1.1.6 ! misho     638:                        fatalx(U_("%s: %s"), info[i], U_(errstr));
1.1.1.5   misho     639:                    details->uid = (uid_t)id;
                    640:                    SET(details->flags, CD_SET_UID);
1.1       misho     641:                    break;
                    642:                }
1.1.1.3   misho     643: #ifdef HAVE_PRIV_SET
                    644:                if (strncmp("runas_privs=", info[i], sizeof("runas_privs=") - 1) == 0) {
                    645:                     const char *endp;
                    646:                    cp = info[i] + sizeof("runas_privs=") - 1;
1.1.1.5   misho     647:                    if (*cp != '\0') {
                    648:                        details->privs = priv_str_to_set(cp, ",", &endp);
                    649:                        if (details->privs == NULL)
1.1.1.3   misho     650:                            warning("invalid runas_privs %s", endp);
1.1.1.5   misho     651:                    }
                    652:                    break;
1.1.1.3   misho     653:                }
                    654:                if (strncmp("runas_limitprivs=", info[i], sizeof("runas_limitprivs=") - 1) == 0) {
                    655:                     const char *endp;
                    656:                    cp = info[i] + sizeof("runas_limitprivs=") - 1;
1.1.1.5   misho     657:                    if (*cp != '\0') {
                    658:                        details->limitprivs = priv_str_to_set(cp, ",", &endp);
                    659:                        if (details->limitprivs == NULL)
1.1.1.3   misho     660:                            warning("invalid runas_limitprivs %s", endp);
1.1.1.5   misho     661:                    }
                    662:                    break;
1.1.1.3   misho     663:                }
                    664: #endif /* HAVE_PRIV_SET */
1.1       misho     665:                break;
                    666:            case 's':
                    667:                SET_STRING("selinux_role=", selinux_role)
                    668:                SET_STRING("selinux_type=", selinux_type)
                    669:                if (strncmp("set_utmp=", info[i], sizeof("set_utmp=") - 1) == 0) {
1.1.1.2   misho     670:                    if (atobool(info[i] + sizeof("set_utmp=") - 1) == true)
1.1       misho     671:                        SET(details->flags, CD_SET_UTMP);
                    672:                    break;
                    673:                }
                    674:                if (strncmp("sudoedit=", info[i], sizeof("sudoedit=") - 1) == 0) {
1.1.1.2   misho     675:                    if (atobool(info[i] + sizeof("sudoedit=") - 1) == true)
1.1       misho     676:                        SET(details->flags, CD_SUDOEDIT);
                    677:                    break;
                    678:                }
                    679:                break;
                    680:            case 't':
                    681:                if (strncmp("timeout=", info[i], sizeof("timeout=") - 1) == 0) {
1.1.1.5   misho     682:                    cp = info[i] + sizeof("timeout=") - 1;
1.1.1.6 ! misho     683:                    details->timeout = strtonum(cp, 0, INT_MAX, &errstr);
        !           684:                    if (errstr != NULL)
        !           685:                        fatalx(U_("%s: %s"), info[i], U_(errstr));
1.1.1.5   misho     686:                    SET(details->flags, CD_SET_TIMEOUT);
1.1       misho     687:                    break;
                    688:                }
                    689:                break;
                    690:            case 'u':
                    691:                if (strncmp("umask=", info[i], sizeof("umask=") - 1) == 0) {
1.1.1.5   misho     692:                    cp = info[i] + sizeof("umask=") - 1;
1.1.1.6 ! misho     693:                    details->umask = atomode(cp, &errstr);
        !           694:                    if (errstr != NULL)
        !           695:                        fatalx(U_("%s: %s"), info[i], U_(errstr));
1.1.1.5   misho     696:                    SET(details->flags, CD_SET_UMASK);
1.1       misho     697:                    break;
                    698:                }
                    699:                if (strncmp("use_pty=", info[i], sizeof("use_pty=") - 1) == 0) {
1.1.1.2   misho     700:                    if (atobool(info[i] + sizeof("use_pty=") - 1) == true)
1.1       misho     701:                        SET(details->flags, CD_USE_PTY);
                    702:                    break;
                    703:                }
                    704:                SET_STRING("utmp_user=", utmp_user)
                    705:                break;
                    706:        }
                    707:     }
                    708: 
                    709:     if (!ISSET(details->flags, CD_SET_EUID))
                    710:        details->euid = details->uid;
                    711: 
1.1.1.2   misho     712: #ifdef HAVE_SETAUTHDB
                    713:     aix_setauthdb(IDtouser(details->euid));
                    714: #endif
                    715:     details->pw = getpwuid(details->euid);
                    716:     if (details->pw != NULL && (details->pw = pw_dup(details->pw)) == NULL)
1.1.1.5   misho     717:        fatal(NULL);
1.1.1.2   misho     718: #ifdef HAVE_SETAUTHDB
                    719:     aix_restoreauthdb();
                    720: #endif
                    721: 
1.1       misho     722: #ifdef HAVE_SELINUX
                    723:     if (details->selinux_role != NULL && is_selinux_enabled() > 0)
                    724:        SET(details->flags, CD_RBAC_ENABLED);
                    725: #endif
1.1.1.2   misho     726:     debug_return;
                    727: }
                    728: 
                    729: static void
1.1.1.5   misho     730: sudo_check_suid(const char *sudo)
1.1.1.2   misho     731: {
1.1.1.5   misho     732:     char pathbuf[PATH_MAX];
1.1.1.2   misho     733:     struct stat sb;
1.1.1.5   misho     734:     bool qualified;
1.1.1.2   misho     735:     debug_decl(sudo_check_suid, SUDO_DEBUG_PCOMM)
                    736: 
                    737:     if (geteuid() != 0) {
1.1.1.5   misho     738:        /* Search for sudo binary in PATH if not fully qualified. */
                    739:        qualified = strchr(sudo, '/') != NULL;
                    740:        if (!qualified) {
                    741:            char *path = getenv_unhooked("PATH");
                    742:            if (path != NULL) {
                    743:                int len;
                    744:                char *cp, *colon;
                    745: 
                    746:                cp = path = estrdup(path);
                    747:                do {
                    748:                    if ((colon = strchr(cp, ':')))
                    749:                        *colon = '\0';
                    750:                    len = snprintf(pathbuf, sizeof(pathbuf), "%s/%s", cp, sudo);
1.1.1.6 ! misho     751:                    if (len <= 0 || (size_t)len >= sizeof(pathbuf))
1.1.1.5   misho     752:                        continue;
                    753:                    if (access(pathbuf, X_OK) == 0) {
                    754:                        sudo = pathbuf;
                    755:                        qualified = true;
                    756:                        break;
                    757:                    }
                    758:                    cp = colon + 1;
                    759:                } while (colon);
                    760:                efree(path);
                    761:            }
                    762:        }
                    763: 
                    764:        if (qualified && stat(sudo, &sb) == 0) {
1.1.1.2   misho     765:            /* Try to determine why sudo was not running as root. */
                    766:            if (sb.st_uid != ROOT_UID || !ISSET(sb.st_mode, S_ISUID)) {
1.1.1.4   misho     767:                fatalx(
1.1.1.6 ! misho     768:                    U_("%s must be owned by uid %d and have the setuid bit set"),
1.1.1.5   misho     769:                    sudo, ROOT_UID);
1.1.1.2   misho     770:            } else {
1.1.1.6 ! misho     771:                fatalx(U_("effective uid is not %d, is %s on a file system "
1.1.1.2   misho     772:                    "with the 'nosuid' option set or an NFS file system without"
1.1.1.5   misho     773:                    " root privileges?"), ROOT_UID, sudo);
1.1.1.2   misho     774:            }
                    775:        } else {
1.1.1.4   misho     776:            fatalx(
1.1.1.6 ! misho     777:                U_("effective uid is not %d, is sudo installed setuid root?"),
1.1.1.2   misho     778:                ROOT_UID);
                    779:        }
                    780:     }
                    781:     debug_return;
1.1       misho     782: }
                    783: 
                    784: /*
                    785:  * Disable core dumps to avoid dropping a core with user password in it.
                    786:  * We will reset this limit before executing the command.
                    787:  * Not all operating systems disable core dumps for setuid processes.
                    788:  */
                    789: static void
                    790: disable_coredumps(void)
                    791: {
1.1.1.5   misho     792: #if defined(RLIMIT_CORE)
1.1       misho     793:     struct rlimit rl;
1.1.1.2   misho     794:     debug_decl(disable_coredumps, SUDO_DEBUG_UTIL)
1.1       misho     795: 
                    796:     /*
1.1.1.2   misho     797:      * Turn off core dumps?
1.1       misho     798:      */
1.1.1.2   misho     799:     if (sudo_conf_disable_coredump()) {
                    800:        (void) getrlimit(RLIMIT_CORE, &corelimit);
                    801:        memcpy(&rl, &corelimit, sizeof(struct rlimit));
                    802:        rl.rlim_cur = 0;
                    803:        (void) setrlimit(RLIMIT_CORE, &rl);
                    804:     }
1.1.1.5   misho     805:     debug_return;
1.1.1.2   misho     806: #endif /* RLIMIT_CORE */
1.1.1.5   misho     807: }
                    808: 
                    809: /*
                    810:  * Unlimit the number of processes since Linux's setuid() will
                    811:  * apply resource limits when changing uid and return EAGAIN if
                    812:  * nproc would be exceeded by the uid switch.
                    813:  */
                    814: static void
                    815: unlimit_nproc(void)
                    816: {
                    817: #ifdef __linux__
                    818:     struct rlimit rl;
                    819:     debug_decl(unlimit_nproc, SUDO_DEBUG_UTIL)
                    820: 
                    821:     (void) getrlimit(RLIMIT_NPROC, &nproclimit);
                    822:     rl.rlim_cur = rl.rlim_max = RLIM_INFINITY;
                    823:     if (setrlimit(RLIMIT_NPROC, &rl) != 0) {
                    824:        memcpy(&rl, &nproclimit, sizeof(struct rlimit));
                    825:        rl.rlim_cur = rl.rlim_max;
                    826:        (void)setrlimit(RLIMIT_NPROC, &rl);
                    827:     }
                    828:     debug_return;
                    829: #endif /* __linux__ */
                    830: }
                    831: 
                    832: /*
                    833:  * Restore saved value of RLIMIT_NPROC.
                    834:  */
                    835: static void
                    836: restore_nproc(void)
                    837: {
                    838: #ifdef __linux__
                    839:     debug_decl(restore_nproc, SUDO_DEBUG_UTIL)
                    840: 
                    841:     (void) setrlimit(RLIMIT_NPROC, &nproclimit);
                    842: 
1.1.1.2   misho     843:     debug_return;
1.1.1.5   misho     844: #endif /* __linux__ */
1.1       misho     845: }
                    846: 
                    847: /*
                    848:  * Setup the execution environment immediately prior to the call to execve()
1.1.1.2   misho     849:  * Returns true on success and false on failure.
1.1       misho     850:  */
1.1.1.2   misho     851: bool
1.1       misho     852: exec_setup(struct command_details *details, const char *ptyname, int ptyfd)
                    853: {
1.1.1.2   misho     854:     bool rval = false;
                    855:     debug_decl(exec_setup, SUDO_DEBUG_EXEC)
1.1       misho     856: 
                    857: #ifdef HAVE_SELINUX
                    858:     if (ISSET(details->flags, CD_RBAC_ENABLED)) {
                    859:        if (selinux_setup(details->selinux_role, details->selinux_type,
                    860:            ptyname ? ptyname : user_details.tty, ptyfd) == -1)
                    861:            goto done;
                    862:     }
                    863: #endif
                    864: 
1.1.1.2   misho     865:     if (details->pw != NULL) {
1.1       misho     866: #ifdef HAVE_PROJECT_H
1.1.1.2   misho     867:        set_project(details->pw);
1.1       misho     868: #endif
1.1.1.3   misho     869: #ifdef HAVE_PRIV_SET
1.1.1.4   misho     870:        if (details->privs != NULL) {
                    871:            if (setppriv(PRIV_SET, PRIV_INHERITABLE, details->privs) != 0) {
                    872:                warning("unable to set privileges");
                    873:                goto done;
                    874:            }
1.1.1.3   misho     875:        }
1.1.1.4   misho     876:        if (details->limitprivs != NULL) {
                    877:            if (setppriv(PRIV_SET, PRIV_LIMIT, details->limitprivs) != 0) {
                    878:                warning("unable to set limit privileges");
                    879:                goto done;
                    880:            }
                    881:        } else if (details->privs != NULL) {
                    882:            if (setppriv(PRIV_SET, PRIV_LIMIT, details->privs) != 0) {
                    883:                warning("unable to set limit privileges");
                    884:                goto done;
                    885:            }
1.1.1.3   misho     886:        }
                    887: #endif /* HAVE_PRIV_SET */
                    888: 
1.1       misho     889: #ifdef HAVE_GETUSERATTR
1.1.1.2   misho     890:        aix_prep_user(details->pw->pw_name, ptyname ? ptyname : user_details.tty);
1.1       misho     891: #endif
                    892: #ifdef HAVE_LOGIN_CAP_H
                    893:        if (details->login_class) {
                    894:            int flags;
                    895:            login_cap_t *lc;
                    896: 
                    897:            /*
1.1.1.2   misho     898:             * We only use setusercontext() to set the nice value and rlimits
                    899:             * unless this is a login shell (sudo -i).
1.1       misho     900:             */
                    901:            lc = login_getclass((char *)details->login_class);
                    902:            if (!lc) {
1.1.1.6 ! misho     903:                warningx(U_("unknown login class %s"), details->login_class);
1.1       misho     904:                errno = ENOENT;
                    905:                goto done;
                    906:            }
1.1.1.2   misho     907:            if (ISSET(sudo_mode, MODE_LOGIN_SHELL)) {
                    908:                /* Set everything except user, group and login name. */
                    909:                flags = LOGIN_SETALL;
                    910:                CLR(flags, LOGIN_SETGROUP|LOGIN_SETLOGIN|LOGIN_SETUSER|LOGIN_SETENV|LOGIN_SETPATH);
                    911:                CLR(details->flags, CD_SET_UMASK); /* LOGIN_UMASK instead */
                    912:            } else {
                    913:                flags = LOGIN_SETRESOURCES|LOGIN_SETPRIORITY;
                    914:            }
                    915:            if (setusercontext(lc, details->pw, details->pw->pw_uid, flags)) {
1.1.1.6 ! misho     916:                warning(U_("unable to set user context"));
1.1.1.5   misho     917:                if (details->pw->pw_uid != ROOT_UID)
1.1       misho     918:                    goto done;
                    919:            }
                    920:        }
                    921: #endif /* HAVE_LOGIN_CAP_H */
                    922:     }
                    923: 
                    924:     /*
                    925:      * Set groups, including supplementary group vector.
                    926:      */
1.1.1.2   misho     927:     if (!ISSET(details->flags, CD_PRESERVE_GROUPS)) {
                    928:        if (details->ngroups >= 0) {
                    929:            if (sudo_setgroups(details->ngroups, details->groups) < 0) {
1.1.1.6 ! misho     930:                warning(U_("unable to set supplementary group IDs"));
1.1.1.2   misho     931:                goto done;
                    932:            }
                    933:        }
                    934:     }
1.1       misho     935: #ifdef HAVE_SETEUID
                    936:     if (ISSET(details->flags, CD_SET_EGID) && setegid(details->egid)) {
1.1.1.6 ! misho     937:        warning(U_("unable to set effective gid to runas gid %u"),
1.1       misho     938:            (unsigned int)details->egid);
                    939:        goto done;
                    940:     }
                    941: #endif
                    942:     if (ISSET(details->flags, CD_SET_GID) && setgid(details->gid)) {
1.1.1.6 ! misho     943:        warning(U_("unable to set gid to runas gid %u"),
1.1       misho     944:            (unsigned int)details->gid);
                    945:        goto done;
                    946:     }
                    947: 
                    948:     if (ISSET(details->flags, CD_SET_PRIORITY)) {
                    949:        if (setpriority(PRIO_PROCESS, 0, details->priority) != 0) {
1.1.1.6 ! misho     950:            warning(U_("unable to set process priority"));
1.1       misho     951:            goto done;
                    952:        }
                    953:     }
                    954:     if (ISSET(details->flags, CD_SET_UMASK))
                    955:        (void) umask(details->umask);
                    956:     if (details->chroot) {
                    957:        if (chroot(details->chroot) != 0 || chdir("/") != 0) {
1.1.1.6 ! misho     958:            warning(U_("unable to change root to %s"), details->chroot);
1.1       misho     959:            goto done;
                    960:        }
                    961:     }
                    962: 
1.1.1.5   misho     963:     /* 
                    964:      * Unlimit the number of processes since Linux's setuid() will
                    965:      * return EAGAIN if RLIMIT_NPROC would be exceeded by the uid switch.
                    966:      */
                    967:     unlimit_nproc();
                    968: 
1.1       misho     969: #ifdef HAVE_SETRESUID
                    970:     if (setresuid(details->uid, details->euid, details->euid) != 0) {
1.1.1.6 ! misho     971:        warning(U_("unable to change to runas uid (%u, %u)"), details->uid,
1.1       misho     972:            details->euid);
                    973:        goto done;
                    974:     }
1.1.1.6 ! misho     975: #elif defined(HAVE_SETREUID)
1.1       misho     976:     if (setreuid(details->uid, details->euid) != 0) {
1.1.1.6 ! misho     977:        warning(U_("unable to change to runas uid (%u, %u)"),
1.1       misho     978:            (unsigned int)details->uid, (unsigned int)details->euid);
                    979:        goto done;
                    980:     }
                    981: #else
                    982:     if (seteuid(details->euid) != 0 || setuid(details->euid) != 0) {
1.1.1.6 ! misho     983:        warning(U_("unable to change to runas uid (%u, %u)"), details->uid,
1.1       misho     984:            details->euid);
                    985:        goto done;
                    986:     }
                    987: #endif /* !HAVE_SETRESUID && !HAVE_SETREUID */
                    988: 
1.1.1.5   misho     989:     /* Restore previous value of RLIMIT_NPROC. */
                    990:     restore_nproc();
                    991: 
1.1       misho     992:     /*
                    993:      * Only change cwd if we have chroot()ed or the policy modules
                    994:      * specifies a different cwd.  Must be done after uid change.
                    995:      */
                    996:     if (details->cwd) {
                    997:        if (details->chroot || strcmp(details->cwd, user_details.cwd) != 0) {
                    998:            /* Note: cwd is relative to the new root, if any. */
                    999:            if (chdir(details->cwd) != 0) {
1.1.1.6 ! misho    1000:                warning(U_("unable to change directory to %s"), details->cwd);
1.1       misho    1001:                goto done;
                   1002:            }
                   1003:        }
                   1004:     }
                   1005: 
1.1.1.2   misho    1006:     rval = true;
1.1       misho    1007: 
                   1008: done:
1.1.1.2   misho    1009:     debug_return_bool(rval);
1.1       misho    1010: }
                   1011: 
                   1012: /*
                   1013:  * Run the command and wait for it to complete.
                   1014:  */
                   1015: int
                   1016: run_command(struct command_details *details)
                   1017: {
                   1018:     struct plugin_container *plugin;
                   1019:     struct command_status cstat;
                   1020:     int exitcode = 1;
1.1.1.2   misho    1021:     debug_decl(run_command, SUDO_DEBUG_EXEC)
1.1       misho    1022: 
                   1023:     cstat.type = CMD_INVALID;
                   1024:     cstat.val = 0;
                   1025: 
1.1.1.2   misho    1026:     sudo_execute(details, &cstat);
1.1       misho    1027: 
                   1028:     switch (cstat.type) {
                   1029:     case CMD_ERRNO:
                   1030:        /* exec_setup() or execve() returned an error. */
1.1.1.2   misho    1031:        sudo_debug_printf(SUDO_DEBUG_DEBUG,
                   1032:            "calling policy close with errno %d", cstat.val);
1.1       misho    1033:        policy_close(&policy_plugin, 0, cstat.val);
1.1.1.6 ! misho    1034:        TAILQ_FOREACH(plugin, &io_plugins, entries) {
1.1.1.2   misho    1035:            sudo_debug_printf(SUDO_DEBUG_DEBUG,
                   1036:                "calling I/O close with errno %d", cstat.val);
1.1       misho    1037:            iolog_close(plugin, 0, cstat.val);
                   1038:        }
                   1039:        exitcode = 1;
                   1040:        break;
                   1041:     case CMD_WSTATUS:
                   1042:        /* Command ran, exited or was killed. */
1.1.1.2   misho    1043:        sudo_debug_printf(SUDO_DEBUG_DEBUG,
                   1044:            "calling policy close with wait status %d", cstat.val);
1.1       misho    1045:        policy_close(&policy_plugin, cstat.val, 0);
1.1.1.6 ! misho    1046:        TAILQ_FOREACH(plugin, &io_plugins, entries) {
1.1.1.2   misho    1047:            sudo_debug_printf(SUDO_DEBUG_DEBUG,
                   1048:                "calling I/O close with wait status %d", cstat.val);
1.1       misho    1049:            iolog_close(plugin, cstat.val, 0);
                   1050:        }
                   1051:        if (WIFEXITED(cstat.val))
                   1052:            exitcode = WEXITSTATUS(cstat.val);
                   1053:        else if (WIFSIGNALED(cstat.val))
                   1054:            exitcode = WTERMSIG(cstat.val) | 128;
                   1055:        break;
                   1056:     default:
1.1.1.6 ! misho    1057:        warningx(U_("unexpected child termination condition: %d"), cstat.type);
1.1       misho    1058:        break;
                   1059:     }
1.1.1.2   misho    1060:     debug_return_int(exitcode);
1.1       misho    1061: }
                   1062: 
                   1063: static int
                   1064: policy_open(struct plugin_container *plugin, char * const settings[],
                   1065:     char * const user_info[], char * const user_env[])
                   1066: {
1.1.1.2   misho    1067:     int rval;
                   1068:     debug_decl(policy_open, SUDO_DEBUG_PCOMM)
                   1069: 
                   1070:     /*
                   1071:      * Backwards compatibility for older API versions
                   1072:      */
                   1073:     switch (plugin->u.generic->version) {
                   1074:     case SUDO_API_MKVERSION(1, 0):
                   1075:     case SUDO_API_MKVERSION(1, 1):
                   1076:        rval = plugin->u.policy_1_0->open(plugin->u.io_1_0->version,
                   1077:            sudo_conversation, _sudo_printf, settings, user_info, user_env);
                   1078:        break;
                   1079:     default:
                   1080:        rval = plugin->u.policy->open(SUDO_API_VERSION, sudo_conversation,
                   1081:            _sudo_printf, settings, user_info, user_env, plugin->options);
                   1082:     }
                   1083: 
                   1084:     debug_return_bool(rval);
1.1       misho    1085: }
                   1086: 
                   1087: static void
                   1088: policy_close(struct plugin_container *plugin, int exit_status, int error)
                   1089: {
1.1.1.2   misho    1090:     debug_decl(policy_close, SUDO_DEBUG_PCOMM)
1.1.1.4   misho    1091:     if (plugin->u.policy->close != NULL)
                   1092:        plugin->u.policy->close(exit_status, error);
                   1093:     else
1.1.1.6 ! misho    1094:        warning(U_("unable to execute %s"), command_details.command);
1.1.1.2   misho    1095:     debug_return;
1.1       misho    1096: }
                   1097: 
                   1098: static int
                   1099: policy_show_version(struct plugin_container *plugin, int verbose)
                   1100: {
1.1.1.2   misho    1101:     debug_decl(policy_show_version, SUDO_DEBUG_PCOMM)
1.1.1.4   misho    1102:     if (plugin->u.policy->show_version == NULL)
                   1103:        debug_return_bool(true);
1.1.1.2   misho    1104:     debug_return_bool(plugin->u.policy->show_version(verbose));
1.1       misho    1105: }
                   1106: 
                   1107: static int
                   1108: policy_check(struct plugin_container *plugin, int argc, char * const argv[],
                   1109:     char *env_add[], char **command_info[], char **argv_out[],
                   1110:     char **user_env_out[])
                   1111: {
1.1.1.2   misho    1112:     debug_decl(policy_check, SUDO_DEBUG_PCOMM)
1.1.1.4   misho    1113:     if (plugin->u.policy->check_policy == NULL) {
1.1.1.6 ! misho    1114:        fatalx(U_("policy plugin %s is missing the `check_policy' method"),
1.1.1.4   misho    1115:            plugin->name);
                   1116:     }
1.1.1.2   misho    1117:     debug_return_bool(plugin->u.policy->check_policy(argc, argv, env_add,
                   1118:        command_info, argv_out, user_env_out));
1.1       misho    1119: }
                   1120: 
                   1121: static int
                   1122: policy_list(struct plugin_container *plugin, int argc, char * const argv[],
                   1123:     int verbose, const char *list_user)
                   1124: {
1.1.1.2   misho    1125:     debug_decl(policy_list, SUDO_DEBUG_PCOMM)
1.1       misho    1126:     if (plugin->u.policy->list == NULL) {
1.1.1.6 ! misho    1127:        warningx(U_("policy plugin %s does not support listing privileges"),
1.1       misho    1128:            plugin->name);
1.1.1.2   misho    1129:        debug_return_bool(false);
1.1       misho    1130:     }
1.1.1.2   misho    1131:     debug_return_bool(plugin->u.policy->list(argc, argv, verbose, list_user));
1.1       misho    1132: }
                   1133: 
                   1134: static int
                   1135: policy_validate(struct plugin_container *plugin)
                   1136: {
1.1.1.2   misho    1137:     debug_decl(policy_validate, SUDO_DEBUG_PCOMM)
1.1       misho    1138:     if (plugin->u.policy->validate == NULL) {
1.1.1.6 ! misho    1139:        warningx(U_("policy plugin %s does not support the -v option"),
1.1       misho    1140:            plugin->name);
1.1.1.2   misho    1141:        debug_return_bool(false);
1.1       misho    1142:     }
1.1.1.2   misho    1143:     debug_return_bool(plugin->u.policy->validate());
1.1       misho    1144: }
                   1145: 
                   1146: static void
                   1147: policy_invalidate(struct plugin_container *plugin, int remove)
                   1148: {
1.1.1.2   misho    1149:     debug_decl(policy_invalidate, SUDO_DEBUG_PCOMM)
1.1       misho    1150:     if (plugin->u.policy->invalidate == NULL) {
1.1.1.6 ! misho    1151:        fatalx(U_("policy plugin %s does not support the -k/-K options"),
1.1       misho    1152:            plugin->name);
                   1153:     }
                   1154:     plugin->u.policy->invalidate(remove);
1.1.1.2   misho    1155:     debug_return;
1.1       misho    1156: }
                   1157: 
1.1.1.2   misho    1158: int
                   1159: policy_init_session(struct command_details *details)
1.1       misho    1160: {
1.1.1.2   misho    1161:     int rval = true;
                   1162:     debug_decl(policy_init_session, SUDO_DEBUG_PCOMM)
                   1163: 
                   1164:     if (policy_plugin.u.policy->init_session) {
                   1165:        /*
                   1166:         * Backwards compatibility for older API versions
                   1167:         */
                   1168:        switch (policy_plugin.u.generic->version) {
                   1169:        case SUDO_API_MKVERSION(1, 0):
                   1170:        case SUDO_API_MKVERSION(1, 1):
                   1171:            rval = policy_plugin.u.policy_1_0->init_session(details->pw);
                   1172:            break;
                   1173:        default:
                   1174:            rval = policy_plugin.u.policy->init_session(details->pw,
                   1175:                &details->envp);
                   1176:        }
                   1177:     }
                   1178:     debug_return_bool(rval);
1.1       misho    1179: }
                   1180: 
                   1181: static int
                   1182: iolog_open(struct plugin_container *plugin, char * const settings[],
                   1183:     char * const user_info[], char * const command_info[],
                   1184:     int argc, char * const argv[], char * const user_env[])
                   1185: {
                   1186:     int rval;
1.1.1.2   misho    1187:     debug_decl(iolog_open, SUDO_DEBUG_PCOMM)
1.1       misho    1188: 
                   1189:     /*
1.1.1.2   misho    1190:      * Backwards compatibility for older API versions
1.1       misho    1191:      */
                   1192:     switch (plugin->u.generic->version) {
                   1193:     case SUDO_API_MKVERSION(1, 0):
                   1194:        rval = plugin->u.io_1_0->open(plugin->u.io_1_0->version,
                   1195:            sudo_conversation, _sudo_printf, settings, user_info, argc, argv,
                   1196:            user_env);
                   1197:        break;
1.1.1.2   misho    1198:     case SUDO_API_MKVERSION(1, 1):
                   1199:        rval = plugin->u.io_1_1->open(plugin->u.io_1_1->version,
                   1200:            sudo_conversation, _sudo_printf, settings, user_info,
                   1201:            command_info, argc, argv, user_env);
                   1202:        break;
1.1       misho    1203:     default:
                   1204:        rval = plugin->u.io->open(SUDO_API_VERSION, sudo_conversation,
1.1.1.2   misho    1205:            _sudo_printf, settings, user_info, command_info,
                   1206:            argc, argv, user_env, plugin->options);
1.1       misho    1207:     }
1.1.1.2   misho    1208:     debug_return_bool(rval);
1.1       misho    1209: }
                   1210: 
                   1211: static void
                   1212: iolog_close(struct plugin_container *plugin, int exit_status, int error)
                   1213: {
1.1.1.2   misho    1214:     debug_decl(iolog_close, SUDO_DEBUG_PCOMM)
1.1.1.4   misho    1215:     if (plugin->u.io->close != NULL)
                   1216:        plugin->u.io->close(exit_status, error);
1.1.1.2   misho    1217:     debug_return;
1.1       misho    1218: }
                   1219: 
                   1220: static int
                   1221: iolog_show_version(struct plugin_container *plugin, int verbose)
                   1222: {
1.1.1.2   misho    1223:     debug_decl(iolog_show_version, SUDO_DEBUG_PCOMM)
1.1.1.4   misho    1224:     if (plugin->u.io->show_version == NULL)
                   1225:        debug_return_bool(true);
1.1.1.2   misho    1226:     debug_return_bool(plugin->u.io->show_version(verbose));
1.1       misho    1227: }
                   1228: 
                   1229: /*
1.1.1.2   misho    1230:  * Remove the specified I/O logging plugin from the io_plugins list.
                   1231:  * Deregisters any hooks before unlinking, then frees the container.
1.1       misho    1232:  */
1.1.1.2   misho    1233: static void
                   1234: iolog_unlink(struct plugin_container *plugin)
1.1       misho    1235: {
1.1.1.2   misho    1236:     debug_decl(iolog_unlink, SUDO_DEBUG_PCOMM)
1.1       misho    1237: 
1.1.1.2   misho    1238:     /* Deregister hooks, if any. */
                   1239:     if (plugin->u.io->version >= SUDO_API_MKVERSION(1, 2)) {
                   1240:        if (plugin->u.io->deregister_hooks != NULL)
                   1241:            plugin->u.io->deregister_hooks(SUDO_HOOK_VERSION,
                   1242:                deregister_hook);
                   1243:     }
                   1244:     /* Remove from io_plugins list and free. */
1.1.1.6 ! misho    1245:     TAILQ_REMOVE(&io_plugins, plugin, entries);
1.1.1.2   misho    1246:     efree(plugin);
1.1       misho    1247: 
1.1.1.2   misho    1248:     debug_return;
1.1       misho    1249: }

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>