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