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

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>
                     57: #if TIME_WITH_SYS_TIME
                     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: 
                     86: #include "sudo.h"
                     87: #include "sudo_plugin.h"
                     88: #include "sudo_plugin_int.h"
1.1.1.4 ! misho      89: #include "sudo_usage.h"
1.1       misho      90: 
                     91: /*
                     92:  * Local variables
                     93:  */
                     94: struct plugin_container policy_plugin;
                     95: struct plugin_container_list io_plugins;
                     96: struct user_details user_details;
                     97: const char *list_user, *runas_user, *runas_group; /* 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.2   misho     136: #endif /* RLIMIT_CORE */
1.1       misho     137: #if defined(__linux__)
                    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.4 ! misho     203:        fatalx(_("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.4 ! misho     211:            fatalx(_("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);
                    219:            tq_foreach_fwd(&io_plugins, plugin) {
                    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. */
                    253:            for (plugin = io_plugins.first; plugin != NULL; plugin = next) {
                    254:                next = plugin->next;
                    255:                ok = iolog_open(plugin, settings, user_info,
                    256:                    command_info, nargc, nargv, envp);
                    257:                switch (ok) {
1.1.1.2   misho     258:                case 1:
1.1       misho     259:                    break;
1.1.1.2   misho     260:                case 0:
                    261:                    /* I/O plugin asked to be disabled, remove and free. */
                    262:                    iolog_unlink(plugin);
1.1       misho     263:                    break;
                    264:                case -2:
                    265:                    usage(1);
                    266:                    break;
                    267:                default:
1.1.1.4 ! misho     268:                    fatalx(_("error initializing I/O plugin %s"),
1.1       misho     269:                        plugin->name);
                    270:                }
                    271:            }
1.1.1.2   misho     272:            /* Setup command details and run command/edit. */
1.1       misho     273:            command_info_to_details(command_info, &command_details);
                    274:            command_details.argv = argv_out;
                    275:            command_details.envp = user_env_out;
                    276:            if (ISSET(sudo_mode, MODE_BACKGROUND))
                    277:                SET(command_details.flags, CD_BACKGROUND);
1.1.1.2   misho     278:            /* Become full root (not just setuid) so user cannot kill us. */
1.1.1.4 ! misho     279:            if (setuid(ROOT_UID) == -1)
        !           280:                warning("setuid(%d)", ROOT_UID);
1.1       misho     281:            /* Restore coredumpsize resource limit before running. */
1.1.1.2   misho     282: #ifdef RLIMIT_CORE
                    283:            if (sudo_conf_disable_coredump())
                    284:                (void) setrlimit(RLIMIT_CORE, &corelimit);
                    285: #endif /* RLIMIT_CORE */
1.1       misho     286:            if (ISSET(command_details.flags, CD_SUDOEDIT)) {
                    287:                exitcode = sudo_edit(&command_details);
                    288:            } else {
                    289:                exitcode = run_command(&command_details);
                    290:            }
                    291:            /* The close method was called by sudo_edit/run_command. */
                    292:            break;
                    293:        default:
1.1.1.4 ! misho     294:            fatalx(_("unexpected sudo mode 0x%x"), sudo_mode);
1.1       misho     295:     }
1.1.1.2   misho     296:     sudo_debug_exit_int(__func__, __FILE__, __LINE__, sudo_debug_subsys, exitcode);                
1.1       misho     297:     exit(exitcode);
                    298: }
                    299: 
1.1.1.4 ! misho     300: int
        !           301: os_init_common(int argc, char *argv[], char *envp[])
        !           302: {
        !           303: #if !defined(HAVE_GETPROGNAME) && !defined(HAVE___PROGNAME)
        !           304:     if (argc > 0)
        !           305:        setprogname(argv[0]);
        !           306: #endif
        !           307:     return 0;
        !           308: }
        !           309: 
1.1       misho     310: /*
                    311:  * Ensure that stdin, stdout and stderr are open; set to /dev/null if not.
                    312:  * Some operating systems do this automatically in the kernel or libc.
                    313:  */
                    314: static void
                    315: fix_fds(void)
                    316: {
                    317:     int miss[3], devnull = -1;
1.1.1.2   misho     318:     debug_decl(fix_fds, SUDO_DEBUG_UTIL)
1.1       misho     319: 
                    320:     /*
                    321:      * stdin, stdout and stderr must be open; set them to /dev/null
                    322:      * if they are closed.
                    323:      */
                    324:     miss[STDIN_FILENO] = fcntl(STDIN_FILENO, F_GETFL, 0) == -1;
                    325:     miss[STDOUT_FILENO] = fcntl(STDOUT_FILENO, F_GETFL, 0) == -1;
                    326:     miss[STDERR_FILENO] = fcntl(STDERR_FILENO, F_GETFL, 0) == -1;
                    327:     if (miss[STDIN_FILENO] || miss[STDOUT_FILENO] || miss[STDERR_FILENO]) {
                    328:        if ((devnull = open(_PATH_DEVNULL, O_RDWR, 0644)) == -1)
1.1.1.4 ! misho     329:            fatal(_("unable to open %s"), _PATH_DEVNULL);
1.1       misho     330:        if (miss[STDIN_FILENO] && dup2(devnull, STDIN_FILENO) == -1)
1.1.1.4 ! misho     331:            fatal("dup2");
1.1       misho     332:        if (miss[STDOUT_FILENO] && dup2(devnull, STDOUT_FILENO) == -1)
1.1.1.4 ! misho     333:            fatal("dup2");
1.1       misho     334:        if (miss[STDERR_FILENO] && dup2(devnull, STDERR_FILENO) == -1)
1.1.1.4 ! misho     335:            fatal("dup2");
1.1       misho     336:        if (devnull > STDERR_FILENO)
                    337:            close(devnull);
                    338:     }
1.1.1.2   misho     339:     debug_return;
1.1       misho     340: }
                    341: 
                    342: /*
                    343:  * Allocate space for groups and fill in using getgrouplist()
1.1.1.4 ! misho     344:  * for when we cannot (or don't want to) use getgroups().
1.1       misho     345:  */
                    346: static int
