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

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

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