]> granicus.if.org Git - sudo/commitdiff
In the timestamp record, include the start time of the terminal
authorTodd C. Miller <Todd.Miller@sudo.ws>
Sat, 16 Dec 2017 12:53:05 +0000 (05:53 -0700)
committerTodd C. Miller <Todd.Miller@sudo.ws>
Sat, 16 Dec 2017 12:53:05 +0000 (05:53 -0700)
session leader for tty-based timestamps or the start time of the
parent process for ppid-based timestamps.  Idea from Duncan Overbruck.

MANIFEST
doc/sudoers.cat
doc/sudoers.man.in
doc/sudoers.mdoc.in
plugins/sudoers/Makefile.in
plugins/sudoers/check.h
plugins/sudoers/starttime.c [new file with mode: 0644]
plugins/sudoers/timestamp.c

index 26fb997f7e0756efedc6f5abe38b731748cbeacc..e8b796dbe5fbc14902d622157581cd618edf4485 100644 (file)
--- a/MANIFEST
+++ b/MANIFEST
@@ -515,6 +515,7 @@ plugins/sudoers/set_perms.c
 plugins/sudoers/solaris_audit.c
 plugins/sudoers/solaris_audit.h
 plugins/sudoers/sssd.c
+plugins/sudoers/starttime.c
 plugins/sudoers/sudo_nss.c
 plugins/sudoers/sudo_nss.h
 plugins/sudoers/sudo_printf.c
index 7761d0e3db939422ef0704df2d0b226d6203f2dc..bf3d7c6548ad16a0f3991ebd6daca059fdf9fc92 100644 (file)
@@ -2772,19 +2772,21 @@ S\bSE\bEC\bCU\bUR\bRI\bIT\bTY\bY N\bNO\bOT\bTE\bES\bS
      with a date greater than current_time + 2 * TIMEOUT will be ignored and
      s\bsu\bud\bdo\boe\ber\brs\bs will log and complain.
 
-     Since time stamp files live in the file system, they can outlive a user's
-     login session.  As a result, a user may be able to login, run a command
-     with s\bsu\bud\bdo\bo after authenticating, logout, login again, and run s\bsu\bud\bdo\bo without
-     authenticating so long as the record's time stamp is within 5 minutes (or
-     whatever value the timeout is set to in the _\bs_\bu_\bd_\bo_\be_\br_\bs file).  When the
-     _\bt_\bt_\by_\b__\bt_\bi_\bc_\bk_\be_\bt_\bs option is enabled, the time stamp record includes the device
-     number of the terminal the user authenticated with.  This provides per-
-     tty granularity but time stamp records still may outlive the user's
-     session.  The time stamp record also includes the session ID of the
-     process that last authenticated.  This prevents processes in different
-     terminal sessions from using the same time stamp record.  It also helps
-     reduce the chance that a user will be able to run s\bsu\bud\bdo\bo without entering a
-     password when logging out and back in again on the same terminal.
+     If the _\bt_\bi_\bm_\be_\bs_\bt_\ba_\bm_\bp_\b__\bt_\by_\bp_\be option is set to "tty", the time stamp record
+     includes the device number of the terminal the user authenticated with.
+     This provides per-terminal granularity but time stamp records may still
+     outlive the user's session.
+
+     Unless the _\bt_\bi_\bm_\be_\bs_\bt_\ba_\bm_\bp_\b__\bt_\by_\bp_\be option is set to "global", the time stamp
+     record also includes the session ID of the process that last
+     authenticated.  This prevents processes in different terminal sessions
+     from using the same time stamp record.  On systems where a process's
+     start time can be queried, the start time of the session leader is
+     recorded in the time stamp record.  If no terminal is present or the
+     _\bt_\bi_\bm_\be_\bs_\bt_\ba_\bm_\bp_\b__\bt_\by_\bp_\be option is set to "ppid", the start time of the parent
+     process is used instead.  In most cases this will prevent a time stamp
+     record from being re-used without the user entering a password when
+     logging out and back in again.
 
 D\bDE\bEB\bBU\bUG\bGG\bGI\bIN\bNG\bG
      Versions 1.8.4 and higher of the s\bsu\bud\bdo\boe\ber\brs\bs plugin support a flexible
