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

1.1       millert     1: /*
1.9.2.1 ! millert     2:  * Copyright (c) 1996, 1998-2001 Todd C. Miller <Todd.Miller@courtesan.com>
1.1       millert     3:  * All rights reserved.
                      4:  *
                      5:  * Redistribution and use in source and binary forms, with or without
                      6:  * modification, are permitted provided that the following conditions
                      7:  * are met:
                      8:  *
                      9:  * 1. Redistributions of source code must retain the above copyright
                     10:  *    notice, this list of conditions and the following disclaimer.
                     11:  *
                     12:  * 2. Redistributions in binary form must reproduce the above copyright
                     13:  *    notice, this list of conditions and the following disclaimer in the
                     14:  *    documentation and/or other materials provided with the distribution.
                     15:  *
                     16:  * 3. The name of the author may not be used to endorse or promote products
                     17:  *    derived from this software without specific prior written permission.
                     18:  *
                     19:  * 4. Products derived from this software may not be called "Sudo" nor
                     20:  *    may "Sudo" appear in their names without specific prior written
                     21:  *    permission from the author.
                     22:  *
                     23:  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
                     24:  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
                     25:  * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
                     26:  * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
                     27:  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
                     28:  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
                     29:  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
                     30:  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
                     31:  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
                     32:  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
                     33:  */
                     34:
                     35: /*
                     36:  * Lock the sudoers file for safe editing (ala vipw) and check for parse errors.
                     37:  */
                     38:
                     39: #include "config.h"
                     40:
1.9.2.1 ! millert    41: #include <sys/types.h>
        !            42: #include <sys/param.h>
        !            43: #include <sys/stat.h>
        !            44: #include <sys/file.h>
        !            45: #include <sys/wait.h>
1.1       millert    46: #include <stdio.h>
                     47: #ifdef STDC_HEADERS
1.9.2.1 ! millert    48: # include <stdlib.h>
        !            49: # include <stddef.h>
        !            50: #else
        !            51: # ifdef HAVE_STDLIB_H
        !            52: #  include <stdlib.h>
        !            53: # endif
1.1       millert    54: #endif /* STDC_HEADERS */
1.9.2.1 ! millert    55: #ifdef HAVE_STRING_H
        !            56: # include <string.h>
        !            57: #else
        !            58: # ifdef HAVE_STRINGS_H
        !            59: #  include <strings.h>
        !            60: # endif
        !            61: #endif /* HAVE_STRING_H */
1.1       millert    62: #ifdef HAVE_UNISTD_H
                     63: #include <unistd.h>
                     64: #endif /* HAVE_UNISTD_H */
                     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.9.2.1 ! millert    76: static const char rcsid[] = "$Sudo: visudo.c,v 1.146 2002/01/17 15:35:54 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.9.2.1 ! millert    86: static int run_command         __P((char *, char **));
        !            87: static int check_syntax                __P((int));
1.1       millert    88: int command_matches            __P((char *, char *, char *, char *));
                     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.1       millert    92: int usergr_matches             __P((char *, char *));
                     93: void init_parser               __P((void));
                     94: void yyrestart                 __P((FILE *));
                     95:
                     96: /*
                     97:  * External globals exported by the parser
                     98:  */
                     99: extern FILE *yyin, *yyout;
                    100: extern int errorlineno;
                    101: extern int pedantic;
1.9.2.1 ! millert   102: extern int quiet;
        !           103:
        !           104: /* For getopt(3) */
        !           105: extern char *optarg;
        !           106: extern int optind;
