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