1.1.1.4 ! misho     347: fill_group_list(struct user_details *ud, int system_maxgroups)
1.1       misho     348: {
1.1.1.4 ! misho     349:     int tries, rval = -1;
1.1.1.2   misho     350:     debug_decl(fill_group_list, SUDO_DEBUG_UTIL)
1.1       misho     351: 
                    352:     /*
1.1.1.4 ! misho     353:      * If user specified a max number of groups, use it, otherwise keep
        !           354:      * trying getgrouplist() until we have enough room in the array.
1.1       misho     355:      */
1.1.1.4 ! misho     356:     ud->ngroups = sudo_conf_max_groups();
        !           357:     if (ud->ngroups != -1) {
1.1       misho     358:        ud->groups = emalloc2(ud->ngroups, sizeof(GETGROUPS_T));
1.1.1.4 ! misho     359:        /* No error on insufficient space if user specified max_groups. */
        !           360:        (void)getgrouplist(ud->username, ud->gid, ud->groups, &ud->ngroups);
        !           361:        rval = 0;
        !           362:     } else {
        !           363:        /*
        !           364:         * It is possible to belong to more groups in the group database
        !           365:         * than NGROUPS_MAX.  We start off with NGROUPS_MAX * 4 entries
        !           366:         * and double this as needed.
        !           367:         */
        !           368:        ud->groups = NULL;
        !           369:        ud->ngroups = system_maxgroups << 1;
        !           370:        for (tries = 0; tries < 10 && rval == -1; tries++) {
        !           371:            ud->ngroups <<= 1;
        !           372:            efree(ud->groups);
        !           373:            ud->groups = emalloc2(ud->ngroups, sizeof(GETGROUPS_T));
        !           374:            rval = getgrouplist(ud->username, ud->gid, ud->groups, &ud->ngroups);
        !           375:        }
1.1       misho     376:     }
1.1.1.2   misho     377:     debug_return_int(rval);
1.1       misho     378: }
                    379: 
                    380: static char *
                    381: get_user_groups(struct user_details *ud)
                    382: {
                    383:     char *cp, *gid_list = NULL;
                    384:     size_t glsize;
1.1.1.4 ! misho     385:     int i, len, maxgroups, group_source;
1.1.1.2   misho     386:     debug_decl(get_user_groups, SUDO_DEBUG_UTIL)
1.1       misho     387: 
1.1.1.4 ! misho     388: #if defined(HAVE_SYSCONF) && defined(_SC_NGROUPS_MAX)
        !           389:     maxgroups = (int)sysconf(_SC_NGROUPS_MAX);
        !           390:     if (maxgroups < 0)
        !           391: #endif
        !           392:        maxgroups = NGROUPS_MAX;
        !           393: 
1.1       misho     394:     ud->groups = NULL;
1.1.1.4 ! misho     395:     group_source = sudo_conf_group_source();
        !           396:     if (group_source != GROUP_SOURCE_DYNAMIC) {
        !           397:        if ((ud->ngroups = getgroups(0, NULL)) > 0) {
        !           398:            /* Use groups from kernel if not too many or source is static. */
        !           399:            if (ud->ngroups < maxgroups || group_source == GROUP_SOURCE_STATIC) {
        !           400:                ud->groups = emalloc2(ud->ngroups, sizeof(GETGROUPS_T));
        !           401:                if (getgroups(ud->ngroups, ud->groups) < 0) {
        !           402:                    efree(ud->groups);
        !           403:                    ud->groups = NULL;
        !           404:                }
        !           405:            }
1.1       misho     406:        }
                    407:     }
                    408:     if (ud->groups == NULL) {
1.1.1.4 ! misho     409:        /*
        !           410:         * Query group database if kernel list is too small or disabled.
        !           411:         * Typically, this is because NFS can only support up to 16 groups.
        !           412:         */
        !           413:        if (fill_group_list(ud, maxgroups) == -1)
        !           414:            fatal(_("unable to get group vector"));
1.1       misho     415:     }
                    416: 
                    417:     /*
                    418:      * Format group list as a comma-separated string of gids.
                    419:      */
                    420:     glsize = sizeof("groups=") - 1 + (ud->ngroups * (MAX_UID_T_LEN + 1));
                    421:     gid_list = emalloc(glsize);
                    422:     memcpy(gid_list, "groups=", sizeof("groups=") - 1);
                    423:     cp = gid_list + sizeof("groups=") - 1;
                    424:     for (i = 0; i < ud->ngroups; i++) {
                    425:        /* XXX - check rval */
                    426:        len = snprintf(cp, glsize - (cp - gid_list), "%s%u",
                    427:            i ? "," : "", (unsigned int)ud->groups[i]);
                    428:        cp += len;
                    429:     }
1.1.1.2   misho     430:     debug_return_str(gid_list);
1.1       misho     431: }
                    432: 
                    433: /*
                    434:  * Return user information as an array of name=value pairs.
                    435:  * and fill in struct user_details (which shares the same strings).
                    436:  */
                    437: static char **
                    438: get_user_info(struct user_details *ud)
                    439: {
1.1.1.4 ! misho     440:     char *cp, **user_info, cwd[PATH_MAX], host[HOST_NAME_MAX + 1];
1.1       misho     441:     struct passwd *pw;
1.1.1.2   misho     442:     int fd, i = 0;
                    443:     debug_decl(get_user_info, SUDO_DEBUG_UTIL)
1.1       misho     444: 
                    445:     /* XXX - bound check number of entries */
                    446:     user_info = emalloc2(32, sizeof(char *));
                    447: 
1.1.1.2   misho     448:     ud->pid = getpid();
                    449:     ud->ppid = getppid();
                    450:     ud->pgid = getpgid(0);
                    451:     ud->tcpgid = (pid_t)-1;
                    452:     fd = open(_PATH_TTY, O_RDWR|O_NOCTTY|O_NONBLOCK, 0);
                    453:     if (fd != -1) {
                    454:        ud->tcpgid = tcgetpgrp(fd);
                    455:        close(fd);
                    456:     }
                    457:     ud->sid = getsid(0);
                    458: 
1.1       misho     459:     ud->uid = getuid();
                    460:     ud->euid = geteuid();
                    461:     ud->gid = getgid();
                    462:     ud->egid = getegid();
                    463: 
                    464:     pw = getpwuid(ud->uid);
                    465:     if (pw == NULL)
1.1.1.4 ! misho     466:        fatalx(_("unknown uid %u: who are you?"), (unsigned int)ud->uid);
1.1       misho     467: 
                    468:     user_info[i] = fmt_string("user", pw->pw_name);
                    469:     if (user_info[i] == NULL)
1.1.1.4 ! misho     470:        fatalx(NULL);
1.1       misho     471:     ud->username = user_info[i] + sizeof("user=") - 1;
                    472: 
                    473:     /* Stash user's shell for use with the -s flag; don't pass to plugin. */
                    474:     if ((ud->shell = getenv("SHELL")) == NULL || ud->shell[0] == '\0') {
                    475:        ud->shell = pw->pw_shell[0] ? pw->pw_shell : _PATH_BSHELL;
                    476:     }
                    477:     ud->shell = estrdup(ud->shell);
                    478: 
1.1.1.2   misho     479:     easprintf(&user_info[++i], "pid=%d", (int)ud->pid);
                    480:     easprintf(&user_info[++i], "ppid=%d", (int)ud->ppid);
                    481:     easprintf(&user_info[++i], "pgid=%d", (int)ud->pgid);
                    482:     easprintf(&user_info[++i], "tcpgid=%d", (int)ud->tcpgid);
                    483:     easprintf(&user_info[++i], "sid=%d", (int)ud->sid);
                    484: 
1.1       misho     485:     easprintf(&user_info[++i], "uid=%u", (unsigned int)ud->uid);
                    486:     easprintf(&user_info[++i], "euid=%u", (unsigned int)ud->euid);
                    487:     easprintf(&user_info[++i], "gid=%u", (unsigned int)ud->gid);
                    488:     easprintf(&user_info[++i], "egid=%u", (unsigned int)ud->egid);
                    489: 
                    490:     if ((cp = get_user_groups(ud)) != NULL)
                    491:        user_info[++i] = cp;
                    492: 
                    493:     if (getcwd(cwd, sizeof(cwd)) != NULL) {
                    494:        user_info[++i] = fmt_string("cwd", cwd);
                    495:        if (user_info[i] == NULL)
1.1.1.4 ! misho     496:            fatalx(NULL);
1.1       misho     497:        ud->cwd = user_info[i] + sizeof("cwd=") - 1;
                    498:     }
                    499: 
1.1.1.2   misho     500:     if ((cp = get_process_ttyname()) != NULL) {
1.1       misho     501:        user_info[++i] = fmt_string("tty", cp);
                    502:        if (user_info[i] == NULL)
1.1.1.4 ! misho     503:            fatalx(NULL);
1.1       misho     504:        ud->tty = user_info[i] + sizeof("tty=") - 1;
1.1.1.2   misho     505:        efree(cp);
1.1       misho     506:     }
                    507: 
                    508:     if (gethostname(host, sizeof(host)) == 0)
                    509:        host[sizeof(host) - 1] = '\0';
                    510:     else
                    511:        strlcpy(host, "localhost", sizeof(host));
                    512:     user_info[++i] = fmt_string("host", host);
                    513:     if (user_info[i] == NULL)
1.1.1.4 ! misho     514:        fatalx(NULL);
1.1       misho     515:     ud->host = user_info[i] + sizeof("host=") - 1;
                    516: 
                    517:     get_ttysize(&ud->ts_lines, &ud->ts_cols);
                    518:     easprintf(&user_info[++i], "lines=%d", ud->ts_lines);
                    519:     easprintf(&user_info[++i], "cols=%d", ud->ts_cols);
                    520: 
                    521:     user_info[++i] = NULL;
                    522: 
1.1.1.2   misho     523:     debug_return_ptr(user_info);
1.1       misho     524: }
                    525: 
                    526: /*
                    527:  * Convert a command_info array into a command_details structure.
                    528:  */
                    529: static void
                    530: command_info_to_details(char * const info[], struct command_details *details)
                    531: {
                    532:     int i;
                    533:     long lval;
                    534:     unsigned long ulval;
                    535:     char *cp, *ep;
1.1.1.2   misho     536:     debug_decl(command_info_to_details, SUDO_DEBUG_PCOMM)
1.1       misho     537: 
                    538:     memset(details, 0, sizeof(*details));
                    539:     details->closefrom = -1;
                    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) {
                    556:                    cp = info[i] + sizeof("closefrom=") - 1;
                    557:                    if (*cp == '\0')
                    558:                        break;
                    559:                    errno = 0;
                    560:                    lval = strtol(cp, &ep, 0);
                    561:                    if (*cp != '\0' && *ep == '\0' &&
                    562:                        !(errno == ERANGE &&
                    563:                        (lval == LONG_MAX || lval == LONG_MIN)) &&
                    564:                        lval < INT_MAX && lval > INT_MIN) {
                    565:                        details->closefrom = (int)lval;
                    566:                    }
                    567:                    break;
                    568:                }
                    569:                break;
