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: #ifdef 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 = TAILQ_HEAD_INITIALIZER(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(U_("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(U_("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: TAILQ_FOREACH(plugin, &io_plugins, entries) {
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: TAILQ_FOREACH_SAFE(plugin, &io_plugins, entries, next) {
254: ok = iolog_open(plugin, settings, user_info,
255: command_info, nargc, nargv, envp);
256: switch (ok) {
257: case 1:
258: break;
259: case 0:
260: /* I/O plugin asked to be disabled, remove and free. */
261: iolog_unlink(plugin);
262: break;
263: case -2:
264: usage(1);
265: break;
266: default:
267: fatalx(U_("error initializing I/O plugin %s"),
268: plugin->name);
269: }
270: }
271: /* Setup command details and run command/edit. */
272: command_info_to_details(command_info, &command_details);
273: command_details.argv = argv_out;
274: command_details.envp = user_env_out;
275: if (ISSET(sudo_mode, MODE_BACKGROUND))
276: SET(command_details.flags, CD_BACKGROUND);
277: /* Become full root (not just setuid) so user cannot kill us. */
278: if (setuid(ROOT_UID) == -1)
279: warning("setuid(%d)", ROOT_UID);
280: /* Restore coredumpsize resource limit before running. */
281: #ifdef RLIMIT_CORE
282: if (sudo_conf_disable_coredump())
283: (void) setrlimit(RLIMIT_CORE, &corelimit);
284: #endif /* RLIMIT_CORE */
285: if (ISSET(command_details.flags, CD_SUDOEDIT)) {
286: exitcode = sudo_edit(&command_details);
287: } else {
288: exitcode = run_command(&command_details);
289: }
290: /* The close method was called by sudo_edit/run_command. */
291: break;
292: default:
293: fatalx(U_("unexpected sudo mode 0x%x"), sudo_mode);
294: }
295: sudo_debug_exit_int(__func__, __FILE__, __LINE__, sudo_debug_subsys, exitcode);
296: exit(exitcode);
297: }
298:
299: int
300: os_init_common(int argc, char *argv[], char *envp[])
301: {
302: initprogname(argc > 0 ? argv[0] : "sudo");
303: #ifdef STATIC_SUDOERS_PLUGIN
304: preload_static_symbols();
305: #endif
306: return 0;
307: }
308:
309: /*
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.
312: */
313: static void
314: fix_fds(void)
315: {
316: int miss[3], devnull = -1;
317: debug_decl(fix_fds, SUDO_DEBUG_UTIL)
318:
319: /*
320: * stdin, stdout and stderr must be open; set them to /dev/null
321: * if they are closed.
322: */
323: miss[STDIN_FILENO] = fcntl(STDIN_FILENO, F_GETFL, 0) == -1;
324: miss[STDOUT_FILENO] = fcntl(STDOUT_FILENO, F_GETFL, 0) == -1;
325: miss[STDERR_FILENO] = fcntl(STDERR_FILENO, F_GETFL, 0) == -1;
326: if (miss[STDIN_FILENO] || miss[STDOUT_FILENO] || miss[STDERR_FILENO]) {
327: if ((devnull = open(_PATH_DEVNULL, O_RDWR, 0644)) == -1)
328: fatal(U_("unable to open %s"), _PATH_DEVNULL);
329: if (miss[STDIN_FILENO] && dup2(devnull, STDIN_FILENO) == -1)
330: fatal("dup2");
331: if (miss[STDOUT_FILENO] && dup2(devnull, STDOUT_FILENO) == -1)
332: fatal("dup2");
333: if (miss[STDERR_FILENO] && dup2(devnull, STDERR_FILENO) == -1)
334: fatal("dup2");
335: if (devnull > STDERR_FILENO)
336: close(devnull);
337: }
338: debug_return;
339: }
340:
341: /*
342: * Allocate space for groups and fill in using getgrouplist()
343: * for when we cannot (or don't want to) use getgroups().
344: */
345: static int
346: fill_group_list(struct user_details *ud, int system_maxgroups)
347: {
348: int tries, rval = -1;
349: debug_decl(fill_group_list, SUDO_DEBUG_UTIL)
350:
351: /*
352: * If user specified a max number of groups, use it, otherwise keep
353: * trying getgrouplist() until we have enough room in the array.
354: */
355: ud->ngroups = sudo_conf_max_groups();
356: if (ud->ngroups > 0) {
357: ud->groups = emalloc2(ud->ngroups, sizeof(GETGROUPS_T));
358: /* No error on insufficient space if user specified max_groups. */
359: (void)getgrouplist(ud->username, ud->gid, ud->groups, &ud->ngroups);
360: rval = 0;
361: } else {
362: /*
363: * It is possible to belong to more groups in the group database
364: * than NGROUPS_MAX. We start off with NGROUPS_MAX * 4 entries
365: * and double this as needed.
366: */
367: ud->groups = NULL;
368: ud->ngroups = system_maxgroups << 1;
369: for (tries = 0; tries < 10 && rval == -1; tries++) {
370: ud->ngroups <<= 1;
371: efree(ud->groups);
372: ud->groups = emalloc2(ud->ngroups, sizeof(GETGROUPS_T));
373: rval = getgrouplist(ud->username, ud->gid, ud->groups, &ud->ngroups);
374: }
375: }
376: debug_return_int(rval);
377: }
378:
379: static char *
380: get_user_groups(struct user_details *ud)
381: {
382: char *cp, *gid_list = NULL;
383: size_t glsize;
384: int i, len, maxgroups, group_source;
385: debug_decl(get_user_groups, SUDO_DEBUG_UTIL)
386:
387: #if defined(HAVE_SYSCONF) && defined(_SC_NGROUPS_MAX)
388: maxgroups = (int)sysconf(_SC_NGROUPS_MAX);
389: if (maxgroups < 0)
390: #endif
391: maxgroups = NGROUPS_MAX;
392:
393: ud->groups = NULL;
394: group_source = sudo_conf_group_source();
395: if (group_source != GROUP_SOURCE_DYNAMIC) {
396: if ((ud->ngroups = getgroups(0, NULL)) > 0) {
397: /* Use groups from kernel if not too many or source is static. */
398: if (ud->ngroups < maxgroups || group_source == GROUP_SOURCE_STATIC) {
399: ud->groups = emalloc2(ud->ngroups, sizeof(GETGROUPS_T));
400: if (getgroups(ud->ngroups, ud->groups) < 0) {
401: efree(ud->groups);
402: ud->groups = NULL;
403: }
404: }
405: }
406: }
407: if (ud->groups == NULL) {
408: /*
409: * Query group database if kernel list is too small or disabled.
410: * Typically, this is because NFS can only support up to 16 groups.
411: */
412: if (fill_group_list(ud, maxgroups) == -1)
413: fatal(U_("unable to get group vector"));
414: }
415:
416: /*
417: * Format group list as a comma-separated string of gids.
418: */
419: glsize = sizeof("groups=") - 1 + (ud->ngroups * (MAX_UID_T_LEN + 1));
420: gid_list = emalloc(glsize);
421: memcpy(gid_list, "groups=", sizeof("groups=") - 1);
422: cp = gid_list + sizeof("groups=") - 1;
423: for (i = 0; i < ud->ngroups; i++) {
424: /* XXX - check rval */
425: len = snprintf(cp, glsize - (cp - gid_list), "%s%u",
426: i ? "," : "", (unsigned int)ud->groups[i]);
427: cp += len;
428: }
429: debug_return_str(gid_list);
430: }
431:
432: /*
433: * Return user information as an array of name=value pairs.
434: * and fill in struct user_details (which shares the same strings).
435: */
436: static char **
437: get_user_info(struct user_details *ud)
438: {
439: char *cp, **user_info, cwd[PATH_MAX], host[HOST_NAME_MAX + 1];
440: struct passwd *pw;
441: int fd, i = 0;
442: debug_decl(get_user_info, SUDO_DEBUG_UTIL)
443:
444: /* XXX - bound check number of entries */
445: user_info = emalloc2(32, sizeof(char *));
446:
447: ud->pid = getpid();
448: ud->ppid = getppid();
449: ud->pgid = getpgid(0);
450: ud->tcpgid = (pid_t)-1;
451: fd = open(_PATH_TTY, O_RDWR|O_NOCTTY|O_NONBLOCK, 0);
452: if (fd != -1) {
453: ud->tcpgid = tcgetpgrp(fd);
454: close(fd);
455: }
456: ud->sid = getsid(0);
457:
458: ud->uid = getuid();
459: ud->euid = geteuid();
460: ud->gid = getgid();
461: ud->egid = getegid();
462:
463: pw = getpwuid(ud->uid);
464: if (pw == NULL)
465: fatalx(U_("unknown uid %u: who are you?"), (unsigned int)ud->uid);
466:
467: user_info[i] = fmt_string("user", pw->pw_name);
468: if (user_info[i] == NULL)
469: fatal(NULL);
470: ud->username = user_info[i] + sizeof("user=") - 1;
471:
472: /* Stash user's shell for use with the -s flag; don't pass to plugin. */
473: if ((ud->shell = getenv("SHELL")) == NULL || ud->shell[0] == '\0') {
474: ud->shell = pw->pw_shell[0] ? pw->pw_shell : _PATH_BSHELL;
475: }
476: ud->shell = estrdup(ud->shell);
477:
478: easprintf(&user_info[++i], "pid=%d", (int)ud->pid);
479: easprintf(&user_info[++i], "ppid=%d", (int)ud->ppid);
480: easprintf(&user_info[++i], "pgid=%d", (int)ud->pgid);
481: easprintf(&user_info[++i], "tcpgid=%d", (int)ud->tcpgid);
482: easprintf(&user_info[++i], "sid=%d", (int)ud->sid);
483:
484: easprintf(&user_info[++i], "uid=%u", (unsigned int)ud->uid);
485: easprintf(&user_info[++i], "euid=%u", (unsigned int)ud->euid);
486: easprintf(&user_info[++i], "gid=%u", (unsigned int)ud->gid);
487: easprintf(&user_info[++i], "egid=%u", (unsigned int)ud->egid);
488:
489: if ((cp = get_user_groups(ud)) != NULL)
490: user_info[++i] = cp;
491:
492: if (getcwd(cwd, sizeof(cwd)) != NULL) {
493: user_info[++i] = fmt_string("cwd", cwd);
494: if (user_info[i] == NULL)
495: fatal(NULL);
496: ud->cwd = user_info[i] + sizeof("cwd=") - 1;
497: }
498:
499: if ((cp = get_process_ttyname()) != NULL) {
500: user_info[++i] = fmt_string("tty", cp);
501: if (user_info[i] == NULL)
502: fatal(NULL);
503: ud->tty = user_info[i] + sizeof("tty=") - 1;
504: efree(cp);
505: }
506:
507: if (gethostname(host, sizeof(host)) == 0)
508: host[sizeof(host) - 1] = '\0';
509: else
510: strlcpy(host, "localhost", sizeof(host));
511: user_info[++i] = fmt_string("host", host);
512: if (user_info[i] == NULL)
513: fatal(NULL);
514: ud->host = user_info[i] + sizeof("host=") - 1;
515:
516: get_ttysize(&ud->ts_lines, &ud->ts_cols);
517: easprintf(&user_info[++i], "lines=%d", ud->ts_lines);
518: easprintf(&user_info[++i], "cols=%d", ud->ts_cols);
519:
520: user_info[++i] = NULL;
521:
522: debug_return_ptr(user_info);
523: }
524:
525: /*
526: * Convert a command_info array into a command_details structure.
527: */
528: static void
529: command_info_to_details(char * const info[], struct command_details *details)
530: {
531: int i;
532: id_t id;
533: char *cp;
534: const char *errstr;
535: debug_decl(command_info_to_details, SUDO_DEBUG_PCOMM)
536:
537: memset(details, 0, sizeof(*details));
538: details->closefrom = -1;
539: TAILQ_INIT(&details->preserved_fds);
540:
541: #define SET_STRING(s, n) \
542: if (strncmp(s, info[i], sizeof(s) - 1) == 0 && info[i][sizeof(s) - 1]) { \
543: details->n = info[i] + sizeof(s) - 1; \
544: break; \
545: }
546:
547: sudo_debug_printf(SUDO_DEBUG_INFO, "command info from plugin:");
548: for (i = 0; info[i] != NULL; i++) {
549: sudo_debug_printf(SUDO_DEBUG_INFO, " %d: %s", i, info[i]);
550: switch (info[i][0]) {
551: case 'c':
552: SET_STRING("chroot=", chroot)
553: SET_STRING("command=", command)
554: SET_STRING("cwd=", cwd)
555: if (strncmp("closefrom=", info[i], sizeof("closefrom=") - 1) == 0) {
556: cp = info[i] + sizeof("closefrom=") - 1;
557: details->closefrom = strtonum(cp, 0, INT_MAX, &errstr);
558: if (errstr != NULL)
559: fatalx(U_("%s: %s"), info[i], U_(errstr));
560: break;
561: }
562: break;
563: case 'e':
564: if (strncmp("exec_background=", info[i], sizeof("exec_background=") - 1) == 0) {
565: if (atobool(info[i] + sizeof("exec_background=") - 1) == true)
566: SET(details->flags, CD_EXEC_BG);
567: break;
568: }
569: break;
570: case 'l':
571: SET_STRING("login_class=", login_class)
572: break;
573: case 'n':
574: if (strncmp("nice=", info[i], sizeof("nice=") - 1) == 0) {
575: cp = info[i] + sizeof("nice=") - 1;
576: details->priority = strtonum(cp, INT_MIN, INT_MAX, &errstr);
577: if (errstr != NULL)
578: fatalx(U_("%s: %s"), info[i], U_(errstr));
579: SET(details->flags, CD_SET_PRIORITY);
580: break;
581: }
582: if (strncmp("noexec=", info[i], sizeof("noexec=") - 1) == 0) {
583: if (atobool(info[i] + sizeof("noexec=") - 1) == true)
584: SET(details->flags, CD_NOEXEC);
585: break;
586: }
587: break;
588: case 'p':
589: if (strncmp("preserve_groups=", info[i], sizeof("preserve_groups=") - 1) == 0) {
590: if (atobool(info[i] + sizeof("preserve_groups=") - 1) == true)
591: SET(details->flags, CD_PRESERVE_GROUPS);
592: break;
593: }
594: if (strncmp("preserve_fds=", info[i], sizeof("preserve_fds=") - 1) == 0) {
595: parse_preserved_fds(&details->preserved_fds,
596: info[i] + sizeof("preserve_fds=") - 1);
597: break;
598: }
599: break;
600: case 'r':
601: if (strncmp("runas_egid=", info[i], sizeof("runas_egid=") - 1) == 0) {
602: cp = info[i] + sizeof("runas_egid=") - 1;
603: id = atoid(cp, NULL, NULL, &errstr);
604: if (errstr != NULL)
605: fatalx(U_("%s: %s"), info[i], U_(errstr));
606: details->egid = (gid_t)id;
607: SET(details->flags, CD_SET_EGID);
608: break;
609: }
610: if (strncmp("runas_euid=", info[i], sizeof("runas_euid=") - 1) == 0) {
611: cp = info[i] + sizeof("runas_euid=") - 1;
612: id = atoid(cp, NULL, NULL, &errstr);
613: if (errstr != NULL)
614: fatalx(U_("%s: %s"), info[i], U_(errstr));
615: details->euid = (uid_t)id;
616: SET(details->flags, CD_SET_EUID);
617: break;
618: }
619: if (strncmp("runas_gid=", info[i], sizeof("runas_gid=") - 1) == 0) {
620: cp = info[i] + sizeof("runas_gid=") - 1;
621: id = atoid(cp, NULL, NULL, &errstr);
622: if (errstr != NULL)
623: fatalx(U_("%s: %s"), info[i], U_(errstr));
624: details->gid = (gid_t)id;
625: SET(details->flags, CD_SET_GID);
626: break;
627: }
628: if (strncmp("runas_groups=", info[i], sizeof("runas_groups=") - 1) == 0) {
629: /* parse_gid_list() will call fatalx() on error. */
630: cp = info[i] + sizeof("runas_groups=") - 1;
631: details->ngroups = parse_gid_list(cp, NULL, &details->groups);
632: break;
633: }
634: if (strncmp("runas_uid=", info[i], sizeof("runas_uid=") - 1) == 0) {
635: cp = info[i] + sizeof("runas_uid=") - 1;
636: id = atoid(cp, NULL, NULL, &errstr);
637: if (errstr != NULL)
638: fatalx(U_("%s: %s"), info[i], U_(errstr));
639: details->uid = (uid_t)id;
640: SET(details->flags, CD_SET_UID);
641: break;
642: }
643: #ifdef HAVE_PRIV_SET
644: if (strncmp("runas_privs=", info[i], sizeof("runas_privs=") - 1) == 0) {
645: const char *endp;
646: cp = info[i] + sizeof("runas_privs=") - 1;
647: if (*cp != '\0') {
648: details->privs = priv_str_to_set(cp, ",", &endp);
649: if (details->privs == NULL)
650: warning("invalid runas_privs %s", endp);
651: }
652: break;
653: }
654: if (strncmp("runas_limitprivs=", info[i], sizeof("runas_limitprivs=") - 1) == 0) {
655: const char *endp;
656: cp = info[i] + sizeof("runas_limitprivs=") - 1;
657: if (*cp != '\0') {
658: details->limitprivs = priv_str_to_set(cp, ",", &endp);
659: if (details->limitprivs == NULL)
660: warning("invalid runas_limitprivs %s", endp);
661: }
662: break;
663: }
664: #endif /* HAVE_PRIV_SET */
665: break;
666: case 's':
667: SET_STRING("selinux_role=", selinux_role)
668: SET_STRING("selinux_type=", selinux_type)
669: if (strncmp("set_utmp=", info[i], sizeof("set_utmp=") - 1) == 0) {
670: if (atobool(info[i] + sizeof("set_utmp=") - 1) == true)
671: SET(details->flags, CD_SET_UTMP);
672: break;
673: }
674: if (strncmp("sudoedit=", info[i], sizeof("sudoedit=") - 1) == 0) {
675: if (atobool(info[i] + sizeof("sudoedit=") - 1) == true)
676: SET(details->flags, CD_SUDOEDIT);
677: break;
678: }
679: break;
680: case 't':
681: if (strncmp("timeout=", info[i], sizeof("timeout=") - 1) == 0) {
682: cp = info[i] + sizeof("timeout=") - 1;
683: details->timeout = strtonum(cp, 0, INT_MAX, &errstr);
684: if (errstr != NULL)
685: fatalx(U_("%s: %s"), info[i], U_(errstr));
686: SET(details->flags, CD_SET_TIMEOUT);
687: break;
688: }
689: break;
690: case 'u':
691: if (strncmp("umask=", info[i], sizeof("umask=") - 1) == 0) {
692: cp = info[i] + sizeof("umask=") - 1;
693: details->umask = atomode(cp, &errstr);
694: if (errstr != NULL)
695: fatalx(U_("%s: %s"), info[i], U_(errstr));
696: SET(details->flags, CD_SET_UMASK);
697: break;
698: }
699: if (strncmp("use_pty=", info[i], sizeof("use_pty=") - 1) == 0) {
700: if (atobool(info[i] + sizeof("use_pty=") - 1) == true)
701: SET(details->flags, CD_USE_PTY);
702: break;
703: }
704: SET_STRING("utmp_user=", utmp_user)
705: break;
706: }
707: }
708:
709: if (!ISSET(details->flags, CD_SET_EUID))
710: details->euid = details->uid;
711:
712: #ifdef HAVE_SETAUTHDB
713: aix_setauthdb(IDtouser(details->euid));
714: #endif
715: details->pw = getpwuid(details->euid);
716: if (details->pw != NULL && (details->pw = pw_dup(details->pw)) == NULL)
717: fatal(NULL);
718: #ifdef HAVE_SETAUTHDB
719: aix_restoreauthdb();
720: #endif
721:
722: #ifdef HAVE_SELINUX
723: if (details->selinux_role != NULL && is_selinux_enabled() > 0)
724: SET(details->flags, CD_RBAC_ENABLED);
725: #endif
726: debug_return;
727: }
728:
729: static void
730: sudo_check_suid(const char *sudo)
731: {
732: char pathbuf[PATH_MAX];
733: struct stat sb;
734: bool qualified;
735: debug_decl(sudo_check_suid, SUDO_DEBUG_PCOMM)
736:
737: if (geteuid() != 0) {
738: /* Search for sudo binary in PATH if not fully qualified. */
739: qualified = strchr(sudo, '/') != NULL;
740: if (!qualified) {
741: char *path = getenv_unhooked("PATH");
742: if (path != NULL) {
743: int len;
744: char *cp, *colon;
745:
746: cp = path = estrdup(path);
747: do {
748: if ((colon = strchr(cp, ':')))
749: *colon = '\0';
750: len = snprintf(pathbuf, sizeof(pathbuf), "%s/%s", cp, sudo);
751: if (len <= 0 || (size_t)len >= sizeof(pathbuf))
752: continue;
753: if (access(pathbuf, X_OK) == 0) {
754: sudo = pathbuf;
755: qualified = true;
756: break;
757: }
758: cp = colon + 1;
759: } while (colon);
760: efree(path);
761: }
762: }
763:
764: if (qualified && stat(sudo, &sb) == 0) {
765: /* Try to determine why sudo was not running as root. */
766: if (sb.st_uid != ROOT_UID || !ISSET(sb.st_mode, S_ISUID)) {
767: fatalx(
768: U_("%s must be owned by uid %d and have the setuid bit set"),
769: sudo, ROOT_UID);
770: } else {
771: fatalx(U_("effective uid is not %d, is %s on a file system "
772: "with the 'nosuid' option set or an NFS file system without"
773: " root privileges?"), ROOT_UID, sudo);
774: }
775: } else {
776: fatalx(
777: U_("effective uid is not %d, is sudo installed setuid root?"),
778: ROOT_UID);
779: }
780: }
781: debug_return;
782: }
783:
784: /*
785: * Disable core dumps to avoid dropping a core with user password in it.
786: * We will reset this limit before executing the command.
787: * Not all operating systems disable core dumps for setuid processes.
788: */
789: static void
790: disable_coredumps(void)
791: {
792: #if defined(RLIMIT_CORE)
793: struct rlimit rl;
794: debug_decl(disable_coredumps, SUDO_DEBUG_UTIL)
795:
796: /*
797: * Turn off core dumps?
798: */
799: if (sudo_conf_disable_coredump()) {
800: (void) getrlimit(RLIMIT_CORE, &corelimit);
801: memcpy(&rl, &corelimit, sizeof(struct rlimit));
802: rl.rlim_cur = 0;
803: (void) setrlimit(RLIMIT_CORE, &rl);
804: }
805: debug_return;
806: #endif /* RLIMIT_CORE */
807: }
808:
809: /*
810: * Unlimit the number of processes since Linux's setuid() will
811: * apply resource limits when changing uid and return EAGAIN if
812: * nproc would be exceeded by the uid switch.
813: */
814: static void
815: unlimit_nproc(void)
816: {
817: #ifdef __linux__
818: struct rlimit rl;
819: debug_decl(unlimit_nproc, SUDO_DEBUG_UTIL)
820:
821: (void) getrlimit(RLIMIT_NPROC, &nproclimit);
822: rl.rlim_cur = rl.rlim_max = RLIM_INFINITY;
823: if (setrlimit(RLIMIT_NPROC, &rl) != 0) {
824: memcpy(&rl, &nproclimit, sizeof(struct rlimit));
825: rl.rlim_cur = rl.rlim_max;
826: (void)setrlimit(RLIMIT_NPROC, &rl);
827: }
828: debug_return;
829: #endif /* __linux__ */
830: }
831:
832: /*
833: * Restore saved value of RLIMIT_NPROC.
834: */
835: static void
836: restore_nproc(void)
837: {
838: #ifdef __linux__
839: debug_decl(restore_nproc, SUDO_DEBUG_UTIL)
840:
841: (void) setrlimit(RLIMIT_NPROC, &nproclimit);
842:
843: debug_return;
844: #endif /* __linux__ */
845: }
846:
847: /*
848: * Setup the execution environment immediately prior to the call to execve()
849: * Returns true on success and false on failure.
850: */
851: bool
852: exec_setup(struct command_details *details, const char *ptyname, int ptyfd)
853: {
854: bool rval = false;
855: debug_decl(exec_setup, SUDO_DEBUG_EXEC)
856:
857: #ifdef HAVE_SELINUX
858: if (ISSET(details->flags, CD_RBAC_ENABLED)) {
859: if (selinux_setup(details->selinux_role, details->selinux_type,
860: ptyname ? ptyname : user_details.tty, ptyfd) == -1)
861: goto done;
862: }
863: #endif
864:
865: if (details->pw != NULL) {
866: #ifdef HAVE_PROJECT_H
867: set_project(details->pw);
868: #endif
869: #ifdef HAVE_PRIV_SET
870: if (details->privs != NULL) {
871: if (setppriv(PRIV_SET, PRIV_INHERITABLE, details->privs) != 0) {
872: warning("unable to set privileges");
873: goto done;
874: }
875: }
876: if (details->limitprivs != NULL) {
877: if (setppriv(PRIV_SET, PRIV_LIMIT, details->limitprivs) != 0) {
878: warning("unable to set limit privileges");
879: goto done;
880: }
881: } else if (details->privs != NULL) {
882: if (setppriv(PRIV_SET, PRIV_LIMIT, details->privs) != 0) {
883: warning("unable to set limit privileges");
884: goto done;
885: }
886: }
887: #endif /* HAVE_PRIV_SET */
888:
889: #ifdef HAVE_GETUSERATTR
890: aix_prep_user(details->pw->pw_name, ptyname ? ptyname : user_details.tty);
891: #endif
892: #ifdef HAVE_LOGIN_CAP_H
893: if (details->login_class) {
894: int flags;
895: login_cap_t *lc;
896:
897: /*
898: * We only use setusercontext() to set the nice value and rlimits
899: * unless this is a login shell (sudo -i).
900: */
901: lc = login_getclass((char *)details->login_class);
902: if (!lc) {
903: warningx(U_("unknown login class %s"), details->login_class);
904: errno = ENOENT;
905: goto done;
906: }
907: if (ISSET(sudo_mode, MODE_LOGIN_SHELL)) {
908: /* Set everything except user, group and login name. */
909: flags = LOGIN_SETALL;
910: CLR(flags, LOGIN_SETGROUP|LOGIN_SETLOGIN|LOGIN_SETUSER|LOGIN_SETENV|LOGIN_SETPATH);
911: CLR(details->flags, CD_SET_UMASK); /* LOGIN_UMASK instead */
912: } else {
913: flags = LOGIN_SETRESOURCES|LOGIN_SETPRIORITY;
914: }
915: if (setusercontext(lc, details->pw, details->pw->pw_uid, flags)) {
916: warning(U_("unable to set user context"));
917: if (details->pw->pw_uid != ROOT_UID)
918: goto done;
919: }
920: }
921: #endif /* HAVE_LOGIN_CAP_H */
922: }
923:
924: /*
925: * Set groups, including supplementary group vector.
926: */
927: if (!ISSET(details->flags, CD_PRESERVE_GROUPS)) {
928: if (details->ngroups >= 0) {
929: if (sudo_setgroups(details->ngroups, details->groups) < 0) {
930: warning(U_("unable to set supplementary group IDs"));
931: goto done;
932: }
933: }
934: }
935: #ifdef HAVE_SETEUID
936: if (ISSET(details->flags, CD_SET_EGID) && setegid(details->egid)) {
937: warning(U_("unable to set effective gid to runas gid %u"),
938: (unsigned int)details->egid);
939: goto done;
940: }
941: #endif
942: if (ISSET(details->flags, CD_SET_GID) && setgid(details->gid)) {
943: warning(U_("unable to set gid to runas gid %u"),
944: (unsigned int)details->gid);
945: goto done;
946: }
947:
948: if (ISSET(details->flags, CD_SET_PRIORITY)) {
949: if (setpriority(PRIO_PROCESS, 0, details->priority) != 0) {
950: warning(U_("unable to set process priority"));
951: goto done;
952: }
953: }
954: if (ISSET(details->flags, CD_SET_UMASK))
955: (void) umask(details->umask);
956: if (details->chroot) {
957: if (chroot(details->chroot) != 0 || chdir("/") != 0) {
958: warning(U_("unable to change root to %s"), details->chroot);
959: goto done;
960: }
961: }
962:
963: /*
964: * Unlimit the number of processes since Linux's setuid() will
965: * return EAGAIN if RLIMIT_NPROC would be exceeded by the uid switch.
966: */
967: unlimit_nproc();
968:
969: #ifdef HAVE_SETRESUID
970: if (setresuid(details->uid, details->euid, details->euid) != 0) {
971: warning(U_("unable to change to runas uid (%u, %u)"), details->uid,
972: details->euid);
973: goto done;
974: }
975: #elif defined(HAVE_SETREUID)
976: if (setreuid(details->uid, details->euid) != 0) {
977: warning(U_("unable to change to runas uid (%u, %u)"),
978: (unsigned int)details->uid, (unsigned int)details->euid);
979: goto done;
980: }
981: #else
982: if (seteuid(details->euid) != 0 || setuid(details->euid) != 0) {
983: warning(U_("unable to change to runas uid (%u, %u)"), details->uid,
984: details->euid);
985: goto done;
986: }
987: #endif /* !HAVE_SETRESUID && !HAVE_SETREUID */
988:
989: /* Restore previous value of RLIMIT_NPROC. */
990: restore_nproc();
991:
992: /*
993: * Only change cwd if we have chroot()ed or the policy modules
994: * specifies a different cwd. Must be done after uid change.
995: */
996: if (details->cwd) {
997: if (details->chroot || strcmp(details->cwd, user_details.cwd) != 0) {
998: /* Note: cwd is relative to the new root, if any. */
999: if (chdir(details->cwd) != 0) {
1000: warning(U_("unable to change directory to %s"), details->cwd);
1001: goto done;
1002: }
1003: }
1004: }
1005:
1006: rval = true;
1007:
1008: done:
1009: debug_return_bool(rval);
1010: }
1011:
1012: /*
1013: * Run the command and wait for it to complete.
1014: */
1015: int
1016: run_command(struct command_details *details)
1017: {
1018: struct plugin_container *plugin;
1019: struct command_status cstat;
1020: int exitcode = 1;
1021: debug_decl(run_command, SUDO_DEBUG_EXEC)
1022:
1023: cstat.type = CMD_INVALID;
1024: cstat.val = 0;
1025:
1026: sudo_execute(details, &cstat);
1027:
1028: switch (cstat.type) {
1029: case CMD_ERRNO:
1030: /* exec_setup() or execve() returned an error. */
1031: sudo_debug_printf(SUDO_DEBUG_DEBUG,
1032: "calling policy close with errno %d", cstat.val);
1033: policy_close(&policy_plugin, 0, cstat.val);
1034: TAILQ_FOREACH(plugin, &io_plugins, entries) {
1035: sudo_debug_printf(SUDO_DEBUG_DEBUG,
1036: "calling I/O close with errno %d", cstat.val);
1037: iolog_close(plugin, 0, cstat.val);
1038: }
1039: exitcode = 1;
1040: break;
1041: case CMD_WSTATUS:
1042: /* Command ran, exited or was killed. */
1043: sudo_debug_printf(SUDO_DEBUG_DEBUG,
1044: "calling policy close with wait status %d", cstat.val);
1045: policy_close(&policy_plugin, cstat.val, 0);
1046: TAILQ_FOREACH(plugin, &io_plugins, entries) {
1047: sudo_debug_printf(SUDO_DEBUG_DEBUG,
1048: "calling I/O close with wait status %d", cstat.val);
1049: iolog_close(plugin, cstat.val, 0);
1050: }
1051: if (WIFEXITED(cstat.val))
1052: exitcode = WEXITSTATUS(cstat.val);
1053: else if (WIFSIGNALED(cstat.val))
1054: exitcode = WTERMSIG(cstat.val) | 128;
1055: break;
1056: default:
1057: warningx(U_("unexpected child termination condition: %d"), cstat.type);
1058: break;
1059: }
1060: debug_return_int(exitcode);
1061: }
1062:
1063: static int
1064: policy_open(struct plugin_container *plugin, char * const settings[],
1065: char * const user_info[], char * const user_env[])
1066: {
1067: int rval;
1068: debug_decl(policy_open, SUDO_DEBUG_PCOMM)
1069:
1070: /*
1071: * Backwards compatibility for older API versions
1072: */
1073: switch (plugin->u.generic->version) {
1074: case SUDO_API_MKVERSION(1, 0):
1075: case SUDO_API_MKVERSION(1, 1):
1076: rval = plugin->u.policy_1_0->open(plugin->u.io_1_0->version,
1077: sudo_conversation, _sudo_printf, settings, user_info, user_env);
1078: break;
1079: default:
1080: rval = plugin->u.policy->open(SUDO_API_VERSION, sudo_conversation,
1081: _sudo_printf, settings, user_info, user_env, plugin->options);
1082: }
1083:
1084: debug_return_bool(rval);
1085: }
1086:
1087: static void
1088: policy_close(struct plugin_container *plugin, int exit_status, int error)
1089: {
1090: debug_decl(policy_close, SUDO_DEBUG_PCOMM)
1091: if (plugin->u.policy->close != NULL)
1092: plugin->u.policy->close(exit_status, error);
1093: else
1094: warning(U_("unable to execute %s"), command_details.command);
1095: debug_return;
1096: }
1097:
1098: static int
1099: policy_show_version(struct plugin_container *plugin, int verbose)
1100: {
1101: debug_decl(policy_show_version, SUDO_DEBUG_PCOMM)
1102: if (plugin->u.policy->show_version == NULL)
1103: debug_return_bool(true);
1104: debug_return_bool(plugin->u.policy->show_version(verbose));
1105: }
1106:
1107: static int
1108: policy_check(struct plugin_container *plugin, int argc, char * const argv[],
1109: char *env_add[], char **command_info[], char **argv_out[],
1110: char **user_env_out[])
1111: {
1112: debug_decl(policy_check, SUDO_DEBUG_PCOMM)
1113: if (plugin->u.policy->check_policy == NULL) {
1114: fatalx(U_("policy plugin %s is missing the `check_policy' method"),
1115: plugin->name);
1116: }
1117: debug_return_bool(plugin->u.policy->check_policy(argc, argv, env_add,
1118: command_info, argv_out, user_env_out));
1119: }
1120:
1121: static int
1122: policy_list(struct plugin_container *plugin, int argc, char * const argv[],
1123: int verbose, const char *list_user)
1124: {
1125: debug_decl(policy_list, SUDO_DEBUG_PCOMM)
1126: if (plugin->u.policy->list == NULL) {
1127: warningx(U_("policy plugin %s does not support listing privileges"),
1128: plugin->name);
1129: debug_return_bool(false);
1130: }
1131: debug_return_bool(plugin->u.policy->list(argc, argv, verbose, list_user));
1132: }
1133:
1134: static int
1135: policy_validate(struct plugin_container *plugin)
1136: {
1137: debug_decl(policy_validate, SUDO_DEBUG_PCOMM)
1138: if (plugin->u.policy->validate == NULL) {
1139: warningx(U_("policy plugin %s does not support the -v option"),
1140: plugin->name);
1141: debug_return_bool(false);
1142: }
1143: debug_return_bool(plugin->u.policy->validate());
1144: }
1145:
1146: static void
1147: policy_invalidate(struct plugin_container *plugin, int remove)
1148: {
1149: debug_decl(policy_invalidate, SUDO_DEBUG_PCOMM)
1150: if (plugin->u.policy->invalidate == NULL) {
1151: fatalx(U_("policy plugin %s does not support the -k/-K options"),
1152: plugin->name);
1153: }
1154: plugin->u.policy->invalidate(remove);
1155: debug_return;
1156: }
1157:
1158: int
1159: policy_init_session(struct command_details *details)
1160: {
1161: int rval = true;
1162: debug_decl(policy_init_session, SUDO_DEBUG_PCOMM)
1163:
1164: if (policy_plugin.u.policy->init_session) {
1165: /*
1166: * Backwards compatibility for older API versions
1167: */
1168: switch (policy_plugin.u.generic->version) {
1169: case SUDO_API_MKVERSION(1, 0):
1170: case SUDO_API_MKVERSION(1, 1):
1171: rval = policy_plugin.u.policy_1_0->init_session(details->pw);
1172: break;
1173: default:
1174: rval = policy_plugin.u.policy->init_session(details->pw,
1175: &details->envp);
1176: }
1177: }
1178: debug_return_bool(rval);
1179: }
1180:
1181: static int
1182: iolog_open(struct plugin_container *plugin, char * const settings[],
1183: char * const user_info[], char * const command_info[],
1184: int argc, char * const argv[], char * const user_env[])
1185: {
1186: int rval;
1187: debug_decl(iolog_open, SUDO_DEBUG_PCOMM)
1188:
1189: /*
1190: * Backwards compatibility for older API versions
1191: */
1192: switch (plugin->u.generic->version) {
1193: case SUDO_API_MKVERSION(1, 0):
1194: rval = plugin->u.io_1_0->open(plugin->u.io_1_0->version,
1195: sudo_conversation, _sudo_printf, settings, user_info, argc, argv,
1196: user_env);
1197: break;
1198: case SUDO_API_MKVERSION(1, 1):
1199: rval = plugin->u.io_1_1->open(plugin->u.io_1_1->version,
1200: sudo_conversation, _sudo_printf, settings, user_info,
1201: command_info, argc, argv, user_env);
1202: break;
1203: default:
1204: rval = plugin->u.io->open(SUDO_API_VERSION, sudo_conversation,
1205: _sudo_printf, settings, user_info, command_info,
1206: argc, argv, user_env, plugin->options);
1207: }
1208: debug_return_bool(rval);
1209: }
1210:
1211: static void
1212: iolog_close(struct plugin_container *plugin, int exit_status, int error)
1213: {
1214: debug_decl(iolog_close, SUDO_DEBUG_PCOMM)
1215: if (plugin->u.io->close != NULL)
1216: plugin->u.io->close(exit_status, error);
1217: debug_return;
1218: }
1219:
1220: static int
1221: iolog_show_version(struct plugin_container *plugin, int verbose)
1222: {
1223: debug_decl(iolog_show_version, SUDO_DEBUG_PCOMM)
1224: if (plugin->u.io->show_version == NULL)
1225: debug_return_bool(true);
1226: debug_return_bool(plugin->u.io->show_version(verbose));
1227: }
1228:
1229: /*
1230: * Remove the specified I/O logging plugin from the io_plugins list.
1231: * Deregisters any hooks before unlinking, then frees the container.
1232: */
1233: static void
1234: iolog_unlink(struct plugin_container *plugin)
1235: {
1236: debug_decl(iolog_unlink, SUDO_DEBUG_PCOMM)
1237:
1238: /* Deregister hooks, if any. */
1239: if (plugin->u.io->version >= SUDO_API_MKVERSION(1, 2)) {
1240: if (plugin->u.io->deregister_hooks != NULL)
1241: plugin->u.io->deregister_hooks(SUDO_HOOK_VERSION,
1242: deregister_hook);
1243: }
1244: /* Remove from io_plugins list and free. */
1245: TAILQ_REMOVE(&io_plugins, plugin, entries);
1246: efree(plugin);
1247:
1248: debug_return;
1249: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>