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