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

Annotation of src/usr.bin/sudo/visudo.c, Revision 1.17

1.1       millert     1: /*
1.17    ! millert     2:  * Copyright (c) 1996, 1998-2004 Todd C. Miller <Todd.Miller@courtesan.com>
1.1       millert     3:  *
1.17    ! 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.17    ! 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.15      millert    15:  *
                     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.
1.1       millert    19:  */
                     20:
                     21: /*
                     22:  * Lock the sudoers file for safe editing (ala vipw) and check for parse errors.
                     23:  */
                     24:
1.14      millert    25: #define _SUDO_MAIN
                     26:
1.17    ! millert    27: #ifdef __TANDEM
        !            28: # include <floss.h>
        !            29: #endif
        !            30:
1.1       millert    31: #include "config.h"
                     32:
1.11      millert    33: #include <sys/types.h>
                     34: #include <sys/param.h>
                     35: #include <sys/stat.h>
1.17    ! millert    36: #include <sys/time.h>
        !            37: #ifndef __TANDEM
        !            38: # include <sys/file.h>
        !            39: #endif
1.11      millert    40: #include <sys/wait.h>
1.1       millert    41: #include <stdio.h>
                     42: #ifdef STDC_HEADERS
1.11      millert    43: # include <stdlib.h>
                     44: # include <stddef.h>
                     45: #else
                     46: # ifdef HAVE_STDLIB_H
                     47: #  include <stdlib.h>
                     48: # endif
1.1       millert    49: #endif /* STDC_HEADERS */
1.11      millert    50: #ifdef HAVE_STRING_H
                     51: # include <string.h>
                     52: #else
                     53: # ifdef HAVE_STRINGS_H
                     54: #  include <strings.h>
                     55: # endif
                     56: #endif /* HAVE_STRING_H */
1.1       millert    57: #ifdef HAVE_UNISTD_H
                     58: #include <unistd.h>
                     59: #endif /* HAVE_UNISTD_H */
1.14      millert    60: #ifdef HAVE_ERR_H
                     61: # include <err.h>
                     62: #else
                     63: # include "emul/err.h"
                     64: #endif /* HAVE_ERR_H */
1.1       millert    65: #include <ctype.h>
                     66: #include <pwd.h>
                     67: #include <time.h>
                     68: #include <signal.h>
                     69: #include <errno.h>
                     70: #include <fcntl.h>
                     71:
                     72: #include "sudo.h"
                     73: #include "version.h"
                     74:
                     75: #ifndef lint
1.17    ! millert    76: static const char rcsid[] = "$Sudo: visudo.c,v 1.170 2004/09/08 15:48:23 millert Exp $";
1.1       millert    77: #endif /* lint */
                     78:
                     79: /*
                     80:  * Function prototypes
                     81:  */
                     82: static void usage              __P((void));
                     83: static char whatnow            __P((void));
                     84: static RETSIGTYPE Exit         __P((int));
                     85: static void setup_signals      __P((void));
1.11      millert    86: static int run_command         __P((char *, char **));
                     87: static int check_syntax                __P((int));
1.17    ! millert    88: int command_matches            __P((char *, char *));
1.1       millert    89: int addr_matches               __P((char *));
1.4       millert    90: int hostname_matches           __P((char *, char *, char *));
1.3       millert    91: int netgr_matches              __P((char *, char *, char *, char *));
1.17    ! millert    92: int usergr_matches             __P((char *, char *, struct passwd *));
        !            93: int userpw_matches             __P((char *, char *, struct passwd *));
1.1       millert    94: void init_parser               __P((void));
1.17    ! millert    95: void yyerror                   __P((char *));
1.1       millert    96: void yyrestart                 __P((FILE *));
                     97:
                     98: /*
                     99:  * External globals exported by the parser
                    100:  */
                    101: extern FILE *yyin, *yyout;
                    102: extern int errorlineno;
                    103: extern int pedantic;
1.11      millert   104: extern int quiet;
                    105:
                    106: /* For getopt(3) */
                    107: extern char *optarg;
                    108: extern int optind;
1.1       millert   109:
                    110: /*
                    111:  * Globals
                    112:  */
                    113: char **Argv;
                    114: char *sudoers = _PATH_SUDOERS;
                    115: char *stmp = _PATH_SUDOERS_TMP;
                    116: struct sudo_user sudo_user;
