[BACK]Return to sudo.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / sudo

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: }