Annotation of embedaddon/sudo/src/sudo.c, revision 1.1.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>