1.1       millert   107:
                    108: /*
                    109:  * Globals
                    110:  */
                    111: char **Argv;
                    112: char *sudoers = _PATH_SUDOERS;
                    113: char *stmp = _PATH_SUDOERS_TMP;
                    114: struct sudo_user sudo_user;
                    115: int parse_error = FALSE;
                    116:
                    117: int
                    118: main(argc, argv)
                    119:     int argc;
                    120:     char **argv;
                    121: {
                    122:     char buf[MAXPATHLEN*2];            /* buffer used for copying files */
1.4       millert   123:     char *Editor;                      /* editor to use */
1.9.2.1 ! millert   124:     char *UserEditor;                  /* editor user wants to use */
        !           125:     char *EditorPath;                  /* colon-separated list of editors */
        !           126:     char *av[4];                       /* argument vector for run_command */
        !           127:     int checkonly;                     /* only check existing file? */
1.1       millert   128:     int sudoers_fd;                    /* sudoers file descriptor */
                    129:     int stmp_fd;                       /* stmp file descriptor */
                    130:     int n;                             /* length parameter */
1.9.2.1 ! millert   131:     int ch;                            /* getopt char */
1.1       millert   132:     time_t now;                                /* time now */
                    133:     struct stat stmp_sb, sudoers_sb;   /* to check for changes */
                    134:
                    135:     /* Warn about aliases that are used before being defined. */
                    136:     pedantic = 1;
                    137:
                    138:     /*
                    139:      * Parse command line options
                    140:      */
                    141:     Argv = argv;
                    142:
                    143:     /*
                    144:      * Arg handling.
                    145:      */
1.9.2.1 ! millert   146:     checkonly = 0;
        !           147:     while ((ch = getopt(argc, argv, "Vcf:sq")) != -1) {
        !           148:        switch (ch) {
        !           149:            case 'V':
        !           150:                (void) printf("visudo version %s\n", version);
        !           151:                exit(0);
        !           152:            case 'c':
        !           153:                checkonly++;            /* check mode */
        !           154:                break;
        !           155:            case 'f':
        !           156:                sudoers = optarg;       /* sudoers file path */
        !           157:                easprintf(&stmp, "%s.tmp", optarg);
        !           158:                break;
        !           159:            case 's':
        !           160:                pedantic++;             /* strict mode */
        !           161:                break;
        !           162:            case 'q':
        !           163:                quiet++;                /* quiet mode */
        !           164:                break;
        !           165:            default:
        !           166:                usage();
1.1       millert   167:        }
                    168:     }
1.9.2.1 ! millert   169:     argc -= optind;
        !           170:     argv += optind;
        !           171:     if (argc)
        !           172:        usage();
1.1       millert   173:
                    174:     /* Mock up a fake sudo_user struct. */
                    175:     user_host = user_shost = user_cmnd = "";
                    176:     if ((sudo_user.pw = getpwuid(getuid())) == NULL) {
                    177:        (void) fprintf(stderr, "%s: Can't find you in the passwd database.\n",
                    178:            Argv[0]);
                    179:        exit(1);
                    180:     }
                    181:
1.4       millert   182:     /* Setup defaults data structures. */
                    183:     init_defaults();
1.1       millert   184:
1.9.2.1 ! millert   185:     if (checkonly)
        !           186:        exit(check_syntax(quiet));
        !           187:
1.1       millert   188:     /*
1.3       millert   189:      * Open sudoers, lock it and stat it.
                    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.3       millert   193:     if (sudoers_fd == -1) {
                    194:        (void) fprintf(stderr, "%s: %s: %s\n", Argv[0], sudoers,
                    195:            strerror(errno));
1.9.2.1 ! millert   196:        exit(1);
1.1       millert   197:     }
1.3       millert   198:     if (!lock_file(sudoers_fd, SUDO_TLOCK)) {
1.1       millert   199:        (void) fprintf(stderr, "%s: sudoers file busy, try again later.\n",
                    200:            Argv[0]);
                    201:        exit(1);
                    202:     }
1.3       millert   203: #ifdef HAVE_FSTAT
                    204:     if (fstat(sudoers_fd, &sudoers_sb) == -1) {
1.1       millert   205: #else
1.3       millert   206:     if (stat(sudoers, &sudoers_sb) == -1) {
1.1       millert   207: #endif
1.3       millert   208:        (void) fprintf(stderr, "%s: can't stat %s: %s\n",
                    209:            Argv[0], sudoers, strerror(errno));
1.9.2.1 ! millert   210:        exit(1);
1.1       millert   211:     }
                    212:
1.3       millert   213:     /*
                    214:      * Open sudoers temp file.
                    215:      */
                    216:     stmp_fd = open(stmp, O_WRONLY | O_CREAT | O_TRUNC, 0600);
                    217:     if (stmp_fd < 0) {
                    218:        (void) fprintf(stderr, "%s: %s: %s\n", Argv[0], stmp, strerror(errno));
                    219:        exit(1);
                    220:     }
                    221:
1.1       millert   222:     /* Install signal handlers to clean up stmp if we are killed. */
                    223:     setup_signals();
                    224:
                    225:     /* Copy sudoers -> stmp and reset the mtime */
1.3       millert   226:     if (sudoers_sb.st_size) {
1.1       millert   227:        while ((n = read(sudoers_fd, buf, sizeof(buf))) > 0)
                    228:            if (write(stmp_fd, buf, n) != n) {
                    229:                (void) fprintf(stderr, "%s: Write failed: %s\n", Argv[0],
                    230:                strerror(errno));
                    231:                Exit(-1);
                    232:            }
                    233:
1.3       millert   234:        (void) close(stmp_fd);
                    235:        (void) touch(stmp, sudoers_sb.st_mtime);
1.4       millert   236:
                    237:        /* Parse sudoers to pull in editor and env_editor conf values. */
                    238:        if ((yyin = fopen(stmp, "r"))) {
                    239:            yyout = stdout;
                    240:            init_parser();
                    241:            yyparse();
                    242:            parse_error = FALSE;
                    243:            yyrestart(yyin);
                    244:            fclose(yyin);
                    245:        }
1.3       millert   246:     } else
                    247:        (void) close(stmp_fd);
1.1       millert   248:
                    249:     /*
1.9.2.1 ! millert   250:      * Check EDITOR and VISUAL environment variables to see which editor
        !           251:      * the user wants to use (we may not end up using it though).
        !           252:      * If the path is not fully-qualified, make it so and check that
        !           253:      * the specified executable actually exists.
        !           254:      */
        !           255:     if ((UserEditor = getenv("EDITOR")) == NULL || *UserEditor == '\0')
        !           256:        UserEditor = getenv("VISUAL");
        !           257:     if (UserEditor && *UserEditor == '\0')
        !           258:        UserEditor = NULL;
        !           259:     else if (UserEditor) {
        !           260:        if (find_path(UserEditor, &Editor, getenv("PATH")) == FOUND) {
        !           261:            UserEditor = Editor;
        !           262:        } else {
        !           263:            if (def_flag(I_ENV_EDITOR)) {
        !           264:                /* If we are honoring $EDITOR this is a fatal error. */
        !           265:                (void) fprintf(stderr,
        !           266:                    "%s: specified editor (%s) doesn't exist!\n",
        !           267:                    Argv[0], UserEditor);
        !           268:                Exit(-1);
        !           269:            } else {
        !           270:                /* Otherwise, just ignore $EDITOR. */
        !           271:                UserEditor = NULL;
        !           272:            }
        !           273:        }
        !           274:     }
        !           275:
        !           276:     /*
        !           277:      * See if we can use the user's choice of editors either because
        !           278:      * we allow any $EDITOR or because $EDITOR is in the allowable list.
1.4       millert   279:      */
1.9.2.1 ! millert   280:     Editor = EditorPath = NULL;
        !           281:     if (def_flag(I_ENV_EDITOR) && UserEditor)
        !           282:        Editor = UserEditor;
        !           283:     else if (UserEditor) {
        !           284:        struct stat editor_sb;
        !           285:        struct stat user_editor_sb;
        !           286:        char *base, *userbase;
        !           287:
        !           288:        if (stat(UserEditor, &user_editor_sb) != 0) {
        !           289:            /* Should never happen since we already checked above. */
        !           290:            (void) fprintf(stderr, "%s: unable to stat editor (%s): %s\n",
        !           291:                Argv[0], UserEditor, strerror(errno));
        !           292:            Exit(-1);
        !           293:        }
        !           294:        EditorPath = estrdup(def_str(I_EDITOR));
        !           295:        Editor = strtok(EditorPath, ":");
        !           296:        do {
        !           297:            /*
        !           298:             * Both Editor and UserEditor should be fully qualified but
        !           299:             * check anyway...
        !           300:             */
        !           301:            if ((base = strrchr(Editor, '/')) == NULL)
        !           302:                continue;
        !           303:            if ((userbase = strrchr(UserEditor, '/')) == NULL) {
        !           304:                Editor = NULL;
        !           305:                break;
        !           306:            }
        !           307:            base++, userbase++;
        !           308:
        !           309:            /*
        !           310:             * We compare the basenames first and then use stat to match
        !           311:             * for sure.
        !           312:             */
        !           313:            if (strcmp(base, userbase) == 0) {
        !           314:                if (stat(Editor, &editor_sb) == 0 && S_ISREG(editor_sb.st_mode)
        !           315:                    && (editor_sb.st_mode & 0000111) &&
        !           316:                    editor_sb.st_dev == user_editor_sb.st_dev &&
        !           317:                    editor_sb.st_ino == user_editor_sb.st_ino)
        !           318:                    break;
        !           319:            }
        !           320:        } while ((Editor = strtok(NULL, ":")));
        !           321:     }
1.4       millert   322:
                    323:     /*
1.9.2.1 ! millert   324:      * Can't use $EDITOR, try each element of I_EDITOR until we
        !           325:      * find one that exists, is regular, and is executable.
1.1       millert   326:      */
1.9.2.1 ! millert   327:     if (Editor == NULL || *Editor == '\0') {
        !           328:        if (EditorPath != NULL)
        !           329:            free(EditorPath);
        !           330:        EditorPath = estrdup(def_str(I_EDITOR));
        !           331:        Editor = strtok(EditorPath, ":");
        !           332:        do {
        !           333:            if (sudo_goodpath(Editor))
        !           334:                break;
        !           335:        } while ((Editor = strtok(NULL, ":")));
        !           336:
        !           337:        /* Bleah, none of the editors existed! */
        !           338:        if (Editor == NULL || *Editor == '\0') {
        !           339:            (void) fprintf(stderr, "%s: no editor found (editor path = %s)\n",
        !           340:                Argv[0], def_str(I_EDITOR));
1.1       millert   341:            Exit(-1);
                    342:        }
1.9.2.1 ! millert   343:     }
        !           344:
        !           345:     /*
        !           346:      * Edit the temp file and parse it (for sanity checking)
        !           347:      */
        !           348:     do {
        !           349:        char linestr[64];
        !           350:
        !           351:        /* Build up argument vector for the command */
        !           352:        if ((av[0] = strrchr(Editor, '/')) != NULL)
        !           353:            av[0]++;
1.1       millert   354:        else
1.9.2.1 ! millert   355:            av[0] = Editor;
        !           356:        n = 1;
        !           357:        if (parse_error == TRUE) {
        !           358:            (void) snprintf(linestr, sizeof(linestr), "+%d", errorlineno);
        !           359:            av[n++] = linestr;
        !           360:        }
        !           361:        av[n++] = stmp;
        !           362:        av[n++] = NULL;
1.1       millert   363:
1.9.2.1 ! millert   364:        /*
        !           365:         * Do the edit:
        !           366:         *  We cannot check the editor's exit value against 0 since
        !           367:         *  XPG4 specifies that vi's exit value is a function of the
        !           368:         *  number of errors during editing (?!?!).
        !           369:         */
1.1       millert   370:        now = time(NULL);
1.9.2.1 ! millert   371:        if (run_command(Editor, av) != -1) {
1.1       millert   372:            /*
                    373:             * Sanity checks.
                    374:             */
                    375:            if (stat(stmp, &stmp_sb) < 0) {
                    376:                (void) fprintf(stderr,
                    377:                    "%s: Can't stat temporary file (%s), %s unchanged.\n",
                    378:                    Argv[0], stmp, sudoers);
                    379:                Exit(-1);
                    380:            }
                    381:            if (stmp_sb.st_size == 0) {
                    382:                (void) fprintf(stderr,
                    383:                    "%s: Zero length temporary file (%s), %s unchanged.\n",
                    384:                    Argv[0], stmp, sudoers);
                    385:                Exit(-1);
                    386:            }
                    387:
                    388:            /*
                    389:             * Passed sanity checks so reopen stmp file and check
                    390:             * for parse errors.
                    391:             */
                    392:            yyout = stdout;
                    393:            if (parse_error)
                    394:                yyin = freopen(stmp, "r", yyin);
                    395:            else
                    396:                yyin = fopen(stmp, "r");
                    397:            if (yyin == NULL) {
                    398:                (void) fprintf(stderr,
                    399:                    "%s: Can't re-open temporary file (%s), %s unchanged.\n",
                    400:                    Argv[0], stmp, sudoers);
                    401:                Exit(-1);
                    402:            }
                    403:
                    404:            /* Clean slate for each parse */
1.5       millert   405:            user_runas = NULL;
1.1       millert   406:            init_defaults();
                    407:            init_parser();
                    408:
                    409:            /* Parse the sudoers file */
                    410:            if (yyparse() && parse_error != TRUE) {
                    411:                (void) fprintf(stderr,
                    412:                    "%s: Failed to parse temporary file (%s), unknown error.\n",
                    413:                    Argv[0], stmp);
                    414:                parse_error = TRUE;
                    415:            }
                    416:        } else {
                    417:            (void) fprintf(stderr,
1.9.2.1 ! millert   418:                "%s: Editor (%s) failed, %s unchanged.\n", Argv[0],
        !           419:                    Editor, sudoers);
1.1       millert   420:            Exit(-1);
                    421:        }
                    422:
                    423:        /*
                    424:         * Got an error, prompt the user for what to do now
                    425:         */
                    426:        if (parse_error == TRUE) {
                    427:            switch (whatnow()) {
1.7       millert   428:                case 'Q' :      parse_error = FALSE;    /* ignore parse error */
1.1       millert   429:                                break;
1.8       millert   430:                case 'x' :      if (sudoers_sb.st_size == 0)
                    431:                                    unlink(sudoers);
                    432:                                Exit(0);
1.1       millert   433:                                break;
                    434:            }
                    435:            yyrestart(yyin);    /* reset lexer */
                    436:        }
                    437:     } while (parse_error == TRUE);
                    438:
                    439:     /*
                    440:      * If the user didn't change the temp file, just unlink it.
                    441:      */
                    442:     if (sudoers_sb.st_mtime != now && sudoers_sb.st_mtime == stmp_sb.st_mtime &&
                    443:        sudoers_sb.st_size == stmp_sb.st_size) {
                    444:        (void) fprintf(stderr, "%s: sudoers file unchanged.\n", Argv[0]);
                    445:        Exit(0);
                    446:     }
                    447:
                    448:     /*
                    449:      * Change mode and ownership of temp file so when
                    450:      * we move it to sudoers things are kosher.
                    451:      */
                    452:     if (chown(stmp, SUDOERS_UID, SUDOERS_GID)) {
                    453:        (void) fprintf(stderr,
                    454:            "%s: Unable to set (uid, gid) of %s to (%d, %d): %s\n",
                    455:            Argv[0], stmp, SUDOERS_UID, SUDOERS_GID, strerror(errno));
                    456:        Exit(-1);
                    457:     }
                    458:     if (chmod(stmp, SUDOERS_MODE)) {
                    459:        (void) fprintf(stderr,
                    460:            "%s: Unable to change mode of %s to %o: %s\n",
                    461:            Argv[0], stmp, SUDOERS_MODE, strerror(errno));
                    462:        Exit(-1);
                    463:     }
                    464:
                    465:     /*
                    466:      * Now that we have a sane stmp file (parses ok) it needs to be
                    467:      * rename(2)'d to sudoers.  If the rename(2) fails we try using
                    468:      * mv(1) in case stmp and sudoers are on different filesystems.
                    469:      */
                    470:     if (rename(stmp, sudoers)) {
                    471:        if (errno == EXDEV) {
                    472:            (void) fprintf(stderr,
                    473:              "%s: %s and %s not on the same filesystem, using mv to rename.\n",
                    474:              Argv[0], stmp, sudoers);
                    475:
1.9.2.1 ! millert   476:            /* Build up argument vector for the command */
        !           477:            if ((av[0] = strrchr(_PATH_MV, '/')) != NULL)
        !           478:                av[0]++;
        !           479:            else
        !           480:                av[0] = _PATH_MV;
        !           481:            av[1] = stmp;
        !           482:            av[2] = sudoers;
        !           483:            av[3] = NULL;
1.1       millert   484:
1.9.2.1 ! millert   485:            /* And run it... */
        !           486:            if (run_command(_PATH_MV, av)) {
1.1       millert   487:                (void) fprintf(stderr,
1.9.2.1 ! millert   488:                               "%s: Command failed: '%s %s %s', %s unchanged.\n",
        !           489:                               Argv[0], _PATH_MV, stmp, sudoers, sudoers);
1.1       millert   490:                Exit(-1);
                    491:            }
                    492:        } else {
                    493:            (void) fprintf(stderr, "%s: Error renaming %s, %s unchanged: %s\n",
                    494:                                   Argv[0], stmp, sudoers, strerror(errno));
                    495:            Exit(-1);
                    496:        }
                    497:     }
                    498:
1.3       millert   499:     exit(0);
1.1       millert   500: }
                    501:
                    502: /*
                    503:  * Dummy *_matches routines.
                    504:  * These exist to allow us to use the same parser as sudo(8).
                    505:  */
                    506: int
                    507: command_matches(cmnd, cmnd_args, path, sudoers_args)
                    508:     char *cmnd;
                    509:     char *cmnd_args;
                    510:     char *path;
                    511:     char *sudoers_args;
                    512: {
                    513:     return(TRUE);
                    514: }
                    515:
                    516: int
                    517: addr_matches(n)
                    518:     char *n;
