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