From: Todd C. Miller Date: Sat, 7 Aug 1999 09:59:43 +0000 (+0000) Subject: o Move lock_file() and touch() into fileops.c so visudo can use them X-Git-Tag: SUDO_1_6_0~149 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=76148d5316bb30d704d8e2d2ddebddea2eedea1d;p=sudo o Move lock_file() and touch() into fileops.c so visudo can use them o Visudo now locks the sudoers temp file instead of bailing when the temp file already exists. This fixes the problem of stale temp files but it does *require* that you not try to put the temp file in a world-writable directory. This shoud not be an issue as the temp file should live in the same dir as sudoers. o Visudo now only installs the temp file as sudoers if it changed. --- diff --git a/CHANGES b/CHANGES index 36c90b17a..65272ee78 100644 --- a/CHANGES +++ b/CHANGES @@ -1106,3 +1106,19 @@ Sudo 1.5.9 released. 346) Sudo is now available under a BSD/Apache style license. This is possible because it no longer contains any of the original 1.1 code. + +347) Added configuration info when sudo is run with the -V flag by root. + +348) Change visudo tmp file from /etc/stmp -> /etc/sudoers.tmp since + Solaris uses stmp for shadow temp file. Also rename _PATH_SUDO_SUDOERS + to _PATH_SUDOERS and _PATH_SUDO_STMP to _PATH_SUDOERS_TMP. + +349) Added configure option to set syslog priorities. + +350) Sudo now locks its log file to prevent mangled entries. + +351) Visudo now locks the sudoers temp file instead of bailing when + the temp file already exists. This fixes the problem of stale + temp files but it does *require* that you not try to put the + temp file in a world-writable directory. This shoud not be + an issue as the temp file should live in the same dir as sudoers. diff --git a/Makefile.in b/Makefile.in index 575cc6d57..89f3efd6a 100644 --- a/Makefile.in +++ b/Makefile.in @@ -108,9 +108,9 @@ SHELL = /bin/sh PROGS = @PROGS@ -SRCS = alloc.c check.c find_path.c getspwuid.c goodpath.c interfaces.c \ - logging.c parse.c parse.lex parse.yacc sudo.c sudo_setenv.c tgetpass.c \ - version.c visudo.c $(AUTH_SRCS) +SRCS = alloc.c check.c fileops.c find_path.c getspwuid.c goodpath.c \ + interfaces.c logging.c parse.c parse.lex parse.yacc sudo.c \ + sudo_setenv.c tgetpass.c version.c visudo.c $(AUTH_SRCS) AUTH_SRCS = auth/afs.c auth/aix_auth.c auth/dce.c auth/fwtk.c auth/kerb4.c \ auth/kerb5.c auth/pam.c auth/passwd.c auth/rfc1938.c \ @@ -120,11 +120,11 @@ AUTH_OBJS = sudo_auth.o @AUTH_OBJS@ PARSEOBJS = sudo.tab.o lex.yy.o alloc.o -SUDOBJS = check.o getspwuid.o goodpath.o find_path.o interfaces.o logging.o \ - parse.o sudo.o sudo_setenv.o tgetpass.o version.o \ +SUDOBJS = check.o getspwuid.o goodpath.o fileops.o find_path.o interfaces.o \ + logging.o parse.o sudo.o sudo_setenv.o tgetpass.o version.o \ $(AUTH_OBJS) $(PARSEOBJS) -VISUDOBJS = visudo.o $(PARSEOBJS) +VISUDOBJS = visudo.o fileops.o $(PARSEOBJS) TESTOBJS = interfaces.o testsudoers.o $(PARSEOBJS) diff --git a/TODO b/TODO index 1251314e9..d19150018 100644 --- a/TODO +++ b/TODO @@ -47,5 +47,3 @@ TODO list (most will be addressed in the next rewrite) 19) Sudo should have a separate error message for when the user is in sudoers but not allowed to run stuff on that host, and send mail. - -20) Add configure check for locking functions and lock logfile. diff --git a/TROUBLESHOOTING b/TROUBLESHOOTING index 1f68b4569..8ca4163e3 100644 --- a/TROUBLESHOOTING +++ b/TROUBLESHOOTING @@ -120,10 +120,7 @@ A) You probably didn't install the gcc-fixed include files. Q) When I run "visudo" it says "sudoers file busy, try again later." and doesn't do anything. -A) You have a stale sudoers temporary file. The default location is - /etc/sudoers.tmp. If you delete this file visudo will be happy again, - but make sure to check that no one else is running visudo at - the time. +A) Someone else is currently editing the sudoers file with visudo. Q) When I try to use "cd" with sudo it says "cd: command not found". A) "cd" is a shell builtin, you can't run it as a command since diff --git a/check.c b/check.c index 55bdfc8b3..64feff4f0 100644 --- a/check.c +++ b/check.c @@ -57,13 +57,6 @@ #include #include #include -#ifdef HAVE_UTIME -# ifdef HAVE_UTIME_H -# include -# endif /* HAVE_UTIME_H */ -#else -# include "emul/utime.h" -#endif /* HAVE_UTIME */ #include "sudo.h" @@ -81,7 +74,6 @@ static const char rcsid[] = "$Sudo$"; int user_is_exempt __P((void)); static void build_timestamp __P((char **, char **)); static int timestamp_status __P((char *, char *, char *, int)); -static int touch __P((char *, time_t)); #ifndef NO_PASSWD static char *expand_prompt __P((char *, char *, char *)); static void lecture __P((void)); @@ -265,29 +257,6 @@ user_is_exempt() return(FALSE); } -/* - * Update the access and modify times on a file. - */ -static int -touch(path, when) - char *path; - time_t when; -{ -#ifdef HAVE_UTIME_POSIX - struct utimbuf ut, *utp; - - ut.actime = ut.modtime = when; - utp = &ut; -#else - /* BSD <= 4.3 has no struct utimbuf */ - time_t utp[2]; - - utp[0] = utp[1] = when; -#endif /* HAVE_UTIME_POSIX */ - - return(utime(path, utp)); -} - /* * Fills in timestampdir as well as timestampfile if using tty tickets. */ diff --git a/compat.h b/compat.h index fa5329dcc..9dc54cf3a 100644 --- a/compat.h +++ b/compat.h @@ -43,103 +43,116 @@ /* Deal with ANSI stuff reasonably. */ #ifndef __P -# if defined (__cplusplus) || defined (__STDC__) -# define __P(args) args -# else -# define __P(args) () -# endif +# if defined (__cplusplus) || defined (__STDC__) +# define __P(args) args +# else +# define __P(args) () +# endif #endif /* __P */ /* * Some systems (ie ISC V/386) do not define MAXPATHLEN even in param.h */ #ifndef MAXPATHLEN -# define MAXPATHLEN 1024 +# define MAXPATHLEN 1024 #endif /* * Some systems do not define MAXHOSTNAMELEN. */ #ifndef MAXHOSTNAMELEN -# define MAXHOSTNAMELEN 64 +# define MAXHOSTNAMELEN 64 #endif /* * 4.2BSD lacks FD_* macros (we only use FD_SET and FD_ZERO) */ #ifndef FD_SETSIZE -#define FD_SET(fd, fds) ((fds) -> fds_bits[0] |= (1 << (fd))) -#define FD_ZERO(fds) ((fds) -> fds_bits[0] = 0) +# define FD_SET(fd, fds) ((fds) -> fds_bits[0] |= (1 << (fd))) +# define FD_ZERO(fds) ((fds) -> fds_bits[0] = 0) #endif /* !FD_SETSIZE */ /* * Posix versions for those without... */ #ifndef _S_IFMT -# define _S_IFMT S_IFMT +# define _S_IFMT S_IFMT #endif /* _S_IFMT */ #ifndef _S_IFREG -# define _S_IFREG S_IFREG +# define _S_IFREG S_IFREG #endif /* _S_IFREG */ #ifndef _S_IFDIR -# define _S_IFDIR S_IFDIR +# define _S_IFDIR S_IFDIR #endif /* _S_IFDIR */ #ifndef _S_IFLNK -#define _S_IFLNK S_IFLNK +# define _S_IFLNK S_IFLNK #endif /* _S_IFLNK */ #ifndef S_ISREG -# define S_ISREG(m) (((m) & _S_IFMT) == _S_IFREG) +# define S_ISREG(m) (((m) & _S_IFMT) == _S_IFREG) #endif /* S_ISREG */ #ifndef S_ISDIR -# define S_ISDIR(m) (((m) & _S_IFMT) == _S_IFDIR) +# define S_ISDIR(m) (((m) & _S_IFMT) == _S_IFDIR) #endif /* S_ISDIR */ /* * Some OS's may not have this. */ #ifndef S_IRWXU -# define S_IRWXU 0000700 /* rwx for owner */ +# define S_IRWXU 0000700 /* rwx for owner */ #endif /* S_IRWXU */ /* * In case this is not defined in or */ #ifndef howmany -#define howmany(x, y) (((x) + ((y) - 1)) / (y)) +# define howmany(x, y) (((x) + ((y) - 1)) / (y)) #endif /* * These should be defined in but not everyone has them. */ #ifndef STDIN_FILENO -# define STDIN_FILENO 0 +# define STDIN_FILENO 0 #endif #ifndef STDOUT_FILENO -# define STDOUT_FILENO 1 +# define STDOUT_FILENO 1 #endif #ifndef STDERR_FILENO -# define STDERR_FILENO 2 +# define STDERR_FILENO 2 +#endif + +/* + * These should be defined in but not everyone has them. + */ +#ifndef SEEK_SET +# define SEEK_SET 0 +#endif +#ifndef SEEK_CUR +# define SEEK_CUR 1 +#endif +#ifndef SEEK_END +# define SEEK_END 2 #endif /* * BSD defines these in but others may not. */ #ifndef MIN -#define MIN(a,b) (((a)<(b))?(a):(b)) +# define MIN(a,b) (((a)<(b))?(a):(b)) #endif #ifndef MAX -#define MAX(a,b) (((a)>(b))?(a):(b)) +# define MAX(a,b) (((a)>(b))?(a):(b)) #endif /* * Emulate seteuid() for HP-UX via setresuid(2) and seteuid(2) for others. */ #ifndef HAVE_SETEUID -# ifdef __hpux -# define seteuid(_EUID) (setresuid((uid_t) -1, _EUID, (uid_t) -1)) -# else -# define seteuid(_EUID) (setreuid((uid_t) -1, _EUID)) -# endif /* __hpux */ +# ifdef __hpux +# define seteuid(_EUID) (setresuid((uid_t) -1, _EUID, (uid_t) -1)) +# else +# define seteuid(_EUID) (setreuid((uid_t) -1, _EUID)) +# endif /* __hpux */ #endif /* HAVE_SETEUID */ #endif /* _SUDO_COMPAT_H */ diff --git a/config.h.in b/config.h.in index b351b1a19..f72cc5438 100644 --- a/config.h.in +++ b/config.h.in @@ -292,6 +292,9 @@ /* Define if you have flock(2). */ #undef HAVE_FLOCK +/* Define if you have ftruncate(2). */ +#undef HAVE_FTRUNCATE + /* Define if you have snprintf(3). */ #undef HAVE_SNPRINTF diff --git a/configure b/configure index d0e48efda..5305434ba 100755 --- a/configure +++ b/configure @@ -5254,7 +5254,7 @@ EOF ;; esac -for ac_func in strchr strrchr memcpy memset sysconf sigaction tzset strcasecmp seteuid +for ac_func in strchr strrchr memcpy memset sysconf sigaction tzset strcasecmp seteuid ftruncate do echo $ac_n "checking for $ac_func""... $ac_c" 1>&6 echo "configure:5261: checking for $ac_func" >&5 diff --git a/configure.in b/configure.in index 398847794..a2651e0f1 100644 --- a/configure.in +++ b/configure.in @@ -1349,7 +1349,7 @@ esac dnl dnl Function checks dnl -AC_CHECK_FUNCS(strchr strrchr memcpy memset sysconf sigaction tzset strcasecmp seteuid) +AC_CHECK_FUNCS(strchr strrchr memcpy memset sysconf sigaction tzset strcasecmp seteuid ftruncate) if test -n "$SECUREWARE"; then AC_CHECK_FUNCS(bigcrypt) AC_CHECK_FUNCS(set_auth_parameters) diff --git a/fileops.c b/fileops.c new file mode 100644 index 000000000..3801cc235 --- /dev/null +++ b/fileops.c @@ -0,0 +1,149 @@ +/* + * Copyright (c) 1999 Todd C. Miller + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * 4. Products derived from this software may not be called "Sudo" nor + * may "Sudo" appear in their names without specific prior written + * permission from the author. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#include +#ifdef HAVE_UNISTD_H +#include +#endif /* HAVE_UNISTD_H */ +#include +#include +#include +#include +#ifdef HAVE_UTIME +# ifdef HAVE_UTIME_H +# include +# endif /* HAVE_UTIME_H */ +#else +# include "emul/utime.h" +#endif /* HAVE_UTIME */ + +#include "sudo.h" + +#ifndef lint +static const char rcsid[] = "$Sudo$"; +#endif /* lint */ + +/* + * Update the access and modify times on a file. + */ +int +touch(path, when) + char *path; + time_t when; +{ +#ifdef HAVE_UTIME_POSIX + struct utimbuf ut, *utp; + + ut.actime = ut.modtime = when; + utp = &ut; +#else + /* BSD <= 4.3 has no struct utimbuf */ + time_t utp[2]; + + utp[0] = utp[1] = when; +#endif /* HAVE_UTIME_POSIX */ + + return(utime(path, utp)); +} + +/* + * Lock/unlock a file. + */ +#ifdef HAVE_LOCKF +int +lock_file(fd, lockit) + int fd; + int lockit; +{ + int op = 0; + + switch (lockit) { + case SUDO_LOCK: + op = F_LOCK; + break; + case SUDO_TLOCK: + op = F_TLOCK; + break; + case SUDO_UNLOCK: + op = F_ULOCK; + break; + } + return(lockf(fd, op, 0) == 0); +} +#elif HAVE_FLOCK +int +lock_file(fd, lockit) + int fd; + int lockit; +{ + int op = 0; + + switch (lockit) { + case SUDO_LOCK: + op = LOCK_EX; + break; + case SUDO_TLOCK: + op = LOCK_EX | LOCK_NB; + break; + case SUDO_UNLOCK: + op = LOCK_EX; + break; + } + return(flock(fd, op) == 0); +} +#else +int +lock_file(fd, lockit) + int fd; + int lockit; +{ +#ifdef F_SETLK + int func; + struct flock lock; + + lock.l_start = 0; + lock.l_len = 0; + lock.l_pid = getpid(); + lock.l_type = (lockit == SUDO_UNLOCK) ? F_UNLCK : F_WRLCK; + lock.l_whence = SEEK_SET; + func = (lockit == SUDO_TLOCK) ? F_SETLK : F_SETLKW; + + return(fcntl(fd, func, &lock) == 0); +#else + return(TRUE); +#endif +} +#endif diff --git a/logging.c b/logging.c index 2841fffa5..ef1a4715d 100644 --- a/logging.c +++ b/logging.c @@ -47,7 +47,6 @@ #ifdef HAVE_STRINGS_H #include #endif /* HAVE_STRINGS_H */ -#include #include #include #include @@ -68,7 +67,6 @@ static void do_syslog __P((int, char *)); #endif #if (LOGGING & SLOG_FILE) static void do_logfile __P((char *)); -static int lock_file __P((FILE *, int)); #endif static void send_mail __P((char *)); @@ -179,7 +177,7 @@ do_logfile(msg) _PATH_SUDO_LOGFILE, strerror(errno)); send_mail(full_line); free(full_line); - } else if (!lock_file(fp, TRUE)) { + } else if (!lock_file(fileno(fp), SUDO_LOCK)) { easprintf(&full_line, "Can't lock log file: %s: %s", _PATH_SUDO_LOGFILE, strerror(errno)); send_mail(full_line); @@ -252,7 +250,7 @@ do_logfile(msg) free(full_line); # endif (void) fflush(fp); - (void) lock_file(fp, FALSE); + (void) lock_file(fileno(fp), SUDO_UNLOCK); (void) fclose(fp); } @@ -572,50 +570,3 @@ reapchild(sig) #endif /* POSIX_SIGNALS */ errno = serrno; } - -/* - * Lock/unlock a file. - */ -#ifdef HAVE_LOCKF -static int -lock_file(fp, lockit) - FILE *fp; - int lockit; -{ - int op; - - op = lockit ? F_LOCK : F_ULOCK; - return(lockf(fileno(fp), op, 0) == 0); -} -#elif HAVE_FLOCK -static int -lock_file(fp, lockit) - FILE *fp; - int lockit; -{ - int op; - - op = lockit ? LOCK_EX : LOCK_UN; - return(flock(fileno(fp), op) == 0); -} -#else -static int -lock_file(fp, lockit) - FILE *fp; - int lockit; -{ -#ifdef F_SETLK - struct flock lock; - - lock.l_start = 0; - lock.l_len = 0; - lock.l_pid = getpid(); - lock.l_type = lockit ? F_WRLCK : F_UNLCK; - lock.l_whence = SEEK_SET; - - return(fcntl(fileno(fp), F_SETLKW, &lock) == 0); -#else - return(TRUE); -#endif -} -#endif diff --git a/sudo.h b/sudo.h index 24a5da948..f1af78faf 100644 --- a/sudo.h +++ b/sudo.h @@ -133,6 +133,13 @@ struct sudo_user { */ #define SUDO_PASS_MAX 256 +/* + * Flags for lock_file() + */ +#define SUDO_LOCK 1 /* lock a file */ +#define SUDO_TLOCK 2 /* test & lock a file (non-blocking) */ +#define SUDO_UNLOCK 4 /* unlock a file */ + /* * Function prototypes */ @@ -164,6 +171,8 @@ char *estrdup __P((const char *)); void easprintf __P((char **, const char *, ...)); void evasprintf __P((char **, const char *, va_list)); void print_version __P((void)); +int lock_file __P((int, int)); +int touch __P((char *, time_t)); YY_DECL; /* Only provide extern declarations outside of sudo.c. */ diff --git a/version.c b/version.c index 02c87e784..aa81b9284 100644 --- a/version.c +++ b/version.c @@ -76,12 +76,19 @@ print_version() /* * Print compile-time options if root. + * At some point these will all be in a structure of some kind + * to allow overriding the defaults from sudoers. + * XXX - reorganize for readability. */ if (getuid() == 0) { + (void) printf("\nSudoers file: %s, mode 0%o, uid %d, gid %d\n", + _PATH_SUDOERS, SUDOERS_MODE, SUDOERS_UID, SUDOERS_GID); + (void) printf("Sudoers temp file: %s\n", _PATH_SUDOERS_TMP); + #ifdef WITHOUT_PASSWD - (void) puts("\nNo Authentication configured\n"); + (void) puts("No Authentication configured\n"); #else - (void) fputs("\nAuthentication methods:", stdout); + (void) fputs("Authentication methods:", stdout); for (auth = auth_switch; auth->name; auth++) { (void) putchar(' '); (void) fputs(auth->name, stdout); @@ -137,6 +144,8 @@ print_version() (void) fputs(" goons", stdout); # endif (void) putchar('\n'); +# else + (void) printf("Incorrect password message: %s\n", INCORRECT_PASSWORD); #endif #ifdef SUDO_UMASK @@ -169,14 +178,39 @@ print_version() (void) printf("Editor for visudo: %s\n", EDITOR); #endif +#if defined(IGNORE_DOT_PATH) || defined(DONT_LEAK_PATH_INFO) || defined(SECURE_PATH) + puts("$PATH options:"); #ifdef SECURE_PATH - (void) printf("Secure PATH: %s\n", SECURE_PATH); + (void) printf(" PATH override: %s\n", SECURE_PATH); +#endif +# ifdef IGNORE_DOT_PATH + (void) puts(" ignore '.'"); +# endif +# ifdef DONT_LEAK_PATH_INFO + (void) puts(" no information leakage"); +# endif #endif #ifdef _PATH_SENDMAIL (void) printf("Mailer path: %s\n", _PATH_SENDMAIL); (void) printf("Send mail to: %s\n", ALERTMAIL); (void) printf("Mail subject: %s\n", MAILSUBJECT); + (void) fputs("Send mail on: 'error'", stdout); +# ifdef SEND_MAIL_WHEN_NO_USER + (void) fputs(" 'unlisted user'", stdout); +# endif +# ifdef SEND_MAIL_WHEN_NOT_OK + (void) fputs(" 'authentication failure'", stdout); +# endif + (void) putchar('\n'); +#endif + +#ifdef SHELL_IF_NO_ARGS + puts("When invoked with no arguments sudo will start a shell") +#endif + +#ifdef SHELL_SETS_HOME + puts("Sudo will set $HOME to the homedir of the target user"); #endif (void) printf("Default password prompt: %s\n", PASSPROMPT); @@ -187,14 +221,6 @@ print_version() #else (void) puts("no"); #endif - -/* stopped at INCORRECT_PASSWORD */ - - /* XXX - more */ - -/* --D_PATH_SUDO_SUDOERS=\"/etc/sudoers\" -D_PATH_SUDO_STMP=\"/etc/stmp\" -DSUDOERS_UID=0 -DSUDOERS_GID=0 -DSUDOERS_MODE=0440 -*/ } } diff --git a/visudo.c b/visudo.c index 491e4e85a..e9dcf91a2 100644 --- a/visudo.c +++ b/visudo.c @@ -56,6 +56,7 @@ #endif /* HAVE_MALLOC_H && !STDC_HEADERS */ #include #include +#include #include #include #include @@ -124,8 +125,9 @@ main(argc, argv) int sudoers_fd; /* sudoers file descriptor */ int stmp_fd; /* stmp file descriptor */ int n; /* length parameter */ + time_t now; /* time now */ + struct stat stmp_sb, sudoers_sb; /* to check for changes */ - (void) setbuf(stderr, (char *)NULL); /* unbuffered stderr */ /* * Parse command line options @@ -166,30 +168,44 @@ main(argc, argv) #endif /* ENV_EDITOR */ /* - * Copy sudoers file to stmp + * Open sudoers temp file and grab a lock. */ - stmp_fd = open(stmp, O_WRONLY | O_CREAT | O_EXCL, 0600); + stmp_fd = open(stmp, O_WRONLY | O_CREAT, 0600); if (stmp_fd < 0) { - if (errno == EEXIST) { - (void) fprintf(stderr, "%s: sudoers file busy, try again later.\n", - Argv[0]); - exit(1); - } (void) fprintf(stderr, "%s: %s\n", Argv[0], strerror(errno)); + exit(1); + } + if (!lock_file(stmp_fd, SUDO_TLOCK)) { + (void) fprintf(stderr, "%s: sudoers file busy, try again later.\n", + Argv[0]); + exit(1); + } +#ifdef HAVE_FTRUNCATE + if (ftruncate(stmp_fd, 0) == -1) { +#else + if (truncate(stmp, 0) == -1) { +#endif + (void) fprintf(stderr, "%s: can't truncate %s: %s\n", Argv[0], + stmp, strerror(errno)); Exit(-1); } /* Install signal handlers to clean up stmp if we are killed. */ setup_signals(); + (void) memset(&sudoers_sb, 0, sizeof(sudoers_sb)); + if (stat(sudoers, &sudoers_sb) == -1 && errno != ENOENT) { + (void) fprintf(stderr, "%s: %s\n", Argv[0], strerror(errno)); + Exit(-1); + } sudoers_fd = open(sudoers, O_RDONLY); - if (sudoers_fd < 0 && errno != ENOENT) { + if (sudoers_fd == -1 && errno != ENOENT) { (void) fprintf(stderr, "%s: %s\n", Argv[0], strerror(errno)); Exit(-1); } - /* Copy sudoers -> stmp */ - if (sudoers_fd >= 0) { + /* Copy sudoers -> stmp and reset the mtime */ + if (sudoers_fd != -1) { while ((n = read(sudoers_fd, buf, sizeof(buf))) > 0) if (write(stmp_fd, buf, n) != n) { (void) fprintf(stderr, "%s: Write failed: %s\n", Argv[0], @@ -200,6 +216,7 @@ main(argc, argv) (void) close(sudoers_fd); } (void) close(stmp_fd); + (void) touch(stmp, sudoers_sb.st_mtime); /* * Edit the temp file and parse it (for sanity checking) @@ -219,20 +236,19 @@ main(argc, argv) (void) sprintf(buf, "%s %s", Editor, stmp); /* Do the edit -- some SYSV editors exit with 1 instead of 0 */ + now = time(NULL); n = system(buf); if (n != -1 && ((n >> 8) == 0 || (n >> 8) == 1)) { - struct stat statbuf; /* for sanity checking */ - /* * Sanity checks. */ - if (stat(stmp, &statbuf) < 0) { + if (stat(stmp, &stmp_sb) < 0) { (void) fprintf(stderr, "%s: Can't stat temporary file (%s), %s unchanged.\n", Argv[0], stmp, sudoers); Exit(-1); } - if (statbuf.st_size == 0) { + if (stmp_sb.st_size == 0) { (void) fprintf(stderr, "%s: Zero length temporary file (%s), %s unchanged.\n", Argv[0], stmp, sudoers); @@ -285,6 +301,15 @@ main(argc, argv) } } while (parse_error == TRUE); + /* + * If the user didn't change the temp file, just unlink it. + */ + if (sudoers_sb.st_mtime != now && sudoers_sb.st_mtime == stmp_sb.st_mtime && + sudoers_sb.st_size == stmp_sb.st_size) { + (void) fprintf(stderr, "%s: sudoers file unchanged.\n", Argv[0]); + Exit(0); + } + /* * Change mode and ownership of temp file so when * we move it to sudoers things are kosher. @@ -469,5 +494,5 @@ static void usage() { (void) fprintf(stderr, "usage: %s [-V]\n", Argv[0]); - Exit(-1); + exit(1); }