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

1.1     ! millert     1: /*
        !             2:  * Copyright (c) 1996, 1998, 1999 Todd C. Miller <Todd.Miller@courtesan.com>
        !             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:
        !            41: #include <stdio.h>
        !            42: #ifdef STDC_HEADERS
        !            43: #include <stdlib.h>
        !            44: #endif /* STDC_HEADERS */
        !            45: #ifdef HAVE_UNISTD_H
        !            46: #include <unistd.h>
        !            47: #endif /* HAVE_UNISTD_H */
        !            48: #ifdef HAVE_STRING_H
        !            49: #include <string.h>
        !            50: #endif /* HAVE_STRING_H */
        !            51: #ifdef HAVE_STRINGS_H
        !            52: #include <strings.h>
        !            53: #endif /* HAVE_STRINGS_H */
        !            54: #if defined(HAVE_MALLOC_H) && !defined(STDC_HEADERS)
        !            55: #include <malloc.h>
        !            56: #endif /* HAVE_MALLOC_H && !STDC_HEADERS */
        !            57: #include <ctype.h>
        !            58: #include <pwd.h>
        !            59: #include <time.h>
        !            60: #include <signal.h>
        !            61: #include <errno.h>
        !            62: #include <fcntl.h>
        !            63: #include <sys/types.h>
        !            64: #include <sys/param.h>
        !            65: #include <sys/stat.h>
        !            66: #include <sys/file.h>
        !            67:
        !            68: #include "sudo.h"
        !            69: #include "version.h"
        !            70:
        !            71: #ifndef STDC_HEADERS
        !            72: #ifndef __GNUC__       /* gcc has its own malloc */
        !            73: extern char *malloc    __P((size_t));
        !            74: #endif /* __GNUC__ */
        !            75: extern char *getenv    __P((const char *));
        !            76: extern int stat                __P((const char *, struct stat *));
        !            77: #endif /* !STDC_HEADERS */
        !            78:
        !            79: #if defined(POSIX_SIGNALS) && !defined(SA_RESETHAND)
        !            80: #define SA_RESETHAND    0
        !            81: #endif /* POSIX_SIGNALS && !SA_RESETHAND */
        !            82:
        !            83: #ifndef lint
        !            84: static const char rcsid[] = "$Sudo: visudo.c,v 1.116 1999/11/09 20:12:20 millert Exp $";
        !            85: #endif /* lint */
        !            86:
        !            87: /*
        !            88:  * Function prototypes
        !            89:  */
        !            90: static void usage              __P((void));
        !            91: static char whatnow            __P((void));
        !            92: static RETSIGTYPE Exit         __P((int));
        !            93: static void setup_signals      __P((void));
        !            94: int command_matches            __P((char *, char *, char *, char *));
        !            95: int addr_matches               __P((char *));
        !            96: int netgr_matches              __P((char *, char *, char *));
        !            97: int usergr_matches             __P((char *, char *));
        !            98: void init_parser               __P((void));
        !            99: void yyrestart                 __P((FILE *));
        !           100:
        !           101: /*
        !           102:  * External globals exported by the parser
        !           103:  */
        !           104: extern FILE *yyin, *yyout;
        !           105: extern int errorlineno;
        !           106: extern int pedantic;
        !           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:
        !           118: int
        !           119: main(argc, argv)
        !           120:     int argc;
        !           121:     char **argv;
        !           122: {
        !           123:     char buf[MAXPATHLEN*2];            /* buffer used for copying files */
        !           124:     char *Editor = EDITOR;             /* editor to use (default is EDITOR */
        !           125:     int sudoers_fd;                    /* sudoers file descriptor */
        !           126:     int stmp_fd;                       /* stmp file descriptor */
        !           127:     int n;                             /* length parameter */
        !           128:     time_t now;                                /* time now */
        !           129:     struct stat stmp_sb, sudoers_sb;   /* to check for changes */
        !           130:
        !           131:     /* Warn about aliases that are used before being defined. */
        !           132:     pedantic = 1;
        !           133:
        !           134:     /*
        !           135:      * Parse command line options
        !           136:      */
        !           137:     Argv = argv;
        !           138:
        !           139:     /*
        !           140:      * Arg handling.
        !           141:      */
        !           142:     while (--argc) {
        !           143:        if (!strcmp(argv[argc], "-V")) {
        !           144:            (void) printf("visudo version %s\n", version);
        !           145:            exit(0);
        !           146:        } else if (!strcmp(argv[argc], "-s")) {
        !           147:            pedantic++;                 /* strict mode */
        !           148:        } else {
        !           149:            usage();
        !           150:        }
        !           151:     }
        !           152:
        !           153:     /* Mock up a fake sudo_user struct. */
        !           154:     user_host = user_shost = user_cmnd = "";
        !           155:     if ((sudo_user.pw = getpwuid(getuid())) == NULL) {
        !           156:        (void) fprintf(stderr, "%s: Can't find you in the passwd database.\n",
        !           157:            Argv[0]);
        !           158:        exit(1);
        !           159:     }
        !           160:
        !           161: #ifdef ENV_EDITOR
        !           162:     /*
        !           163:      * If we are allowing EDITOR and VISUAL envariables set Editor
        !           164:      * base on whichever exists...
        !           165:      */
        !           166:     if (!(Editor = getenv("EDITOR")))
        !           167:        if (!(Editor = getenv("VISUAL")))
        !           168:            Editor = EDITOR;
        !           169: #endif /* ENV_EDITOR */
        !           170:
        !           171:     /*
        !           172:      * Open sudoers temp file and grab a lock.
        !           173:      */
        !           174:     stmp_fd = open(stmp, O_WRONLY | O_CREAT, 0600);
        !           175:     if (stmp_fd < 0) {
        !           176:        (void) fprintf(stderr, "%s: %s\n", Argv[0], strerror(errno));
        !           177:        exit(1);
        !           178:     }
        !           179:     if (!lock_file(stmp_fd, SUDO_TLOCK)) {
        !           180:        (void) fprintf(stderr, "%s: sudoers file busy, try again later.\n",
        !           181:            Argv[0]);
        !           182:        exit(1);
        !           183:     }
        !           184: #ifdef HAVE_FTRUNCATE
        !           185:     if (ftruncate(stmp_fd, 0) == -1) {
        !           186: #else
        !           187:     if (truncate(stmp, 0) == -1) {
        !           188: #endif
        !           189:        (void) fprintf(stderr, "%s: can't truncate %s: %s\n", Argv[0],
        !           190:            stmp, strerror(errno));
        !           191:        Exit(-1);
        !           192:     }
        !           193:
        !           194:     /* Install signal handlers to clean up stmp if we are killed. */
        !           195:     setup_signals();
        !           196:
        !           197:     (void) memset(&sudoers_sb, 0, sizeof(sudoers_sb));
        !           198:     if (stat(sudoers, &sudoers_sb) == -1 && errno != ENOENT) {
        !           199:        (void) fprintf(stderr, "%s: %s\n", Argv[0], strerror(errno));
        !           200:        Exit(-1);
        !           201:     }
        !           202:     sudoers_fd = open(sudoers, O_RDONLY);
        !           203:     if (sudoers_fd == -1 && errno != ENOENT) {
        !           204:        (void) fprintf(stderr, "%s: %s\n", Argv[0], strerror(errno));
        !           205:        Exit(-1);
        !           206:     }
        !           207:
        !           208:     /* Copy sudoers -> stmp and reset the mtime */
        !           209:     if (sudoers_fd != -1) {
        !           210:        while ((n = read(sudoers_fd, buf, sizeof(buf))) > 0)
        !           211:            if (write(stmp_fd, buf, n) != n) {
        !           212:                (void) fprintf(stderr, "%s: Write failed: %s\n", Argv[0],
        !           213:                strerror(errno));
        !           214:                Exit(-1);
        !           215:            }
        !           216:
        !           217:        (void) close(sudoers_fd);
        !           218:     }
        !           219:     (void) close(stmp_fd);
        !           220:     (void) touch(stmp, sudoers_sb.st_mtime);
        !           221:
        !           222:     /*
        !           223:      * Edit the temp file and parse it (for sanity checking)
        !           224:      */
        !           225:     do {
        !           226:        /*
        !           227:         * Build up a buffer to execute
        !           228:         */
        !           229:        if (strlen(Editor) + strlen(stmp) + 30 > sizeof(buf)) {
        !           230:            (void) fprintf(stderr, "%s: Buffer too short (line %d).\n",
        !           231:                           Argv[0], __LINE__);
        !           232:            Exit(-1);
        !           233:        }
        !           234:        if (parse_error == TRUE)
        !           235:            (void) sprintf(buf, "%s +%d %s", Editor, errorlineno, stmp);
        !           236:        else
        !           237:            (void) sprintf(buf, "%s %s", Editor, stmp);
        !           238:
        !           239:        /* Do the edit -- some SYSV editors exit with 1 instead of 0 */
        !           240:        now = time(NULL);
        !           241:        n = system(buf);
        !           242:        if (n != -1 && ((n >> 8) == 0 || (n >> 8) == 1)) {
        !           243:            /*
        !           244:             * Sanity checks.
        !           245:             */
        !           246:            if (stat(stmp, &stmp_sb) < 0) {
        !           247:                (void) fprintf(stderr,
        !           248:                    "%s: Can't stat temporary file (%s), %s unchanged.\n",
        !           249:                    Argv[0], stmp, sudoers);
        !           250:                Exit(-1);
        !           251:            }
        !           252:            if (stmp_sb.st_size == 0) {
        !           253:                (void) fprintf(stderr,
        !           254:                    "%s: Zero length temporary file (%s), %s unchanged.\n",
        !           255:                    Argv[0], stmp, sudoers);
        !           256:                Exit(-1);
        !           257:            }
        !           258:
        !           259:            /*
        !           260:             * Passed sanity checks so reopen stmp file and check
        !           261:             * for parse errors.
        !           262:             */
        !           263:            yyout = stdout;
        !           264:            if (parse_error)
        !           265:                yyin = freopen(stmp, "r", yyin);
        !           266:            else
        !           267:                yyin = fopen(stmp, "r");
        !           268:            if (yyin == NULL) {
        !           269:                (void) fprintf(stderr,
        !           270:                    "%s: Can't re-open temporary file (%s), %s unchanged.\n",
        !           271:                    Argv[0], stmp, sudoers);
        !           272:                Exit(-1);
        !           273:            }
        !           274:
        !           275:            /* Clean slate for each parse */
        !           276:            init_defaults();
        !           277:            init_parser();
        !           278:
        !           279:            /* Parse the sudoers file */
        !           280:            if (yyparse() && parse_error != TRUE) {
        !           281:                (void) fprintf(stderr,
        !           282:                    "%s: Failed to parse temporary file (%s), unknown error.\n",
        !           283:                    Argv[0], stmp);
        !           284:                parse_error = TRUE;
        !           285:            }
        !           286:        } else {
        !           287:            (void) fprintf(stderr,
        !           288:                "%s: Editor (%s) failed with exit status %d, %s unchanged.\n",
        !           289:                Argv[0], Editor, n, sudoers);
        !           290:            Exit(-1);
        !           291:        }
        !           292:
        !           293:        /*
        !           294:         * Got an error, prompt the user for what to do now
        !           295:         */
        !           296:        if (parse_error == TRUE) {
        !           297:            switch (whatnow()) {
        !           298:                case 'q' :      parse_error = FALSE;    /* ignore parse error */
        !           299:                                break;
        !           300:                case 'x' :      Exit(0);
        !           301:                                break;
        !           302:            }
        !           303:            yyrestart(yyin);    /* reset lexer */
        !           304:        }
        !           305:     } while (parse_error == TRUE);
        !           306:
        !           307:     /*
        !           308:      * If the user didn't change the temp file, just unlink it.
        !           309:      */
        !           310:     if (sudoers_sb.st_mtime != now && sudoers_sb.st_mtime == stmp_sb.st_mtime &&
        !           311:        sudoers_sb.st_size == stmp_sb.st_size) {
        !           312:        (void) fprintf(stderr, "%s: sudoers file unchanged.\n", Argv[0]);
        !           313:        Exit(0);
        !           314:     }
        !           315:
        !           316:     /*
        !           317:      * Change mode and ownership of temp file so when
        !           318:      * we move it to sudoers things are kosher.
        !           319:      */
        !           320:     if (chown(stmp, SUDOERS_UID, SUDOERS_GID)) {
        !           321:        (void) fprintf(stderr,
        !           322:            "%s: Unable to set (uid, gid) of %s to (%d, %d): %s\n",
        !           323:            Argv[0], stmp, SUDOERS_UID, SUDOERS_GID, strerror(errno));
        !           324:        Exit(-1);
        !           325:     }
        !           326:     if (chmod(stmp, SUDOERS_MODE)) {
        !           327:        (void) fprintf(stderr,
        !           328:            "%s: Unable to change mode of %s to %o: %s\n",
        !           329:            Argv[0], stmp, SUDOERS_MODE, strerror(errno));
        !           330:        Exit(-1);
        !           331:     }
        !           332:
        !           333:     /*
        !           334:      * Now that we have a sane stmp file (parses ok) it needs to be
        !           335:      * rename(2)'d to sudoers.  If the rename(2) fails we try using
        !           336:      * mv(1) in case stmp and sudoers are on different filesystems.
        !           337:      */
        !           338:     if (rename(stmp, sudoers)) {
        !           339:        if (errno == EXDEV) {
        !           340:            char *tmpbuf;
        !           341:
        !           342:            (void) fprintf(stderr,
        !           343:              "%s: %s and %s not on the same filesystem, using mv to rename.\n",
        !           344:              Argv[0], stmp, sudoers);
        !           345:
        !           346:            /* Allocate just enough space for tmpbuf */
        !           347:            n = sizeof(char) * (strlen(_PATH_MV) + strlen(stmp) +
        !           348:                  strlen(sudoers) + 4);
        !           349:            if ((tmpbuf = (char *) malloc(n)) == NULL) {
        !           350:                (void) fprintf(stderr,
        !           351:                              "%s: Cannot alocate memory, %s unchanged: %s\n",
        !           352:                              Argv[0], sudoers, strerror(errno));
        !           353:                Exit(-1);
        !           354:            }
        !           355:
        !           356:            /* Build up command and execute it */
        !           357:            (void) sprintf(tmpbuf, "%s %s %s", _PATH_MV, stmp, sudoers);
        !           358:            if (system(tmpbuf)) {
        !           359:                (void) fprintf(stderr,
        !           360:                               "%s: Command failed: '%s', %s unchanged.\n",
        !           361:                               Argv[0], tmpbuf, sudoers);
        !           362:                Exit(-1);
        !           363:            }
        !           364:            free(tmpbuf);
        !           365:        } else {
        !           366:            (void) fprintf(stderr, "%s: Error renaming %s, %s unchanged: %s\n",
        !           367:                                   Argv[0], stmp, sudoers, strerror(errno));
        !           368:            Exit(-1);
        !           369:        }
        !           370:     }
        !           371:
        !           372:     return(0);
        !           373: }
        !           374:
        !           375: /*
        !           376:  * Dummy *_matches routines.
        !           377:  * These exist to allow us to use the same parser as sudo(8).
        !           378:  */
        !           379: int
        !           380: command_matches(cmnd, cmnd_args, path, sudoers_args)
        !           381:     char *cmnd;
        !           382:     char *cmnd_args;
        !           383:     char *path;
        !           384:     char *sudoers_args;
        !           385: {
        !           386:     return(TRUE);
        !           387: }
        !           388:
        !           389: int
        !           390: addr_matches(n)
        !           391:     char *n;
        !           392: {
        !           393:     return(TRUE);
        !           394: }
        !           395:
        !           396: int
        !           397: usergr_matches(g, u)
        !           398:     char *g, *u;
        !           399: {
        !           400:     return(TRUE);
        !           401: }
        !           402:
        !           403: int
        !           404: netgr_matches(n, h, u)
        !           405:     char *n, *h, *u;
        !           406: {
        !           407:     return(TRUE);
        !           408: }
        !           409:
        !           410: /*
        !           411:  * Assuming a parse error occurred, prompt the user for what they want
        !           412:  * to do now.  Returns the first letter of their choice.
        !           413:  */
        !           414: static char
        !           415: whatnow()
        !           416: {
        !           417:     int choice, c;
        !           418:
        !           419:     for (;;) {
        !           420:        (void) fputs("What now? ", stdout);
        !           421:        choice = getchar();
        !           422:        for (c = choice; c != '\n' && c != EOF;)
        !           423:            c = getchar();
        !           424:
        !           425:        if (choice == 'e' || choice == 'x' || choice == 'Q')
        !           426:            break;
        !           427:        else {
        !           428:            (void) puts("Options are:");
        !           429:            (void) puts("  (e)dit sudoers file again");
        !           430:            (void) puts("  e(x)it without saving changes to sudoers file");
        !           431:            (void) puts("  (Q)uit and save changes to sudoers file (DANGER!)\n");
        !           432:        }
        !           433:     }
        !           434:
        !           435:     return(choice);
        !           436: }
        !           437:
        !           438: /*
        !           439:  * Install signal handlers for visudo.
        !           440:  */
        !           441: static void
        !           442: setup_signals()
        !           443: {
        !           444: #ifdef POSIX_SIGNALS
        !           445:        struct sigaction action;                /* POSIX signal structure */
        !           446: #endif /* POSIX_SIGNALS */
        !           447:
        !           448:        /*
        !           449:         * Setup signal handlers to cleanup nicely.
        !           450:         */
        !           451: #ifdef POSIX_SIGNALS
        !           452:        (void) memset((VOID *)&action, 0, sizeof(action));
        !           453:        action.sa_handler = Exit;
        !           454:        action.sa_flags = SA_RESETHAND;
        !           455:        (void) sigaction(SIGTERM, &action, NULL);
        !           456:        (void) sigaction(SIGHUP, &action, NULL);
        !           457:        (void) sigaction(SIGINT, &action, NULL);
        !           458:        (void) sigaction(SIGQUIT, &action, NULL);
        !           459: #else
        !           460:        (void) signal(SIGTERM, Exit);
        !           461:        (void) signal(SIGHUP, Exit);
        !           462:        (void) signal(SIGINT, Exit);
        !           463:        (void) signal(SIGQUIT, Exit);
        !           464: #endif /* POSIX_SIGNALS */
        !           465: }
        !           466:
        !           467: /*
        !           468:  * Unlink the sudoers temp file (if it exists) and exit.
        !           469:  * Used in place of a normal exit() and as a signal handler.
        !           470:  * A positive parameter is considered to be a signal and is reported.
        !           471:  */
        !           472: static RETSIGTYPE
        !           473: Exit(sig)
        !           474:     int sig;
        !           475: {
        !           476:     (void) unlink(stmp);
        !           477:
        !           478:     if (sig > 0)
        !           479:        (void) fprintf(stderr, "%s exiting, caught signal %d.\n", Argv[0], sig);
        !           480:
        !           481:     exit(-sig);
        !           482: }
        !           483:
        !           484: static void
        !           485: usage()
        !           486: {
        !           487:     (void) fprintf(stderr, "usage: %s [-s] [-V]\n", Argv[0]);
        !           488:     exit(1);
        !           489: }