]> granicus.if.org Git - sudo/commitdiff
o Move lock_file() and touch() into fileops.c so visudo can use them
authorTodd C. Miller <Todd.Miller@courtesan.com>
Sat, 7 Aug 1999 09:59:43 +0000 (09:59 +0000)
committerTodd C. Miller <Todd.Miller@courtesan.com>
Sat, 7 Aug 1999 09:59:43 +0000 (09:59 +0000)
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.

14 files changed:
CHANGES
Makefile.in
TODO
TROUBLESHOOTING
check.c
compat.h
config.h.in
configure
configure.in
fileops.c [new file with mode: 0644]
logging.c
sudo.h
version.c
visudo.c

diff --git a/CHANGES b/CHANGES
index 36c90b17a57d07df218baca35fcb9b5ffced0b68..65272ee78bb1d52ac764062326053742fa17c553 100644 (file)
--- 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.
index 575cc6d57a165626035d98c618ae7a406b692efc..89f3efd6ae80be301fc5b77077d6abc371e9173e 100644 (file)
@@ -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 1251314e9f15936809a52040bd8c0630c050dc39..d191500184372638e4a0444be53d534bca4ea736 100644 (file)
--- 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.
index 1f68b4569f8db07244323761fae52c38152ea2d7..8ca4163e34606a9a0adb4faf4262b893ec63f741 100644 (file)
@@ -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 55bdfc8b344fa5c301662c8aff14f67786592458..64feff4f06819584062f4e515caf682fb5fa8028 100644 (file)
--- a/check.c
+++ b/check.c
 #include <sys/file.h>
 #include <pwd.h>
 #include <grp.h>
-#ifdef HAVE_UTIME
-#  ifdef HAVE_UTIME_H
-#    include <utime.h>
-#  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.
  */
index fa5329dcc78fda234dd1167a7410290d34e2d41d..9dc54cf3a1311a1394f539e666ad04394146929a 100644 (file)
--- a/compat.h
+++ b/compat.h
 
 /* 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 <sys/types.h> or <sys/select.h>
  */
 #ifndef howmany
-#define howmany(x, y)  (((x) + ((y) - 1)) / (y))
+# define howmany(x, y) (((x) + ((y) - 1)) / (y))
 #endif
 
 /*
  * These should be defined in <unistd.h> 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 <unistd.h> 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 <sys/param.h> 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 */
index b351b1a194cc3c93cca210f7650d900f310c8637..f72cc543862685d1c3219660f7a5f81934b5a6a5 100644 (file)
 /* 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
 
index d0e48efda3f738d0c4bbb4c5c1793ce5f388aff5..5305434ba95ffb037184ae14bc9a4d9a40bc75bc 100755 (executable)
--- 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
index 398847794feabaca90e72e8e657a98002849905e..a2651e0f14f3af76dff1fd85a0507267afb63139 100644 (file)
@@ -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 (file)
index 0000000..3801cc2
--- /dev/null
+++ b/fileops.c
@@ -0,0 +1,149 @@
+/*
+ * Copyright (c) 1999 Todd C. Miller <Todd.Miller@courtesan.com>
+ * 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 <stdio.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif /* HAVE_UNISTD_H */
+#include <fcntl.h>
+#include <time.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#ifdef HAVE_UTIME
+# ifdef HAVE_UTIME_H
+#  include <utime.h>
+# 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
index 2841fffa5c6b1a879ee62ae34cccc28ca72cf4ca..ef1a4715da46ca7c1f3d0189309e3815fe755ede 100644 (file)
--- a/logging.c
+++ b/logging.c
@@ -47,7 +47,6 @@
 #ifdef HAVE_STRINGS_H
 #include <strings.h>
 #endif /* HAVE_STRINGS_H */
-#include <fcntl.h>
 #include <pwd.h>
 #include <signal.h>
 #include <time.h>
@@ -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 24a5da948173901d68e21f923525701b59384a98..f1af78fafb601f1b606c65d5f472e64545b95985 100644 (file)
--- 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. */
index 02c87e7840049dd7061881efae94bd3647e5232d..aa81b9284c148573d0c21db521250b6c3ad36cbe 100644 (file)
--- 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
-*/
     }
 }
 
index 491e4e85a5f6f37e3ce94187fb2ceb7b0858322a..e9dcf91a2f109d1f6689a736d7721641b517185c 100644 (file)
--- a/visudo.c
+++ b/visudo.c
@@ -56,6 +56,7 @@
 #endif /* HAVE_MALLOC_H && !STDC_HEADERS */
 #include <ctype.h>
 #include <pwd.h>
+#include <time.h>
 #include <signal.h>
 #include <errno.h>
 #include <fcntl.h>
@@ -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);
 }