1.17    ! millert   117: int Argc, parse_error = FALSE;
1.1       millert   118:
                    119: int
                    120: main(argc, argv)
                    121:     int argc;
                    122:     char **argv;
                    123: {
1.17    ! millert   124:     char buf[PATH_MAX*2];              /* buffer used for copying files */
1.4       millert   125:     char *Editor;                      /* editor to use */
1.11      millert   126:     char *UserEditor;                  /* editor user wants to use */
                    127:     char *EditorPath;                  /* colon-separated list of editors */
                    128:     char *av[4];                       /* argument vector for run_command */
                    129:     int checkonly;                     /* only check existing file? */
1.1       millert   130:     int sudoers_fd;                    /* sudoers file descriptor */
                    131:     int stmp_fd;                       /* stmp file descriptor */
                    132:     int n;                             /* length parameter */
1.11      millert   133:     int ch;                            /* getopt char */
1.17    ! millert   134:     struct timespec ts1, ts2;          /* time before and after edit */
        !           135:     struct timespec sudoers_mtim;      /* starting mtime of sudoers file */
        !           136:     off_t sudoers_size;                        /* starting size of sudoers file */
        !           137:     struct stat sb;                    /* stat buffer */
1.1       millert   138:
                    139:     /* Warn about aliases that are used before being defined. */
                    140:     pedantic = 1;
                    141:
1.17    ! millert   142:     Argv = argv;
        !           143:     if ((Argc = argc) < 1)
        !           144:        usage();
1.1       millert   145:
                    146:     /*
                    147:      * Arg handling.
                    148:      */
1.11      millert   149:     checkonly = 0;
                    150:     while ((ch = getopt(argc, argv, "Vcf:sq")) != -1) {
                    151:        switch (ch) {
                    152:            case 'V':
1.14      millert   153:                (void) printf("%s version %s\n", getprogname(), version);
1.11      millert   154:                exit(0);
                    155:            case 'c':
                    156:                checkonly++;            /* check mode */
                    157:                break;
                    158:            case 'f':
                    159:                sudoers = optarg;       /* sudoers file path */
                    160:                easprintf(&stmp, "%s.tmp", optarg);
                    161:                break;
                    162:            case 's':
                    163:                pedantic++;             /* strict mode */
                    164:                break;
                    165:            case 'q':
                    166:                quiet++;                /* quiet mode */
                    167:                break;
                    168:            default:
                    169:                usage();
1.1       millert   170:        }
                    171:     }
1.11      millert   172:     argc -= optind;
                    173:     argv += optind;
                    174:     if (argc)
                    175:        usage();
1.1       millert   176:
                    177:     /* Mock up a fake sudo_user struct. */
                    178:     user_host = user_shost = user_cmnd = "";
1.14      millert   179:     if ((sudo_user.pw = getpwuid(getuid())) == NULL)
                    180:        errx(1, "you don't exist in the passwd database");
1.1       millert   181:
1.4       millert   182:     /* Setup defaults data structures. */
                    183:     init_defaults();
1.1       millert   184:
1.11      millert   185:     if (checkonly)
                    186:        exit(check_syntax(quiet));
                    187:
1.1       millert   188:     /*
1.17    ! millert   189:      * Open sudoers, lock it and stat it.
1.3       millert   190:      * sudoers_fd must remain open throughout in order to hold the lock.
1.1       millert   191:      */
1.6       millert   192:     sudoers_fd = open(sudoers, O_RDWR | O_CREAT, SUDOERS_MODE);
1.14      millert   193:     if (sudoers_fd == -1)
                    194:        err(1, "%s", sudoers);
                    195:     if (!lock_file(sudoers_fd, SUDO_TLOCK))
                    196:        errx(1, "sudoers file busy, try again later");
1.3       millert   197: #ifdef HAVE_FSTAT
1.17    ! millert   198:     if (fstat(sudoers_fd, &sb) == -1)
1.1       millert   199: #else
1.17    ! millert   200:     if (stat(sudoers, &sb) == -1)
1.1       millert   201: #endif
1.14      millert   202:        err(1, "can't stat %s", sudoers);
1.17    ! millert   203:     sudoers_size = sb.st_size;
        !           204:     sudoers_mtim.tv_sec = mtim_getsec(sb);
        !           205:     sudoers_mtim.tv_nsec = mtim_getnsec(sb);
1.1       millert   206:
1.3       millert   207:     /*
                    208:      * Open sudoers temp file.
                    209:      */
                    210:     stmp_fd = open(stmp, O_WRONLY | O_CREAT | O_TRUNC, 0600);
1.14      millert   211:     if (stmp_fd < 0)
                    212:        err(1, "%s", stmp);
1.3       millert   213:
1.1       millert   214:     /* Install signal handlers to clean up stmp if we are killed. */
                    215:     setup_signals();
                    216:
                    217:     /* Copy sudoers -> stmp and reset the mtime */
1.17    ! millert   218:     if (sudoers_size) {
1.1       millert   219:        while ((n = read(sudoers_fd, buf, sizeof(buf))) > 0)
1.14      millert   220:            if (write(stmp_fd, buf, n) != n)
                    221:                err(1, "write error");
1.1       millert   222:
1.13      millert   223:        /* Add missing newline at EOF if needed. */
                    224:        if (n > 0 && buf[n - 1] != '\n') {
                    225:            buf[0] = '\n';
                    226:            write(stmp_fd, buf, 1);
                    227:        }
                    228:
1.17    ! millert   229:        (void) touch(stmp_fd, stmp, &sudoers_mtim);
1.3       millert   230:        (void) close(stmp_fd);
1.4       millert   231:
                    232:        /* Parse sudoers to pull in editor and env_editor conf values. */
                    233:        if ((yyin = fopen(stmp, "r"))) {
                    234:            yyout = stdout;
1.13      millert   235:            n = quiet;
                    236:            quiet = 1;
1.4       millert   237:            init_parser();
                    238:            yyparse();
                    239:            parse_error = FALSE;
1.13      millert   240:            quiet = n;
1.4       millert   241:            fclose(yyin);
                    242:        }
1.3       millert   243:     } else
                    244:        (void) close(stmp_fd);
1.1       millert   245:
                    246:     /*
1.17    ! millert   247:      * Check VISUAL and EDITOR environment variables to see which editor
1.11      millert   248:      * the user wants to use (we may not end up using it though).
                    249:      * If the path is not fully-qualified, make it so and check that
                    250:      * the specified executable actually exists.
1.4       millert   251:      */
1.17    ! millert   252:     if ((UserEditor = getenv("VISUAL")) == NULL || *UserEditor == '\0')
        !           253:        UserEditor = getenv("EDITOR");
1.11      millert   254:     if (UserEditor && *UserEditor == '\0')
                    255:        UserEditor = NULL;
                    256:     else if (UserEditor) {
1.17    ! millert   257:        if (find_path(UserEditor, &Editor, NULL, getenv("PATH")) == FOUND) {
1.11      millert   258:            UserEditor = Editor;
                    259:        } else {
1.17    ! millert   260:            if (def_env_editor) {
1.11      millert   261:                /* If we are honoring $EDITOR this is a fatal error. */
1.14      millert   262:                warnx("specified editor (%s) doesn't exist!", UserEditor);
1.11      millert   263:                Exit(-1);
                    264:            } else {
                    265:                /* Otherwise, just ignore $EDITOR. */
                    266:                UserEditor = NULL;
                    267:            }
                    268:        }
                    269:     }
                    270:
                    271:     /*
                    272:      * See if we can use the user's choice of editors either because
                    273:      * we allow any $EDITOR or because $EDITOR is in the allowable list.
                    274:      */
1.12      millert   275:     Editor = EditorPath = NULL;
1.17    ! millert   276:     if (def_env_editor && UserEditor)
1.11      millert   277:        Editor = UserEditor;
                    278:     else if (UserEditor) {
                    279:        struct stat editor_sb;
                    280:        struct stat user_editor_sb;
                    281:        char *base, *userbase;
                    282:
                    283:        if (stat(UserEditor, &user_editor_sb) != 0) {
                    284:            /* Should never happen since we already checked above. */
1.14      millert   285:            warn("unable to stat editor (%s)", UserEditor);
1.11      millert   286:            Exit(-1);
                    287:        }
1.17    ! millert   288:        EditorPath = estrdup(def_editor);
1.11      millert   289:        Editor = strtok(EditorPath, ":");
                    290:        do {
                    291:            /*
                    292:             * Both Editor and UserEditor should be fully qualified but
                    293:             * check anyway...
                    294:             */
                    295:            if ((base = strrchr(Editor, '/')) == NULL)
                    296:                continue;
                    297:            if ((userbase = strrchr(UserEditor, '/')) == NULL) {
                    298:                Editor = NULL;
                    299:                break;
                    300:            }
                    301:            base++, userbase++;
                    302:
                    303:            /*
                    304:             * We compare the basenames first and then use stat to match
                    305:             * for sure.
                    306:             */
                    307:            if (strcmp(base, userbase) == 0) {
                    308:                if (stat(Editor, &editor_sb) == 0 && S_ISREG(editor_sb.st_mode)
                    309:                    && (editor_sb.st_mode & 0000111) &&
                    310:                    editor_sb.st_dev == user_editor_sb.st_dev &&
                    311:                    editor_sb.st_ino == user_editor_sb.st_ino)
                    312:                    break;
                    313:            }
                    314:        } while ((Editor = strtok(NULL, ":")));
                    315:     }
                    316:
                    317:     /*
1.17    ! millert   318:      * Can't use $EDITOR, try each element of def_editor until we
1.11      millert   319:      * find one that exists, is regular, and is executable.
                    320:      */
                    321:     if (Editor == NULL || *Editor == '\0') {
1.12      millert   322:        if (EditorPath != NULL)
                    323:            free(EditorPath);
1.17    ! millert   324:        EditorPath = estrdup(def_editor);
1.11      millert   325:        Editor = strtok(EditorPath, ":");
                    326:        do {
1.17    ! millert   327:            if (sudo_goodpath(Editor, NULL))
1.11      millert   328:                break;
                    329:        } while ((Editor = strtok(NULL, ":")));
                    330:
                    331:        /* Bleah, none of the editors existed! */
                    332:        if (Editor == NULL || *Editor == '\0') {
1.17    ! millert   333:            warnx("no editor found (editor path = %s)", def_editor);
1.11      millert   334:            Exit(-1);
                    335:        }
                    336:     }
1.4       millert   337:
                    338:     /*
1.1       millert   339:      * Edit the temp file and parse it (for sanity checking)
                    340:      */
                    341:     do {
1.11      millert   342:        char linestr[64];
                    343:
                    344:        /* Build up argument vector for the command */
                    345:        if ((av[0] = strrchr(Editor, '/')) != NULL)
                    346:            av[0]++;
                    347:        else
                    348:            av[0] = Editor;
                    349:        n = 1;
                    350:        if (parse_error == TRUE) {
                    351:            (void) snprintf(linestr, sizeof(linestr), "+%d", errorlineno);
                    352:            av[n++] = linestr;
                    353:        }
                    354:        av[n++] = stmp;
                    355:        av[n++] = NULL;
                    356:
1.1       millert   357:        /*
1.11      millert   358:         * Do the edit:
                    359:         *  We cannot check the editor's exit value against 0 since
                    360:         *  XPG4 specifies that vi's exit value is a function of the
                    361:         *  number of errors during editing (?!?!).
1.1       millert   362:         */
1.17    ! millert   363:        gettime(&ts1);
1.11      millert   364:        if (run_command(Editor, av) != -1) {
1.17    ! millert   365:            gettime(&ts2);
1.1       millert   366:            /*
                    367:             * Sanity checks.
                    368:             */
1.17    ! millert   369:            if (stat(stmp, &sb) < 0) {
1.14      millert   370:                warnx("cannot stat temporary file (%s), %s unchanged",
                    371:                    stmp, sudoers);
1.1       millert   372:                Exit(-1);
                    373:            }
1.17    ! millert   374:            if (sb.st_size == 0) {
1.14      millert   375:                warnx("zero length temporary file (%s), %s unchanged",
                    376:                    stmp, sudoers);
1.1       millert   377:                Exit(-1);
                    378:            }
                    379:
                    380:            /*
                    381:             * Passed sanity checks so reopen stmp file and check
                    382:             * for parse errors.
                    383:             */
                    384:            yyout = stdout;
1.13      millert   385:            yyin = fopen(stmp, "r+");
1.1       millert   386:            if (yyin == NULL) {
1.14      millert   387:                warnx("can't re-open temporary file (%s), %s unchanged.",
                    388:                    stmp, sudoers);
1.1       millert   389:                Exit(-1);
                    390:            }
                    391:
1.13      millert   392:            /* Add missing newline at EOF if needed. */
                    393:            if (fseek(yyin, -1, SEEK_END) == 0 && (ch = fgetc(yyin)) != '\n')
                    394:                fputc('\n', yyin);
                    395:            rewind(yyin);
                    396:
1.1       millert   397:            /* Clean slate for each parse */
1.5       millert   398:            user_runas = NULL;
1.1       millert   399:            init_defaults();
                    400:            init_parser();
                    401:
1.13      millert   402:            /* Parse the sudoers temp file */
                    403:            yyrestart(yyin);
1.1       millert   404:            if (yyparse() && parse_error != TRUE) {
1.14      millert   405:                warnx("unabled to parse temporary file (%s), unknown error",
                    406:                    stmp);
1.1       millert   407:                parse_error = TRUE;
                    408:            }
1.13      millert   409:            fclose(yyin);
1.1       millert   410:        } else {
1.14      millert   411:            warnx("editor (%s) failed, %s unchanged", Editor, sudoers);
1.1       millert   412:            Exit(-1);
                    413:        }
                    414:
                    415:        /*
                    416:         * Got an error, prompt the user for what to do now
                    417:         */
                    418:        if (parse_error == TRUE) {
                    419:            switch (whatnow()) {
1.7       millert   420:                case 'Q' :      parse_error = FALSE;    /* ignore parse error */
1.1       millert   421:                                break;
1.17    ! millert   422:                case 'x' :      if (sudoers_size == 0)
1.8       millert   423:                                    unlink(sudoers);
                    424:                                Exit(0);
1.1       millert   425:                                break;
                    426:            }
                    427:        }
                    428:     } while (parse_error == TRUE);
                    429:
                    430:     /*
                    431:      * If the user didn't change the temp file, just unlink it.
                    432:      */
1.17    ! millert   433:     if (sudoers_size == sb.st_size &&
        !           434:        sudoers_mtim.tv_sec == mtim_getsec(sb) &&
        !           435:        sudoers_mtim.tv_nsec == mtim_getnsec(sb)) {
        !           436:        /*
        !           437:         * If mtime and size match but the user spent no measurable
        !           438:         * time in the editor we can't tell if the file was changed.
        !           439:         */
        !           440:        timespecsub(&ts1, &ts2, &ts2);
        !           441:        if (timespecisset(&ts2)) {
        !           442:            warnx("sudoers file unchanged");
        !           443:            Exit(0);
        !           444:        }
1.1       millert   445:     }
                    446:
                    447:     /*
                    448:      * Change mode and ownership of temp file so when
                    449:      * we move it to sudoers things are kosher.
                    450:      */
                    451:     if (chown(stmp, SUDOERS_UID, SUDOERS_GID)) {
1.14      millert   452:        warn("unable to set (uid, gid) of %s to (%d, %d)",
                    453:            stmp, SUDOERS_UID, SUDOERS_GID);
1.1       millert   454:        Exit(-1);
                    455:     }
                    456:     if (chmod(stmp, SUDOERS_MODE)) {
1.14      millert   457:        warn("unable to change mode of %s to 0%o", stmp, SUDOERS_MODE);
1.1       millert   458:        Exit(-1);
                    459:     }
                    460:
                    461:     /*
                    462:      * Now that we have a sane stmp file (parses ok) it needs to be
                    463:      * rename(2)'d to sudoers.  If the rename(2) fails we try using
1.17    ! millert   464:      * mv(1) in case stmp and sudoers are on different file systems.
1.1       millert   465:      */
                    466:     if (rename(stmp, sudoers)) {
                    467:        if (errno == EXDEV) {
1.17    ! millert   468:            warnx("%s and %s not on the same file system, using mv to rename",
1.14      millert   469:              stmp, sudoers);
1.1       millert   470:
1.11      millert   471:            /* Build up argument vector for the command */
                    472:            if ((av[0] = strrchr(_PATH_MV, '/')) != NULL)
                    473:                av[0]++;
                    474:            else
                    475:                av[0] = _PATH_MV;
                    476:            av[1] = stmp;
                    477:            av[2] = sudoers;
                    478:            av[3] = NULL;
1.1       millert   479:
1.11      millert   480:            /* And run it... */
                    481:            if (run_command(_PATH_MV, av)) {
1.14      millert   482:                warnx("command failed: '%s %s %s', %s unchanged",
                    483:                    _PATH_MV, stmp, sudoers, sudoers);
1.1       millert   484:                Exit(-1);
                    485:            }
                    486:        } else {
1.14      millert   487:            warn("error renaming %s, %s unchanged", stmp, sudoers);
1.1       millert   488:            Exit(-1);
                    489:        }
                    490:     }
                    491:
1.3       millert   492:     exit(0);
1.1       millert   493: }
                    494:
                    495: /*
                    496:  * Dummy *_matches routines.
                    497:  * These exist to allow us to use the same parser as sudo(8).
                    498:  */
                    499: int