1.1.1.4 ! misho     570:            case 'e':
        !           571:                if (strncmp("exec_background=", info[i], sizeof("exec_background=") - 1) == 0) {
        !           572:                    if (atobool(info[i] + sizeof("exec_background=") - 1) == true)
        !           573:                        SET(details->flags, CD_EXEC_BG);
        !           574:                    break;
        !           575:                }
        !           576:                break;
1.1       misho     577:            case 'l':
                    578:                SET_STRING("login_class=", login_class)
                    579:                break;
                    580:            case 'n':
                    581:                /* XXX - bounds check  -NZERO to NZERO (inclusive). */
                    582:                if (strncmp("nice=", info[i], sizeof("nice=") - 1) == 0) {
                    583:                    cp = info[i] + sizeof("nice=") - 1;
                    584:                    if (*cp == '\0')
                    585:                        break;
                    586:                    errno = 0;
                    587:                    lval = strtol(cp, &ep, 0);
                    588:                    if (*cp != '\0' && *ep == '\0' &&
                    589:                        !(errno == ERANGE &&
                    590:                        (lval == LONG_MAX || lval == LONG_MIN)) &&
                    591:                        lval < INT_MAX && lval > INT_MIN) {
                    592:                        details->priority = (int)lval;
                    593:                        SET(details->flags, CD_SET_PRIORITY);
                    594:                    }
                    595:                    break;
                    596:                }
                    597:                if (strncmp("noexec=", info[i], sizeof("noexec=") - 1) == 0) {
1.1.1.2   misho     598:                    if (atobool(info[i] + sizeof("noexec=") - 1) == true)
1.1       misho     599:                        SET(details->flags, CD_NOEXEC);
                    600:                    break;
                    601:                }
                    602:                break;
                    603:            case 'p':
                    604:                if (strncmp("preserve_groups=", info[i], sizeof("preserve_groups=") - 1) == 0) {
1.1.1.2   misho     605:                    if (atobool(info[i] + sizeof("preserve_groups=") - 1) == true)
1.1       misho     606:                        SET(details->flags, CD_PRESERVE_GROUPS);
                    607:                    break;
                    608:                }
                    609:                break;
                    610:            case 'r':
                    611:                if (strncmp("runas_egid=", info[i], sizeof("runas_egid=") - 1) == 0) {
                    612:                    cp = info[i] + sizeof("runas_egid=") - 1;
                    613:                    if (*cp == '\0')
                    614:                        break;
                    615:                    errno = 0;
                    616:                    ulval = strtoul(cp, &ep, 0);
                    617:                    if (*cp != '\0' && *ep == '\0' &&
                    618:                        (errno != ERANGE || ulval != ULONG_MAX)) {
                    619:                        details->egid = (gid_t)ulval;
                    620:                        SET(details->flags, CD_SET_EGID);
                    621:                    }
                    622:                    break;
                    623:                }
                    624:                if (strncmp("runas_euid=", info[i], sizeof("runas_euid=") - 1) == 0) {
                    625:                    cp = info[i] + sizeof("runas_euid=") - 1;
                    626:                    if (*cp == '\0')
                    627:                        break;
                    628:                    errno = 0;
                    629:                    ulval = strtoul(cp, &ep, 0);
                    630:                    if (*cp != '\0' && *ep == '\0' &&
                    631:                        (errno != ERANGE || ulval != ULONG_MAX)) {
                    632:                        details->euid = (uid_t)ulval;
                    633:                        SET(details->flags, CD_SET_EUID);
                    634:                    }
                    635:                    break;
                    636:                }
                    637:                if (strncmp("runas_gid=", info[i], sizeof("runas_gid=") - 1) == 0) {
                    638:                    cp = info[i] + sizeof("runas_gid=") - 1;
                    639:                    if (*cp == '\0')
                    640:                        break;
                    641:                    errno = 0;
                    642:                    ulval = strtoul(cp, &ep, 0);
                    643:                    if (*cp != '\0' && *ep == '\0' &&
                    644:                        (errno != ERANGE || ulval != ULONG_MAX)) {
                    645:                        details->gid = (gid_t)ulval;
                    646:                        SET(details->flags, CD_SET_GID);
                    647:                    }
                    648:                    break;
                    649:                }
                    650:                if (strncmp("runas_groups=", info[i], sizeof("runas_groups=") - 1) == 0) {
                    651:                    int j;
                    652: 
                    653:                    /* count groups, alloc and fill in */
                    654:                    cp = info[i] + sizeof("runas_groups=") - 1;
                    655:                    if (*cp == '\0')
                    656:                        break;
                    657:                    for (;;) {
                    658:                        details->ngroups++;
                    659:                        if ((cp = strchr(cp, ',')) == NULL)
                    660:                            break;
                    661:                        cp++;
                    662:                    }
                    663:                    if (details->ngroups != 0) {
                    664:                        details->groups =
                    665:                            emalloc2(details->ngroups, sizeof(GETGROUPS_T));
                    666:                        cp = info[i] + sizeof("runas_groups=") - 1;
                    667:                        for (j = 0; j < details->ngroups;) {
                    668:                            errno = 0;
                    669:                            ulval = strtoul(cp, &ep, 0);
                    670:                            if (*cp == '\0' || (*ep != ',' && *ep != '\0') ||
                    671:                                (ulval == ULONG_MAX && errno == ERANGE)) {
                    672:                                break;
                    673:                            }
                    674:                            details->groups[j++] = (gid_t)ulval;
                    675:                            cp = ep + 1;
                    676:                        }
                    677:                        details->ngroups = j;
                    678:                    }
                    679:                    break;
                    680:                }
                    681:                if (strncmp("runas_uid=", info[i], sizeof("runas_uid=") - 1) == 0) {
                    682:                    cp = info[i] + sizeof("runas_uid=") - 1;
                    683:                    if (*cp == '\0')
                    684:                        break;
                    685:                    errno = 0;
                    686:                    ulval = strtoul(cp, &ep, 0);
                    687:                    if (*cp != '\0' && *ep == '\0' &&
                    688:                        (errno != ERANGE || ulval != ULONG_MAX)) {
                    689:                        details->uid = (uid_t)ulval;
                    690:                        SET(details->flags, CD_SET_UID);
                    691:                    }
                    692:                    break;
                    693:                }
