Annotation of src/usr.bin/sudo/sudo.c, Revision 1.20
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.2 millert 299:
1.17 millert 300: /*
301: * Look up the timestamp dir owner if one is specified.
302: */
303: if (def_str(I_TIMESTAMPOWNER)) {
304: struct passwd *pw;
305:
306: if (*def_str(I_TIMESTAMPOWNER) == '#')
307: pw = getpwuid(atoi(def_str(I_TIMESTAMPOWNER) + 1));
308: else
309: pw = getpwnam(def_str(I_TIMESTAMPOWNER));
310: if (!pw)
311: log_error(0, "timestamp owner (%s): No such user",
312: def_str(I_TIMESTAMPOWNER));
313: timestamp_uid = pw->pw_uid;
314: }
315:
1.2 millert 316: /* This goes after the sudoers parse since we honor sudoers options. */
1.1 millert 317: if (sudo_mode == MODE_KILL || sudo_mode == MODE_INVALIDATE) {
318: remove_timestamp((sudo_mode == MODE_KILL));
319: exit(0);
320: }
321:
322: if (validated & VALIDATE_ERROR)
323: log_error(0, "parse error in %s near line %d", _PATH_SUDOERS,
324: errorlineno);
325:
326: /* Is root even allowed to run sudo? */
327: if (user_uid == 0 && !def_flag(I_ROOT_SUDO)) {
1.17 millert 328: (void) fprintf(stderr,
329: "Sorry, %s has been configured to not allow root to run it.\n",
1.19 millert 330: getprogname());
1.1 millert 331: exit(1);
332: }
333:
1.11 millert 334: /* If given the -P option, set the "preserve_groups" flag. */
335: if (sudo_mode & MODE_PRESERVE_GROUPS)
336: def_flag(I_PRESERVE_GROUPS) = TRUE;
337:
1.3 millert 338: /* If no command line args and "set_home" is not set, error out. */
339: if ((sudo_mode & MODE_IMPLIED_SHELL) && !def_flag(I_SHELL_NOARGS))
340: usage(1);
341:
1.15 millert 342: /* May need to set $HOME to target user if we are running a command. */
343: if ((sudo_mode & MODE_RUN) && (def_flag(I_ALWAYS_SET_HOME) ||
344: ((sudo_mode & MODE_SHELL) && def_flag(I_SET_HOME))))
1.2 millert 345: sudo_mode |= MODE_RESET_HOME;
346:
1.1 millert 347: /* Bail if a tty is required and we don't have one. */
348: if (def_flag(I_REQUIRETTY)) {
349: if ((fd = open(_PATH_TTY, O_RDWR|O_NOCTTY)) == -1)
350: log_error(NO_MAIL, "sorry, you must have a tty to run sudo");
351: else
352: (void) close(fd);
353: }
354:
1.6 millert 355: /* Fill in passwd struct based on user we are authenticating as. */
356: auth_pw = get_authpw();
1.4 millert 357:
1.1 millert 358: /* Require a password unless the NOPASS tag was set. */
359: if (!(validated & FLAG_NOPASS))
360: check_user();
361:
1.14 millert 362: /* Build up custom environment that avoids any nasty bits. */
363: new_environ = rebuild_env(sudo_mode, envp);
364:
1.1 millert 365: if (validated & VALIDATE_OK) {
366: /* Finally tell the user if the command did not exist. */
367: if (cmnd_status == NOT_FOUND_DOT) {
1.19 millert 368: 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 369: exit(1);
370: } else if (cmnd_status == NOT_FOUND) {
1.19 millert 371: warnx("%s: command not found", user_cmnd);
1.1 millert 372: exit(1);
373: }
374:
375: log_auth(validated, 1);
376: if (sudo_mode == MODE_VALIDATE)
377: exit(0);
378: else if (sudo_mode == MODE_LIST) {
379: list_matches();
380: exit(0);
381: }
382:
383: /* This *must* have been set if we got a match but... */
384: if (safe_cmnd == NULL) {
385: log_error(MSG_ONLY,
1.11 millert 386: "internal error, safe_cmnd never got set for %s; %s",
1.1 millert 387: user_cmnd,
388: "please report this error at http://courtesan.com/sudo/bugs/");
389: }
390:
391: /* Override user's umask if configured to do so. */
392: if (def_ival(I_UMASK) != 0777)
393: (void) umask(def_mode(I_UMASK));
394:
1.4 millert 395: /* Restore coredumpsize resource limit. */
396: #if defined(RLIMIT_CORE) && !defined(SUDO_DEVEL)
397: (void) setrlimit(RLIMIT_CORE, &corelimit);
1.20 ! millert 398: #endif /* RLIMIT_CORE && !SUDO_DEVEL */
1.4 millert 399:
400: /* Become specified user or root. */
1.17 millert 401: set_perms(PERM_RUNAS);
1.16 millert 402:
403: /* Close the password and group files */
404: endpwent();
405: endgrent();
1.12 millert 406:
407: /* Install the new environment. */
408: environ = new_environ;
1.5 millert 409:
1.17 millert 410: /* Restore signal handlers before we exec. */
411: (void) sigaction(SIGINT, &saved_sa_int, NULL);
412: (void) sigaction(SIGQUIT, &saved_sa_quit, NULL);
413: (void) sigaction(SIGTSTP, &saved_sa_tstp, NULL);
414: (void) sigaction(SIGCHLD, &saved_sa_chld, NULL);
415:
1.1 millert 416: #ifndef PROFILING
417: if ((sudo_mode & MODE_BACKGROUND) && fork() > 0)
418: exit(0);
419: else
420: EXEC(safe_cmnd, NewArgv); /* run the command */
421: #else
422: exit(0);
423: #endif /* PROFILING */
424: /*
425: * If we got here then the exec() failed...
426: */
1.19 millert 427: warn("unable to execute %s", safe_cmnd);
1.11 millert 428: exit(127);
1.1 millert 429: } else if ((validated & FLAG_NO_USER) || (validated & FLAG_NO_HOST)) {
430: log_auth(validated, 1);
431: exit(1);
432: } else if (validated & VALIDATE_NOT_OK) {
433: if (def_flag(I_PATH_INFO)) {
434: /*
435: * We'd like to not leak path info at all here, but that can
436: * *really* confuse the users. To really close the leak we'd
437: * have to say "not allowed to run foo" even when the problem
438: * is just "no foo in path" since the user can trivially set
439: * their path to just contain a single dir.
440: */
441: log_auth(validated,
442: !(cmnd_status == NOT_FOUND_DOT || cmnd_status == NOT_FOUND));
443: if (cmnd_status == NOT_FOUND)
1.19 millert 444: warnx("%s: command not found", user_cmnd);
1.1 millert 445: else if (cmnd_status == NOT_FOUND_DOT)
1.19 millert 446: 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 447: } else {
448: /* Just tell the user they are not allowed to run foo. */
449: log_auth(validated, 1);
450: }
451: exit(1);
452: } else {
453: /* should never get here */
454: log_auth(validated, 1);
455: exit(1);
456: }
457: exit(0); /* not reached */
458: }
459:
460: /*
461: * Initialize timezone, set umask, fill in ``sudo_user'' struct and
462: * load the ``interfaces'' array.
463: */
464: static int
465: init_vars(sudo_mode)
466: int sudo_mode;
467: {
468: char *p, thost[MAXHOSTNAMELEN];
1.11 millert 469: int nohostname, rval;
1.1 millert 470:
471: /* Sanity check command from user. */
1.19 millert 472: if (user_cmnd == NULL && strlen(NewArgv[0]) >= MAXPATHLEN)
473: errx(1, "%s: File name too long", NewArgv[0]);
1.1 millert 474:
475: #ifdef HAVE_TZSET
476: (void) tzset(); /* set the timezone if applicable */
477: #endif /* HAVE_TZSET */
478:
479: /* Default value for cmnd and cwd, overridden later. */
480: if (user_cmnd == NULL)
481: user_cmnd = NewArgv[0];
1.17 millert 482: (void) strlcpy(user_cwd, "unknown", sizeof(user_cwd));
1.1 millert 483:
484: /*
485: * We avoid gethostbyname() if possible since we don't want
486: * sudo to block if DNS or NIS is hosed.
487: * "host" is the (possibly fully-qualified) hostname and
488: * "shost" is the unqualified form of the hostname.
489: */
1.11 millert 490: nohostname = gethostname(thost, sizeof(thost));
491: if (nohostname)
492: user_host = user_shost = "localhost";
493: else {
1.1 millert 494: user_host = estrdup(thost);
1.11 millert 495: if (def_flag(I_FQDN)) {
496: /* Defer call to set_fqdn() until log_error() is safe. */
497: user_shost = user_host;
1.1 millert 498: } else {
1.11 millert 499: if ((p = strchr(user_host, '.'))) {
500: *p = '\0';
501: user_shost = estrdup(user_host);
502: *p = '.';
503: } else {
504: user_shost = user_host;
505: }
1.1 millert 506: }
507: }
508:
509: if ((p = ttyname(STDIN_FILENO)) || (p = ttyname(STDOUT_FILENO))) {
510: if (strncmp(p, _PATH_DEV, sizeof(_PATH_DEV) - 1) == 0)
511: p += sizeof(_PATH_DEV) - 1;
512: user_tty = estrdup(p);
513: } else
514: user_tty = "unknown";
515:
516: /*
517: * Get a local copy of the user's struct passwd with the shadow password
518: * if necessary. It is assumed that euid is 0 at this point so we
519: * can read the shadow passwd file if necessary.
520: */
521: if ((sudo_user.pw = sudo_getpwuid(getuid())) == NULL) {
522: /* Need to make a fake struct passwd for logging to work. */
523: struct passwd pw;
524: char pw_name[MAX_UID_T_LEN + 1];
525:
526: pw.pw_uid = getuid();
1.17 millert 527: (void) snprintf(pw_name, sizeof(pw_name), "%lu",
528: (unsigned long) pw.pw_uid);
1.1 millert 529: pw.pw_name = pw_name;
530: sudo_user.pw = &pw;
531:
1.17 millert 532: log_error(0, "uid %lu does not exist in the passwd file!",
533: (unsigned long) pw.pw_uid);
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: {
859: int fd, maxfd;
860: #ifdef HAVE_SETRLIMIT
861: struct rlimit rl;
862: #endif
863:
864: #if defined(RLIMIT_CORE) && !defined(SUDO_DEVEL)
865: /*
866: * Turn off core dumps.
867: */
1.4 millert 868: (void) getrlimit(RLIMIT_CORE, &corelimit);
1.20 ! millert 869: memcpy(&rl, &corelimit, sizeof(struct rlimit));
! 870: rl.rlim_cur = 0;
1.1 millert 871: (void) setrlimit(RLIMIT_CORE, &rl);
1.20 ! millert 872: #endif /* RLIMIT_CORE && !SUDO_DEVEL */
1.1 millert 873:
874: /*
875: * Close any open fd's other than stdin, stdout and stderr.
876: */
877: #ifdef HAVE_SYSCONF
1.3 millert 878: maxfd = sysconf(_SC_OPEN_MAX) - 1;
1.1 millert 879: #else
1.3 millert 880: maxfd = getdtablesize() - 1;
1.1 millert 881: #endif /* HAVE_SYSCONF */
1.3 millert 882: #ifdef RLIMIT_NOFILE
883: if (getrlimit(RLIMIT_NOFILE, &rl) == 0) {
884: if (rl.rlim_max != RLIM_INFINITY && rl.rlim_max <= maxfd)
885: maxfd = rl.rlim_max - 1;
886: }
887: #endif /* RLIMIT_NOFILE */
1.1 millert 888:
889: for (fd = maxfd; fd > STDERR_FILENO; fd--)
890: (void) close(fd);
891:
1.17 millert 892: /*
893: * Make set_perms point to the correct function.
894: * If we are using setresuid() or setreuid() we only need to set this
895: * once. If we are using POSIX saved uids we will switch to
896: * set_perms_nosuid after sudoers has been parsed if the "stay_suid"
897: * option is not set.
898: */
899: #if defined(HAVE_SETRESUID) || defined(HAVE_SETREUID)
900: set_perms = set_perms_suid;
901: #else
902: # if !defined(NO_SAVED_IDS) && defined(_SC_SAVED_IDS) && defined(_SC_VERSION)
1.11 millert 903: if (sysconf(_SC_SAVED_IDS) == 1 && sysconf(_SC_VERSION) >= 199009)
904: set_perms = set_perms_posix;
905: else
1.17 millert 906: # endif
907: set_perms = set_perms_nosuid;
908: #endif /* HAVE_SETRESUID || HAVE_SETREUID */
1.1 millert 909: }
910:
1.8 millert 911: #ifdef HAVE_LOGIN_CAP_H
912: static void
1.4 millert 913: set_loginclass(pw)
914: struct passwd *pw;
915: {
916: int errflags;
917:
918: /*
919: * Don't make it a fatal error if the user didn't specify the login
920: * class themselves. We do this because if login.conf gets
921: * corrupted we want the admin to be able to use sudo to fix it.
922: */
923: if (login_class)
924: errflags = NO_MAIL|MSG_ONLY;
925: else
926: errflags = NO_MAIL|MSG_ONLY|NO_EXIT;
927:
928: if (login_class && strcmp(login_class, "-") != 0) {
1.19 millert 929: if (strcmp(*user_runas, "root") != 0 && user_uid != 0)
930: errx(1, "only root can use -c %s", login_class);
1.4 millert 931: } else {
932: login_class = pw->pw_class;
933: if (!login_class || !*login_class)
934: login_class =
935: (pw->pw_uid == 0) ? LOGIN_DEFROOTCLASS : LOGIN_DEFCLASS;
936: }
937:
938: lc = login_getclass(login_class);
1.10 millert 939: if (!lc || !lc->lc_class || strcmp(lc->lc_class, login_class) != 0) {
1.4 millert 940: log_error(errflags, "unknown login class: %s", login_class);
1.11 millert 941: if (!lc)
942: lc = login_getclass(NULL); /* needed for login_getstyle() later */
1.10 millert 943: }
1.4 millert 944: }
945: #else
1.8 millert 946: static void
1.4 millert 947: set_loginclass(pw)
948: struct passwd *pw;
949: {
950: }
1.8 millert 951: #endif /* HAVE_LOGIN_CAP_H */
1.4 millert 952:
1.1 millert 953: /*
1.2 millert 954: * Look up the fully qualified domain name and set user_host and user_shost.
955: */
956: void
957: set_fqdn()
958: {
959: struct hostent *hp;
960: char *p;
961:
1.14 millert 962: if (!(hp = gethostbyname(user_host))) {
963: log_error(MSG_ONLY|NO_EXIT,
964: "unable to lookup %s via gethostbyname()", user_host);
965: } else {
966: if (user_shost != user_host)
967: free(user_shost);
968: free(user_host);
969: user_host = estrdup(hp->h_name);
1.2 millert 970: }
971: if ((p = strchr(user_host, '.'))) {
972: *p = '\0';
973: user_shost = estrdup(user_host);
974: *p = '.';
975: } else {
976: user_shost = user_host;
977: }
978: }
979:
980: /*
1.6 millert 981: * Get passwd entry for the user we are going to authenticate as.
982: * By default, this is the user invoking sudo...
1.4 millert 983: */
1.6 millert 984: static struct passwd *
985: get_authpw()
1.4 millert 986: {
987: struct passwd *pw;
988:
989: if (def_ival(I_ROOTPW)) {
1.6 millert 990: if ((pw = sudo_getpwuid(0)) == NULL)
1.4 millert 991: log_error(0, "uid 0 does not exist in the passwd file!");
992: } else if (def_ival(I_RUNASPW)) {
1.11 millert 993: if ((pw = sudo_getpwnam(def_str(I_RUNAS_DEFAULT))) == NULL)
1.4 millert 994: log_error(0, "user %s does not exist in the passwd file!",
1.11 millert 995: def_str(I_RUNAS_DEFAULT));
1.4 millert 996: } else if (def_ival(I_TARGETPW)) {
997: if (**user_runas == '#') {
1.6 millert 998: if ((pw = sudo_getpwuid(atoi(*user_runas + 1))) == NULL)
1.4 millert 999: log_error(0, "uid %s does not exist in the passwd file!",
1000: user_runas);
1001: } else {
1.6 millert 1002: if ((pw = sudo_getpwnam(*user_runas)) == NULL)
1.4 millert 1003: log_error(0, "user %s does not exist in the passwd file!",
1004: user_runas);
1005: }
1.6 millert 1006: } else
1007: pw = sudo_user.pw;
1008:
1009: return(pw);
1.4 millert 1010: }
1011:
1012: /*
1.1 millert 1013: * Tell which options are mutually exclusive and exit.
1014: */
1015: static void
1016: usage_excl(exit_val)
1017: int exit_val;
1018: {
1019: (void) fprintf(stderr,
1.2 millert 1020: "Only one of the -h, -k, -K, -l, -s, -v or -V options may be used\n");
1.1 millert 1021: usage(exit_val);
1022: }
1023:
1024: /*
1025: * Give usage message and exit.
1026: */
1027: static void
1028: usage(exit_val)
1029: int exit_val;
1030: {
1.8 millert 1031:
1032: (void) fprintf(stderr, "usage: sudo -V | -h | -L | -l | -v | -k | -K | %s",
1.11 millert 1033: "[-H] [-P] [-S] [-b] [-p prompt]\n [-u username/#uid] ");
1.8 millert 1034: #ifdef HAVE_LOGIN_CAP_H
1035: (void) fprintf(stderr, "[-c class] ");
1036: #endif
1037: #ifdef HAVE_BSD_AUTH_H
1038: (void) fprintf(stderr, "[-a auth_type] ");
1.4 millert 1039: #endif
1.8 millert 1040: (void) fprintf(stderr, "-s | <command>\n");
1.1 millert 1041: exit(exit_val);
1042: }