Annotation of src/usr.bin/sudo/sudo.c, Revision 1.22
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.21 millert 532: /*
533: * If we are in -k/-K mode, just spew to stderr. It is not unusual for
534: * users to place "sudo -k" in a .logout file which can cause sudo to
535: * be run during reboot after the YP/NIS/NIS+/LDAP/etc daemon has died.
536: */
537: if (sudo_mode & (MODE_INVALIDATE|MODE_KILL))
538: errx(1, "uid %s does not exist in the passwd file!", pw_name);
539: log_error(0, "uid %s does not exist in the passwd file!", pw_name);
1.1 millert 540: }
1.15 millert 541: if (user_shell == NULL || *user_shell == '\0')
542: user_shell = sudo_user.pw->pw_shell;
1.1 millert 543:
544: /* It is now safe to use log_error() and set_perms() */
545:
546: /*
1.11 millert 547: * Must defer set_fqdn() until it is safe to call log_error()
548: */
549: if (def_flag(I_FQDN))
550: set_fqdn();
551:
552: if (nohostname)
553: log_error(USE_ERRNO|MSG_ONLY, "can't get hostname");
554:
555: /*
1.1 millert 556: * Get current working directory. Try as user, fall back to root.
557: */
1.17 millert 558: set_perms(PERM_USER);
1.1 millert 559: if (!getcwd(user_cwd, sizeof(user_cwd))) {
1.17 millert 560: set_perms(PERM_ROOT);
1.1 millert 561: if (!getcwd(user_cwd, sizeof(user_cwd))) {
1.19 millert 562: warnx("cannot get working directory");
1.17 millert 563: (void) strlcpy(user_cwd, "unknown", sizeof(user_cwd));
1.1 millert 564: }
565: } else
1.17 millert 566: set_perms(PERM_ROOT);
1.1 millert 567:
568: /*
569: * If we were given the '-s' option (run shell) we need to redo
570: * NewArgv and NewArgc.
571: */
572: if ((sudo_mode & MODE_SHELL)) {
573: char **dst, **src = NewArgv;
574:
1.17 millert 575: NewArgv = (char **) emalloc2((++NewArgc + 1), sizeof(char *));
1.1 millert 576: if (user_shell && *user_shell) {
577: NewArgv[0] = user_shell;
1.19 millert 578: } else
579: errx(1, "unable to determine shell");
1.1 millert 580:
1.19 millert 581: /* copy the args from NewArgv */
1.1 millert 582: for (dst = NewArgv + 1; (*dst = *src) != NULL; ++src, ++dst)
583: ;
584: }
585:
1.8 millert 586: /* Set login class if applicable. */
587: set_loginclass(sudo_user.pw);
588:
1.1 millert 589: /* Resolve the path and return. */
1.11 millert 590: if ((sudo_mode & MODE_RUN)) {
591: /* XXX - should call this as runas user, not root. */
592: rval = find_path(NewArgv[0], &user_cmnd, user_path);
593: if (rval != FOUND) {
594: /* Failed as root, try as invoking user. */
1.17 millert 595: set_perms(PERM_USER);
1.11 millert 596: rval = find_path(NewArgv[0], &user_cmnd, user_path);
1.17 millert 597: set_perms(PERM_ROOT);
1.11 millert 598: }
599:
600: /* set user_args */
601: if (NewArgc > 1) {
602: char *to, **from;
1.17 millert 603: size_t size, n;
1.11 millert 604:
605: /* If MODE_SHELL not set then NewArgv is contiguous so just count */
606: if (!(sudo_mode & MODE_SHELL)) {
607: size = (size_t) (NewArgv[NewArgc-1] - NewArgv[1]) +
608: strlen(NewArgv[NewArgc-1]) + 1;
609: } else {
610: for (size = 0, from = NewArgv + 1; *from; from++)
611: size += strlen(*from) + 1;
612: }
613:
614: /* alloc and copy. */
1.17 millert 615: user_args = (char *) emalloc(size);
616: for (to = user_args, from = NewArgv + 1; *from; from++) {
617: n = strlcpy(to, *from, size - (to - user_args));
1.19 millert 618: if (n >= size - (to - user_args))
619: errx(1, "internal error, init_vars() overflow");
1.17 millert 620: to += n;
1.11 millert 621: *to++ = ' ';
622: }
623: *--to = '\0';
624: }
625: } else
626: rval = FOUND;
627:
628: return(rval);
1.1 millert 629: }
630:
631: /*
632: * Command line argument parsing, can't use getopt(3).
633: */
634: static int
1.19 millert 635: parse_args(argc, argv)
636: int argc;
637: char **argv;
1.1 millert 638: {
1.17 millert 639: int rval = MODE_RUN; /* what mode is sudo to be run in? */
1.1 millert 640: int excl = 0; /* exclusive arg, no others allowed */
641:
1.19 millert 642: NewArgv = argv + 1;
643: NewArgc = argc - 1;
1.1 millert 644:
1.2 millert 645: if (NewArgc == 0) { /* no options and no command */
1.3 millert 646: rval |= (MODE_IMPLIED_SHELL | MODE_SHELL);
1.1 millert 647: return(rval);
648: }
649:
650: while (NewArgc > 0 && NewArgv[0][0] == '-') {
1.19 millert 651: if (NewArgv[0][1] != '\0' && NewArgv[0][2] != '\0')
652: warnx("please use single character options");
1.1 millert 653:
654: switch (NewArgv[0][1]) {
655: case 'p':
656: /* Must have an associated prompt. */
657: if (NewArgv[1] == NULL)
658: usage(1);
659:
660: user_prompt = NewArgv[1];
661:
662: NewArgc--;
663: NewArgv++;
664: break;
665: case 'u':
666: /* Must have an associated runas user. */
667: if (NewArgv[1] == NULL)
668: usage(1);
669:
670: user_runas = &NewArgv[1];
671:
672: NewArgc--;
673: NewArgv++;
674: break;
1.8 millert 675: #ifdef HAVE_BSD_AUTH_H
676: case 'a':
677: /* Must have an associated authentication style. */
678: if (NewArgv[1] == NULL)
679: usage(1);
680:
681: login_style = NewArgv[1];
682:
683: NewArgc--;
684: NewArgv++;
685: break;
686: #endif
687: #ifdef HAVE_LOGIN_CAP_H
1.4 millert 688: case 'c':
689: /* Must have an associated login class. */
690: if (NewArgv[1] == NULL)
691: usage(1);
692:
693: login_class = NewArgv[1];
1.11 millert 694: def_flag(I_USE_LOGINCLASS) = TRUE;
1.4 millert 695:
696: NewArgc--;
697: NewArgv++;
698: break;
699: #endif
1.1 millert 700: case 'b':
701: rval |= MODE_BACKGROUND;
702: break;
703: case 'v':
704: rval = MODE_VALIDATE;
705: if (excl && excl != 'v')
706: usage_excl(1);
707: excl = 'v';
708: break;
709: case 'k':
710: rval = MODE_INVALIDATE;
711: if (excl && excl != 'k')
712: usage_excl(1);
713: excl = 'k';
714: break;
715: case 'K':
716: rval = MODE_KILL;
717: if (excl && excl != 'K')
718: usage_excl(1);
719: excl = 'K';
720: break;
721: case 'L':
722: rval = MODE_LISTDEFS;
723: if (excl && excl != 'L')
724: usage_excl(1);
725: excl = 'L';
726: break;
727: case 'l':
728: rval = MODE_LIST;
729: if (excl && excl != 'l')
730: usage_excl(1);
731: excl = 'l';
732: break;
733: case 'V':
734: rval = MODE_VERSION;
735: if (excl && excl != 'V')
736: usage_excl(1);
737: excl = 'V';
738: break;
739: case 'h':
740: rval = MODE_HELP;
741: if (excl && excl != 'h')
742: usage_excl(1);
743: excl = 'h';
744: break;
745: case 's':
746: rval |= MODE_SHELL;
1.2 millert 747: if (excl && excl != 's')
748: usage_excl(1);
749: excl = 's';
1.1 millert 750: break;
751: case 'H':
752: rval |= MODE_RESET_HOME;
753: break;
1.11 millert 754: case 'P':
755: rval |= MODE_PRESERVE_GROUPS;
756: break;
1.4 millert 757: case 'S':
758: tgetpass_flags |= TGP_STDIN;
759: break;
1.1 millert 760: case '-':
761: NewArgc--;
762: NewArgv++;
1.2 millert 763: if (rval == MODE_RUN)
1.3 millert 764: rval |= (MODE_IMPLIED_SHELL | MODE_SHELL);
1.1 millert 765: return(rval);
766: case '\0':
1.19 millert 767: warnx("'-' requires an argument");
1.1 millert 768: usage(1);
769: default:
1.19 millert 770: warnx("illegal option `%s'", NewArgv[0]);
1.1 millert 771: usage(1);
772: }
773: NewArgc--;
774: NewArgv++;
775: }
776:
777: if (NewArgc > 0 && !(rval & MODE_RUN))
778: usage(1);
779:
780: return(rval);
781: }
782:
783: /*
784: * Sanity check sudoers mode/owner/type.
785: * Leaves a file pointer to the sudoers file open in ``fp''.
786: */
787: static void
788: check_sudoers()
789: {
790: struct stat statbuf;
791: int rootstat, i;
792: char c;
793:
794: /*
795: * Fix the mode and group on sudoers file from old default.
796: * Only works if filesystem is readable/writable by root.
797: */
1.17 millert 798: if ((rootstat = stat_sudoers(_PATH_SUDOERS, &statbuf)) == 0 &&
1.1 millert 799: SUDOERS_UID == statbuf.st_uid && SUDOERS_MODE != 0400 &&
800: (statbuf.st_mode & 0007777) == 0400) {
801:
802: if (chmod(_PATH_SUDOERS, SUDOERS_MODE) == 0) {
1.19 millert 803: warnx("fixed mode on %s", _PATH_SUDOERS);
1.3 millert 804: statbuf.st_mode |= SUDOERS_MODE;
1.1 millert 805: if (statbuf.st_gid != SUDOERS_GID) {
806: if (!chown(_PATH_SUDOERS,(uid_t) -1,SUDOERS_GID)) {
1.19 millert 807: warnx("set group on %s", _PATH_SUDOERS);
1.1 millert 808: statbuf.st_gid = SUDOERS_GID;
1.19 millert 809: } else
810: warn("unable to set group on %s", _PATH_SUDOERS);
1.1 millert 811: }
1.19 millert 812: } else
813: warn("unable to fix mode on %s", _PATH_SUDOERS);
1.1 millert 814: }
815:
816: /*
817: * Sanity checks on sudoers file. Must be done as sudoers
818: * file owner. We already did a stat as root, so use that
819: * data if we can't stat as sudoers file owner.
820: */
1.17 millert 821: set_perms(PERM_SUDOERS);
1.1 millert 822:
1.17 millert 823: if (rootstat != 0 && stat_sudoers(_PATH_SUDOERS, &statbuf) != 0)
1.1 millert 824: log_error(USE_ERRNO, "can't stat %s", _PATH_SUDOERS);
825: else if (!S_ISREG(statbuf.st_mode))
826: log_error(0, "%s is not a regular file", _PATH_SUDOERS);
1.9 millert 827: else if (statbuf.st_size == 0)
828: log_error(0, "%s is zero length", _PATH_SUDOERS);
1.1 millert 829: else if ((statbuf.st_mode & 07777) != SUDOERS_MODE)
830: log_error(0, "%s is mode 0%o, should be 0%o", _PATH_SUDOERS,
831: (statbuf.st_mode & 07777), SUDOERS_MODE);
832: else if (statbuf.st_uid != SUDOERS_UID)
1.17 millert 833: log_error(0, "%s is owned by uid %lu, should be %lu", _PATH_SUDOERS,
834: (unsigned long) statbuf.st_uid, SUDOERS_UID);
1.1 millert 835: else if (statbuf.st_gid != SUDOERS_GID)
1.17 millert 836: log_error(0, "%s is owned by gid %lu, should be %lu", _PATH_SUDOERS,
837: (unsigned long) statbuf.st_gid, SUDOERS_GID);
1.1 millert 838: else {
839: /* Solaris sometimes returns EAGAIN so try 10 times */
840: for (i = 0; i < 10 ; i++) {
841: errno = 0;
842: if ((sudoers_fp = fopen(_PATH_SUDOERS, "r")) == NULL ||
843: fread(&c, sizeof(c), 1, sudoers_fp) != 1) {
844: sudoers_fp = NULL;
845: if (errno != EAGAIN && errno != EWOULDBLOCK)
846: break;
847: } else
848: break;
849: sleep(1);
850: }
851: if (sudoers_fp == NULL)
852: log_error(USE_ERRNO, "can't open %s", _PATH_SUDOERS);
853: }
854:
1.17 millert 855: set_perms(PERM_ROOT); /* change back to root */
1.1 millert 856: }
857:
858: /*
859: * Close all open files (except std*) and turn off core dumps.
1.11 millert 860: * Also sets the set_perms() pointer to the correct function.
1.1 millert 861: */
862: static void
863: initial_setup()
864: {
1.22 ! millert 865: #if defined(RLIMIT_CORE) && !defined(SUDO_DEVEL)
1.1 millert 866: struct rlimit rl;
867:
868: /*
869: * Turn off core dumps.
870: */
1.4 millert 871: (void) getrlimit(RLIMIT_CORE, &corelimit);
1.20 millert 872: memcpy(&rl, &corelimit, sizeof(struct rlimit));
873: rl.rlim_cur = 0;
1.1 millert 874: (void) setrlimit(RLIMIT_CORE, &rl);
1.20 millert 875: #endif /* RLIMIT_CORE && !SUDO_DEVEL */
1.1 millert 876:
1.22 ! millert 877: closefrom(STDERR_FILENO + 1);
1.1 millert 878:
1.17 millert 879: /*
880: * Make set_perms point to the correct function.
881: * If we are using setresuid() or setreuid() we only need to set this
882: * once. If we are using POSIX saved uids we will switch to
883: * set_perms_nosuid after sudoers has been parsed if the "stay_suid"
884: * option is not set.
885: */
886: #if defined(HAVE_SETRESUID) || defined(HAVE_SETREUID)
887: set_perms = set_perms_suid;
888: #else
889: # if !defined(NO_SAVED_IDS) && defined(_SC_SAVED_IDS) && defined(_SC_VERSION)
1.11 millert 890: if (sysconf(_SC_SAVED_IDS) == 1 && sysconf(_SC_VERSION) >= 199009)
891: set_perms = set_perms_posix;
892: else
1.17 millert 893: # endif
894: set_perms = set_perms_nosuid;
895: #endif /* HAVE_SETRESUID || HAVE_SETREUID */
1.1 millert 896: }
897:
1.8 millert 898: #ifdef HAVE_LOGIN_CAP_H
899: static void
1.4 millert 900: set_loginclass(pw)
901: struct passwd *pw;
902: {
903: int errflags;
904:
905: /*
906: * Don't make it a fatal error if the user didn't specify the login
907: * class themselves. We do this because if login.conf gets
908: * corrupted we want the admin to be able to use sudo to fix it.
909: */
910: if (login_class)
911: errflags = NO_MAIL|MSG_ONLY;
912: else
913: errflags = NO_MAIL|MSG_ONLY|NO_EXIT;
914:
915: if (login_class && strcmp(login_class, "-") != 0) {
1.19 millert 916: if (strcmp(*user_runas, "root") != 0 && user_uid != 0)
917: errx(1, "only root can use -c %s", login_class);
1.4 millert 918: } else {
919: login_class = pw->pw_class;
920: if (!login_class || !*login_class)
921: login_class =
922: (pw->pw_uid == 0) ? LOGIN_DEFROOTCLASS : LOGIN_DEFCLASS;
923: }
924:
925: lc = login_getclass(login_class);
1.10 millert 926: if (!lc || !lc->lc_class || strcmp(lc->lc_class, login_class) != 0) {
1.4 millert 927: log_error(errflags, "unknown login class: %s", login_class);
1.11 millert 928: if (!lc)
929: lc = login_getclass(NULL); /* needed for login_getstyle() later */
1.10 millert 930: }
1.4 millert 931: }
932: #else
1.8 millert 933: static void
1.4 millert 934: set_loginclass(pw)
935: struct passwd *pw;
936: {
937: }
1.8 millert 938: #endif /* HAVE_LOGIN_CAP_H */
1.4 millert 939:
1.1 millert 940: /*
1.2 millert 941: * Look up the fully qualified domain name and set user_host and user_shost.
942: */
943: void
944: set_fqdn()
945: {
946: struct hostent *hp;
947: char *p;
948:
1.14 millert 949: if (!(hp = gethostbyname(user_host))) {
950: log_error(MSG_ONLY|NO_EXIT,
951: "unable to lookup %s via gethostbyname()", user_host);
952: } else {
953: if (user_shost != user_host)
954: free(user_shost);
955: free(user_host);
956: user_host = estrdup(hp->h_name);
1.2 millert 957: }
958: if ((p = strchr(user_host, '.'))) {
959: *p = '\0';
960: user_shost = estrdup(user_host);
961: *p = '.';
962: } else {
963: user_shost = user_host;
964: }
965: }
966:
967: /*
1.6 millert 968: * Get passwd entry for the user we are going to authenticate as.
969: * By default, this is the user invoking sudo...
1.4 millert 970: */
1.6 millert 971: static struct passwd *
972: get_authpw()
1.4 millert 973: {
974: struct passwd *pw;
975:
976: if (def_ival(I_ROOTPW)) {
1.6 millert 977: if ((pw = sudo_getpwuid(0)) == NULL)
1.4 millert 978: log_error(0, "uid 0 does not exist in the passwd file!");
979: } else if (def_ival(I_RUNASPW)) {
1.11 millert 980: if ((pw = sudo_getpwnam(def_str(I_RUNAS_DEFAULT))) == NULL)
1.4 millert 981: log_error(0, "user %s does not exist in the passwd file!",
1.11 millert 982: def_str(I_RUNAS_DEFAULT));
1.4 millert 983: } else if (def_ival(I_TARGETPW)) {
984: if (**user_runas == '#') {
1.6 millert 985: if ((pw = sudo_getpwuid(atoi(*user_runas + 1))) == NULL)
1.4 millert 986: log_error(0, "uid %s does not exist in the passwd file!",
987: user_runas);
988: } else {
1.6 millert 989: if ((pw = sudo_getpwnam(*user_runas)) == NULL)
1.4 millert 990: log_error(0, "user %s does not exist in the passwd file!",
991: user_runas);
992: }
1.6 millert 993: } else
994: pw = sudo_user.pw;
995:
996: return(pw);
1.4 millert 997: }
998:
999: /*
1.1 millert 1000: * Tell which options are mutually exclusive and exit.
1001: */
1002: static void
1003: usage_excl(exit_val)
1004: int exit_val;
1005: {
1006: (void) fprintf(stderr,
1.2 millert 1007: "Only one of the -h, -k, -K, -l, -s, -v or -V options may be used\n");
1.1 millert 1008: usage(exit_val);
1009: }
1010:
1011: /*
1012: * Give usage message and exit.
1013: */
1014: static void
1015: usage(exit_val)
1016: int exit_val;
1017: {
1.8 millert 1018:
1019: (void) fprintf(stderr, "usage: sudo -V | -h | -L | -l | -v | -k | -K | %s",
1.11 millert 1020: "[-H] [-P] [-S] [-b] [-p prompt]\n [-u username/#uid] ");
1.8 millert 1021: #ifdef HAVE_LOGIN_CAP_H
1022: (void) fprintf(stderr, "[-c class] ");
1023: #endif
1024: #ifdef HAVE_BSD_AUTH_H
1025: (void) fprintf(stderr, "[-a auth_type] ");
1.4 millert 1026: #endif
1.8 millert 1027: (void) fprintf(stderr, "-s | <command>\n");
1.1 millert 1028: exit(exit_val);
1029: }