Annotation of src/usr.bin/sudo/sudo.c, Revision 1.10.2.1
1.1 millert 1: /*
1.10.2.1! millert 2: * Copyright (c) 1993-1996,1998-2002 Todd C. Miller <Todd.Miller@courtesan.com>
1.1 millert 3: * All rights reserved.
4: *
5: * Redistribution and use in source and binary forms, with or without
6: * modification, are permitted provided that the following conditions
7: * are met:
8: *
9: * 1. Redistributions of source code must retain the above copyright
10: * notice, this list of conditions and the following disclaimer.
11: *
12: * 2. Redistributions in binary form must reproduce the above copyright
13: * notice, this list of conditions and the following disclaimer in the
14: * documentation and/or other materials provided with the distribution.
15: *
16: * 3. The name of the author may not be used to endorse or promote products
17: * derived from this software without specific prior written permission.
18: *
19: * 4. Products derived from this software may not be called "Sudo" nor
20: * may "Sudo" appear in their names without specific prior written
21: * permission from the author.
22: *
23: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
24: * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
25: * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
26: * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
27: * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
28: * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
29: * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
30: * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
31: * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
32: * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33: *
34: * For a brief history of sudo, please see the HISTORY file included
35: * with this distribution.
36: */
37:
38: #define _SUDO_SUDO_C
39:
40: #include "config.h"
41:
1.10.2.1! millert 42: #include <sys/types.h>
! 43: #include <sys/stat.h>
! 44: #include <sys/param.h>
! 45: #include <sys/socket.h>
! 46: #ifdef HAVE_SETRLIMIT
! 47: # include <sys/time.h>
! 48: # include <sys/resource.h>
! 49: #endif
1.1 millert 50: #include <stdio.h>
51: #ifdef STDC_HEADERS
1.10.2.1! millert 52: # include <stdlib.h>
! 53: # include <stddef.h>
! 54: #else
! 55: # ifdef HAVE_STDLIB_H
! 56: # include <stdlib.h>
! 57: # endif
1.1 millert 58: #endif /* STDC_HEADERS */
59: #ifdef HAVE_STRING_H
1.10.2.1! millert 60: # if defined(HAVE_MEMORY_H) && !defined(STDC_HEADERS)
! 61: # include <memory.h>
! 62: # endif
! 63: # include <string.h>
! 64: #else
! 65: # ifdef HAVE_STRINGS_H
! 66: # include <strings.h>
! 67: # endif
1.1 millert 68: #endif /* HAVE_STRING_H */
1.10.2.1! millert 69: #ifdef HAVE_UNISTD_H
! 70: # include <unistd.h>
! 71: #endif /* HAVE_UNISTD_H */
1.1 millert 72: #include <pwd.h>
73: #include <errno.h>
74: #include <fcntl.h>
75: #include <signal.h>
76: #include <grp.h>
77: #include <time.h>
78: #include <netinet/in.h>
79: #include <netdb.h>
80: #if defined(HAVE_GETPRPWNAM) && defined(HAVE_SET_AUTH_PARAMETERS)
81: # ifdef __hpux
82: # undef MAXINT
83: # include <hpsecurity.h>
84: # else
85: # include <sys/security.h>
86: # endif /* __hpux */
87: # include <prot.h>
88: #endif /* HAVE_GETPRPWNAM && HAVE_SET_AUTH_PARAMETERS */
1.8 millert 89: #ifdef HAVE_LOGIN_CAP_H
1.4 millert 90: # include <login_cap.h>
91: # ifndef LOGIN_DEFROOTCLASS
92: # define LOGIN_DEFROOTCLASS "daemon"
93: # endif
94: #endif
1.1 millert 95:
96: #include "sudo.h"
97: #include "interfaces.h"
98: #include "version.h"
99:
100: #ifndef lint
1.10.2.1! millert 101: static const char rcsid[] = "$Sudo: sudo.c,v 1.318 2002/01/15 23:43:59 millert Exp $";
1.1 millert 102: #endif /* lint */
103:
104: /*
105: * Prototypes
106: */
107: static int init_vars __P((int));
1.10.2.1! millert 108: static int parse_args __P((void));
! 109: static void check_sudoers __P((void));
1.1 millert 110: static void initial_setup __P((void));
1.10.2.1! millert 111: static void set_loginclass __P((struct passwd *));
! 112: static void usage __P((int));
! 113: static void usage_excl __P((int));
1.6 millert 114: static struct passwd *get_authpw __P((void));
1.1 millert 115: extern void list_matches __P((void));
1.10.2.1! millert 116: extern char **rebuild_env __P((int, char **));
! 117: extern char **zero_env __P((char **));
! 118: extern struct passwd *sudo_getpwnam __P((const char *));
! 119: extern struct passwd *sudo_getpwuid __P((uid_t));
1.1 millert 120:
121: /*
122: * Globals
123: */
124: int Argc;
125: char **Argv;
126: int NewArgc = 0;
127: char **NewArgv = NULL;
128: struct sudo_user sudo_user;
1.6 millert 129: struct passwd *auth_pw;
1.1 millert 130: FILE *sudoers_fp = NULL;
131: struct interface *interfaces;
132: int num_interfaces;
1.4 millert 133: int tgetpass_flags;
1.1 millert 134: extern int errorlineno;
1.4 millert 135: #if defined(RLIMIT_CORE) && !defined(SUDO_DEVEL)
136: static struct rlimit corelimit;
137: #endif /* RLIMIT_CORE */
1.8 millert 138: #ifdef HAVE_LOGIN_CAP_H
139: login_cap_t *lc;
140: #endif /* HAVE_LOGIN_CAP_H */
141: #ifdef HAVE_BSD_AUTH_H
142: char *login_style;
143: #endif /* HAVE_BSD_AUTH_H */
1.10.2.1! millert 144: void (*set_perms) __P((int, int));
1.1 millert 145:
146:
147: int
1.10.2.1! millert 148: main(argc, argv, envp)
1.1 millert 149: int argc;
150: char **argv;
1.10.2.1! millert 151: char **envp;
1.1 millert 152: {
153: int validated;
154: int fd;
155: int cmnd_status;
156: int sudo_mode;
1.10.2.1! millert 157: int pwflag;
! 158: char **new_environ;
! 159: sigaction_t sa;
1.1 millert 160: extern int printmatches;
1.10.2.1! millert 161: extern char **environ;
1.1 millert 162:
163: /* Must be done as the first thing... */
164: #if defined(HAVE_GETPRPWNAM) && defined(HAVE_SET_AUTH_PARAMETERS)
165: (void) set_auth_parameters(argc, argv);
166: # ifdef HAVE_INITPRIVS
167: initprivs();
168: # endif
169: #endif /* HAVE_GETPRPWNAM && HAVE_SET_AUTH_PARAMETERS */
170:
1.10.2.1! millert 171: /* Zero out the environment. */
! 172: environ = zero_env(envp);
! 173:
1.1 millert 174: Argv = argv;
175: Argc = argc;
176:
177: if (geteuid() != 0) {
178: (void) fprintf(stderr, "Sorry, %s must be setuid root.\n", Argv[0]);
179: exit(1);
180: }
181:
182: /*
1.10.2.1! millert 183: * Ignore keyboard-generated signals so the user cannot interrupt
! 184: * us at some point and avoid the logging.
1.1 millert 185: */
1.10.2.1! millert 186: sigemptyset(&sa.sa_mask);
! 187: sa.sa_flags = SA_RESTART;
! 188: sa.sa_handler = SIG_IGN;
! 189: (void) sigaction(SIGINT, &sa, NULL);
! 190: (void) sigaction(SIGQUIT, &sa, NULL);
! 191: (void) sigaction(SIGTSTP, &sa, NULL);
1.1 millert 192:
193: /*
194: * Setup signal handlers, turn off core dumps, and close open files.
195: */
196: initial_setup();
1.10.2.1! millert 197: setpwent();
1.1 millert 198:
199: /* Parse our arguments. */
200: sudo_mode = parse_args();
201:
202: /* Setup defaults data structures. */
203: init_defaults();
204:
1.10.2.1! millert 205: /* Load the list of local ip addresses and netmasks. */
! 206: load_interfaces();
! 207:
! 208: pwflag = 0;
1.1 millert 209: if (sudo_mode & MODE_SHELL)
210: user_cmnd = "shell";
211: else
212: switch (sudo_mode) {
213: case MODE_VERSION:
214: (void) printf("Sudo version %s\n", version);
215: if (getuid() == 0) {
216: putchar('\n');
217: dump_auth_methods();
218: dump_defaults();
1.10.2.1! millert 219: dump_interfaces();
! 220: dump_badenv();
1.1 millert 221: }
222: exit(0);
223: break;
224: case MODE_HELP:
225: usage(0);
226: break;
227: case MODE_VALIDATE:
228: user_cmnd = "validate";
1.10.2.1! millert 229: pwflag = I_VERIFYPW_I;
1.1 millert 230: break;
231: case MODE_KILL:
232: case MODE_INVALIDATE:
233: user_cmnd = "kill";
1.10.2.1! millert 234: pwflag = -1;
1.1 millert 235: break;
236: case MODE_LISTDEFS:
237: list_options();
238: exit(0);
239: break;
240: case MODE_LIST:
241: user_cmnd = "list";
1.10.2.1! millert 242: pwflag = I_LISTPW_I;
1.1 millert 243: printmatches = 1;
244: break;
245: }
246:
247: /* Must have a command to run... */
248: if (user_cmnd == NULL && NewArgc == 0)
249: usage(1);
250:
251: cmnd_status = init_vars(sudo_mode);
252:
253: check_sudoers(); /* check mode/owner on _PATH_SUDOERS */
254:
1.2 millert 255: /* Validate the user but don't search for pseudo-commands. */
1.10.2.1! millert 256: validated = sudoers_lookup(pwflag);
! 257:
! 258: /*
! 259: * If we have POSIX saved uids and the stay_setuid flag was not set,
! 260: * set the real, effective and saved uids to 0 and use set_perms_fallback()
! 261: * instead of set_perms_posix().
! 262: */
! 263: #if !defined(NO_SAVED_IDS) && defined(_SC_SAVED_IDS) && defined(_SC_VERSION)
! 264: if (!def_flag(I_STAY_SETUID) && set_perms == set_perms_posix) {
! 265: if (setuid(0)) {
! 266: perror("setuid(0)");
! 267: exit(1);
! 268: }
! 269: set_perms = set_perms_fallback;
! 270: }
! 271: #endif
! 272:
! 273: /*
! 274: * Look up runas user passwd struct. If we are given a uid then
! 275: * there may be no corresponding passwd(5) entry (which is OK).
! 276: */
! 277: if (**user_runas == '#') {
! 278: runas_pw = sudo_getpwuid(atoi(*user_runas + 1));
! 279: if (runas_pw == NULL) {
! 280: runas_pw = emalloc(sizeof(struct passwd));
! 281: (void) memset((VOID *)runas_pw, 0, sizeof(struct passwd));
! 282: runas_pw->pw_uid = atoi(*user_runas + 1);
! 283: }
! 284: } else {
! 285: runas_pw = sudo_getpwnam(*user_runas);
! 286: if (runas_pw == NULL)
! 287: log_error(NO_MAIL|MSG_ONLY, "no passwd entry for %s!", *user_runas);
! 288: }
1.2 millert 289:
290: /* This goes after the sudoers parse since we honor sudoers options. */
1.1 millert 291: if (sudo_mode == MODE_KILL || sudo_mode == MODE_INVALIDATE) {
292: remove_timestamp((sudo_mode == MODE_KILL));
293: exit(0);
294: }
295:
296: if (validated & VALIDATE_ERROR)
297: log_error(0, "parse error in %s near line %d", _PATH_SUDOERS,
298: errorlineno);
299:
300: /* Is root even allowed to run sudo? */
301: if (user_uid == 0 && !def_flag(I_ROOT_SUDO)) {
302: (void) fputs("You are already root, you don't need to use sudo.\n",
303: stderr);
304: exit(1);
305: }
306:
1.10.2.1! millert 307: /* If given the -P option, set the "preserve_groups" flag. */
! 308: if (sudo_mode & MODE_PRESERVE_GROUPS)
! 309: def_flag(I_PRESERVE_GROUPS) = TRUE;
! 310:
1.3 millert 311: /* If no command line args and "set_home" is not set, error out. */
312: if ((sudo_mode & MODE_IMPLIED_SHELL) && !def_flag(I_SHELL_NOARGS))
313: usage(1);
314:
1.10.2.1! millert 315: /* May need to set $HOME to target user if we are running a command. */
! 316: if ((sudo_mode & MODE_RUN) && (def_flag(I_ALWAYS_SET_HOME) ||
! 317: ((sudo_mode & MODE_SHELL) && def_flag(I_SET_HOME))))
1.2 millert 318: sudo_mode |= MODE_RESET_HOME;
319:
1.1 millert 320: /* Bail if a tty is required and we don't have one. */
321: if (def_flag(I_REQUIRETTY)) {
322: if ((fd = open(_PATH_TTY, O_RDWR|O_NOCTTY)) == -1)
323: log_error(NO_MAIL, "sorry, you must have a tty to run sudo");
324: else
325: (void) close(fd);
326: }
327:
1.6 millert 328: /* Fill in passwd struct based on user we are authenticating as. */
329: auth_pw = get_authpw();
1.4 millert 330:
1.1 millert 331: /* Require a password unless the NOPASS tag was set. */
332: if (!(validated & FLAG_NOPASS))
333: check_user();
334:
1.10.2.1! millert 335: /* Build up custom environment that avoids any nasty bits. */
! 336: new_environ = rebuild_env(sudo_mode, envp);
! 337:
1.1 millert 338: if (validated & VALIDATE_OK) {
339: /* Finally tell the user if the command did not exist. */
340: if (cmnd_status == NOT_FOUND_DOT) {
341: (void) fprintf(stderr, "%s: ignoring `%s' found in '.'\nUse `sudo ./%s' if this is the `%s' you wish to run.\n", Argv[0], user_cmnd, user_cmnd, user_cmnd);
342: exit(1);
343: } else if (cmnd_status == NOT_FOUND) {
344: (void) fprintf(stderr, "%s: %s: command not found\n", Argv[0],
345: user_cmnd);
346: exit(1);
347: }
348:
349: log_auth(validated, 1);
350: if (sudo_mode == MODE_VALIDATE)
351: exit(0);
352: else if (sudo_mode == MODE_LIST) {
353: list_matches();
354: exit(0);
355: }
356:
357: /* This *must* have been set if we got a match but... */
358: if (safe_cmnd == NULL) {
359: log_error(MSG_ONLY,
1.10.2.1! millert 360: "internal error, safe_cmnd never got set for %s; %s",
1.1 millert 361: user_cmnd,
362: "please report this error at http://courtesan.com/sudo/bugs/");
363: }
364:
1.10.2.1! millert 365: /* Reset signal handlers before we exec. */
! 366: sigemptyset(&sa.sa_mask);
! 367: sa.sa_flags = SA_RESTART;
! 368: sa.sa_handler = SIG_DFL;
! 369: (void) sigaction(SIGINT, &sa, NULL);
! 370: (void) sigaction(SIGQUIT, &sa, NULL);
! 371: (void) sigaction(SIGTSTP, &sa, NULL);
1.1 millert 372:
1.10.2.1! millert 373: /* Close the password file */
! 374: endpwent();
1.1 millert 375:
376: /* Override user's umask if configured to do so. */
377: if (def_ival(I_UMASK) != 0777)
378: (void) umask(def_mode(I_UMASK));
379:
1.4 millert 380: /* Restore coredumpsize resource limit. */
381: #if defined(RLIMIT_CORE) && !defined(SUDO_DEVEL)
382: (void) setrlimit(RLIMIT_CORE, &corelimit);
383: #endif /* RLIMIT_CORE */
384:
385: /* Become specified user or root. */
386: set_perms(PERM_RUNAS, sudo_mode);
1.5 millert 387:
1.10.2.1! millert 388: /* Install the new environment. */
! 389: environ = new_environ;
1.4 millert 390:
1.1 millert 391: #ifndef PROFILING
392: if ((sudo_mode & MODE_BACKGROUND) && fork() > 0)
393: exit(0);
394: else
395: EXEC(safe_cmnd, NewArgv); /* run the command */
396: #else
397: exit(0);
398: #endif /* PROFILING */
399: /*
400: * If we got here then the exec() failed...
401: */
402: (void) fprintf(stderr, "%s: unable to exec %s: %s\n",
403: Argv[0], safe_cmnd, strerror(errno));
1.10.2.1! millert 404: exit(127);
1.1 millert 405: } else if ((validated & FLAG_NO_USER) || (validated & FLAG_NO_HOST)) {
406: log_auth(validated, 1);
407: exit(1);
408: } else if (validated & VALIDATE_NOT_OK) {
409: if (def_flag(I_PATH_INFO)) {
410: /*
411: * We'd like to not leak path info at all here, but that can
412: * *really* confuse the users. To really close the leak we'd
413: * have to say "not allowed to run foo" even when the problem
414: * is just "no foo in path" since the user can trivially set
415: * their path to just contain a single dir.
416: */
417: log_auth(validated,
418: !(cmnd_status == NOT_FOUND_DOT || cmnd_status == NOT_FOUND));
419: if (cmnd_status == NOT_FOUND)
420: (void) fprintf(stderr, "%s: %s: command not found\n", Argv[0],
421: user_cmnd);
422: else if (cmnd_status == NOT_FOUND_DOT)
423: (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);
424: } else {
425: /* Just tell the user they are not allowed to run foo. */
426: log_auth(validated, 1);
427: }
428: exit(1);
429: } else {
430: /* should never get here */
431: log_auth(validated, 1);
432: exit(1);
433: }
434: exit(0); /* not reached */
435: }
436:
437: /*
438: * Initialize timezone, set umask, fill in ``sudo_user'' struct and
439: * load the ``interfaces'' array.
440: */
441: static int
442: init_vars(sudo_mode)
443: int sudo_mode;
444: {
445: char *p, thost[MAXHOSTNAMELEN];
1.10.2.1! millert 446: int nohostname, rval;
1.1 millert 447:
448: /* Sanity check command from user. */
449: if (user_cmnd == NULL && strlen(NewArgv[0]) >= MAXPATHLEN) {
450: (void) fprintf(stderr, "%s: %s: Pathname too long\n", Argv[0],
451: NewArgv[0]);
452: exit(1);
453: }
454:
455: #ifdef HAVE_TZSET
456: (void) tzset(); /* set the timezone if applicable */
457: #endif /* HAVE_TZSET */
458:
459: /* Default value for cmnd and cwd, overridden later. */
460: if (user_cmnd == NULL)
461: user_cmnd = NewArgv[0];
462: (void) strcpy(user_cwd, "unknown");
463:
464: /*
465: * We avoid gethostbyname() if possible since we don't want
466: * sudo to block if DNS or NIS is hosed.
467: * "host" is the (possibly fully-qualified) hostname and
468: * "shost" is the unqualified form of the hostname.
469: */
1.10.2.1! millert 470: nohostname = gethostname(thost, sizeof(thost));
! 471: if (nohostname)
! 472: user_host = user_shost = "localhost";
1.2 millert 473: else {
1.10.2.1! millert 474: user_host = estrdup(thost);
! 475: if (def_flag(I_FQDN)) {
! 476: /* Defer call to set_fqdn() until log_error() is safe. */
1.2 millert 477: user_shost = user_host;
1.10.2.1! millert 478: } else {
! 479: if ((p = strchr(user_host, '.'))) {
! 480: *p = '\0';
! 481: user_shost = estrdup(user_host);
! 482: *p = '.';
! 483: } else {
! 484: user_shost = user_host;
! 485: }
1.1 millert 486: }
487: }
488:
489: if ((p = ttyname(STDIN_FILENO)) || (p = ttyname(STDOUT_FILENO))) {
490: if (strncmp(p, _PATH_DEV, sizeof(_PATH_DEV) - 1) == 0)
491: p += sizeof(_PATH_DEV) - 1;
492: user_tty = estrdup(p);
493: } else
494: user_tty = "unknown";
495:
496: /*
497: * Get a local copy of the user's struct passwd with the shadow password
498: * if necessary. It is assumed that euid is 0 at this point so we
499: * can read the shadow passwd file if necessary.
500: */
501: if ((sudo_user.pw = sudo_getpwuid(getuid())) == NULL) {
502: /* Need to make a fake struct passwd for logging to work. */
503: struct passwd pw;
504: char pw_name[MAX_UID_T_LEN + 1];
505:
506: pw.pw_uid = getuid();
507: (void) sprintf(pw_name, "%ld", (long) pw.pw_uid);
508: pw.pw_name = pw_name;
509: sudo_user.pw = &pw;
510:
511: log_error(0, "uid %ld does not exist in the passwd file!",
512: (long) pw.pw_uid);
513: }
1.10.2.1! millert 514: if (user_shell == NULL || *user_shell == '\0')
! 515: user_shell = sudo_user.pw->pw_shell;
1.1 millert 516:
517: /* It is now safe to use log_error() and set_perms() */
518:
519: /*
1.10.2.1! millert 520: * Must defer set_fqdn() until it is safe to call log_error()
! 521: */
! 522: if (def_flag(I_FQDN))
! 523: set_fqdn();
! 524:
! 525: if (nohostname)
! 526: log_error(USE_ERRNO|MSG_ONLY, "can't get hostname");
! 527:
! 528: /*
1.1 millert 529: * Get current working directory. Try as user, fall back to root.
530: */
531: set_perms(PERM_USER, sudo_mode);
532: if (!getcwd(user_cwd, sizeof(user_cwd))) {
533: set_perms(PERM_ROOT, sudo_mode);
534: if (!getcwd(user_cwd, sizeof(user_cwd))) {
535: (void) fprintf(stderr, "%s: Can't get working directory!\n",
536: Argv[0]);
537: (void) strcpy(user_cwd, "unknown");
538: }
539: } else
540: set_perms(PERM_ROOT, sudo_mode);
541:
542: /*
543: * If we were given the '-s' option (run shell) we need to redo
544: * NewArgv and NewArgc.
545: */
546: if ((sudo_mode & MODE_SHELL)) {
547: char **dst, **src = NewArgv;
548:
549: NewArgv = (char **) emalloc (sizeof(char *) * (++NewArgc + 1));
550: if (user_shell && *user_shell) {
551: NewArgv[0] = user_shell;
552: } else {
553: (void) fprintf(stderr, "%s: Unable to determine shell.", Argv[0]);
554: exit(1);
555: }
556:
557: /* copy the args from Argv */
558: for (dst = NewArgv + 1; (*dst = *src) != NULL; ++src, ++dst)
559: ;
560: }
561:
1.8 millert 562: /* Set login class if applicable. */
563: set_loginclass(sudo_user.pw);
564:
1.1 millert 565: /* Resolve the path and return. */
1.10.2.1! millert 566: if ((sudo_mode & MODE_RUN)) {
! 567: /* XXX - should call this as runas user, not root. */
! 568: rval = find_path(NewArgv[0], &user_cmnd, user_path);
! 569: if (rval != FOUND) {
! 570: /* Failed as root, try as invoking user. */
! 571: set_perms(PERM_USER, sudo_mode);
! 572: rval = find_path(NewArgv[0], &user_cmnd, user_path);
! 573: set_perms(PERM_ROOT, sudo_mode);
! 574: }
! 575:
! 576: /* set user_args */
! 577: if (NewArgc > 1) {
! 578: char *to, **from;
! 579: size_t size;
! 580:
! 581: /* If MODE_SHELL not set then NewArgv is contiguous so just count */
! 582: if (!(sudo_mode & MODE_SHELL)) {
! 583: size = (size_t) (NewArgv[NewArgc-1] - NewArgv[1]) +
! 584: strlen(NewArgv[NewArgc-1]) + 1;
! 585: } else {
! 586: for (size = 0, from = NewArgv + 1; *from; from++)
! 587: size += strlen(*from) + 1;
! 588: }
! 589:
! 590: /* alloc and copy. */
! 591: to = user_args = (char *) emalloc(size);
! 592: for (from = NewArgv + 1; *from; from++) {
! 593: (void) strcpy(to, *from);
! 594: to += strlen(*from);
! 595: *to++ = ' ';
! 596: }
! 597: *--to = '\0';
! 598: }
! 599: } else
! 600: rval = FOUND;
! 601:
! 602: return(rval);
1.1 millert 603: }
604:
605: /*
606: * Command line argument parsing, can't use getopt(3).
607: */
608: static int
609: parse_args()
610: {
611: int rval = MODE_RUN; /* what mode is suod to be run in? */
612: int excl = 0; /* exclusive arg, no others allowed */
613:
614: NewArgv = Argv + 1;
615: NewArgc = Argc - 1;
616:
1.2 millert 617: if (NewArgc == 0) { /* no options and no command */
1.3 millert 618: rval |= (MODE_IMPLIED_SHELL | MODE_SHELL);
1.1 millert 619: return(rval);
620: }
621:
622: while (NewArgc > 0 && NewArgv[0][0] == '-') {
623: if (NewArgv[0][1] != '\0' && NewArgv[0][2] != '\0') {
624: (void) fprintf(stderr, "%s: Please use single character options\n",
625: Argv[0]);
626: usage(1);
627: }
628:
629: switch (NewArgv[0][1]) {
630: case 'p':
631: /* Must have an associated prompt. */
632: if (NewArgv[1] == NULL)
633: usage(1);
634:
635: user_prompt = NewArgv[1];
636:
637: /* Shift Argv over and adjust Argc. */
638: NewArgc--;
639: NewArgv++;
640: break;
641: case 'u':
642: /* Must have an associated runas user. */
643: if (NewArgv[1] == NULL)
644: usage(1);
645:
646: user_runas = &NewArgv[1];
647:
648: /* Shift Argv over and adjust Argc. */
649: NewArgc--;
650: NewArgv++;
651: break;
1.8 millert 652: #ifdef HAVE_BSD_AUTH_H
653: case 'a':
654: /* Must have an associated authentication style. */
655: if (NewArgv[1] == NULL)
656: usage(1);
657:
658: login_style = NewArgv[1];
659:
660: /* Shift Argv over and adjust Argc. */
661: NewArgc--;
662: NewArgv++;
663: break;
664: #endif
665: #ifdef HAVE_LOGIN_CAP_H
1.4 millert 666: case 'c':
667: /* Must have an associated login class. */
668: if (NewArgv[1] == NULL)
669: usage(1);
670:
671: login_class = NewArgv[1];
1.10.2.1! millert 672: def_flag(I_USE_LOGINCLASS) = TRUE;
1.4 millert 673:
674: /* Shift Argv over and adjust Argc. */
675: NewArgc--;
676: NewArgv++;
677: break;
678: #endif
1.1 millert 679: case 'b':
680: rval |= MODE_BACKGROUND;
681: break;
682: case 'v':
683: rval = MODE_VALIDATE;
684: if (excl && excl != 'v')
685: usage_excl(1);
686: excl = 'v';
687: break;
688: case 'k':
689: rval = MODE_INVALIDATE;
690: if (excl && excl != 'k')
691: usage_excl(1);
692: excl = 'k';
693: break;
694: case 'K':
695: rval = MODE_KILL;
696: if (excl && excl != 'K')
697: usage_excl(1);
698: excl = 'K';
699: break;
700: case 'L':
701: rval = MODE_LISTDEFS;
702: if (excl && excl != 'L')
703: usage_excl(1);
704: excl = 'L';
705: break;
706: case 'l':
707: rval = MODE_LIST;
708: if (excl && excl != 'l')
709: usage_excl(1);
710: excl = 'l';
711: break;
712: case 'V':
713: rval = MODE_VERSION;
714: if (excl && excl != 'V')
715: usage_excl(1);
716: excl = 'V';
717: break;
718: case 'h':
719: rval = MODE_HELP;
720: if (excl && excl != 'h')
721: usage_excl(1);
722: excl = 'h';
723: break;
724: case 's':
725: rval |= MODE_SHELL;
1.2 millert 726: if (excl && excl != 's')
727: usage_excl(1);
728: excl = 's';
1.1 millert 729: break;
730: case 'H':
731: rval |= MODE_RESET_HOME;
732: break;
1.10.2.1! millert 733: case 'P':
! 734: rval |= MODE_PRESERVE_GROUPS;
! 735: break;
1.4 millert 736: case 'S':
737: tgetpass_flags |= TGP_STDIN;
738: break;
1.1 millert 739: case '-':
740: NewArgc--;
741: NewArgv++;
1.2 millert 742: if (rval == MODE_RUN)
1.3 millert 743: rval |= (MODE_IMPLIED_SHELL | MODE_SHELL);
1.1 millert 744: return(rval);
745: case '\0':
746: (void) fprintf(stderr, "%s: '-' requires an argument\n",
747: Argv[0]);
748: usage(1);
749: default:
750: (void) fprintf(stderr, "%s: Illegal option %s\n", Argv[0],
751: NewArgv[0]);
752: usage(1);
753: }
754: NewArgc--;
755: NewArgv++;
756: }
757:
758: if (NewArgc > 0 && !(rval & MODE_RUN))
759: usage(1);
760:
761: return(rval);
762: }
763:
764: /*
765: * Sanity check sudoers mode/owner/type.
766: * Leaves a file pointer to the sudoers file open in ``fp''.
767: */
768: static void
769: check_sudoers()
770: {
771: struct stat statbuf;
772: int rootstat, i;
773: char c;
774:
775: /*
776: * Fix the mode and group on sudoers file from old default.
777: * Only works if filesystem is readable/writable by root.
778: */
779: if ((rootstat = lstat(_PATH_SUDOERS, &statbuf)) == 0 &&
780: SUDOERS_UID == statbuf.st_uid && SUDOERS_MODE != 0400 &&
781: (statbuf.st_mode & 0007777) == 0400) {
782:
783: if (chmod(_PATH_SUDOERS, SUDOERS_MODE) == 0) {
784: (void) fprintf(stderr, "%s: fixed mode on %s\n",
785: Argv[0], _PATH_SUDOERS);
1.3 millert 786: statbuf.st_mode |= SUDOERS_MODE;
1.1 millert 787: if (statbuf.st_gid != SUDOERS_GID) {
788: if (!chown(_PATH_SUDOERS,(uid_t) -1,SUDOERS_GID)) {
789: (void) fprintf(stderr, "%s: set group on %s\n",
790: Argv[0], _PATH_SUDOERS);
791: statbuf.st_gid = SUDOERS_GID;
792: } else {
793: (void) fprintf(stderr,"%s: Unable to set group on %s: %s\n",
794: Argv[0], _PATH_SUDOERS, strerror(errno));
795: }
796: }
797: } else {
798: (void) fprintf(stderr, "%s: Unable to fix mode on %s: %s\n",
799: Argv[0], _PATH_SUDOERS, strerror(errno));
800: }
801: }
802:
803: /*
804: * Sanity checks on sudoers file. Must be done as sudoers
805: * file owner. We already did a stat as root, so use that
806: * data if we can't stat as sudoers file owner.
807: */
808: set_perms(PERM_SUDOERS, 0);
809:
810: if (rootstat != 0 && lstat(_PATH_SUDOERS, &statbuf) != 0)
811: log_error(USE_ERRNO, "can't stat %s", _PATH_SUDOERS);
812: else if (!S_ISREG(statbuf.st_mode))
813: log_error(0, "%s is not a regular file", _PATH_SUDOERS);
1.9 millert 814: else if (statbuf.st_size == 0)
815: log_error(0, "%s is zero length", _PATH_SUDOERS);
1.1 millert 816: else if ((statbuf.st_mode & 07777) != SUDOERS_MODE)
817: log_error(0, "%s is mode 0%o, should be 0%o", _PATH_SUDOERS,
818: (statbuf.st_mode & 07777), SUDOERS_MODE);
819: else if (statbuf.st_uid != SUDOERS_UID)
820: log_error(0, "%s is owned by uid %ld, should be %d", _PATH_SUDOERS,
821: (long) statbuf.st_uid, SUDOERS_UID);
822: else if (statbuf.st_gid != SUDOERS_GID)
823: log_error(0, "%s is owned by gid %ld, should be %d", _PATH_SUDOERS,
824: (long) statbuf.st_gid, SUDOERS_GID);
825: else {
826: /* Solaris sometimes returns EAGAIN so try 10 times */
827: for (i = 0; i < 10 ; i++) {
828: errno = 0;
829: if ((sudoers_fp = fopen(_PATH_SUDOERS, "r")) == NULL ||
830: fread(&c, sizeof(c), 1, sudoers_fp) != 1) {
831: sudoers_fp = NULL;
832: if (errno != EAGAIN && errno != EWOULDBLOCK)
833: break;
834: } else
835: break;
836: sleep(1);
837: }
838: if (sudoers_fp == NULL)
839: log_error(USE_ERRNO, "can't open %s", _PATH_SUDOERS);
840: }
841:
842: set_perms(PERM_ROOT, 0); /* change back to root */
843: }
844:
845: /*
846: * Close all open files (except std*) and turn off core dumps.
1.10.2.1! millert 847: * Also sets the set_perms() pointer to the correct function.
1.1 millert 848: */
849: static void
850: initial_setup()
851: {
852: int fd, maxfd;
853: #ifdef HAVE_SETRLIMIT
854: struct rlimit rl;
855: #endif
1.10.2.1! millert 856: sigaction_t sa;
1.1 millert 857:
858: #if defined(RLIMIT_CORE) && !defined(SUDO_DEVEL)
859: /*
860: * Turn off core dumps.
861: */
1.4 millert 862: (void) getrlimit(RLIMIT_CORE, &corelimit);
1.1 millert 863: rl.rlim_cur = rl.rlim_max = 0;
864: (void) setrlimit(RLIMIT_CORE, &rl);
865: #endif /* RLIMIT_CORE */
866:
867: /*
868: * Close any open fd's other than stdin, stdout and stderr.
869: */
870: #ifdef HAVE_SYSCONF
1.3 millert 871: maxfd = sysconf(_SC_OPEN_MAX) - 1;
1.1 millert 872: #else
1.3 millert 873: maxfd = getdtablesize() - 1;
1.1 millert 874: #endif /* HAVE_SYSCONF */
1.3 millert 875: #ifdef RLIMIT_NOFILE
876: if (getrlimit(RLIMIT_NOFILE, &rl) == 0) {
877: if (rl.rlim_max != RLIM_INFINITY && rl.rlim_max <= maxfd)
878: maxfd = rl.rlim_max - 1;
879: }
880: #endif /* RLIMIT_NOFILE */
1.1 millert 881:
882: for (fd = maxfd; fd > STDERR_FILENO; fd--)
883: (void) close(fd);
884:
885: /* Catch children as they die... */
1.10.2.1! millert 886: sigemptyset(&sa.sa_mask);
! 887: sa.sa_flags = SA_RESTART;
1.1 millert 888: sa.sa_handler = reapchild;
889: (void) sigaction(SIGCHLD, &sa, NULL);
1.10.2.1! millert 890:
! 891: /* Set set_perms pointer to the correct function */
! 892: #if !defined(NO_SAVED_IDS) && defined(_SC_SAVED_IDS) && defined(_SC_VERSION)
! 893: if (sysconf(_SC_SAVED_IDS) == 1 && sysconf(_SC_VERSION) >= 199009)
! 894: set_perms = set_perms_posix;
! 895: else
! 896: #endif
! 897: set_perms = set_perms_fallback;
1.1 millert 898: }
899:
1.8 millert 900: #ifdef HAVE_LOGIN_CAP_H
901: static void
1.4 millert 902: set_loginclass(pw)
903: struct passwd *pw;
904: {
905: int errflags;
906:
907: /*
908: * Don't make it a fatal error if the user didn't specify the login
909: * class themselves. We do this because if login.conf gets
910: * corrupted we want the admin to be able to use sudo to fix it.
911: */
912: if (login_class)
913: errflags = NO_MAIL|MSG_ONLY;
914: else
915: errflags = NO_MAIL|MSG_ONLY|NO_EXIT;
916:
917: if (login_class && strcmp(login_class, "-") != 0) {
918: if (strcmp(*user_runas, "root") != 0 && user_uid != 0) {
919: (void) fprintf(stderr, "%s: only root can use -c %s\n",
920: Argv[0], login_class);
921: exit(1);
922: }
923: } else {
924: login_class = pw->pw_class;
925: if (!login_class || !*login_class)
926: login_class =
927: (pw->pw_uid == 0) ? LOGIN_DEFROOTCLASS : LOGIN_DEFCLASS;
928: }
929:
930: lc = login_getclass(login_class);
1.10 millert 931: if (!lc || !lc->lc_class || strcmp(lc->lc_class, login_class) != 0) {
1.4 millert 932: log_error(errflags, "unknown login class: %s", login_class);
1.10.2.1! millert 933: if (!lc)
! 934: lc = login_getclass(NULL); /* needed for login_getstyle() later */
1.10 millert 935: }
1.4 millert 936: }
937: #else
1.8 millert 938: static void
1.4 millert 939: set_loginclass(pw)
940: struct passwd *pw;
941: {
942: }
1.8 millert 943: #endif /* HAVE_LOGIN_CAP_H */
1.4 millert 944:
1.1 millert 945: /*
1.2 millert 946: * Look up the fully qualified domain name and set user_host and user_shost.
947: */
948: void
949: set_fqdn()
950: {
951: struct hostent *hp;
952: char *p;
953:
1.10.2.1! millert 954: if (!(hp = gethostbyname(user_host))) {
! 955: log_error(MSG_ONLY|NO_EXIT,
! 956: "unable to lookup %s via gethostbyname()", user_host);
! 957: } else {
! 958: if (user_shost != user_host)
! 959: free(user_shost);
! 960: free(user_host);
! 961: user_host = estrdup(hp->h_name);
1.2 millert 962: }
963: if ((p = strchr(user_host, '.'))) {
964: *p = '\0';
965: user_shost = estrdup(user_host);
966: *p = '.';
967: } else {
968: user_shost = user_host;
969: }
970: }
971:
972: /*
1.6 millert 973: * Get passwd entry for the user we are going to authenticate as.
974: * By default, this is the user invoking sudo...
1.4 millert 975: */
1.6 millert 976: static struct passwd *
977: get_authpw()
1.4 millert 978: {
979: struct passwd *pw;
980:
981: if (def_ival(I_ROOTPW)) {
1.6 millert 982: if ((pw = sudo_getpwuid(0)) == NULL)
1.4 millert 983: log_error(0, "uid 0 does not exist in the passwd file!");
984: } else if (def_ival(I_RUNASPW)) {
1.10.2.1! millert 985: if ((pw = sudo_getpwnam(def_str(I_RUNAS_DEFAULT))) == NULL)
1.4 millert 986: log_error(0, "user %s does not exist in the passwd file!",
1.10.2.1! millert 987: def_str(I_RUNAS_DEFAULT));
1.4 millert 988: } else if (def_ival(I_TARGETPW)) {
989: if (**user_runas == '#') {
1.6 millert 990: if ((pw = sudo_getpwuid(atoi(*user_runas + 1))) == NULL)
1.4 millert 991: log_error(0, "uid %s does not exist in the passwd file!",
992: user_runas);
993: } else {
1.6 millert 994: if ((pw = sudo_getpwnam(*user_runas)) == NULL)
1.4 millert 995: log_error(0, "user %s does not exist in the passwd file!",
996: user_runas);
997: }
1.6 millert 998: } else
999: pw = sudo_user.pw;
1000:
1001: return(pw);
1.4 millert 1002: }
1003:
1004: /*
1.1 millert 1005: * Tell which options are mutually exclusive and exit.
1006: */
1007: static void
1008: usage_excl(exit_val)
1009: int exit_val;
1010: {
1011: (void) fprintf(stderr,
1.2 millert 1012: "Only one of the -h, -k, -K, -l, -s, -v or -V options may be used\n");
1.1 millert 1013: usage(exit_val);
1014: }
1015:
1016: /*
1017: * Give usage message and exit.
1018: */
1019: static void
1020: usage(exit_val)
1021: int exit_val;
1022: {
1.8 millert 1023:
1024: (void) fprintf(stderr, "usage: sudo -V | -h | -L | -l | -v | -k | -K | %s",
1.10.2.1! millert 1025: "[-H] [-P] [-S] [-b] [-p prompt]\n [-u username/#uid] ");
1.8 millert 1026: #ifdef HAVE_LOGIN_CAP_H
1027: (void) fprintf(stderr, "[-c class] ");
1028: #endif
1029: #ifdef HAVE_BSD_AUTH_H
1030: (void) fprintf(stderr, "[-a auth_type] ");
1.4 millert 1031: #endif
1.8 millert 1032: (void) fprintf(stderr, "-s | <command>\n");
1.1 millert 1033: exit(exit_val);
1034: }