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