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