2 * Copyright (c) 2009-2011 Todd C. Miller <Todd.Miller@courtesan.com>
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.
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.
23 #include <sys/types.h>
24 #include <sys/param.h>
27 #include <sys/socket.h>
28 #ifdef HAVE_SYS_SELECT_H
29 # include <sys/select.h>
30 #endif /* HAVE_SYS_SELECT_H */
32 #include <sys/resource.h>
41 #endif /* STDC_HEADERS */
43 # if defined(HAVE_MEMORY_H) && !defined(STDC_HEADERS)
47 #endif /* HAVE_STRING_H */
50 #endif /* HAVE_STRINGS_H */
53 #endif /* HAVE_UNISTD_H */
61 #if TIME_WITH_SYS_TIME
67 #ifdef HAVE_LOGIN_CAP_H
68 # include <login_cap.h>
72 # include <sys/task.h>
75 # include <selinux/selinux.h>
79 #endif /* HAVE_SETAUTHDB */
80 #if defined(HAVE_GETPRPWNAM) && defined(HAVE_SET_AUTH_PARAMETERS)
83 # include <hpsecurity.h>
85 # include <sys/security.h>
88 #endif /* HAVE_GETPRPWNAM && HAVE_SET_AUTH_PARAMETERS */
94 #include "sudo_plugin.h"
95 #include "sudo_plugin_int.h"
96 #include <sudo_usage.h>
101 struct plugin_container policy_plugin;
102 struct plugin_container_list io_plugins;
103 struct user_details user_details;
104 const char *list_user, *runas_user, *runas_group; /* extern for parse_args.c */
110 static void fix_fds(void);
111 static void disable_coredumps(void);
112 static char **get_user_info(struct user_details *);
113 static void command_info_to_details(char * const info[],
114 struct command_details *details);
115 static int policy_open(struct plugin_container *plugin, char * const settings[],
116 char * const user_info[], char * const user_env[]);
117 static void policy_close(struct plugin_container *plugin, int exit_status,
119 static int iolog_open(struct plugin_container *plugin, char * const settings[],
120 char * const user_info[], char * const command_details[],
121 int argc, char * const argv[], char * const user_env[]);
122 static void iolog_close(struct plugin_container *plugin, int exit_status,
124 static char *escape_cmnd(const char *src);
126 /* Policy plugin convenience functions. */
127 static int policy_open(struct plugin_container *plugin, char * const settings[],
128 char * const user_info[], char * const user_env[]);
129 static void policy_close(struct plugin_container *plugin, int exit_status,
131 static int policy_show_version(struct plugin_container *plugin, int verbose);
132 static int policy_check(struct plugin_container *plugin, int argc,
133 char * const argv[], char *env_add[], char **command_info[],
134 char **argv_out[], char **user_env_out[]);
135 static int policy_list(struct plugin_container *plugin, int argc,
136 char * const argv[], int verbose, const char *list_user);
137 static int policy_validate(struct plugin_container *plugin);
138 static void policy_invalidate(struct plugin_container *plugin, int remove);
139 static int policy_init_session(struct plugin_container *plugin,
142 /* I/O log plugin convenience functions. */
143 static int iolog_open(struct plugin_container *plugin, char * const settings[],
144 char * const user_info[], char * const command_details[],
145 int argc, char * const argv[], char * const user_env[]);
146 static void iolog_close(struct plugin_container *plugin, int exit_status,
148 static int iolog_show_version(struct plugin_container *plugin, int verbose);
150 #if defined(RLIMIT_CORE) && !defined(SUDO_DEVEL)
151 static struct rlimit corelimit;
152 #endif /* RLIMIT_CORE && !SUDO_DEVEL */
153 #if defined(__linux__)
154 static struct rlimit nproclimit;
158 main(int argc, char *argv[], char *envp[])
160 int nargc, sudo_mode, exitcode = 0;
161 char **nargv, **settings, **env_add;
162 char **user_info, **command_info, **argv_out, **user_env_out;
163 struct plugin_container *plugin, *next;
164 struct command_details command_details;
167 #if defined(SUDO_DEVEL) && defined(__OpenBSD__)
168 extern char *malloc_options;
169 malloc_options = "AFGJPR";
172 #ifdef HAVE_SETLOCALE
173 setlocale(LC_ALL, "");
176 #if !defined(HAVE_GETPROGNAME) && !defined(HAVE___PROGNAME)
178 setprogname(argv[0]);
181 /* Must be done before we do any password lookups */
182 #if defined(HAVE_GETPRPWNAM) && defined(HAVE_SET_AUTH_PARAMETERS)
183 (void) set_auth_parameters(argc, argv);
184 # ifdef HAVE_INITPRIVS
187 #endif /* HAVE_GETPRPWNAM && HAVE_SET_AUTH_PARAMETERS */
190 errorx(1, "must be setuid root");
192 /* Reset signal mask, disable core dumps and make sure fds 0-2 are open. */
193 (void) sigemptyset(&mask);
194 (void) sigprocmask(SIG_SETMASK, &mask, NULL);
198 /* Fill in user_info with user name, uid, cwd, etc. */
199 memset(&user_details, 0, sizeof(user_details));
200 user_info = get_user_info(&user_details);
202 /* Parse command line arguments. */
203 sudo_mode = parse_args(argc, argv, &nargc, &nargv, &settings, &env_add);
204 sudo_debug(9, "sudo_mode %d", sudo_mode);
206 /* Print sudo version early, in case of plugin init failure. */
207 if (ISSET(sudo_mode, MODE_VERSION)) {
208 printf("Sudo version %s\n", PACKAGE_VERSION);
209 if (user_details.uid == ROOT_UID)
210 (void) printf("Configure args: %s\n", CONFIGURE_ARGS);
213 /* Read sudo.conf and load plugins. */
214 if (!sudo_load_plugins(_PATH_SUDO_CONF, &policy_plugin, &io_plugins))
215 errorx(1, "fatal error, unable to load plugins");
217 /* Open policy plugin. */
218 ok = policy_open(&policy_plugin, settings, user_info, envp);
223 errorx(1, "unable to initialize policy plugin");
226 switch (sudo_mode & MODE_MASK) {
228 policy_show_version(&policy_plugin, !user_details.uid);
229 tq_foreach_fwd(&io_plugins, plugin) {
230 ok = iolog_open(plugin, settings, user_info, NULL,
233 iolog_show_version(plugin, !user_details.uid);
237 case MODE_VALIDATE|MODE_INVALIDATE:
238 ok = policy_validate(&policy_plugin);
241 case MODE_INVALIDATE:
242 policy_invalidate(&policy_plugin, sudo_mode == MODE_KILL);
246 case MODE_CHECK|MODE_INVALIDATE:
248 case MODE_LIST|MODE_INVALIDATE:
249 ok = policy_list(&policy_plugin, nargc, nargv,
250 ISSET(sudo_mode, MODE_LONG_LIST), list_user);
254 ok = policy_check(&policy_plugin, nargc, nargv, env_add,
255 &command_info, &argv_out, &user_env_out);
256 sudo_debug(8, "policy plugin returns %d", ok);
260 exit(1); /* plugin printed error message */
262 /* Open I/O plugins once policy plugin succeeds. */
263 for (plugin = io_plugins.first; plugin != NULL; plugin = next) {
265 ok = iolog_open(plugin, settings, user_info,
266 command_info, nargc, nargv, envp);
271 /* I/O plugin asked to be disabled, remove from list. */
272 tq_remove(&io_plugins, plugin);
278 errorx(1, "error initializing I/O plugin %s", plugin->name);
281 command_info_to_details(command_info, &command_details);
282 command_details.argv = argv_out;
283 command_details.envp = user_env_out;
284 if (ISSET(sudo_mode, MODE_BACKGROUND))
285 SET(command_details.flags, CD_BACKGROUND);
286 /* Restore coredumpsize resource limit before running. */
287 #if defined(RLIMIT_CORE) && !defined(SUDO_DEVEL)
288 (void) setrlimit(RLIMIT_CORE, &corelimit);
289 #endif /* RLIMIT_CORE && !SUDO_DEVEL */
290 if (ISSET(command_details.flags, CD_SUDOEDIT)) {
291 exitcode = sudo_edit(&command_details);
293 if (ISSET(sudo_mode, MODE_SHELL)) {
294 /* Escape meta chars if running a shell with args. */
295 if (argv_out[1] != NULL && strcmp(argv_out[1], "-c") == 0 &&
296 argv_out[2] != NULL && argv_out[3] == NULL)
297 argv_out[2] = escape_cmnd(argv_out[2]);
299 exitcode = run_command(&command_details);
301 /* The close method was called by sudo_edit/run_command. */
304 errorx(1, "unexpected sudo mode 0x%x", sudo_mode);
310 * Ensure that stdin, stdout and stderr are open; set to /dev/null if not.
311 * Some operating systems do this automatically in the kernel or libc.
316 int miss[3], devnull = -1;
319 * stdin, stdout and stderr must be open; set them to /dev/null
320 * if they are closed.
322 miss[STDIN_FILENO] = fcntl(STDIN_FILENO, F_GETFL, 0) == -1;
323 miss[STDOUT_FILENO] = fcntl(STDOUT_FILENO, F_GETFL, 0) == -1;
324 miss[STDERR_FILENO] = fcntl(STDERR_FILENO, F_GETFL, 0) == -1;
325 if (miss[STDIN_FILENO] || miss[STDOUT_FILENO] || miss[STDERR_FILENO]) {
326 if ((devnull = open(_PATH_DEVNULL, O_RDWR, 0644)) == -1)
327 error(1, "unable to open %s", _PATH_DEVNULL);
328 if (miss[STDIN_FILENO] && dup2(devnull, STDIN_FILENO) == -1)
330 if (miss[STDOUT_FILENO] && dup2(devnull, STDOUT_FILENO) == -1)
332 if (miss[STDERR_FILENO] && dup2(devnull, STDERR_FILENO) == -1)
334 if (devnull > STDERR_FILENO)
340 get_user_groups(struct user_details *ud)
342 char *gid_list = NULL;
343 #ifdef HAVE_GETGROUPS
348 if ((ud->ngroups = getgroups(0, NULL)) <= 0)
351 ud->groups = emalloc2(ud->ngroups, sizeof(GETGROUPS_T));
352 if (getgroups(ud->ngroups, ud->groups) < 0)
353 error(1, "can't get group vector");
354 glsize = sizeof("groups=") - 1 + (ud->ngroups * (MAX_UID_T_LEN + 1));
355 gid_list = emalloc(glsize);
356 memcpy(gid_list, "groups=", sizeof("groups=") - 1);
357 cp = gid_list + sizeof("groups=") - 1;
358 for (i = 0; i < ud->ngroups; i++) {
359 /* XXX - check rval */
360 len = snprintf(cp, glsize - (cp - gid_list), "%s%u",
361 i ? "," : "", (unsigned int)ud->groups[i]);
369 * Return user information as an array of name=value pairs.
370 * and fill in struct user_details (which shares the same strings).
373 get_user_info(struct user_details *ud)
376 char host[MAXHOSTNAMELEN];
377 char **user_info, *cp;
381 /* XXX - bound check number of entries */
382 user_info = emalloc2(32, sizeof(char *));
385 ud->euid = geteuid();
387 ud->egid = getegid();
389 pw = getpwuid(ud->uid);
391 errorx(1, "unknown uid %u: who are you?", (unsigned int)ud->uid);
393 user_info[i] = fmt_string("user", pw->pw_name);
394 if (user_info[i] == NULL)
395 errorx(1, "unable to allocate memory");
396 ud->username = user_info[i] + sizeof("user=") - 1;
398 /* Stash user's shell for use with the -s flag; don't pass to plugin. */
399 if ((ud->shell = getenv("SHELL")) == NULL || ud->shell[0] == '\0') {
400 ud->shell = pw->pw_shell[0] ? pw->pw_shell : _PATH_BSHELL;
402 ud->shell = estrdup(ud->shell);
404 easprintf(&user_info[++i], "uid=%u", (unsigned int)ud->uid);
405 easprintf(&user_info[++i], "euid=%u", (unsigned int)ud->euid);
406 easprintf(&user_info[++i], "gid=%u", (unsigned int)ud->gid);
407 easprintf(&user_info[++i], "egid=%u", (unsigned int)ud->egid);
409 if ((cp = get_user_groups(ud)) != NULL)
412 if (getcwd(cwd, sizeof(cwd)) != NULL) {
413 user_info[++i] = fmt_string("cwd", cwd);
414 if (user_info[i] == NULL)
415 errorx(1, "unable to allocate memory");
416 ud->cwd = user_info[i] + sizeof("cwd=") - 1;
419 if ((cp = ttyname(STDIN_FILENO)) || (cp = ttyname(STDOUT_FILENO)) ||
420 (cp = ttyname(STDERR_FILENO))) {
421 user_info[++i] = fmt_string("tty", cp);
422 if (user_info[i] == NULL)
423 errorx(1, "unable to allocate memory");
424 ud->tty = user_info[i] + sizeof("tty=") - 1;
427 if (gethostname(host, sizeof(host)) == 0)
428 host[sizeof(host) - 1] = '\0';
430 strlcpy(host, "localhost", sizeof(host));
431 user_info[++i] = fmt_string("host", host);
432 if (user_info[i] == NULL)
433 errorx(1, "unable to allocate memory");
434 ud->host = user_info[i] + sizeof("host=") - 1;
436 get_ttysize(&ud->ts_lines, &ud->ts_cols);
437 easprintf(&user_info[++i], "lines=%d", ud->ts_lines);
438 easprintf(&user_info[++i], "cols=%d", ud->ts_cols);
440 user_info[++i] = NULL;
446 * Convert a command_info array into a command_details structure.
449 command_info_to_details(char * const info[], struct command_details *details)
456 memset(details, 0, sizeof(*details));
457 details->closefrom = -1;
459 #define SET_STRING(s, n) \
460 if (strncmp(s, info[i], sizeof(s) - 1) == 0 && info[i][sizeof(s) - 1]) { \
461 details->n = info[i] + sizeof(s) - 1; \
465 for (i = 0; info[i] != NULL; i++) {
466 sudo_debug(9, "command info: %s", info[i]);
467 switch (info[i][0]) {
469 SET_STRING("chroot=", chroot)
470 SET_STRING("command=", command)
471 SET_STRING("cwd=", cwd)
472 if (strncmp("closefrom=", info[i], sizeof("closefrom=") - 1) == 0) {
473 cp = info[i] + sizeof("closefrom=") - 1;
477 lval = strtol(cp, &ep, 0);
478 if (*cp != '\0' && *ep == '\0' &&
480 (lval == LONG_MAX || lval == LONG_MIN)) &&
481 lval < INT_MAX && lval > INT_MIN) {
482 details->closefrom = (int)lval;
488 SET_STRING("login_class=", login_class)
491 /* XXX - bounds check -NZERO to NZERO (inclusive). */
492 if (strncmp("nice=", info[i], sizeof("nice=") - 1) == 0) {
493 cp = info[i] + sizeof("nice=") - 1;
497 lval = strtol(cp, &ep, 0);
498 if (*cp != '\0' && *ep == '\0' &&
500 (lval == LONG_MAX || lval == LONG_MIN)) &&
501 lval < INT_MAX && lval > INT_MIN) {
502 details->priority = (int)lval;
503 SET(details->flags, CD_SET_PRIORITY);
507 if (strncmp("noexec=", info[i], sizeof("noexec=") - 1) == 0) {
508 if (atobool(info[i] + sizeof("noexec=") - 1) == TRUE)
509 SET(details->flags, CD_NOEXEC);
512 /* XXX - deprecated */
513 if (strncmp("noexec_file=", info[i], sizeof("noexec_file=") - 1) == 0) {
514 noexec_path = info[i] + sizeof("noexec_file=") - 1;
519 if (strncmp("preserve_groups=", info[i], sizeof("preserve_groups=") - 1) == 0) {
520 if (atobool(info[i] + sizeof("preserve_groups=") - 1) == TRUE)
521 SET(details->flags, CD_PRESERVE_GROUPS);
526 if (strncmp("runas_egid=", info[i], sizeof("runas_egid=") - 1) == 0) {
527 cp = info[i] + sizeof("runas_egid=") - 1;
531 ulval = strtoul(cp, &ep, 0);
532 if (*cp != '\0' && *ep == '\0' &&
533 (errno != ERANGE || ulval != ULONG_MAX)) {
534 details->egid = (gid_t)ulval;
535 SET(details->flags, CD_SET_EGID);
539 if (strncmp("runas_euid=", info[i], sizeof("runas_euid=") - 1) == 0) {
540 cp = info[i] + sizeof("runas_euid=") - 1;
544 ulval = strtoul(cp, &ep, 0);
545 if (*cp != '\0' && *ep == '\0' &&
546 (errno != ERANGE || ulval != ULONG_MAX)) {
547 details->euid = (uid_t)ulval;
548 SET(details->flags, CD_SET_EUID);
552 if (strncmp("runas_gid=", info[i], sizeof("runas_gid=") - 1) == 0) {
553 cp = info[i] + sizeof("runas_gid=") - 1;
557 ulval = strtoul(cp, &ep, 0);
558 if (*cp != '\0' && *ep == '\0' &&
559 (errno != ERANGE || ulval != ULONG_MAX)) {
560 details->gid = (gid_t)ulval;
561 SET(details->flags, CD_SET_GID);
565 if (strncmp("runas_groups=", info[i], sizeof("runas_groups=") - 1) == 0) {
568 /* count groups, alloc and fill in */
569 cp = info[i] + sizeof("runas_groups=") - 1;
574 if ((cp = strchr(cp, ',')) == NULL)
578 if (details->ngroups != 0) {
580 emalloc2(details->ngroups, sizeof(GETGROUPS_T));
581 cp = info[i] + sizeof("runas_groups=") - 1;
582 for (j = 0; j < details->ngroups;) {
584 ulval = strtoul(cp, &ep, 0);
585 if (*cp == '\0' || (*ep != ',' && *ep != '\0') ||
586 (ulval == ULONG_MAX && errno == ERANGE)) {
589 details->groups[j++] = (gid_t)ulval;
592 details->ngroups = j;
596 if (strncmp("runas_uid=", info[i], sizeof("runas_uid=") - 1) == 0) {
597 cp = info[i] + sizeof("runas_uid=") - 1;
601 ulval = strtoul(cp, &ep, 0);
602 if (*cp != '\0' && *ep == '\0' &&
603 (errno != ERANGE || ulval != ULONG_MAX)) {
604 details->uid = (uid_t)ulval;
605 SET(details->flags, CD_SET_UID);
611 SET_STRING("selinux_role=", selinux_role)
612 SET_STRING("selinux_type=", selinux_type)
613 if (strncmp("set_utmp=", info[i], sizeof("set_utmp=") - 1) == 0) {
614 if (atobool(info[i] + sizeof("set_utmp=") - 1) == TRUE)
615 SET(details->flags, CD_SET_UTMP);
618 if (strncmp("sudoedit=", info[i], sizeof("sudoedit=") - 1) == 0) {
619 if (atobool(info[i] + sizeof("sudoedit=") - 1) == TRUE)
620 SET(details->flags, CD_SUDOEDIT);
625 if (strncmp("timeout=", info[i], sizeof("timeout=") - 1) == 0) {
626 cp = info[i] + sizeof("timeout=") - 1;
630 lval = strtol(cp, &ep, 0);
631 if (*cp != '\0' && *ep == '\0' &&
633 (lval == LONG_MAX || lval == LONG_MIN)) &&
634 lval <= INT_MAX && lval >= 0) {
635 details->timeout = (int)lval;
636 SET(details->flags, CD_SET_TIMEOUT);
642 if (strncmp("umask=", info[i], sizeof("umask=") - 1) == 0) {
643 cp = info[i] + sizeof("umask=") - 1;
647 ulval = strtoul(cp, &ep, 8);
648 if (*cp != '\0' && *ep == '\0' &&
649 (errno != ERANGE || ulval != ULONG_MAX)) {
650 details->umask = (uid_t)ulval;
651 SET(details->flags, CD_SET_UMASK);
655 if (strncmp("use_pty=", info[i], sizeof("use_pty=") - 1) == 0) {
656 if (atobool(info[i] + sizeof("use_pty=") - 1) == TRUE)
657 SET(details->flags, CD_USE_PTY);
660 SET_STRING("utmp_user=", utmp_user)
665 if (!ISSET(details->flags, CD_SET_EUID))
666 details->euid = details->uid;
669 if (details->selinux_role != NULL && is_selinux_enabled() > 0)
670 SET(details->flags, CD_RBAC_ENABLED);
675 * Disable core dumps to avoid dropping a core with user password in it.
676 * We will reset this limit before executing the command.
677 * Not all operating systems disable core dumps for setuid processes.
680 disable_coredumps(void)
682 #if defined(__linux__) || (defined(RLIMIT_CORE) && !defined(SUDO_DEVEL))
686 #if defined(__linux__)
688 * Unlimit the number of processes since Linux's setuid() will
689 * apply resource limits when changing uid and return EAGAIN if
690 * nproc would be violated by the uid switch.
692 (void) getrlimit(RLIMIT_NPROC, &nproclimit);
693 rl.rlim_cur = rl.rlim_max = RLIM_INFINITY;
694 if (setrlimit(RLIMIT_NPROC, &rl)) {
695 memcpy(&rl, &nproclimit, sizeof(struct rlimit));
696 rl.rlim_cur = rl.rlim_max;
697 (void)setrlimit(RLIMIT_NPROC, &rl);
699 #endif /* __linux__ */
700 #if defined(RLIMIT_CORE) && !defined(SUDO_DEVEL)
702 * Turn off core dumps.
704 (void) getrlimit(RLIMIT_CORE, &corelimit);
705 memcpy(&rl, &corelimit, sizeof(struct rlimit));
707 (void) setrlimit(RLIMIT_CORE, &rl);
708 #endif /* RLIMIT_CORE && !SUDO_DEVEL */
711 #ifdef HAVE_PROJECT_H
713 set_project(struct passwd *pw)
716 char buf[PROJECT_BUFSZ];
720 * Collect the default project for the user and settaskid
723 if (getdefaultproj(pw->pw_name, &proj, buf, sizeof(buf)) != NULL) {
724 errval = setproject(proj.pj_name, pw->pw_name, TASK_NORMAL);
728 case SETPROJ_ERR_TASK:
731 warningx("resource control limit has been reached");
734 warningx("user \"%s\" is not a member of project \"%s\"",
735 pw->pw_name, proj.pj_name);
738 warningx("the invoking task is final");
741 warningx("could not join project \"%s\"", proj.pj_name);
743 case SETPROJ_ERR_POOL:
746 warningx("no resource pool accepting default bindings "
747 "exists for project \"%s\"", proj.pj_name);
750 warningx("specified resource pool does not exist for "
751 "project \"%s\"", proj.pj_name);
754 warningx("could not bind to default resource pool for "
755 "project \"%s\"", proj.pj_name);
760 warningx("setproject failed for project \"%s\"", proj.pj_name);
762 warningx("warning, resource control assignment failed for "
763 "project \"%s\"", proj.pj_name);
767 warning("getdefaultproj");
771 #endif /* HAVE_PROJECT_H */
774 * Disable execution of child processes in the command we are about
775 * to run. On systems with privilege sets, we can remove the exec
776 * privilege. On other systems we use LD_PRELOAD and the like.
779 disable_execute(struct command_details *details)
781 char *cp, **ev, **nenvp;
782 int env_len = 0, env_size = 128;
785 /* Solaris privileges, remove PRIV_PROC_EXEC post-execve. */
786 if (priv_set(PRIV_OFF, PRIV_LIMIT, "PRIV_PROC_EXEC", NULL) == 0)
788 warning("unable to remove PRIV_PROC_EXEC from PRIV_LIMIT");
789 #endif /* HAVE_PRIV_SET */
791 nenvp = emalloc2(env_size, sizeof(char *));
792 for (ev = details->envp; *ev != NULL; ev++) {
793 if (env_len + 2 > env_size) {
795 nenvp = erealloc3(nenvp, env_size, sizeof(char *));
798 * Prune out existing preloaded libraries.
799 * XXX - should save and append instead of replacing.
801 #if defined(__darwin__) || defined(__APPLE__)
802 if (strncmp(*ev, "DYLD_INSERT_LIBRARIES=", sizeof("DYLD_INSERT_LIBRARIES=") - 1) == 0)
804 if (strncmp(*ev, "DYLD_FORCE_FLAT_NAMESPACE=", sizeof("DYLD_INSERT_LIBRARIES=") - 1) == 0)
806 #elif defined(__osf__) || defined(__sgi)
807 if (strncmp(*ev, "_RLD_LIST=", sizeof("_RLD_LIST=") - 1) == 0)
810 if (strncmp(*ev, "LDR_PRELOAD=", sizeof("LDR_PRELOAD=") - 1) == 0)
813 if (strncmp(*ev, "LD_PRELOAD=", sizeof("LD_PRELOAD=") - 1) == 0)
816 nenvp[env_len++] = *ev;
820 * Preload a noexec file? For a list of LD_PRELOAD-alikes, see
821 * http://www.fortran-2000.com/ArnaudRecipes/sharedlib.html
822 * XXX - need to support 32-bit and 64-bit variants
824 #if defined(__darwin__) || defined(__APPLE__)
825 nenvp[env_len++] = "DYLD_FORCE_FLAT_NAMESPACE=";
826 cp = fmt_string("DYLD_INSERT_LIBRARIES", noexec_path);
827 #elif defined(__osf__) || defined(__sgi)
828 easprintf(&cp, "_RLD_LIST=%s:DEFAULT", noexec_path);
830 cp = fmt_string("LDR_PRELOAD", noexec_path);
832 cp = fmt_string("LD_PRELOAD", noexec_path);
836 nenvp[env_len++] = cp;
837 nenvp[env_len] = NULL;
839 details->envp = nenvp;
843 * Setup the execution environment immediately prior to the call to execve()
844 * Returns TRUE on success and FALSE on failure.
847 exec_setup(struct command_details *details, const char *ptyname, int ptyfd)
852 #ifdef HAVE_SETAUTHDB
853 aix_setauthdb(IDtouser(details->euid));
855 pw = getpwuid(details->euid);
856 #ifdef HAVE_SETAUTHDB
861 * Call policy plugin's session init before other setup occurs.
862 * The session init code is expected to print an error as needed.
864 if (policy_init_session(&policy_plugin, pw) != TRUE)
868 if (ISSET(details->flags, CD_RBAC_ENABLED)) {
869 if (selinux_setup(details->selinux_role, details->selinux_type,
870 ptyname ? ptyname : user_details.tty, ptyfd) == -1)
876 #ifdef HAVE_PROJECT_H
879 #ifdef HAVE_GETUSERATTR
880 aix_prep_user(pw->pw_name, ptyname ? ptyname : user_details.tty);
882 #ifdef HAVE_LOGIN_CAP_H
883 if (details->login_class) {
888 * We only use setusercontext() to set the nice value and rlimits.
890 lc = login_getclass((char *)details->login_class);
892 warningx("unknown login class %s", details->login_class);
896 flags = LOGIN_SETRESOURCES|LOGIN_SETPRIORITY;
897 if (setusercontext(lc, pw, pw->pw_uid, flags)) {
898 if (pw->pw_uid != ROOT_UID) {
899 warning("unable to set user context");
902 warning("unable to set user context");
905 #endif /* HAVE_LOGIN_CAP_H */
909 * Set groups, including supplementary group vector.
912 if (ISSET(details->flags, CD_SET_EGID) && setegid(details->egid)) {
913 warning("unable to set egid to runas gid %u", details->egid);
917 if (ISSET(details->flags, CD_SET_GID) && setgid(details->gid)) {
918 warning("unable to set gid to runas gid %u", details->gid);
922 if (!ISSET(details->flags, CD_PRESERVE_GROUPS)) {
923 #ifdef HAVE_GETGROUPS
924 if (details->ngroups >= 0) {
925 if (setgroups(details->ngroups, details->groups) < 0) {
926 warning("unable to set supplementary group IDs");
931 if (pw && initgroups(pw->pw_name, pw->pw_gid) < 0) {
932 warning("unable to set supplementary group IDs");
938 if (ISSET(details->flags, CD_SET_PRIORITY)) {
939 if (setpriority(PRIO_PROCESS, 0, details->priority) != 0) {
940 warning("unable to set process priority");
944 if (ISSET(details->flags, CD_SET_UMASK))
945 (void) umask(details->umask);
946 if (details->chroot) {
947 if (chroot(details->chroot) != 0 || chdir("/") != 0) {
948 warning("unable to change root to %s", details->chroot);
953 if (ISSET(details->flags, CD_NOEXEC))
954 disable_execute(details);
956 #ifdef HAVE_SETRESUID
957 if (setresuid(details->uid, details->euid, details->euid) != 0) {
958 warning("unable to change to runas uid (%u, %u)", details->uid,
963 if (setreuid(details->uid, details->euid) != 0) {
964 warning("unable to change to runas uid (%u, %u)", details->uid,
969 if (seteuid(details->euid) != 0 || setuid(details->euid) != 0) {
970 warning("unable to change to runas uid (%u, %u)", details->uid,
974 #endif /* !HAVE_SETRESUID && !HAVE_SETREUID */
977 * Only change cwd if we have chroot()ed or the policy modules
978 * specifies a different cwd. Must be done after uid change.
981 if (details->chroot || strcmp(details->cwd, user_details.cwd) != 0) {
982 /* Note: cwd is relative to the new root, if any. */
983 if (chdir(details->cwd) != 0) {
984 warning("unable to change directory to %s", details->cwd);
991 * Restore nproc resource limit if pam_limits didn't do it for us.
992 * We must do this *after* the uid change to avoid potential EAGAIN
995 #if defined(__linux__)
998 if (getrlimit(RLIMIT_NPROC, &rl) == 0) {
999 if (rl.rlim_cur == RLIM_INFINITY && rl.rlim_max == RLIM_INFINITY)
1000 (void) setrlimit(RLIMIT_NPROC, &nproclimit);
1012 * Escape any non-alpha numeric or blank characters to make sure
1013 * they are not interpreted specially by the shell.
1016 escape_cmnd(const char *src)
1020 /* Worst case scenario, we have to escape everything. */
1021 cmnd = dst = emalloc((2 * strlen(src)) + 1);
1022 while (*src != '\0') {
1023 if (!isalnum((unsigned char)*src) && !isspace((unsigned char)*src) &&
1024 *src != '_' && *src != '-') {
1025 /* quote potential meta character */
1036 * Run the command and wait for it to complete.
1039 run_command(struct command_details *details)
1041 struct plugin_container *plugin;
1042 struct command_status cstat;
1045 cstat.type = CMD_INVALID;
1048 sudo_execve(details, &cstat);
1050 switch (cstat.type) {
1052 /* exec_setup() or execve() returned an error. */
1053 sudo_debug(9, "calling policy close with errno");
1054 policy_close(&policy_plugin, 0, cstat.val);
1055 tq_foreach_fwd(&io_plugins, plugin) {
1056 sudo_debug(9, "calling I/O close with errno");
1057 iolog_close(plugin, 0, cstat.val);
1062 /* Command ran, exited or was killed. */
1063 sudo_debug(9, "calling policy close with wait status");
1064 policy_close(&policy_plugin, cstat.val, 0);
1065 tq_foreach_fwd(&io_plugins, plugin) {
1066 sudo_debug(9, "calling I/O close with wait status");
1067 iolog_close(plugin, cstat.val, 0);
1069 if (WIFEXITED(cstat.val))
1070 exitcode = WEXITSTATUS(cstat.val);
1071 else if (WIFSIGNALED(cstat.val))
1072 exitcode = WTERMSIG(cstat.val) | 128;
1075 warningx("unexpected child termination condition: %d", cstat.type);
1082 policy_open(struct plugin_container *plugin, char * const settings[],
1083 char * const user_info[], char * const user_env[])
1085 return plugin->u.policy->open(SUDO_API_VERSION, sudo_conversation,
1086 _sudo_printf, settings, user_info, user_env);
1090 policy_close(struct plugin_container *plugin, int exit_status, int error)
1092 plugin->u.policy->close(exit_status, error);
1096 policy_show_version(struct plugin_container *plugin, int verbose)
1098 return plugin->u.policy->show_version(verbose);
1102 policy_check(struct plugin_container *plugin, int argc, char * const argv[],
1103 char *env_add[], char **command_info[], char **argv_out[],
1104 char **user_env_out[])
1106 return plugin->u.policy->check_policy(argc, argv, env_add, command_info,
1107 argv_out, user_env_out);
1111 policy_list(struct plugin_container *plugin, int argc, char * const argv[],
1112 int verbose, const char *list_user)
1114 if (plugin->u.policy->list == NULL) {
1115 warningx("policy plugin %s does not support listing privileges",
1119 return plugin->u.policy->list(argc, argv, verbose, list_user);
1123 policy_validate(struct plugin_container *plugin)
1125 if (plugin->u.policy->validate == NULL) {
1126 warningx("policy plugin %s does not support the -v flag",
1130 return plugin->u.policy->validate();
1134 policy_invalidate(struct plugin_container *plugin, int remove)
1136 if (plugin->u.policy->invalidate == NULL) {
1137 errorx(1, "policy plugin %s does not support the -k/-K flags",
1140 plugin->u.policy->invalidate(remove);
1144 policy_init_session(struct plugin_container *plugin, struct passwd *pwd)
1146 if (plugin->u.policy->init_session)
1147 return plugin->u.policy->init_session(pwd);
1152 iolog_open(struct plugin_container *plugin, char * const settings[],
1153 char * const user_info[], char * const command_info[],
1154 int argc, char * const argv[], char * const user_env[])
1159 * Backwards compatibility for API major 1, minor 0
1161 switch (plugin->u.generic->version) {
1162 case SUDO_API_MKVERSION(1, 0):
1163 rval = plugin->u.io_1_0->open(plugin->u.io_1_0->version,
1164 sudo_conversation, _sudo_printf, settings, user_info, argc, argv,
1168 rval = plugin->u.io->open(SUDO_API_VERSION, sudo_conversation,
1169 _sudo_printf, settings, user_info, command_info, argc, argv,
1176 iolog_close(struct plugin_container *plugin, int exit_status, int error)
1178 plugin->u.io->close(exit_status, error);
1182 iolog_show_version(struct plugin_container *plugin, int verbose)
1184 return plugin->u.io->show_version(verbose);
1188 * Simple debugging/logging.
1191 sudo_debug(int level, const char *fmt, ...)
1196 if (level > debug_level)
1199 /* Backet fmt with program name and a newline to make it a single write */
1200 easprintf(&fmt2, "%s: %s\n", getprogname(), fmt);
1202 vfprintf(stderr, fmt2, ap);