@@ -2886,4 +2888,4 @@ D\bDI\bIS\bSC\bCL\bLA\bAI\bIM\bME\bER\bR
      file distributed with s\bsu\bud\bdo\bo or https://www.sudo.ws/license.html for
      complete details.
 
-Sudo 1.8.22                    December 11, 2017                   Sudo 1.8.22
+Sudo 1.8.22                    December 15, 2017                   Sudo 1.8.22
index 15c11203df1777d81512e5f1b695c61ab18ba3fb..f0729d290d3a0dc8c7fdeb8cc0f30a3f9b6b554e 100644 (file)
@@ -21,7 +21,7 @@
 .\" Agency (DARPA) and Air Force Research Laboratory, Air Force
 .\" Materiel Command, USAF, under agreement number F39502-99-1-0512.
 .\"
-.TH "SUDOERS" "5" "December 11, 2017" "Sudo @PACKAGE_VERSION@" "File Formats Manual"
+.TH "SUDOERS" "5" "December 15, 2017" "Sudo @PACKAGE_VERSION@" "File Formats Manual"
 .nh
 .if n .ad l
 .SH "NAME"
@@ -5497,31 +5497,33 @@ will be ignored and
 \fBsudoers\fR
 will log and complain.
 .PP
-Since time stamp files live in the file system, they can outlive a
-user's login session.
-As a result, a user may be able to login, run a command with
-\fBsudo\fR
-after authenticating, logout, login again, and run
-\fBsudo\fR
-without authenticating so long as the record's time stamp is within
-\fR@timeout@\fR
-minutes (or whatever value the timeout is set to in the
-\fIsudoers\fR
-file).
-When the
-\fItty_tickets\fR
-option is enabled, the time stamp record includes the device
-number of the terminal the user authenticated with.
-This provides per-tty granularity but time stamp records still
-may outlive the user's session.
-The time stamp record also includes the session ID of the process
+If the
+\fItimestamp_type\fR
+option is set to
+\(Lqtty\(Rq,
+the time stamp record includes the device number of the terminal
+the user authenticated with.
+This provides per-terminal granularity but time stamp records may still
+outlive the user's session.
+.PP
+Unless the
+\fItimestamp_type\fR
+option is set to
+\(Lqglobal\(Rq,
+the time stamp record also includes the session ID of the process
 that last authenticated.
 This prevents processes in different terminal sessions from using
 the same time stamp record.
-It also helps reduce the chance that a user will be able to run
-\fBsudo\fR
-without entering a password when logging out and back in again
-on the same terminal.
+On systems where a process's start time can be queried,
+the start time of the session leader
+is recorded in the time stamp record.
+If no terminal is present or the
+\fItimestamp_type\fR
+option is set to
+\(Lqppid\(Rq,
+the start time of the parent process is used instead.
+In most cases this will prevent a time stamp record from being re-used
+without the user entering a password when logging out and back in again.
 .SH "DEBUGGING"
 Versions 1.8.4 and higher of the
 \fBsudoers\fR
index 2773d6c2ab69bf39177dab0f219c96bde3a91b3c..b41265423d3a1fcf58a83a88c0b8addc20dba239 100644 (file)
@@ -19,7 +19,7 @@
 .\" Agency (DARPA) and Air Force Research Laboratory, Air Force
 .\" Materiel Command, USAF, under agreement number F39502-99-1-0512.
 .\"
-.Dd December 11, 2017
+.Dd December 15, 2017
 .Dt SUDOERS @mansectform@
 .Os Sudo @PACKAGE_VERSION@
 .Sh NAME
@@ -5089,31 +5089,33 @@ will be ignored and
 .Nm sudoers
 will log and complain.
 .Pp
-Since time stamp files live in the file system, they can outlive a
-user's login session.
-As a result, a user may be able to login, run a command with
-.Nm sudo
-after authenticating, logout, login again, and run
-.Nm sudo
-without authenticating so long as the record's time stamp is within
-.Li @timeout@
-minutes (or whatever value the timeout is set to in the
-.Em sudoers
-file).
-When the
-.Em tty_tickets
-option is enabled, the time stamp record includes the device
-number of the terminal the user authenticated with.
-This provides per-tty granularity but time stamp records still
-may outlive the user's session.
-The time stamp record also includes the session ID of the process
+If the
+.Em timestamp_type
+option is set to
+.Dq tty ,
+the time stamp record includes the device number of the terminal
+the user authenticated with.
+This provides per-terminal granularity but time stamp records may still
+outlive the user's session.
+.Pp
+Unless the
+.Em timestamp_type
+option is set to
+.Dq global ,
+the time stamp record also includes the session ID of the process
 that last authenticated.
 This prevents processes in different terminal sessions from using
 the same time stamp record.
-It also helps reduce the chance that a user will be able to run
-.Nm sudo
-without entering a password when logging out and back in again
-on the same terminal.
+On systems where a process's start time can be queried,
+the start time of the session leader
+is recorded in the time stamp record.
+If no terminal is present or the
+.Em timestamp_type
+option is set to
+.Dq ppid ,
+the start time of the parent process is used instead.
+In most cases this will prevent a time stamp record from being re-used
+without the user entering a password when logging out and back in again.
 .Sh DEBUGGING
 Versions 1.8.4 and higher of the
 .Nm
index 0319a93099f46c73db0b4f1a384dd392f856a10c..2949cc0b73e31c5f427c2494adff42f557f20b00 100644 (file)
@@ -161,7 +161,8 @@ SUDOERS_OBJS = $(AUTH_OBJS) boottime.lo check.lo editor.lo env.lo \
               env_pattern.lo find_path.lo gc.lo goodpath.lo group_plugin.lo \
               interfaces.lo iolog.lo iolog_path.lo locale.lo logging.lo \
               logwrap.lo mkdir_parents.lo parse.lo policy.lo prompt.lo \
-              set_perms.lo sudo_nss.lo sudoers.lo timestamp.lo @SUDOERS_OBJS@
+              set_perms.lo starttime.lo sudo_nss.lo sudoers.lo \
+              timestamp.lo @SUDOERS_OBJS@
 
 VISUDO_OBJS = editor.o find_path.o goodpath.o locale.o sudo_printf.o visudo.o \
              visudo_json.o
@@ -607,12 +608,13 @@ check_addr.o: $(srcdir)/regress/parser/check_addr.c $(devdir)/def_data.h \
               $(top_builddir)/pathnames.h
        $(CC) -c $(CPPFLAGS) $(CFLAGS) $(ASAN_CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(srcdir)/regress/parser/check_addr.c
 check_base64.o: $(srcdir)/regress/parser/check_base64.c \
-                $(incdir)/sudo_compat.h $(top_builddir)/config.h
+                $(incdir)/compat/stdbool.h $(incdir)/sudo_compat.h \
+                $(incdir)/sudo_util.h $(top_builddir)/config.h
        $(CC) -c $(CPPFLAGS) $(CFLAGS) $(ASAN_CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(srcdir)/regress/parser/check_base64.c
 check_digest.o: $(srcdir)/regress/parser/check_digest.c \
                 $(incdir)/compat/stdbool.h $(incdir)/sudo_compat.h \
                 $(incdir)/sudo_fatal.h $(incdir)/sudo_queue.h \
-                $(srcdir)/parse.h $(top_builddir)/config.h
+                $(incdir)/sudo_util.h $(srcdir)/parse.h $(top_builddir)/config.h
        $(CC) -c $(CPPFLAGS) $(CFLAGS) $(ASAN_CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(srcdir)/regress/parser/check_digest.c
 check_env_pattern.o: $(srcdir)/regress/env_match/check_env_pattern.c \
                      $(devdir)/def_data.h $(incdir)/compat/stdbool.h \
@@ -634,11 +636,12 @@ check_fill.o: $(srcdir)/regress/parser/check_fill.c $(devdir)/gram.h \
 check_gentime.o: $(srcdir)/regress/parser/check_gentime.c \
                  $(incdir)/compat/stdbool.h $(incdir)/sudo_compat.h \
                  $(incdir)/sudo_debug.h $(incdir)/sudo_queue.h \
-                 $(srcdir)/parse.h $(srcdir)/sudoers_debug.h \
-                 $(top_builddir)/config.h
+                 $(incdir)/sudo_util.h $(srcdir)/parse.h \
+                 $(srcdir)/sudoers_debug.h $(top_builddir)/config.h
        $(CC) -c $(CPPFLAGS) $(CFLAGS) $(ASAN_CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(srcdir)/regress/parser/check_gentime.c
 check_hexchar.o: $(srcdir)/regress/parser/check_hexchar.c \
-                 $(incdir)/sudo_compat.h $(top_builddir)/config.h
+                 $(incdir)/compat/stdbool.h $(incdir)/sudo_compat.h \
+                 $(incdir)/sudo_util.h $(top_builddir)/config.h
        $(CC) -c $(CPPFLAGS) $(CFLAGS) $(ASAN_CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(srcdir)/regress/parser/check_hexchar.c
 check_iolog_path.o: $(srcdir)/regress/iolog_path/check_iolog_path.c \
                     $(devdir)/def_data.c $(devdir)/def_data.h \
@@ -1111,6 +1114,16 @@ sssd.lo: $(srcdir)/sssd.c $(devdir)/def_data.h $(incdir)/compat/stdbool.h \
          $(srcdir)/sudoers_debug.h $(top_builddir)/config.h \
          $(top_builddir)/pathnames.h
        $(LIBTOOL) $(LTFLAGS) --mode=compile $(CC) -c $(CPPFLAGS) $(CFLAGS) $(ASAN_CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(srcdir)/sssd.c
+starttime.lo: $(srcdir)/starttime.c $(devdir)/def_data.h \
+              $(incdir)/compat/stdbool.h $(incdir)/sudo_compat.h \
+              $(incdir)/sudo_conf.h $(incdir)/sudo_debug.h \
+              $(incdir)/sudo_fatal.h $(incdir)/sudo_gettext.h \
+              $(incdir)/sudo_plugin.h $(incdir)/sudo_queue.h \
+              $(incdir)/sudo_util.h $(srcdir)/check.h $(srcdir)/defaults.h \
+              $(srcdir)/logging.h $(srcdir)/sudo_nss.h $(srcdir)/sudoers.h \
+              $(srcdir)/sudoers_debug.h $(top_builddir)/config.h \
+              $(top_builddir)/pathnames.h
+       $(LIBTOOL) $(LTFLAGS) --mode=compile $(CC) -c $(CPPFLAGS) $(CFLAGS) $(ASAN_CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(srcdir)/starttime.c
 sudo_auth.lo: $(authdir)/sudo_auth.c $(devdir)/def_data.h \
               $(incdir)/compat/stdbool.h $(incdir)/sudo_compat.h \
               $(incdir)/sudo_conf.h $(incdir)/sudo_debug.h \
index eee1e4cadd125c8188b6f9885354f0ffda807647..15a0dc706677798aa5c921f55213f139f944912b 100644 (file)
@@ -34,7 +34,7 @@
  * records.  Each record starts with a 16-bit version number and a 16-bit
  * record size.  Multiple record types can coexist in the same file.
  */
-#define        TS_VERSION              1
+#define        TS_VERSION              2
 
 /* Time stamp entry types */
 #define TS_GLOBAL              0x01
@@ -46,7 +46,6 @@
 #define TS_DISABLED            0x01    /* entry disabled */
 #define TS_ANYUID              0x02    /* ignore uid, only valid in the key */
 
-/* XXX - may also want to store uid/gid of tty device */
 struct timestamp_entry {
     unsigned short version;    /* version number */
     unsigned short size;       /* entry size */
@@ -54,6 +53,7 @@ struct timestamp_entry {
     unsigned short flags;      /* TS_DISABLED, TS_ANYUID */
     uid_t auth_uid;            /* uid to authenticate as */
     pid_t sid;                 /* session ID associated with tty/ppid */
+    struct timespec start_time;        /* session/ppid start time */
     struct timespec ts;                /* timestamp (CLOCK_MONOTONIC) */
     union {
        dev_t ttydev;           /* tty device number */
@@ -66,6 +66,7 @@ void  timestamp_close(void *vcookie);
 bool  timestamp_lock(void *vcookie, struct passwd *pw);
 bool  timestamp_update(void *vcookie, struct passwd *pw);
 int   timestamp_status(void *vcookie, struct passwd *pw);
+int   get_starttime(pid_t pid, struct timespec *starttime);
 bool  already_lectured(int status);
 int   set_lectured(void);
 
diff --git a/plugins/sudoers/starttime.c b/plugins/sudoers/starttime.c
new file mode 100644 (file)
index 0000000..d49359d
--- /dev/null
@@ -0,0 +1,282 @@
+/*
+ * Copyright (c) 2012-2017 Todd C. Miller <Todd.Miller@sudo.ws>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <config.h>
+
+/* Large files not supported by procfs.h on Solaris. */
+#if defined(HAVE_STRUCT_PSINFO_PR_TTYDEV)
+# undef _FILE_OFFSET_BITS
+# undef _LARGE_FILES
+#endif
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#if defined(HAVE_STRUCT_KINFO_PROC_P_TDEV) || defined (HAVE_STRUCT_KINFO_PROC_KP_EPROC_E_TDEV) || defined(HAVE_STRUCT_KINFO_PROC2_P_TDEV)
+# include <sys/sysctl.h>
+#elif defined(HAVE_STRUCT_KINFO_PROC_KI_TDEV)
+# include <sys/sysctl.h>
+# include <sys/user.h>
+#endif
+#if defined(HAVE_PROCFS_H)
+# include <procfs.h>
+#elif defined(HAVE_SYS_PROCFS_H)
+# include <sys/procfs.h>
+#endif
+#ifdef HAVE_PSTAT_GETPROC
+# include <sys/pstat.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#ifdef HAVE_STRING_H
+# include <string.h>
+#endif /* HAVE_STRING_H */
+#ifdef HAVE_STRINGS_H
+# include <strings.h>
+#endif /* HAVE_STRINGS_H */
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <unistd.h>
+
+#include "sudoers.h"
+#include "check.h"
+
+/*
+ * Arguments for sysctl(2) when reading the process start time.
+ */
+#if defined(HAVE_STRUCT_KINFO_PROC2_P_TDEV)
+# define SUDO_KERN_PROC                KERN_PROC2
+# define sudo_kinfo_proc       kinfo_proc2
+# define sudo_kp_namelen       6
+#elif defined(HAVE_STRUCT_KINFO_PROC_P_TDEV)
+# define SUDO_KERN_PROC                KERN_PROC
+# define sudo_kinfo_proc       kinfo_proc
+# define sudo_kp_namelen       6
+#elif defined(HAVE_STRUCT_KINFO_PROC_KI_TDEV)
+# define SUDO_KERN_PROC                KERN_PROC
+# define sudo_kinfo_proc       kinfo_proc
+# define sudo_kp_namelen       4
+#endif
+
+/*
+ * Store start time of the specified process in starttime.
+ */
+
+#if defined(sudo_kinfo_proc)
+int
+get_starttime(pid_t pid, struct timespec *starttime)
+{
+    struct sudo_kinfo_proc *ki_proc = NULL;
+    size_t size = sizeof(*ki_proc);
+    int mib[6], rc;
+    debug_decl(get_starttime, SUDO_DEBUG_UTIL)
+
+    /*
+     * Lookup start time for pid via sysctl.
+     */
+    mib[0] = CTL_KERN;
+    mib[1] = SUDO_KERN_PROC;
+    mib[2] = KERN_PROC_PID;
+    mib[3] = (int)pid;
+    mib[4] = sizeof(*ki_proc);
+    mib[5] = 1;
+    do {
+       struct sudo_kinfo_proc *kp;
+
+       size += size / 10;
+       if ((kp = realloc(ki_proc, size)) == NULL) {
+           rc = -1;
+           break;              /* really out of memory. */
+       }
+       ki_proc = kp;
+       rc = sysctl(mib, sudo_kp_namelen, ki_proc, &size, NULL, 0);
+    } while (rc == -1 && errno == ENOMEM);
+    if (rc != -1) {
+#if defined(HAVE_STRUCT_KINFO_PROC_KI_TDEV)
+       /* FreeBSD and macOS */
+       starttime->tv_sec = ki_proc->ki_start->tv_sec;
+       starttime->tv_nsec = ki_proc->ki_start->tv_usec / 1000;
+#else
+       /* NetBSD and OpenBSD */
+       starttime->tv_sec = ki_proc->p_ustart_sec;
+       starttime->tv_nsec = ki_proc->p_ustart_usec / 1000;
+#endif
+    } else {
+       sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO|SUDO_DEBUG_ERRNO,
+           "unable to get start time for %d via KERN_PROC", (int)pid);
+    }
+    free(ki_proc);
+
+    debug_return_int(rc == -1 ? -1 : 0);
+}
+#elif defined(HAVE_STRUCT_PSINFO_PR_TTYDEV)
+int
+get_starttime(pid_t pid, struct timespec *starttime)
+{
+    struct psinfo psinfo;
+    char path[PATH_MAX];
+    ssize_t nread;
+    int fd, ret = -1;
+    debug_decl(get_starttime, SUDO_DEBUG_UTIL)
+
+    /* Determine the start time from pr_start in /proc/pid/psinfo. */
+    snprintf(path, sizeof(path), "/proc/%u/psinfo", (unsigned int)pid);
+    if ((fd = open(path, O_RDONLY, 0)) != -1) {
+       nread = read(fd, &psinfo, sizeof(psinfo));
+       close(fd);
+       if (nread == (ssize_t)sizeof(psinfo)) {
+           starttime->tv_sec = psinfo.pr_start.tv_sec;
+           starttime->tv_nsec = psinfo.pr_start.tv_nsec;
+           ret = 0;
+       }
+    }
+
+    if (ret == -1)
+       sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO|SUDO_DEBUG_ERRNO,
+           "unable to get start time for %d via %s", (int)pid, path);
+    debug_return_int(ret);
+}
+#elif defined(__linux__)
+int
+get_starttime(pid_t pid, struct timespec *starttime)
+{
+    char path[PATH_MAX];
+    char *cp, buf[1024];
+    ssize_t nread;
+    int ret = -1;
+    int fd = -1;
+    long tps;
+    debug_decl(get_process_ttyname, SUDO_DEBUG_UTIL)
+
+    /*
+     * Start time is in ticks per second on Linux.
+     */
+    tps = sysconf(_SC_CLK_TCK);
+    if (tps == -1)
+       goto done;
+
+    /*
+     * Determine the start time from 22nd field in /proc/pid/stat.
+     * Ignore /proc/self/stat if it contains embedded NUL bytes.
+     * XXX - refactor common code with ttyname.c?
+     */
+    snprintf(path, sizeof(path), "/proc/%u/stat", (unsigned int)pid);
+    if ((fd = open(path, O_RDONLY | O_NOFOLLOW)) != -1) {
+       cp = buf;
+       while ((nread = read(fd, cp, buf + sizeof(buf) - cp)) != 0) {
+           if (nread == -1) {
+               if (errno == EAGAIN || errno == EINTR)
+                   continue;
+               break;
+           }
+           cp += nread;
+           if (cp >= buf + sizeof(buf))
+               break;
+       }
+       if (nread == 0 && memchr(buf, '\0', cp - buf) == NULL) {
+           /*
+            * Field 22 is the start time (%ull).
+            * Since the process name at field 2 "(comm)" may include
+            * whitespace (including newlines), start at the last ')' found.
+            */
+           *cp = '\0';
+           cp = strrchr(buf, ')');
+           if (cp != NULL) {
+               char *ep = cp;
+               int field = 1;
+
+               while (*++ep != '\0') {
+                   if (*ep == ' ') {
+                       *ep = '\0';
+                       if (++field == 22) {
+                           unsigned long long ullval;
+
+                           /* Must start with a digit (not negative). */
+                           if (!isdigit((unsigned char)*cp)) {
+                               errno = EINVAL;
+                               goto done;
+                           }
+
+                           /* starttime is %ul in 2.4 and %ull in >= 2.6 */
+                           errno = 0;
+                           ullval = strtoull(cp, &ep, 10);
+                           if (ep == cp || *ep != ' ') {
+                               errno = EINVAL;
+                               goto done;
+                           }
+                           if (errno == ERANGE && ullval == ULLONG_MAX)
+                               goto done;
+
+                           /* Convert from ticks to timespec */
+                           starttime->tv_sec = ullval / tps;
+                           starttime->tv_nsec =
+                               (ullval % tps) * (1000000000 / tps);
+
+                           ret = 0;
+                           goto done;
+                       }
+                       cp = ep + 1;
+                   }
+               }
+           }
+       }
+    }
+    errno = ENOENT;
+
+done:
+    if (fd != -1)
+       close(fd);
+    if (ret == -1)
+       sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO|SUDO_DEBUG_ERRNO,
+           "unable to get start time for %d via %s", (int)pid, path);
+    debug_return_int(ret);
+}
+#elif defined(HAVE_PSTAT_GETPROC)
+int
+get_starttime(pid_t pid, struct timespec *starttime)
+{
+    struct pst_status pstat;
+    int rc;
+    debug_decl(get_starttime, SUDO_DEBUG_UTIL)
+
+    /*
+     * Determine the start time from pst_start in struct pst_status.
+     * We may get EOVERFLOW if the whole thing doesn't fit but that is OK.
+     */
+    rc = pstat_getproc(&pstat, sizeof(pstat), (size_t)0, (int)getpid());
+    if (rc != -1 || errno == EOVERFLOW) {
+       starttime->tv_sec = pstat.pst_start;
+       starttime->tv_nsec = 0;
+       debug_return_int(0);
+    }
+
+    sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO|SUDO_DEBUG_ERRNO,
+       "unable to get start time for %d via pstat_getproc", (int)pid);
+    debug_return_int(-1);
+}
+#else
+int
+get_starttime(pid_t pid, struct timespec *starttime)
+{
+    debug_decl(get_starttime, SUDO_DEBUG_UTIL)
+
+    sudo_debug_printf(SUDO_DEBUG_WARN|SUDO_DEBUG_LINENO,
+       "process start time not supported by sudo on this system");
+    debug_return_int(-1);
+}
+#endif
index 3b7b8728fe4c15336aa53c661245fcb06eced55a..07b045cc63a8e1ef6c3724f62a70112b9bdda3eb 100644 (file)
@@ -90,10 +90,15 @@ ts_match_record(struct timestamp_entry *key, struct timestamp_entry *entry)
        /* verify parent pid */
        if (entry->u.ppid != key->u.ppid)
            debug_return_bool(false);
+       if (sudo_timespeccmp(&entry->start_time, &key->start_time, !=))
+           debug_return_bool(false);
+        break;
        break;
     case TS_TTY:
        if (entry->u.ttydev != key->u.ttydev)
            debug_return_bool(false);
+       if (sudo_timespeccmp(&entry->start_time, &key->start_time, !=))
+           debug_return_bool(false);
        break;
     default:
        /* unknown record type, ignore it */
@@ -334,6 +339,8 @@ ts_init_key(struct timestamp_entry *entry, struct passwd *pw, int flags,
            /* tty-based time stamp */
            entry->type = TS_TTY;
            entry->u.ttydev = sb.st_rdev;
+           if (entry->sid != -1)
+               get_starttime(entry->sid, &entry->start_time);
            break;
        }
        /* FALLTHROUGH */
@@ -341,6 +348,7 @@ ts_init_key(struct timestamp_entry *entry, struct passwd *pw, int flags,
        /* ppid-based time stamp */
        entry->type = TS_PPID;
        entry->u.ppid = getppid();
+       get_starttime(entry->u.ppid, &entry->start_time);
        break;
     default:
        /* global time stamp */