From c56ec5dbcf34b0ec16c45d75ca959e8c7b54ba26 Mon Sep 17 00:00:00 2001 From: "Todd C. Miller" Date: Mon, 27 Mar 1995 18:26:36 +0000 Subject: [PATCH] rewrote from scratch based on 4.3BSD vipw.c --- visudo.c | 374 ++++++++++++++++++++++++++++++------------------------- 1 file changed, 202 insertions(+), 172 deletions(-) diff --git a/visudo.c b/visudo.c index 63ea45a9a..a7daff9b8 100644 --- a/visudo.c +++ b/visudo.c @@ -1,15 +1,5 @@ /* - * CU sudo version 1.3.1 (based on Root Group sudo version 1.1) - * - * This software comes with no waranty whatsoever, use at your own risk. - * - * Please send bugs, changes, problems to sudo-bugs@cs.colorado.edu - * - */ - -/* - * sudo version 1.1 allows users to execute commands as root - * Copyright (C) 1991 The Root Group, Inc. + * CU sudo version 1.3.1 * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -25,12 +15,14 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * - ************************************************************************** - * visudo.c, sudo project - * David R. Hieb - * March 18, 1991 + * Please send bugs, changes, problems to sudo-bugs@cs.colorado.edu + * + ******************************************************************* + * + * visudo.c -- locks the sudoers file for safe editing and check + * for parse errors. * - * edit, lock and parse the sudoers file in a fashion similiar to /etc/vipw. + * Todd C. Miller (millert@colorado.edu) Sat Mar 25 21:50:36 MST 1995 */ #ifndef lint @@ -46,10 +38,17 @@ static char rcsid[] = "$Id$"; #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 */ +#ifdef HAVE_MALLOC_H +#include +#endif /* HAVE_MALLOC_H */ #include #include -#include -#include #include #include #include @@ -61,85 +60,67 @@ static char rcsid[] = "$Id$"; #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 */ + +/* + * Function prototypes + */ +static void usage __P((void)); +static RETSIGTYPE Exit __P((int)); +int path_matches __P((char *, char *)); +int ntwk_matches __P((char *)); + + +/* + * External globals + */ extern FILE *yyin, *yyout; -extern int errorlineno; +extern int errorlineno, sudolineno; -#ifndef SA_RESETHAND -#define SA_RESETHAND 0 -#endif /* SA_RESETHAND */ /* * Globals */ char **Argv; -char buffer[BUFSIZ]; char *sudoers = _PATH_SUDO_SUDOERS; -char *sudoers_tmp_file = _PATH_SUDO_STMP; +char *stmp = _PATH_SUDO_STMP; int parse_error = FALSE; -char host[] = ""; -char *user = ""; -char *cmnd = ""; - /* - * local functions not visible outside visudo.c + * For the parsing routines */ -static void usage __P((void)); -static RETSIGTYPE Exit __P((int)); - - -/* dummy *_matches routines */ -int -path_matches(cmnd, path) -char *cmnd, *path; -{ - return(TRUE); -} - -int -ntwk_matches(n) -char *n; -{ - return(TRUE); -} +char host[] = ""; +char *user = ""; +char *cmnd = ""; -main(argc, argv) +int main(argc, argv) int argc; char **argv; { - int sudoers_fd; - int sudoers_tmp_fd; - FILE *sudoers_tmp_fp; - int num_chars; - struct stat sbuf; - struct passwd *sudoers_pw; - char * Editor = EDITOR; + char buf[BUFSIZ]; /* buffer used for copying files */ + char * Editor = EDITOR; /* Editor to use (default is EDITOR */ + int sudoers_fd; /* sudoers file descriptor */ + int stmp_fd; /* stmp file descriptor */ + int n; /* length parameter */ + struct passwd *pwd; /* to look up info for SUDOERS_OWNER */ #ifdef POSIX_SIGNALS - struct sigaction action; + struct sigaction action; /* posix signal structure */ #endif /* POSIX_SIGNALS */ - Argv = argv; - - if (argc > 1) { - /* - * print version string and exit if we got -V - */ - if (!strcmp(Argv[1], "-V")) { - (void) printf("visudo version %s\n", version); - exit(0); - } else { - usage(); - } - - } /* - * handle the signals + * Setup signal handlers */ #ifdef POSIX_SIGNALS (void) bzero((char *)(&action), sizeof(action)); @@ -168,194 +149,243 @@ main(argc, argv) (void) signal(SIGQUIT, SIG_IGN); #endif /* POSIX_SIGNALS */ + (void) setbuf(stderr, (char *)NULL); /* unbuffered stderr */ + /* - * need to lookup passwd entry for sudoers file owner + * Parse command line options */ - if (!(sudoers_pw = getpwnam(SUDOERS_OWNER))) { - (void) fprintf(stderr, - "%s: no passwd entry for sudoers file owner (%s)\n", - Argv[0], SUDOERS_OWNER); - exit(1); - } - - setbuf(stderr, NULL); + Argv = argv; /* - * we only want SUDOERS_OWNER to be able to read/write the sudoers_tmp_file + * If passesd -V then print version, else print usage + * if any other option... */ - umask(077); + if (argc == 2) + if (!strcmp(Argv[1], "-V")) { + (void) printf("visudo version %s\n", version); + Exit(0); + } else { + usage(); + } + else if (argc != 1) + usage(); #ifdef ENV_EDITOR /* - * set up the Editor variable correctly + * If we are allowing EDITOR and VISUAL envariables set Editor + * base on whichever exists... */ - if ( (Editor = getenv("EDITOR")) == NULL) - if ( (Editor = getenv("VISUAL")) == NULL ) + if (!(Editor = getenv("EDITOR"))) + if (!(Editor = getenv("VISUAL"))) Editor = EDITOR; #endif /* ENV_EDITOR */ /* - * open the sudoers file read only + * Need to find who should own the sudoers file */ - if ((sudoers_fd = open(sudoers, O_RDONLY)) < 0) { - (void) fprintf(stderr, "%s: ", Argv[0]); - perror(sudoers); + if (!(pwd = getpwnam(SUDOERS_OWNER))) { + (void) fprintf(stderr, + "%s: no passwd entry for sudoers file owner (%s)\n", + Argv[0], SUDOERS_OWNER); Exit(1); } /* - * open the temporary sudoers file with the correct flags + * Copy sudoers file to stmp */ - if ((sudoers_tmp_fd = open(sudoers_tmp_file, O_WRONLY | O_CREAT | O_EXCL, - 0600)) < 0) { + stmp_fd = open(stmp, O_WRONLY | O_CREAT | O_EXCL, 0600); + if (stmp_fd < 0) { if (errno == EEXIST) { (void) fprintf(stderr, "%s: sudoers file busy\n", Argv[0]); - exit(1); + Exit(1); } (void) fprintf(stderr, "%s: ", Argv[0]); - perror(sudoers_tmp_file); - exit(1); + perror(stmp); + Exit(1); + } + + sudoers_fd = open(sudoers, O_RDONLY); + if (sudoers_fd < 0) { + (void) fprintf(stderr, "%s: ", Argv[0]); + perror(sudoers); + Exit(1); } /* - * transfer the contents of the sudoers file to the temporary sudoers file + * Copy the data */ - while ((num_chars = read(sudoers_fd, buffer, sizeof(buffer))) > 0) - (void) write(sudoers_tmp_fd, buffer, num_chars); + while ((n = read(sudoers_fd, buf, sizeof(buf))) > 0) + if (write(stmp_fd, buf, n) != n) { + (void) fprintf(stderr, "%s: Write failed: ", Argv[0]); + perror(""); + Exit(1); + } (void) close(sudoers_fd); - (void) close(sudoers_tmp_fd); + (void) close(stmp_fd); /* - * make sudoers_tmp_file owned by SUDOERS_OWNER so sudo(8) can read it. + * Change ownership of temp file to SUDOERS_OWNER + * so when we move it to sudoers things are kosher. */ - (void) chown(sudoers_tmp_file, sudoers_pw -> pw_uid, -1); + (void) chown(stmp, pwd -> pw_uid, -1); + /* + * Edit the temp file and parse it (for sanity checking) + */ do { /* - * build strings in buffer to be executed by system() + * Build up a buffer to execute */ - if (parse_error) - (void) sprintf(buffer, "%s +%d %s", Editor, errorlineno, - sudoers_tmp_file); + if (parse_error == TRUE) + (void) sprintf(buf, "%s +%d %s", Editor, errorlineno, stmp); else - (void) sprintf(buffer, "%s %s", Editor, sudoers_tmp_file); + (void) sprintf(buf, "%s %s", Editor, stmp); - /* edit the file */ - if (system(buffer) == 0) { + /* do the edit */ + if (system(buf) == 0) { + struct stat statbuf; /* for sanity checking */ - /* can't stat file */ - if (stat(sudoers_tmp_file, &sbuf) < 0) { + /* make sure stmp exists */ + if (stat(stmp, &statbuf) < 0) { (void) fprintf(stderr, - "%s: can't stat temporary file, %s unchanged\n", sudoers, - Argv[0]); + "%s: Can't stat temporary file (%s), %s unchanged.\n", + Argv[0], stmp, sudoers); Exit(1); } - /* file has size == 0 */ - if (sbuf.st_size == 0) { - (void) fprintf(stderr, "%s: bad temporary file, %s unchanged\n", - sudoers, Argv[0]); + /* check for zero length file */ + if (statbuf.st_size == 0) { + (void) fprintf(stderr, + "%s: Zero length temporary file (%s), %s unchanged.\n", + Argv[0], stmp, sudoers); Exit(1); } - /* re-open the sudoers file for parsing */ - if ((sudoers_tmp_fp = fopen(sudoers_tmp_file, "r")) == NULL) { + /* + * passed sanity checks so reopen stmp file and check + * for parse errors. + */ + yyout = stdout; + yyin = fopen(stmp, "r"); + if (yyin == NULL) { (void) fprintf(stderr, - "%s: can't re-open temporary file, %s unchanged\n", - sudoers, Argv[0]); + "%s: Can't re-open temporary file (%s), %s unchanged.\n", + Argv[0], stmp, sudoers); Exit(1); } - yyin = sudoers_tmp_fp; - yyout = stdout; - /* clean slate for each parse */ parse_error = FALSE; + errorlineno = -1; + sudolineno = 1; - /* parse the file */ + /* parse the sudoers file */ if (yyparse()) { - (void) fprintf(stderr, "yyparse() failed\n"); + (void) fprintf(stderr, + "%s: Failed to parse temporary file (%s), %s unchanged.\n", + Argv[0], stmp, sudoers); Exit(1); } - (void) fclose(sudoers_tmp_fp); + (void) fclose(yyin); + } else { + (void) fprintf(stderr, "%s: Editor (%s) failed, %s unchanged.\n", + Argv[0], Editor, sudoers); + Exit(1); } - } while (parse_error); + + /* XXX - do whatnow() here */ + } while (parse_error == TRUE); /* - * Once the temporary sudoers file is gramatically correct, we can - * rename it to the real sudoers file. If the rename(2) fails - * we try using mv(1) in case the temp and sudoers files are on - * different filesystems. + * Now that we have a sane stmp file (parse ok) it needs to be + * rename(2)'d to sudoers. If the rename(2) fails we try using + * mv(1) in case stmp and sudoers are on different filesystems. */ - if (rename(sudoers_tmp_file, sudoers) != 0) { - int status, len; - char *tmpbuf; - - /* Print a warning/error */ - (void) fprintf(stderr, "%s: ", Argv[0]); - perror("rename"); + 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: ", + Argv[0], sudoers); + perror(""); + Exit(1); + } - /* Allocate just enough space for tmpbuf */ - len = sizeof(char) * (strlen(_PATH_MV) + strlen(sudoers_tmp_file) + - strlen(sudoers) + 4); - if ((tmpbuf = (char *) malloc(len)) == NULL) { - (void) fprintf(stderr, "%s: cannot allocate memory: ", Argv[0]); + /* Build up command and execute it */ + (void) sprintf(tmpbuf, "%s %s %s", _PATH_MV, stmp, sudoers); + if (system(tmpbuf)) { + (void) fprintf(stderr, + "%s: Command failed: '%s', %s unchanged.\n", + Argv[0], tmpbuf, sudoers); + Exit(1); + } + (void) free(tmpbuf); + } else { + (void) fprintf(stderr, "%s: Error renaming %s, %s unchanged: ", + Argv[0], stmp, sudoers); perror(""); Exit(1); } - (void) sprintf(tmpbuf, "%s %s %s", _PATH_MV, sudoers_tmp_file, sudoers); - status = system(tmpbuf); - status = status >> 8; - if (status) { - (void) fprintf(stderr, "Command failed: '%s', %s unchanged.\n", - tmpbuf, sudoers); - Exit(1); - } else { - (void) fprintf(stderr, "Used '%s' instead.\n", tmpbuf); - } - (void) free(tmpbuf); - } - /* - * The chmod is a non-fatal error. + * Make the new sudoers file readable only by owner. + * If this fail it is ok since the file is only least rw owner. */ - if (chmod(sudoers, 0400) != 0) { + if (chmod(sudoers, 0400)) { (void) fprintf(stderr, "%s: Warning, unable to chmod 0400 %s: ", Argv[0], sudoers); perror(""); } - exit(0); + return(0); } -/********************************************************************** - * - * usage() - * - * this function just gives you instructions and exits +/* + * dummy *_matches routines */ +int path_matches(cmnd, path) + char *cmnd, *path; +{ + return(TRUE); +} + + +int ntwk_matches(n) + char *n; +{ + return(TRUE); +} + +/* + * usage() -- prints a help message and exits. + */ static void usage() { (void) fprintf(stderr, "usage: %s [-V]\n", Argv[0]); - exit(1); + Exit(1); } -/********************************************************************** - * - * Exit() - * - * this function cleans up and exits +/* + * Exit() -- unlinks the sudoers temp file (if there) and exits. + * Used in place of a normal exit() and as a signal handler. */ - static RETSIGTYPE Exit(sig) int sig; { - (void) unlink(sudoers_tmp_file); + (void) unlink(stmp); exit(sig); } -- 2.40.0