1.1.1.3   misho     694: #ifdef HAVE_PRIV_SET
                    695:                if (strncmp("runas_privs=", info[i], sizeof("runas_privs=") - 1) == 0) {
                    696:                     const char *endp;
                    697:                    cp = info[i] + sizeof("runas_privs=") - 1;
                    698:                    if (*cp == '\0')
                    699:                        break;
                    700:                    errno = 0;
                    701:                    details->privs = priv_str_to_set(cp, ",", &endp);
                    702:                    if (details->privs == NULL)
                    703:                            warning("invalid runas_privs %s", endp);
                    704:                }
                    705:                if (strncmp("runas_limitprivs=", info[i], sizeof("runas_limitprivs=") - 1) == 0) {
                    706:                     const char *endp;
                    707:                    cp = info[i] + sizeof("runas_limitprivs=") - 1;
                    708:                    if (*cp == '\0')
                    709:                        break;
                    710:                    errno = 0;
                    711:                    details->limitprivs = priv_str_to_set(cp, ",", &endp);
                    712:                    if (details->limitprivs == NULL)
                    713:                            warning("invalid runas_limitprivs %s", endp);
                    714:                }
                    715: #endif /* HAVE_PRIV_SET */
1.1       misho     716:                break;
                    717:            case 's':
                    718:                SET_STRING("selinux_role=", selinux_role)
                    719:                SET_STRING("selinux_type=", selinux_type)
                    720:                if (strncmp("set_utmp=", info[i], sizeof("set_utmp=") - 1) == 0) {
1.1.1.2   misho     721:                    if (atobool(info[i] + sizeof("set_utmp=") - 1) == true)
1.1       misho     722:                        SET(details->flags, CD_SET_UTMP);
                    723:                    break;
                    724:                }
                    725:                if (strncmp("sudoedit=", info[i], sizeof("sudoedit=") - 1) == 0) {
1.1.1.2   misho     726:                    if (atobool(info[i] + sizeof("sudoedit=") - 1) == true)
1.1       misho     727:                        SET(details->flags, CD_SUDOEDIT);
                    728:                    break;
                    729:                }
                    730:                break;
                    731:            case 't':
                    732:                if (strncmp("timeout=", info[i], sizeof("timeout=") - 1) == 0) {
                    733:                    cp = info[i] + sizeof("timeout=") - 1;
                    734:                    if (*cp == '\0')
                    735:                        break;
                    736:                    errno = 0;
                    737:                    lval = strtol(cp, &ep, 0);
                    738:                    if (*cp != '\0' && *ep == '\0' &&
                    739:                        !(errno == ERANGE &&
                    740:                        (lval == LONG_MAX || lval == LONG_MIN)) &&
                    741:                        lval <= INT_MAX && lval >= 0) {
                    742:                        details->timeout = (int)lval;
                    743:                        SET(details->flags, CD_SET_TIMEOUT);
                    744:                    }
                    745:                    break;
                    746:                }
                    747:                break;
                    748:            case 'u':
                    749:                if (strncmp("umask=", info[i], sizeof("umask=") - 1) == 0) {
                    750:                    cp = info[i] + sizeof("umask=") - 1;
                    751:                    if (*cp == '\0')
                    752:                        break;
                    753:                    errno = 0;
                    754:                    ulval = strtoul(cp, &ep, 8);
                    755:                    if (*cp != '\0' && *ep == '\0' &&
                    756:                        (errno != ERANGE || ulval != ULONG_MAX)) {
                    757:                        details->umask = (uid_t)ulval;
                    758:                        SET(details->flags, CD_SET_UMASK);
                    759:                    }
                    760:                    break;
                    761:                }
                    762:                if (strncmp("use_pty=", info[i], sizeof("use_pty=") - 1) == 0) {
1.1.1.2   misho     763:                    if (atobool(info[i] + sizeof("use_pty=") - 1) == true)
1.1       misho     764:                        SET(details->flags, CD_USE_PTY);
                    765:                    break;
                    766:                }
                    767:                SET_STRING("utmp_user=", utmp_user)
                    768:                break;
                    769:        }
                    770:     }
                    771: 
                    772:     if (!ISSET(details->flags, CD_SET_EUID))
                    773:        details->euid = details->uid;
                    774: 
