Annotation of src/usr.bin/sudo/sudo.c, Revision 1.25
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.13 millert 278:
279: /*
1.17 millert 280: * If we are using set_perms_posix() and the stay_setuid flag was not set,
281: * set the real, effective and saved uids to 0 and use set_perms_nosuid()
1.13 millert 282: * instead of set_perms_posix().
283: */
1.17 millert 284: #if !defined(HAVE_SETRESUID) && !defined(HAVE_SETREUID) && \
285: !defined(NO_SAVED_IDS) && defined(_SC_SAVED_IDS) && defined(_SC_VERSION)
1.23 millert 286: if (!def_stay_setuid && set_perms == set_perms_posix) {
1.13 millert 287: if (setuid(0)) {
288: perror("setuid(0)");
289: exit(1);
290: }
1.17 millert 291: set_perms = set_perms_nosuid;
1.13 millert 292: }
293: #endif
1.1 millert 294:
1.11 millert 295: /*
1.17 millert 296: * Look up the timestamp dir owner if one is specified.
297: */
1.23 millert 298: if (def_timestampowner) {
1.17 millert 299: struct passwd *pw;
300:
1.23 millert 301: if (*def_timestampowner == '#')
302: pw = getpwuid(atoi(def_timestampowner + 1));
1.17 millert 303: else
1.23 millert 304: pw = getpwnam(def_timestampowner);
1.17 millert 305: if (!pw)
306: log_error(0, "timestamp owner (%s): No such user",
1.23 millert 307: def_timestampowner);
1.17 millert 308: timestamp_uid = pw->pw_uid;
309: }
310:
1.2 millert 311: /* This goes after the sudoers parse since we honor sudoers options. */
1.1 millert 312: if (sudo_mode == MODE_KILL || sudo_mode == MODE_INVALIDATE) {
313: remove_timestamp((sudo_mode == MODE_KILL));
314: exit(0);
315: }
316:
1.23 millert 317: if (ISSET(validated, VALIDATE_ERROR))
1.1 millert 318: log_error(0, "parse error in %s near line %d", _PATH_SUDOERS,
319: errorlineno);
320:
321: /* Is root even allowed to run sudo? */
1.23 millert 322: if (user_uid == 0 && !def_root_sudo) {
1.17 millert 323: (void) fprintf(stderr,
324: "Sorry, %s has been configured to not allow root to run it.\n",
1.19 millert 325: getprogname());
1.1 millert 326: exit(1);
327: }
328:
1.11 millert 329: /* If given the -P option, set the "preserve_groups" flag. */
1.23 millert 330: if (ISSET(sudo_mode, MODE_PRESERVE_GROUPS))
331: def_preserve_groups = TRUE;
1.11 millert 332:
1.3 millert 333: /* If no command line args and "set_home" is not set, error out. */
1.23 millert 334: if (ISSET(sudo_mode, MODE_IMPLIED_SHELL) && !def_shell_noargs)
1.3 millert 335: usage(1);
336:
1.15 millert 337: /* May need to set $HOME to target user if we are running a command. */
1.23 millert 338: if (ISSET(sudo_mode, MODE_RUN) && (def_always_set_home ||
339: (ISSET(sudo_mode, MODE_SHELL) && def_set_home)))
340: SET(sudo_mode, MODE_RESET_HOME);
1.2 millert 341:
1.1 millert 342: /* Bail if a tty is required and we don't have one. */
1.23 millert 343: if (def_requiretty) {
1.1 millert 344: if ((fd = open(_PATH_TTY, O_RDWR|O_NOCTTY)) == -1)
345: log_error(NO_MAIL, "sorry, you must have a tty to run sudo");
346: else
347: (void) close(fd);
348: }
349:
1.6 millert 350: /* Fill in passwd struct based on user we are authenticating as. */
351: auth_pw = get_authpw();
1.4 millert 352:
1.23 millert 353: /* Require a password if sudoers says so. */
354: if (!ISSET(validated, FLAG_NOPASS))
355: check_user(ISSET(validated, FLAG_CHECK_USER));
356:
357: /* If run as root with SUDO_USER set, set sudo_user.pw to that user. */
358: if (user_uid == 0 && prev_user != NULL && strcmp(prev_user, "root") != 0) {
359: struct passwd *pw;
360:
361: if ((pw = sudo_getpwnam(prev_user)) != NULL) {
362: free(sudo_user.pw);
363: sudo_user.pw = pw;
364: }
365: }
1.1 millert 366:
1.23 millert 367: /* Build a new environment that avoids any nasty bits if we have a cmnd. */
368: if (ISSET(sudo_mode, MODE_RUN))
369: new_environ = rebuild_env(envp, sudo_mode, ISSET(validated, FLAG_NOEXEC));
370: else
371: new_environ = envp;
1.14 millert 372:
1.23 millert 373: if (ISSET(validated, VALIDATE_OK)) {
1.1 millert 374: /* Finally tell the user if the command did not exist. */
375: if (cmnd_status == NOT_FOUND_DOT) {
1.19 millert 376: 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 377: exit(1);
378: } else if (cmnd_status == NOT_FOUND) {
1.19 millert 379: warnx("%s: command not found", user_cmnd);
1.1 millert 380: exit(1);
381: }
382:
383: log_auth(validated, 1);
384: if (sudo_mode == MODE_VALIDATE)
385: exit(0);
386: else if (sudo_mode == MODE_LIST) {
387: list_matches();
1.23 millert 388: #ifdef HAVE_LDAP
389: sudo_ldap_list_matches();
390: #endif
1.1 millert 391: exit(0);
392: }
393:
394: /* This *must* have been set if we got a match but... */
395: if (safe_cmnd == NULL) {
396: log_error(MSG_ONLY,
1.11 millert 397: "internal error, safe_cmnd never got set for %s; %s",
1.1 millert 398: user_cmnd,
399: "please report this error at http://courtesan.com/sudo/bugs/");
400: }
401:
402: /* Override user's umask if configured to do so. */
1.23 millert 403: if (def_umask != 0777)
404: (void) umask(def_umask);
1.1 millert 405:
1.4 millert 406: /* Restore coredumpsize resource limit. */
407: #if defined(RLIMIT_CORE) && !defined(SUDO_DEVEL)
408: (void) setrlimit(RLIMIT_CORE, &corelimit);
1.20 millert 409: #endif /* RLIMIT_CORE && !SUDO_DEVEL */
1.4 millert 410:
1.23 millert 411: /* Become specified user or root if executing a command. */
412: if (ISSET(sudo_mode, MODE_RUN))
413: set_perms(PERM_FULL_RUNAS);
1.16 millert 414:
415: /* Close the password and group files */
416: endpwent();
417: endgrent();
1.12 millert 418:
1.23 millert 419: /* Install the real environment. */
1.12 millert 420: environ = new_environ;
1.5 millert 421:
1.23 millert 422: if (ISSET(sudo_mode, MODE_LOGIN_SHELL)) {
423: char *p;
424:
425: /* Convert /bin/sh -> -sh so shell knows it is a login shell */
426: if ((p = strrchr(NewArgv[0], '/')) == NULL)
427: p = NewArgv[0];
428: *p = '-';
429: NewArgv[0] = p;
430:
431: /* Change to target user's homedir. */
432: if (chdir(runas_pw->pw_dir) == -1)
433: warn("unable to change directory to %s", runas_pw->pw_dir);
434: }
435:
436: if (ISSET(sudo_mode, MODE_EDIT))
437: exit(sudo_edit(NewArgc, NewArgv));
438:
1.17 millert 439: /* Restore signal handlers before we exec. */
440: (void) sigaction(SIGINT, &saved_sa_int, NULL);
441: (void) sigaction(SIGQUIT, &saved_sa_quit, NULL);
442: (void) sigaction(SIGTSTP, &saved_sa_tstp, NULL);
443: (void) sigaction(SIGCHLD, &saved_sa_chld, NULL);
444:
1.1 millert 445: #ifndef PROFILING
1.23 millert 446: if (ISSET(sudo_mode, MODE_BACKGROUND) && fork() > 0)
1.1 millert 447: exit(0);
448: else
1.23 millert 449: EXECV(safe_cmnd, NewArgv); /* run the command */
1.1 millert 450: #else
451: exit(0);
452: #endif /* PROFILING */
453: /*
454: * If we got here then the exec() failed...
455: */
1.19 millert 456: warn("unable to execute %s", safe_cmnd);
1.11 millert 457: exit(127);
1.23 millert 458: } else if (ISSET(validated, FLAG_NO_USER) || (validated & FLAG_NO_HOST)) {
1.1 millert 459: log_auth(validated, 1);
460: exit(1);
1.23 millert 461: } else if (ISSET(validated, VALIDATE_NOT_OK)) {
462: if (def_path_info) {
1.1 millert 463: /*
464: * We'd like to not leak path info at all here, but that can
465: * *really* confuse the users. To really close the leak we'd
466: * have to say "not allowed to run foo" even when the problem
467: * is just "no foo in path" since the user can trivially set
468: * their path to just contain a single dir.
469: */
470: log_auth(validated,
471: !(cmnd_status == NOT_FOUND_DOT || cmnd_status == NOT_FOUND));
472: if (cmnd_status == NOT_FOUND)
1.19 millert 473: warnx("%s: command not found", user_cmnd);
1.1 millert 474: else if (cmnd_status == NOT_FOUND_DOT)
1.19 millert 475: 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 476: } else {
477: /* Just tell the user they are not allowed to run foo. */
478: log_auth(validated, 1);
479: }
480: exit(1);
481: } else {
482: /* should never get here */
483: log_auth(validated, 1);
484: exit(1);
485: }
486: exit(0); /* not reached */
487: }
488:
489: /*
490: * Initialize timezone, set umask, fill in ``sudo_user'' struct and
491: * load the ``interfaces'' array.
492: */
493: static int
494: init_vars(sudo_mode)
495: int sudo_mode;
496: {
497: char *p, thost[MAXHOSTNAMELEN];
1.11 millert 498: int nohostname, rval;
1.1 millert 499:
500: /* Sanity check command from user. */
1.23 millert 501: if (user_cmnd == NULL && strlen(NewArgv[0]) >= PATH_MAX)
1.19 millert 502: errx(1, "%s: File name too long", NewArgv[0]);
1.1 millert 503:
504: #ifdef HAVE_TZSET
505: (void) tzset(); /* set the timezone if applicable */
506: #endif /* HAVE_TZSET */
507:
508: /* Default value for cmnd and cwd, overridden later. */
509: if (user_cmnd == NULL)
510: user_cmnd = NewArgv[0];
1.17 millert 511: (void) strlcpy(user_cwd, "unknown", sizeof(user_cwd));
1.1 millert 512:
513: /*
514: * We avoid gethostbyname() if possible since we don't want
515: * sudo to block if DNS or NIS is hosed.
516: * "host" is the (possibly fully-qualified) hostname and
517: * "shost" is the unqualified form of the hostname.
518: */
1.11 millert 519: nohostname = gethostname(thost, sizeof(thost));
520: if (nohostname)
521: user_host = user_shost = "localhost";
522: else {
1.1 millert 523: user_host = estrdup(thost);
1.23 millert 524: if (def_fqdn) {
1.11 millert 525: /* Defer call to set_fqdn() until log_error() is safe. */
526: user_shost = user_host;
1.1 millert 527: } else {
1.11 millert 528: if ((p = strchr(user_host, '.'))) {
529: *p = '\0';
530: user_shost = estrdup(user_host);
531: *p = '.';
532: } else {
533: user_shost = user_host;
534: }
1.1 millert 535: }
536: }
537:
538: if ((p = ttyname(STDIN_FILENO)) || (p = ttyname(STDOUT_FILENO))) {
539: if (strncmp(p, _PATH_DEV, sizeof(_PATH_DEV) - 1) == 0)
540: p += sizeof(_PATH_DEV) - 1;
541: user_tty = estrdup(p);
542: } else
543: user_tty = "unknown";
544:
545: /*
546: * Get a local copy of the user's struct passwd with the shadow password
547: * if necessary. It is assumed that euid is 0 at this point so we
548: * can read the shadow passwd file if necessary.
549: */
550: if ((sudo_user.pw = sudo_getpwuid(getuid())) == NULL) {
551: /* Need to make a fake struct passwd for logging to work. */
552: struct passwd pw;
553: char pw_name[MAX_UID_T_LEN + 1];
554:
555: pw.pw_uid = getuid();
1.17 millert 556: (void) snprintf(pw_name, sizeof(pw_name), "%lu",
557: (unsigned long) pw.pw_uid);
1.1 millert 558: pw.pw_name = pw_name;
559: sudo_user.pw = &pw;
560:
1.21 millert 561: /*
562: * If we are in -k/-K mode, just spew to stderr. It is not unusual for
563: * users to place "sudo -k" in a .logout file which can cause sudo to
564: * be run during reboot after the YP/NIS/NIS+/LDAP/etc daemon has died.
565: */
566: if (sudo_mode & (MODE_INVALIDATE|MODE_KILL))
567: errx(1, "uid %s does not exist in the passwd file!", pw_name);
568: log_error(0, "uid %s does not exist in the passwd file!", pw_name);
1.1 millert 569: }
1.15 millert 570: if (user_shell == NULL || *user_shell == '\0')
571: user_shell = sudo_user.pw->pw_shell;
1.1 millert 572:
573: /* It is now safe to use log_error() and set_perms() */
574:
1.23 millert 575: if (def_fqdn)
576: set_fqdn(); /* may call log_error() */
1.11 millert 577:
578: if (nohostname)
579: log_error(USE_ERRNO|MSG_ONLY, "can't get hostname");
580:
1.23 millert 581: set_runaspw(*user_runas); /* may call log_error() */
582: if (*user_runas[0] == '#' && runas_pw->pw_name && runas_pw->pw_name[0])
583: *user_runas = estrdup(runas_pw->pw_name);
584:
1.11 millert 585: /*
1.1 millert 586: * Get current working directory. Try as user, fall back to root.
587: */
1.17 millert 588: set_perms(PERM_USER);
1.1 millert 589: if (!getcwd(user_cwd, sizeof(user_cwd))) {
1.17 millert 590: set_perms(PERM_ROOT);
1.1 millert 591: if (!getcwd(user_cwd, sizeof(user_cwd))) {
1.19 millert 592: warnx("cannot get working directory");
1.17 millert 593: (void) strlcpy(user_cwd, "unknown", sizeof(user_cwd));
1.1 millert 594: }
595: } else
1.17 millert 596: set_perms(PERM_ROOT);
1.1 millert 597:
598: /*
1.23 millert 599: * If we were given the '-e', '-i' or '-s' options we need to redo
1.1 millert 600: * NewArgv and NewArgc.
601: */
1.23 millert 602: if ((sudo_mode & (MODE_SHELL | MODE_EDIT))) {
1.1 millert 603: char **dst, **src = NewArgv;
604:
1.17 millert 605: NewArgv = (char **) emalloc2((++NewArgc + 1), sizeof(char *));
1.23 millert 606: if (ISSET(sudo_mode, MODE_EDIT))
607: NewArgv[0] = "sudoedit";
608: else if (ISSET(sudo_mode, MODE_LOGIN_SHELL))
609: NewArgv[0] = runas_pw->pw_shell;
610: else if (user_shell && *user_shell)
1.1 millert 611: NewArgv[0] = user_shell;
1.23 millert 612: else
1.19 millert 613: errx(1, "unable to determine shell");
1.1 millert 614:
1.19 millert 615: /* copy the args from NewArgv */
1.1 millert 616: for (dst = NewArgv + 1; (*dst = *src) != NULL; ++src, ++dst)
617: ;
618: }
619:
1.8 millert 620: /* Set login class if applicable. */
621: set_loginclass(sudo_user.pw);
622:
1.1 millert 623: /* Resolve the path and return. */
1.23 millert 624: rval = FOUND;
625: user_stat = emalloc(sizeof(struct stat));
626: if (sudo_mode & (MODE_RUN | MODE_EDIT)) {
627: if (ISSET(sudo_mode, MODE_RUN)) {
628: /* XXX - default_runas may be modified during parsing of sudoers */
629: set_perms(PERM_RUNAS);
630: rval = find_path(NewArgv[0], &user_cmnd, user_stat, user_path);
1.17 millert 631: set_perms(PERM_ROOT);
1.23 millert 632: if (rval != FOUND) {
633: /* Failed as root, try as invoking user. */
634: set_perms(PERM_USER);
635: rval = find_path(NewArgv[0], &user_cmnd, user_stat, user_path);
636: set_perms(PERM_ROOT);
637: }
1.11 millert 638: }
639:
640: /* set user_args */
641: if (NewArgc > 1) {
642: char *to, **from;
1.17 millert 643: size_t size, n;
1.11 millert 644:
1.23 millert 645: /* If we didn't realloc NewArgv it is contiguous so just count. */
646: if (!(sudo_mode & (MODE_SHELL | MODE_EDIT))) {
1.11 millert 647: size = (size_t) (NewArgv[NewArgc-1] - NewArgv[1]) +
648: strlen(NewArgv[NewArgc-1]) + 1;
649: } else {
650: for (size = 0, from = NewArgv + 1; *from; from++)
651: size += strlen(*from) + 1;
652: }
653:
1.23 millert 654: /* Alloc and build up user_args. */
1.17 millert 655: user_args = (char *) emalloc(size);
656: for (to = user_args, from = NewArgv + 1; *from; from++) {
657: n = strlcpy(to, *from, size - (to - user_args));
1.19 millert 658: if (n >= size - (to - user_args))
659: errx(1, "internal error, init_vars() overflow");
1.17 millert 660: to += n;
1.11 millert 661: *to++ = ' ';
662: }
663: *--to = '\0';
664: }
1.23 millert 665: }
666: if ((user_base = strrchr(user_cmnd, '/')) != NULL)
667: user_base++;
668: else
669: user_base = user_cmnd;
1.11 millert 670:
671: return(rval);
1.1 millert 672: }
673:
674: /*
675: * Command line argument parsing, can't use getopt(3).
676: */
677: static int
1.19 millert 678: parse_args(argc, argv)
679: int argc;
680: char **argv;
1.1 millert 681: {
1.17 millert 682: int rval = MODE_RUN; /* what mode is sudo to be run in? */
1.1 millert 683: int excl = 0; /* exclusive arg, no others allowed */
684:
1.19 millert 685: NewArgv = argv + 1;
686: NewArgc = argc - 1;
1.1 millert 687:
1.23 millert 688: /* First, check to see if we were invoked as "sudoedit". */
689: if (strcmp(getprogname(), "sudoedit") == 0) {
690: rval = MODE_EDIT;
691: excl = 'e';
692: } else
693: rval = MODE_RUN;
694:
695: if (NewArgc == 0 && rval == MODE_RUN) { /* no options and no command */
696: SET(rval, (MODE_IMPLIED_SHELL | MODE_SHELL));
1.1 millert 697: return(rval);
698: }
699:
700: while (NewArgc > 0 && NewArgv[0][0] == '-') {
1.19 millert 701: if (NewArgv[0][1] != '\0' && NewArgv[0][2] != '\0')
702: warnx("please use single character options");
1.1 millert 703:
704: switch (NewArgv[0][1]) {
705: case 'p':
706: /* Must have an associated prompt. */
707: if (NewArgv[1] == NULL)
708: usage(1);
709:
710: user_prompt = NewArgv[1];
711:
712: NewArgc--;
713: NewArgv++;
714: break;
715: case 'u':
716: /* Must have an associated runas user. */
717: if (NewArgv[1] == NULL)
718: usage(1);
719:
720: user_runas = &NewArgv[1];
721:
722: NewArgc--;
723: NewArgv++;
724: break;
1.8 millert 725: #ifdef HAVE_BSD_AUTH_H
726: case 'a':
727: /* Must have an associated authentication style. */
728: if (NewArgv[1] == NULL)
729: usage(1);
730:
731: login_style = NewArgv[1];
732:
733: NewArgc--;
734: NewArgv++;
735: break;
736: #endif
737: #ifdef HAVE_LOGIN_CAP_H
1.4 millert 738: case 'c':
739: /* Must have an associated login class. */
740: if (NewArgv[1] == NULL)
741: usage(1);
742:
743: login_class = NewArgv[1];
1.23 millert 744: def_use_loginclass = TRUE;
1.4 millert 745:
746: NewArgc--;
747: NewArgv++;
748: break;
749: #endif
1.1 millert 750: case 'b':
1.23 millert 751: SET(rval, MODE_BACKGROUND);
752: break;
753: case 'e':
754: rval = MODE_EDIT;
755: if (excl && excl != 'e')
756: usage_excl(1);
757: excl = 'e';
1.1 millert 758: break;
759: case 'v':
760: rval = MODE_VALIDATE;
761: if (excl && excl != 'v')
762: usage_excl(1);
763: excl = 'v';
764: break;
1.23 millert 765: case 'i':
766: SET(rval, (MODE_LOGIN_SHELL | MODE_SHELL));
767: def_env_reset = TRUE;
768: if (excl && excl != 'i')
769: usage_excl(1);
770: excl = 'i';
771: break;
1.1 millert 772: case 'k':
773: rval = MODE_INVALIDATE;
774: if (excl && excl != 'k')
775: usage_excl(1);
776: excl = 'k';
777: break;
778: case 'K':
779: rval = MODE_KILL;
780: if (excl && excl != 'K')
781: usage_excl(1);
782: excl = 'K';
783: break;
784: case 'L':
785: rval = MODE_LISTDEFS;
786: if (excl && excl != 'L')
787: usage_excl(1);
788: excl = 'L';
789: break;
790: case 'l':
791: rval = MODE_LIST;
792: if (excl && excl != 'l')
793: usage_excl(1);
794: excl = 'l';
795: break;
796: case 'V':
797: rval = MODE_VERSION;
798: if (excl && excl != 'V')
799: usage_excl(1);
800: excl = 'V';
801: break;
802: case 'h':
803: rval = MODE_HELP;
804: if (excl && excl != 'h')
805: usage_excl(1);
806: excl = 'h';
807: break;
808: case 's':
1.23 millert 809: SET(rval, MODE_SHELL);
1.2 millert 810: if (excl && excl != 's')
811: usage_excl(1);
812: excl = 's';
1.1 millert 813: break;
814: case 'H':
1.23 millert 815: SET(rval, MODE_RESET_HOME);
1.1 millert 816: break;
1.11 millert 817: case 'P':
1.23 millert 818: SET(rval, MODE_PRESERVE_GROUPS);
1.11 millert 819: break;
1.4 millert 820: case 'S':
1.23 millert 821: SET(tgetpass_flags, TGP_STDIN);
1.4 millert 822: break;
1.1 millert 823: case '-':
824: NewArgc--;
825: NewArgv++;
1.2 millert 826: if (rval == MODE_RUN)
1.23 millert 827: SET(rval, (MODE_IMPLIED_SHELL | MODE_SHELL));
1.1 millert 828: return(rval);
829: case '\0':
1.19 millert 830: warnx("'-' requires an argument");
1.1 millert 831: usage(1);
832: default:
1.19 millert 833: warnx("illegal option `%s'", NewArgv[0]);
1.1 millert 834: usage(1);
835: }
836: NewArgc--;
837: NewArgv++;
838: }
839:
1.24 millert 840: if (user_runas != NULL && !ISSET(rval, (MODE_EDIT|MODE_RUN))) {
841: if (excl != '\0')
842: warnx("the `-u' and '-%c' options may not be used together", excl);
843: usage(1);
844: }
845:
1.23 millert 846: if ((NewArgc == 0 && (rval & MODE_EDIT)) ||
847: (NewArgc > 0 && !(rval & (MODE_RUN | MODE_EDIT))))
1.1 millert 848: usage(1);
849:
850: return(rval);
851: }
852:
853: /*
854: * Sanity check sudoers mode/owner/type.
855: * Leaves a file pointer to the sudoers file open in ``fp''.
856: */
857: static void
858: check_sudoers()
859: {
860: struct stat statbuf;
861: int rootstat, i;
862: char c;
863:
864: /*
865: * Fix the mode and group on sudoers file from old default.
1.23 millert 866: * Only works if file system is readable/writable by root.
1.1 millert 867: */
1.17 millert 868: if ((rootstat = stat_sudoers(_PATH_SUDOERS, &statbuf)) == 0 &&
1.1 millert 869: SUDOERS_UID == statbuf.st_uid && SUDOERS_MODE != 0400 &&
870: (statbuf.st_mode & 0007777) == 0400) {
871:
872: if (chmod(_PATH_SUDOERS, SUDOERS_MODE) == 0) {
1.19 millert 873: warnx("fixed mode on %s", _PATH_SUDOERS);
1.23 millert 874: SET(statbuf.st_mode, SUDOERS_MODE);
1.1 millert 875: if (statbuf.st_gid != SUDOERS_GID) {
876: if (!chown(_PATH_SUDOERS,(uid_t) -1,SUDOERS_GID)) {
1.19 millert 877: warnx("set group on %s", _PATH_SUDOERS);
1.1 millert 878: statbuf.st_gid = SUDOERS_GID;
1.19 millert 879: } else
880: warn("unable to set group on %s", _PATH_SUDOERS);
1.1 millert 881: }
1.19 millert 882: } else
883: warn("unable to fix mode on %s", _PATH_SUDOERS);
1.1 millert 884: }
885:
886: /*
887: * Sanity checks on sudoers file. Must be done as sudoers
888: * file owner. We already did a stat as root, so use that
889: * data if we can't stat as sudoers file owner.
890: */
1.17 millert 891: set_perms(PERM_SUDOERS);
1.1 millert 892:
1.17 millert 893: if (rootstat != 0 && stat_sudoers(_PATH_SUDOERS, &statbuf) != 0)
1.1 millert 894: log_error(USE_ERRNO, "can't stat %s", _PATH_SUDOERS);
895: else if (!S_ISREG(statbuf.st_mode))
896: log_error(0, "%s is not a regular file", _PATH_SUDOERS);
1.9 millert 897: else if (statbuf.st_size == 0)
898: log_error(0, "%s is zero length", _PATH_SUDOERS);
1.1 millert 899: else if ((statbuf.st_mode & 07777) != SUDOERS_MODE)
900: log_error(0, "%s is mode 0%o, should be 0%o", _PATH_SUDOERS,
901: (statbuf.st_mode & 07777), SUDOERS_MODE);
902: else if (statbuf.st_uid != SUDOERS_UID)
1.17 millert 903: log_error(0, "%s is owned by uid %lu, should be %lu", _PATH_SUDOERS,
1.25 ! millert 904: (unsigned long) statbuf.st_uid, (unsigned long) SUDOERS_UID);
1.1 millert 905: else if (statbuf.st_gid != SUDOERS_GID)
1.17 millert 906: log_error(0, "%s is owned by gid %lu, should be %lu", _PATH_SUDOERS,
1.25 ! millert 907: (unsigned long) statbuf.st_gid, (unsigned long) SUDOERS_GID);
1.1 millert 908: else {
909: /* Solaris sometimes returns EAGAIN so try 10 times */
910: for (i = 0; i < 10 ; i++) {
911: errno = 0;
912: if ((sudoers_fp = fopen(_PATH_SUDOERS, "r")) == NULL ||
913: fread(&c, sizeof(c), 1, sudoers_fp) != 1) {
914: sudoers_fp = NULL;
915: if (errno != EAGAIN && errno != EWOULDBLOCK)
916: break;
917: } else
918: break;
919: sleep(1);
920: }
921: if (sudoers_fp == NULL)
922: log_error(USE_ERRNO, "can't open %s", _PATH_SUDOERS);
923: }
924:
1.17 millert 925: set_perms(PERM_ROOT); /* change back to root */
1.1 millert 926: }
927:
928: /*
929: * Close all open files (except std*) and turn off core dumps.
1.11 millert 930: * Also sets the set_perms() pointer to the correct function.
1.1 millert 931: */
932: static void
933: initial_setup()
934: {
1.22 millert 935: #if defined(RLIMIT_CORE) && !defined(SUDO_DEVEL)
1.1 millert 936: struct rlimit rl;
937:
938: /*
939: * Turn off core dumps.
940: */
1.4 millert 941: (void) getrlimit(RLIMIT_CORE, &corelimit);
1.20 millert 942: memcpy(&rl, &corelimit, sizeof(struct rlimit));
943: rl.rlim_cur = 0;
1.1 millert 944: (void) setrlimit(RLIMIT_CORE, &rl);
1.20 millert 945: #endif /* RLIMIT_CORE && !SUDO_DEVEL */
1.1 millert 946:
1.22 millert 947: closefrom(STDERR_FILENO + 1);
1.1 millert 948:
1.17 millert 949: /*
950: * Make set_perms point to the correct function.
951: * If we are using setresuid() or setreuid() we only need to set this
952: * once. If we are using POSIX saved uids we will switch to
953: * set_perms_nosuid after sudoers has been parsed if the "stay_suid"
954: * option is not set.
955: */
956: #if defined(HAVE_SETRESUID) || defined(HAVE_SETREUID)
957: set_perms = set_perms_suid;
958: #else
959: # if !defined(NO_SAVED_IDS) && defined(_SC_SAVED_IDS) && defined(_SC_VERSION)
1.11 millert 960: if (sysconf(_SC_SAVED_IDS) == 1 && sysconf(_SC_VERSION) >= 199009)
961: set_perms = set_perms_posix;
962: else
1.17 millert 963: # endif
964: set_perms = set_perms_nosuid;
965: #endif /* HAVE_SETRESUID || HAVE_SETREUID */
1.1 millert 966: }
967:
1.8 millert 968: #ifdef HAVE_LOGIN_CAP_H
969: static void
1.4 millert 970: set_loginclass(pw)
971: struct passwd *pw;
972: {
973: int errflags;
974:
975: /*
976: * Don't make it a fatal error if the user didn't specify the login
977: * class themselves. We do this because if login.conf gets
978: * corrupted we want the admin to be able to use sudo to fix it.
979: */
980: if (login_class)
981: errflags = NO_MAIL|MSG_ONLY;
982: else
983: errflags = NO_MAIL|MSG_ONLY|NO_EXIT;
984:
985: if (login_class && strcmp(login_class, "-") != 0) {
1.19 millert 986: if (strcmp(*user_runas, "root") != 0 && user_uid != 0)
987: errx(1, "only root can use -c %s", login_class);
1.4 millert 988: } else {
989: login_class = pw->pw_class;
990: if (!login_class || !*login_class)
991: login_class =
992: (pw->pw_uid == 0) ? LOGIN_DEFROOTCLASS : LOGIN_DEFCLASS;
993: }
994:
995: lc = login_getclass(login_class);
1.10 millert 996: if (!lc || !lc->lc_class || strcmp(lc->lc_class, login_class) != 0) {
1.4 millert 997: log_error(errflags, "unknown login class: %s", login_class);
1.11 millert 998: if (!lc)
999: lc = login_getclass(NULL); /* needed for login_getstyle() later */
1.10 millert 1000: }
1.4 millert 1001: }
1002: #else
1.8 millert 1003: static void
1.4 millert 1004: set_loginclass(pw)
1005: struct passwd *pw;
1006: {
1007: }
1.8 millert 1008: #endif /* HAVE_LOGIN_CAP_H */
1.4 millert 1009:
1.1 millert 1010: /*
1.2 millert 1011: * Look up the fully qualified domain name and set user_host and user_shost.
1012: */
1013: void
1014: set_fqdn()
1015: {
1016: struct hostent *hp;
1017: char *p;
1018:
1.14 millert 1019: if (!(hp = gethostbyname(user_host))) {
1020: log_error(MSG_ONLY|NO_EXIT,
1021: "unable to lookup %s via gethostbyname()", user_host);
1022: } else {
1023: if (user_shost != user_host)
1024: free(user_shost);
1025: free(user_host);
1026: user_host = estrdup(hp->h_name);
1.2 millert 1027: }
1028: if ((p = strchr(user_host, '.'))) {
1029: *p = '\0';
1030: user_shost = estrdup(user_host);
1031: *p = '.';
1032: } else {
1033: user_shost = user_host;
1034: }
1035: }
1036:
1037: /*
1.23 millert 1038: * Get passwd entry for the user we are going to run commands as.
1039: * By default, this is "root". Updates runas_pw as a side effect.
1040: */
1041: int
1042: set_runaspw(user)
1043: char *user;
1044: {
1045: if (runas_pw != NULL) {
1046: if (user_runas != &def_runas_default)
1047: return(TRUE); /* don't override -u option */
1048: free(runas_pw);
1049: }
1050: if (*user == '#') {
1051: runas_pw = sudo_getpwuid(atoi(user + 1));
1052: if (runas_pw == NULL) {
1053: runas_pw = emalloc(sizeof(struct passwd));
1054: (void) memset((VOID *)runas_pw, 0, sizeof(struct passwd));
1055: runas_pw->pw_uid = atoi(user + 1);
1056: }
1057: } else {
1058: runas_pw = sudo_getpwnam(user);
1059: if (runas_pw == NULL)
1060: log_error(NO_MAIL|MSG_ONLY, "no passwd entry for %s!", user);
1061: }
1062: return(TRUE);
1063: }
1064:
1065: /*
1.6 millert 1066: * Get passwd entry for the user we are going to authenticate as.
1.23 millert 1067: * By default, this is the user invoking sudo. In the most common
1068: * case, this matches sudo_user.pw or runas_pw.
1.4 millert 1069: */
1.6 millert 1070: static struct passwd *
1071: get_authpw()
1.4 millert 1072: {
1073: struct passwd *pw;
1074:
1.23 millert 1075: if (def_rootpw) {
1076: if (runas_pw->pw_uid == 0)
1077: pw = runas_pw;
1078: else if ((pw = sudo_getpwuid(0)) == NULL)
1.4 millert 1079: log_error(0, "uid 0 does not exist in the passwd file!");
1.23 millert 1080: } else if (def_runaspw) {
1081: if (strcmp(def_runas_default, *user_runas) == 0)
1082: pw = runas_pw;
1083: else if ((pw = sudo_getpwnam(def_runas_default)) == NULL)
1.4 millert 1084: log_error(0, "user %s does not exist in the passwd file!",
1.23 millert 1085: def_runas_default);
1086: } else if (def_targetpw) {
1087: if (runas_pw->pw_name == NULL)
1088: log_error(NO_MAIL|MSG_ONLY, "no passwd entry for %lu!",
1.25 ! millert 1089: (unsigned long) runas_pw->pw_uid);
1.23 millert 1090: pw = runas_pw;
1.6 millert 1091: } else
1092: pw = sudo_user.pw;
1093:
1094: return(pw);
1.4 millert 1095: }
1096:
1097: /*
1.1 millert 1098: * Tell which options are mutually exclusive and exit.
1099: */
1100: static void
1101: usage_excl(exit_val)
1102: int exit_val;
1103: {
1.23 millert 1104: warnx("Only one of the -e, -h, -k, -K, -l, -s, -v or -V options may be used");
1.1 millert 1105: usage(exit_val);
1106: }
1107:
1108: /*
1109: * Give usage message and exit.
1110: */
1111: static void
1112: usage(exit_val)
1113: int exit_val;
1114: {
1.23 millert 1115: char **p;
1116: int linelen, linemax, ulen;
1117: static char *uvec[] = {
1118: " [-HPSb]",
1119: #ifdef HAVE_BSD_AUTH_H
1120: " [-a auth_type]",
1121: #endif
1.8 millert 1122: #ifdef HAVE_LOGIN_CAP_H
1.23 millert 1123: " [-c class|-]",
1.8 millert 1124: #endif
1.23 millert 1125: " [-p prompt]",
1126: " [-u username|#uid]",
1127: " { -e file [...] | -i | -s | <command> }",
1128: NULL
1129: };
1130:
1131: /*
1132: * For sudoedit, replace the last entry in the usage vector.
1133: * For sudo, print the secondary usage.
1134: */
1135: if (strcmp(getprogname(), "sudoedit") == 0) {
1136: /* Replace the last entry in the usage vector. */
1137: for (p = uvec; p[1] != NULL; p++)
1138: continue;
1139: *p = " file [...]";
1140: } else {
1141: fprintf(stderr, "usage: %s -K | -L | -V | -h | -k | -l | -v\n",
1142: getprogname());
1143: }
1144:
1145: /*
1146: * Print the main usage and wrap lines as needed.
1147: * Assumes an 80-character wide terminal, which is kind of bogus...
1148: */
1149: ulen = (int)strlen(getprogname()) + 7;
1150: linemax = 80;
1151: linelen = linemax - ulen;
1152: printf("usage: %s", getprogname());
1153: for (p = uvec; *p != NULL; p++) {
1154: if (linelen == linemax || (linelen -= strlen(*p)) >= 0) {
1155: fputs(*p, stdout);
1156: } else {
1157: p--;
1158: linelen = linemax;
1159: printf("\n%*s", ulen, "");
1160: }
1161: }
1162: putchar('\n');
1.1 millert 1163: exit(exit_val);
1164: }