[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.27

1.1       millert     1: /*
1.27    ! millert     2:  * Copyright (c) 1993-1996,1998-2007 Todd C. Miller <Todd.Miller@courtesan.com>
1.1       millert     3:  *
1.23      millert     4:  * Permission to use, copy, modify, and distribute this software for any
                      5:  * purpose with or without fee is hereby granted, provided that the above
                      6:  * copyright notice and this permission notice appear in all copies.
1.1       millert     7:  *
1.23      millert     8:  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
                      9:  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
                     10:  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
                     11:  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
                     12:  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
                     13:  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
                     14:  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1.1       millert    15:  *
1.20      millert    16:  * Sponsored in part by the Defense Advanced Research Projects
                     17:  * Agency (DARPA) and Air Force Research Laboratory, Air Force
                     18:  * Materiel Command, USAF, under agreement number F39502-99-1-0512.
                     19:  *
1.1       millert    20:  * For a brief history of sudo, please see the HISTORY file included
                     21:  * with this distribution.
                     22:  */
                     23:
1.19      millert    24: #define _SUDO_MAIN
1.1       millert    25:
1.23      millert    26: #ifdef __TANDEM
                     27: # include <floss.h>
                     28: #endif
                     29:
1.27    ! millert    30: #include <config.h>
1.1       millert    31:
1.11      millert    32: #include <sys/types.h>
                     33: #include <sys/stat.h>
                     34: #include <sys/param.h>
                     35: #include <sys/socket.h>
                     36: #ifdef HAVE_SETRLIMIT
                     37: # include <sys/time.h>
                     38: # include <sys/resource.h>
                     39: #endif
1.1       millert    40: #include <stdio.h>
                     41: #ifdef STDC_HEADERS
1.11      millert    42: # include <stdlib.h>
                     43: # include <stddef.h>
                     44: #else
                     45: # ifdef HAVE_STDLIB_H
                     46: #  include <stdlib.h>
                     47: # endif
1.1       millert    48: #endif /* STDC_HEADERS */
1.11      millert    49: #ifdef HAVE_STRING_H
                     50: # if defined(HAVE_MEMORY_H) && !defined(STDC_HEADERS)
                     51: #  include <memory.h>
                     52: # endif
                     53: # include <string.h>
                     54: #else
                     55: # ifdef HAVE_STRINGS_H
                     56: #  include <strings.h>
                     57: # endif
                     58: #endif /* HAVE_STRING_H */
1.1       millert    59: #ifdef HAVE_UNISTD_H
1.11      millert    60: # include <unistd.h>
1.1       millert    61: #endif /* HAVE_UNISTD_H */
1.19      millert    62: #ifdef HAVE_ERR_H
                     63: # include <err.h>
                     64: #else
                     65: # include "emul/err.h"
                     66: #endif /* HAVE_ERR_H */
1.1       millert    67: #include <pwd.h>
                     68: #include <errno.h>
                     69: #include <fcntl.h>
                     70: #include <signal.h>
                     71: #include <grp.h>
1.27    ! millert    72: #if TIME_WITH_SYS_TIME
        !            73: # include <time.h>
        !            74: #endif
        !            75: #ifdef HAVE_SETLOCALE
        !            76: # include <locale.h>
        !            77: #endif
1.1       millert    78: #include <netinet/in.h>
                     79: #include <netdb.h>
                     80: #if defined(HAVE_GETPRPWNAM) && defined(HAVE_SET_AUTH_PARAMETERS)
                     81: # ifdef __hpux
                     82: #  undef MAXINT
                     83: #  include <hpsecurity.h>
                     84: # else
                     85: #  include <sys/security.h>
                     86: # endif /* __hpux */
                     87: # include <prot.h>
                     88: #endif /* HAVE_GETPRPWNAM && HAVE_SET_AUTH_PARAMETERS */
1.8       millert    89: #ifdef HAVE_LOGIN_CAP_H
1.4       millert    90: # include <login_cap.h>
                     91: # ifndef LOGIN_DEFROOTCLASS
                     92: #  define LOGIN_DEFROOTCLASS   "daemon"
                     93: # endif
                     94: #endif
1.27    ! millert    95: #ifdef HAVE_PROJECT_H
        !            96: # include <project.h>
        !            97: # include <sys/task.h>
        !            98: #endif
1.1       millert    99:
                    100: #include "sudo.h"
                    101: #include "interfaces.h"
                    102: #include "version.h"
                    103:
                    104: #ifndef lint
1.27    ! millert   105: __unused __unused static const char rcsid[] = "$Sudo: sudo.c,v 1.369.2.26 2007/07/22 19:21:01 millert Exp $";
1.1       millert   106: #endif /* lint */
                    107:
                    108: /*
                    109:  * Prototypes
                    110:  */
1.27    ! millert   111: static int init_vars                   __P((int, char **));
1.19      millert   112: static int parse_args                  __P((int, char **));
1.11      millert   113: static void check_sudoers              __P((void));
                    114: static void initial_setup              __P((void));
                    115: static void set_loginclass             __P((struct passwd *));
1.27    ! millert   116: static void set_project                        __P((struct passwd *));
        !           117: static void usage                      __P((int))
        !           118:                                            __attribute__((__noreturn__));
        !           119: static void usage_excl                 __P((int))
        !           120:                                            __attribute__((__noreturn__));
1.1       millert   121: static void usage_excl                 __P((int));
1.6       millert   122: static struct passwd *get_authpw       __P((void));
1.27    ! millert   123: extern int sudo_edit                   __P((int, char **, char **));
1.11      millert   124: extern void list_matches               __P((void));
1.23      millert   125: extern char **rebuild_env              __P((char **, int, int));
1.27    ! millert   126: extern void validate_env_vars          __P((struct list_member *));
        !           127: extern char **insert_env_vars          __P((char **, struct list_member *));
1.11      millert   128: extern struct passwd *sudo_getpwnam    __P((const char *));
1.1       millert   129: extern struct passwd *sudo_getpwuid    __P((uid_t));
1.23      millert   130: extern struct passwd *sudo_pwdup       __P((const struct passwd *));
1.1       millert   131:
                    132: /*
                    133:  * Globals
                    134:  */
1.19      millert   135: int Argc, NewArgc;
                    136: char **Argv, **NewArgv;
1.23      millert   137: char *prev_user;
1.1       millert   138: struct sudo_user sudo_user;
1.6       millert   139: struct passwd *auth_pw;
1.19      millert   140: FILE *sudoers_fp;
1.1       millert   141: struct interface *interfaces;
                    142: int num_interfaces;
1.4       millert   143: int tgetpass_flags;
1.17      millert   144: uid_t timestamp_uid;
1.1       millert   145: extern int errorlineno;
1.4       millert   146: #if defined(RLIMIT_CORE) && !defined(SUDO_DEVEL)
                    147: static struct rlimit corelimit;
1.20      millert   148: #endif /* RLIMIT_CORE && !SUDO_DEVEL */
1.8       millert   149: #ifdef HAVE_LOGIN_CAP_H
                    150: login_cap_t *lc;
                    151: #endif /* HAVE_LOGIN_CAP_H */
                    152: #ifdef HAVE_BSD_AUTH_H
                    153: char *login_style;
                    154: #endif /* HAVE_BSD_AUTH_H */
1.23      millert   155: sigaction_t saved_sa_int, saved_sa_quit, saved_sa_tstp, saved_sa_chld;
1.1       millert   156:
                    157:
                    158: int
1.11      millert   159: main(argc, argv, envp)
1.1       millert   160:     int argc;
                    161:     char **argv;
1.11      millert   162:     char **envp;
1.1       millert   163: {
                    164:     int validated;
                    165:     int fd;
                    166:     int cmnd_status;
                    167:     int sudo_mode;
1.11      millert   168:     int pwflag;
1.23      millert   169:     sigaction_t sa;
1.11      millert   170:     extern int printmatches;
1.1       millert   171:     extern char **environ;
                    172:
1.27    ! millert   173: #ifdef HAVE_SETLOCALE
        !           174:     setlocale(LC_ALL, "");
        !           175: #endif
        !           176:
1.19      millert   177:     Argv = argv;
1.23      millert   178:     if ((Argc = argc) < 1)
                    179:        usage(1);
1.19      millert   180:
1.1       millert   181:     /* Must be done as the first thing... */
                    182: #if defined(HAVE_GETPRPWNAM) && defined(HAVE_SET_AUTH_PARAMETERS)
1.19      millert   183:     (void) set_auth_parameters(Argc, Argv);
1.1       millert   184: # ifdef HAVE_INITPRIVS
                    185:     initprivs();
                    186: # endif
                    187: #endif /* HAVE_GETPRPWNAM && HAVE_SET_AUTH_PARAMETERS */
                    188:
1.19      millert   189:     if (geteuid() != 0)
                    190:        errx(1, "must be setuid root");
1.1       millert   191:
                    192:     /*
1.17      millert   193:      * Signal setup:
                    194:      * Ignore keyboard-generated signals so the user cannot interrupt
                    195:      *  us at some point and avoid the logging.
                    196:      *  Install handler to wait for children when they exit.
1.1       millert   197:      */
1.11      millert   198:     sigemptyset(&sa.sa_mask);
                    199:     sa.sa_flags = SA_RESTART;
                    200:     sa.sa_handler = SIG_IGN;
1.17      millert   201:     (void) sigaction(SIGINT, &sa, &saved_sa_int);
                    202:     (void) sigaction(SIGQUIT, &sa, &saved_sa_quit);
                    203:     (void) sigaction(SIGTSTP, &sa, &saved_sa_tstp);
                    204:     sa.sa_handler = reapchild;
                    205:     (void) sigaction(SIGCHLD, &sa, &saved_sa_chld);
1.1       millert   206:
                    207:     /*
1.27    ! millert   208:      * Turn off core dumps and close open files.
1.1       millert   209:      */
                    210:     initial_setup();
1.11      millert   211:     setpwent();
1.1       millert   212:
                    213:     /* Parse our arguments. */
1.19      millert   214:     sudo_mode = parse_args(Argc, Argv);
1.1       millert   215:
                    216:     /* Setup defaults data structures. */
                    217:     init_defaults();
                    218:
1.11      millert   219:     /* Load the list of local ip addresses and netmasks.  */
                    220:     load_interfaces();
                    221:
                    222:     pwflag = 0;
1.23      millert   223:     if (ISSET(sudo_mode, MODE_SHELL))
1.1       millert   224:        user_cmnd = "shell";
1.23      millert   225:     else if (ISSET(sudo_mode, MODE_EDIT))
                    226:        user_cmnd = "sudoedit";
1.1       millert   227:     else
                    228:        switch (sudo_mode) {
                    229:            case MODE_VERSION:
                    230:                (void) printf("Sudo version %s\n", version);
                    231:                if (getuid() == 0) {
                    232:                    putchar('\n');
1.27    ! millert   233:                    (void) printf("Sudoers path: %s\n", _PATH_SUDOERS);
1.1       millert   234:                    dump_auth_methods();
                    235:                    dump_defaults();
1.11      millert   236:                    dump_interfaces();
1.1       millert   237:                }
                    238:                exit(0);
                    239:                break;
                    240:            case MODE_HELP:
                    241:                usage(0);
                    242:                break;
                    243:            case MODE_VALIDATE:
                    244:                user_cmnd = "validate";
1.23      millert   245:                pwflag = I_VERIFYPW;
1.1       millert   246:                break;
                    247:            case MODE_KILL:
                    248:            case MODE_INVALIDATE:
                    249:                user_cmnd = "kill";
1.11      millert   250:                pwflag = -1;
1.1       millert   251:                break;
                    252:            case MODE_LISTDEFS:
                    253:                list_options();
                    254:                exit(0);
                    255:                break;
                    256:            case MODE_LIST:
                    257:                user_cmnd = "list";
1.23      millert   258:                pwflag = I_LISTPW;
1.1       millert   259:                printmatches = 1;
                    260:                break;
                    261:        }
                    262:
                    263:     /* Must have a command to run... */
                    264:     if (user_cmnd == NULL && NewArgc == 0)
                    265:        usage(1);
                    266:
1.27    ! millert   267:     cmnd_status = init_vars(sudo_mode, environ);
1.1       millert   268:
1.23      millert   269: #ifdef HAVE_LDAP
                    270:     validated = sudo_ldap_check(pwflag);
                    271:
                    272:     /* Skip reading /etc/sudoers if LDAP told us to */
                    273:     if (def_ignore_local_sudoers); /* skips */
                    274:     else if (ISSET(validated, VALIDATE_OK) && !printmatches); /* skips */
                    275:     else if (ISSET(validated, VALIDATE_OK) && printmatches)
                    276:     {
                    277:        check_sudoers();        /* check mode/owner on _PATH_SUDOERS */
                    278:
                    279:        /* User is found in LDAP and we want a list of all sudo commands the
                    280:         * user can do, so consult sudoers but throw away result.
                    281:         */
                    282:        sudoers_lookup(pwflag);
                    283:     }
                    284:     else
                    285: #endif
                    286:     {
                    287:        check_sudoers();        /* check mode/owner on _PATH_SUDOERS */
1.1       millert   288:
1.23      millert   289:        /* Validate the user but don't search for pseudo-commands. */
                    290:        validated = sudoers_lookup(pwflag);
                    291:     }
1.26      millert   292:     if (safe_cmnd == NULL)
1.27    ! millert   293:        safe_cmnd = estrdup(user_cmnd);
1.1       millert   294:
1.11      millert   295:     /*
1.17      millert   296:      * Look up the timestamp dir owner if one is specified.
                    297:      */
1.23      millert   298:     if (def_timestampowner) {
1.17      millert   299:        struct passwd *pw;
                    300:
1.23      millert   301:        if (*def_timestampowner == '#')
                    302:            pw = getpwuid(atoi(def_timestampowner + 1));
1.17      millert   303:        else
1.23      millert   304:            pw = getpwnam(def_timestampowner);
1.17      millert   305:        if (!pw)
                    306:            log_error(0, "timestamp owner (%s): No such user",
1.23      millert   307:                def_timestampowner);
1.17      millert   308:        timestamp_uid = pw->pw_uid;
                    309:     }
                    310:
1.2       millert   311:     /* This goes after the sudoers parse since we honor sudoers options. */
1.1       millert   312:     if (sudo_mode == MODE_KILL || sudo_mode == MODE_INVALIDATE) {
                    313:        remove_timestamp((sudo_mode == MODE_KILL));
                    314:        exit(0);
                    315:     }
                    316:
1.23      millert   317:     if (ISSET(validated, VALIDATE_ERROR))
1.1       millert   318:        log_error(0, "parse error in %s near line %d", _PATH_SUDOERS,
                    319:            errorlineno);
                    320:
                    321:     /* Is root even allowed to run sudo? */
1.23      millert   322:     if (user_uid == 0 && !def_root_sudo) {
1.17      millert   323:        (void) fprintf(stderr,
                    324:            "Sorry, %s has been configured to not allow root to run it.\n",
1.19      millert   325:            getprogname());
1.1       millert   326:        exit(1);
                    327:     }
                    328:
1.11      millert   329:     /* If given the -P option, set the "preserve_groups" flag. */
1.23      millert   330:     if (ISSET(sudo_mode, MODE_PRESERVE_GROUPS))
                    331:        def_preserve_groups = TRUE;
1.11      millert   332:
1.3       millert   333:     /* If no command line args and "set_home" is not set, error out. */
1.23      millert   334:     if (ISSET(sudo_mode, MODE_IMPLIED_SHELL) && !def_shell_noargs)
1.3       millert   335:        usage(1);
                    336:
1.1       millert   337:     /* Bail if a tty is required and we don't have one.  */
1.23      millert   338:     if (def_requiretty) {
1.1       millert   339:        if ((fd = open(_PATH_TTY, O_RDWR|O_NOCTTY)) == -1)
                    340:            log_error(NO_MAIL, "sorry, you must have a tty to run sudo");
                    341:        else
                    342:            (void) close(fd);
                    343:     }
                    344:
1.27    ! millert   345:     /* User may have overriden environment resetting via the -E flag. */
        !           346:     if (ISSET(sudo_mode, MODE_PRESERVE_ENV) && ISSET(validated, FLAG_SETENV))
        !           347:        def_env_reset = FALSE;
        !           348:
        !           349:     /* Build a new environment that avoids any nasty bits. */
        !           350:     environ = rebuild_env(environ, sudo_mode, ISSET(validated, FLAG_NOEXEC));
        !           351:
1.6       millert   352:     /* Fill in passwd struct based on user we are authenticating as.  */
                    353:     auth_pw = get_authpw();
1.4       millert   354:
1.23      millert   355:     /* Require a password if sudoers says so.  */
                    356:     if (!ISSET(validated, FLAG_NOPASS))
1.27    ! millert   357:        check_user(validated);
1.23      millert   358:
                    359:     /* If run as root with SUDO_USER set, set sudo_user.pw to that user. */
                    360:     if (user_uid == 0 && prev_user != NULL && strcmp(prev_user, "root") != 0) {
                    361:            struct passwd *pw;
                    362:
                    363:            if ((pw = sudo_getpwnam(prev_user)) != NULL) {
1.27    ! millert   364:                    efree(sudo_user.pw);
1.23      millert   365:                    sudo_user.pw = pw;
                    366:            }
                    367:     }
1.1       millert   368:
1.23      millert   369:     if (ISSET(validated, VALIDATE_OK)) {
1.1       millert   370:        /* Finally tell the user if the command did not exist. */
                    371:        if (cmnd_status == NOT_FOUND_DOT) {
1.19      millert   372:            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   373:            exit(1);
                    374:        } else if (cmnd_status == NOT_FOUND) {
1.19      millert   375:            warnx("%s: command not found", user_cmnd);
1.1       millert   376:            exit(1);
                    377:        }
                    378:
1.27    ! millert   379:        /* If user specified env vars make sure sudoers allows it. */
        !           380:        if (ISSET(sudo_mode, MODE_RUN) && !ISSET(validated, FLAG_SETENV)) {
        !           381:            if (ISSET(sudo_mode, MODE_PRESERVE_ENV))
        !           382:                log_error(NO_MAIL,
        !           383:                    "sorry, you are not allowed to preserve the environment");
        !           384:            else
        !           385:                validate_env_vars(sudo_user.env_vars);
        !           386:        }
        !           387:
1.1       millert   388:        log_auth(validated, 1);
                    389:        if (sudo_mode == MODE_VALIDATE)
                    390:            exit(0);
                    391:        else if (sudo_mode == MODE_LIST) {
                    392:            list_matches();
1.23      millert   393: #ifdef HAVE_LDAP
                    394:            sudo_ldap_list_matches();
                    395: #endif
1.1       millert   396:            exit(0);
                    397:        }
                    398:
                    399:        /* Override user's umask if configured to do so. */
1.23      millert   400:        if (def_umask != 0777)
                    401:            (void) umask(def_umask);
1.1       millert   402:
1.4       millert   403:        /* Restore coredumpsize resource limit. */
                    404: #if defined(RLIMIT_CORE) && !defined(SUDO_DEVEL)
                    405:        (void) setrlimit(RLIMIT_CORE, &corelimit);
1.20      millert   406: #endif /* RLIMIT_CORE && !SUDO_DEVEL */
1.4       millert   407:
1.23      millert   408:        /* Become specified user or root if executing a command. */
                    409:        if (ISSET(sudo_mode, MODE_RUN))
                    410:            set_perms(PERM_FULL_RUNAS);
1.16      millert   411:
                    412:        /* Close the password and group files */
                    413:        endpwent();
                    414:        endgrent();
1.12      millert   415:
1.23      millert   416:        if (ISSET(sudo_mode, MODE_LOGIN_SHELL)) {
                    417:            char *p;
                    418:
                    419:            /* Convert /bin/sh -> -sh so shell knows it is a login shell */
                    420:            if ((p = strrchr(NewArgv[0], '/')) == NULL)
                    421:                p = NewArgv[0];
                    422:            *p = '-';
                    423:            NewArgv[0] = p;
                    424:
                    425:            /* Change to target user's homedir. */
                    426:            if (chdir(runas_pw->pw_dir) == -1)
                    427:                warn("unable to change directory to %s", runas_pw->pw_dir);
                    428:        }
                    429:
                    430:        if (ISSET(sudo_mode, MODE_EDIT))
1.27    ! millert   431:            exit(sudo_edit(NewArgc, NewArgv, envp));
        !           432:
        !           433:        /* Insert user-specified environment variables. */
        !           434:        environ = insert_env_vars(environ, sudo_user.env_vars);
1.23      millert   435:
1.17      millert   436:        /* Restore signal handlers before we exec. */
                    437:        (void) sigaction(SIGINT, &saved_sa_int, NULL);
                    438:        (void) sigaction(SIGQUIT, &saved_sa_quit, NULL);
                    439:        (void) sigaction(SIGTSTP, &saved_sa_tstp, NULL);
                    440:        (void) sigaction(SIGCHLD, &saved_sa_chld, NULL);
                    441:
1.1       millert   442: #ifndef PROFILING
1.23      millert   443:        if (ISSET(sudo_mode, MODE_BACKGROUND) && fork() > 0)
1.1       millert   444:            exit(0);
                    445:        else
1.27    ! millert   446:            execve(safe_cmnd, NewArgv, environ);
1.1       millert   447: #else
                    448:        exit(0);
                    449: #endif /* PROFILING */
                    450:        /*
                    451:         * If we got here then the exec() failed...
                    452:         */
1.27    ! millert   453:        if (errno == ENOEXEC) {
        !           454:            NewArgv--;                  /* at least one extra slot... */
        !           455:            NewArgv[0] = "sh";
        !           456:            NewArgv[1] = safe_cmnd;
        !           457:            execve(_PATH_BSHELL, NewArgv, environ);
        !           458:        }
1.19      millert   459:        warn("unable to execute %s", safe_cmnd);
1.11      millert   460:        exit(127);
1.23      millert   461:     } else if (ISSET(validated, FLAG_NO_USER) || (validated & FLAG_NO_HOST)) {
1.1       millert   462:        log_auth(validated, 1);
                    463:        exit(1);
1.23      millert   464:     } else if (ISSET(validated, VALIDATE_NOT_OK)) {
                    465:        if (def_path_info) {
1.1       millert   466:            /*
                    467:             * We'd like to not leak path info at all here, but that can
                    468:             * *really* confuse the users.  To really close the leak we'd
                    469:             * have to say "not allowed to run foo" even when the problem
                    470:             * is just "no foo in path" since the user can trivially set
                    471:             * their path to just contain a single dir.
                    472:             */
                    473:            log_auth(validated,
                    474:                !(cmnd_status == NOT_FOUND_DOT || cmnd_status == NOT_FOUND));
                    475:            if (cmnd_status == NOT_FOUND)
1.19      millert   476:                warnx("%s: command not found", user_cmnd);
1.1       millert   477:            else if (cmnd_status == NOT_FOUND_DOT)
1.19      millert   478:                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   479:        } else {
                    480:            /* Just tell the user they are not allowed to run foo. */
                    481:            log_auth(validated, 1);
                    482:        }
                    483:        exit(1);
                    484:     } else {
                    485:        /* should never get here */
                    486:        log_auth(validated, 1);
                    487:        exit(1);
                    488:     }
                    489:     exit(0);   /* not reached */
                    490: }
                    491:
                    492: /*
                    493:  * Initialize timezone, set umask, fill in ``sudo_user'' struct and
                    494:  * load the ``interfaces'' array.
                    495:  */
                    496: static int
1.27    ! millert   497: init_vars(sudo_mode, envp)
1.1       millert   498:     int sudo_mode;
1.27    ! millert   499:     char **envp;
1.1       millert   500: {
1.27    ! millert   501:     char *p, **ep, thost[MAXHOSTNAMELEN];
1.11      millert   502:     int nohostname, rval;
1.1       millert   503:
                    504:     /* Sanity check command from user. */
1.23      millert   505:     if (user_cmnd == NULL && strlen(NewArgv[0]) >= PATH_MAX)
1.19      millert   506:        errx(1, "%s: File name too long", NewArgv[0]);
1.1       millert   507:
                    508: #ifdef HAVE_TZSET
                    509:     (void) tzset();            /* set the timezone if applicable */
                    510: #endif /* HAVE_TZSET */
                    511:
                    512:     /* Default value for cmnd and cwd, overridden later. */
                    513:     if (user_cmnd == NULL)
                    514:        user_cmnd = NewArgv[0];
1.17      millert   515:     (void) strlcpy(user_cwd, "unknown", sizeof(user_cwd));
1.1       millert   516:
                    517:     /*
                    518:      * We avoid gethostbyname() if possible since we don't want
                    519:      * sudo to block if DNS or NIS is hosed.
                    520:      * "host" is the (possibly fully-qualified) hostname and
                    521:      * "shost" is the unqualified form of the hostname.
                    522:      */
1.11      millert   523:     nohostname = gethostname(thost, sizeof(thost));
                    524:     if (nohostname)
                    525:        user_host = user_shost = "localhost";
                    526:     else {
1.1       millert   527:        user_host = estrdup(thost);
1.23      millert   528:        if (def_fqdn) {
1.11      millert   529:            /* Defer call to set_fqdn() until log_error() is safe. */
                    530:            user_shost = user_host;
1.1       millert   531:        } else {
1.11      millert   532:            if ((p = strchr(user_host, '.'))) {
                    533:                *p = '\0';
                    534:                user_shost = estrdup(user_host);
                    535:                *p = '.';
                    536:            } else {
                    537:                user_shost = user_host;
                    538:            }
1.1       millert   539:        }
                    540:     }
                    541:
                    542:     if ((p = ttyname(STDIN_FILENO)) || (p = ttyname(STDOUT_FILENO))) {
                    543:        if (strncmp(p, _PATH_DEV, sizeof(_PATH_DEV) - 1) == 0)
                    544:            p += sizeof(_PATH_DEV) - 1;
                    545:        user_tty = estrdup(p);
                    546:     } else
                    547:        user_tty = "unknown";
                    548:
1.27    ! millert   549:     for (ep = envp; *ep; ep++) {
        !           550:        switch (**ep) {
        !           551:            case 'P':
        !           552:                if (strncmp("PATH=", *ep, 5) == 0)
        !           553:                    user_path = *ep + 5;
        !           554:                break;
        !           555:            case 'S':
        !           556:                if (strncmp("SHELL=", *ep, 6) == 0)
        !           557:                    user_shell = *ep + 6;
        !           558:                else if (!user_prompt && strncmp("SUDO_PROMPT=", *ep, 12) == 0)
        !           559:                    user_prompt = *ep + 12;
        !           560:                else if (strncmp("SUDO_USER=", *ep, 10) == 0)
        !           561:                    prev_user = *ep + 10;
        !           562:                break;
        !           563:
        !           564:            }
        !           565:     }
        !           566:
1.1       millert   567:     /*
                    568:      * Get a local copy of the user's struct passwd with the shadow password
                    569:      * if necessary.  It is assumed that euid is 0 at this point so we
                    570:      * can read the shadow passwd file if necessary.
                    571:      */
                    572:     if ((sudo_user.pw = sudo_getpwuid(getuid())) == NULL) {
                    573:        /* Need to make a fake struct passwd for logging to work. */
                    574:        struct passwd pw;
                    575:        char pw_name[MAX_UID_T_LEN + 1];
                    576:
                    577:        pw.pw_uid = getuid();
1.17      millert   578:        (void) snprintf(pw_name, sizeof(pw_name), "%lu",
                    579:            (unsigned long) pw.pw_uid);
1.1       millert   580:        pw.pw_name = pw_name;
                    581:        sudo_user.pw = &pw;
                    582:
1.21      millert   583:        /*
                    584:         * If we are in -k/-K mode, just spew to stderr.  It is not unusual for
                    585:         * users to place "sudo -k" in a .logout file which can cause sudo to
                    586:         * be run during reboot after the YP/NIS/NIS+/LDAP/etc daemon has died.
                    587:         */
                    588:        if (sudo_mode & (MODE_INVALIDATE|MODE_KILL))
                    589:            errx(1, "uid %s does not exist in the passwd file!", pw_name);
                    590:        log_error(0, "uid %s does not exist in the passwd file!", pw_name);
1.1       millert   591:     }
1.15      millert   592:     if (user_shell == NULL || *user_shell == '\0')
                    593:        user_shell = sudo_user.pw->pw_shell;
1.1       millert   594:
                    595:     /* It is now safe to use log_error() and set_perms() */
                    596:
1.27    ! millert   597: #ifdef HAVE_GETGROUPS
        !           598:     if ((user_ngroups = getgroups(0, NULL)) > 0) {
        !           599:        user_groups = emalloc2(user_ngroups, sizeof(gid_t));
        !           600:        if (getgroups(user_ngroups, user_groups) < 0)
        !           601:            log_error(USE_ERRNO|MSG_ONLY, "can't get group vector");
        !           602:     } else
        !           603:        user_ngroups = 0;
        !           604: #endif
        !           605:
1.23      millert   606:     if (def_fqdn)
                    607:        set_fqdn();                     /* may call log_error() */
1.11      millert   608:
                    609:     if (nohostname)
                    610:        log_error(USE_ERRNO|MSG_ONLY, "can't get hostname");
                    611:
1.23      millert   612:     set_runaspw(*user_runas);          /* may call log_error() */
                    613:     if (*user_runas[0] == '#' && runas_pw->pw_name && runas_pw->pw_name[0])
                    614:        *user_runas = estrdup(runas_pw->pw_name);
                    615:
1.11      millert   616:     /*
1.1       millert   617:      * Get current working directory.  Try as user, fall back to root.
                    618:      */
1.17      millert   619:     set_perms(PERM_USER);
1.1       millert   620:     if (!getcwd(user_cwd, sizeof(user_cwd))) {
1.17      millert   621:        set_perms(PERM_ROOT);
1.1       millert   622:        if (!getcwd(user_cwd, sizeof(user_cwd))) {
1.19      millert   623:            warnx("cannot get working directory");
1.17      millert   624:            (void) strlcpy(user_cwd, "unknown", sizeof(user_cwd));
1.1       millert   625:        }
                    626:     } else
1.17      millert   627:        set_perms(PERM_ROOT);
1.1       millert   628:
                    629:     /*
1.23      millert   630:      * If we were given the '-e', '-i' or '-s' options we need to redo
1.1       millert   631:      * NewArgv and NewArgc.
                    632:      */
1.23      millert   633:     if ((sudo_mode & (MODE_SHELL | MODE_EDIT))) {
1.1       millert   634:        char **dst, **src = NewArgv;
                    635:
1.27    ! millert   636:        /* Allocate an extra slot for execve() failure (ENOEXEC). */
        !           637:        NewArgv = (char **) emalloc2((++NewArgc + 2), sizeof(char *));
        !           638:        NewArgv++;
1.23      millert   639:        if (ISSET(sudo_mode, MODE_EDIT))
                    640:            NewArgv[0] = "sudoedit";
                    641:        else if (ISSET(sudo_mode, MODE_LOGIN_SHELL))
                    642:            NewArgv[0] = runas_pw->pw_shell;
                    643:        else if (user_shell && *user_shell)
1.1       millert   644:            NewArgv[0] = user_shell;
1.23      millert   645:        else
1.19      millert   646:            errx(1, "unable to determine shell");
1.1       millert   647:
1.19      millert   648:        /* copy the args from NewArgv */
1.1       millert   649:        for (dst = NewArgv + 1; (*dst = *src) != NULL; ++src, ++dst)
1.27    ! millert   650:            continue;
1.1       millert   651:     }
                    652:
1.8       millert   653:     /* Set login class if applicable. */
                    654:     set_loginclass(sudo_user.pw);
                    655:
1.27    ! millert   656:     /* Set project if applicable. */
        !           657:     set_project(runas_pw);
        !           658:
1.1       millert   659:     /* Resolve the path and return. */
1.23      millert   660:     rval = FOUND;
                    661:     user_stat = emalloc(sizeof(struct stat));
                    662:     if (sudo_mode & (MODE_RUN | MODE_EDIT)) {
                    663:        if (ISSET(sudo_mode, MODE_RUN)) {
                    664:            /* XXX - default_runas may be modified during parsing of sudoers */
                    665:            set_perms(PERM_RUNAS);
                    666:            rval = find_path(NewArgv[0], &user_cmnd, user_stat, user_path);
1.17      millert   667:            set_perms(PERM_ROOT);
1.23      millert   668:            if (rval != FOUND) {
                    669:                /* Failed as root, try as invoking user. */
                    670:                set_perms(PERM_USER);
                    671:                rval = find_path(NewArgv[0], &user_cmnd, user_stat, user_path);
                    672:                set_perms(PERM_ROOT);
                    673:            }
1.11      millert   674:        }
                    675:
                    676:        /* set user_args */
                    677:        if (NewArgc > 1) {
                    678:            char *to, **from;
1.17      millert   679:            size_t size, n;
1.11      millert   680:
1.23      millert   681:            /* If we didn't realloc NewArgv it is contiguous so just count. */
                    682:            if (!(sudo_mode & (MODE_SHELL | MODE_EDIT))) {
1.11      millert   683:                size = (size_t) (NewArgv[NewArgc-1] - NewArgv[1]) +
                    684:                        strlen(NewArgv[NewArgc-1]) + 1;
                    685:            } else {
                    686:                for (size = 0, from = NewArgv + 1; *from; from++)
                    687:                    size += strlen(*from) + 1;
                    688:            }
                    689:
1.23      millert   690:            /* Alloc and build up user_args. */
1.17      millert   691:            user_args = (char *) emalloc(size);
                    692:            for (to = user_args, from = NewArgv + 1; *from; from++) {
                    693:                n = strlcpy(to, *from, size - (to - user_args));
1.19      millert   694:                if (n >= size - (to - user_args))
                    695:                    errx(1, "internal error, init_vars() overflow");
1.17      millert   696:                to += n;
1.11      millert   697:                *to++ = ' ';
                    698:            }
                    699:            *--to = '\0';
                    700:        }
1.23      millert   701:     }
                    702:     if ((user_base = strrchr(user_cmnd, '/')) != NULL)
                    703:        user_base++;
                    704:     else
                    705:        user_base = user_cmnd;
1.11      millert   706:
                    707:     return(rval);
1.1       millert   708: }
                    709:
                    710: /*
                    711:  * Command line argument parsing, can't use getopt(3).
                    712:  */
                    713: static int
1.19      millert   714: parse_args(argc, argv)
                    715:     int argc;
                    716:     char **argv;
1.1       millert   717: {
1.17      millert   718:     int rval = MODE_RUN;               /* what mode is sudo to be run in? */
1.1       millert   719:     int excl = 0;                      /* exclusive arg, no others allowed */
                    720:
1.19      millert   721:     NewArgv = argv + 1;
                    722:     NewArgc = argc - 1;
1.1       millert   723:
1.23      millert   724:     /* First, check to see if we were invoked as "sudoedit". */
                    725:     if (strcmp(getprogname(), "sudoedit") == 0) {
                    726:        rval = MODE_EDIT;
                    727:        excl = 'e';
                    728:     } else
                    729:        rval = MODE_RUN;
                    730:
1.27    ! millert   731:     while (NewArgc > 0) {
        !           732:        if (NewArgv[0][0] == '-') {
        !           733:            if (NewArgv[0][1] != '\0' && NewArgv[0][2] != '\0')
        !           734:                warnx("please use single character options");
        !           735:
        !           736:            switch (NewArgv[0][1]) {
        !           737:                case 'p':
        !           738:                    /* Must have an associated prompt. */
        !           739:                    if (NewArgv[1] == NULL)
        !           740:                        usage(1);
1.1       millert   741:
1.27    ! millert   742:                    user_prompt = NewArgv[1];
1.1       millert   743:
1.27    ! millert   744:                    NewArgc--;
        !           745:                    NewArgv++;
        !           746:                    break;
        !           747:                case 'u':
        !           748:                    /* Must have an associated runas user. */
        !           749:                    if (NewArgv[1] == NULL)
        !           750:                        usage(1);
1.1       millert   751:
1.27    ! millert   752:                    user_runas = &NewArgv[1];
1.1       millert   753:
1.27    ! millert   754:                    NewArgc--;
        !           755:                    NewArgv++;
        !           756:                    break;
1.8       millert   757: #ifdef HAVE_BSD_AUTH_H
1.27    ! millert   758:                case 'a':
        !           759:                    /* Must have an associated authentication style. */
        !           760:                    if (NewArgv[1] == NULL)
        !           761:                        usage(1);
1.8       millert   762:
1.27    ! millert   763:                    login_style = NewArgv[1];
1.8       millert   764:
1.27    ! millert   765:                    NewArgc--;
        !           766:                    NewArgv++;
        !           767:                    break;
1.8       millert   768: #endif
                    769: #ifdef HAVE_LOGIN_CAP_H
1.27    ! millert   770:                case 'c':
        !           771:                    /* Must have an associated login class. */
        !           772:                    if (NewArgv[1] == NULL)
        !           773:                        usage(1);
1.4       millert   774:
1.27    ! millert   775:                    login_class = NewArgv[1];
        !           776:                    def_use_loginclass = TRUE;
1.4       millert   777:
1.27    ! millert   778:                    NewArgc--;
        !           779:                    NewArgv++;
        !           780:                    break;
1.4       millert   781: #endif
1.27    ! millert   782:                case 'b':
        !           783:                    SET(rval, MODE_BACKGROUND);
        !           784:                    break;
        !           785:                case 'e':
        !           786:                    rval = MODE_EDIT;
        !           787:                    if (excl && excl != 'e')
        !           788:                        usage_excl(1);
        !           789:                    excl = 'e';
        !           790:                    break;
        !           791:                case 'v':
        !           792:                    rval = MODE_VALIDATE;
        !           793:                    if (excl && excl != 'v')
        !           794:                        usage_excl(1);
        !           795:                    excl = 'v';
        !           796:                    break;
        !           797:                case 'i':
        !           798:                    SET(rval, (MODE_LOGIN_SHELL | MODE_SHELL));
        !           799:                    def_env_reset = TRUE;
        !           800:                    if (excl && excl != 'i')
        !           801:                        usage_excl(1);
        !           802:                    excl = 'i';
        !           803:                    break;
        !           804:                case 'k':
        !           805:                    rval = MODE_INVALIDATE;
        !           806:                    if (excl && excl != 'k')
        !           807:                        usage_excl(1);
        !           808:                    excl = 'k';
        !           809:                    break;
        !           810:                case 'K':
        !           811:                    rval = MODE_KILL;
        !           812:                    if (excl && excl != 'K')
        !           813:                        usage_excl(1);
        !           814:                    excl = 'K';
        !           815:                    break;
        !           816:                case 'L':
        !           817:                    rval = MODE_LISTDEFS;
        !           818:                    if (excl && excl != 'L')
        !           819:                        usage_excl(1);
        !           820:                    excl = 'L';
        !           821:                    break;
        !           822:                case 'l':
        !           823:                    rval = MODE_LIST;
        !           824:                    if (excl && excl != 'l')
        !           825:                        usage_excl(1);
        !           826:                    excl = 'l';
        !           827:                    break;
        !           828:                case 'V':
        !           829:                    rval = MODE_VERSION;
        !           830:                    if (excl && excl != 'V')
        !           831:                        usage_excl(1);
        !           832:                    excl = 'V';
        !           833:                    break;
        !           834:                case 'h':
        !           835:                    rval = MODE_HELP;
        !           836:                    if (excl && excl != 'h')
        !           837:                        usage_excl(1);
        !           838:                    excl = 'h';
        !           839:                    break;
        !           840:                case 's':
        !           841:                    SET(rval, MODE_SHELL);
        !           842:                    if (excl && excl != 's')
        !           843:                        usage_excl(1);
        !           844:                    excl = 's';
        !           845:                    break;
        !           846:                case 'H':
        !           847:                    SET(rval, MODE_RESET_HOME);
        !           848:                    break;
        !           849:                case 'P':
        !           850:                    SET(rval, MODE_PRESERVE_GROUPS);
        !           851:                    break;
        !           852:                case 'S':
        !           853:                    SET(tgetpass_flags, TGP_STDIN);
        !           854:                    break;
        !           855:                case 'E':
        !           856:                    SET(rval, MODE_PRESERVE_ENV);
        !           857:                    break;
        !           858:                case '-':
        !           859:                    NewArgc--;
        !           860:                    NewArgv++;
        !           861:                    goto args_done;
        !           862:                case '\0':
        !           863:                    warnx("'-' requires an argument");
        !           864:                    usage(1);
        !           865:                default:
        !           866:                    warnx("illegal option `%s'", NewArgv[0]);
        !           867:                    usage(1);
        !           868:            }
        !           869:        } else if (NewArgv[0][0] != '/' && strchr(NewArgv[0], '=') != NULL) {
        !           870:            /* Could be an environment variable. */
        !           871:            struct list_member *ev;
        !           872:            ev = emalloc(sizeof(*ev));
        !           873:            ev->value = NewArgv[0];
        !           874:            ev->next = sudo_user.env_vars;
        !           875:            sudo_user.env_vars = ev;
        !           876:        } else {
        !           877:            /* Not an arg */
        !           878:            break;
1.1       millert   879:        }
                    880:        NewArgc--;
                    881:        NewArgv++;
                    882:     }
1.27    ! millert   883: args_done:
        !           884:
        !           885:     if (ISSET(rval, MODE_EDIT) &&
        !           886:        (ISSET(rval, MODE_PRESERVE_ENV) || sudo_user.env_vars != NULL)) {
        !           887:        if (ISSET(rval, MODE_PRESERVE_ENV))
        !           888:            warnx("the `-E' option is not valid in edit mode");
        !           889:        if (sudo_user.env_vars != NULL)
        !           890:            warnx("you may not specify environment variables in edit mode");
        !           891:        usage(1);
        !           892:     }
1.1       millert   893:
1.24      millert   894:     if (user_runas != NULL && !ISSET(rval, (MODE_EDIT|MODE_RUN))) {
                    895:        if (excl != '\0')
                    896:            warnx("the `-u' and '-%c' options may not be used together", excl);
                    897:        usage(1);
                    898:     }
1.23      millert   899:     if ((NewArgc == 0 && (rval & MODE_EDIT)) ||
                    900:        (NewArgc > 0 && !(rval & (MODE_RUN | MODE_EDIT))))
1.1       millert   901:        usage(1);
1.27    ! millert   902:     if (NewArgc == 0 && rval == MODE_RUN)
        !           903:        SET(rval, (MODE_IMPLIED_SHELL | MODE_SHELL));
1.1       millert   904:
                    905:     return(rval);
                    906: }
                    907:
                    908: /*
                    909:  * Sanity check sudoers mode/owner/type.
                    910:  * Leaves a file pointer to the sudoers file open in ``fp''.
                    911:  */
                    912: static void
                    913: check_sudoers()
                    914: {
                    915:     struct stat statbuf;
                    916:     int rootstat, i;
                    917:     char c;
                    918:
                    919:     /*
                    920:      * Fix the mode and group on sudoers file from old default.
1.23      millert   921:      * Only works if file system is readable/writable by root.
1.1       millert   922:      */
1.17      millert   923:     if ((rootstat = stat_sudoers(_PATH_SUDOERS, &statbuf)) == 0 &&
1.1       millert   924:        SUDOERS_UID == statbuf.st_uid && SUDOERS_MODE != 0400 &&
                    925:        (statbuf.st_mode & 0007777) == 0400) {
                    926:
                    927:        if (chmod(_PATH_SUDOERS, SUDOERS_MODE) == 0) {
1.19      millert   928:            warnx("fixed mode on %s", _PATH_SUDOERS);
1.23      millert   929:            SET(statbuf.st_mode, SUDOERS_MODE);
1.1       millert   930:            if (statbuf.st_gid != SUDOERS_GID) {
                    931:                if (!chown(_PATH_SUDOERS,(uid_t) -1,SUDOERS_GID)) {
1.19      millert   932:                    warnx("set group on %s", _PATH_SUDOERS);
1.1       millert   933:                    statbuf.st_gid = SUDOERS_GID;
1.19      millert   934:                } else
                    935:                    warn("unable to set group on %s", _PATH_SUDOERS);
1.1       millert   936:            }
1.19      millert   937:        } else
                    938:            warn("unable to fix mode on %s", _PATH_SUDOERS);
1.1       millert   939:     }
                    940:
                    941:     /*
                    942:      * Sanity checks on sudoers file.  Must be done as sudoers
                    943:      * file owner.  We already did a stat as root, so use that
                    944:      * data if we can't stat as sudoers file owner.
                    945:      */
1.17      millert   946:     set_perms(PERM_SUDOERS);
1.1       millert   947:
1.17      millert   948:     if (rootstat != 0 && stat_sudoers(_PATH_SUDOERS, &statbuf) != 0)
1.1       millert   949:        log_error(USE_ERRNO, "can't stat %s", _PATH_SUDOERS);
                    950:     else if (!S_ISREG(statbuf.st_mode))
                    951:        log_error(0, "%s is not a regular file", _PATH_SUDOERS);
1.9       millert   952:     else if (statbuf.st_size == 0)
                    953:        log_error(0, "%s is zero length", _PATH_SUDOERS);
1.1       millert   954:     else if ((statbuf.st_mode & 07777) != SUDOERS_MODE)
                    955:        log_error(0, "%s is mode 0%o, should be 0%o", _PATH_SUDOERS,
1.27    ! millert   956:            (unsigned int) (statbuf.st_mode & 07777),
        !           957:            (unsigned int) SUDOERS_MODE);
1.1       millert   958:     else if (statbuf.st_uid != SUDOERS_UID)
1.17      millert   959:        log_error(0, "%s is owned by uid %lu, should be %lu", _PATH_SUDOERS,
1.25      millert   960:            (unsigned long) statbuf.st_uid, (unsigned long) SUDOERS_UID);
1.1       millert   961:     else if (statbuf.st_gid != SUDOERS_GID)
1.17      millert   962:        log_error(0, "%s is owned by gid %lu, should be %lu", _PATH_SUDOERS,
1.25      millert   963:            (unsigned long) statbuf.st_gid, (unsigned long) SUDOERS_GID);
1.1       millert   964:     else {
                    965:        /* Solaris sometimes returns EAGAIN so try 10 times */
                    966:        for (i = 0; i < 10 ; i++) {
                    967:            errno = 0;
                    968:            if ((sudoers_fp = fopen(_PATH_SUDOERS, "r")) == NULL ||
                    969:                fread(&c, sizeof(c), 1, sudoers_fp) != 1) {
                    970:                sudoers_fp = NULL;
                    971:                if (errno != EAGAIN && errno != EWOULDBLOCK)
                    972:                    break;
                    973:            } else
                    974:                break;
                    975:            sleep(1);
                    976:        }
                    977:        if (sudoers_fp == NULL)
                    978:            log_error(USE_ERRNO, "can't open %s", _PATH_SUDOERS);
                    979:     }
                    980:
1.17      millert   981:     set_perms(PERM_ROOT);              /* change back to root */
1.1       millert   982: }
                    983:
                    984: /*
                    985:  * Close all open files (except std*) and turn off core dumps.
                    986:  */
                    987: static void
                    988: initial_setup()
                    989: {
1.27    ! millert   990:     int miss[3], devnull = -1;
1.22      millert   991: #if defined(RLIMIT_CORE) && !defined(SUDO_DEVEL)
1.1       millert   992:     struct rlimit rl;
                    993:
                    994:     /*
                    995:      * Turn off core dumps.
                    996:      */
1.4       millert   997:     (void) getrlimit(RLIMIT_CORE, &corelimit);
1.20      millert   998:     memcpy(&rl, &corelimit, sizeof(struct rlimit));
                    999:     rl.rlim_cur = 0;
1.1       millert  1000:     (void) setrlimit(RLIMIT_CORE, &rl);
1.20      millert  1001: #endif /* RLIMIT_CORE && !SUDO_DEVEL */
1.1       millert  1002:
1.17      millert  1003:     /*
1.27    ! millert  1004:      * stdin, stdout and stderr must be open; set them to /dev/null
        !          1005:      * if they are closed and close all other fds.
1.17      millert  1006:      */
1.27    ! millert  1007:     miss[STDIN_FILENO] = fcntl(STDIN_FILENO, F_GETFL, 0) == -1;
        !          1008:     miss[STDOUT_FILENO] = fcntl(STDOUT_FILENO, F_GETFL, 0) == -1;
        !          1009:     miss[STDERR_FILENO] = fcntl(STDERR_FILENO, F_GETFL, 0) == -1;
        !          1010:     if (miss[STDIN_FILENO] || miss[STDOUT_FILENO] || miss[STDERR_FILENO]) {
        !          1011:        if ((devnull = open(_PATH_DEVNULL, O_RDWR, 0644)) != -1) {
        !          1012:            if (miss[STDIN_FILENO])
        !          1013:                (void) dup2(devnull, STDIN_FILENO);
        !          1014:            if (miss[STDOUT_FILENO])
        !          1015:                (void) dup2(devnull, STDOUT_FILENO);
        !          1016:            if (miss[STDERR_FILENO])
        !          1017:                (void) dup2(devnull, STDERR_FILENO);
        !          1018:        }
        !          1019:     }
        !          1020:     closefrom(STDERR_FILENO + 1);
1.1       millert  1021: }
                   1022:
1.8       millert  1023: #ifdef HAVE_LOGIN_CAP_H
                   1024: static void
1.4       millert  1025: set_loginclass(pw)
                   1026:     struct passwd *pw;
                   1027: {
                   1028:     int errflags;
                   1029:
                   1030:     /*
                   1031:      * Don't make it a fatal error if the user didn't specify the login
                   1032:      * class themselves.  We do this because if login.conf gets
                   1033:      * corrupted we want the admin to be able to use sudo to fix it.
                   1034:      */
                   1035:     if (login_class)
                   1036:        errflags = NO_MAIL|MSG_ONLY;
                   1037:     else
                   1038:        errflags = NO_MAIL|MSG_ONLY|NO_EXIT;
                   1039:
                   1040:     if (login_class && strcmp(login_class, "-") != 0) {
1.19      millert  1041:        if (strcmp(*user_runas, "root") != 0 && user_uid != 0)
                   1042:            errx(1, "only root can use -c %s", login_class);
1.4       millert  1043:     } else {
                   1044:        login_class = pw->pw_class;
                   1045:        if (!login_class || !*login_class)
                   1046:            login_class =
                   1047:                (pw->pw_uid == 0) ? LOGIN_DEFROOTCLASS : LOGIN_DEFCLASS;
                   1048:     }
                   1049:
                   1050:     lc = login_getclass(login_class);
1.10      millert  1051:     if (!lc || !lc->lc_class || strcmp(lc->lc_class, login_class) != 0) {
1.4       millert  1052:        log_error(errflags, "unknown login class: %s", login_class);
1.11      millert  1053:        if (!lc)
                   1054:            lc = login_getclass(NULL);  /* needed for login_getstyle() later */
1.10      millert  1055:     }
1.4       millert  1056: }
                   1057: #else
1.8       millert  1058: static void
1.4       millert  1059: set_loginclass(pw)
                   1060:     struct passwd *pw;
                   1061: {
                   1062: }
1.8       millert  1063: #endif /* HAVE_LOGIN_CAP_H */
1.4       millert  1064:
1.27    ! millert  1065: #ifdef HAVE_PROJECT_H
        !          1066: static void
        !          1067: set_project(pw)
        !          1068:     struct passwd *pw;
        !          1069: {
        !          1070:     int errflags = NO_MAIL|MSG_ONLY|NO_EXIT;
        !          1071:     int errval;
        !          1072:     struct project proj;
        !          1073:     struct project *resultp = '\0';
        !          1074:     char buf[1024];
        !          1075:
        !          1076:     /*
        !          1077:      * Collect the default project for the user and settaskid
        !          1078:      */
        !          1079:     setprojent();
        !          1080:     if (resultp = getdefaultproj(pw->pw_name, &proj, buf, sizeof(buf))) {
        !          1081:        errval = setproject(resultp->pj_name, pw->pw_name, TASK_NORMAL);
        !          1082:        if (errval != 0) {
        !          1083:            switch(errval) {
        !          1084:            case SETPROJ_ERR_TASK:
        !          1085:                if (errno == EAGAIN)
        !          1086:                    log_error(errflags, "resource control limit has been reached");
        !          1087:                else if (errno == ESRCH)
        !          1088:                    log_error(errflags, "user \"%s\" is not a member of "
        !          1089:                        "project \"%s\"", pw->pw_name, resultp->pj_name);
        !          1090:                else if (errno == EACCES)
        !          1091:                    log_error(errflags, "the invoking task is final");
        !          1092:                else
        !          1093:                    log_error(errflags, "could not join project \"%s\"",
        !          1094:                        resultp->pj_name);
        !          1095:                break;
        !          1096:            case SETPROJ_ERR_POOL:
        !          1097:                if (errno == EACCES)
        !          1098:                    log_error(errflags, "no resource pool accepting "
        !          1099:                            "default bindings exists for project \"%s\"",
        !          1100:                            resultp->pj_name);
        !          1101:                else if (errno == ESRCH)
        !          1102:                    log_error(errflags, "specified resource pool does "
        !          1103:                            "not exist for project \"%s\"", resultp->pj_name);
        !          1104:                else
        !          1105:                    log_error(errflags, "could not bind to default "
        !          1106:                            "resource pool for project \"%s\"", resultp->pj_name);
        !          1107:                break;
        !          1108:            default:
        !          1109:                if (errval <= 0) {
        !          1110:                    log_error(errflags, "setproject failed for project \"%s\"",
        !          1111:                        resultp->pj_name);
        !          1112:                } else {
        !          1113:                    log_error(errflags, "warning, resource control assignment "
        !          1114:                        "failed for project \"%s\"", resultp->pj_name);
        !          1115:                }
        !          1116:            }
        !          1117:        }
        !          1118:     } else {
        !          1119:        log_error(errflags, "getdefaultproj() error: %s", strerror(errno));
        !          1120:     }
        !          1121:     endprojent();
        !          1122: }
        !          1123: #else
        !          1124: static void
        !          1125: set_project(pw)
        !          1126:     struct passwd *pw;
        !          1127: {
        !          1128: }
        !          1129: #endif /* HAVE_PROJECT_H */
        !          1130:
1.1       millert  1131: /*
1.2       millert  1132:  * Look up the fully qualified domain name and set user_host and user_shost.
                   1133:  */
                   1134: void
                   1135: set_fqdn()
                   1136: {
                   1137:     struct hostent *hp;
                   1138:     char *p;
                   1139:
1.14      millert  1140:     if (!(hp = gethostbyname(user_host))) {
                   1141:        log_error(MSG_ONLY|NO_EXIT,
                   1142:            "unable to lookup %s via gethostbyname()", user_host);
                   1143:     } else {
                   1144:        if (user_shost != user_host)
1.27    ! millert  1145:            efree(user_shost);
        !          1146:        efree(user_host);
1.14      millert  1147:        user_host = estrdup(hp->h_name);
1.2       millert  1148:     }
                   1149:     if ((p = strchr(user_host, '.'))) {
                   1150:        *p = '\0';
                   1151:        user_shost = estrdup(user_host);
                   1152:        *p = '.';
                   1153:     } else {
                   1154:        user_shost = user_host;
                   1155:     }
                   1156: }
                   1157:
                   1158: /*
1.23      millert  1159:  * Get passwd entry for the user we are going to run commands as.
                   1160:  * By default, this is "root".  Updates runas_pw as a side effect.
                   1161:  */
                   1162: int
                   1163: set_runaspw(user)
                   1164:     char *user;
                   1165: {
                   1166:     if (runas_pw != NULL) {
                   1167:        if (user_runas != &def_runas_default)
                   1168:            return(TRUE);               /* don't override -u option */
1.27    ! millert  1169:        efree(runas_pw);
1.23      millert  1170:     }
                   1171:     if (*user == '#') {
                   1172:        runas_pw = sudo_getpwuid(atoi(user + 1));
                   1173:        if (runas_pw == NULL) {
                   1174:            runas_pw = emalloc(sizeof(struct passwd));
                   1175:            (void) memset((VOID *)runas_pw, 0, sizeof(struct passwd));
                   1176:            runas_pw->pw_uid = atoi(user + 1);
                   1177:        }
                   1178:     } else {
                   1179:        runas_pw = sudo_getpwnam(user);
                   1180:        if (runas_pw == NULL)
                   1181:            log_error(NO_MAIL|MSG_ONLY, "no passwd entry for %s!", user);
                   1182:     }
                   1183:     return(TRUE);
                   1184: }
                   1185:
                   1186: /*
1.6       millert  1187:  * Get passwd entry for the user we are going to authenticate as.
1.23      millert  1188:  * By default, this is the user invoking sudo.  In the most common
                   1189:  * case, this matches sudo_user.pw or runas_pw.
1.4       millert  1190:  */
1.6       millert  1191: static struct passwd *
                   1192: get_authpw()
1.4       millert  1193: {
                   1194:     struct passwd *pw;
                   1195:
1.23      millert  1196:     if (def_rootpw) {
                   1197:        if (runas_pw->pw_uid == 0)
                   1198:            pw = runas_pw;
                   1199:        else if ((pw = sudo_getpwuid(0)) == NULL)
1.4       millert  1200:            log_error(0, "uid 0 does not exist in the passwd file!");
1.23      millert  1201:     } else if (def_runaspw) {
                   1202:        if (strcmp(def_runas_default, *user_runas) == 0)
                   1203:            pw = runas_pw;
                   1204:        else if ((pw = sudo_getpwnam(def_runas_default)) == NULL)
1.4       millert  1205:            log_error(0, "user %s does not exist in the passwd file!",
1.23      millert  1206:                def_runas_default);
                   1207:     } else if (def_targetpw) {
                   1208:        if (runas_pw->pw_name == NULL)
                   1209:            log_error(NO_MAIL|MSG_ONLY, "no passwd entry for %lu!",
1.25      millert  1210:                (unsigned long) runas_pw->pw_uid);
1.23      millert  1211:        pw = runas_pw;
1.6       millert  1212:     } else
                   1213:        pw = sudo_user.pw;
                   1214:
                   1215:     return(pw);
1.4       millert  1216: }
                   1217:
                   1218: /*
1.1       millert  1219:  * Tell which options are mutually exclusive and exit.
                   1220:  */
                   1221: static void
                   1222: usage_excl(exit_val)
                   1223:     int exit_val;
                   1224: {
1.27    ! millert  1225:     warnx("Only one of the -e, -h, i, -k, -K, -l, -s, -v or -V options may be used");
1.1       millert  1226:     usage(exit_val);
                   1227: }
                   1228:
                   1229: /*
                   1230:  * Give usage message and exit.
                   1231:  */
                   1232: static void
                   1233: usage(exit_val)
                   1234:     int exit_val;
                   1235: {
1.27    ! millert  1236:     char **p, **uvec[4];
        !          1237:     int i, linelen, linemax, ulen;
        !          1238:     static char *uvec1[] = {
        !          1239:        " -h | -K | -k | -L | -l | -V | -v",
        !          1240:        NULL
        !          1241:     };
        !          1242:     static char *uvec2[] = {
        !          1243:        " [-bEHPS]",
1.23      millert  1244: #ifdef HAVE_BSD_AUTH_H
                   1245:        " [-a auth_type]",
                   1246: #endif
1.8       millert  1247: #ifdef HAVE_LOGIN_CAP_H
1.23      millert  1248:        " [-c class|-]",
1.8       millert  1249: #endif
1.23      millert  1250:        " [-p prompt]",
                   1251:        " [-u username|#uid]",
1.27    ! millert  1252:        " [VAR=value]",
        !          1253:        " {-i | -s | <command>}",
        !          1254:        NULL
        !          1255:     };
        !          1256:     static char *uvec3[] = {
        !          1257:        " -e",
        !          1258:        " [-S]",
        !          1259: #ifdef HAVE_BSD_AUTH_H
        !          1260:        " [-a auth_type]",
        !          1261: #endif
        !          1262: #ifdef HAVE_LOGIN_CAP_H
        !          1263:        " [-c class|-]",
        !          1264: #endif
        !          1265:        " [-p prompt]",
        !          1266:        " [-u username|#uid]",
        !          1267:        " file ...",
1.23      millert  1268:        NULL
                   1269:     };
                   1270:
                   1271:     /*
1.27    ! millert  1272:      * Use usage vectors appropriate to the progname.
1.23      millert  1273:      */
                   1274:     if (strcmp(getprogname(), "sudoedit") == 0) {
1.27    ! millert  1275:        uvec[0] = uvec3 + 1;
        !          1276:        uvec[1] = NULL;
1.23      millert  1277:     } else {
1.27    ! millert  1278:        uvec[0] = uvec1;
        !          1279:        uvec[1] = uvec2;
        !          1280:        uvec[2] = uvec3;
        !          1281:        uvec[3] = NULL;
1.23      millert  1282:     }
                   1283:
                   1284:     /*
1.27    ! millert  1285:      * Print usage and wrap lines as needed.
1.23      millert  1286:      * Assumes an 80-character wide terminal, which is kind of bogus...
                   1287:      */
                   1288:     ulen = (int)strlen(getprogname()) + 7;
                   1289:     linemax = 80;
1.27    ! millert  1290:     for (i = 0; uvec[i] != NULL; i++) {
        !          1291:        linelen = linemax - ulen;
        !          1292:        printf("usage: %s", getprogname());
        !          1293:        for (p = uvec[i]; *p != NULL; p++) {
        !          1294:            if (linelen == linemax || (linelen -= strlen(*p)) >= 0) {
        !          1295:                fputs(*p, stdout);
        !          1296:            } else {
        !          1297:                p--;
        !          1298:                linelen = linemax;
        !          1299:                printf("\n%*s", ulen, "");
        !          1300:            }
1.23      millert  1301:        }
1.27    ! millert  1302:        putchar('\n');
1.23      millert  1303:     }
1.1       millert  1304:     exit(exit_val);
                   1305: }