1.1.1.2   misho     775: #ifdef HAVE_SETAUTHDB
                    776:     aix_setauthdb(IDtouser(details->euid));
                    777: #endif
                    778:     details->pw = getpwuid(details->euid);
                    779:     if (details->pw != NULL && (details->pw = pw_dup(details->pw)) == NULL)
1.1.1.4 ! misho     780:        fatalx(NULL);
1.1.1.2   misho     781: #ifdef HAVE_SETAUTHDB
                    782:     aix_restoreauthdb();
                    783: #endif
                    784: 
1.1       misho     785: #ifdef HAVE_SELINUX
                    786:     if (details->selinux_role != NULL && is_selinux_enabled() > 0)
                    787:        SET(details->flags, CD_RBAC_ENABLED);
                    788: #endif
1.1.1.2   misho     789:     debug_return;
                    790: }
                    791: 
                    792: static void
                    793: sudo_check_suid(const char *path)
                    794: {
                    795:     struct stat sb;
                    796:     debug_decl(sudo_check_suid, SUDO_DEBUG_PCOMM)
                    797: 
                    798:     if (geteuid() != 0) {
                    799:        if (strchr(path, '/') != NULL && stat(path, &sb) == 0) {
                    800:            /* Try to determine why sudo was not running as root. */
                    801:            if (sb.st_uid != ROOT_UID || !ISSET(sb.st_mode, S_ISUID)) {
1.1.1.4 ! misho     802:                fatalx(
1.1.1.2   misho     803:                    _("%s must be owned by uid %d and have the setuid bit set"),
                    804:                    path, ROOT_UID);
                    805:            } else {
1.1.1.4 ! misho     806:                fatalx(_("effective uid is not %d, is %s on a file system "
1.1.1.2   misho     807:                    "with the 'nosuid' option set or an NFS file system without"
                    808:                    " root privileges?"), ROOT_UID, path);
                    809:            }
                    810:        } else {
1.1.1.4 ! misho     811:            fatalx(
1.1.1.2   misho     812:                _("effective uid is not %d, is sudo installed setuid root?"),
                    813:                ROOT_UID);
                    814:        }
                    815:     }
                    816:     debug_return;
1.1       misho     817: }
                    818: 
                    819: /*
                    820:  * Disable core dumps to avoid dropping a core with user password in it.
                    821:  * We will reset this limit before executing the command.
                    822:  * Not all operating systems disable core dumps for setuid processes.
                    823:  */
                    824: static void
                    825: disable_coredumps(void)
                    826: {
1.1.1.2   misho     827: #if defined(__linux__) || defined(RLIMIT_CORE)
1.1       misho     828:     struct rlimit rl;
                    829: #endif
1.1.1.2   misho     830:     debug_decl(disable_coredumps, SUDO_DEBUG_UTIL)
1.1       misho     831: 
                    832: #if defined(__linux__)
                    833:     /*
                    834:      * Unlimit the number of processes since Linux's setuid() will
                    835:      * apply resource limits when changing uid and return EAGAIN if
                    836:      * nproc would be violated by the uid switch.
                    837:      */
                    838:     (void) getrlimit(RLIMIT_NPROC, &nproclimit);
                    839:     rl.rlim_cur = rl.rlim_max = RLIM_INFINITY;
                    840:     if (setrlimit(RLIMIT_NPROC, &rl)) {
                    841:        memcpy(&rl, &nproclimit, sizeof(struct rlimit));
                    842:        rl.rlim_cur = rl.rlim_max;
                    843:        (void)setrlimit(RLIMIT_NPROC, &rl);
                    844:     }
                    845: #endif /* __linux__ */
1.1.1.2   misho     846: #ifdef RLIMIT_CORE
1.1       misho     847:     /*
1.1.1.2   misho     848:      * Turn off core dumps?
1.1       misho     849:      */
1.1.1.2   misho     850:     if (sudo_conf_disable_coredump()) {
                    851:        (void) getrlimit(RLIMIT_CORE, &corelimit);
                    852:        memcpy(&rl, &corelimit, sizeof(struct rlimit));
                    853:        rl.rlim_cur = 0;
                    854:        (void) setrlimit(RLIMIT_CORE, &rl);
                    855:     }
                    856: #endif /* RLIMIT_CORE */
                    857:     debug_return;
1.1       misho     858: }
                    859: 
                    860: /*
                    861:  * Setup the execution environment immediately prior to the call to execve()
1.1.1.2   misho     862:  * Returns true on success and false on failure.
1.1       misho     863:  */
1.1.1.2   misho     864: bool
1.1       misho     865: exec_setup(struct command_details *details, const char *ptyname, int ptyfd)
                    866: {
1.1.1.2   misho     867:     bool rval = false;
                    868:     debug_decl(exec_setup, SUDO_DEBUG_EXEC)
1.1       misho     869: 
                    870: #ifdef HAVE_SELINUX
                    871:     if (ISSET(details->flags, CD_RBAC_ENABLED)) {
                    872:        if (selinux_setup(details->selinux_role, details->selinux_type,
                    873:            ptyname ? ptyname : user_details.tty, ptyfd) == -1)
                    874:            goto done;
                    875:     }
                    876: #endif
                    877: 
1.1.1.2   misho     878:     if (details->pw != NULL) {
1.1       misho     879: #ifdef HAVE_PROJECT_H
1.1.1.2   misho     880:        set_project(details->pw);
1.1       misho     881: #endif
1.1.1.3   misho     882: #ifdef HAVE_PRIV_SET
1.1.1.4 ! misho     883:        if (details->privs != NULL) {
        !           884:            if (setppriv(PRIV_SET, PRIV_INHERITABLE, details->privs) != 0) {
        !           885:                warning("unable to set privileges");
        !           886:                goto done;
        !           887:            }
1.1.1.3   misho     888:        }
1.1.1.4 ! misho     889:        if (details->limitprivs != NULL) {
        !           890:            if (setppriv(PRIV_SET, PRIV_LIMIT, details->limitprivs) != 0) {
        !           891:                warning("unable to set limit privileges");
        !           892:                goto done;
        !           893:            }
        !           894:        } else if (details->privs != NULL) {
        !           895:            if (setppriv(PRIV_SET, PRIV_LIMIT, details->privs) != 0) {
        !           896:                warning("unable to set limit privileges");
        !           897:                goto done;
        !           898:            }
1.1.1.3   misho     899:        }
                    900: #endif /* HAVE_PRIV_SET */
                    901: 
1.1       misho     902: #ifdef HAVE_GETUSERATTR
1.1.1.2   misho     903:        aix_prep_user(details->pw->pw_name, ptyname ? ptyname : user_details.tty);
1.1       misho     904: #endif
                    905: #ifdef HAVE_LOGIN_CAP_H
                    906:        if (details->login_class) {
                    907:            int flags;
                    908:            login_cap_t *lc;
                    909: 
                    910:            /*
1.1.1.2   misho     911:             * We only use setusercontext() to set the nice value and rlimits
                    912:             * unless this is a login shell (sudo -i).
1.1       misho     913:             */
                    914:            lc = login_getclass((char *)details->login_class);
                    915:            if (!lc) {
                    916:                warningx(_("unknown login class %s"), details->login_class);
                    917:                errno = ENOENT;
                    918:                goto done;
                    919:            }
1.1.1.2   misho     920:            if (ISSET(sudo_mode, MODE_LOGIN_SHELL)) {
                    921:                /* Set everything except user, group and login name. */
                    922:                flags = LOGIN_SETALL;
                    923:                CLR(flags, LOGIN_SETGROUP|LOGIN_SETLOGIN|LOGIN_SETUSER|LOGIN_SETENV|LOGIN_SETPATH);
                    924:                CLR(details->flags, CD_SET_UMASK); /* LOGIN_UMASK instead */
                    925:            } else {
                    926:                flags = LOGIN_SETRESOURCES|LOGIN_SETPRIORITY;
                    927:            }
                    928:            if (setusercontext(lc, details->pw, details->pw->pw_uid, flags)) {
                    929:                if (details->pw->pw_uid != ROOT_UID) {
1.1       misho     930:                    warning(_("unable to set user context"));
                    931:                    goto done;
                    932:                } else
                    933:                    warning(_("unable to set user context"));
                    934:            }
                    935:        }
                    936: #endif /* HAVE_LOGIN_CAP_H */
                    937:     }
                    938: 
                    939:     /*
                    940:      * Set groups, including supplementary group vector.
                    941:      */
1.1.1.2   misho     942:     if (!ISSET(details->flags, CD_PRESERVE_GROUPS)) {
                    943:        if (details->ngroups >= 0) {
                    944:            if (sudo_setgroups(details->ngroups, details->groups) < 0) {
                    945:                warning(_("unable to set supplementary group IDs"));
                    946:                goto done;
                    947:            }
                    948:        }
                    949:     }
1.1       misho     950: #ifdef HAVE_SETEUID
                    951:     if (ISSET(details->flags, CD_SET_EGID) && setegid(details->egid)) {
                    952:        warning(_("unable to set effective gid to runas gid %u"),
                    953:            (unsigned int)details->egid);
                    954:        goto done;
                    955:     }
                    956: #endif
                    957:     if (ISSET(details->flags, CD_SET_GID) && setgid(details->gid)) {
                    958:        warning(_("unable to set gid to runas gid %u"),
                    959:            (unsigned int)details->gid);
                    960:        goto done;
                    961:     }
                    962: 
                    963:     if (ISSET(details->flags, CD_SET_PRIORITY)) {
                    964:        if (setpriority(PRIO_PROCESS, 0, details->priority) != 0) {
                    965:            warning(_("unable to set process priority"));
                    966:            goto done;
                    967:        }
                    968:     }
                    969:     if (ISSET(details->flags, CD_SET_UMASK))
                    970:        (void) umask(details->umask);
                    971:     if (details->chroot) {
                    972:        if (chroot(details->chroot) != 0 || chdir("/") != 0) {
                    973:            warning(_("unable to change root to %s"), details->chroot);
                    974:            goto done;
                    975:        }
                    976:     }
                    977: 
                    978: #ifdef HAVE_SETRESUID
                    979:     if (setresuid(details->uid, details->euid, details->euid) != 0) {
                    980:        warning(_("unable to change to runas uid (%u, %u)"), details->uid,
                    981:            details->euid);
                    982:        goto done;
                    983:     }
                    984: #elif HAVE_SETREUID
                    985:     if (setreuid(details->uid, details->euid) != 0) {
                    986:        warning(_("unable to change to runas uid (%u, %u)"),
                    987:            (unsigned int)details->uid, (unsigned int)details->euid);
                    988:        goto done;
                    989:     }
                    990: #else
                    991:     if (seteuid(details->euid) != 0 || setuid(details->euid) != 0) {
                    992:        warning(_("unable to change to runas uid (%u, %u)"), details->uid,
                    993:            details->euid);
                    994:        goto done;
                    995:     }
                    996: #endif /* !HAVE_SETRESUID && !HAVE_SETREUID */
                    997: 
                    998:     /*
                    999:      * Only change cwd if we have chroot()ed or the policy modules
                   1000:      * specifies a different cwd.  Must be done after uid change.
                   1001:      */
                   1002:     if (details->cwd) {
                   1003:        if (details->chroot || strcmp(details->cwd, user_details.cwd) != 0) {
                   1004:            /* Note: cwd is relative to the new root, if any. */
                   1005:            if (chdir(details->cwd) != 0) {
                   1006:                warning(_("unable to change directory to %s"), details->cwd);
                   1007:                goto done;
                   1008:            }
                   1009:        }
                   1010:     }
                   1011: 
                   1012:     /*
1.1.1.3   misho    1013:      * SuSE Enterprise Linux uses RLIMIT_NPROC and _SC_CHILD_MAX
                   1014:      * interchangably.  This causes problems when setting RLIMIT_NPROC
                   1015:      * to RLIM_INFINITY due to a bug in bash where bash tries to honor
                   1016:      * the value of _SC_CHILD_MAX but treats a value of -1 as an error,
                   1017:      * and uses a default value of 32 instead.
                   1018:      *
                   1019:      * To work around this problem, we restore the nproc resource limit
                   1020:      * if sysconf(_SC_CHILD_MAX) is negative.  In most cases, pam_limits
                   1021:      * will set RLIMIT_NPROC for us.
                   1022:      *
1.1       misho    1023:      * We must do this *after* the uid change to avoid potential EAGAIN
                   1024:      * from setuid().
                   1025:      */
1.1.1.3   misho    1026: #if defined(__linux__) && defined(_SC_CHILD_MAX)
1.1       misho    1027:     {
                   1028:        struct rlimit rl;
1.1.1.3   misho    1029:        long l;
                   1030:        errno = 0;
                   1031:        l = sysconf(_SC_CHILD_MAX);
                   1032:        if (l == -1 && errno == 0 && getrlimit(RLIMIT_NPROC, &rl) == 0) {
1.1       misho    1033:            if (rl.rlim_cur == RLIM_INFINITY && rl.rlim_max == RLIM_INFINITY)
                   1034:                (void) setrlimit(RLIMIT_NPROC, &nproclimit);
                   1035:        }
                   1036:     }
                   1037: #endif
                   1038: 
1.1.1.2   misho    1039:     rval = true;
1.1       misho    1040: 
                   1041: done:
1.1.1.2   misho    1042:     debug_return_bool(rval);
1.1       misho    1043: }
                   1044: 
                   1045: /*
                   1046:  * Run the command and wait for it to complete.
                   1047:  */
                   1048: int
                   1049: run_command(struct command_details *details)
                   1050: {
                   1051:     struct plugin_container *plugin;
                   1052:     struct command_status cstat;
                   1053:     int exitcode = 1;
1.1.1.2   misho    1054:     debug_decl(run_command, SUDO_DEBUG_EXEC)
1.1       misho    1055: 
                   1056:     cstat.type = CMD_INVALID;
                   1057:     cstat.val = 0;
                   1058: 
1.1.1.2   misho    1059:     sudo_execute(details, &cstat);
1.1       misho    1060: 
                   1061:     switch (cstat.type) {
                   1062:     case CMD_ERRNO:
                   1063:        /* exec_setup() or execve() returned an error. */
1.1.1.2   misho    1064:        sudo_debug_printf(SUDO_DEBUG_DEBUG,
                   1065:            "calling policy close with errno %d", cstat.val);
1.1       misho    1066:        policy_close(&policy_plugin, 0, cstat.val);
                   1067:        tq_foreach_fwd(&io_plugins, plugin) {
1.1.1.2   misho    1068:            sudo_debug_printf(SUDO_DEBUG_DEBUG,
                   1069:                "calling I/O close with errno %d", cstat.val);
1.1       misho    1070:            iolog_close(plugin, 0, cstat.val);
                   1071:        }
                   1072:        exitcode = 1;
                   1073:        break;
                   1074:     case CMD_WSTATUS:
                   1075:        /* Command ran, exited or was killed. */
1.1.1.2   misho    1076:        sudo_debug_printf(SUDO_DEBUG_DEBUG,
                   1077:            "calling policy close with wait status %d", cstat.val);
1.1       misho    1078:        policy_close(&policy_plugin, cstat.val, 0);
                   1079:        tq_foreach_fwd(&io_plugins, plugin) {
1.1.1.2   misho    1080:            sudo_debug_printf(SUDO_DEBUG_DEBUG,
                   1081:                "calling I/O close with wait status %d", cstat.val);
1.1       misho    1082:            iolog_close(plugin, cstat.val, 0);
                   1083:        }
                   1084:        if (WIFEXITED(cstat.val))
                   1085:            exitcode = WEXITSTATUS(cstat.val);
                   1086:        else if (WIFSIGNALED(cstat.val))
                   1087:            exitcode = WTERMSIG(cstat.val) | 128;
                   1088:        break;
                   1089:     default:
                   1090:        warningx(_("unexpected child termination condition: %d"), cstat.type);
                   1091:        break;
                   1092:     }
1.1.1.2   misho    1093:     debug_return_int(exitcode);
1.1       misho    1094: }
                   1095: 
                   1096: static int
                   1097: policy_open(struct plugin_container *plugin, char * const settings[],
                   1098:     char * const user_info[], char * const user_env[])
                   1099: {
1.1.1.2   misho    1100:     int rval;
                   1101:     debug_decl(policy_open, SUDO_DEBUG_PCOMM)
                   1102: 
                   1103:     /*
                   1104:      * Backwards compatibility for older API versions
                   1105:      */
                   1106:     switch (plugin->u.generic->version) {
                   1107:     case SUDO_API_MKVERSION(1, 0):
                   1108:     case SUDO_API_MKVERSION(1, 1):
                   1109:        rval = plugin->u.policy_1_0->open(plugin->u.io_1_0->version,
                   1110:            sudo_conversation, _sudo_printf, settings, user_info, user_env);
                   1111:        break;
                   1112:     default:
                   1113:        rval = plugin->u.policy->open(SUDO_API_VERSION, sudo_conversation,
                   1114:            _sudo_printf, settings, user_info, user_env, plugin->options);
                   1115:     }
                   1116: 
                   1117:     debug_return_bool(rval);
1.1       misho    1118: }
                   1119: 
                   1120: static void
                   1121: policy_close(struct plugin_container *plugin, int exit_status, int error)
                   1122: {
1.1.1.2   misho    1123:     debug_decl(policy_close, SUDO_DEBUG_PCOMM)
1.1.1.4 ! misho    1124:     if (plugin->u.policy->close != NULL)
        !          1125:        plugin->u.policy->close(exit_status, error);
        !          1126:     else
        !          1127:        warning(_("unable to execute %s"), command_details.command);
1.1.1.2   misho    1128:     debug_return;
1.1       misho    1129: }
                   1130: 
                   1131: static int
                   1132: policy_show_version(struct plugin_container *plugin, int verbose)
                   1133: {
1.1.1.2   misho    1134:     debug_decl(policy_show_version, SUDO_DEBUG_PCOMM)
1.1.1.4 ! misho    1135:     if (plugin->u.policy->show_version == NULL)
        !          1136:        debug_return_bool(true);
1.1.1.2   misho    1137:     debug_return_bool(plugin->u.policy->show_version(verbose));
1.1       misho    1138: }
                   1139: 
                   1140: static int
                   1141: policy_check(struct plugin_container *plugin, int argc, char * const argv[],
                   1142:     char *env_add[], char **command_info[], char **argv_out[],
                   1143:     char **user_env_out[])
                   1144: {
1.1.1.2   misho    1145:     debug_decl(policy_check, SUDO_DEBUG_PCOMM)
1.1.1.4 ! misho    1146:     if (plugin->u.policy->check_policy == NULL) {
        !          1147:        fatalx(_("policy plugin %s is missing the `check_policy' method"),
        !          1148:            plugin->name);
        !          1149:     }
1.1.1.2   misho    1150:     debug_return_bool(plugin->u.policy->check_policy(argc, argv, env_add,
                   1151:        command_info, argv_out, user_env_out));
1.1       misho    1152: }
                   1153: 
                   1154: static int
                   1155: policy_list(struct plugin_container *plugin, int argc, char * const argv[],
                   1156:     int verbose, const char *list_user)
                   1157: {
1.1.1.2   misho    1158:     debug_decl(policy_list, SUDO_DEBUG_PCOMM)
1.1       misho    1159:     if (plugin->u.policy->list == NULL) {
                   1160:        warningx(_("policy plugin %s does not support listing privileges"),
                   1161:            plugin->name);
1.1.1.2   misho    1162:        debug_return_bool(false);
1.1       misho    1163:     }
1.1.1.2   misho    1164:     debug_return_bool(plugin->u.policy->list(argc, argv, verbose, list_user));
1.1       misho    1165: }
                   1166: 
                   1167: static int
                   1168: policy_validate(struct plugin_container *plugin)
                   1169: {
1.1.1.2   misho    1170:     debug_decl(policy_validate, SUDO_DEBUG_PCOMM)
1.1       misho    1171:     if (plugin->u.policy->validate == NULL) {
                   1172:        warningx(_("policy plugin %s does not support the -v option"),
                   1173:            plugin->name);
1.1.1.2   misho    1174:        debug_return_bool(false);
1.1       misho    1175:     }
1.1.1.2   misho    1176:     debug_return_bool(plugin->u.policy->validate());
1.1       misho    1177: }
                   1178: 
                   1179: static void
                   1180: policy_invalidate(struct plugin_container *plugin, int remove)
                   1181: {
1.1.1.2   misho    1182:     debug_decl(policy_invalidate, SUDO_DEBUG_PCOMM)
1.1       misho    1183:     if (plugin->u.policy->invalidate == NULL) {
1.1.1.4 ! misho    1184:        fatalx(_("policy plugin %s does not support the -k/-K options"),
1.1       misho    1185:            plugin->name);
                   1186:     }
                   1187:     plugin->u.policy->invalidate(remove);
1.1.1.2   misho    1188:     debug_return;
1.1       misho    1189: }
                   1190: 
