Annotation of src/usr.bin/sudo/sudo.c, Revision 1.9
1.1 millert 1: /*
1.3 millert 2: * Copyright (c) 1994-1996,1998-2000 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:
42: #include <stdio.h>
43: #ifdef STDC_HEADERS
44: #include <stdlib.h>
45: #endif /* STDC_HEADERS */
46: #ifdef HAVE_UNISTD_H
47: #include <unistd.h>
48: #endif /* HAVE_UNISTD_H */
49: #ifdef HAVE_STRING_H
50: #include <string.h>
51: #endif /* HAVE_STRING_H */
52: #ifdef HAVE_STRINGS_H
53: #include <strings.h>
54: #endif /* HAVE_STRINGS_H */
55: #include <pwd.h>
56: #include <errno.h>
57: #include <fcntl.h>
58: #include <signal.h>
59: #include <grp.h>
60: #include <time.h>
61: #include <sys/types.h>
62: #include <sys/stat.h>
63: #include <sys/param.h>
64: #include <netinet/in.h>
65: #include <netdb.h>
66: #ifdef HAVE_SETRLIMIT
67: #include <sys/time.h>
68: #include <sys/resource.h>
69: #endif
70: #if defined(HAVE_GETPRPWNAM) && defined(HAVE_SET_AUTH_PARAMETERS)
71: # ifdef __hpux
72: # undef MAXINT
73: # include <hpsecurity.h>
74: # else
75: # include <sys/security.h>
76: # endif /* __hpux */
77: # include <prot.h>
78: #endif /* HAVE_GETPRPWNAM && HAVE_SET_AUTH_PARAMETERS */
1.8 millert 79: #ifdef HAVE_LOGIN_CAP_H
1.4 millert 80: # include <login_cap.h>
81: # ifndef LOGIN_DEFROOTCLASS
82: # define LOGIN_DEFROOTCLASS "daemon"
83: # endif
84: #endif
1.1 millert 85:
86: #include "sudo.h"
87: #include "interfaces.h"
88: #include "version.h"
89:
90: #ifndef STDC_HEADERS
91: extern char *getenv __P((char *));
92: #endif /* STDC_HEADERS */
93:
94: #ifndef lint
1.4 millert 95: static const char rcsid[] = "$Sudo: sudo.c,v 1.278 2000/03/24 20:13:12 millert Exp $";
1.1 millert 96: #endif /* lint */
97:
98: /*
99: * Local type declarations
100: */
101: struct env_table {
102: char *name;
103: int len;
104: };
105:
106: /*
107: * Prototypes
108: */
109: static int parse_args __P((void));
110: static void usage __P((int));
111: static void usage_excl __P((int));
112: static void check_sudoers __P((void));
113: static int init_vars __P((int));
1.8 millert 114: static void set_loginclass __P((struct passwd *));
1.1 millert 115: static void add_env __P((int));
116: static void clean_env __P((char **, struct env_table *));
117: static void initial_setup __P((void));
1.6 millert 118: static struct passwd *get_authpw __P((void));
1.1 millert 119: extern struct passwd *sudo_getpwuid __P((uid_t));
1.6 millert 120: extern struct passwd *sudo_getpwnam __P((const char *));
1.1 millert 121: extern void list_matches __P((void));
122:
123: /*
124: * Globals
125: */
126: int Argc;
127: char **Argv;
128: int NewArgc = 0;
129: char **NewArgv = NULL;
130: struct sudo_user sudo_user;
1.6 millert 131: struct passwd *auth_pw;
1.1 millert 132: FILE *sudoers_fp = NULL;
133: struct interface *interfaces;
134: int num_interfaces;
1.4 millert 135: int tgetpass_flags;
1.1 millert 136: extern int errorlineno;
1.4 millert 137: static char *runas_homedir = NULL; /* XXX */
138: #if defined(RLIMIT_CORE) && !defined(SUDO_DEVEL)
139: static struct rlimit corelimit;
140: #endif /* RLIMIT_CORE */
1.8 millert 141: #ifdef HAVE_LOGIN_CAP_H
142: login_cap_t *lc;
143: #endif /* HAVE_LOGIN_CAP_H */
144: #ifdef HAVE_BSD_AUTH_H
145: char *login_style;
146: #endif /* HAVE_BSD_AUTH_H */
1.1 millert 147:
148: /*
149: * Table of "bad" envariables to remove and len for strncmp()
150: */
151: static struct env_table badenv_table[] = {
152: { "IFS=", 4 },
153: { "LOCALDOMAIN=", 12 },
154: { "RES_OPTIONS=", 12 },
155: { "HOSTALIASES=", 12 },
156: { "LD_", 3 },
157: { "_RLD", 4 },
158: #ifdef __hpux
159: { "SHLIB_PATH=", 11 },
160: #endif /* __hpux */
161: #ifdef _AIX
162: { "LIBPATH=", 8 },
163: #endif /* _AIX */
164: #ifdef HAVE_KERB4
165: { "KRB_CONF", 8 },
166: #endif /* HAVE_KERB4 */
167: #ifdef HAVE_KERB5
168: { "KRB5_CONFIG", 11 },
169: #endif /* HAVE_KERB5 */
170: { "ENV=", 4 },
171: { "BASH_ENV=", 9 },
172: { (char *) NULL, 0 }
173: };
174:
175:
176: int
177: main(argc, argv)
178: int argc;
179: char **argv;
180: {
181: int validated;
182: int fd;
183: int cmnd_status;
184: int sudo_mode;
185: #ifdef POSIX_SIGNALS
186: sigset_t set, oset;
187: #else
188: int omask;
189: #endif /* POSIX_SIGNALS */
190: extern char **environ;
191: extern int printmatches;
192:
193: /* Must be done as the first thing... */
194: #if defined(HAVE_GETPRPWNAM) && defined(HAVE_SET_AUTH_PARAMETERS)
195: (void) set_auth_parameters(argc, argv);
196: # ifdef HAVE_INITPRIVS
197: initprivs();
198: # endif
199: #endif /* HAVE_GETPRPWNAM && HAVE_SET_AUTH_PARAMETERS */
200:
201: Argv = argv;
202: Argc = argc;
203:
204: if (geteuid() != 0) {
205: (void) fprintf(stderr, "Sorry, %s must be setuid root.\n", Argv[0]);
206: exit(1);
207: }
208:
209: /*
210: * Block signals so the user cannot interrupt us at some point and
211: * avoid the logging.
212: */
213: #ifdef POSIX_SIGNALS
214: (void) sigemptyset(&set);
215: (void) sigaddset(&set, SIGINT);
216: (void) sigaddset(&set, SIGQUIT);
217: (void) sigaddset(&set, SIGTSTP);
218: (void) sigprocmask(SIG_BLOCK, &set, &oset);
219: #else
220: omask = sigblock(sigmask(SIGINT)|sigmask(SIGQUIT)|sigmask(SIGTSTP));
221: #endif /* POSIX_SIGNALS */
222:
223: /*
224: * Setup signal handlers, turn off core dumps, and close open files.
225: */
226: initial_setup();
227:
228: /*
229: * Set the prompt based on $SUDO_PROMPT (can be overridden by `-p')
230: */
231: user_prompt = getenv("SUDO_PROMPT");
232:
233: /* Parse our arguments. */
234: sudo_mode = parse_args();
235:
236: /* Setup defaults data structures. */
237: init_defaults();
238:
239: if (sudo_mode & MODE_SHELL)
240: user_cmnd = "shell";
241: else
242: switch (sudo_mode) {
243: case MODE_VERSION:
244: (void) printf("Sudo version %s\n", version);
245: if (getuid() == 0) {
246: putchar('\n');
247: dump_auth_methods();
248: dump_defaults();
249: }
250: exit(0);
251: break;
252: case MODE_HELP:
253: usage(0);
254: break;
255: case MODE_VALIDATE:
256: user_cmnd = "validate";
257: break;
258: case MODE_KILL:
259: case MODE_INVALIDATE:
260: user_cmnd = "kill";
261: break;
262: case MODE_LISTDEFS:
263: list_options();
264: exit(0);
265: break;
266: case MODE_LIST:
267: user_cmnd = "list";
268: printmatches = 1;
269: break;
270: }
271:
272: /* Must have a command to run... */
273: if (user_cmnd == NULL && NewArgc == 0)
274: usage(1);
275:
276: clean_env(environ, badenv_table);
277:
278: cmnd_status = init_vars(sudo_mode);
279:
280: /* At this point, ruid == euid == 0 */
281:
282: check_sudoers(); /* check mode/owner on _PATH_SUDOERS */
283:
1.2 millert 284: add_env(!(sudo_mode & MODE_SHELL)); /* add in SUDO_* envariables */
285:
286: /* Validate the user but don't search for pseudo-commands. */
1.7 millert 287: validated = sudoers_lookup(sudo_mode);
1.2 millert 288:
289: /* This goes after the sudoers parse since we honor sudoers options. */
1.1 millert 290: if (sudo_mode == MODE_KILL || sudo_mode == MODE_INVALIDATE) {
291: remove_timestamp((sudo_mode == MODE_KILL));
292: exit(0);
293: }
294:
295: if (validated & VALIDATE_ERROR)
296: log_error(0, "parse error in %s near line %d", _PATH_SUDOERS,
297: errorlineno);
298:
299: /* Is root even allowed to run sudo? */
300: if (user_uid == 0 && !def_flag(I_ROOT_SUDO)) {
301: (void) fputs("You are already root, you don't need to use sudo.\n",
302: stderr);
303: exit(1);
304: }
305:
1.3 millert 306: /* If no command line args and "set_home" is not set, error out. */
307: if ((sudo_mode & MODE_IMPLIED_SHELL) && !def_flag(I_SHELL_NOARGS))
308: usage(1);
309:
1.2 millert 310: /* May need to set $HOME to target user. */
311: if ((sudo_mode & MODE_SHELL) && def_flag(I_SET_HOME))
312: sudo_mode |= MODE_RESET_HOME;
313:
1.1 millert 314: /* Bail if a tty is required and we don't have one. */
315: if (def_flag(I_REQUIRETTY)) {
316: if ((fd = open(_PATH_TTY, O_RDWR|O_NOCTTY)) == -1)
317: log_error(NO_MAIL, "sorry, you must have a tty to run sudo");
318: else
319: (void) close(fd);
320: }
321:
1.6 millert 322: /* Fill in passwd struct based on user we are authenticating as. */
323: auth_pw = get_authpw();
1.4 millert 324:
1.1 millert 325: /* Require a password unless the NOPASS tag was set. */
326: if (!(validated & FLAG_NOPASS))
327: check_user();
328:
329: if (validated & VALIDATE_OK) {
330: /* Finally tell the user if the command did not exist. */
331: if (cmnd_status == NOT_FOUND_DOT) {
332: (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);
333: exit(1);
334: } else if (cmnd_status == NOT_FOUND) {
335: (void) fprintf(stderr, "%s: %s: command not found\n", Argv[0],
336: user_cmnd);
337: exit(1);
338: }
339:
340: log_auth(validated, 1);
341: if (sudo_mode == MODE_VALIDATE)
342: exit(0);
343: else if (sudo_mode == MODE_LIST) {
344: list_matches();
345: exit(0);
346: }
347:
348: /* This *must* have been set if we got a match but... */
349: if (safe_cmnd == NULL) {
350: log_error(MSG_ONLY,
1.6 millert 351: "internal error, cmnd_safe never got set for %s; %s",
1.1 millert 352: user_cmnd,
353: "please report this error at http://courtesan.com/sudo/bugs/");
354: }
355:
356: if (def_ival(I_LOGFACSTR))
357: closelog();
358:
359: /* Reset signal mask before we exec. */
360: #ifdef POSIX_SIGNALS
361: (void) sigprocmask(SIG_SETMASK, &oset, NULL);
362: #else
363: (void) sigsetmask(omask);
364: #endif /* POSIX_SIGNALS */
365:
366: /* Override user's umask if configured to do so. */
367: if (def_ival(I_UMASK) != 0777)
368: (void) umask(def_mode(I_UMASK));
369:
370: /* Replace the PATH envariable with a secure one. */
371: if (def_str(I_SECURE_PATH) && !user_is_exempt())
1.8 millert 372: sudo_setenv("PATH", def_str(I_SECURE_PATH));
1.1 millert 373:
1.4 millert 374: /* Restore coredumpsize resource limit. */
375: #if defined(RLIMIT_CORE) && !defined(SUDO_DEVEL)
376: (void) setrlimit(RLIMIT_CORE, &corelimit);
377: #endif /* RLIMIT_CORE */
378:
379: /* Become specified user or root. */
380: set_perms(PERM_RUNAS, sudo_mode);
1.5 millert 381:
382: /* Set $HOME for `sudo -H'. Only valid at PERM_RUNAS. */
383: if ((sudo_mode & MODE_RESET_HOME) && runas_homedir)
1.8 millert 384: sudo_setenv("HOME", runas_homedir);
1.4 millert 385:
1.1 millert 386: #ifndef PROFILING
387: if ((sudo_mode & MODE_BACKGROUND) && fork() > 0)
388: exit(0);
389: else
390: EXEC(safe_cmnd, NewArgv); /* run the command */
391: #else
392: exit(0);
393: #endif /* PROFILING */
394: /*
395: * If we got here then the exec() failed...
396: */
397: (void) fprintf(stderr, "%s: unable to exec %s: %s\n",
398: Argv[0], safe_cmnd, strerror(errno));
399: exit(-1);
400: } else if ((validated & FLAG_NO_USER) || (validated & FLAG_NO_HOST)) {
401: log_auth(validated, 1);
402: exit(1);
403: } else if (validated & VALIDATE_NOT_OK) {
404: if (def_flag(I_PATH_INFO)) {
405: /*
406: * We'd like to not leak path info at all here, but that can
407: * *really* confuse the users. To really close the leak we'd
408: * have to say "not allowed to run foo" even when the problem
409: * is just "no foo in path" since the user can trivially set
410: * their path to just contain a single dir.
411: */
412: log_auth(validated,
413: !(cmnd_status == NOT_FOUND_DOT || cmnd_status == NOT_FOUND));
414: if (cmnd_status == NOT_FOUND)
415: (void) fprintf(stderr, "%s: %s: command not found\n", Argv[0],
416: user_cmnd);
417: else if (cmnd_status == NOT_FOUND_DOT)
418: (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);
419: } else {
420: /* Just tell the user they are not allowed to run foo. */
421: log_auth(validated, 1);
422: }
423: exit(1);
424: } else {
425: /* should never get here */
426: log_auth(validated, 1);
427: exit(1);
428: }
429: exit(0); /* not reached */
430: }
431:
432: /*
433: * Initialize timezone, set umask, fill in ``sudo_user'' struct and
434: * load the ``interfaces'' array.
435: */
436: static int
437: init_vars(sudo_mode)
438: int sudo_mode;
439: {
440: char *p, thost[MAXHOSTNAMELEN];
441:
442: /* Sanity check command from user. */
443: if (user_cmnd == NULL && strlen(NewArgv[0]) >= MAXPATHLEN) {
444: (void) fprintf(stderr, "%s: %s: Pathname too long\n", Argv[0],
445: NewArgv[0]);
446: exit(1);
447: }
448:
449: #ifdef HAVE_TZSET
450: (void) tzset(); /* set the timezone if applicable */
451: #endif /* HAVE_TZSET */
452:
453: /* Default value for cmnd and cwd, overridden later. */
454: if (user_cmnd == NULL)
455: user_cmnd = NewArgv[0];
456: (void) strcpy(user_cwd, "unknown");
457:
458: /*
459: * We avoid gethostbyname() if possible since we don't want
460: * sudo to block if DNS or NIS is hosed.
461: * "host" is the (possibly fully-qualified) hostname and
462: * "shost" is the unqualified form of the hostname.
463: */
464: if ((gethostname(thost, sizeof(thost)))) {
465: user_host = "localhost";
466: log_error(USE_ERRNO|MSG_ONLY, "can't get hostname");
467: } else
468: user_host = estrdup(thost);
1.2 millert 469: if (def_flag(I_FQDN))
470: set_fqdn();
471: else {
472: if ((p = strchr(user_host, '.'))) {
473: *p = '\0';
474: user_shost = estrdup(user_host);
475: *p = '.';
1.1 millert 476: } else {
1.2 millert 477: user_shost = user_host;
1.1 millert 478: }
479: }
480:
481: if ((p = ttyname(STDIN_FILENO)) || (p = ttyname(STDOUT_FILENO))) {
482: if (strncmp(p, _PATH_DEV, sizeof(_PATH_DEV) - 1) == 0)
483: p += sizeof(_PATH_DEV) - 1;
484: user_tty = estrdup(p);
485: } else
486: user_tty = "unknown";
487:
488: /*
489: * Get a local copy of the user's struct passwd with the shadow password
490: * if necessary. It is assumed that euid is 0 at this point so we
491: * can read the shadow passwd file if necessary.
492: */
493: if ((sudo_user.pw = sudo_getpwuid(getuid())) == NULL) {
494: /* Need to make a fake struct passwd for logging to work. */
495: struct passwd pw;
496: char pw_name[MAX_UID_T_LEN + 1];
497:
498: pw.pw_uid = getuid();
499: (void) sprintf(pw_name, "%ld", (long) pw.pw_uid);
500: pw.pw_name = pw_name;
501: sudo_user.pw = &pw;
502:
503: log_error(0, "uid %ld does not exist in the passwd file!",
504: (long) pw.pw_uid);
505: }
506:
507: /* It is now safe to use log_error() and set_perms() */
508:
509: /*
510: * Get current working directory. Try as user, fall back to root.
511: */
512: set_perms(PERM_USER, sudo_mode);
513: if (!getcwd(user_cwd, sizeof(user_cwd))) {
514: set_perms(PERM_ROOT, sudo_mode);
515: if (!getcwd(user_cwd, sizeof(user_cwd))) {
516: (void) fprintf(stderr, "%s: Can't get working directory!\n",
517: Argv[0]);
518: (void) strcpy(user_cwd, "unknown");
519: }
520: } else
521: set_perms(PERM_ROOT, sudo_mode);
522:
523: /*
524: * Load the list of local ip addresses and netmasks into
525: * the interfaces array.
526: */
527: load_interfaces();
528:
529: /*
530: * If we were given the '-s' option (run shell) we need to redo
531: * NewArgv and NewArgc.
532: */
533: if ((sudo_mode & MODE_SHELL)) {
534: char **dst, **src = NewArgv;
535:
536: NewArgv = (char **) emalloc (sizeof(char *) * (++NewArgc + 1));
537: if (user_shell && *user_shell) {
538: NewArgv[0] = user_shell;
539: } else {
540: (void) fprintf(stderr, "%s: Unable to determine shell.", Argv[0]);
541: exit(1);
542: }
543:
544: /* copy the args from Argv */
545: for (dst = NewArgv + 1; (*dst = *src) != NULL; ++src, ++dst)
546: ;
547: }
548:
1.8 millert 549: /* Set login class if applicable. */
550: set_loginclass(sudo_user.pw);
551:
1.1 millert 552: /* Resolve the path and return. */
553: if ((sudo_mode & MODE_RUN))
554: return(find_path(NewArgv[0], &user_cmnd));
555: else
556: return(FOUND);
557: }
558:
559: /*
560: * Command line argument parsing, can't use getopt(3).
561: */
562: static int
563: parse_args()
564: {
565: int rval = MODE_RUN; /* what mode is suod to be run in? */
566: int excl = 0; /* exclusive arg, no others allowed */
567:
568: NewArgv = Argv + 1;
569: NewArgc = Argc - 1;
570:
1.2 millert 571: if (NewArgc == 0) { /* no options and no command */
1.3 millert 572: rval |= (MODE_IMPLIED_SHELL | MODE_SHELL);
1.1 millert 573: return(rval);
574: }
575:
576: while (NewArgc > 0 && NewArgv[0][0] == '-') {
577: if (NewArgv[0][1] != '\0' && NewArgv[0][2] != '\0') {
578: (void) fprintf(stderr, "%s: Please use single character options\n",
579: Argv[0]);
580: usage(1);
581: }
582:
583: switch (NewArgv[0][1]) {
584: case 'p':
585: /* Must have an associated prompt. */
586: if (NewArgv[1] == NULL)
587: usage(1);
588:
589: user_prompt = NewArgv[1];
590:
591: /* Shift Argv over and adjust Argc. */
592: NewArgc--;
593: NewArgv++;
594: break;
595: case 'u':
596: /* Must have an associated runas user. */
597: if (NewArgv[1] == NULL)
598: usage(1);
599:
600: user_runas = &NewArgv[1];
601:
602: /* Shift Argv over and adjust Argc. */
603: NewArgc--;
604: NewArgv++;
605: break;
1.8 millert 606: #ifdef HAVE_BSD_AUTH_H
607: case 'a':
608: /* Must have an associated authentication style. */
609: if (NewArgv[1] == NULL)
610: usage(1);
611:
612: login_style = NewArgv[1];
613:
614: /* Shift Argv over and adjust Argc. */
615: NewArgc--;
616: NewArgv++;
617: break;
618: #endif
619: #ifdef HAVE_LOGIN_CAP_H
1.4 millert 620: case 'c':
621: /* Must have an associated login class. */
622: if (NewArgv[1] == NULL)
623: usage(1);
624:
625: login_class = NewArgv[1];
626: def_flag(I_LOGINCLASS) = TRUE;
627:
628: /* Shift Argv over and adjust Argc. */
629: NewArgc--;
630: NewArgv++;
631: break;
632: #endif
1.1 millert 633: case 'b':
634: rval |= MODE_BACKGROUND;
635: break;
636: case 'v':
637: rval = MODE_VALIDATE;
638: if (excl && excl != 'v')
639: usage_excl(1);
640: excl = 'v';
641: break;
642: case 'k':
643: rval = MODE_INVALIDATE;
644: if (excl && excl != 'k')
645: usage_excl(1);
646: excl = 'k';
647: break;
648: case 'K':
649: rval = MODE_KILL;
650: if (excl && excl != 'K')
651: usage_excl(1);
652: excl = 'K';
653: break;
654: case 'L':
655: rval = MODE_LISTDEFS;
656: if (excl && excl != 'L')
657: usage_excl(1);
658: excl = 'L';
659: break;
660: case 'l':
661: rval = MODE_LIST;
662: if (excl && excl != 'l')
663: usage_excl(1);
664: excl = 'l';
665: break;
666: case 'V':
667: rval = MODE_VERSION;
668: if (excl && excl != 'V')
669: usage_excl(1);
670: excl = 'V';
671: break;
672: case 'h':
673: rval = MODE_HELP;
674: if (excl && excl != 'h')
675: usage_excl(1);
676: excl = 'h';
677: break;
678: case 's':
679: rval |= MODE_SHELL;
1.2 millert 680: if (excl && excl != 's')
681: usage_excl(1);
682: excl = 's';
1.1 millert 683: break;
684: case 'H':
685: rval |= MODE_RESET_HOME;
686: break;
1.4 millert 687: case 'S':
688: tgetpass_flags |= TGP_STDIN;
689: break;
1.1 millert 690: case '-':
691: NewArgc--;
692: NewArgv++;
1.2 millert 693: if (rval == MODE_RUN)
1.3 millert 694: rval |= (MODE_IMPLIED_SHELL | MODE_SHELL);
1.1 millert 695: return(rval);
696: case '\0':
697: (void) fprintf(stderr, "%s: '-' requires an argument\n",
698: Argv[0]);
699: usage(1);
700: default:
701: (void) fprintf(stderr, "%s: Illegal option %s\n", Argv[0],
702: NewArgv[0]);
703: usage(1);
704: }
705: NewArgc--;
706: NewArgv++;
707: }
708:
709: if (NewArgc > 0 && !(rval & MODE_RUN))
710: usage(1);
711:
712: return(rval);
713: }
714:
715: /*
716: * Add sudo-specific variables into the environment.
717: * Sets ``cmnd_args'' as a side effect.
718: */
719: static void
720: add_env(contiguous)
721: int contiguous;
722: {
723: char idstr[MAX_UID_T_LEN + 1];
724: size_t size;
725: char *buf;
726:
727: /* Add the SUDO_COMMAND envariable (cmnd + args). */
728: size = strlen(user_cmnd) + 1;
729: if (NewArgc > 1) {
730: char *to, **from;
731:
732: if (contiguous) {
733: size += (size_t) (NewArgv[NewArgc-1] - NewArgv[1]) +
734: strlen(NewArgv[NewArgc-1]) + 1;
735: } else {
736: for (from = &NewArgv[1]; *from; from++)
737: size += strlen(*from) + 1;
738: }
739:
740: buf = (char *) emalloc(size);
741:
742: /*
743: * Copy the command and it's arguments info buf.
744: */
745: (void) strcpy(buf, user_cmnd);
746: to = buf + strlen(user_cmnd);
747: for (from = &NewArgv[1]; *from; from++) {
748: *to++ = ' ';
749: (void) strcpy(to, *from);
750: to += strlen(*from);
751: }
752: } else {
753: buf = user_cmnd;
754: }
1.8 millert 755: sudo_setenv("SUDO_COMMAND", buf);
1.1 millert 756: if (NewArgc > 1)
757: free(buf);
758:
759: /* Grab a pointer to the flat arg string from the environment. */
760: if (NewArgc > 1 && (user_args = getenv("SUDO_COMMAND"))) {
761: if ((user_args = strchr(user_args, ' ')))
762: user_args++;
763: else
764: user_args = NULL;
765: }
766:
1.8 millert 767: /* Add the SUDO_USER, SUDO_UID, SUDO_GID environment variables. */
768: sudo_setenv("SUDO_USER", user_name);
1.1 millert 769: (void) sprintf(idstr, "%ld", (long) user_uid);
1.8 millert 770: sudo_setenv("SUDO_UID", idstr);
1.1 millert 771: (void) sprintf(idstr, "%ld", (long) user_gid);
1.8 millert 772: sudo_setenv("SUDO_GID", idstr);
1.1 millert 773:
774: /* Set PS1 if SUDO_PS1 is set. */
775: if ((buf = getenv("SUDO_PS1")))
1.8 millert 776: sudo_setenv("PS1", buf);
1.1 millert 777: }
778:
779: /*
780: * Sanity check sudoers mode/owner/type.
781: * Leaves a file pointer to the sudoers file open in ``fp''.
782: */
783: static void
784: check_sudoers()
785: {
786: struct stat statbuf;
787: int rootstat, i;
788: char c;
789:
790: /*
791: * Fix the mode and group on sudoers file from old default.
792: * Only works if filesystem is readable/writable by root.
793: */
794: if ((rootstat = lstat(_PATH_SUDOERS, &statbuf)) == 0 &&
795: SUDOERS_UID == statbuf.st_uid && SUDOERS_MODE != 0400 &&
796: (statbuf.st_mode & 0007777) == 0400) {
797:
798: if (chmod(_PATH_SUDOERS, SUDOERS_MODE) == 0) {
799: (void) fprintf(stderr, "%s: fixed mode on %s\n",
800: Argv[0], _PATH_SUDOERS);
1.3 millert 801: statbuf.st_mode |= SUDOERS_MODE;
1.1 millert 802: if (statbuf.st_gid != SUDOERS_GID) {
803: if (!chown(_PATH_SUDOERS,(uid_t) -1,SUDOERS_GID)) {
804: (void) fprintf(stderr, "%s: set group on %s\n",
805: Argv[0], _PATH_SUDOERS);
806: statbuf.st_gid = SUDOERS_GID;
807: } else {
808: (void) fprintf(stderr,"%s: Unable to set group on %s: %s\n",
809: Argv[0], _PATH_SUDOERS, strerror(errno));
810: }
811: }
812: } else {
813: (void) fprintf(stderr, "%s: Unable to fix mode on %s: %s\n",
814: Argv[0], _PATH_SUDOERS, strerror(errno));
815: }
816: }
817:
818: /*
819: * Sanity checks on sudoers file. Must be done as sudoers
820: * file owner. We already did a stat as root, so use that
821: * data if we can't stat as sudoers file owner.
822: */
823: set_perms(PERM_SUDOERS, 0);
824:
825: if (rootstat != 0 && lstat(_PATH_SUDOERS, &statbuf) != 0)
826: log_error(USE_ERRNO, "can't stat %s", _PATH_SUDOERS);
827: else if (!S_ISREG(statbuf.st_mode))
828: log_error(0, "%s is not a regular file", _PATH_SUDOERS);
1.9 ! millert 829: else if (statbuf.st_size == 0)
! 830: log_error(0, "%s is zero length", _PATH_SUDOERS);
1.1 millert 831: else if ((statbuf.st_mode & 07777) != SUDOERS_MODE)
832: log_error(0, "%s is mode 0%o, should be 0%o", _PATH_SUDOERS,
833: (statbuf.st_mode & 07777), SUDOERS_MODE);
834: else if (statbuf.st_uid != SUDOERS_UID)
835: log_error(0, "%s is owned by uid %ld, should be %d", _PATH_SUDOERS,
836: (long) statbuf.st_uid, SUDOERS_UID);
837: else if (statbuf.st_gid != SUDOERS_GID)
838: log_error(0, "%s is owned by gid %ld, should be %d", _PATH_SUDOERS,
839: (long) statbuf.st_gid, SUDOERS_GID);
840: else {
841: /* Solaris sometimes returns EAGAIN so try 10 times */
842: for (i = 0; i < 10 ; i++) {
843: errno = 0;
844: if ((sudoers_fp = fopen(_PATH_SUDOERS, "r")) == NULL ||
845: fread(&c, sizeof(c), 1, sudoers_fp) != 1) {
846: sudoers_fp = NULL;
847: if (errno != EAGAIN && errno != EWOULDBLOCK)
848: break;
849: } else
850: break;
851: sleep(1);
852: }
853: if (sudoers_fp == NULL)
854: log_error(USE_ERRNO, "can't open %s", _PATH_SUDOERS);
855: }
856:
857: set_perms(PERM_ROOT, 0); /* change back to root */
858: }
859:
860: /*
861: * Remove environment variables that match the entries in badenv_table.
862: */
863: static void
864: clean_env(envp, badenv_table)
865: char **envp;
866: struct env_table *badenv_table;
867: {
868: struct env_table *bad;
869: char **cur;
870:
871: /*
872: * Remove any envars that match entries in badenv_table.
873: */
874: for (cur = envp; *cur; cur++) {
875: for (bad = badenv_table; bad->name; bad++) {
876: if (strncmp(*cur, bad->name, bad->len) == 0) {
877: /* Got a match so remove it. */
878: char **move;
879:
880: for (move = cur; *move; move++)
881: *move = *(move + 1);
882:
883: cur--;
884:
885: break;
886: }
887: }
888: }
889: }
890:
891: /*
892: * Set real and effective uids and gids based on perm.
893: */
894: void
895: set_perms(perm, sudo_mode)
896: int perm;
897: int sudo_mode;
898: {
899: struct passwd *pw;
900:
901: /*
902: * First, set real & effective uids to root.
903: * If perm is PERM_ROOT then we don't need to do anything else.
904: */
905: if (setuid(0)) {
906: perror("setuid(0)");
907: exit(1);
908: }
909:
910: switch (perm) {
911: case PERM_USER:
912: (void) setgid(user_gid);
913:
914: if (seteuid(user_uid)) {
915: perror("seteuid(user_uid)");
916: exit(1);
917: }
918: break;
919:
920: case PERM_FULL_USER:
921: (void) setgid(user_gid);
922:
923: if (setuid(user_uid)) {
924: perror("setuid(user_uid)");
925: exit(1);
926: }
927: break;
928:
929: case PERM_RUNAS:
930: /* XXX - add group/gid support */
931: if (**user_runas == '#') {
932: if (setuid(atoi(*user_runas + 1))) {
933: (void) fprintf(stderr,
934: "%s: cannot set uid to %s: %s\n",
935: Argv[0], *user_runas, strerror(errno));
936: exit(1);
937: }
938: } else {
939: if (!(pw = getpwnam(*user_runas))) {
940: (void) fprintf(stderr,
941: "%s: no passwd entry for %s!\n",
942: Argv[0], *user_runas);
943: exit(1);
944: }
945:
946: /* Set $USER and $LOGNAME to target user */
1.4 millert 947: if (def_flag(I_LOGNAME)) {
1.8 millert 948: sudo_setenv("USER", pw->pw_name);
949: sudo_setenv("LOGNAME", pw->pw_name);
1.1 millert 950: }
1.4 millert 951:
1.8 millert 952: #ifdef HAVE_LOGIN_CAP_H
1.4 millert 953: if (def_flag(I_LOGINCLASS)) {
954: /*
955: * setusercontext() will set uid/gid/etc
956: * for us so no need to do it below.
957: */
1.8 millert 958: if (setusercontext(lc, pw, pw->pw_uid,
959: LOGIN_SETUSER|LOGIN_SETGROUP|LOGIN_SETRESOURCES|LOGIN_SETPRIORITY))
960: log_error(
961: NO_MAIL|USE_ERRNO|MSG_ONLY,
962: "setusercontext() failed for login class %s",
963: login_class);
964: else
1.4 millert 965: break;
1.1 millert 966: }
1.8 millert 967: #endif /* HAVE_LOGIN_CAP_H */
1.1 millert 968:
969: if (setgid(pw->pw_gid)) {
970: (void) fprintf(stderr,
971: "%s: cannot set gid to %ld: %s\n",
972: Argv[0], (long) pw->pw_gid,
973: strerror(errno));
974: exit(1);
975: }
1.2 millert 976: #ifdef HAVE_INITGROUPS
1.1 millert 977: /*
978: * Initialize group vector only if are
979: * going to run as a non-root user.
980: */
981: if (strcmp(*user_runas, "root") != 0 &&
982: initgroups(*user_runas, pw->pw_gid)
983: == -1) {
984: (void) fprintf(stderr,
985: "%s: cannot set group vector: %s\n",
986: Argv[0], strerror(errno));
987: exit(1);
988: }
1.2 millert 989: #endif /* HAVE_INITGROUPS */
1.1 millert 990: if (setuid(pw->pw_uid)) {
991: (void) fprintf(stderr,
992: "%s: cannot set uid to %ld: %s\n",
993: Argv[0], (long) pw->pw_uid,
994: strerror(errno));
995: exit(1);
996: }
997: if (sudo_mode & MODE_RESET_HOME)
998: runas_homedir = pw->pw_dir;
999: }
1000: break;
1001:
1002: case PERM_SUDOERS:
1003: if (setgid(SUDOERS_GID)) {
1004: perror("setgid(SUDOERS_GID)");
1005: exit(1);
1006: }
1007:
1008: /*
1009: * If SUDOERS_UID == 0 and SUDOERS_MODE
1010: * is group readable we use a non-zero
1011: * uid in order to avoid NFS lossage.
1012: * Using uid 1 is a bit bogus but should
1013: * work on all OS's.
1014: */
1015: if (SUDOERS_UID == 0) {
1016: if ((SUDOERS_MODE & 040) && seteuid(1)) {
1017: perror("seteuid(1)");
1018: exit(1);
1019: }
1020: } else {
1021: if (seteuid(SUDOERS_UID)) {
1022: perror("seteuid(SUDOERS_UID)");
1023: exit(1);
1024: }
1025: }
1026: break;
1027: }
1028: }
1029:
1030: /*
1031: * Close all open files (except std*) and turn off core dumps.
1032: */
1033: static void
1034: initial_setup()
1035: {
1036: int fd, maxfd;
1037: #ifdef HAVE_SETRLIMIT
1038: struct rlimit rl;
1039: #endif
1040: #ifdef POSIX_SIGNALS
1041: struct sigaction sa;
1042: #endif
1043:
1044: #if defined(RLIMIT_CORE) && !defined(SUDO_DEVEL)
1045: /*
1046: * Turn off core dumps.
1047: */
1.4 millert 1048: (void) getrlimit(RLIMIT_CORE, &corelimit);
1.1 millert 1049: rl.rlim_cur = rl.rlim_max = 0;
1050: (void) setrlimit(RLIMIT_CORE, &rl);
1051: #endif /* RLIMIT_CORE */
1052:
1053: /*
1054: * Close any open fd's other than stdin, stdout and stderr.
1055: */
1056: #ifdef HAVE_SYSCONF
1.3 millert 1057: maxfd = sysconf(_SC_OPEN_MAX) - 1;
1.1 millert 1058: #else
1.3 millert 1059: maxfd = getdtablesize() - 1;
1.1 millert 1060: #endif /* HAVE_SYSCONF */
1.3 millert 1061: #ifdef RLIMIT_NOFILE
1062: if (getrlimit(RLIMIT_NOFILE, &rl) == 0) {
1063: if (rl.rlim_max != RLIM_INFINITY && rl.rlim_max <= maxfd)
1064: maxfd = rl.rlim_max - 1;
1065: }
1066: #endif /* RLIMIT_NOFILE */
1.1 millert 1067:
1068: for (fd = maxfd; fd > STDERR_FILENO; fd--)
1069: (void) close(fd);
1070:
1071: /* Catch children as they die... */
1072: #ifdef POSIX_SIGNALS
1073: (void) memset((VOID *)&sa, 0, sizeof(sa));
1074: sa.sa_handler = reapchild;
1075: (void) sigaction(SIGCHLD, &sa, NULL);
1076: #else
1077: (void) signal(SIGCHLD, reapchild);
1078: #endif /* POSIX_SIGNALS */
1079: }
1080:
1.8 millert 1081: #ifdef HAVE_LOGIN_CAP_H
1082: static void
1.4 millert 1083: set_loginclass(pw)
1084: struct passwd *pw;
1085: {
1086: int errflags;
1087:
1088: /*
1089: * Don't make it a fatal error if the user didn't specify the login
1090: * class themselves. We do this because if login.conf gets
1091: * corrupted we want the admin to be able to use sudo to fix it.
1092: */
1093: if (login_class)
1094: errflags = NO_MAIL|MSG_ONLY;
1095: else
1096: errflags = NO_MAIL|MSG_ONLY|NO_EXIT;
1097:
1098: if (login_class && strcmp(login_class, "-") != 0) {
1099: if (strcmp(*user_runas, "root") != 0 && user_uid != 0) {
1100: (void) fprintf(stderr, "%s: only root can use -c %s\n",
1101: Argv[0], login_class);
1102: exit(1);
1103: }
1104: } else {
1105: login_class = pw->pw_class;
1106: if (!login_class || !*login_class)
1107: login_class =
1108: (pw->pw_uid == 0) ? LOGIN_DEFROOTCLASS : LOGIN_DEFCLASS;
1109: }
1110:
1111: lc = login_getclass(login_class);
1.8 millert 1112: if (!lc || !lc->lc_class || strcmp(lc->lc_class, login_class) != 0)
1.4 millert 1113: log_error(errflags, "unknown login class: %s", login_class);
1114: }
1115: #else
1.8 millert 1116: static void
1.4 millert 1117: set_loginclass(pw)
1118: struct passwd *pw;
1119: {
1120: }
1.8 millert 1121: #endif /* HAVE_LOGIN_CAP_H */
1.4 millert 1122:
1.1 millert 1123: /*
1.2 millert 1124: * Look up the fully qualified domain name and set user_host and user_shost.
1125: */
1126: void
1127: set_fqdn()
1128: {
1129: struct hostent *hp;
1130: char *p;
1131:
1132: if (def_flag(I_FQDN)) {
1133: if (!(hp = gethostbyname(user_host))) {
1134: log_error(USE_ERRNO|MSG_ONLY|NO_EXIT,
1135: "unable to lookup %s via gethostbyname()", user_host);
1136: } else {
1137: free(user_host);
1138: user_host = estrdup(hp->h_name);
1139: }
1140: }
1141: if (user_shost != user_host)
1142: free(user_shost);
1143: if ((p = strchr(user_host, '.'))) {
1144: *p = '\0';
1145: user_shost = estrdup(user_host);
1146: *p = '.';
1147: } else {
1148: user_shost = user_host;
1149: }
1150: }
1151:
1152: /*
1.6 millert 1153: * Get passwd entry for the user we are going to authenticate as.
1154: * By default, this is the user invoking sudo...
1.4 millert 1155: */
1.6 millert 1156: static struct passwd *
1157: get_authpw()
1.4 millert 1158: {
1159: struct passwd *pw;
1160:
1161: if (def_ival(I_ROOTPW)) {
1.6 millert 1162: if ((pw = sudo_getpwuid(0)) == NULL)
1.4 millert 1163: log_error(0, "uid 0 does not exist in the passwd file!");
1164: } else if (def_ival(I_RUNASPW)) {
1.6 millert 1165: if ((pw = sudo_getpwnam(def_str(I_RUNAS_DEF))) == NULL)
1.4 millert 1166: log_error(0, "user %s does not exist in the passwd file!",
1167: def_str(I_RUNAS_DEF));
1168: } else if (def_ival(I_TARGETPW)) {
1169: if (**user_runas == '#') {
1.6 millert 1170: if ((pw = sudo_getpwuid(atoi(*user_runas + 1))) == NULL)
1.4 millert 1171: log_error(0, "uid %s does not exist in the passwd file!",
1172: user_runas);
1173: } else {
1.6 millert 1174: if ((pw = sudo_getpwnam(*user_runas)) == NULL)
1.4 millert 1175: log_error(0, "user %s does not exist in the passwd file!",
1176: user_runas);
1177: }
1.6 millert 1178: } else
1179: pw = sudo_user.pw;
1180:
1181: return(pw);
1.4 millert 1182: }
1183:
1184: /*
1.1 millert 1185: * Tell which options are mutually exclusive and exit.
1186: */
1187: static void
1188: usage_excl(exit_val)
1189: int exit_val;
1190: {
1191: (void) fprintf(stderr,
1.2 millert 1192: "Only one of the -h, -k, -K, -l, -s, -v or -V options may be used\n");
1.1 millert 1193: usage(exit_val);
1194: }
1195:
1196: /*
1197: * Give usage message and exit.
1198: */
1199: static void
1200: usage(exit_val)
1201: int exit_val;
1202: {
1.8 millert 1203:
1204: (void) fprintf(stderr, "usage: sudo -V | -h | -L | -l | -v | -k | -K | %s",
1205: "[-H] [-S] [-b] [-p prompt]\n [-u username/#uid] ");
1206: #ifdef HAVE_LOGIN_CAP_H
1207: (void) fprintf(stderr, "[-c class] ");
1208: #endif
1209: #ifdef HAVE_BSD_AUTH_H
1210: (void) fprintf(stderr, "[-a auth_type] ");
1.4 millert 1211: #endif
1.8 millert 1212: (void) fprintf(stderr, "-s | <command>\n");
1.1 millert 1213: exit(exit_val);
1214: }