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