1.4       millert   519: {
                    520:     return(TRUE);
                    521: }
                    522:
                    523: int
                    524: hostname_matches(s, l, p)
                    525:     char *s, *l, *p;
1.1       millert   526: {
                    527:     return(TRUE);
                    528: }
                    529:
                    530: int
                    531: usergr_matches(g, u)
                    532:     char *g, *u;
                    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.9.2.1 ! millert   550: int
        !           551: user_is_exempt()
        !           552: {
        !           553:     return(TRUE);
        !           554: }
        !           555:
        !           556: void
        !           557: init_envtables()
        !           558: {
        !           559:     return;
        !           560: }
        !           561:
1.1       millert   562: /*
                    563:  * Assuming a parse error occurred, prompt the user for what they want
                    564:  * to do now.  Returns the first letter of their choice.
                    565:  */
                    566: static char
                    567: whatnow()
                    568: {
                    569:     int choice, c;
                    570:
                    571:     for (;;) {
                    572:        (void) fputs("What now? ", stdout);
                    573:        choice = getchar();
                    574:        for (c = choice; c != '\n' && c != EOF;)
                    575:            c = getchar();
                    576:
1.3       millert   577:        switch (choice) {
                    578:            case EOF:
                    579:                choice = 'x';
                    580:                /* FALLTHROUGH */
                    581:            case 'e':
                    582:            case 'x':
                    583:            case 'Q':
                    584:                return(choice);
                    585:            default:
                    586:                (void) puts("Options are:");
                    587:                (void) puts("  (e)dit sudoers file again");
                    588:                (void) puts("  e(x)it without saving changes to sudoers file");
                    589:                (void) puts("  (Q)uit and save changes to sudoers file (DANGER!)\n");
1.1       millert   590:        }
                    591:     }
                    592: }
                    593:
                    594: /*
                    595:  * Install signal handlers for visudo.
                    596:  */
                    597: static void
                    598: setup_signals()
                    599: {
1.9.2.1 ! millert   600:        sigaction_t sa;
1.1       millert   601:
                    602:        /*
                    603:         * Setup signal handlers to cleanup nicely.
                    604:         */
1.9.2.1 ! millert   605:        sigemptyset(&sa.sa_mask);
        !           606:        sa.sa_flags = SA_RESTART;
        !           607:        sa.sa_handler = Exit;
        !           608:        (void) sigaction(SIGTERM, &sa, NULL);
        !           609:        (void) sigaction(SIGHUP, &sa, NULL);
        !           610:        (void) sigaction(SIGINT, &sa, NULL);
        !           611:        (void) sigaction(SIGQUIT, &sa, NULL);
        !           612: }
        !           613:
        !           614: static int
        !           615: run_command(path, argv)
        !           616:     char *path;
        !           617:     char **argv;
        !           618: {
        !           619:     int status;
        !           620:     pid_t pid;
        !           621:     sigset_t set, oset;
        !           622:
        !           623:     (void) sigemptyset(&set);
        !           624:     (void) sigaddset(&set, SIGCHLD);
        !           625:     (void) sigprocmask(SIG_BLOCK, &set, &oset);
        !           626:
        !           627:     switch (pid = fork()) {
        !           628:        case -1:
        !           629:            (void) fprintf(stderr,
        !           630:                "%s: unable to run %s: %s\n", Argv[0], path, strerror(errno));
        !           631:            Exit(-1);
        !           632:            break;      /* NOTREACHED */
        !           633:        case 0:
        !           634:            (void) sigprocmask(SIG_SETMASK, &oset, NULL);
        !           635:            execv(path, argv);
        !           636:            (void) fprintf(stderr,
        !           637:                "%s: unable to run %s: %s\n", Argv[0], path, strerror(errno));
        !           638:            _exit(127);
        !           639:            break;      /* NOTREACHED */
        !           640:     }
        !           641:
        !           642: #ifdef sudo_waitpid
        !           643:     pid = sudo_waitpid(pid, &status, 0);
1.1       millert   644: #else
1.9.2.1 ! millert   645:     pid = wait(&status);
        !           646: #endif
        !           647:
        !           648:     (void) sigprocmask(SIG_SETMASK, &oset, NULL);
        !           649:
        !           650:     /* XXX - should use WEXITSTATUS() */
        !           651:     return(pid == -1 ? -1 : (status >> 8));
        !           652: }
        !           653:
        !           654: static int
        !           655: check_syntax(quiet)
        !           656:     int quiet;
        !           657: {
        !           658:
        !           659:     if ((yyin = fopen(sudoers, "r")) == NULL) {
        !           660:        if (!quiet)
        !           661:            (void) fprintf(stderr, "%s: unable to open %s: %s\n", Argv[0],
        !           662:                sudoers, strerror(errno));
        !           663:        exit(1);
        !           664:     }
        !           665:     yyout = stdout;
        !           666:     init_parser();
        !           667:     if (yyparse() && parse_error != TRUE) {
        !           668:        if (!quiet)
        !           669:            (void) fprintf(stderr,
        !           670:                "%s: failed to parse %s file, unknown error.\n",
        !           671:                Argv[0], sudoers);
        !           672:        parse_error = TRUE;
        !           673:     }
        !           674:     if (!quiet){
        !           675:        if (parse_error)
        !           676:            (void) printf("parse error in %s near line %d\n", sudoers,
        !           677:                errorlineno);
        !           678:        else
        !           679:            (void) printf("%s file parsed OK\n", sudoers);
        !           680:     }
        !           681:
        !           682:     return(parse_error == TRUE);
1.1       millert   683: }
                    684:
                    685: /*
                    686:  * Unlink the sudoers temp file (if it exists) and exit.
                    687:  * Used in place of a normal exit() and as a signal handler.
1.9.2.1 ! millert   688:  * A positive parameter indicates we were called as a signal handler.
1.1       millert   689:  */
                    690: static RETSIGTYPE
                    691: Exit(sig)
                    692:     int sig;
                    693: {
1.9.2.1 ! millert   694:     char *emsg = " exiting due to signal.\n";
1.1       millert   695:
1.9.2.1 ! millert   696:     (void) unlink(stmp);
1.1       millert   697:
1.9.2.1 ! millert   698:     if (sig > 0) {
        !           699:        write(STDERR_FILENO, Argv[0], strlen(Argv[0]));
        !           700:        write(STDERR_FILENO, emsg, sizeof(emsg) - 1);
        !           701:        _exit(-sig);
        !           702:     }
        !           703:     exit(-sig);
1.1       millert   704: }
                    705:
                    706: static void
                    707: usage()
                    708: {
1.9.2.1 ! millert   709:     (void) fprintf(stderr, "usage: %s [-c] [-f sudoers] [-q] [-s] [-V]\n",
        !           710:        Argv[0]);
1.1       millert   711:     exit(1);
                    712: }