=================================================================== RCS file: /cvsrepo/anoncvs/cvs/src/usr.bin/sudo/Attic/visudo.c,v retrieving revision 1.9 retrieving revision 1.9.4.1 diff -u -r1.9 -r1.9.4.1 --- src/usr.bin/sudo/Attic/visudo.c 2001/01/19 17:58:19 1.9 +++ src/usr.bin/sudo/Attic/visudo.c 2002/01/18 16:14:46 1.9.4.1 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 1998-2000 Todd C. Miller + * Copyright (c) 1996, 1998-2001 Todd C. Miller * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -38,50 +38,42 @@ #include "config.h" +#include +#include +#include +#include +#include #include #ifdef STDC_HEADERS -#include +# include +# include +#else +# ifdef HAVE_STDLIB_H +# include +# endif #endif /* STDC_HEADERS */ +#ifdef HAVE_STRING_H +# include +#else +# ifdef HAVE_STRINGS_H +# include +# endif +#endif /* HAVE_STRING_H */ #ifdef HAVE_UNISTD_H #include #endif /* HAVE_UNISTD_H */ -#ifdef HAVE_STRING_H -#include -#endif /* HAVE_STRING_H */ -#ifdef HAVE_STRINGS_H -#include -#endif /* HAVE_STRINGS_H */ -#if defined(HAVE_MALLOC_H) && !defined(STDC_HEADERS) -#include -#endif /* HAVE_MALLOC_H && !STDC_HEADERS */ #include #include #include #include #include #include -#include -#include -#include -#include #include "sudo.h" #include "version.h" -#ifndef STDC_HEADERS -#ifndef __GNUC__ /* gcc has its own malloc */ -extern char *malloc __P((size_t)); -#endif /* __GNUC__ */ -extern char *getenv __P((const char *)); -extern int stat __P((const char *, struct stat *)); -#endif /* !STDC_HEADERS */ - -#if defined(POSIX_SIGNALS) && !defined(SA_RESETHAND) -#define SA_RESETHAND 0 -#endif /* POSIX_SIGNALS && !SA_RESETHAND */ - #ifndef lint -static const char rcsid[] = "$Sudo: visudo.c,v 1.126 2000/03/23 04:38:22 millert Exp $"; +static const char rcsid[] = "$Sudo: visudo.c,v 1.146 2002/01/17 15:35:54 millert Exp $"; #endif /* lint */ /* @@ -91,6 +83,8 @@ static char whatnow __P((void)); static RETSIGTYPE Exit __P((int)); static void setup_signals __P((void)); +static int run_command __P((char *, char **)); +static int check_syntax __P((int)); int command_matches __P((char *, char *, char *, char *)); int addr_matches __P((char *)); int hostname_matches __P((char *, char *, char *)); @@ -105,7 +99,12 @@ extern FILE *yyin, *yyout; extern int errorlineno; extern int pedantic; +extern int quiet; +/* For getopt(3) */ +extern char *optarg; +extern int optind; + /* * Globals */ @@ -115,7 +114,6 @@ struct sudo_user sudo_user; int parse_error = FALSE; - int main(argc, argv) int argc; @@ -123,9 +121,14 @@ { char buf[MAXPATHLEN*2]; /* buffer used for copying files */ char *Editor; /* editor to use */ + char *UserEditor; /* editor user wants to use */ + char *EditorPath; /* colon-separated list of editors */ + char *av[4]; /* argument vector for run_command */ + int checkonly; /* only check existing file? */ int sudoers_fd; /* sudoers file descriptor */ int stmp_fd; /* stmp file descriptor */ int n; /* length parameter */ + int ch; /* getopt char */ time_t now; /* time now */ struct stat stmp_sb, sudoers_sb; /* to check for changes */ @@ -140,16 +143,33 @@ /* * Arg handling. */ - while (--argc) { - if (!strcmp(argv[argc], "-V")) { - (void) printf("visudo version %s\n", version); - exit(0); - } else if (!strcmp(argv[argc], "-s")) { - pedantic++; /* strict mode */ - } else { - usage(); + checkonly = 0; + while ((ch = getopt(argc, argv, "Vcf:sq")) != -1) { + switch (ch) { + case 'V': + (void) printf("visudo version %s\n", version); + exit(0); + case 'c': + checkonly++; /* check mode */ + break; + case 'f': + sudoers = optarg; /* sudoers file path */ + easprintf(&stmp, "%s.tmp", optarg); + break; + case 's': + pedantic++; /* strict mode */ + break; + case 'q': + quiet++; /* quiet mode */ + break; + default: + usage(); } } + argc -= optind; + argv += optind; + if (argc) + usage(); /* Mock up a fake sudo_user struct. */ user_host = user_shost = user_cmnd = ""; @@ -162,6 +182,9 @@ /* Setup defaults data structures. */ init_defaults(); + if (checkonly) + exit(check_syntax(quiet)); + /* * Open sudoers, lock it and stat it. * sudoers_fd must remain open throughout in order to hold the lock. @@ -170,7 +193,7 @@ if (sudoers_fd == -1) { (void) fprintf(stderr, "%s: %s: %s\n", Argv[0], sudoers, strerror(errno)); - Exit(-1); + exit(1); } if (!lock_file(sudoers_fd, SUDO_TLOCK)) { (void) fprintf(stderr, "%s: sudoers file busy, try again later.\n", @@ -184,7 +207,7 @@ #endif (void) fprintf(stderr, "%s: can't stat %s: %s\n", Argv[0], sudoers, strerror(errno)); - Exit(-1); + exit(1); } /* @@ -224,34 +247,128 @@ (void) close(stmp_fd); /* - * If we are allowing EDITOR and VISUAL envariables set Editor - * base on whichever exists... + * Check EDITOR and VISUAL environment variables to see which editor + * the user wants to use (we may not end up using it though). + * If the path is not fully-qualified, make it so and check that + * the specified executable actually exists. */ - if (!def_flag(I_ENV_EDITOR) || - (!(Editor = getenv("EDITOR")) && !(Editor = getenv("VISUAL")))) - Editor = estrdup(def_str(I_EDITOR)); + if ((UserEditor = getenv("EDITOR")) == NULL || *UserEditor == '\0') + UserEditor = getenv("VISUAL"); + if (UserEditor && *UserEditor == '\0') + UserEditor = NULL; + else if (UserEditor) { + if (find_path(UserEditor, &Editor, getenv("PATH")) == FOUND) { + UserEditor = Editor; + } else { + if (def_flag(I_ENV_EDITOR)) { + /* If we are honoring $EDITOR this is a fatal error. */ + (void) fprintf(stderr, + "%s: specified editor (%s) doesn't exist!\n", + Argv[0], UserEditor); + Exit(-1); + } else { + /* Otherwise, just ignore $EDITOR. */ + UserEditor = NULL; + } + } + } /* - * Edit the temp file and parse it (for sanity checking) + * See if we can use the user's choice of editors either because + * we allow any $EDITOR or because $EDITOR is in the allowable list. */ - do { - /* - * Build up a buffer to execute - */ - if (strlen(Editor) + strlen(stmp) + 30 > sizeof(buf)) { - (void) fprintf(stderr, "%s: Buffer too short (line %d).\n", - Argv[0], __LINE__); + Editor = EditorPath = NULL; + if (def_flag(I_ENV_EDITOR) && UserEditor) + Editor = UserEditor; + else if (UserEditor) { + struct stat editor_sb; + struct stat user_editor_sb; + char *base, *userbase; + + if (stat(UserEditor, &user_editor_sb) != 0) { + /* Should never happen since we already checked above. */ + (void) fprintf(stderr, "%s: unable to stat editor (%s): %s\n", + Argv[0], UserEditor, strerror(errno)); Exit(-1); } - if (parse_error == TRUE) - (void) sprintf(buf, "%s +%d %s", Editor, errorlineno, stmp); + EditorPath = estrdup(def_str(I_EDITOR)); + Editor = strtok(EditorPath, ":"); + do { + /* + * Both Editor and UserEditor should be fully qualified but + * check anyway... + */ + if ((base = strrchr(Editor, '/')) == NULL) + continue; + if ((userbase = strrchr(UserEditor, '/')) == NULL) { + Editor = NULL; + break; + } + base++, userbase++; + + /* + * We compare the basenames first and then use stat to match + * for sure. + */ + if (strcmp(base, userbase) == 0) { + if (stat(Editor, &editor_sb) == 0 && S_ISREG(editor_sb.st_mode) + && (editor_sb.st_mode & 0000111) && + editor_sb.st_dev == user_editor_sb.st_dev && + editor_sb.st_ino == user_editor_sb.st_ino) + break; + } + } while ((Editor = strtok(NULL, ":"))); + } + + /* + * Can't use $EDITOR, try each element of I_EDITOR until we + * find one that exists, is regular, and is executable. + */ + if (Editor == NULL || *Editor == '\0') { + if (EditorPath != NULL) + free(EditorPath); + EditorPath = estrdup(def_str(I_EDITOR)); + Editor = strtok(EditorPath, ":"); + do { + if (sudo_goodpath(Editor)) + break; + } while ((Editor = strtok(NULL, ":"))); + + /* Bleah, none of the editors existed! */ + if (Editor == NULL || *Editor == '\0') { + (void) fprintf(stderr, "%s: no editor found (editor path = %s)\n", + Argv[0], def_str(I_EDITOR)); + Exit(-1); + } + } + + /* + * Edit the temp file and parse it (for sanity checking) + */ + do { + char linestr[64]; + + /* Build up argument vector for the command */ + if ((av[0] = strrchr(Editor, '/')) != NULL) + av[0]++; else - (void) sprintf(buf, "%s %s", Editor, stmp); + av[0] = Editor; + n = 1; + if (parse_error == TRUE) { + (void) snprintf(linestr, sizeof(linestr), "+%d", errorlineno); + av[n++] = linestr; + } + av[n++] = stmp; + av[n++] = NULL; - /* Do the edit -- some SYSV editors exit with 1 instead of 0 */ + /* + * Do the edit: + * We cannot check the editor's exit value against 0 since + * XPG4 specifies that vi's exit value is a function of the + * number of errors during editing (?!?!). + */ now = time(NULL); - n = system(buf); - if (n != -1 && ((n >> 8) == 0 || (n >> 8) == 1)) { + if (run_command(Editor, av) != -1) { /* * Sanity checks. */ @@ -298,8 +415,8 @@ } } else { (void) fprintf(stderr, - "%s: Editor (%s) failed with exit status %d, %s unchanged.\n", - Argv[0], Editor, n, sudoers); + "%s: Editor (%s) failed, %s unchanged.\n", Argv[0], + Editor, sudoers); Exit(-1); } @@ -352,31 +469,26 @@ */ if (rename(stmp, sudoers)) { if (errno == EXDEV) { - char *tmpbuf; - (void) fprintf(stderr, "%s: %s and %s not on the same filesystem, using mv to rename.\n", Argv[0], stmp, sudoers); - /* Allocate just enough space for tmpbuf */ - n = sizeof(char) * (strlen(_PATH_MV) + strlen(stmp) + - strlen(sudoers) + 4); - if ((tmpbuf = (char *) malloc(n)) == NULL) { - (void) fprintf(stderr, - "%s: Cannot alocate memory, %s unchanged: %s\n", - Argv[0], sudoers, strerror(errno)); - Exit(-1); - } + /* Build up argument vector for the command */ + if ((av[0] = strrchr(_PATH_MV, '/')) != NULL) + av[0]++; + else + av[0] = _PATH_MV; + av[1] = stmp; + av[2] = sudoers; + av[3] = NULL; - /* Build up command and execute it */ - (void) sprintf(tmpbuf, "%s %s %s", _PATH_MV, stmp, sudoers); - if (system(tmpbuf)) { + /* And run it... */ + if (run_command(_PATH_MV, av)) { (void) fprintf(stderr, - "%s: Command failed: '%s', %s unchanged.\n", - Argv[0], tmpbuf, sudoers); + "%s: Command failed: '%s %s %s', %s unchanged.\n", + Argv[0], _PATH_MV, stmp, sudoers, sudoers); Exit(-1); } - free(tmpbuf); } else { (void) fprintf(stderr, "%s: Error renaming %s, %s unchanged: %s\n", Argv[0], stmp, sudoers, strerror(errno)); @@ -435,6 +547,18 @@ return; } +int +user_is_exempt() +{ + return(TRUE); +} + +void +init_envtables() +{ + return; +} + /* * Assuming a parse error occurred, prompt the user for what they want * to do now. Returns the first letter of their choice. @@ -473,49 +597,116 @@ static void setup_signals() { -#ifdef POSIX_SIGNALS - struct sigaction action; /* POSIX signal structure */ -#endif /* POSIX_SIGNALS */ + sigaction_t sa; /* * Setup signal handlers to cleanup nicely. */ -#ifdef POSIX_SIGNALS - (void) memset((VOID *)&action, 0, sizeof(action)); - action.sa_handler = Exit; - action.sa_flags = SA_RESETHAND; - (void) sigaction(SIGTERM, &action, NULL); - (void) sigaction(SIGHUP, &action, NULL); - (void) sigaction(SIGINT, &action, NULL); - (void) sigaction(SIGQUIT, &action, NULL); + sigemptyset(&sa.sa_mask); + sa.sa_flags = SA_RESTART; + sa.sa_handler = Exit; + (void) sigaction(SIGTERM, &sa, NULL); + (void) sigaction(SIGHUP, &sa, NULL); + (void) sigaction(SIGINT, &sa, NULL); + (void) sigaction(SIGQUIT, &sa, NULL); +} + +static int +run_command(path, argv) + char *path; + char **argv; +{ + int status; + pid_t pid; + sigset_t set, oset; + + (void) sigemptyset(&set); + (void) sigaddset(&set, SIGCHLD); + (void) sigprocmask(SIG_BLOCK, &set, &oset); + + switch (pid = fork()) { + case -1: + (void) fprintf(stderr, + "%s: unable to run %s: %s\n", Argv[0], path, strerror(errno)); + Exit(-1); + break; /* NOTREACHED */ + case 0: + (void) sigprocmask(SIG_SETMASK, &oset, NULL); + execv(path, argv); + (void) fprintf(stderr, + "%s: unable to run %s: %s\n", Argv[0], path, strerror(errno)); + _exit(127); + break; /* NOTREACHED */ + } + +#ifdef sudo_waitpid + pid = sudo_waitpid(pid, &status, 0); #else - (void) signal(SIGTERM, Exit); - (void) signal(SIGHUP, Exit); - (void) signal(SIGINT, Exit); - (void) signal(SIGQUIT, Exit); -#endif /* POSIX_SIGNALS */ + pid = wait(&status); +#endif + + (void) sigprocmask(SIG_SETMASK, &oset, NULL); + + /* XXX - should use WEXITSTATUS() */ + return(pid == -1 ? -1 : (status >> 8)); } +static int +check_syntax(quiet) + int quiet; +{ + + if ((yyin = fopen(sudoers, "r")) == NULL) { + if (!quiet) + (void) fprintf(stderr, "%s: unable to open %s: %s\n", Argv[0], + sudoers, strerror(errno)); + exit(1); + } + yyout = stdout; + init_parser(); + if (yyparse() && parse_error != TRUE) { + if (!quiet) + (void) fprintf(stderr, + "%s: failed to parse %s file, unknown error.\n", + Argv[0], sudoers); + parse_error = TRUE; + } + if (!quiet){ + if (parse_error) + (void) printf("parse error in %s near line %d\n", sudoers, + errorlineno); + else + (void) printf("%s file parsed OK\n", sudoers); + } + + return(parse_error == TRUE); +} + /* * Unlink the sudoers temp file (if it exists) and exit. * Used in place of a normal exit() and as a signal handler. - * A positive parameter is considered to be a signal and is reported. + * A positive parameter indicates we were called as a signal handler. */ static RETSIGTYPE Exit(sig) int sig; { + char *emsg = " exiting due to signal.\n"; + (void) unlink(stmp); - if (sig > 0) /* XXX signal race */ - (void) fprintf(stderr, "%s exiting, caught signal %d.\n", Argv[0], sig); - - exit(-sig); /* XXX for signal case, should be _exit() */ + if (sig > 0) { + write(STDERR_FILENO, Argv[0], strlen(Argv[0])); + write(STDERR_FILENO, emsg, sizeof(emsg) - 1); + _exit(-sig); + } + exit(-sig); } static void usage() { - (void) fprintf(stderr, "usage: %s [-s] [-V]\n", Argv[0]); + (void) fprintf(stderr, "usage: %s [-c] [-f sudoers] [-q] [-s] [-V]\n", + Argv[0]); exit(1); }