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

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