File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / sudo / src / sudo.c
Revision 1.1.1.5 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Mon Oct 14 07:56:35 2013 UTC (10 years, 11 months ago) by misho
Branches: sudo, MAIN
CVS tags: v1_8_8p0, v1_8_8, HEAD
v 1.8.8

    1: /*
    2:  * Copyright (c) 2009-2013 Todd C. Miller <Todd.Miller@courtesan.com>
    3:  *
    4:  * Permission to use, copy, modify, and distribute this software for any
    5:  * purpose with or without fee is hereby granted, provided that the above
    6:  * copyright notice and this permission notice appear in all copies.
    7:  *
    8:  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
    9:  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
   10:  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
   11:  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
   12:  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
   13:  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
   14:  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
   15:  */
   16: 
   17: #ifdef __TANDEM
   18: # include <floss.h>
   19: #endif
   20: 
   21: #include <config.h>
   22: 
   23: #include <sys/types.h>
   24: #include <sys/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>
   62: # ifndef LOGIN_SETENV
   63: #  define LOGIN_SETENV	0
   64: # endif
   65: #endif
   66: #ifdef HAVE_PROJECT_H
   67: # include <project.h>
   68: # include <sys/task.h>
   69: #endif
   70: #ifdef HAVE_SELINUX
   71: # include <selinux/selinux.h>
   72: #endif
   73: #ifdef HAVE_SETAUTHDB
   74: # include <usersec.h>
   75: #endif /* HAVE_SETAUTHDB */
   76: #if defined(HAVE_GETPRPWNAM) && defined(HAVE_SET_AUTH_PARAMETERS)
   77: # ifdef __hpux
   78: #  undef MAXINT
   79: #  include <hpsecurity.h>
   80: # else
   81: #  include <sys/security.h>
   82: # endif /* __hpux */
   83: # include <prot.h>
   84: #endif /* HAVE_GETPRPWNAM && HAVE_SET_AUTH_PARAMETERS */
   85: 
   86: #include <sudo_usage.h>
   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;
   97: const char *list_user; /* extern for parse_args.c */
   98: static struct command_details command_details;
   99: static int sudo_mode;
  100: 
  101: /*
  102:  * Local functions
  103:  */
  104: static void fix_fds(void);
  105: static void disable_coredumps(void);
  106: static void sudo_check_suid(const char *path);
  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);
  132: static void iolog_unlink(struct plugin_container *plugin);
  133: 
  134: #ifdef RLIMIT_CORE
  135: static struct rlimit corelimit;
  136: #endif
  137: #ifdef __linux__
  138: static struct rlimit nproclimit;
  139: #endif
  140: 
  141: __dso_public int main(int argc, char *argv[], char *envp[]);
  142: 
  143: int
  144: main(int argc, char *argv[], char *envp[])
  145: {
  146:     int nargc, ok, exitcode = 0;
  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;
  151:     debug_decl(main, SUDO_DEBUG_MAIN)
  152: 
  153:     os_init(argc, argv, envp);
  154: 
  155:     setlocale(LC_ALL, "");
  156:     bindtextdomain(PACKAGE_NAME, LOCALEDIR);
  157:     textdomain(PACKAGE_NAME);
  158: 
  159: #ifdef HAVE_TZSET
  160:     (void) tzset();
  161: #endif /* HAVE_TZSET */
  162: 
  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: 
  171:     /* Make sure we are setuid root. */
  172:     sudo_check_suid(argv[0]);
  173: 
  174:     /* Reset signal mask, save signal state and make sure fds 0-2 are open. */
  175:     (void) sigemptyset(&mask);
  176:     (void) sigprocmask(SIG_SETMASK, &mask, NULL);
  177:     save_signals();
  178:     fix_fds();
  179: 
  180:     /* Read sudo.conf. */
  181:     sudo_conf_read(NULL);
  182: 
  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: 
  187:     /* Disable core dumps if not enabled in sudo.conf. */
  188:     disable_coredumps();
  189: 
  190:     /* Parse command line arguments. */
  191:     sudo_mode = parse_args(argc, argv, &nargc, &nargv, &settings, &env_add);
  192:     sudo_debug_printf(SUDO_DEBUG_DEBUG, "sudo_mode %d", sudo_mode);
  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: 
  201:     /* Load plugins. */
  202:     if (!sudo_load_plugins(&policy_plugin, &io_plugins))
  203: 	fatalx(_("fatal error, unable to load plugins"));
  204: 
  205:     /* Open policy plugin. */
  206:     ok = policy_open(&policy_plugin, settings, user_info, envp);
  207:     if (ok != 1) {
  208: 	if (ok == -2)
  209: 	    usage(1);
  210: 	else
  211: 	    fatalx(_("unable to initialize policy plugin"));
  212:     }
  213: 
  214:     init_signals();
  215: 
  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);
  222: 		if (ok != -1)
  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);
  229: 	    exit(ok != 1);
  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);
  241: 	    exit(ok != 1);
  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);
  246: 	    sudo_debug_printf(SUDO_DEBUG_INFO, "policy plugin returns %d", ok);
  247: 	    if (ok != 1) {
  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) {
  258: 		case 1:
  259: 		    break;
  260: 		case 0:
  261: 		    /* I/O plugin asked to be disabled, remove and free. */
  262: 		    iolog_unlink(plugin);
  263: 		    break;
  264: 		case -2:
  265: 		    usage(1);
  266: 		    break;
  267: 		default:
  268: 		    fatalx(_("error initializing I/O plugin %s"),
  269: 			plugin->name);
  270: 		}
  271: 	    }
  272: 	    /* Setup command details and run command/edit. */
  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);
  278: 	    /* Become full root (not just setuid) so user cannot kill us. */
  279: 	    if (setuid(ROOT_UID) == -1)
  280: 		warning("setuid(%d)", ROOT_UID);
  281: 	    /* Restore coredumpsize resource limit before running. */
  282: #ifdef RLIMIT_CORE
  283: 	    if (sudo_conf_disable_coredump())
  284: 		(void) setrlimit(RLIMIT_CORE, &corelimit);
  285: #endif /* RLIMIT_CORE */
  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:
  294: 	    fatalx(_("unexpected sudo mode 0x%x"), sudo_mode);
  295:     }
  296:     sudo_debug_exit_int(__func__, __FILE__, __LINE__, sudo_debug_subsys, exitcode);                
  297:     exit(exitcode);
  298: }
  299: 
  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: 
  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;
  318:     debug_decl(fix_fds, SUDO_DEBUG_UTIL)
  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)
  329: 	    fatal(_("unable to open %s"), _PATH_DEVNULL);
  330: 	if (miss[STDIN_FILENO] && dup2(devnull, STDIN_FILENO) == -1)
  331: 	    fatal("dup2");
  332: 	if (miss[STDOUT_FILENO] && dup2(devnull, STDOUT_FILENO) == -1)
  333: 	    fatal("dup2");
  334: 	if (miss[STDERR_FILENO] && dup2(devnull, STDERR_FILENO) == -1)
  335: 	    fatal("dup2");
  336: 	if (devnull > STDERR_FILENO)
  337: 	    close(devnull);
  338:     }
  339:     debug_return;
  340: }
  341: 
  342: /*
  343:  * Allocate space for groups and fill in using getgrouplist()
  344:  * for when we cannot (or don't want to) use getgroups().
  345:  */
  346: static int
  347: fill_group_list(struct user_details *ud, int system_maxgroups)
  348: {
  349:     int tries, rval = -1;
  350:     debug_decl(fill_group_list, SUDO_DEBUG_UTIL)
  351: 
  352:     /*
  353:      * If user specified a max number of groups, use it, otherwise keep
  354:      * trying getgrouplist() until we have enough room in the array.
  355:      */
  356:     ud->ngroups = sudo_conf_max_groups();
  357:     if (ud->ngroups > 0) {
  358: 	ud->groups = emalloc2(ud->ngroups, sizeof(GETGROUPS_T));
  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: 	}
  376:     }
  377:     debug_return_int(rval);
  378: }
  379: 
  380: static char *
  381: get_user_groups(struct user_details *ud)
  382: {
  383:     char *cp, *gid_list = NULL;
  384:     size_t glsize;
  385:     int i, len, maxgroups, group_source;
  386:     debug_decl(get_user_groups, SUDO_DEBUG_UTIL)
  387: 
  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: 
  394:     ud->groups = NULL;
  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: 	    }
  406: 	}
  407:     }
  408:     if (ud->groups == NULL) {
  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"));
  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:     }
  430:     debug_return_str(gid_list);
  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: {
  440:     char *cp, **user_info, cwd[PATH_MAX], host[HOST_NAME_MAX + 1];
  441:     struct passwd *pw;
  442:     int fd, i = 0;
  443:     debug_decl(get_user_info, SUDO_DEBUG_UTIL)
  444: 
  445:     /* XXX - bound check number of entries */
  446:     user_info = emalloc2(32, sizeof(char *));
  447: 
  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: 
  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)
  466: 	fatalx(_("unknown uid %u: who are you?"), (unsigned int)ud->uid);
  467: 
  468:     user_info[i] = fmt_string("user", pw->pw_name);
  469:     if (user_info[i] == NULL)
  470: 	fatal(NULL);
  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: 
  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: 
  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)
  496: 	    fatal(NULL);
  497: 	ud->cwd = user_info[i] + sizeof("cwd=") - 1;
  498:     }
  499: 
  500:     if ((cp = get_process_ttyname()) != NULL) {
  501: 	user_info[++i] = fmt_string("tty", cp);
  502: 	if (user_info[i] == NULL)
  503: 	    fatal(NULL);
  504: 	ud->tty = user_info[i] + sizeof("tty=") - 1;
  505: 	efree(cp);
  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)
  514: 	fatal(NULL);
  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: 
  523:     debug_return_ptr(user_info);
  524: }
  525: 
  526: /*
  527:  * Convert a command_info array into a command_details structure.
  528:  */
  529: static void
  530: command_info_to_details(char * const info[], struct command_details *details)
  531: {
  532:     int i;
  533:     id_t id;
  534:     long lval;
  535:     char *cp, *ep;
  536:     const char *errstr;
  537:     debug_decl(command_info_to_details, SUDO_DEBUG_PCOMM)
  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: 
  548:     sudo_debug_printf(SUDO_DEBUG_INFO, "command info from plugin:");
  549:     for (i = 0; info[i] != NULL; i++) {
  550: 	sudo_debug_printf(SUDO_DEBUG_INFO, "    %d: %s", i, info[i]);
  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;
  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;
  567: 		    break;
  568: 		}
  569: 		break;
  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;
  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;
  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);
  593: 		    break;
  594: 		}
  595: 		if (strncmp("noexec=", info[i], sizeof("noexec=") - 1) == 0) {
  596: 		    if (atobool(info[i] + sizeof("noexec=") - 1) == true)
  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) {
  603: 		    if (atobool(info[i] + sizeof("preserve_groups=") - 1) == true)
  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;
  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);
  616: 		    break;
  617: 		}
  618: 		if (strncmp("runas_euid=", info[i], sizeof("runas_euid=") - 1) == 0) {
  619: 		    cp = info[i] + sizeof("runas_euid=") - 1;
  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);
  625: 		    break;
  626: 		}
  627: 		if (strncmp("runas_gid=", info[i], sizeof("runas_gid=") - 1) == 0) {
  628: 		    cp = info[i] + sizeof("runas_gid=") - 1;
  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);
  634: 		    break;
  635: 		}
  636: 		if (strncmp("runas_groups=", info[i], sizeof("runas_groups=") - 1) == 0) {
  637: 		    /* parse_gid_list() will call fatalx() on error. */
  638: 		    cp = info[i] + sizeof("runas_groups=") - 1;
  639: 		    details->ngroups = parse_gid_list(cp, NULL, &details->groups);
  640: 		    break;
  641: 		}
  642: 		if (strncmp("runas_uid=", info[i], sizeof("runas_uid=") - 1) == 0) {
  643: 		    cp = info[i] + sizeof("runas_uid=") - 1;
  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);
  649: 		    break;
  650: 		}
  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;
  655: 	            if (*cp != '\0') {
  656: 			details->privs = priv_str_to_set(cp, ",", &endp);
  657: 			if (details->privs == NULL)
  658: 			    warning("invalid runas_privs %s", endp);
  659: 		    }
  660: 		    break;
  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;
  665: 	            if (*cp != '\0') {
  666: 			details->limitprivs = priv_str_to_set(cp, ",", &endp);
  667: 			if (details->limitprivs == NULL)
  668: 			    warning("invalid runas_limitprivs %s", endp);
  669: 		    }
  670: 		    break;
  671: 		}
  672: #endif /* HAVE_PRIV_SET */
  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) {
  678: 		    if (atobool(info[i] + sizeof("set_utmp=") - 1) == true)
  679: 			SET(details->flags, CD_SET_UTMP);
  680: 		    break;
  681: 		}
  682: 		if (strncmp("sudoedit=", info[i], sizeof("sudoedit=") - 1) == 0) {
  683: 		    if (atobool(info[i] + sizeof("sudoedit=") - 1) == true)
  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;
  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);
  701: 		    break;
  702: 		}
  703: 		break;
  704: 	    case 'u':
  705: 		if (strncmp("umask=", info[i], sizeof("umask=") - 1) == 0) {
  706: 		    errno = 0;
  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);
  717: 		    break;
  718: 		}
  719: 		if (strncmp("use_pty=", info[i], sizeof("use_pty=") - 1) == 0) {
  720: 		    if (atobool(info[i] + sizeof("use_pty=") - 1) == true)
  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: 
  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)
  737: 	fatal(NULL);
  738: #ifdef HAVE_SETAUTHDB
  739:     aix_restoreauthdb();
  740: #endif
  741: 
  742: #ifdef HAVE_SELINUX
  743:     if (details->selinux_role != NULL && is_selinux_enabled() > 0)
  744: 	SET(details->flags, CD_RBAC_ENABLED);
  745: #endif
  746:     debug_return;
  747: }
  748: 
  749: static void
  750: sudo_check_suid(const char *sudo)
  751: {
  752:     char pathbuf[PATH_MAX];
  753:     struct stat sb;
  754:     bool qualified;
  755:     debug_decl(sudo_check_suid, SUDO_DEBUG_PCOMM)
  756: 
  757:     if (geteuid() != 0) {
  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) {
  785: 	    /* Try to determine why sudo was not running as root. */
  786: 	    if (sb.st_uid != ROOT_UID || !ISSET(sb.st_mode, S_ISUID)) {
  787: 		fatalx(
  788: 		    _("%s must be owned by uid %d and have the setuid bit set"),
  789: 		    sudo, ROOT_UID);
  790: 	    } else {
  791: 		fatalx(_("effective uid is not %d, is %s on a file system "
  792: 		    "with the 'nosuid' option set or an NFS file system without"
  793: 		    " root privileges?"), ROOT_UID, sudo);
  794: 	    }
  795: 	} else {
  796: 	    fatalx(
  797: 		_("effective uid is not %d, is sudo installed setuid root?"),
  798: 		ROOT_UID);
  799: 	}
  800:     }
  801:     debug_return;
  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: {
  812: #if defined(RLIMIT_CORE)
  813:     struct rlimit rl;
  814:     debug_decl(disable_coredumps, SUDO_DEBUG_UTIL)
  815: 
  816:     /*
  817:      * Turn off core dumps?
  818:      */
  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:     }
  825:     debug_return;
  826: #endif /* RLIMIT_CORE */
  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: 
  863:     debug_return;
  864: #endif /* __linux__ */
  865: }
  866: 
  867: /*
  868:  * Setup the execution environment immediately prior to the call to execve()
  869:  * Returns true on success and false on failure.
  870:  */
  871: bool
  872: exec_setup(struct command_details *details, const char *ptyname, int ptyfd)
  873: {
  874:     bool rval = false;
  875:     debug_decl(exec_setup, SUDO_DEBUG_EXEC)
  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: 
  885:     if (details->pw != NULL) {
  886: #ifdef HAVE_PROJECT_H
  887: 	set_project(details->pw);
  888: #endif
  889: #ifdef HAVE_PRIV_SET
  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: 	    }
  895: 	}
  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: 	    }
  906: 	}
  907: #endif /* HAVE_PRIV_SET */
  908: 
  909: #ifdef HAVE_GETUSERATTR
  910: 	aix_prep_user(details->pw->pw_name, ptyname ? ptyname : user_details.tty);
  911: #endif
  912: #ifdef HAVE_LOGIN_CAP_H
  913: 	if (details->login_class) {
  914: 	    int flags;
  915: 	    login_cap_t *lc;
  916: 
  917: 	    /*
  918: 	     * We only use setusercontext() to set the nice value and rlimits
  919: 	     * unless this is a login shell (sudo -i).
  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: 	    }
  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)) {
  936: 		warning(_("unable to set user context"));
  937: 		if (details->pw->pw_uid != ROOT_UID)
  938: 		    goto done;
  939: 	    }
  940: 	}
  941: #endif /* HAVE_LOGIN_CAP_H */
  942:     }
  943: 
  944:     /*
  945:      * Set groups, including supplementary group vector.
  946:      */
  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:     }
  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: 
  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: 
  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: 
 1009:     /* Restore previous value of RLIMIT_NPROC. */
 1010:     restore_nproc();
 1011: 
 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: 
 1026:     rval = true;
 1027: 
 1028: done:
 1029:     debug_return_bool(rval);
 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;
 1041:     debug_decl(run_command, SUDO_DEBUG_EXEC)
 1042: 
 1043:     cstat.type = CMD_INVALID;
 1044:     cstat.val = 0;
 1045: 
 1046:     sudo_execute(details, &cstat);
 1047: 
 1048:     switch (cstat.type) {
 1049:     case CMD_ERRNO:
 1050: 	/* exec_setup() or execve() returned an error. */
 1051: 	sudo_debug_printf(SUDO_DEBUG_DEBUG,
 1052: 	    "calling policy close with errno %d", cstat.val);
 1053: 	policy_close(&policy_plugin, 0, cstat.val);
 1054: 	tq_foreach_fwd(&io_plugins, plugin) {
 1055: 	    sudo_debug_printf(SUDO_DEBUG_DEBUG,
 1056: 		"calling I/O close with errno %d", cstat.val);
 1057: 	    iolog_close(plugin, 0, cstat.val);
 1058: 	}
 1059: 	exitcode = 1;
 1060: 	break;
 1061:     case CMD_WSTATUS:
 1062: 	/* Command ran, exited or was killed. */
 1063: 	sudo_debug_printf(SUDO_DEBUG_DEBUG,
 1064: 	    "calling policy close with wait status %d", cstat.val);
 1065: 	policy_close(&policy_plugin, cstat.val, 0);
 1066: 	tq_foreach_fwd(&io_plugins, plugin) {
 1067: 	    sudo_debug_printf(SUDO_DEBUG_DEBUG,
 1068: 		"calling I/O close with wait status %d", cstat.val);
 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:     }
 1080:     debug_return_int(exitcode);
 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: {
 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);
 1105: }
 1106: 
 1107: static void
 1108: policy_close(struct plugin_container *plugin, int exit_status, int error)
 1109: {
 1110:     debug_decl(policy_close, SUDO_DEBUG_PCOMM)
 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);
 1115:     debug_return;
 1116: }
 1117: 
 1118: static int
 1119: policy_show_version(struct plugin_container *plugin, int verbose)
 1120: {
 1121:     debug_decl(policy_show_version, SUDO_DEBUG_PCOMM)
 1122:     if (plugin->u.policy->show_version == NULL)
 1123: 	debug_return_bool(true);
 1124:     debug_return_bool(plugin->u.policy->show_version(verbose));
 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: {
 1132:     debug_decl(policy_check, SUDO_DEBUG_PCOMM)
 1133:     if (plugin->u.policy->check_policy == NULL) {
 1134: 	fatalx(_("policy plugin %s is missing the `check_policy' method"),
 1135: 	    plugin->name);
 1136:     }
 1137:     debug_return_bool(plugin->u.policy->check_policy(argc, argv, env_add,
 1138: 	command_info, argv_out, user_env_out));
 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: {
 1145:     debug_decl(policy_list, SUDO_DEBUG_PCOMM)
 1146:     if (plugin->u.policy->list == NULL) {
 1147: 	warningx(_("policy plugin %s does not support listing privileges"),
 1148: 	    plugin->name);
 1149: 	debug_return_bool(false);
 1150:     }
 1151:     debug_return_bool(plugin->u.policy->list(argc, argv, verbose, list_user));
 1152: }
 1153: 
 1154: static int
 1155: policy_validate(struct plugin_container *plugin)
 1156: {
 1157:     debug_decl(policy_validate, SUDO_DEBUG_PCOMM)
 1158:     if (plugin->u.policy->validate == NULL) {
 1159: 	warningx(_("policy plugin %s does not support the -v option"),
 1160: 	    plugin->name);
 1161: 	debug_return_bool(false);
 1162:     }
 1163:     debug_return_bool(plugin->u.policy->validate());
 1164: }
 1165: 
 1166: static void
 1167: policy_invalidate(struct plugin_container *plugin, int remove)
 1168: {
 1169:     debug_decl(policy_invalidate, SUDO_DEBUG_PCOMM)
 1170:     if (plugin->u.policy->invalidate == NULL) {
 1171: 	fatalx(_("policy plugin %s does not support the -k/-K options"),
 1172: 	    plugin->name);
 1173:     }
 1174:     plugin->u.policy->invalidate(remove);
 1175:     debug_return;
 1176: }
 1177: 
 1178: int
 1179: policy_init_session(struct command_details *details)
 1180: {
 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);
 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;
 1207:     debug_decl(iolog_open, SUDO_DEBUG_PCOMM)
 1208: 
 1209:     /*
 1210:      * Backwards compatibility for older API versions
 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;
 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;
 1223:     default:
 1224: 	rval = plugin->u.io->open(SUDO_API_VERSION, sudo_conversation,
 1225: 	    _sudo_printf, settings, user_info, command_info,
 1226: 	    argc, argv, user_env, plugin->options);
 1227:     }
 1228:     debug_return_bool(rval);
 1229: }
 1230: 
 1231: static void
 1232: iolog_close(struct plugin_container *plugin, int exit_status, int error)
 1233: {
 1234:     debug_decl(iolog_close, SUDO_DEBUG_PCOMM)
 1235:     if (plugin->u.io->close != NULL)
 1236: 	plugin->u.io->close(exit_status, error);
 1237:     debug_return;
 1238: }
 1239: 
 1240: static int
 1241: iolog_show_version(struct plugin_container *plugin, int verbose)
 1242: {
 1243:     debug_decl(iolog_show_version, SUDO_DEBUG_PCOMM)
 1244:     if (plugin->u.io->show_version == NULL)
 1245: 	debug_return_bool(true);
 1246:     debug_return_bool(plugin->u.io->show_version(verbose));
 1247: }
 1248: 
 1249: /*
 1250:  * Remove the specified I/O logging plugin from the io_plugins list.
 1251:  * Deregisters any hooks before unlinking, then frees the container.
 1252:  */
 1253: static void
 1254: iolog_unlink(struct plugin_container *plugin)
 1255: {
 1256:     debug_decl(iolog_unlink, SUDO_DEBUG_PCOMM)
 1257: 
 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);
 1267: 
 1268:     debug_return;
 1269: }

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