1.17    ! millert   500: command_matches(path, sudoers_args)
1.1       millert   501:     char *path;
                    502:     char *sudoers_args;
                    503: {
                    504:     return(TRUE);
                    505: }
                    506:
                    507: int
                    508: addr_matches(n)
                    509:     char *n;
1.4       millert   510: {
                    511:     return(TRUE);
                    512: }
                    513:
                    514: int
                    515: hostname_matches(s, l, p)
                    516:     char *s, *l, *p;
1.1       millert   517: {
                    518:     return(TRUE);
                    519: }
                    520:
                    521: int
1.17    ! millert   522: usergr_matches(g, u, pw)
1.1       millert   523:     char *g, *u;
1.17    ! millert   524:     struct passwd *pw;
        !           525: {
        !           526:     return(TRUE);
        !           527: }
        !           528:
        !           529: int
        !           530: userpw_matches(s, u, pw)
        !           531:     char *s, *u;
        !           532:     struct passwd *pw;
1.1       millert   533: {
                    534:     return(TRUE);
                    535: }
                    536:
                    537: int
1.3       millert   538: netgr_matches(n, h, sh, u)
                    539:     char *n, *h, *sh, *u;
1.1       millert   540: {
                    541:     return(TRUE);
1.2       millert   542: }
                    543:
                    544: void
                    545: set_fqdn()
                    546: {
                    547:     return;
1.1       millert   548: }
                    549:
1.11      millert   550: int
1.17    ! millert   551: set_runaspw(user)
        !           552:     char *user;
        !           553: {
        !           554:     extern int sudolineno, used_runas;
        !           555:
        !           556:     if (used_runas) {
        !           557:        (void) fprintf(stderr,
        !           558:            "%s: runas_default set after old value is in use near line %d\n",
        !           559:            pedantic > 1 ? "Error" : "Warning", sudolineno);
        !           560:        if (pedantic > 1)
        !           561:            yyerror(NULL);
        !           562:     }
        !           563:     return(TRUE);
        !           564: }
        !           565:
        !           566: int
1.11      millert   567: user_is_exempt()
                    568: {
                    569:     return(TRUE);
                    570: }
                    571:
                    572: void
                    573: init_envtables()
                    574: {
                    575:     return;
                    576: }
                    577:
1.1       millert   578: /*
                    579:  * Assuming a parse error occurred, prompt the user for what they want
                    580:  * to do now.  Returns the first letter of their choice.
                    581:  */
                    582: static char
                    583: whatnow()
                    584: {
                    585:     int choice, c;
                    586:
                    587:     for (;;) {
                    588:        (void) fputs("What now? ", stdout);
                    589:        choice = getchar();
                    590:        for (c = choice; c != '\n' && c != EOF;)
                    591:            c = getchar();
                    592:
1.3       millert   593:        switch (choice) {
                    594:            case EOF:
                    595:                choice = 'x';
                    596:                /* FALLTHROUGH */
                    597:            case 'e':
                    598:            case 'x':
                    599:            case 'Q':
                    600:                return(choice);
                    601:            default:
                    602:                (void) puts("Options are:");
                    603:                (void) puts("  (e)dit sudoers file again");
                    604:                (void) puts("  e(x)it without saving changes to sudoers file");
                    605:                (void) puts("  (Q)uit and save changes to sudoers file (DANGER!)\n");
1.1       millert   606:        }
                    607:     }
                    608: }
                    609:
                    610: /*
                    611:  * Install signal handlers for visudo.
                    612:  */
                    613: static void
                    614: setup_signals()
                    615: {
1.11      millert   616:        sigaction_t sa;
1.1       millert   617:
                    618:        /*
                    619:         * Setup signal handlers to cleanup nicely.
                    620:         */
1.11      millert   621:        sigemptyset(&sa.sa_mask);
                    622:        sa.sa_flags = SA_RESTART;
                    623:        sa.sa_handler = Exit;
                    624:        (void) sigaction(SIGTERM, &sa, NULL);
                    625:        (void) sigaction(SIGHUP, &sa, NULL);
                    626:        (void) sigaction(SIGINT, &sa, NULL);
                    627:        (void) sigaction(SIGQUIT, &sa, NULL);
                    628: }
                    629:
                    630: static int
                    631: run_command(path, argv)
                    632:     char *path;
                    633:     char **argv;
                    634: {
                    635:     int status;
                    636:     pid_t pid;
                    637:     sigset_t set, oset;
                    638:
                    639:     (void) sigemptyset(&set);
                    640:     (void) sigaddset(&set, SIGCHLD);
                    641:     (void) sigprocmask(SIG_BLOCK, &set, &oset);
                    642:
                    643:     switch (pid = fork()) {
                    644:        case -1:
1.14      millert   645:            warn("unable to run %s", path);
1.11      millert   646:            Exit(-1);
                    647:            break;      /* NOTREACHED */
                    648:        case 0:
                    649:            (void) sigprocmask(SIG_SETMASK, &oset, NULL);
                    650:            execv(path, argv);
1.14      millert   651:            warn("unable to run %s", path);
1.11      millert   652:            _exit(127);
                    653:            break;      /* NOTREACHED */
                    654:     }
                    655:
                    656: #ifdef sudo_waitpid
                    657:     pid = sudo_waitpid(pid, &status, 0);
1.1       millert   658: #else
1.11      millert   659:     pid = wait(&status);
                    660: #endif
                    661:
                    662:     (void) sigprocmask(SIG_SETMASK, &oset, NULL);
                    663:
1.17    ! millert   664:     if (pid == -1 || !WIFEXITED(status))
        !           665:        return(-1);
        !           666:     return(WEXITSTATUS(status));
1.11      millert   667: }
                    668:
                    669: static int
                    670: check_syntax(quiet)
                    671:     int quiet;
                    672: {
                    673:
                    674:     if ((yyin = fopen(sudoers, "r")) == NULL) {
                    675:        if (!quiet)
1.14      millert   676:            warn("unable to open %s", sudoers);
1.11      millert   677:        exit(1);
                    678:     }
                    679:     yyout = stdout;
                    680:     init_parser();
                    681:     if (yyparse() && parse_error != TRUE) {
                    682:        if (!quiet)
1.14      millert   683:            warnx("failed to parse %s file, unknown error", sudoers);
1.11      millert   684:        parse_error = TRUE;
                    685:     }
                    686:     if (!quiet){
                    687:        if (parse_error)
                    688:            (void) printf("parse error in %s near line %d\n", sudoers,
                    689:                errorlineno);
                    690:        else
                    691:            (void) printf("%s file parsed OK\n", sudoers);
                    692:     }
                    693:
                    694:     return(parse_error == TRUE);
1.1       millert   695: }
                    696:
                    697: /*
                    698:  * Unlink the sudoers temp file (if it exists) and exit.
                    699:  * Used in place of a normal exit() and as a signal handler.
1.10      millert   700:  * A positive parameter indicates we were called as a signal handler.
1.1       millert   701:  */
                    702: static RETSIGTYPE
                    703: Exit(sig)
                    704:     int sig;
                    705: {
1.16      millert   706: #define        emsg     " exiting due to signal.\n"
1.10      millert   707:
1.1       millert   708:     (void) unlink(stmp);
                    709:
1.10      millert   710:     if (sig > 0) {
1.14      millert   711:        write(STDERR_FILENO, getprogname(), strlen(getprogname()));
1.10      millert   712:        write(STDERR_FILENO, emsg, sizeof(emsg) - 1);
1.16      millert   713:        _exit(sig);
1.10      millert   714:     }
                    715:     exit(-sig);
1.1       millert   716: }
                    717:
                    718: static void
                    719: usage()
                    720: {
1.11      millert   721:     (void) fprintf(stderr, "usage: %s [-c] [-f sudoers] [-q] [-s] [-V]\n",
1.14      millert   722:        getprogname());
1.1       millert   723:     exit(1);
                    724: }