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

1.1     ! misho       1: /*
        !             2:  * Copyright (c) 2009-2011 Todd C. Miller <Todd.Miller@courtesan.com>
        !             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/param.h>
        !            25: #include <sys/stat.h>
        !            26: #include <sys/wait.h>
        !            27: #include <sys/socket.h>
        !            28: #ifdef HAVE_SYS_SELECT_H
        !            29: # include <sys/select.h>
        !            30: #endif /* HAVE_SYS_SELECT_H */
        !            31: #include <sys/time.h>
        !            32: #include <sys/resource.h>
        !            33: #include <stdio.h>
        !            34: #ifdef STDC_HEADERS
        !            35: # include <stdlib.h>
        !            36: # include <stddef.h>
        !            37: #else
        !            38: # ifdef HAVE_STDLIB_H
        !            39: #  include <stdlib.h>
        !            40: # endif
        !            41: #endif /* STDC_HEADERS */
        !            42: #ifdef HAVE_STRING_H
        !            43: # if defined(HAVE_MEMORY_H) && !defined(STDC_HEADERS)
        !            44: #  include <memory.h>
        !            45: # endif
        !            46: # include <string.h>
        !            47: #endif /* HAVE_STRING_H */
        !            48: #ifdef HAVE_STRINGS_H
        !            49: # include <strings.h>
        !            50: #endif /* HAVE_STRINGS_H */
        !            51: #ifdef HAVE_UNISTD_H
        !            52: # include <unistd.h>
        !            53: #endif /* HAVE_UNISTD_H */
        !            54: #include <ctype.h>
        !            55: #include <errno.h>
        !            56: #include <fcntl.h>
        !            57: #include <limits.h>
        !            58: #include <signal.h>
        !            59: #include <grp.h>
        !            60: #include <pwd.h>
        !            61: #if TIME_WITH_SYS_TIME
        !            62: # include <time.h>
        !            63: #endif
        !            64: #ifdef HAVE_SETLOCALE
        !            65: # include <locale.h>
        !            66: #endif
        !            67: #ifdef HAVE_LOGIN_CAP_H
        !            68: # include <login_cap.h>
        !            69: #endif
        !            70: #ifdef HAVE_PROJECT_H
        !            71: # include <project.h>
        !            72: # include <sys/task.h>
        !            73: #endif
        !            74: #ifdef HAVE_SELINUX
        !            75: # include <selinux/selinux.h>
        !            76: #endif
        !            77: #ifdef HAVE_SETAUTHDB
        !            78: # include <usersec.h>
        !            79: #endif /* HAVE_SETAUTHDB */
        !            80: #if defined(HAVE_GETPRPWNAM) && defined(HAVE_SET_AUTH_PARAMETERS)
        !            81: # ifdef __hpux
        !            82: #  undef MAXINT
        !            83: #  include <hpsecurity.h>
        !            84: # else
        !            85: #  include <sys/security.h>
        !            86: # endif /* __hpux */
        !            87: # include <prot.h>
        !            88: #endif /* HAVE_GETPRPWNAM && HAVE_SET_AUTH_PARAMETERS */
        !            89: #ifdef HAVE_PRIV_SET
        !            90: # include <priv.h>
        !            91: #endif
        !            92: 
        !            93: #include "sudo.h"
        !            94: #include "sudo_plugin.h"
        !            95: #include "sudo_plugin_int.h"
        !            96: #include <sudo_usage.h>
        !            97: 
        !            98: /*
        !            99:  * Local variables
        !           100:  */
        !           101: struct plugin_container policy_plugin;
        !           102: struct plugin_container_list io_plugins;
        !           103: struct user_details user_details;
        !           104: const char *list_user, *runas_user, *runas_group; /* extern for parse_args.c */
        !           105: int debug_level;
        !           106: 
        !           107: /*
        !           108:  * Local functions
        !           109:  */
        !           110: static void fix_fds(void);
        !           111: static void disable_coredumps(void);
        !           112: static char **get_user_info(struct user_details *);
        !           113: static void command_info_to_details(char * const info[],
        !           114:     struct command_details *details);
        !           115: static int policy_open(struct plugin_container *plugin, char * const settings[],
        !           116:     char * const user_info[], char * const user_env[]);
        !           117: static void policy_close(struct plugin_container *plugin, int exit_status,
        !           118:     int error);
        !           119: static int iolog_open(struct plugin_container *plugin, char * const settings[],
        !           120:     char * const user_info[], char * const command_details[],
        !           121:     int argc, char * const argv[], char * const user_env[]);
        !           122: static void iolog_close(struct plugin_container *plugin, int exit_status,
        !           123:     int error);
        !           124: 
        !           125: /* Policy plugin convenience functions. */
        !           126: static int policy_open(struct plugin_container *plugin, char * const settings[],
        !           127:     char * const user_info[], char * const user_env[]);
        !           128: static void policy_close(struct plugin_container *plugin, int exit_status,
        !           129:     int error);
        !           130: static int policy_show_version(struct plugin_container *plugin, int verbose);
        !           131: static int policy_check(struct plugin_container *plugin, int argc,
        !           132:     char * const argv[], char *env_add[], char **command_info[],
        !           133:     char **argv_out[], char **user_env_out[]);
        !           134: static int policy_list(struct plugin_container *plugin, int argc,
        !           135:     char * const argv[], int verbose, const char *list_user);
        !           136: static int policy_validate(struct plugin_container *plugin);
        !           137: static void policy_invalidate(struct plugin_container *plugin, int remove);
        !           138: static int policy_init_session(struct plugin_container *plugin,
        !           139:     struct passwd *pwd);
        !           140: 
        !           141: /* I/O log plugin convenience functions. */
        !           142: static int iolog_open(struct plugin_container *plugin, char * const settings[],
        !           143:     char * const user_info[], char * const command_details[],
        !           144:     int argc, char * const argv[], char * const user_env[]);
        !           145: static void iolog_close(struct plugin_container *plugin, int exit_status,
        !           146:     int error);
        !           147: static int iolog_show_version(struct plugin_container *plugin, int verbose);
        !           148: 
        !           149: #if defined(RLIMIT_CORE) && !defined(SUDO_DEVEL)
        !           150: static struct rlimit corelimit;
        !           151: #endif /* RLIMIT_CORE && !SUDO_DEVEL */
        !           152: #if defined(__linux__)
        !           153: static struct rlimit nproclimit;
        !           154: #endif
        !           155: 
        !           156: int
        !           157: main(int argc, char *argv[], char *envp[])
        !           158: {
        !           159:     int nargc, sudo_mode, exitcode = 0;
        !           160:     char **nargv, **settings, **env_add;
        !           161:     char **user_info, **command_info, **argv_out, **user_env_out;
        !           162:     struct plugin_container *plugin, *next;
        !           163:     struct command_details command_details;
        !           164:     sigset_t mask;
        !           165:     int ok;
        !           166: #if defined(SUDO_DEVEL) && defined(__OpenBSD__)
        !           167:     extern char *malloc_options;
        !           168:     malloc_options = "AFGJPR";
        !           169: #endif
        !           170: 
        !           171: #if !defined(HAVE_GETPROGNAME) && !defined(HAVE___PROGNAME)
        !           172:     if (argc > 0)
        !           173:        setprogname(argv[0]);
        !           174: #endif
        !           175: 
        !           176: #ifdef HAVE_SETLOCALE
        !           177:     setlocale(LC_ALL, "");
        !           178: #endif
        !           179:     bindtextdomain(PACKAGE_NAME, LOCALEDIR);
        !           180:     textdomain(PACKAGE_NAME);
        !           181: 
        !           182:     /* Must be done before we do any password lookups */
        !           183: #if defined(HAVE_GETPRPWNAM) && defined(HAVE_SET_AUTH_PARAMETERS)
        !           184:     (void) set_auth_parameters(argc, argv);
        !           185: # ifdef HAVE_INITPRIVS
        !           186:     initprivs();
        !           187: # endif
        !           188: #endif /* HAVE_GETPRPWNAM && HAVE_SET_AUTH_PARAMETERS */
        !           189: 
        !           190:     if (geteuid() != 0)
        !           191:        errorx(1, _("must be setuid root"));
        !           192: 
        !           193:     /* Reset signal mask, disable core dumps and make sure fds 0-2 are open. */
        !           194:     (void) sigemptyset(&mask);
        !           195:     (void) sigprocmask(SIG_SETMASK, &mask, NULL);
        !           196:     disable_coredumps();
        !           197:     fix_fds();
        !           198: 
        !           199:     /* Fill in user_info with user name, uid, cwd, etc. */
        !           200:     memset(&user_details, 0, sizeof(user_details));
        !           201:     user_info = get_user_info(&user_details);
        !           202: 
        !           203:     /* Parse command line arguments. */
        !           204:     sudo_mode = parse_args(argc, argv, &nargc, &nargv, &settings, &env_add);
        !           205:     sudo_debug(9, "sudo_mode %d", sudo_mode);
        !           206: 
        !           207:     /* Print sudo version early, in case of plugin init failure. */
        !           208:     if (ISSET(sudo_mode, MODE_VERSION)) {
        !           209:        printf(_("Sudo version %s\n"), PACKAGE_VERSION);
        !           210:        if (user_details.uid == ROOT_UID)
        !           211:            (void) printf(_("Configure options: %s\n"), CONFIGURE_ARGS);
        !           212:     }
        !           213: 
        !           214:     /* Read sudo.conf and load plugins. */
        !           215:     if (!sudo_load_plugins(_PATH_SUDO_CONF, &policy_plugin, &io_plugins))
        !           216:        errorx(1, _("fatal error, unable to load plugins"));
        !           217: 
        !           218:     /* Open policy plugin. */
        !           219:     ok = policy_open(&policy_plugin, settings, user_info, envp);
        !           220:     if (ok != TRUE) {
        !           221:        if (ok == -2)
        !           222:            usage(1);
        !           223:        else
        !           224:            errorx(1, _("unable to initialize policy plugin"));
        !           225:     }
        !           226: 
        !           227:     switch (sudo_mode & MODE_MASK) {
        !           228:        case MODE_VERSION:
        !           229:            policy_show_version(&policy_plugin, !user_details.uid);
        !           230:            tq_foreach_fwd(&io_plugins, plugin) {
        !           231:                ok = iolog_open(plugin, settings, user_info, NULL,
        !           232:                    nargc, nargv, envp);
        !           233:                if (ok == TRUE)
        !           234:                    iolog_show_version(plugin, !user_details.uid);
        !           235:            }
        !           236:            break;
        !           237:        case MODE_VALIDATE:
        !           238:        case MODE_VALIDATE|MODE_INVALIDATE:
        !           239:            ok = policy_validate(&policy_plugin);
        !           240:            exit(ok != TRUE);
        !           241:        case MODE_KILL:
        !           242:        case MODE_INVALIDATE:
        !           243:            policy_invalidate(&policy_plugin, sudo_mode == MODE_KILL);
        !           244:            exit(0);
        !           245:            break;
        !           246:        case MODE_CHECK:
        !           247:        case MODE_CHECK|MODE_INVALIDATE:
        !           248:        case MODE_LIST:
        !           249:        case MODE_LIST|MODE_INVALIDATE:
        !           250:            ok = policy_list(&policy_plugin, nargc, nargv,
        !           251:                ISSET(sudo_mode, MODE_LONG_LIST), list_user);
        !           252:            exit(ok != TRUE);
        !           253:        case MODE_EDIT:
        !           254:        case MODE_RUN:
        !           255:            ok = policy_check(&policy_plugin, nargc, nargv, env_add,
        !           256:                &command_info, &argv_out, &user_env_out);
        !           257:            sudo_debug(8, "policy plugin returns %d", ok);
        !           258:            if (ok != TRUE) {
        !           259:                if (ok == -2)
        !           260:                    usage(1);
        !           261:                exit(1); /* plugin printed error message */
        !           262:            }
        !           263:            /* Open I/O plugins once policy plugin succeeds. */
        !           264:            for (plugin = io_plugins.first; plugin != NULL; plugin = next) {
        !           265:                next = plugin->next;
        !           266:                ok = iolog_open(plugin, settings, user_info,
        !           267:                    command_info, nargc, nargv, envp);
        !           268:                switch (ok) {
        !           269:                case TRUE:
        !           270:                    break;
        !           271:                case FALSE:
        !           272:                    /* I/O plugin asked to be disabled, remove from list. */
        !           273:                    tq_remove(&io_plugins, plugin);
        !           274:                    break;
        !           275:                case -2:
        !           276:                    usage(1);
        !           277:                    break;
        !           278:                default:
        !           279:                    errorx(1, _("error initializing I/O plugin %s"),
        !           280:                        plugin->name);
        !           281:                }
        !           282:            }
        !           283:            command_info_to_details(command_info, &command_details);
        !           284:            command_details.argv = argv_out;
        !           285:            command_details.envp = user_env_out;
        !           286:            if (ISSET(sudo_mode, MODE_BACKGROUND))
        !           287:                SET(command_details.flags, CD_BACKGROUND);
        !           288:            /* Restore coredumpsize resource limit before running. */
        !           289: #if defined(RLIMIT_CORE) && !defined(SUDO_DEVEL)
        !           290:            (void) setrlimit(RLIMIT_CORE, &corelimit);
        !           291: #endif /* RLIMIT_CORE && !SUDO_DEVEL */
        !           292:            if (ISSET(command_details.flags, CD_SUDOEDIT)) {
        !           293:                exitcode = sudo_edit(&command_details);
        !           294:            } else {
        !           295:                exitcode = run_command(&command_details);
        !           296:            }
        !           297:            /* The close method was called by sudo_edit/run_command. */
        !           298:            break;
        !           299:        default:
        !           300:            errorx(1, _("unexpected sudo mode 0x%x"), sudo_mode);
        !           301:     }
        !           302:     exit(exitcode);
        !           303: }
        !           304: 
        !           305: /*
        !           306:  * Ensure that stdin, stdout and stderr are open; set to /dev/null if not.
        !           307:  * Some operating systems do this automatically in the kernel or libc.
        !           308:  */
        !           309: static void
        !           310: fix_fds(void)
        !           311: {
        !           312:     int miss[3], devnull = -1;
        !           313: 
        !           314:     /*
        !           315:      * stdin, stdout and stderr must be open; set them to /dev/null
        !           316:      * if they are closed.
        !           317:      */
        !           318:     miss[STDIN_FILENO] = fcntl(STDIN_FILENO, F_GETFL, 0) == -1;
        !           319:     miss[STDOUT_FILENO] = fcntl(STDOUT_FILENO, F_GETFL, 0) == -1;
        !           320:     miss[STDERR_FILENO] = fcntl(STDERR_FILENO, F_GETFL, 0) == -1;
        !           321:     if (miss[STDIN_FILENO] || miss[STDOUT_FILENO] || miss[STDERR_FILENO]) {
        !           322:        if ((devnull = open(_PATH_DEVNULL, O_RDWR, 0644)) == -1)
        !           323:            error(1, _("unable to open %s"), _PATH_DEVNULL);
        !           324:        if (miss[STDIN_FILENO] && dup2(devnull, STDIN_FILENO) == -1)
        !           325:            error(1, "dup2");
        !           326:        if (miss[STDOUT_FILENO] && dup2(devnull, STDOUT_FILENO) == -1)
        !           327:            error(1, "dup2");
        !           328:        if (miss[STDERR_FILENO] && dup2(devnull, STDERR_FILENO) == -1)
        !           329:            error(1, "dup2");
        !           330:        if (devnull > STDERR_FILENO)
        !           331:            close(devnull);
        !           332:     }
        !           333: }
        !           334: 
        !           335: /*
        !           336:  * Allocate space for groups and fill in using getgrouplist()
        !           337:  * for when we cannot use getgroups().
        !           338:  */
        !           339: static int
        !           340: fill_group_list(struct user_details *ud)
        !           341: {
        !           342:     int maxgroups, tries, rval = -1;
        !           343: 
        !           344: #if defined(HAVE_SYSCONF) && defined(_SC_NGROUPS_MAX)
        !           345:     maxgroups = (int)sysconf(_SC_NGROUPS_MAX);
        !           346:     if (maxgroups < 0)
        !           347: #endif
        !           348:        maxgroups = NGROUPS_MAX;
        !           349: 
        !           350:     /*
        !           351:      * It is possible to belong to more groups in the group database
        !           352:      * than NGROUPS_MAX.  We start off with NGROUPS_MAX * 2 entries
        !           353:      * and double this as needed.
        !           354:      */
        !           355:     ud->groups = NULL;
        !           356:     ud->ngroups = maxgroups;
        !           357:     for (tries = 0; tries < 10 && rval == -1; tries++) {
        !           358:        ud->ngroups *= 2;
        !           359:        efree(ud->groups);
        !           360:        ud->groups = emalloc2(ud->ngroups, sizeof(GETGROUPS_T));
        !           361:        rval = getgrouplist(ud->username, ud->gid, ud->groups, &ud->ngroups);
        !           362:     }
        !           363:     return rval;
        !           364: }
        !           365: 
        !           366: static char *
        !           367: get_user_groups(struct user_details *ud)
        !           368: {
        !           369:     char *cp, *gid_list = NULL;
        !           370:     size_t glsize;
        !           371:     int i, len;
        !           372: 
        !           373:     /*
        !           374:      * Systems with mbr_check_membership() support more than NGROUPS_MAX
        !           375:      * groups so we cannot use getgroups().
        !           376:      */
        !           377:     ud->groups = NULL;
        !           378: #ifndef HAVE_MBR_CHECK_MEMBERSHIP
        !           379:     if ((ud->ngroups = getgroups(0, NULL)) > 0) {
        !           380:        ud->groups = emalloc2(ud->ngroups, sizeof(GETGROUPS_T));
        !           381:        if (getgroups(ud->ngroups, ud->groups) < 0) {
        !           382:            efree(ud->groups);
        !           383:            ud->groups = NULL;
        !           384:        }
        !           385:     }
        !           386: #endif /* HAVE_MBR_CHECK_MEMBERSHIP */
        !           387:     if (ud->groups == NULL) {
        !           388:        if (fill_group_list(ud) == -1)
        !           389:            error(1, _("unable to get group vector"));
        !           390:     }
        !           391: 
        !           392:     /*
        !           393:      * Format group list as a comma-separated string of gids.
        !           394:      */
        !           395:     glsize = sizeof("groups=") - 1 + (ud->ngroups * (MAX_UID_T_LEN + 1));
        !           396:     gid_list = emalloc(glsize);
        !           397:     memcpy(gid_list, "groups=", sizeof("groups=") - 1);
        !           398:     cp = gid_list + sizeof("groups=") - 1;
        !           399:     for (i = 0; i < ud->ngroups; i++) {
        !           400:        /* XXX - check rval */
        !           401:        len = snprintf(cp, glsize - (cp - gid_list), "%s%u",
        !           402:            i ? "," : "", (unsigned int)ud->groups[i]);
        !           403:        cp += len;
        !           404:     }
        !           405:     return gid_list;
        !           406: }
        !           407: 
        !           408: /*
        !           409:  * Return user information as an array of name=value pairs.
        !           410:  * and fill in struct user_details (which shares the same strings).
        !           411:  */
        !           412: static char **
        !           413: get_user_info(struct user_details *ud)
        !           414: {
        !           415:     char cwd[PATH_MAX];
        !           416:     char host[MAXHOSTNAMELEN];
        !           417:     char **user_info, *cp;
        !           418:     struct passwd *pw;
        !           419:     int i = 0;
        !           420: 
        !           421:     /* XXX - bound check number of entries */
        !           422:     user_info = emalloc2(32, sizeof(char *));
        !           423: 
        !           424:     ud->uid = getuid();
        !           425:     ud->euid = geteuid();
        !           426:     ud->gid = getgid();
        !           427:     ud->egid = getegid();
        !           428: 
        !           429:     pw = getpwuid(ud->uid);
        !           430:     if (pw == NULL)
        !           431:        errorx(1, _("unknown uid %u: who are you?"), (unsigned int)ud->uid);
        !           432: 
        !           433:     user_info[i] = fmt_string("user", pw->pw_name);
        !           434:     if (user_info[i] == NULL)
        !           435:        errorx(1, _("unable to allocate memory"));
        !           436:     ud->username = user_info[i] + sizeof("user=") - 1;
        !           437: 
        !           438:     /* Stash user's shell for use with the -s flag; don't pass to plugin. */
        !           439:     if ((ud->shell = getenv("SHELL")) == NULL || ud->shell[0] == '\0') {
        !           440:        ud->shell = pw->pw_shell[0] ? pw->pw_shell : _PATH_BSHELL;
        !           441:     }
        !           442:     ud->shell = estrdup(ud->shell);
        !           443: 
        !           444:     easprintf(&user_info[++i], "uid=%u", (unsigned int)ud->uid);
        !           445:     easprintf(&user_info[++i], "euid=%u", (unsigned int)ud->euid);
        !           446:     easprintf(&user_info[++i], "gid=%u", (unsigned int)ud->gid);
        !           447:     easprintf(&user_info[++i], "egid=%u", (unsigned int)ud->egid);
        !           448: 
        !           449:     if ((cp = get_user_groups(ud)) != NULL)
        !           450:        user_info[++i] = cp;
        !           451: 
        !           452:     if (getcwd(cwd, sizeof(cwd)) != NULL) {
        !           453:        user_info[++i] = fmt_string("cwd", cwd);
        !           454:        if (user_info[i] == NULL)
        !           455:            errorx(1, _("unable to allocate memory"));
        !           456:        ud->cwd = user_info[i] + sizeof("cwd=") - 1;
        !           457:     }
        !           458: 
        !           459:     if ((cp = ttyname(STDIN_FILENO)) || (cp = ttyname(STDOUT_FILENO)) ||
        !           460:        (cp = ttyname(STDERR_FILENO))) {
        !           461:        user_info[++i] = fmt_string("tty", cp);
        !           462:        if (user_info[i] == NULL)
        !           463:            errorx(1, _("unable to allocate memory"));
        !           464:        ud->tty = user_info[i] + sizeof("tty=") - 1;
        !           465:     }
        !           466: 
        !           467:     if (gethostname(host, sizeof(host)) == 0)
        !           468:        host[sizeof(host) - 1] = '\0';
        !           469:     else
        !           470:        strlcpy(host, "localhost", sizeof(host));
        !           471:     user_info[++i] = fmt_string("host", host);
        !           472:     if (user_info[i] == NULL)
        !           473:        errorx(1, _("unable to allocate memory"));
        !           474:     ud->host = user_info[i] + sizeof("host=") - 1;
        !           475: 
        !           476:     get_ttysize(&ud->ts_lines, &ud->ts_cols);
        !           477:     easprintf(&user_info[++i], "lines=%d", ud->ts_lines);
        !           478:     easprintf(&user_info[++i], "cols=%d", ud->ts_cols);
        !           479: 
        !           480:     user_info[++i] = NULL;
        !           481: 
        !           482:     return user_info;
        !           483: }
        !           484: 
        !           485: /*
        !           486:  * Convert a command_info array into a command_details structure.
        !           487:  */
        !           488: static void
        !           489: command_info_to_details(char * const info[], struct command_details *details)
        !           490: {
        !           491:     int i;
        !           492:     long lval;
        !           493:     unsigned long ulval;
        !           494:     char *cp, *ep;
        !           495: 
        !           496:     memset(details, 0, sizeof(*details));
        !           497:     details->closefrom = -1;
        !           498: 
        !           499: #define SET_STRING(s, n) \
        !           500:     if (strncmp(s, info[i], sizeof(s) - 1) == 0 && info[i][sizeof(s) - 1]) { \
        !           501:        details->n = info[i] + sizeof(s) - 1; \
        !           502:        break; \
        !           503:     }
        !           504: 
        !           505:     for (i = 0; info[i] != NULL; i++) {
        !           506:        sudo_debug(9, "command info: %s", info[i]);
        !           507:        switch (info[i][0]) {
        !           508:            case 'c':
        !           509:                SET_STRING("chroot=", chroot)
        !           510:                SET_STRING("command=", command)
        !           511:                SET_STRING("cwd=", cwd)
        !           512:                if (strncmp("closefrom=", info[i], sizeof("closefrom=") - 1) == 0) {
        !           513:                    cp = info[i] + sizeof("closefrom=") - 1;
        !           514:                    if (*cp == '\0')
        !           515:                        break;
        !           516:                    errno = 0;
        !           517:                    lval = strtol(cp, &ep, 0);
        !           518:                    if (*cp != '\0' && *ep == '\0' &&
        !           519:                        !(errno == ERANGE &&
        !           520:                        (lval == LONG_MAX || lval == LONG_MIN)) &&
        !           521:                        lval < INT_MAX && lval > INT_MIN) {
        !           522:                        details->closefrom = (int)lval;
        !           523:                    }
        !           524:                    break;
        !           525:                }
        !           526:                break;
        !           527:            case 'l':
        !           528:                SET_STRING("login_class=", login_class)
        !           529:                break;
        !           530:            case 'n':
        !           531:                /* XXX - bounds check  -NZERO to NZERO (inclusive). */
        !           532:                if (strncmp("nice=", info[i], sizeof("nice=") - 1) == 0) {
        !           533:                    cp = info[i] + sizeof("nice=") - 1;
        !           534:                    if (*cp == '\0')
        !           535:                        break;
        !           536:                    errno = 0;
        !           537:                    lval = strtol(cp, &ep, 0);
        !           538:                    if (*cp != '\0' && *ep == '\0' &&
        !           539:                        !(errno == ERANGE &&
        !           540:                        (lval == LONG_MAX || lval == LONG_MIN)) &&
        !           541:                        lval < INT_MAX && lval > INT_MIN) {
        !           542:                        details->priority = (int)lval;
        !           543:                        SET(details->flags, CD_SET_PRIORITY);
        !           544:                    }
        !           545:                    break;
        !           546:                }
        !           547:                if (strncmp("noexec=", info[i], sizeof("noexec=") - 1) == 0) {
        !           548:                    if (atobool(info[i] + sizeof("noexec=") - 1) == TRUE)
        !           549:                        SET(details->flags, CD_NOEXEC);
        !           550:                    break;
        !           551:                }
        !           552: #ifdef _PATH_SUDO_NOEXEC
        !           553:                /* XXX - deprecated */
        !           554:                if (strncmp("noexec_file=", info[i], sizeof("noexec_file=") - 1) == 0) {
        !           555:                    noexec_path = info[i] + sizeof("noexec_file=") - 1;
        !           556:                    break;
        !           557:                }
        !           558: #endif /* _PATH_SUDO_NOEXEC */
        !           559:                break;
        !           560:            case 'p':
        !           561:                if (strncmp("preserve_groups=", info[i], sizeof("preserve_groups=") - 1) == 0) {
        !           562:                    if (atobool(info[i] + sizeof("preserve_groups=") - 1) == TRUE)
        !           563:                        SET(details->flags, CD_PRESERVE_GROUPS);
        !           564:                    break;
        !           565:                }
        !           566:                break;
        !           567:            case 'r':
        !           568:                if (strncmp("runas_egid=", info[i], sizeof("runas_egid=") - 1) == 0) {
        !           569:                    cp = info[i] + sizeof("runas_egid=") - 1;
        !           570:                    if (*cp == '\0')
        !           571:                        break;
        !           572:                    errno = 0;
        !           573:                    ulval = strtoul(cp, &ep, 0);
        !           574:                    if (*cp != '\0' && *ep == '\0' &&
        !           575:                        (errno != ERANGE || ulval != ULONG_MAX)) {
        !           576:                        details->egid = (gid_t)ulval;
        !           577:                        SET(details->flags, CD_SET_EGID);
        !           578:                    }
        !           579:                    break;
        !           580:                }
        !           581:                if (strncmp("runas_euid=", info[i], sizeof("runas_euid=") - 1) == 0) {
        !           582:                    cp = info[i] + sizeof("runas_euid=") - 1;
        !           583:                    if (*cp == '\0')
        !           584:                        break;
        !           585:                    errno = 0;
        !           586:                    ulval = strtoul(cp, &ep, 0);
        !           587:                    if (*cp != '\0' && *ep == '\0' &&
        !           588:                        (errno != ERANGE || ulval != ULONG_MAX)) {
        !           589:                        details->euid = (uid_t)ulval;
        !           590:                        SET(details->flags, CD_SET_EUID);
        !           591:                    }
        !           592:                    break;
        !           593:                }
        !           594:                if (strncmp("runas_gid=", info[i], sizeof("runas_gid=") - 1) == 0) {
        !           595:                    cp = info[i] + sizeof("runas_gid=") - 1;
        !           596:                    if (*cp == '\0')
        !           597:                        break;
        !           598:                    errno = 0;
        !           599:                    ulval = strtoul(cp, &ep, 0);
        !           600:                    if (*cp != '\0' && *ep == '\0' &&
        !           601:                        (errno != ERANGE || ulval != ULONG_MAX)) {
        !           602:                        details->gid = (gid_t)ulval;
        !           603:                        SET(details->flags, CD_SET_GID);
        !           604:                    }
        !           605:                    break;
        !           606:                }
        !           607:                if (strncmp("runas_groups=", info[i], sizeof("runas_groups=") - 1) == 0) {
        !           608:                    int j;
        !           609: 
        !           610:                    /* count groups, alloc and fill in */
        !           611:                    cp = info[i] + sizeof("runas_groups=") - 1;
        !           612:                    if (*cp == '\0')
        !           613:                        break;
        !           614:                    for (;;) {
        !           615:                        details->ngroups++;
        !           616:                        if ((cp = strchr(cp, ',')) == NULL)
        !           617:                            break;
        !           618:                        cp++;
        !           619:                    }
        !           620:                    if (details->ngroups != 0) {
        !           621:                        details->groups =
        !           622:                            emalloc2(details->ngroups, sizeof(GETGROUPS_T));
        !           623:                        cp = info[i] + sizeof("runas_groups=") - 1;
        !           624:                        for (j = 0; j < details->ngroups;) {
        !           625:                            errno = 0;
        !           626:                            ulval = strtoul(cp, &ep, 0);
        !           627:                            if (*cp == '\0' || (*ep != ',' && *ep != '\0') ||
        !           628:                                (ulval == ULONG_MAX && errno == ERANGE)) {
        !           629:                                break;
        !           630:                            }
        !           631:                            details->groups[j++] = (gid_t)ulval;
        !           632:                            cp = ep + 1;
        !           633:                        }
        !           634:                        details->ngroups = j;
        !           635:                    }
        !           636:                    break;
        !           637:                }
        !           638:                if (strncmp("runas_uid=", info[i], sizeof("runas_uid=") - 1) == 0) {
        !           639:                    cp = info[i] + sizeof("runas_uid=") - 1;
        !           640:                    if (*cp == '\0')
        !           641:                        break;
        !           642:                    errno = 0;
        !           643:                    ulval = strtoul(cp, &ep, 0);
        !           644:                    if (*cp != '\0' && *ep == '\0' &&
        !           645:                        (errno != ERANGE || ulval != ULONG_MAX)) {
        !           646:                        details->uid = (uid_t)ulval;
        !           647:                        SET(details->flags, CD_SET_UID);
        !           648:                    }
        !           649:                    break;
        !           650:                }
        !           651:                break;
        !           652:            case 's':
        !           653:                SET_STRING("selinux_role=", selinux_role)
        !           654:                SET_STRING("selinux_type=", selinux_type)
        !           655:                if (strncmp("set_utmp=", info[i], sizeof("set_utmp=") - 1) == 0) {
        !           656:                    if (atobool(info[i] + sizeof("set_utmp=") - 1) == TRUE)
        !           657:                        SET(details->flags, CD_SET_UTMP);
        !           658:                    break;
        !           659:                }
        !           660:                if (strncmp("sudoedit=", info[i], sizeof("sudoedit=") - 1) == 0) {
        !           661:                    if (atobool(info[i] + sizeof("sudoedit=") - 1) == TRUE)
        !           662:                        SET(details->flags, CD_SUDOEDIT);
        !           663:                    break;
        !           664:                }
        !           665:                break;
        !           666:            case 't':
        !           667:                if (strncmp("timeout=", info[i], sizeof("timeout=") - 1) == 0) {
        !           668:                    cp = info[i] + sizeof("timeout=") - 1;
        !           669:                    if (*cp == '\0')
        !           670:                        break;
        !           671:                    errno = 0;
        !           672:                    lval = strtol(cp, &ep, 0);
        !           673:                    if (*cp != '\0' && *ep == '\0' &&
        !           674:                        !(errno == ERANGE &&
        !           675:                        (lval == LONG_MAX || lval == LONG_MIN)) &&
        !           676:                        lval <= INT_MAX && lval >= 0) {
        !           677:                        details->timeout = (int)lval;
        !           678:                        SET(details->flags, CD_SET_TIMEOUT);
        !           679:                    }
        !           680:                    break;
        !           681:                }
        !           682:                break;
        !           683:            case 'u':
        !           684:                if (strncmp("umask=", info[i], sizeof("umask=") - 1) == 0) {
        !           685:                    cp = info[i] + sizeof("umask=") - 1;
        !           686:                    if (*cp == '\0')
        !           687:                        break;
        !           688:                    errno = 0;
        !           689:                    ulval = strtoul(cp, &ep, 8);
        !           690:                    if (*cp != '\0' && *ep == '\0' &&
        !           691:                        (errno != ERANGE || ulval != ULONG_MAX)) {
        !           692:                        details->umask = (uid_t)ulval;
        !           693:                        SET(details->flags, CD_SET_UMASK);
        !           694:                    }
        !           695:                    break;
        !           696:                }
        !           697:                if (strncmp("use_pty=", info[i], sizeof("use_pty=") - 1) == 0) {
        !           698:                    if (atobool(info[i] + sizeof("use_pty=") - 1) == TRUE)
        !           699:                        SET(details->flags, CD_USE_PTY);
        !           700:                    break;
        !           701:                }
        !           702:                SET_STRING("utmp_user=", utmp_user)
        !           703:                break;
        !           704:        }
        !           705:     }
        !           706: 
        !           707:     if (!ISSET(details->flags, CD_SET_EUID))
        !           708:        details->euid = details->uid;
        !           709: 
        !           710: #ifdef HAVE_SELINUX
        !           711:     if (details->selinux_role != NULL && is_selinux_enabled() > 0)
        !           712:        SET(details->flags, CD_RBAC_ENABLED);
        !           713: #endif
        !           714: }
        !           715: 
        !           716: /*
        !           717:  * Disable core dumps to avoid dropping a core with user password in it.
        !           718:  * We will reset this limit before executing the command.
        !           719:  * Not all operating systems disable core dumps for setuid processes.
        !           720:  */
        !           721: static void
        !           722: disable_coredumps(void)
        !           723: {
        !           724: #if defined(__linux__) || (defined(RLIMIT_CORE) && !defined(SUDO_DEVEL))
        !           725:     struct rlimit rl;
        !           726: #endif
        !           727: 
        !           728: #if defined(__linux__)
        !           729:     /*
        !           730:      * Unlimit the number of processes since Linux's setuid() will
        !           731:      * apply resource limits when changing uid and return EAGAIN if
        !           732:      * nproc would be violated by the uid switch.
        !           733:      */
        !           734:     (void) getrlimit(RLIMIT_NPROC, &nproclimit);
        !           735:     rl.rlim_cur = rl.rlim_max = RLIM_INFINITY;
        !           736:     if (setrlimit(RLIMIT_NPROC, &rl)) {
        !           737:        memcpy(&rl, &nproclimit, sizeof(struct rlimit));
        !           738:        rl.rlim_cur = rl.rlim_max;
        !           739:        (void)setrlimit(RLIMIT_NPROC, &rl);
        !           740:     }
        !           741: #endif /* __linux__ */
        !           742: #if defined(RLIMIT_CORE) && !defined(SUDO_DEVEL)
        !           743:     /*
        !           744:      * Turn off core dumps.
        !           745:      */
        !           746:     (void) getrlimit(RLIMIT_CORE, &corelimit);
        !           747:     memcpy(&rl, &corelimit, sizeof(struct rlimit));
        !           748:     rl.rlim_cur = 0;
        !           749:     (void) setrlimit(RLIMIT_CORE, &rl);
        !           750: #endif /* RLIMIT_CORE && !SUDO_DEVEL */
        !           751: }
        !           752: 
        !           753: #ifdef HAVE_PROJECT_H
        !           754: static void
        !           755: set_project(struct passwd *pw)
        !           756: {
        !           757:     struct project proj;
        !           758:     char buf[PROJECT_BUFSZ];
        !           759:     int errval;
        !           760: 
        !           761:     /*
        !           762:      * Collect the default project for the user and settaskid
        !           763:      */
        !           764:     setprojent();
        !           765:     if (getdefaultproj(pw->pw_name, &proj, buf, sizeof(buf)) != NULL) {
        !           766:        errval = setproject(proj.pj_name, pw->pw_name, TASK_NORMAL);
        !           767:        switch(errval) {
        !           768:        case 0:
        !           769:            break;
        !           770:        case SETPROJ_ERR_TASK:
        !           771:            switch (errno) {
        !           772:            case EAGAIN:
        !           773:                warningx(_("resource control limit has been reached"));
        !           774:                break;
        !           775:            case ESRCH:
        !           776:                warningx(_("user \"%s\" is not a member of project \"%s\""),
        !           777:                    pw->pw_name, proj.pj_name);
        !           778:                break;
        !           779:            case EACCES:
        !           780:                warningx(_("the invoking task is final"));
        !           781:                break;
        !           782:            default:
        !           783:                warningx(_("could not join project \"%s\""), proj.pj_name);
        !           784:            }
        !           785:        case SETPROJ_ERR_POOL:
        !           786:            switch (errno) {
        !           787:            case EACCES:
        !           788:                warningx(_("no resource pool accepting default bindings "
        !           789:                    "exists for project \"%s\""), proj.pj_name);
        !           790:                break;
        !           791:            case ESRCH:
        !           792:                warningx(_("specified resource pool does not exist for "
        !           793:                    "project \"%s\""), proj.pj_name);
        !           794:                break;
        !           795:            default:
        !           796:                warningx(_("could not bind to default resource pool for "
        !           797:                    "project \"%s\""), proj.pj_name);
        !           798:            }
        !           799:            break;
        !           800:        default:
        !           801:            if (errval <= 0) {
        !           802:                warningx(_("setproject failed for project \"%s\""), proj.pj_name);
        !           803:            } else {
        !           804:                warningx(_("warning, resource control assignment failed for "
        !           805:                    "project \"%s\""), proj.pj_name);
        !           806:            }
        !           807:        }
        !           808:     } else {
        !           809:        warning("getdefaultproj");
        !           810:     }
        !           811:     endprojent();
        !           812: }
        !           813: #endif /* HAVE_PROJECT_H */
        !           814: 
        !           815: /*
        !           816:  * Disable execution of child processes in the command we are about
        !           817:  * to run.  On systems with privilege sets, we can remove the exec
        !           818:  * privilege.  On other systems we use LD_PRELOAD and the like.
        !           819:  */
        !           820: static void
        !           821: disable_execute(struct command_details *details)
        !           822: {
        !           823: #ifdef _PATH_SUDO_NOEXEC
        !           824:     char *cp, **ev, **nenvp;
        !           825:     int env_len = 0, env_size = 128;
        !           826: #endif /* _PATH_SUDO_NOEXEC */
        !           827: 
        !           828: #ifdef HAVE_PRIV_SET
        !           829:     /* Solaris privileges, remove PRIV_PROC_EXEC post-execve. */
        !           830:     if (priv_set(PRIV_OFF, PRIV_LIMIT, "PRIV_PROC_EXEC", NULL) == 0)
        !           831:        return;
        !           832:     warning(_("unable to remove PRIV_PROC_EXEC from PRIV_LIMIT"));
        !           833: #endif /* HAVE_PRIV_SET */
        !           834: 
        !           835: #ifdef _PATH_SUDO_NOEXEC
        !           836:     nenvp = emalloc2(env_size, sizeof(char *));
        !           837:     for (ev = details->envp; *ev != NULL; ev++) {
        !           838:        if (env_len + 2 > env_size) {
        !           839:            env_size += 128;
        !           840:            nenvp = erealloc3(nenvp, env_size, sizeof(char *));
        !           841:        }
        !           842:        /*
        !           843:         * Prune out existing preloaded libraries.
        !           844:         * XXX - should save and append instead of replacing.
        !           845:         */
        !           846: # if defined(__darwin__) || defined(__APPLE__)
        !           847:        if (strncmp(*ev, "DYLD_INSERT_LIBRARIES=", sizeof("DYLD_INSERT_LIBRARIES=") - 1) == 0)
        !           848:            continue;
        !           849:        if (strncmp(*ev, "DYLD_FORCE_FLAT_NAMESPACE=", sizeof("DYLD_INSERT_LIBRARIES=") - 1) == 0)
        !           850:            continue;
        !           851: # elif defined(__osf__) || defined(__sgi)
        !           852:        if (strncmp(*ev, "_RLD_LIST=", sizeof("_RLD_LIST=") - 1) == 0)
        !           853:            continue;
        !           854: # elif defined(_AIX)
        !           855:        if (strncmp(*ev, "LDR_PRELOAD=", sizeof("LDR_PRELOAD=") - 1) == 0)
        !           856:            continue;
        !           857: # else
        !           858:        if (strncmp(*ev, "LD_PRELOAD=", sizeof("LD_PRELOAD=") - 1) == 0)
        !           859:            continue;
        !           860: # endif
        !           861:        nenvp[env_len++] = *ev;
        !           862:     }
        !           863: 
        !           864:     /*
        !           865:      * Preload a noexec file?  For a list of LD_PRELOAD-alikes, see
        !           866:      * http://www.fortran-2000.com/ArnaudRecipes/sharedlib.html
        !           867:      * XXX - need to support 32-bit and 64-bit variants
        !           868:      */
        !           869: # if defined(__darwin__) || defined(__APPLE__)
        !           870:     nenvp[env_len++] = "DYLD_FORCE_FLAT_NAMESPACE=";
        !           871:     cp = fmt_string("DYLD_INSERT_LIBRARIES", noexec_path);
        !           872: # elif defined(__osf__) || defined(__sgi)
        !           873:     easprintf(&cp, "_RLD_LIST=%s:DEFAULT", noexec_path);
        !           874: # elif defined(_AIX)
        !           875:     cp = fmt_string("LDR_PRELOAD", noexec_path);
        !           876: # else
        !           877:     cp = fmt_string("LD_PRELOAD", noexec_path);
        !           878: # endif
        !           879:     if (cp == NULL)
        !           880:        error(1, NULL);
        !           881:     nenvp[env_len++] = cp;
        !           882:     nenvp[env_len] = NULL;
        !           883: 
        !           884:     details->envp = nenvp;
        !           885: #endif /* _PATH_SUDO_NOEXEC */
        !           886: }
        !           887: 
        !           888: /*
        !           889:  * Setup the execution environment immediately prior to the call to execve()
        !           890:  * Returns TRUE on success and FALSE on failure.
        !           891:  */
        !           892: int
        !           893: exec_setup(struct command_details *details, const char *ptyname, int ptyfd)
        !           894: {
        !           895:     int rval = FALSE;
        !           896:     struct passwd *pw;
        !           897: 
        !           898: #ifdef HAVE_SETAUTHDB
        !           899:     aix_setauthdb(IDtouser(details->euid));
        !           900: #endif
        !           901:     pw = getpwuid(details->euid);
        !           902: #ifdef HAVE_SETAUTHDB
        !           903:     aix_restoreauthdb();
        !           904: #endif
        !           905: 
        !           906:     /*
        !           907:      * Call policy plugin's session init before other setup occurs.
        !           908:      * The session init code is expected to print an error as needed.
        !           909:      */
        !           910:     if (policy_init_session(&policy_plugin, pw) != TRUE)
        !           911:        goto done;
        !           912: 
        !           913: #ifdef HAVE_SELINUX
        !           914:     if (ISSET(details->flags, CD_RBAC_ENABLED)) {
        !           915:        if (selinux_setup(details->selinux_role, details->selinux_type,
        !           916:            ptyname ? ptyname : user_details.tty, ptyfd) == -1)
        !           917:            goto done;
        !           918:     }
        !           919: #endif
        !           920: 
        !           921:     if (pw != NULL) {
        !           922: #ifdef HAVE_PROJECT_H
        !           923:        set_project(pw);
        !           924: #endif
        !           925: #ifdef HAVE_GETUSERATTR
        !           926:        aix_prep_user(pw->pw_name, ptyname ? ptyname : user_details.tty);
        !           927: #endif
        !           928: #ifdef HAVE_LOGIN_CAP_H
        !           929:        if (details->login_class) {
        !           930:            int flags;
        !           931:            login_cap_t *lc;
        !           932: 
        !           933:            /*
        !           934:             * We only use setusercontext() to set the nice value and rlimits.
        !           935:             */
        !           936:            lc = login_getclass((char *)details->login_class);
        !           937:            if (!lc) {
        !           938:                warningx(_("unknown login class %s"), details->login_class);
        !           939:                errno = ENOENT;
        !           940:                goto done;
        !           941:            }
        !           942:            flags = LOGIN_SETRESOURCES|LOGIN_SETPRIORITY;
        !           943:            if (setusercontext(lc, pw, pw->pw_uid, flags)) {
        !           944:                if (pw->pw_uid != ROOT_UID) {
        !           945:                    warning(_("unable to set user context"));
        !           946:                    goto done;
        !           947:                } else
        !           948:                    warning(_("unable to set user context"));
        !           949:            }
        !           950:        }
        !           951: #endif /* HAVE_LOGIN_CAP_H */
        !           952:     }
        !           953: 
        !           954:     /*
        !           955:      * Set groups, including supplementary group vector.
        !           956:      */
        !           957: #ifdef HAVE_SETEUID
        !           958:     if (ISSET(details->flags, CD_SET_EGID) && setegid(details->egid)) {
        !           959:        warning(_("unable to set effective gid to runas gid %u"),
        !           960:            (unsigned int)details->egid);
        !           961:        goto done;
        !           962:     }
        !           963: #endif
        !           964:     if (ISSET(details->flags, CD_SET_GID) && setgid(details->gid)) {
        !           965:        warning(_("unable to set gid to runas gid %u"),
        !           966:            (unsigned int)details->gid);
        !           967:        goto done;
        !           968:     }
        !           969: 
        !           970:     if (!ISSET(details->flags, CD_PRESERVE_GROUPS)) {
        !           971:        if (details->ngroups >= 0) {
        !           972:            if (sudo_setgroups(details->ngroups, details->groups) < 0) {
        !           973:                warning(_("unable to set supplementary group IDs"));
        !           974:                goto done;
        !           975:            }
        !           976:        }
        !           977:     }
        !           978: 
        !           979:     if (ISSET(details->flags, CD_SET_PRIORITY)) {
        !           980:        if (setpriority(PRIO_PROCESS, 0, details->priority) != 0) {
        !           981:            warning(_("unable to set process priority"));
        !           982:            goto done;
        !           983:        }
        !           984:     }
        !           985:     if (ISSET(details->flags, CD_SET_UMASK))
        !           986:        (void) umask(details->umask);
        !           987:     if (details->chroot) {
        !           988:        if (chroot(details->chroot) != 0 || chdir("/") != 0) {
        !           989:            warning(_("unable to change root to %s"), details->chroot);
        !           990:            goto done;
        !           991:        }
        !           992:     }
        !           993: 
        !           994:     if (ISSET(details->flags, CD_NOEXEC))
        !           995:        disable_execute(details);
        !           996: 
        !           997: #ifdef HAVE_SETRESUID
        !           998:     if (setresuid(details->uid, details->euid, details->euid) != 0) {
        !           999:        warning(_("unable to change to runas uid (%u, %u)"), details->uid,
        !          1000:            details->euid);
        !          1001:        goto done;
        !          1002:     }
        !          1003: #elif HAVE_SETREUID
        !          1004:     if (setreuid(details->uid, details->euid) != 0) {
        !          1005:        warning(_("unable to change to runas uid (%u, %u)"),
        !          1006:            (unsigned int)details->uid, (unsigned int)details->euid);
        !          1007:        goto done;
        !          1008:     }
        !          1009: #else
        !          1010:     if (seteuid(details->euid) != 0 || setuid(details->euid) != 0) {
        !          1011:        warning(_("unable to change to runas uid (%u, %u)"), details->uid,
        !          1012:            details->euid);
        !          1013:        goto done;
        !          1014:     }
        !          1015: #endif /* !HAVE_SETRESUID && !HAVE_SETREUID */
        !          1016: 
        !          1017:     /*
        !          1018:      * Only change cwd if we have chroot()ed or the policy modules
        !          1019:      * specifies a different cwd.  Must be done after uid change.
        !          1020:      */
        !          1021:     if (details->cwd) {
        !          1022:        if (details->chroot || strcmp(details->cwd, user_details.cwd) != 0) {
        !          1023:            /* Note: cwd is relative to the new root, if any. */
        !          1024:            if (chdir(details->cwd) != 0) {
        !          1025:                warning(_("unable to change directory to %s"), details->cwd);
        !          1026:                goto done;
        !          1027:            }
        !          1028:        }
        !          1029:     }
        !          1030: 
        !          1031:     /*
        !          1032:      * Restore nproc resource limit if pam_limits didn't do it for us.
        !          1033:      * We must do this *after* the uid change to avoid potential EAGAIN
        !          1034:      * from setuid().
        !          1035:      */
        !          1036: #if defined(__linux__)
        !          1037:     {
        !          1038:        struct rlimit rl;
        !          1039:        if (getrlimit(RLIMIT_NPROC, &rl) == 0) {
        !          1040:            if (rl.rlim_cur == RLIM_INFINITY && rl.rlim_max == RLIM_INFINITY)
        !          1041:                (void) setrlimit(RLIMIT_NPROC, &nproclimit);
        !          1042:        }
        !          1043:     }
        !          1044: #endif
        !          1045: 
        !          1046:     rval = TRUE;
        !          1047: 
        !          1048: done:
        !          1049:     return rval;
        !          1050: }
        !          1051: 
        !          1052: /*
        !          1053:  * Run the command and wait for it to complete.
        !          1054:  */
        !          1055: int
        !          1056: run_command(struct command_details *details)
        !          1057: {
        !          1058:     struct plugin_container *plugin;
        !          1059:     struct command_status cstat;
        !          1060:     int exitcode = 1;
        !          1061: 
        !          1062:     cstat.type = CMD_INVALID;
        !          1063:     cstat.val = 0;
        !          1064: 
        !          1065:     sudo_execve(details, &cstat);
        !          1066: 
        !          1067:     switch (cstat.type) {
        !          1068:     case CMD_ERRNO:
        !          1069:        /* exec_setup() or execve() returned an error. */
        !          1070:        sudo_debug(9, "calling policy close with errno");
        !          1071:        policy_close(&policy_plugin, 0, cstat.val);
        !          1072:        tq_foreach_fwd(&io_plugins, plugin) {
        !          1073:            sudo_debug(9, "calling I/O close with errno");
        !          1074:            iolog_close(plugin, 0, cstat.val);
        !          1075:        }
        !          1076:        exitcode = 1;
        !          1077:        break;
        !          1078:     case CMD_WSTATUS:
        !          1079:        /* Command ran, exited or was killed. */
        !          1080:        sudo_debug(9, "calling policy close with wait status");
        !          1081:        policy_close(&policy_plugin, cstat.val, 0);
        !          1082:        tq_foreach_fwd(&io_plugins, plugin) {
        !          1083:            sudo_debug(9, "calling I/O close with wait status");
        !          1084:            iolog_close(plugin, cstat.val, 0);
        !          1085:        }
        !          1086:        if (WIFEXITED(cstat.val))
        !          1087:            exitcode = WEXITSTATUS(cstat.val);
        !          1088:        else if (WIFSIGNALED(cstat.val))
        !          1089:            exitcode = WTERMSIG(cstat.val) | 128;
        !          1090:        break;
        !          1091:     default:
        !          1092:        warningx(_("unexpected child termination condition: %d"), cstat.type);
        !          1093:        break;
        !          1094:     }
        !          1095:     return exitcode;
        !          1096: }
        !          1097: 
        !          1098: static int
        !          1099: policy_open(struct plugin_container *plugin, char * const settings[],
        !          1100:     char * const user_info[], char * const user_env[])
        !          1101: {
        !          1102:     return plugin->u.policy->open(SUDO_API_VERSION, sudo_conversation,
        !          1103:        _sudo_printf, settings, user_info, user_env);
        !          1104: }
        !          1105: 
        !          1106: static void
        !          1107: policy_close(struct plugin_container *plugin, int exit_status, int error)
        !          1108: {
        !          1109:     plugin->u.policy->close(exit_status, error);
        !          1110: }
        !          1111: 
        !          1112: static int
        !          1113: policy_show_version(struct plugin_container *plugin, int verbose)
        !          1114: {
        !          1115:     return plugin->u.policy->show_version(verbose);
        !          1116: }
        !          1117: 
        !          1118: static int
        !          1119: policy_check(struct plugin_container *plugin, int argc, char * const argv[],
        !          1120:     char *env_add[], char **command_info[], char **argv_out[],
        !          1121:     char **user_env_out[])
        !          1122: {
        !          1123:     return plugin->u.policy->check_policy(argc, argv, env_add, command_info,
        !          1124:        argv_out, user_env_out);
        !          1125: }
        !          1126: 
        !          1127: static int
        !          1128: policy_list(struct plugin_container *plugin, int argc, char * const argv[],
        !          1129:     int verbose, const char *list_user)
        !          1130: {
        !          1131:     if (plugin->u.policy->list == NULL) {
        !          1132:        warningx(_("policy plugin %s does not support listing privileges"),
        !          1133:            plugin->name);
        !          1134:        return FALSE;
        !          1135:     }
        !          1136:     return plugin->u.policy->list(argc, argv, verbose, list_user);
        !          1137: }
        !          1138: 
        !          1139: static int
        !          1140: policy_validate(struct plugin_container *plugin)
        !          1141: {
        !          1142:     if (plugin->u.policy->validate == NULL) {
        !          1143:        warningx(_("policy plugin %s does not support the -v option"),
        !          1144:            plugin->name);
        !          1145:        return FALSE;
        !          1146:     }
        !          1147:     return plugin->u.policy->validate();
        !          1148: }
        !          1149: 
        !          1150: static void
        !          1151: policy_invalidate(struct plugin_container *plugin, int remove)
        !          1152: {
        !          1153:     if (plugin->u.policy->invalidate == NULL) {
        !          1154:        errorx(1, _("policy plugin %s does not support the -k/-K options"),
        !          1155:            plugin->name);
        !          1156:     }
        !          1157:     plugin->u.policy->invalidate(remove);
        !          1158: }
        !          1159: 
        !          1160: static int
        !          1161: policy_init_session(struct plugin_container *plugin, struct passwd *pwd)
        !          1162: {
        !          1163:     if (plugin->u.policy->init_session)
        !          1164:        return plugin->u.policy->init_session(pwd);
        !          1165:     return TRUE;
        !          1166: }
        !          1167: 
        !          1168: static int
        !          1169: iolog_open(struct plugin_container *plugin, char * const settings[],
        !          1170:     char * const user_info[], char * const command_info[],
        !          1171:     int argc, char * const argv[], char * const user_env[])
        !          1172: {
        !          1173:     int rval;
        !          1174: 
        !          1175:     /*
        !          1176:      * Backwards compatibility for API major 1, minor 0
        !          1177:      */
        !          1178:     switch (plugin->u.generic->version) {
        !          1179:     case SUDO_API_MKVERSION(1, 0):
        !          1180:        rval = plugin->u.io_1_0->open(plugin->u.io_1_0->version,
        !          1181:            sudo_conversation, _sudo_printf, settings, user_info, argc, argv,
        !          1182:            user_env);
        !          1183:        break;
        !          1184:     default:
        !          1185:        rval = plugin->u.io->open(SUDO_API_VERSION, sudo_conversation,
        !          1186:            _sudo_printf, settings, user_info, command_info, argc, argv,
        !          1187:            user_env);
        !          1188:     }
        !          1189:     return rval;
        !          1190: }
        !          1191: 
        !          1192: static void
        !          1193: iolog_close(struct plugin_container *plugin, int exit_status, int error)
        !          1194: {
        !          1195:     plugin->u.io->close(exit_status, error);
        !          1196: }
        !          1197: 
        !          1198: static int
        !          1199: iolog_show_version(struct plugin_container *plugin, int verbose)
        !          1200: {
        !          1201:     return plugin->u.io->show_version(verbose);
        !          1202: }
        !          1203: 
        !          1204: /*
        !          1205:  * Simple debugging/logging.
        !          1206:  */
        !          1207: void
        !          1208: sudo_debug(int level, const char *fmt, ...)
        !          1209: {
        !          1210:     va_list ap;
        !          1211:     char *buf;
        !          1212: 
        !          1213:     if (level > debug_level)
        !          1214:        return;
        !          1215: 
        !          1216:     /* Bracket fmt with program name and a newline to make it a single write */
        !          1217:     va_start(ap, fmt);
        !          1218:     evasprintf(&buf, fmt, ap);
        !          1219:     va_end(ap);
        !          1220:     fprintf(stderr, "%s: %s\n", getprogname(), buf);
        !          1221:     efree(buf);
        !          1222: }

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