Annotation of src/usr.bin/sudo/sudo.c, Revision 1.16
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.15 millert 101: static const char rcsid[] = "$Sudo: sudo.c,v 1.318 2002/01/15 23:43:59 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: */
1.15 millert 263: #if !defined(NO_SAVED_IDS) && defined(_SC_SAVED_IDS) && defined(_SC_VERSION)
1.13 millert 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:
290: /* This goes after the sudoers parse since we honor sudoers options. */
1.1 millert 291: if (sudo_mode == MODE_KILL || sudo_mode == MODE_INVALIDATE) {
292: remove_timestamp((sudo_mode == MODE_KILL));
293: exit(0);
294: }
295:
296: if (validated & VALIDATE_ERROR)
297: log_error(0, "parse error in %s near line %d", _PATH_SUDOERS,
298: errorlineno);
299:
300: /* Is root even allowed to run sudo? */
301: if (user_uid == 0 && !def_flag(I_ROOT_SUDO)) {
302: (void) fputs("You are already root, you don't need to use sudo.\n",
303: stderr);
304: exit(1);
305: }
306:
1.11 millert 307: /* If given the -P option, set the "preserve_groups" flag. */
308: if (sudo_mode & MODE_PRESERVE_GROUPS)
309: def_flag(I_PRESERVE_GROUPS) = TRUE;
310:
1.3 millert 311: /* If no command line args and "set_home" is not set, error out. */
312: if ((sudo_mode & MODE_IMPLIED_SHELL) && !def_flag(I_SHELL_NOARGS))
313: usage(1);
314:
1.15 millert 315: /* May need to set $HOME to target user if we are running a command. */
316: if ((sudo_mode & MODE_RUN) && (def_flag(I_ALWAYS_SET_HOME) ||
317: ((sudo_mode & MODE_SHELL) && def_flag(I_SET_HOME))))
1.2 millert 318: sudo_mode |= MODE_RESET_HOME;
319:
1.1 millert 320: /* Bail if a tty is required and we don't have one. */
321: if (def_flag(I_REQUIRETTY)) {
322: if ((fd = open(_PATH_TTY, O_RDWR|O_NOCTTY)) == -1)
323: log_error(NO_MAIL, "sorry, you must have a tty to run sudo");
324: else
325: (void) close(fd);
326: }
327:
1.6 millert 328: /* Fill in passwd struct based on user we are authenticating as. */
329: auth_pw = get_authpw();
1.4 millert 330:
1.1 millert 331: /* Require a password unless the NOPASS tag was set. */
332: if (!(validated & FLAG_NOPASS))
333: check_user();
334:
1.14 millert 335: /* Build up custom environment that avoids any nasty bits. */
336: new_environ = rebuild_env(sudo_mode, envp);
337:
1.1 millert 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:
373: /* Override user's umask if configured to do so. */
374: if (def_ival(I_UMASK) != 0777)
375: (void) umask(def_mode(I_UMASK));
376:
1.4 millert 377: /* Restore coredumpsize resource limit. */
378: #if defined(RLIMIT_CORE) && !defined(SUDO_DEVEL)
379: (void) setrlimit(RLIMIT_CORE, &corelimit);
380: #endif /* RLIMIT_CORE */
381:
382: /* Become specified user or root. */
383: set_perms(PERM_RUNAS, sudo_mode);
1.16 ! millert 384:
! 385: /* Close the password and group files */
! 386: endpwent();
! 387: endgrent();
1.12 millert 388:
389: /* Install the new environment. */
390: environ = new_environ;
1.5 millert 391:
1.1 millert 392: #ifndef PROFILING
393: if ((sudo_mode & MODE_BACKGROUND) && fork() > 0)
394: exit(0);
395: else
396: EXEC(safe_cmnd, NewArgv); /* run the command */
397: #else
398: exit(0);
399: #endif /* PROFILING */
400: /*
401: * If we got here then the exec() failed...
402: */
403: (void) fprintf(stderr, "%s: unable to exec %s: %s\n",
404: Argv[0], safe_cmnd, strerror(errno));
1.11 millert 405: exit(127);
1.1 millert 406: } else if ((validated & FLAG_NO_USER) || (validated & FLAG_NO_HOST)) {
407: log_auth(validated, 1);
408: exit(1);
409: } else if (validated & VALIDATE_NOT_OK) {
410: if (def_flag(I_PATH_INFO)) {
411: /*
412: * We'd like to not leak path info at all here, but that can
413: * *really* confuse the users. To really close the leak we'd
414: * have to say "not allowed to run foo" even when the problem
415: * is just "no foo in path" since the user can trivially set
416: * their path to just contain a single dir.
417: */
418: log_auth(validated,
419: !(cmnd_status == NOT_FOUND_DOT || cmnd_status == NOT_FOUND));
420: if (cmnd_status == NOT_FOUND)
421: (void) fprintf(stderr, "%s: %s: command not found\n", Argv[0],
422: user_cmnd);
423: else if (cmnd_status == NOT_FOUND_DOT)
424: (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);
425: } else {
426: /* Just tell the user they are not allowed to run foo. */
427: log_auth(validated, 1);
428: }
429: exit(1);
430: } else {
431: /* should never get here */
432: log_auth(validated, 1);
433: exit(1);
434: }
435: exit(0); /* not reached */
436: }
437:
438: /*
439: * Initialize timezone, set umask, fill in ``sudo_user'' struct and
440: * load the ``interfaces'' array.
441: */
442: static int
443: init_vars(sudo_mode)
444: int sudo_mode;
445: {
446: char *p, thost[MAXHOSTNAMELEN];
1.11 millert 447: int nohostname, rval;
1.1 millert 448:
449: /* Sanity check command from user. */
450: if (user_cmnd == NULL && strlen(NewArgv[0]) >= MAXPATHLEN) {
451: (void) fprintf(stderr, "%s: %s: Pathname too long\n", Argv[0],
452: NewArgv[0]);
453: exit(1);
454: }
455:
456: #ifdef HAVE_TZSET
457: (void) tzset(); /* set the timezone if applicable */
458: #endif /* HAVE_TZSET */
459:
460: /* Default value for cmnd and cwd, overridden later. */
461: if (user_cmnd == NULL)
462: user_cmnd = NewArgv[0];
463: (void) strcpy(user_cwd, "unknown");
464:
465: /*
466: * We avoid gethostbyname() if possible since we don't want
467: * sudo to block if DNS or NIS is hosed.
468: * "host" is the (possibly fully-qualified) hostname and
469: * "shost" is the unqualified form of the hostname.
470: */
1.11 millert 471: nohostname = gethostname(thost, sizeof(thost));
472: if (nohostname)
473: user_host = user_shost = "localhost";
474: else {
1.1 millert 475: user_host = estrdup(thost);
1.11 millert 476: if (def_flag(I_FQDN)) {
477: /* Defer call to set_fqdn() until log_error() is safe. */
478: user_shost = user_host;
1.1 millert 479: } else {
1.11 millert 480: if ((p = strchr(user_host, '.'))) {
481: *p = '\0';
482: user_shost = estrdup(user_host);
483: *p = '.';
484: } else {
485: user_shost = user_host;
486: }
1.1 millert 487: }
488: }
489:
490: if ((p = ttyname(STDIN_FILENO)) || (p = ttyname(STDOUT_FILENO))) {
491: if (strncmp(p, _PATH_DEV, sizeof(_PATH_DEV) - 1) == 0)
492: p += sizeof(_PATH_DEV) - 1;
493: user_tty = estrdup(p);
494: } else
495: user_tty = "unknown";
496:
497: /*
498: * Get a local copy of the user's struct passwd with the shadow password
499: * if necessary. It is assumed that euid is 0 at this point so we
500: * can read the shadow passwd file if necessary.
501: */
502: if ((sudo_user.pw = sudo_getpwuid(getuid())) == NULL) {
503: /* Need to make a fake struct passwd for logging to work. */
504: struct passwd pw;
505: char pw_name[MAX_UID_T_LEN + 1];
506:
507: pw.pw_uid = getuid();
508: (void) sprintf(pw_name, "%ld", (long) pw.pw_uid);
509: pw.pw_name = pw_name;
510: sudo_user.pw = &pw;
511:
512: log_error(0, "uid %ld does not exist in the passwd file!",
513: (long) pw.pw_uid);
514: }
1.15 millert 515: if (user_shell == NULL || *user_shell == '\0')
516: user_shell = sudo_user.pw->pw_shell;
1.1 millert 517:
518: /* It is now safe to use log_error() and set_perms() */
519:
520: /*
1.11 millert 521: * Must defer set_fqdn() until it is safe to call log_error()
522: */
523: if (def_flag(I_FQDN))
524: set_fqdn();
525:
526: if (nohostname)
527: log_error(USE_ERRNO|MSG_ONLY, "can't get hostname");
528:
529: /*
1.1 millert 530: * Get current working directory. Try as user, fall back to root.
531: */
532: set_perms(PERM_USER, sudo_mode);
533: if (!getcwd(user_cwd, sizeof(user_cwd))) {
534: set_perms(PERM_ROOT, sudo_mode);
535: if (!getcwd(user_cwd, sizeof(user_cwd))) {
536: (void) fprintf(stderr, "%s: Can't get working directory!\n",
537: Argv[0]);
538: (void) strcpy(user_cwd, "unknown");
539: }
540: } else
541: set_perms(PERM_ROOT, sudo_mode);
542:
543: /*
544: * If we were given the '-s' option (run shell) we need to redo
545: * NewArgv and NewArgc.
546: */
547: if ((sudo_mode & MODE_SHELL)) {
548: char **dst, **src = NewArgv;
549:
550: NewArgv = (char **) emalloc (sizeof(char *) * (++NewArgc + 1));
551: if (user_shell && *user_shell) {
552: NewArgv[0] = user_shell;
553: } else {
554: (void) fprintf(stderr, "%s: Unable to determine shell.", Argv[0]);
555: exit(1);
556: }
557:
558: /* copy the args from Argv */
559: for (dst = NewArgv + 1; (*dst = *src) != NULL; ++src, ++dst)
560: ;
561: }
562:
1.8 millert 563: /* Set login class if applicable. */
564: set_loginclass(sudo_user.pw);
565:
1.1 millert 566: /* Resolve the path and return. */
1.11 millert 567: if ((sudo_mode & MODE_RUN)) {
568: /* XXX - should call this as runas user, not root. */
569: rval = find_path(NewArgv[0], &user_cmnd, user_path);
570: if (rval != FOUND) {
571: /* Failed as root, try as invoking user. */
572: set_perms(PERM_USER, sudo_mode);
573: rval = find_path(NewArgv[0], &user_cmnd, user_path);
574: set_perms(PERM_ROOT, sudo_mode);
575: }
576:
577: /* set user_args */
578: if (NewArgc > 1) {
579: char *to, **from;
580: size_t size;
581:
582: /* If MODE_SHELL not set then NewArgv is contiguous so just count */
583: if (!(sudo_mode & MODE_SHELL)) {
584: size = (size_t) (NewArgv[NewArgc-1] - NewArgv[1]) +
585: strlen(NewArgv[NewArgc-1]) + 1;
586: } else {
587: for (size = 0, from = NewArgv + 1; *from; from++)
588: size += strlen(*from) + 1;
589: }
590:
591: /* alloc and copy. */
592: to = user_args = (char *) emalloc(size);
593: for (from = NewArgv + 1; *from; from++) {
594: (void) strcpy(to, *from);
595: to += strlen(*from);
596: *to++ = ' ';
597: }
598: *--to = '\0';
599: }
600: } else
601: rval = FOUND;
602:
603: return(rval);
1.1 millert 604: }
605:
606: /*
607: * Command line argument parsing, can't use getopt(3).
608: */
609: static int
610: parse_args()
611: {
612: int rval = MODE_RUN; /* what mode is suod to be run in? */
613: int excl = 0; /* exclusive arg, no others allowed */
614:
615: NewArgv = Argv + 1;
616: NewArgc = Argc - 1;
617:
1.2 millert 618: if (NewArgc == 0) { /* no options and no command */
1.3 millert 619: rval |= (MODE_IMPLIED_SHELL | MODE_SHELL);
1.1 millert 620: return(rval);
621: }
622:
623: while (NewArgc > 0 && NewArgv[0][0] == '-') {
624: if (NewArgv[0][1] != '\0' && NewArgv[0][2] != '\0') {
625: (void) fprintf(stderr, "%s: Please use single character options\n",
626: Argv[0]);
627: usage(1);
628: }
629:
630: switch (NewArgv[0][1]) {
631: case 'p':
632: /* Must have an associated prompt. */
633: if (NewArgv[1] == NULL)
634: usage(1);
635:
636: user_prompt = NewArgv[1];
637:
638: /* Shift Argv over and adjust Argc. */
639: NewArgc--;
640: NewArgv++;
641: break;
642: case 'u':
643: /* Must have an associated runas user. */
644: if (NewArgv[1] == NULL)
645: usage(1);
646:
647: user_runas = &NewArgv[1];
648:
649: /* Shift Argv over and adjust Argc. */
650: NewArgc--;
651: NewArgv++;
652: break;
1.8 millert 653: #ifdef HAVE_BSD_AUTH_H
654: case 'a':
655: /* Must have an associated authentication style. */
656: if (NewArgv[1] == NULL)
657: usage(1);
658:
659: login_style = NewArgv[1];
660:
661: /* Shift Argv over and adjust Argc. */
662: NewArgc--;
663: NewArgv++;
664: break;
665: #endif
666: #ifdef HAVE_LOGIN_CAP_H
1.4 millert 667: case 'c':
668: /* Must have an associated login class. */
669: if (NewArgv[1] == NULL)
670: usage(1);
671:
672: login_class = NewArgv[1];
1.11 millert 673: def_flag(I_USE_LOGINCLASS) = TRUE;
1.4 millert 674:
675: /* Shift Argv over and adjust Argc. */
676: NewArgc--;
677: NewArgv++;
678: break;
679: #endif
1.1 millert 680: case 'b':
681: rval |= MODE_BACKGROUND;
682: break;
683: case 'v':
684: rval = MODE_VALIDATE;
685: if (excl && excl != 'v')
686: usage_excl(1);
687: excl = 'v';
688: break;
689: case 'k':
690: rval = MODE_INVALIDATE;
691: if (excl && excl != 'k')
692: usage_excl(1);
693: excl = 'k';
694: break;
695: case 'K':
696: rval = MODE_KILL;
697: if (excl && excl != 'K')
698: usage_excl(1);
699: excl = 'K';
700: break;
701: case 'L':
702: rval = MODE_LISTDEFS;
703: if (excl && excl != 'L')
704: usage_excl(1);
705: excl = 'L';
706: break;
707: case 'l':
708: rval = MODE_LIST;
709: if (excl && excl != 'l')
710: usage_excl(1);
711: excl = 'l';
712: break;
713: case 'V':
714: rval = MODE_VERSION;
715: if (excl && excl != 'V')
716: usage_excl(1);
717: excl = 'V';
718: break;
719: case 'h':
720: rval = MODE_HELP;
721: if (excl && excl != 'h')
722: usage_excl(1);
723: excl = 'h';
724: break;
725: case 's':
726: rval |= MODE_SHELL;
1.2 millert 727: if (excl && excl != 's')
728: usage_excl(1);
729: excl = 's';
1.1 millert 730: break;
731: case 'H':
732: rval |= MODE_RESET_HOME;
733: break;
1.11 millert 734: case 'P':
735: rval |= MODE_PRESERVE_GROUPS;
736: break;
1.4 millert 737: case 'S':
738: tgetpass_flags |= TGP_STDIN;
739: break;
1.1 millert 740: case '-':
741: NewArgc--;
742: NewArgv++;
1.2 millert 743: if (rval == MODE_RUN)
1.3 millert 744: rval |= (MODE_IMPLIED_SHELL | MODE_SHELL);
1.1 millert 745: return(rval);
746: case '\0':
747: (void) fprintf(stderr, "%s: '-' requires an argument\n",
748: Argv[0]);
749: usage(1);
750: default:
751: (void) fprintf(stderr, "%s: Illegal option %s\n", Argv[0],
752: NewArgv[0]);
753: usage(1);
754: }
755: NewArgc--;
756: NewArgv++;
757: }
758:
759: if (NewArgc > 0 && !(rval & MODE_RUN))
760: usage(1);
761:
762: return(rval);
763: }
764:
765: /*
766: * Sanity check sudoers mode/owner/type.
767: * Leaves a file pointer to the sudoers file open in ``fp''.
768: */
769: static void
770: check_sudoers()
771: {
772: struct stat statbuf;
773: int rootstat, i;
774: char c;
775:
776: /*
777: * Fix the mode and group on sudoers file from old default.
778: * Only works if filesystem is readable/writable by root.
779: */
780: if ((rootstat = lstat(_PATH_SUDOERS, &statbuf)) == 0 &&
781: SUDOERS_UID == statbuf.st_uid && SUDOERS_MODE != 0400 &&
782: (statbuf.st_mode & 0007777) == 0400) {
783:
784: if (chmod(_PATH_SUDOERS, SUDOERS_MODE) == 0) {
785: (void) fprintf(stderr, "%s: fixed mode on %s\n",
786: Argv[0], _PATH_SUDOERS);
1.3 millert 787: statbuf.st_mode |= SUDOERS_MODE;
1.1 millert 788: if (statbuf.st_gid != SUDOERS_GID) {
789: if (!chown(_PATH_SUDOERS,(uid_t) -1,SUDOERS_GID)) {
790: (void) fprintf(stderr, "%s: set group on %s\n",
791: Argv[0], _PATH_SUDOERS);
792: statbuf.st_gid = SUDOERS_GID;
793: } else {
794: (void) fprintf(stderr,"%s: Unable to set group on %s: %s\n",
795: Argv[0], _PATH_SUDOERS, strerror(errno));
796: }
797: }
798: } else {
799: (void) fprintf(stderr, "%s: Unable to fix mode on %s: %s\n",
800: Argv[0], _PATH_SUDOERS, strerror(errno));
801: }
802: }
803:
804: /*
805: * Sanity checks on sudoers file. Must be done as sudoers
806: * file owner. We already did a stat as root, so use that
807: * data if we can't stat as sudoers file owner.
808: */
809: set_perms(PERM_SUDOERS, 0);
810:
811: if (rootstat != 0 && lstat(_PATH_SUDOERS, &statbuf) != 0)
812: log_error(USE_ERRNO, "can't stat %s", _PATH_SUDOERS);
813: else if (!S_ISREG(statbuf.st_mode))
814: log_error(0, "%s is not a regular file", _PATH_SUDOERS);
1.9 millert 815: else if (statbuf.st_size == 0)
816: log_error(0, "%s is zero length", _PATH_SUDOERS);
1.1 millert 817: else if ((statbuf.st_mode & 07777) != SUDOERS_MODE)
818: log_error(0, "%s is mode 0%o, should be 0%o", _PATH_SUDOERS,
819: (statbuf.st_mode & 07777), SUDOERS_MODE);
820: else if (statbuf.st_uid != SUDOERS_UID)
821: log_error(0, "%s is owned by uid %ld, should be %d", _PATH_SUDOERS,
822: (long) statbuf.st_uid, SUDOERS_UID);
823: else if (statbuf.st_gid != SUDOERS_GID)
824: log_error(0, "%s is owned by gid %ld, should be %d", _PATH_SUDOERS,
825: (long) statbuf.st_gid, SUDOERS_GID);
826: else {
827: /* Solaris sometimes returns EAGAIN so try 10 times */
828: for (i = 0; i < 10 ; i++) {
829: errno = 0;
830: if ((sudoers_fp = fopen(_PATH_SUDOERS, "r")) == NULL ||
831: fread(&c, sizeof(c), 1, sudoers_fp) != 1) {
832: sudoers_fp = NULL;
833: if (errno != EAGAIN && errno != EWOULDBLOCK)
834: break;
835: } else
836: break;
837: sleep(1);
838: }
839: if (sudoers_fp == NULL)
840: log_error(USE_ERRNO, "can't open %s", _PATH_SUDOERS);
841: }
842:
843: set_perms(PERM_ROOT, 0); /* change back to root */
844: }
845:
846: /*
847: * Close all open files (except std*) and turn off core dumps.
1.11 millert 848: * Also sets the set_perms() pointer to the correct function.
1.1 millert 849: */
850: static void
851: initial_setup()
852: {
853: int fd, maxfd;
854: #ifdef HAVE_SETRLIMIT
855: struct rlimit rl;
856: #endif
1.11 millert 857: sigaction_t sa;
1.1 millert 858:
859: #if defined(RLIMIT_CORE) && !defined(SUDO_DEVEL)
860: /*
861: * Turn off core dumps.
862: */
1.4 millert 863: (void) getrlimit(RLIMIT_CORE, &corelimit);
1.1 millert 864: rl.rlim_cur = rl.rlim_max = 0;
865: (void) setrlimit(RLIMIT_CORE, &rl);
866: #endif /* RLIMIT_CORE */
867:
868: /*
869: * Close any open fd's other than stdin, stdout and stderr.
870: */
871: #ifdef HAVE_SYSCONF
1.3 millert 872: maxfd = sysconf(_SC_OPEN_MAX) - 1;
1.1 millert 873: #else
1.3 millert 874: maxfd = getdtablesize() - 1;
1.1 millert 875: #endif /* HAVE_SYSCONF */
1.3 millert 876: #ifdef RLIMIT_NOFILE
877: if (getrlimit(RLIMIT_NOFILE, &rl) == 0) {
878: if (rl.rlim_max != RLIM_INFINITY && rl.rlim_max <= maxfd)
879: maxfd = rl.rlim_max - 1;
880: }
881: #endif /* RLIMIT_NOFILE */
1.1 millert 882:
883: for (fd = maxfd; fd > STDERR_FILENO; fd--)
884: (void) close(fd);
885:
886: /* Catch children as they die... */
1.11 millert 887: sigemptyset(&sa.sa_mask);
888: sa.sa_flags = SA_RESTART;
1.1 millert 889: sa.sa_handler = reapchild;
890: (void) sigaction(SIGCHLD, &sa, NULL);
1.11 millert 891:
892: /* Set set_perms pointer to the correct function */
1.15 millert 893: #if !defined(NO_SAVED_IDS) && defined(_SC_SAVED_IDS) && defined(_SC_VERSION)
1.11 millert 894: if (sysconf(_SC_SAVED_IDS) == 1 && sysconf(_SC_VERSION) >= 199009)
895: set_perms = set_perms_posix;
896: else
897: #endif
898: set_perms = set_perms_fallback;
1.1 millert 899: }
900:
1.8 millert 901: #ifdef HAVE_LOGIN_CAP_H
902: static void
1.4 millert 903: set_loginclass(pw)
904: struct passwd *pw;
905: {
906: int errflags;
907:
908: /*
909: * Don't make it a fatal error if the user didn't specify the login
910: * class themselves. We do this because if login.conf gets
911: * corrupted we want the admin to be able to use sudo to fix it.
912: */
913: if (login_class)
914: errflags = NO_MAIL|MSG_ONLY;
915: else
916: errflags = NO_MAIL|MSG_ONLY|NO_EXIT;
917:
918: if (login_class && strcmp(login_class, "-") != 0) {
919: if (strcmp(*user_runas, "root") != 0 && user_uid != 0) {
920: (void) fprintf(stderr, "%s: only root can use -c %s\n",
921: Argv[0], login_class);
922: exit(1);
923: }
924: } else {
925: login_class = pw->pw_class;
926: if (!login_class || !*login_class)
927: login_class =
928: (pw->pw_uid == 0) ? LOGIN_DEFROOTCLASS : LOGIN_DEFCLASS;
929: }
930:
931: lc = login_getclass(login_class);
1.10 millert 932: if (!lc || !lc->lc_class || strcmp(lc->lc_class, login_class) != 0) {
1.4 millert 933: log_error(errflags, "unknown login class: %s", login_class);
1.11 millert 934: if (!lc)
935: lc = login_getclass(NULL); /* needed for login_getstyle() later */
1.10 millert 936: }
1.4 millert 937: }
938: #else
1.8 millert 939: static void
1.4 millert 940: set_loginclass(pw)
941: struct passwd *pw;
942: {
943: }
1.8 millert 944: #endif /* HAVE_LOGIN_CAP_H */
1.4 millert 945:
1.1 millert 946: /*
1.2 millert 947: * Look up the fully qualified domain name and set user_host and user_shost.
948: */
949: void
950: set_fqdn()
951: {
952: struct hostent *hp;
953: char *p;
954:
1.14 millert 955: if (!(hp = gethostbyname(user_host))) {
956: log_error(MSG_ONLY|NO_EXIT,
957: "unable to lookup %s via gethostbyname()", user_host);
958: } else {
959: if (user_shost != user_host)
960: free(user_shost);
961: free(user_host);
962: user_host = estrdup(hp->h_name);
1.2 millert 963: }
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: }