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

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

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