Annotation of src/usr.bin/sudo/sudo.c, Revision 1.26
1.1 millert 1: /*
1.23 millert 2: * Copyright (c) 1993-1996,1998-2004 Todd C. Miller <Todd.Miller@courtesan.com>
1.1 millert 3: *
1.23 millert 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.
1.1 millert 7: *
1.23 millert 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.
1.1 millert 15: *
1.20 millert 16: * Sponsored in part by the Defense Advanced Research Projects
17: * Agency (DARPA) and Air Force Research Laboratory, Air Force
18: * Materiel Command, USAF, under agreement number F39502-99-1-0512.
19: *
1.1 millert 20: * For a brief history of sudo, please see the HISTORY file included
21: * with this distribution.
22: */
23:
1.19 millert 24: #define _SUDO_MAIN
1.1 millert 25:
1.23 millert 26: #ifdef __TANDEM
27: # include <floss.h>
28: #endif
29:
1.1 millert 30: #include "config.h"
31:
1.11 millert 32: #include <sys/types.h>
33: #include <sys/stat.h>
34: #include <sys/param.h>
35: #include <sys/socket.h>
36: #ifdef HAVE_SETRLIMIT
37: # include <sys/time.h>
38: # include <sys/resource.h>
39: #endif
1.1 millert 40: #include <stdio.h>
41: #ifdef STDC_HEADERS
1.11 millert 42: # include <stdlib.h>
43: # include <stddef.h>
44: #else
45: # ifdef HAVE_STDLIB_H
46: # include <stdlib.h>
47: # endif
1.1 millert 48: #endif /* STDC_HEADERS */
1.11 millert 49: #ifdef HAVE_STRING_H
50: # if defined(HAVE_MEMORY_H) && !defined(STDC_HEADERS)
51: # include <memory.h>
52: # endif
53: # include <string.h>
54: #else
55: # ifdef HAVE_STRINGS_H
56: # include <strings.h>
57: # endif
58: #endif /* HAVE_STRING_H */
1.1 millert 59: #ifdef HAVE_UNISTD_H
1.11 millert 60: # include <unistd.h>
1.1 millert 61: #endif /* HAVE_UNISTD_H */
1.19 millert 62: #ifdef HAVE_ERR_H
63: # include <err.h>
64: #else
65: # include "emul/err.h"
66: #endif /* HAVE_ERR_H */
1.1 millert 67: #include <pwd.h>
68: #include <errno.h>
69: #include <fcntl.h>
70: #include <signal.h>
71: #include <grp.h>
72: #include <time.h>
73: #include <netinet/in.h>
74: #include <netdb.h>
75: #if defined(HAVE_GETPRPWNAM) && defined(HAVE_SET_AUTH_PARAMETERS)
76: # ifdef __hpux
77: # undef MAXINT
78: # include <hpsecurity.h>
79: # else
80: # include <sys/security.h>
81: # endif /* __hpux */
82: # include <prot.h>
83: #endif /* HAVE_GETPRPWNAM && HAVE_SET_AUTH_PARAMETERS */
1.8 millert 84: #ifdef HAVE_LOGIN_CAP_H
1.4 millert 85: # include <login_cap.h>
86: # ifndef LOGIN_DEFROOTCLASS
87: # define LOGIN_DEFROOTCLASS "daemon"
88: # endif
89: #endif
1.1 millert 90:
91: #include "sudo.h"
92: #include "interfaces.h"
93: #include "version.h"
94:
95: #ifndef lint
1.23 millert 96: static const char rcsid[] = "$Sudo: sudo.c,v 1.370 2004/08/24 18:01:13 millert Exp $";
1.1 millert 97: #endif /* lint */
98:
99: /*
100: * Prototypes
101: */
1.11 millert 102: static int init_vars __P((int));
1.19 millert 103: static int parse_args __P((int, char **));
1.11 millert 104: static void check_sudoers __P((void));
105: static void initial_setup __P((void));
106: static void set_loginclass __P((struct passwd *));
1.1 millert 107: static void usage __P((int));
108: static void usage_excl __P((int));
1.6 millert 109: static struct passwd *get_authpw __P((void));
1.23 millert 110: extern int sudo_edit __P((int, char **));
1.11 millert 111: extern void list_matches __P((void));
1.23 millert 112: extern char **rebuild_env __P((char **, int, int));
1.11 millert 113: extern char **zero_env __P((char **));
114: extern struct passwd *sudo_getpwnam __P((const char *));
1.1 millert 115: extern struct passwd *sudo_getpwuid __P((uid_t));
1.23 millert 116: extern struct passwd *sudo_pwdup __P((const struct passwd *));
1.1 millert 117:
118: /*
119: * Globals
120: */
1.19 millert 121: int Argc, NewArgc;
122: char **Argv, **NewArgv;
1.23 millert 123: char *prev_user;
1.1 millert 124: struct sudo_user sudo_user;
1.6 millert 125: struct passwd *auth_pw;
1.19 millert 126: FILE *sudoers_fp;
1.1 millert 127: struct interface *interfaces;
128: int num_interfaces;
1.4 millert 129: int tgetpass_flags;
1.17 millert 130: uid_t timestamp_uid;
1.1 millert 131: extern int errorlineno;
1.4 millert 132: #if defined(RLIMIT_CORE) && !defined(SUDO_DEVEL)
133: static struct rlimit corelimit;
1.20 millert 134: #endif /* RLIMIT_CORE && !SUDO_DEVEL */
1.8 millert 135: #ifdef HAVE_LOGIN_CAP_H
136: login_cap_t *lc;
137: #endif /* HAVE_LOGIN_CAP_H */
138: #ifdef HAVE_BSD_AUTH_H
139: char *login_style;
140: #endif /* HAVE_BSD_AUTH_H */
1.23 millert 141: sigaction_t saved_sa_int, saved_sa_quit, saved_sa_tstp, saved_sa_chld;
1.17 millert 142: void (*set_perms) __P((int));
1.1 millert 143:
144:
145: int
1.11 millert 146: main(argc, argv, envp)
1.1 millert 147: int argc;
148: char **argv;
1.11 millert 149: char **envp;
1.1 millert 150: {
151: int validated;
152: int fd;
153: int cmnd_status;
154: int sudo_mode;
1.11 millert 155: int pwflag;
1.12 millert 156: char **new_environ;
1.23 millert 157: sigaction_t sa;
1.11 millert 158: extern int printmatches;
1.1 millert 159: extern char **environ;
160:
1.19 millert 161: Argv = argv;
1.23 millert 162: if ((Argc = argc) < 1)
163: usage(1);
1.19 millert 164:
1.1 millert 165: /* Must be done as the first thing... */
166: #if defined(HAVE_GETPRPWNAM) && defined(HAVE_SET_AUTH_PARAMETERS)
1.19 millert 167: (void) set_auth_parameters(Argc, Argv);
1.1 millert 168: # ifdef HAVE_INITPRIVS
169: initprivs();
170: # endif
171: #endif /* HAVE_GETPRPWNAM && HAVE_SET_AUTH_PARAMETERS */
172:
1.11 millert 173: /* Zero out the environment. */
174: environ = zero_env(envp);
175:
1.19 millert 176: if (geteuid() != 0)
177: errx(1, "must be setuid root");
1.1 millert 178:
179: /*
1.17 millert 180: * Signal setup:
181: * Ignore keyboard-generated signals so the user cannot interrupt
182: * us at some point and avoid the logging.
183: * Install handler to wait for children when they exit.
1.1 millert 184: */
1.11 millert 185: sigemptyset(&sa.sa_mask);
186: sa.sa_flags = SA_RESTART;
187: sa.sa_handler = SIG_IGN;
1.17 millert 188: (void) sigaction(SIGINT, &sa, &saved_sa_int);
189: (void) sigaction(SIGQUIT, &sa, &saved_sa_quit);
190: (void) sigaction(SIGTSTP, &sa, &saved_sa_tstp);
191: sa.sa_handler = reapchild;
192: (void) sigaction(SIGCHLD, &sa, &saved_sa_chld);
1.1 millert 193:
194: /*
1.17 millert 195: * Turn off core dumps, close open files and setup set_perms().
1.1 millert 196: */
197: initial_setup();
1.11 millert 198: setpwent();
1.1 millert 199:
200: /* Parse our arguments. */
1.19 millert 201: sudo_mode = parse_args(Argc, Argv);
1.1 millert 202:
203: /* Setup defaults data structures. */
204: init_defaults();
205:
1.11 millert 206: /* Load the list of local ip addresses and netmasks. */
207: load_interfaces();
208:
209: pwflag = 0;
1.23 millert 210: if (ISSET(sudo_mode, MODE_SHELL))
1.1 millert 211: user_cmnd = "shell";
1.23 millert 212: else if (ISSET(sudo_mode, MODE_EDIT))
213: user_cmnd = "sudoedit";
1.1 millert 214: else
215: switch (sudo_mode) {
216: case MODE_VERSION:
217: (void) printf("Sudo version %s\n", version);
218: if (getuid() == 0) {
219: putchar('\n');
220: dump_auth_methods();
221: dump_defaults();
1.11 millert 222: dump_interfaces();
1.1 millert 223: }
224: exit(0);
225: break;
226: case MODE_HELP:
227: usage(0);
228: break;
229: case MODE_VALIDATE:
230: user_cmnd = "validate";
1.23 millert 231: pwflag = I_VERIFYPW;
1.1 millert 232: break;
233: case MODE_KILL:
234: case MODE_INVALIDATE:
235: user_cmnd = "kill";
1.11 millert 236: pwflag = -1;
1.1 millert 237: break;
238: case MODE_LISTDEFS:
239: list_options();
240: exit(0);
241: break;
242: case MODE_LIST:
243: user_cmnd = "list";
1.23 millert 244: pwflag = I_LISTPW;
1.1 millert 245: printmatches = 1;
246: break;
247: }
248:
249: /* Must have a command to run... */
250: if (user_cmnd == NULL && NewArgc == 0)
251: usage(1);
252:
253: cmnd_status = init_vars(sudo_mode);
254:
1.23 millert 255: #ifdef HAVE_LDAP
256: validated = sudo_ldap_check(pwflag);
257:
258: /* Skip reading /etc/sudoers if LDAP told us to */
259: if (def_ignore_local_sudoers); /* skips */
260: else if (ISSET(validated, VALIDATE_OK) && !printmatches); /* skips */
261: else if (ISSET(validated, VALIDATE_OK) && printmatches)
262: {
263: check_sudoers(); /* check mode/owner on _PATH_SUDOERS */
264:
265: /* User is found in LDAP and we want a list of all sudo commands the
266: * user can do, so consult sudoers but throw away result.
267: */
268: sudoers_lookup(pwflag);
269: }
270: else
271: #endif
272: {
273: check_sudoers(); /* check mode/owner on _PATH_SUDOERS */
1.1 millert 274:
1.23 millert 275: /* Validate the user but don't search for pseudo-commands. */
276: validated = sudoers_lookup(pwflag);
277: }
1.26 ! millert 278: if (safe_cmnd == NULL)
! 279: safe_cmnd = user_cmnd;
1.13 millert 280:
281: /*
1.17 millert 282: * If we are using set_perms_posix() and the stay_setuid flag was not set,
283: * set the real, effective and saved uids to 0 and use set_perms_nosuid()
1.13 millert 284: * instead of set_perms_posix().
285: */
1.17 millert 286: #if !defined(HAVE_SETRESUID) && !defined(HAVE_SETREUID) && \
287: !defined(NO_SAVED_IDS) && defined(_SC_SAVED_IDS) && defined(_SC_VERSION)
1.23 millert 288: if (!def_stay_setuid && set_perms == set_perms_posix) {
1.13 millert 289: if (setuid(0)) {
290: perror("setuid(0)");
291: exit(1);
292: }
1.17 millert 293: set_perms = set_perms_nosuid;
1.13 millert 294: }
295: #endif
1.1 millert 296:
1.11 millert 297: /*
1.17 millert 298: * Look up the timestamp dir owner if one is specified.
299: */
1.23 millert 300: if (def_timestampowner) {
1.17 millert 301: struct passwd *pw;
302:
1.23 millert 303: if (*def_timestampowner == '#')
304: pw = getpwuid(atoi(def_timestampowner + 1));
1.17 millert 305: else
1.23 millert 306: pw = getpwnam(def_timestampowner);
1.17 millert 307: if (!pw)
308: log_error(0, "timestamp owner (%s): No such user",
1.23 millert 309: def_timestampowner);
1.17 millert 310: timestamp_uid = pw->pw_uid;
311: }
312:
1.2 millert 313: /* This goes after the sudoers parse since we honor sudoers options. */
1.1 millert 314: if (sudo_mode == MODE_KILL || sudo_mode == MODE_INVALIDATE) {
315: remove_timestamp((sudo_mode == MODE_KILL));
316: exit(0);
317: }
318:
1.23 millert 319: if (ISSET(validated, VALIDATE_ERROR))
1.1 millert 320: log_error(0, "parse error in %s near line %d", _PATH_SUDOERS,
321: errorlineno);
322:
323: /* Is root even allowed to run sudo? */
1.23 millert 324: if (user_uid == 0 && !def_root_sudo) {
1.17 millert 325: (void) fprintf(stderr,
326: "Sorry, %s has been configured to not allow root to run it.\n",
1.19 millert 327: getprogname());
1.1 millert 328: exit(1);
329: }
330:
1.11 millert 331: /* If given the -P option, set the "preserve_groups" flag. */
1.23 millert 332: if (ISSET(sudo_mode, MODE_PRESERVE_GROUPS))
333: def_preserve_groups = TRUE;
1.11 millert 334:
1.3 millert 335: /* If no command line args and "set_home" is not set, error out. */
1.23 millert 336: if (ISSET(sudo_mode, MODE_IMPLIED_SHELL) && !def_shell_noargs)
1.3 millert 337: usage(1);
338:
1.15 millert 339: /* May need to set $HOME to target user if we are running a command. */
1.23 millert 340: if (ISSET(sudo_mode, MODE_RUN) && (def_always_set_home ||
341: (ISSET(sudo_mode, MODE_SHELL) && def_set_home)))
342: SET(sudo_mode, MODE_RESET_HOME);
1.2 millert 343:
1.1 millert 344: /* Bail if a tty is required and we don't have one. */
1.23 millert 345: if (def_requiretty) {
1.1 millert 346: if ((fd = open(_PATH_TTY, O_RDWR|O_NOCTTY)) == -1)
347: log_error(NO_MAIL, "sorry, you must have a tty to run sudo");
348: else
349: (void) close(fd);
350: }
351:
1.6 millert 352: /* Fill in passwd struct based on user we are authenticating as. */
353: auth_pw = get_authpw();
1.4 millert 354:
1.23 millert 355: /* Require a password if sudoers says so. */
356: if (!ISSET(validated, FLAG_NOPASS))
357: check_user(ISSET(validated, FLAG_CHECK_USER));
358:
359: /* If run as root with SUDO_USER set, set sudo_user.pw to that user. */
360: if (user_uid == 0 && prev_user != NULL && strcmp(prev_user, "root") != 0) {
361: struct passwd *pw;
362:
363: if ((pw = sudo_getpwnam(prev_user)) != NULL) {
364: free(sudo_user.pw);
365: sudo_user.pw = pw;
366: }
367: }
1.1 millert 368:
1.23 millert 369: /* Build a new environment that avoids any nasty bits if we have a cmnd. */
370: if (ISSET(sudo_mode, MODE_RUN))
371: new_environ = rebuild_env(envp, sudo_mode, ISSET(validated, FLAG_NOEXEC));
372: else
373: new_environ = envp;
1.14 millert 374:
1.23 millert 375: if (ISSET(validated, VALIDATE_OK)) {
1.1 millert 376: /* Finally tell the user if the command did not exist. */
377: if (cmnd_status == NOT_FOUND_DOT) {
1.19 millert 378: warnx("ignoring `%s' found in '.'\nUse `sudo ./%s' if this is the `%s' you wish to run.", user_cmnd, user_cmnd, user_cmnd);
1.1 millert 379: exit(1);
380: } else if (cmnd_status == NOT_FOUND) {
1.19 millert 381: warnx("%s: command not found", user_cmnd);
1.1 millert 382: exit(1);
383: }
384:
385: log_auth(validated, 1);
386: if (sudo_mode == MODE_VALIDATE)
387: exit(0);
388: else if (sudo_mode == MODE_LIST) {
389: list_matches();
1.23 millert 390: #ifdef HAVE_LDAP
391: sudo_ldap_list_matches();
392: #endif
1.1 millert 393: exit(0);
394: }
395:
396: /* Override user's umask if configured to do so. */
1.23 millert 397: if (def_umask != 0777)
398: (void) umask(def_umask);
1.1 millert 399:
1.4 millert 400: /* Restore coredumpsize resource limit. */
401: #if defined(RLIMIT_CORE) && !defined(SUDO_DEVEL)
402: (void) setrlimit(RLIMIT_CORE, &corelimit);
1.20 millert 403: #endif /* RLIMIT_CORE && !SUDO_DEVEL */
1.4 millert 404:
1.23 millert 405: /* Become specified user or root if executing a command. */
406: if (ISSET(sudo_mode, MODE_RUN))
407: set_perms(PERM_FULL_RUNAS);
1.16 millert 408:
409: /* Close the password and group files */
410: endpwent();
411: endgrent();
1.12 millert 412:
1.23 millert 413: /* Install the real environment. */
1.12 millert 414: environ = new_environ;
1.5 millert 415:
1.23 millert 416: if (ISSET(sudo_mode, MODE_LOGIN_SHELL)) {
417: char *p;
418:
419: /* Convert /bin/sh -> -sh so shell knows it is a login shell */
420: if ((p = strrchr(NewArgv[0], '/')) == NULL)
421: p = NewArgv[0];
422: *p = '-';
423: NewArgv[0] = p;
424:
425: /* Change to target user's homedir. */
426: if (chdir(runas_pw->pw_dir) == -1)
427: warn("unable to change directory to %s", runas_pw->pw_dir);
428: }
429:
430: if (ISSET(sudo_mode, MODE_EDIT))
431: exit(sudo_edit(NewArgc, NewArgv));
432:
1.17 millert 433: /* Restore signal handlers before we exec. */
434: (void) sigaction(SIGINT, &saved_sa_int, NULL);
435: (void) sigaction(SIGQUIT, &saved_sa_quit, NULL);
436: (void) sigaction(SIGTSTP, &saved_sa_tstp, NULL);
437: (void) sigaction(SIGCHLD, &saved_sa_chld, NULL);
438:
1.1 millert 439: #ifndef PROFILING
1.23 millert 440: if (ISSET(sudo_mode, MODE_BACKGROUND) && fork() > 0)
1.1 millert 441: exit(0);
442: else
1.23 millert 443: EXECV(safe_cmnd, NewArgv); /* run the command */
1.1 millert 444: #else
445: exit(0);
446: #endif /* PROFILING */
447: /*
448: * If we got here then the exec() failed...
449: */
1.19 millert 450: warn("unable to execute %s", safe_cmnd);
1.11 millert 451: exit(127);
1.23 millert 452: } else if (ISSET(validated, FLAG_NO_USER) || (validated & FLAG_NO_HOST)) {
1.1 millert 453: log_auth(validated, 1);
454: exit(1);
1.23 millert 455: } else if (ISSET(validated, VALIDATE_NOT_OK)) {
456: if (def_path_info) {
1.1 millert 457: /*
458: * We'd like to not leak path info at all here, but that can
459: * *really* confuse the users. To really close the leak we'd
460: * have to say "not allowed to run foo" even when the problem
461: * is just "no foo in path" since the user can trivially set
462: * their path to just contain a single dir.
463: */
464: log_auth(validated,
465: !(cmnd_status == NOT_FOUND_DOT || cmnd_status == NOT_FOUND));
466: if (cmnd_status == NOT_FOUND)
1.19 millert 467: warnx("%s: command not found", user_cmnd);
1.1 millert 468: else if (cmnd_status == NOT_FOUND_DOT)
1.19 millert 469: warnx("ignoring `%s' found in '.'\nUse `sudo ./%s' if this is the `%s' you wish to run.", user_cmnd, user_cmnd, user_cmnd);
1.1 millert 470: } else {
471: /* Just tell the user they are not allowed to run foo. */
472: log_auth(validated, 1);
473: }
474: exit(1);
475: } else {
476: /* should never get here */
477: log_auth(validated, 1);
478: exit(1);
479: }
480: exit(0); /* not reached */
481: }
482:
483: /*
484: * Initialize timezone, set umask, fill in ``sudo_user'' struct and
485: * load the ``interfaces'' array.
486: */
487: static int
488: init_vars(sudo_mode)
489: int sudo_mode;
490: {
491: char *p, thost[MAXHOSTNAMELEN];
1.11 millert 492: int nohostname, rval;
1.1 millert 493:
494: /* Sanity check command from user. */
1.23 millert 495: if (user_cmnd == NULL && strlen(NewArgv[0]) >= PATH_MAX)
1.19 millert 496: errx(1, "%s: File name too long", NewArgv[0]);
1.1 millert 497:
498: #ifdef HAVE_TZSET
499: (void) tzset(); /* set the timezone if applicable */
500: #endif /* HAVE_TZSET */
501:
502: /* Default value for cmnd and cwd, overridden later. */
503: if (user_cmnd == NULL)
504: user_cmnd = NewArgv[0];
1.17 millert 505: (void) strlcpy(user_cwd, "unknown", sizeof(user_cwd));
1.1 millert 506:
507: /*
508: * We avoid gethostbyname() if possible since we don't want
509: * sudo to block if DNS or NIS is hosed.
510: * "host" is the (possibly fully-qualified) hostname and
511: * "shost" is the unqualified form of the hostname.
512: */
1.11 millert 513: nohostname = gethostname(thost, sizeof(thost));
514: if (nohostname)
515: user_host = user_shost = "localhost";
516: else {
1.1 millert 517: user_host = estrdup(thost);
1.23 millert 518: if (def_fqdn) {
1.11 millert 519: /* Defer call to set_fqdn() until log_error() is safe. */
520: user_shost = user_host;
1.1 millert 521: } else {
1.11 millert 522: if ((p = strchr(user_host, '.'))) {
523: *p = '\0';
524: user_shost = estrdup(user_host);
525: *p = '.';
526: } else {
527: user_shost = user_host;
528: }
1.1 millert 529: }
530: }
531:
532: if ((p = ttyname(STDIN_FILENO)) || (p = ttyname(STDOUT_FILENO))) {
533: if (strncmp(p, _PATH_DEV, sizeof(_PATH_DEV) - 1) == 0)
534: p += sizeof(_PATH_DEV) - 1;
535: user_tty = estrdup(p);
536: } else
537: user_tty = "unknown";
538:
539: /*
540: * Get a local copy of the user's struct passwd with the shadow password
541: * if necessary. It is assumed that euid is 0 at this point so we
542: * can read the shadow passwd file if necessary.
543: */
544: if ((sudo_user.pw = sudo_getpwuid(getuid())) == NULL) {
545: /* Need to make a fake struct passwd for logging to work. */
546: struct passwd pw;
547: char pw_name[MAX_UID_T_LEN + 1];
548:
549: pw.pw_uid = getuid();
1.17 millert 550: (void) snprintf(pw_name, sizeof(pw_name), "%lu",
551: (unsigned long) pw.pw_uid);
1.1 millert 552: pw.pw_name = pw_name;
553: sudo_user.pw = &pw;
554:
1.21 millert 555: /*
556: * If we are in -k/-K mode, just spew to stderr. It is not unusual for
557: * users to place "sudo -k" in a .logout file which can cause sudo to
558: * be run during reboot after the YP/NIS/NIS+/LDAP/etc daemon has died.
559: */
560: if (sudo_mode & (MODE_INVALIDATE|MODE_KILL))
561: errx(1, "uid %s does not exist in the passwd file!", pw_name);
562: log_error(0, "uid %s does not exist in the passwd file!", pw_name);
1.1 millert 563: }
1.15 millert 564: if (user_shell == NULL || *user_shell == '\0')
565: user_shell = sudo_user.pw->pw_shell;
1.1 millert 566:
567: /* It is now safe to use log_error() and set_perms() */
568:
1.23 millert 569: if (def_fqdn)
570: set_fqdn(); /* may call log_error() */
1.11 millert 571:
572: if (nohostname)
573: log_error(USE_ERRNO|MSG_ONLY, "can't get hostname");
574:
1.23 millert 575: set_runaspw(*user_runas); /* may call log_error() */
576: if (*user_runas[0] == '#' && runas_pw->pw_name && runas_pw->pw_name[0])
577: *user_runas = estrdup(runas_pw->pw_name);
578:
1.11 millert 579: /*
1.1 millert 580: * Get current working directory. Try as user, fall back to root.
581: */
1.17 millert 582: set_perms(PERM_USER);
1.1 millert 583: if (!getcwd(user_cwd, sizeof(user_cwd))) {
1.17 millert 584: set_perms(PERM_ROOT);
1.1 millert 585: if (!getcwd(user_cwd, sizeof(user_cwd))) {
1.19 millert 586: warnx("cannot get working directory");
1.17 millert 587: (void) strlcpy(user_cwd, "unknown", sizeof(user_cwd));
1.1 millert 588: }
589: } else
1.17 millert 590: set_perms(PERM_ROOT);
1.1 millert 591:
592: /*
1.23 millert 593: * If we were given the '-e', '-i' or '-s' options we need to redo
1.1 millert 594: * NewArgv and NewArgc.
595: */
1.23 millert 596: if ((sudo_mode & (MODE_SHELL | MODE_EDIT))) {
1.1 millert 597: char **dst, **src = NewArgv;
598:
1.17 millert 599: NewArgv = (char **) emalloc2((++NewArgc + 1), sizeof(char *));
1.23 millert 600: if (ISSET(sudo_mode, MODE_EDIT))
601: NewArgv[0] = "sudoedit";
602: else if (ISSET(sudo_mode, MODE_LOGIN_SHELL))
603: NewArgv[0] = runas_pw->pw_shell;
604: else if (user_shell && *user_shell)
1.1 millert 605: NewArgv[0] = user_shell;
1.23 millert 606: else
1.19 millert 607: errx(1, "unable to determine shell");
1.1 millert 608:
1.19 millert 609: /* copy the args from NewArgv */
1.1 millert 610: for (dst = NewArgv + 1; (*dst = *src) != NULL; ++src, ++dst)
611: ;
612: }
613:
1.8 millert 614: /* Set login class if applicable. */
615: set_loginclass(sudo_user.pw);
616:
1.1 millert 617: /* Resolve the path and return. */
1.23 millert 618: rval = FOUND;
619: user_stat = emalloc(sizeof(struct stat));
620: if (sudo_mode & (MODE_RUN | MODE_EDIT)) {
621: if (ISSET(sudo_mode, MODE_RUN)) {
622: /* XXX - default_runas may be modified during parsing of sudoers */
623: set_perms(PERM_RUNAS);
624: rval = find_path(NewArgv[0], &user_cmnd, user_stat, user_path);
1.17 millert 625: set_perms(PERM_ROOT);
1.23 millert 626: if (rval != FOUND) {
627: /* Failed as root, try as invoking user. */
628: set_perms(PERM_USER);
629: rval = find_path(NewArgv[0], &user_cmnd, user_stat, user_path);
630: set_perms(PERM_ROOT);
631: }
1.11 millert 632: }
633:
634: /* set user_args */
635: if (NewArgc > 1) {
636: char *to, **from;
1.17 millert 637: size_t size, n;
1.11 millert 638:
1.23 millert 639: /* If we didn't realloc NewArgv it is contiguous so just count. */
640: if (!(sudo_mode & (MODE_SHELL | MODE_EDIT))) {
1.11 millert 641: size = (size_t) (NewArgv[NewArgc-1] - NewArgv[1]) +
642: strlen(NewArgv[NewArgc-1]) + 1;
643: } else {
644: for (size = 0, from = NewArgv + 1; *from; from++)
645: size += strlen(*from) + 1;
646: }
647:
1.23 millert 648: /* Alloc and build up user_args. */
1.17 millert 649: user_args = (char *) emalloc(size);
650: for (to = user_args, from = NewArgv + 1; *from; from++) {
651: n = strlcpy(to, *from, size - (to - user_args));
1.19 millert 652: if (n >= size - (to - user_args))
653: errx(1, "internal error, init_vars() overflow");
1.17 millert 654: to += n;
1.11 millert 655: *to++ = ' ';
656: }
657: *--to = '\0';
658: }
1.23 millert 659: }
660: if ((user_base = strrchr(user_cmnd, '/')) != NULL)
661: user_base++;
662: else
663: user_base = user_cmnd;
1.11 millert 664:
665: return(rval);
1.1 millert 666: }
667:
668: /*
669: * Command line argument parsing, can't use getopt(3).
670: */
671: static int
1.19 millert 672: parse_args(argc, argv)
673: int argc;
674: char **argv;
1.1 millert 675: {
1.17 millert 676: int rval = MODE_RUN; /* what mode is sudo to be run in? */
1.1 millert 677: int excl = 0; /* exclusive arg, no others allowed */
678:
1.19 millert 679: NewArgv = argv + 1;
680: NewArgc = argc - 1;
1.1 millert 681:
1.23 millert 682: /* First, check to see if we were invoked as "sudoedit". */
683: if (strcmp(getprogname(), "sudoedit") == 0) {
684: rval = MODE_EDIT;
685: excl = 'e';
686: } else
687: rval = MODE_RUN;
688:
689: if (NewArgc == 0 && rval == MODE_RUN) { /* no options and no command */
690: SET(rval, (MODE_IMPLIED_SHELL | MODE_SHELL));
1.1 millert 691: return(rval);
692: }
693:
694: while (NewArgc > 0 && NewArgv[0][0] == '-') {
1.19 millert 695: if (NewArgv[0][1] != '\0' && NewArgv[0][2] != '\0')
696: warnx("please use single character options");
1.1 millert 697:
698: switch (NewArgv[0][1]) {
699: case 'p':
700: /* Must have an associated prompt. */
701: if (NewArgv[1] == NULL)
702: usage(1);
703:
704: user_prompt = NewArgv[1];
705:
706: NewArgc--;
707: NewArgv++;
708: break;
709: case 'u':
710: /* Must have an associated runas user. */
711: if (NewArgv[1] == NULL)
712: usage(1);
713:
714: user_runas = &NewArgv[1];
715:
716: NewArgc--;
717: NewArgv++;
718: break;
1.8 millert 719: #ifdef HAVE_BSD_AUTH_H
720: case 'a':
721: /* Must have an associated authentication style. */
722: if (NewArgv[1] == NULL)
723: usage(1);
724:
725: login_style = NewArgv[1];
726:
727: NewArgc--;
728: NewArgv++;
729: break;
730: #endif
731: #ifdef HAVE_LOGIN_CAP_H
1.4 millert 732: case 'c':
733: /* Must have an associated login class. */
734: if (NewArgv[1] == NULL)
735: usage(1);
736:
737: login_class = NewArgv[1];
1.23 millert 738: def_use_loginclass = TRUE;
1.4 millert 739:
740: NewArgc--;
741: NewArgv++;
742: break;
743: #endif
1.1 millert 744: case 'b':
1.23 millert 745: SET(rval, MODE_BACKGROUND);
746: break;
747: case 'e':
748: rval = MODE_EDIT;
749: if (excl && excl != 'e')
750: usage_excl(1);
751: excl = 'e';
1.1 millert 752: break;
753: case 'v':
754: rval = MODE_VALIDATE;
755: if (excl && excl != 'v')
756: usage_excl(1);
757: excl = 'v';
758: break;
1.23 millert 759: case 'i':
760: SET(rval, (MODE_LOGIN_SHELL | MODE_SHELL));
761: def_env_reset = TRUE;
762: if (excl && excl != 'i')
763: usage_excl(1);
764: excl = 'i';
765: break;
1.1 millert 766: case 'k':
767: rval = MODE_INVALIDATE;
768: if (excl && excl != 'k')
769: usage_excl(1);
770: excl = 'k';
771: break;
772: case 'K':
773: rval = MODE_KILL;
774: if (excl && excl != 'K')
775: usage_excl(1);
776: excl = 'K';
777: break;
778: case 'L':
779: rval = MODE_LISTDEFS;
780: if (excl && excl != 'L')
781: usage_excl(1);
782: excl = 'L';
783: break;
784: case 'l':
785: rval = MODE_LIST;
786: if (excl && excl != 'l')
787: usage_excl(1);
788: excl = 'l';
789: break;
790: case 'V':
791: rval = MODE_VERSION;
792: if (excl && excl != 'V')
793: usage_excl(1);
794: excl = 'V';
795: break;
796: case 'h':
797: rval = MODE_HELP;
798: if (excl && excl != 'h')
799: usage_excl(1);
800: excl = 'h';
801: break;
802: case 's':
1.23 millert 803: SET(rval, MODE_SHELL);
1.2 millert 804: if (excl && excl != 's')
805: usage_excl(1);
806: excl = 's';
1.1 millert 807: break;
808: case 'H':
1.23 millert 809: SET(rval, MODE_RESET_HOME);
1.1 millert 810: break;
1.11 millert 811: case 'P':
1.23 millert 812: SET(rval, MODE_PRESERVE_GROUPS);
1.11 millert 813: break;
1.4 millert 814: case 'S':
1.23 millert 815: SET(tgetpass_flags, TGP_STDIN);
1.4 millert 816: break;
1.1 millert 817: case '-':
818: NewArgc--;
819: NewArgv++;
1.2 millert 820: if (rval == MODE_RUN)
1.23 millert 821: SET(rval, (MODE_IMPLIED_SHELL | MODE_SHELL));
1.1 millert 822: return(rval);
823: case '\0':
1.19 millert 824: warnx("'-' requires an argument");
1.1 millert 825: usage(1);
826: default:
1.19 millert 827: warnx("illegal option `%s'", NewArgv[0]);
1.1 millert 828: usage(1);
829: }
830: NewArgc--;
831: NewArgv++;
832: }
833:
1.24 millert 834: if (user_runas != NULL && !ISSET(rval, (MODE_EDIT|MODE_RUN))) {
835: if (excl != '\0')
836: warnx("the `-u' and '-%c' options may not be used together", excl);
837: usage(1);
838: }
839:
1.23 millert 840: if ((NewArgc == 0 && (rval & MODE_EDIT)) ||
841: (NewArgc > 0 && !(rval & (MODE_RUN | MODE_EDIT))))
1.1 millert 842: usage(1);
843:
844: return(rval);
845: }
846:
847: /*
848: * Sanity check sudoers mode/owner/type.
849: * Leaves a file pointer to the sudoers file open in ``fp''.
850: */
851: static void
852: check_sudoers()
853: {
854: struct stat statbuf;
855: int rootstat, i;
856: char c;
857:
858: /*
859: * Fix the mode and group on sudoers file from old default.
1.23 millert 860: * Only works if file system is readable/writable by root.
1.1 millert 861: */
1.17 millert 862: if ((rootstat = stat_sudoers(_PATH_SUDOERS, &statbuf)) == 0 &&
1.1 millert 863: SUDOERS_UID == statbuf.st_uid && SUDOERS_MODE != 0400 &&
864: (statbuf.st_mode & 0007777) == 0400) {
865:
866: if (chmod(_PATH_SUDOERS, SUDOERS_MODE) == 0) {
1.19 millert 867: warnx("fixed mode on %s", _PATH_SUDOERS);
1.23 millert 868: SET(statbuf.st_mode, SUDOERS_MODE);
1.1 millert 869: if (statbuf.st_gid != SUDOERS_GID) {
870: if (!chown(_PATH_SUDOERS,(uid_t) -1,SUDOERS_GID)) {
1.19 millert 871: warnx("set group on %s", _PATH_SUDOERS);
1.1 millert 872: statbuf.st_gid = SUDOERS_GID;
1.19 millert 873: } else
874: warn("unable to set group on %s", _PATH_SUDOERS);
1.1 millert 875: }
1.19 millert 876: } else
877: warn("unable to fix mode on %s", _PATH_SUDOERS);
1.1 millert 878: }
879:
880: /*
881: * Sanity checks on sudoers file. Must be done as sudoers
882: * file owner. We already did a stat as root, so use that
883: * data if we can't stat as sudoers file owner.
884: */
1.17 millert 885: set_perms(PERM_SUDOERS);
1.1 millert 886:
1.17 millert 887: if (rootstat != 0 && stat_sudoers(_PATH_SUDOERS, &statbuf) != 0)
1.1 millert 888: log_error(USE_ERRNO, "can't stat %s", _PATH_SUDOERS);
889: else if (!S_ISREG(statbuf.st_mode))
890: log_error(0, "%s is not a regular file", _PATH_SUDOERS);
1.9 millert 891: else if (statbuf.st_size == 0)
892: log_error(0, "%s is zero length", _PATH_SUDOERS);
1.1 millert 893: else if ((statbuf.st_mode & 07777) != SUDOERS_MODE)
894: log_error(0, "%s is mode 0%o, should be 0%o", _PATH_SUDOERS,
895: (statbuf.st_mode & 07777), SUDOERS_MODE);
896: else if (statbuf.st_uid != SUDOERS_UID)
1.17 millert 897: log_error(0, "%s is owned by uid %lu, should be %lu", _PATH_SUDOERS,
1.25 millert 898: (unsigned long) statbuf.st_uid, (unsigned long) SUDOERS_UID);
1.1 millert 899: else if (statbuf.st_gid != SUDOERS_GID)
1.17 millert 900: log_error(0, "%s is owned by gid %lu, should be %lu", _PATH_SUDOERS,
1.25 millert 901: (unsigned long) statbuf.st_gid, (unsigned long) SUDOERS_GID);
1.1 millert 902: else {
903: /* Solaris sometimes returns EAGAIN so try 10 times */
904: for (i = 0; i < 10 ; i++) {
905: errno = 0;
906: if ((sudoers_fp = fopen(_PATH_SUDOERS, "r")) == NULL ||
907: fread(&c, sizeof(c), 1, sudoers_fp) != 1) {
908: sudoers_fp = NULL;
909: if (errno != EAGAIN && errno != EWOULDBLOCK)
910: break;
911: } else
912: break;
913: sleep(1);
914: }
915: if (sudoers_fp == NULL)
916: log_error(USE_ERRNO, "can't open %s", _PATH_SUDOERS);
917: }
918:
1.17 millert 919: set_perms(PERM_ROOT); /* change back to root */
1.1 millert 920: }
921:
922: /*
923: * Close all open files (except std*) and turn off core dumps.
1.11 millert 924: * Also sets the set_perms() pointer to the correct function.
1.1 millert 925: */
926: static void
927: initial_setup()
928: {
1.22 millert 929: #if defined(RLIMIT_CORE) && !defined(SUDO_DEVEL)
1.1 millert 930: struct rlimit rl;
931:
932: /*
933: * Turn off core dumps.
934: */
1.4 millert 935: (void) getrlimit(RLIMIT_CORE, &corelimit);
1.20 millert 936: memcpy(&rl, &corelimit, sizeof(struct rlimit));
937: rl.rlim_cur = 0;
1.1 millert 938: (void) setrlimit(RLIMIT_CORE, &rl);
1.20 millert 939: #endif /* RLIMIT_CORE && !SUDO_DEVEL */
1.1 millert 940:
1.22 millert 941: closefrom(STDERR_FILENO + 1);
1.1 millert 942:
1.17 millert 943: /*
944: * Make set_perms point to the correct function.
945: * If we are using setresuid() or setreuid() we only need to set this
946: * once. If we are using POSIX saved uids we will switch to
947: * set_perms_nosuid after sudoers has been parsed if the "stay_suid"
948: * option is not set.
949: */
950: #if defined(HAVE_SETRESUID) || defined(HAVE_SETREUID)
951: set_perms = set_perms_suid;
952: #else
953: # if !defined(NO_SAVED_IDS) && defined(_SC_SAVED_IDS) && defined(_SC_VERSION)
1.11 millert 954: if (sysconf(_SC_SAVED_IDS) == 1 && sysconf(_SC_VERSION) >= 199009)
955: set_perms = set_perms_posix;
956: else
1.17 millert 957: # endif
958: set_perms = set_perms_nosuid;
959: #endif /* HAVE_SETRESUID || HAVE_SETREUID */
1.1 millert 960: }
961:
1.8 millert 962: #ifdef HAVE_LOGIN_CAP_H
963: static void
1.4 millert 964: set_loginclass(pw)
965: struct passwd *pw;
966: {
967: int errflags;
968:
969: /*
970: * Don't make it a fatal error if the user didn't specify the login
971: * class themselves. We do this because if login.conf gets
972: * corrupted we want the admin to be able to use sudo to fix it.
973: */
974: if (login_class)
975: errflags = NO_MAIL|MSG_ONLY;
976: else
977: errflags = NO_MAIL|MSG_ONLY|NO_EXIT;
978:
979: if (login_class && strcmp(login_class, "-") != 0) {
1.19 millert 980: if (strcmp(*user_runas, "root") != 0 && user_uid != 0)
981: errx(1, "only root can use -c %s", login_class);
1.4 millert 982: } else {
983: login_class = pw->pw_class;
984: if (!login_class || !*login_class)
985: login_class =
986: (pw->pw_uid == 0) ? LOGIN_DEFROOTCLASS : LOGIN_DEFCLASS;
987: }
988:
989: lc = login_getclass(login_class);
1.10 millert 990: if (!lc || !lc->lc_class || strcmp(lc->lc_class, login_class) != 0) {
1.4 millert 991: log_error(errflags, "unknown login class: %s", login_class);
1.11 millert 992: if (!lc)
993: lc = login_getclass(NULL); /* needed for login_getstyle() later */
1.10 millert 994: }
1.4 millert 995: }
996: #else
1.8 millert 997: static void
1.4 millert 998: set_loginclass(pw)
999: struct passwd *pw;
1000: {
1001: }
1.8 millert 1002: #endif /* HAVE_LOGIN_CAP_H */
1.4 millert 1003:
1.1 millert 1004: /*
1.2 millert 1005: * Look up the fully qualified domain name and set user_host and user_shost.
1006: */
1007: void
1008: set_fqdn()
1009: {
1010: struct hostent *hp;
1011: char *p;
1012:
1.14 millert 1013: if (!(hp = gethostbyname(user_host))) {
1014: log_error(MSG_ONLY|NO_EXIT,
1015: "unable to lookup %s via gethostbyname()", user_host);
1016: } else {
1017: if (user_shost != user_host)
1018: free(user_shost);
1019: free(user_host);
1020: user_host = estrdup(hp->h_name);
1.2 millert 1021: }
1022: if ((p = strchr(user_host, '.'))) {
1023: *p = '\0';
1024: user_shost = estrdup(user_host);
1025: *p = '.';
1026: } else {
1027: user_shost = user_host;
1028: }
1029: }
1030:
1031: /*
1.23 millert 1032: * Get passwd entry for the user we are going to run commands as.
1033: * By default, this is "root". Updates runas_pw as a side effect.
1034: */
1035: int
1036: set_runaspw(user)
1037: char *user;
1038: {
1039: if (runas_pw != NULL) {
1040: if (user_runas != &def_runas_default)
1041: return(TRUE); /* don't override -u option */
1042: free(runas_pw);
1043: }
1044: if (*user == '#') {
1045: runas_pw = sudo_getpwuid(atoi(user + 1));
1046: if (runas_pw == NULL) {
1047: runas_pw = emalloc(sizeof(struct passwd));
1048: (void) memset((VOID *)runas_pw, 0, sizeof(struct passwd));
1049: runas_pw->pw_uid = atoi(user + 1);
1050: }
1051: } else {
1052: runas_pw = sudo_getpwnam(user);
1053: if (runas_pw == NULL)
1054: log_error(NO_MAIL|MSG_ONLY, "no passwd entry for %s!", user);
1055: }
1056: return(TRUE);
1057: }
1058:
1059: /*
1.6 millert 1060: * Get passwd entry for the user we are going to authenticate as.
1.23 millert 1061: * By default, this is the user invoking sudo. In the most common
1062: * case, this matches sudo_user.pw or runas_pw.
1.4 millert 1063: */
1.6 millert 1064: static struct passwd *
1065: get_authpw()
1.4 millert 1066: {
1067: struct passwd *pw;
1068:
1.23 millert 1069: if (def_rootpw) {
1070: if (runas_pw->pw_uid == 0)
1071: pw = runas_pw;
1072: else if ((pw = sudo_getpwuid(0)) == NULL)
1.4 millert 1073: log_error(0, "uid 0 does not exist in the passwd file!");
1.23 millert 1074: } else if (def_runaspw) {
1075: if (strcmp(def_runas_default, *user_runas) == 0)
1076: pw = runas_pw;
1077: else if ((pw = sudo_getpwnam(def_runas_default)) == NULL)
1.4 millert 1078: log_error(0, "user %s does not exist in the passwd file!",
1.23 millert 1079: def_runas_default);
1080: } else if (def_targetpw) {
1081: if (runas_pw->pw_name == NULL)
1082: log_error(NO_MAIL|MSG_ONLY, "no passwd entry for %lu!",
1.25 millert 1083: (unsigned long) runas_pw->pw_uid);
1.23 millert 1084: pw = runas_pw;
1.6 millert 1085: } else
1086: pw = sudo_user.pw;
1087:
1088: return(pw);
1.4 millert 1089: }
1090:
1091: /*
1.1 millert 1092: * Tell which options are mutually exclusive and exit.
1093: */
1094: static void
1095: usage_excl(exit_val)
1096: int exit_val;
1097: {
1.23 millert 1098: warnx("Only one of the -e, -h, -k, -K, -l, -s, -v or -V options may be used");
1.1 millert 1099: usage(exit_val);
1100: }
1101:
1102: /*
1103: * Give usage message and exit.
1104: */
1105: static void
1106: usage(exit_val)
1107: int exit_val;
1108: {
1.23 millert 1109: char **p;
1110: int linelen, linemax, ulen;
1111: static char *uvec[] = {
1112: " [-HPSb]",
1113: #ifdef HAVE_BSD_AUTH_H
1114: " [-a auth_type]",
1115: #endif
1.8 millert 1116: #ifdef HAVE_LOGIN_CAP_H
1.23 millert 1117: " [-c class|-]",
1.8 millert 1118: #endif
1.23 millert 1119: " [-p prompt]",
1120: " [-u username|#uid]",
1121: " { -e file [...] | -i | -s | <command> }",
1122: NULL
1123: };
1124:
1125: /*
1126: * For sudoedit, replace the last entry in the usage vector.
1127: * For sudo, print the secondary usage.
1128: */
1129: if (strcmp(getprogname(), "sudoedit") == 0) {
1130: /* Replace the last entry in the usage vector. */
1131: for (p = uvec; p[1] != NULL; p++)
1132: continue;
1133: *p = " file [...]";
1134: } else {
1135: fprintf(stderr, "usage: %s -K | -L | -V | -h | -k | -l | -v\n",
1136: getprogname());
1137: }
1138:
1139: /*
1140: * Print the main usage and wrap lines as needed.
1141: * Assumes an 80-character wide terminal, which is kind of bogus...
1142: */
1143: ulen = (int)strlen(getprogname()) + 7;
1144: linemax = 80;
1145: linelen = linemax - ulen;
1146: printf("usage: %s", getprogname());
1147: for (p = uvec; *p != NULL; p++) {
1148: if (linelen == linemax || (linelen -= strlen(*p)) >= 0) {
1149: fputs(*p, stdout);
1150: } else {
1151: p--;
1152: linelen = linemax;
1153: printf("\n%*s", ulen, "");
1154: }
1155: }
1156: putchar('\n');
1.1 millert 1157: exit(exit_val);
1158: }