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