1.1.1.2   misho    1191: int
                   1192: policy_init_session(struct command_details *details)
1.1       misho    1193: {
1.1.1.2   misho    1194:     int rval = true;
                   1195:     debug_decl(policy_init_session, SUDO_DEBUG_PCOMM)
                   1196: 
                   1197:     if (policy_plugin.u.policy->init_session) {
                   1198:        /*
                   1199:         * Backwards compatibility for older API versions
                   1200:         */
                   1201:        switch (policy_plugin.u.generic->version) {
                   1202:        case SUDO_API_MKVERSION(1, 0):
                   1203:        case SUDO_API_MKVERSION(1, 1):
                   1204:            rval = policy_plugin.u.policy_1_0->init_session(details->pw);
                   1205:            break;
                   1206:        default:
                   1207:            rval = policy_plugin.u.policy->init_session(details->pw,
                   1208:                &details->envp);
                   1209:        }
                   1210:     }
                   1211:     debug_return_bool(rval);
1.1       misho    1212: }
                   1213: 
                   1214: static int
                   1215: iolog_open(struct plugin_container *plugin, char * const settings[],
                   1216:     char * const user_info[], char * const command_info[],
                   1217:     int argc, char * const argv[], char * const user_env[])
                   1218: {
                   1219:     int rval;
1.1.1.2   misho    1220:     debug_decl(iolog_open, SUDO_DEBUG_PCOMM)
1.1       misho    1221: 
                   1222:     /*
1.1.1.2   misho    1223:      * Backwards compatibility for older API versions
1.1       misho    1224:      */
                   1225:     switch (plugin->u.generic->version) {
                   1226:     case SUDO_API_MKVERSION(1, 0):
                   1227:        rval = plugin->u.io_1_0->open(plugin->u.io_1_0->version,
                   1228:            sudo_conversation, _sudo_printf, settings, user_info, argc, argv,
                   1229:            user_env);
                   1230:        break;
1.1.1.2   misho    1231:     case SUDO_API_MKVERSION(1, 1):
                   1232:        rval = plugin->u.io_1_1->open(plugin->u.io_1_1->version,
                   1233:            sudo_conversation, _sudo_printf, settings, user_info,
                   1234:            command_info, argc, argv, user_env);
                   1235:        break;
1.1       misho    1236:     default:
                   1237:        rval = plugin->u.io->open(SUDO_API_VERSION, sudo_conversation,
1.1.1.2   misho    1238:            _sudo_printf, settings, user_info, command_info,
                   1239:            argc, argv, user_env, plugin->options);
1.1       misho    1240:     }
1.1.1.2   misho    1241:     debug_return_bool(rval);
1.1       misho    1242: }
                   1243: 
                   1244: static void
                   1245: iolog_close(struct plugin_container *plugin, int exit_status, int error)
                   1246: {
1.1.1.2   misho    1247:     debug_decl(iolog_close, SUDO_DEBUG_PCOMM)
1.1.1.4 ! misho    1248:     if (plugin->u.io->close != NULL)
        !          1249:        plugin->u.io->close(exit_status, error);
1.1.1.2   misho    1250:     debug_return;
1.1       misho    1251: }
                   1252: 
                   1253: static int
                   1254: iolog_show_version(struct plugin_container *plugin, int verbose)
                   1255: {
1.1.1.2   misho    1256:     debug_decl(iolog_show_version, SUDO_DEBUG_PCOMM)
1.1.1.4 ! misho    1257:     if (plugin->u.io->show_version == NULL)
        !          1258:        debug_return_bool(true);
1.1.1.2   misho    1259:     debug_return_bool(plugin->u.io->show_version(verbose));
1.1       misho    1260: }
                   1261: 
                   1262: /*
1.1.1.2   misho    1263:  * Remove the specified I/O logging plugin from the io_plugins list.
                   1264:  * Deregisters any hooks before unlinking, then frees the container.
1.1       misho    1265:  */
1.1.1.2   misho    1266: static void
                   1267: iolog_unlink(struct plugin_container *plugin)
1.1       misho    1268: {
1.1.1.2   misho    1269:     debug_decl(iolog_unlink, SUDO_DEBUG_PCOMM)
1.1       misho    1270: 
1.1.1.2   misho    1271:     /* Deregister hooks, if any. */
                   1272:     if (plugin->u.io->version >= SUDO_API_MKVERSION(1, 2)) {
                   1273:        if (plugin->u.io->deregister_hooks != NULL)
                   1274:            plugin->u.io->deregister_hooks(SUDO_HOOK_VERSION,
                   1275:                deregister_hook);
                   1276:     }
                   1277:     /* Remove from io_plugins list and free. */
                   1278:     tq_remove(&io_plugins, plugin);
                   1279:     efree(plugin);
1.1       misho    1280: 
1.1.1.2   misho    1281:     debug_return;
1.1       misho    1282: }

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