Annotation of src/usr.bin/sudo/sudo.c, Revision 1.13
1.1 millert 1: /*
1.12 millert 2: * Copyright (c) 1993-1996,1998-2002 Todd C. Miller <Todd.Miller@courtesan.com>
1.1 millert 3: * All rights reserved.
4: *
5: * Redistribution and use in source and binary forms, with or without
6: * modification, are permitted provided that the following conditions
7: * are met:
8: *
9: * 1. Redistributions of source code must retain the above copyright
10: * notice, this list of conditions and the following disclaimer.
11: *
12: * 2. Redistributions in binary form must reproduce the above copyright
13: * notice, this list of conditions and the following disclaimer in the
14: * documentation and/or other materials provided with the distribution.
15: *
16: * 3. The name of the author may not be used to endorse or promote products
17: * derived from this software without specific prior written permission.
18: *
19: * 4. Products derived from this software may not be called "Sudo" nor
20: * may "Sudo" appear in their names without specific prior written
21: * permission from the author.
22: *
23: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
24: * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
25: * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
26: * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
27: * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
28: * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
29: * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
30: * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
31: * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
32: * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33: *
34: * For a brief history of sudo, please see the HISTORY file included
35: * with this distribution.
36: */
37:
38: #define _SUDO_SUDO_C
39:
40: #include "config.h"
41:
1.11 millert 42: #include <sys/types.h>
43: #include <sys/stat.h>
44: #include <sys/param.h>
45: #include <sys/socket.h>
46: #ifdef HAVE_SETRLIMIT
47: # include <sys/time.h>
48: # include <sys/resource.h>
49: #endif
1.1 millert 50: #include <stdio.h>
51: #ifdef STDC_HEADERS
1.11 millert 52: # include <stdlib.h>
53: # include <stddef.h>
54: #else
55: # ifdef HAVE_STDLIB_H
56: # include <stdlib.h>
57: # endif
1.1 millert 58: #endif /* STDC_HEADERS */
1.11 millert 59: #ifdef HAVE_STRING_H
60: # if defined(HAVE_MEMORY_H) && !defined(STDC_HEADERS)
61: # include <memory.h>
62: # endif
63: # include <string.h>
64: #else
65: # ifdef HAVE_STRINGS_H
66: # include <strings.h>
67: # endif
68: #endif /* HAVE_STRING_H */
1.1 millert 69: #ifdef HAVE_UNISTD_H
1.11 millert 70: # include <unistd.h>
1.1 millert 71: #endif /* HAVE_UNISTD_H */
72: #include <pwd.h>
73: #include <errno.h>
74: #include <fcntl.h>
75: #include <signal.h>
76: #include <grp.h>
77: #include <time.h>
78: #include <netinet/in.h>
79: #include <netdb.h>
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 */
1.8 millert 89: #ifdef HAVE_LOGIN_CAP_H
1.4 millert 90: # include <login_cap.h>
91: # ifndef LOGIN_DEFROOTCLASS
92: # define LOGIN_DEFROOTCLASS "daemon"
93: # endif
94: #endif
1.1 millert 95:
96: #include "sudo.h"
97: #include "interfaces.h"
98: #include "version.h"
99:
100: #ifndef lint
1.13 ! millert 101: static const char rcsid[] = "$Sudo: sudo.c,v 1.314 2002/01/13 18:29:23 millert Exp $";
1.1 millert 102: #endif /* lint */
103:
104: /*
105: * Prototypes
106: */
1.11 millert 107: static int init_vars __P((int));
108: static int parse_args __P((void));
109: static void check_sudoers __P((void));
110: static void initial_setup __P((void));
111: static void set_loginclass __P((struct passwd *));
1.1 millert 112: static void usage __P((int));
113: static void usage_excl __P((int));
1.6 millert 114: static struct passwd *get_authpw __P((void));
1.11 millert 115: extern void list_matches __P((void));
116: extern char **rebuild_env __P((int, char **));
117: extern char **zero_env __P((char **));
118: extern struct passwd *sudo_getpwnam __P((const char *));
1.1 millert 119: extern struct passwd *sudo_getpwuid __P((uid_t));
120:
121: /*
122: * Globals
123: */
124: int Argc;
125: char **Argv;
126: int NewArgc = 0;
127: char **NewArgv = NULL;
128: struct sudo_user sudo_user;
1.6 millert 129: struct passwd *auth_pw;
1.1 millert 130: FILE *sudoers_fp = NULL;
131: struct interface *interfaces;
132: int num_interfaces;
1.4 millert 133: int tgetpass_flags;
1.1 millert 134: extern int errorlineno;
1.4 millert 135: #if defined(RLIMIT_CORE) && !defined(SUDO_DEVEL)
136: static struct rlimit corelimit;
137: #endif /* RLIMIT_CORE */
1.8 millert 138: #ifdef HAVE_LOGIN_CAP_H
139: login_cap_t *lc;
140: #endif /* HAVE_LOGIN_CAP_H */
141: #ifdef HAVE_BSD_AUTH_H
142: char *login_style;
143: #endif /* HAVE_BSD_AUTH_H */
1.11 millert 144: void (*set_perms) __P((int, int));
1.1 millert 145:
146:
147: int
1.11 millert 148: main(argc, argv, envp)
1.1 millert 149: int argc;
150: char **argv;
1.11 millert 151: char **envp;
1.1 millert 152: {
153: int validated;
154: int fd;
155: int cmnd_status;
156: int sudo_mode;
1.11 millert 157: int pwflag;
1.12 millert 158: char **new_environ;
1.11 millert 159: sigaction_t sa;
160: extern int printmatches;
1.1 millert 161: extern char **environ;
162:
163: /* Must be done as the first thing... */
164: #if defined(HAVE_GETPRPWNAM) && defined(HAVE_SET_AUTH_PARAMETERS)
165: (void) set_auth_parameters(argc, argv);
166: # ifdef HAVE_INITPRIVS
167: initprivs();
168: # endif
169: #endif /* HAVE_GETPRPWNAM && HAVE_SET_AUTH_PARAMETERS */
170:
1.11 millert 171: /* Zero out the environment. */
172: environ = zero_env(envp);
173:
1.1 millert 174: Argv = argv;
175: Argc = argc;
176:
177: if (geteuid() != 0) {
178: (void) fprintf(stderr, "Sorry, %s must be setuid root.\n", Argv[0]);
179: exit(1);
180: }
181:
182: /*
1.11 millert 183: * Ignore keyboard-generated signals so the user cannot interrupt
184: * us at some point and avoid the logging.
1.1 millert 185: */
1.11 millert 186: sigemptyset(&sa.sa_mask);
187: sa.sa_flags = SA_RESTART;
188: sa.sa_handler = SIG_IGN;
189: (void) sigaction(SIGINT, &sa, NULL);
190: (void) sigaction(SIGQUIT, &sa, NULL);
191: (void) sigaction(SIGTSTP, &sa, NULL);
1.1 millert 192:
193: /*
194: * Setup signal handlers, turn off core dumps, and close open files.
195: */
196: initial_setup();
1.11 millert 197: setpwent();
1.1 millert 198:
199: /* Parse our arguments. */
200: sudo_mode = parse_args();
201:
202: /* Setup defaults data structures. */
203: init_defaults();
204:
1.11 millert 205: /* Load the list of local ip addresses and netmasks. */
206: load_interfaces();
207:
208: pwflag = 0;
1.1 millert 209: if (sudo_mode & MODE_SHELL)
210: user_cmnd = "shell";
211: else
212: switch (sudo_mode) {
213: case MODE_VERSION:
214: (void) printf("Sudo version %s\n", version);
215: if (getuid() == 0) {
216: putchar('\n');
217: dump_auth_methods();
218: dump_defaults();
1.11 millert 219: dump_interfaces();
220: dump_badenv();
1.1 millert 221: }
222: exit(0);
223: break;
224: case MODE_HELP:
225: usage(0);
226: break;
227: case MODE_VALIDATE:
228: user_cmnd = "validate";
1.11 millert 229: pwflag = I_VERIFYPW_I;
1.1 millert 230: break;
231: case MODE_KILL:
232: case MODE_INVALIDATE:
233: user_cmnd = "kill";
1.11 millert 234: pwflag = -1;
1.1 millert 235: break;
236: case MODE_LISTDEFS:
237: list_options();
238: exit(0);
239: break;
240: case MODE_LIST:
241: user_cmnd = "list";
1.11 millert 242: pwflag = I_LISTPW_I;
1.1 millert 243: printmatches = 1;
244: break;
245: }
246:
247: /* Must have a command to run... */
248: if (user_cmnd == NULL && NewArgc == 0)
249: usage(1);
250:
251: cmnd_status = init_vars(sudo_mode);
252:
1.11 millert 253: check_sudoers(); /* check mode/owner on _PATH_SUDOERS */
1.1 millert 254:
1.11 millert 255: /* Validate the user but don't search for pseudo-commands. */
256: validated = sudoers_lookup(pwflag);
1.13 ! millert 257:
! 258: /*
! 259: * If we have POSIX saved uids and the stay_setuid flag was not set,
! 260: * set the real, effective and saved uids to 0 and use set_perms_fallback()
! 261: * instead of set_perms_posix().
! 262: */
! 263: #if defined(_SC_SAVED_IDS) && defined(_SC_VERSION)
! 264: if (!def_flag(I_STAY_SETUID) && set_perms == set_perms_posix) {
! 265: if (setuid(0)) {
! 266: perror("setuid(0)");
! 267: exit(1);
! 268: }
! 269: set_perms = set_perms_fallback;
! 270: }
! 271: #endif
1.1 millert 272:
1.11 millert 273: /*
274: * Look up runas user passwd struct. If we are given a uid then
275: * there may be no corresponding passwd(5) entry (which is OK).
276: */
277: if (**user_runas == '#') {
278: runas_pw = sudo_getpwuid(atoi(*user_runas + 1));
279: if (runas_pw == NULL) {
280: runas_pw = emalloc(sizeof(struct passwd));
281: (void) memset((VOID *)runas_pw, 0, sizeof(struct passwd));
282: runas_pw->pw_uid = atoi(*user_runas + 1);
283: }
284: } else {
285: runas_pw = sudo_getpwnam(*user_runas);
286: if (runas_pw == NULL)
287: log_error(NO_MAIL|MSG_ONLY, "no passwd entry for %s!", *user_runas);
288: }
1.2 millert 289:
1.12 millert 290: /* Build up custom environment that avoids any nasty bits. */
291: new_environ = rebuild_env(sudo_mode, envp);
1.2 millert 292:
293: /* This goes after the sudoers parse since we honor sudoers options. */
1.1 millert 294: if (sudo_mode == MODE_KILL || sudo_mode == MODE_INVALIDATE) {
295: remove_timestamp((sudo_mode == MODE_KILL));
296: exit(0);
297: }
298:
299: if (validated & VALIDATE_ERROR)
300: log_error(0, "parse error in %s near line %d", _PATH_SUDOERS,
301: errorlineno);
302:
303: /* Is root even allowed to run sudo? */
304: if (user_uid == 0 && !def_flag(I_ROOT_SUDO)) {
305: (void) fputs("You are already root, you don't need to use sudo.\n",
306: stderr);
307: exit(1);
308: }
309:
1.11 millert 310: /* If given the -P option, set the "preserve_groups" flag. */
311: if (sudo_mode & MODE_PRESERVE_GROUPS)
312: def_flag(I_PRESERVE_GROUPS) = TRUE;
313:
1.3 millert 314: /* If no command line args and "set_home" is not set, error out. */
315: if ((sudo_mode & MODE_IMPLIED_SHELL) && !def_flag(I_SHELL_NOARGS))
316: usage(1);
317:
1.2 millert 318: /* May need to set $HOME to target user. */
1.11 millert 319: if (def_flag(I_ALWAYS_SET_HOME) ||
320: ((sudo_mode & MODE_SHELL) && def_flag(I_SET_HOME)))
1.2 millert 321: sudo_mode |= MODE_RESET_HOME;
322:
1.1 millert 323: /* Bail if a tty is required and we don't have one. */
324: if (def_flag(I_REQUIRETTY)) {
325: if ((fd = open(_PATH_TTY, O_RDWR|O_NOCTTY)) == -1)
326: log_error(NO_MAIL, "sorry, you must have a tty to run sudo");
327: else
328: (void) close(fd);
329: }
330:
1.6 millert 331: /* Fill in passwd struct based on user we are authenticating as. */
332: auth_pw = get_authpw();
1.4 millert 333:
1.1 millert 334: /* Require a password unless the NOPASS tag was set. */
335: if (!(validated & FLAG_NOPASS))
336: check_user();
337:
338: if (validated & VALIDATE_OK) {
339: /* Finally tell the user if the command did not exist. */
340: if (cmnd_status == NOT_FOUND_DOT) {
341: (void) fprintf(stderr, "%s: ignoring `%s' found in '.'\nUse `sudo ./%s' if this is the `%s' you wish to run.\n", Argv[0], user_cmnd, user_cmnd, user_cmnd);
342: exit(1);
343: } else if (cmnd_status == NOT_FOUND) {
344: (void) fprintf(stderr, "%s: %s: command not found\n", Argv[0],
345: user_cmnd);
346: exit(1);
347: }
348:
349: log_auth(validated, 1);
350: if (sudo_mode == MODE_VALIDATE)
351: exit(0);
352: else if (sudo_mode == MODE_LIST) {
353: list_matches();
354: exit(0);
355: }
356:
357: /* This *must* have been set if we got a match but... */
358: if (safe_cmnd == NULL) {
359: log_error(MSG_ONLY,
1.11 millert 360: "internal error, safe_cmnd never got set for %s; %s",
1.1 millert 361: user_cmnd,
362: "please report this error at http://courtesan.com/sudo/bugs/");
363: }
364:
1.11 millert 365: /* Reset signal handlers before we exec. */
366: sigemptyset(&sa.sa_mask);
367: sa.sa_flags = SA_RESTART;
368: sa.sa_handler = SIG_DFL;
369: (void) sigaction(SIGINT, &sa, NULL);
370: (void) sigaction(SIGQUIT, &sa, NULL);
371: (void) sigaction(SIGTSTP, &sa, NULL);
1.1 millert 372:
1.11 millert 373: /* Close the password file */
374: endpwent();
1.1 millert 375:
376: /* Override user's umask if configured to do so. */
377: if (def_ival(I_UMASK) != 0777)
378: (void) umask(def_mode(I_UMASK));
379:
1.4 millert 380: /* Restore coredumpsize resource limit. */
381: #if defined(RLIMIT_CORE) && !defined(SUDO_DEVEL)
382: (void) setrlimit(RLIMIT_CORE, &corelimit);
383: #endif /* RLIMIT_CORE */
384:
385: /* Become specified user or root. */
386: set_perms(PERM_RUNAS, sudo_mode);
1.12 millert 387:
388: /* Install the new environment. */
389: environ = new_environ;
1.5 millert 390:
1.1 millert 391: #ifndef PROFILING
392: if ((sudo_mode & MODE_BACKGROUND) && fork() > 0)
393: exit(0);
394: else
395: EXEC(safe_cmnd, NewArgv); /* run the command */
396: #else
397: exit(0);
398: #endif /* PROFILING */
399: /*
400: * If we got here then the exec() failed...
401: */
402: (void) fprintf(stderr, "%s: unable to exec %s: %s\n",
403: Argv[0], safe_cmnd, strerror(errno));
1.11 millert 404: exit(127);
1.1 millert 405: } else if ((validated & FLAG_NO_USER) || (validated & FLAG_NO_HOST)) {
406: log_auth(validated, 1);
407: exit(1);
408: } else if (validated & VALIDATE_NOT_OK) {
409: if (def_flag(I_PATH_INFO)) {
410: /*
411: * We'd like to not leak path info at all here, but that can
412: * *really* confuse the users. To really close the leak we'd
413: * have to say "not allowed to run foo" even when the problem
414: * is just "no foo in path" since the user can trivially set
415: * their path to just contain a single dir.
416: */
417: log_auth(validated,
418: !(cmnd_status == NOT_FOUND_DOT || cmnd_status == NOT_FOUND));
419: if (cmnd_status == NOT_FOUND)
420: (void) fprintf(stderr, "%s: %s: command not found\n", Argv[0],
421: user_cmnd);
422: else if (cmnd_status == NOT_FOUND_DOT)
423: (void) fprintf(stderr, "%s: ignoring `%s' found in '.'\nUse `sudo ./%s' if this is the `%s' you wish to run.\n", Argv[0], user_cmnd, user_cmnd, user_cmnd);
424: } else {
425: /* Just tell the user they are not allowed to run foo. */
426: log_auth(validated, 1);
427: }
428: exit(1);
429: } else {
430: /* should never get here */
431: log_auth(validated, 1);
432: exit(1);
433: }
434: exit(0); /* not reached */
435: }
436:
437: /*
438: * Initialize timezone, set umask, fill in ``sudo_user'' struct and
439: * load the ``interfaces'' array.
440: */
441: static int
442: init_vars(sudo_mode)
443: int sudo_mode;
444: {
445: char *p, thost[MAXHOSTNAMELEN];
1.11 millert 446: int nohostname, rval;
1.1 millert 447:
448: /* Sanity check command from user. */
449: if (user_cmnd == NULL && strlen(NewArgv[0]) >= MAXPATHLEN) {
450: (void) fprintf(stderr, "%s: %s: Pathname too long\n", Argv[0],
451: NewArgv[0]);
452: exit(1);
453: }
454:
455: #ifdef HAVE_TZSET
456: (void) tzset(); /* set the timezone if applicable */
457: #endif /* HAVE_TZSET */
458:
459: /* Default value for cmnd and cwd, overridden later. */
460: if (user_cmnd == NULL)
461: user_cmnd = NewArgv[0];
462: (void) strcpy(user_cwd, "unknown");
463:
464: /*
465: * We avoid gethostbyname() if possible since we don't want
466: * sudo to block if DNS or NIS is hosed.
467: * "host" is the (possibly fully-qualified) hostname and
468: * "shost" is the unqualified form of the hostname.
469: */
1.11 millert 470: nohostname = gethostname(thost, sizeof(thost));
471: if (nohostname)
472: user_host = user_shost = "localhost";
473: else {
1.1 millert 474: user_host = estrdup(thost);
1.11 millert 475: if (def_flag(I_FQDN)) {
476: /* Defer call to set_fqdn() until log_error() is safe. */
477: user_shost = user_host;
1.1 millert 478: } else {
1.11 millert 479: if ((p = strchr(user_host, '.'))) {
480: *p = '\0';
481: user_shost = estrdup(user_host);
482: *p = '.';
483: } else {
484: user_shost = user_host;
485: }
1.1 millert 486: }
487: }
488:
489: if ((p = ttyname(STDIN_FILENO)) || (p = ttyname(STDOUT_FILENO))) {
490: if (strncmp(p, _PATH_DEV, sizeof(_PATH_DEV) - 1) == 0)
491: p += sizeof(_PATH_DEV) - 1;
492: user_tty = estrdup(p);
493: } else
494: user_tty = "unknown";
495:
496: /*
497: * Get a local copy of the user's struct passwd with the shadow password
498: * if necessary. It is assumed that euid is 0 at this point so we
499: * can read the shadow passwd file if necessary.
500: */
501: if ((sudo_user.pw = sudo_getpwuid(getuid())) == NULL) {
502: /* Need to make a fake struct passwd for logging to work. */
503: struct passwd pw;
504: char pw_name[MAX_UID_T_LEN + 1];
505:
506: pw.pw_uid = getuid();
507: (void) sprintf(pw_name, "%ld", (long) pw.pw_uid);
508: pw.pw_name = pw_name;
509: sudo_user.pw = &pw;
510:
511: log_error(0, "uid %ld does not exist in the passwd file!",
512: (long) pw.pw_uid);
513: }
1.11 millert 514: user_shell = sudo_user.pw->pw_shell;
1.1 millert 515:
516: /* It is now safe to use log_error() and set_perms() */
517:
518: /*
1.11 millert 519: * Must defer set_fqdn() until it is safe to call log_error()
520: */
521: if (def_flag(I_FQDN))
522: set_fqdn();
523:
524: if (nohostname)
525: log_error(USE_ERRNO|MSG_ONLY, "can't get hostname");
526:
527: /*
1.1 millert 528: * Get current working directory. Try as user, fall back to root.
529: */
530: set_perms(PERM_USER, sudo_mode);
531: if (!getcwd(user_cwd, sizeof(user_cwd))) {
532: set_perms(PERM_ROOT, sudo_mode);
533: if (!getcwd(user_cwd, sizeof(user_cwd))) {
534: (void) fprintf(stderr, "%s: Can't get working directory!\n",
535: Argv[0]);
536: (void) strcpy(user_cwd, "unknown");
537: }
538: } else
539: set_perms(PERM_ROOT, sudo_mode);
540:
541: /*
542: * If we were given the '-s' option (run shell) we need to redo
543: * NewArgv and NewArgc.
544: */
545: if ((sudo_mode & MODE_SHELL)) {
546: char **dst, **src = NewArgv;
547:
548: NewArgv = (char **) emalloc (sizeof(char *) * (++NewArgc + 1));
549: if (user_shell && *user_shell) {
550: NewArgv[0] = user_shell;
551: } else {
552: (void) fprintf(stderr, "%s: Unable to determine shell.", Argv[0]);
553: exit(1);
554: }
555:
556: /* copy the args from Argv */
557: for (dst = NewArgv + 1; (*dst = *src) != NULL; ++src, ++dst)
558: ;
559: }
560:
1.8 millert 561: /* Set login class if applicable. */
562: set_loginclass(sudo_user.pw);
563:
1.1 millert 564: /* Resolve the path and return. */
1.11 millert 565: if ((sudo_mode & MODE_RUN)) {
566: /* XXX - should call this as runas user, not root. */
567: rval = find_path(NewArgv[0], &user_cmnd, user_path);
568: if (rval != FOUND) {
569: /* Failed as root, try as invoking user. */
570: set_perms(PERM_USER, sudo_mode);
571: rval = find_path(NewArgv[0], &user_cmnd, user_path);
572: set_perms(PERM_ROOT, sudo_mode);
573: }
574:
575: /* set user_args */
576: if (NewArgc > 1) {
577: char *to, **from;
578: size_t size;
579:
580: /* If MODE_SHELL not set then NewArgv is contiguous so just count */
581: if (!(sudo_mode & MODE_SHELL)) {
582: size = (size_t) (NewArgv[NewArgc-1] - NewArgv[1]) +
583: strlen(NewArgv[NewArgc-1]) + 1;
584: } else {
585: for (size = 0, from = NewArgv + 1; *from; from++)
586: size += strlen(*from) + 1;
587: }
588:
589: /* alloc and copy. */
590: to = user_args = (char *) emalloc(size);
591: for (from = NewArgv + 1; *from; from++) {
592: (void) strcpy(to, *from);
593: to += strlen(*from);
594: *to++ = ' ';
595: }
596: *--to = '\0';
597: }
598: } else
599: rval = FOUND;
600:
601: return(rval);
1.1 millert 602: }
603:
604: /*
605: * Command line argument parsing, can't use getopt(3).
606: */
607: static int
608: parse_args()
609: {
610: int rval = MODE_RUN; /* what mode is suod to be run in? */
611: int excl = 0; /* exclusive arg, no others allowed */
612:
613: NewArgv = Argv + 1;
614: NewArgc = Argc - 1;
615:
1.2 millert 616: if (NewArgc == 0) { /* no options and no command */
1.3 millert 617: rval |= (MODE_IMPLIED_SHELL | MODE_SHELL);
1.1 millert 618: return(rval);
619: }
620:
621: while (NewArgc > 0 && NewArgv[0][0] == '-') {
622: if (NewArgv[0][1] != '\0' && NewArgv[0][2] != '\0') {
623: (void) fprintf(stderr, "%s: Please use single character options\n",
624: Argv[0]);
625: usage(1);
626: }
627:
628: switch (NewArgv[0][1]) {
629: case 'p':
630: /* Must have an associated prompt. */
631: if (NewArgv[1] == NULL)
632: usage(1);
633:
634: user_prompt = NewArgv[1];
635:
636: /* Shift Argv over and adjust Argc. */
637: NewArgc--;
638: NewArgv++;
639: break;
640: case 'u':
641: /* Must have an associated runas user. */
642: if (NewArgv[1] == NULL)
643: usage(1);
644:
645: user_runas = &NewArgv[1];
646:
647: /* Shift Argv over and adjust Argc. */
648: NewArgc--;
649: NewArgv++;
650: break;
1.8 millert 651: #ifdef HAVE_BSD_AUTH_H
652: case 'a':
653: /* Must have an associated authentication style. */
654: if (NewArgv[1] == NULL)
655: usage(1);
656:
657: login_style = NewArgv[1];
658:
659: /* Shift Argv over and adjust Argc. */
660: NewArgc--;
661: NewArgv++;
662: break;
663: #endif
664: #ifdef HAVE_LOGIN_CAP_H
1.4 millert 665: case 'c':
666: /* Must have an associated login class. */
667: if (NewArgv[1] == NULL)
668: usage(1);
669:
670: login_class = NewArgv[1];
1.11 millert 671: def_flag(I_USE_LOGINCLASS) = TRUE;
1.4 millert 672:
673: /* Shift Argv over and adjust Argc. */
674: NewArgc--;
675: NewArgv++;
676: break;
677: #endif
1.1 millert 678: case 'b':
679: rval |= MODE_BACKGROUND;
680: break;
681: case 'v':
682: rval = MODE_VALIDATE;
683: if (excl && excl != 'v')
684: usage_excl(1);
685: excl = 'v';
686: break;
687: case 'k':
688: rval = MODE_INVALIDATE;
689: if (excl && excl != 'k')
690: usage_excl(1);
691: excl = 'k';
692: break;
693: case 'K':
694: rval = MODE_KILL;
695: if (excl && excl != 'K')
696: usage_excl(1);
697: excl = 'K';
698: break;
699: case 'L':
700: rval = MODE_LISTDEFS;
701: if (excl && excl != 'L')
702: usage_excl(1);
703: excl = 'L';
704: break;
705: case 'l':
706: rval = MODE_LIST;
707: if (excl && excl != 'l')
708: usage_excl(1);
709: excl = 'l';
710: break;
711: case 'V':
712: rval = MODE_VERSION;
713: if (excl && excl != 'V')
714: usage_excl(1);
715: excl = 'V';
716: break;
717: case 'h':
718: rval = MODE_HELP;
719: if (excl && excl != 'h')
720: usage_excl(1);
721: excl = 'h';
722: break;
723: case 's':
724: rval |= MODE_SHELL;
1.2 millert 725: if (excl && excl != 's')
726: usage_excl(1);
727: excl = 's';
1.1 millert 728: break;
729: case 'H':
730: rval |= MODE_RESET_HOME;
731: break;
1.11 millert 732: case 'P':
733: rval |= MODE_PRESERVE_GROUPS;
734: break;
1.4 millert 735: case 'S':
736: tgetpass_flags |= TGP_STDIN;
737: break;
1.1 millert 738: case '-':
739: NewArgc--;
740: NewArgv++;
1.2 millert 741: if (rval == MODE_RUN)
1.3 millert 742: rval |= (MODE_IMPLIED_SHELL | MODE_SHELL);
1.1 millert 743: return(rval);
744: case '\0':
745: (void) fprintf(stderr, "%s: '-' requires an argument\n",
746: Argv[0]);
747: usage(1);
748: default:
749: (void) fprintf(stderr, "%s: Illegal option %s\n", Argv[0],
750: NewArgv[0]);
751: usage(1);
752: }
753: NewArgc--;
754: NewArgv++;
755: }
756:
757: if (NewArgc > 0 && !(rval & MODE_RUN))
758: usage(1);
759:
760: return(rval);
761: }
762:
763: /*
764: * Sanity check sudoers mode/owner/type.
765: * Leaves a file pointer to the sudoers file open in ``fp''.
766: */
767: static void
768: check_sudoers()
769: {
770: struct stat statbuf;
771: int rootstat, i;
772: char c;
773:
774: /*
775: * Fix the mode and group on sudoers file from old default.
776: * Only works if filesystem is readable/writable by root.
777: */
778: if ((rootstat = lstat(_PATH_SUDOERS, &statbuf)) == 0 &&
779: SUDOERS_UID == statbuf.st_uid && SUDOERS_MODE != 0400 &&
780: (statbuf.st_mode & 0007777) == 0400) {
781:
782: if (chmod(_PATH_SUDOERS, SUDOERS_MODE) == 0) {
783: (void) fprintf(stderr, "%s: fixed mode on %s\n",
784: Argv[0], _PATH_SUDOERS);
1.3 millert 785: statbuf.st_mode |= SUDOERS_MODE;
1.1 millert 786: if (statbuf.st_gid != SUDOERS_GID) {
787: if (!chown(_PATH_SUDOERS,(uid_t) -1,SUDOERS_GID)) {
788: (void) fprintf(stderr, "%s: set group on %s\n",
789: Argv[0], _PATH_SUDOERS);
790: statbuf.st_gid = SUDOERS_GID;
791: } else {
792: (void) fprintf(stderr,"%s: Unable to set group on %s: %s\n",
793: Argv[0], _PATH_SUDOERS, strerror(errno));
794: }
795: }
796: } else {
797: (void) fprintf(stderr, "%s: Unable to fix mode on %s: %s\n",
798: Argv[0], _PATH_SUDOERS, strerror(errno));
799: }
800: }
801:
802: /*
803: * Sanity checks on sudoers file. Must be done as sudoers
804: * file owner. We already did a stat as root, so use that
805: * data if we can't stat as sudoers file owner.
806: */
807: set_perms(PERM_SUDOERS, 0);
808:
809: if (rootstat != 0 && lstat(_PATH_SUDOERS, &statbuf) != 0)
810: log_error(USE_ERRNO, "can't stat %s", _PATH_SUDOERS);
811: else if (!S_ISREG(statbuf.st_mode))
812: log_error(0, "%s is not a regular file", _PATH_SUDOERS);
1.9 millert 813: else if (statbuf.st_size == 0)
814: log_error(0, "%s is zero length", _PATH_SUDOERS);
1.1 millert 815: else if ((statbuf.st_mode & 07777) != SUDOERS_MODE)
816: log_error(0, "%s is mode 0%o, should be 0%o", _PATH_SUDOERS,
817: (statbuf.st_mode & 07777), SUDOERS_MODE);
818: else if (statbuf.st_uid != SUDOERS_UID)
819: log_error(0, "%s is owned by uid %ld, should be %d", _PATH_SUDOERS,
820: (long) statbuf.st_uid, SUDOERS_UID);
821: else if (statbuf.st_gid != SUDOERS_GID)
822: log_error(0, "%s is owned by gid %ld, should be %d", _PATH_SUDOERS,
823: (long) statbuf.st_gid, SUDOERS_GID);
824: else {
825: /* Solaris sometimes returns EAGAIN so try 10 times */
826: for (i = 0; i < 10 ; i++) {
827: errno = 0;
828: if ((sudoers_fp = fopen(_PATH_SUDOERS, "r")) == NULL ||
829: fread(&c, sizeof(c), 1, sudoers_fp) != 1) {
830: sudoers_fp = NULL;
831: if (errno != EAGAIN && errno != EWOULDBLOCK)
832: break;
833: } else
834: break;
835: sleep(1);
836: }
837: if (sudoers_fp == NULL)
838: log_error(USE_ERRNO, "can't open %s", _PATH_SUDOERS);
839: }
840:
841: set_perms(PERM_ROOT, 0); /* change back to root */
842: }
843:
844: /*
845: * Close all open files (except std*) and turn off core dumps.
1.11 millert 846: * Also sets the set_perms() pointer to the correct function.
1.1 millert 847: */
848: static void
849: initial_setup()
850: {
851: int fd, maxfd;
852: #ifdef HAVE_SETRLIMIT
853: struct rlimit rl;
854: #endif
1.11 millert 855: sigaction_t sa;
1.1 millert 856:
857: #if defined(RLIMIT_CORE) && !defined(SUDO_DEVEL)
858: /*
859: * Turn off core dumps.
860: */
1.4 millert 861: (void) getrlimit(RLIMIT_CORE, &corelimit);
1.1 millert 862: rl.rlim_cur = rl.rlim_max = 0;
863: (void) setrlimit(RLIMIT_CORE, &rl);
864: #endif /* RLIMIT_CORE */
865:
866: /*
867: * Close any open fd's other than stdin, stdout and stderr.
868: */
869: #ifdef HAVE_SYSCONF
1.3 millert 870: maxfd = sysconf(_SC_OPEN_MAX) - 1;
1.1 millert 871: #else
1.3 millert 872: maxfd = getdtablesize() - 1;
1.1 millert 873: #endif /* HAVE_SYSCONF */
1.3 millert 874: #ifdef RLIMIT_NOFILE
875: if (getrlimit(RLIMIT_NOFILE, &rl) == 0) {
876: if (rl.rlim_max != RLIM_INFINITY && rl.rlim_max <= maxfd)
877: maxfd = rl.rlim_max - 1;
878: }
879: #endif /* RLIMIT_NOFILE */
1.1 millert 880:
881: for (fd = maxfd; fd > STDERR_FILENO; fd--)
882: (void) close(fd);
883:
884: /* Catch children as they die... */
1.11 millert 885: sigemptyset(&sa.sa_mask);
886: sa.sa_flags = SA_RESTART;
1.1 millert 887: sa.sa_handler = reapchild;
888: (void) sigaction(SIGCHLD, &sa, NULL);
1.11 millert 889:
890: /* Set set_perms pointer to the correct function */
891: #if defined(_SC_SAVED_IDS) && defined(_SC_VERSION)
892: if (sysconf(_SC_SAVED_IDS) == 1 && sysconf(_SC_VERSION) >= 199009)
893: set_perms = set_perms_posix;
894: else
895: #endif
896: set_perms = set_perms_fallback;
1.1 millert 897: }
898:
1.8 millert 899: #ifdef HAVE_LOGIN_CAP_H
900: static void
1.4 millert 901: set_loginclass(pw)
902: struct passwd *pw;
903: {
904: int errflags;
905:
906: /*
907: * Don't make it a fatal error if the user didn't specify the login
908: * class themselves. We do this because if login.conf gets
909: * corrupted we want the admin to be able to use sudo to fix it.
910: */
911: if (login_class)
912: errflags = NO_MAIL|MSG_ONLY;
913: else
914: errflags = NO_MAIL|MSG_ONLY|NO_EXIT;
915:
916: if (login_class && strcmp(login_class, "-") != 0) {
917: if (strcmp(*user_runas, "root") != 0 && user_uid != 0) {
918: (void) fprintf(stderr, "%s: only root can use -c %s\n",
919: Argv[0], login_class);
920: exit(1);
921: }
922: } else {
923: login_class = pw->pw_class;
924: if (!login_class || !*login_class)
925: login_class =
926: (pw->pw_uid == 0) ? LOGIN_DEFROOTCLASS : LOGIN_DEFCLASS;
927: }
928:
929: lc = login_getclass(login_class);
1.10 millert 930: if (!lc || !lc->lc_class || strcmp(lc->lc_class, login_class) != 0) {
1.4 millert 931: log_error(errflags, "unknown login class: %s", login_class);
1.11 millert 932: if (!lc)
933: lc = login_getclass(NULL); /* needed for login_getstyle() later */
1.10 millert 934: }
1.4 millert 935: }
936: #else
1.8 millert 937: static void
1.4 millert 938: set_loginclass(pw)
939: struct passwd *pw;
940: {
941: }
1.8 millert 942: #endif /* HAVE_LOGIN_CAP_H */
1.4 millert 943:
1.1 millert 944: /*
1.2 millert 945: * Look up the fully qualified domain name and set user_host and user_shost.
946: */
947: void
948: set_fqdn()
949: {
950: struct hostent *hp;
951: char *p;
952:
953: if (def_flag(I_FQDN)) {
954: if (!(hp = gethostbyname(user_host))) {
1.11 millert 955: log_error(MSG_ONLY|NO_EXIT,
1.2 millert 956: "unable to lookup %s via gethostbyname()", user_host);
957: } else {
958: free(user_host);
959: user_host = estrdup(hp->h_name);
960: }
961: }
962: if (user_shost != user_host)
963: free(user_shost);
964: if ((p = strchr(user_host, '.'))) {
965: *p = '\0';
966: user_shost = estrdup(user_host);
967: *p = '.';
968: } else {
969: user_shost = user_host;
970: }
971: }
972:
973: /*
1.6 millert 974: * Get passwd entry for the user we are going to authenticate as.
975: * By default, this is the user invoking sudo...
1.4 millert 976: */
1.6 millert 977: static struct passwd *
978: get_authpw()
1.4 millert 979: {
980: struct passwd *pw;
981:
982: if (def_ival(I_ROOTPW)) {
1.6 millert 983: if ((pw = sudo_getpwuid(0)) == NULL)
1.4 millert 984: log_error(0, "uid 0 does not exist in the passwd file!");
985: } else if (def_ival(I_RUNASPW)) {
1.11 millert 986: if ((pw = sudo_getpwnam(def_str(I_RUNAS_DEFAULT))) == NULL)
1.4 millert 987: log_error(0, "user %s does not exist in the passwd file!",
1.11 millert 988: def_str(I_RUNAS_DEFAULT));
1.4 millert 989: } else if (def_ival(I_TARGETPW)) {
990: if (**user_runas == '#') {
1.6 millert 991: if ((pw = sudo_getpwuid(atoi(*user_runas + 1))) == NULL)
1.4 millert 992: log_error(0, "uid %s does not exist in the passwd file!",
993: user_runas);
994: } else {
1.6 millert 995: if ((pw = sudo_getpwnam(*user_runas)) == NULL)
1.4 millert 996: log_error(0, "user %s does not exist in the passwd file!",
997: user_runas);
998: }
1.6 millert 999: } else
1000: pw = sudo_user.pw;
1001:
1002: return(pw);
1.4 millert 1003: }
1004:
1005: /*
1.1 millert 1006: * Tell which options are mutually exclusive and exit.
1007: */
1008: static void
1009: usage_excl(exit_val)
1010: int exit_val;
1011: {
1012: (void) fprintf(stderr,
1.2 millert 1013: "Only one of the -h, -k, -K, -l, -s, -v or -V options may be used\n");
1.1 millert 1014: usage(exit_val);
1015: }
1016:
1017: /*
1018: * Give usage message and exit.
1019: */
1020: static void
1021: usage(exit_val)
1022: int exit_val;
1023: {
1.8 millert 1024:
1025: (void) fprintf(stderr, "usage: sudo -V | -h | -L | -l | -v | -k | -K | %s",
1.11 millert 1026: "[-H] [-P] [-S] [-b] [-p prompt]\n [-u username/#uid] ");
1.8 millert 1027: #ifdef HAVE_LOGIN_CAP_H
1028: (void) fprintf(stderr, "[-c class] ");
1029: #endif
1030: #ifdef HAVE_BSD_AUTH_H
1031: (void) fprintf(stderr, "[-a auth_type] ");
1.4 millert 1032: #endif
1.8 millert 1033: (void) fprintf(stderr, "-s | <command>\n");
1.1 millert 1034: exit(exit_val);
1035: }