In the timestamp record, include the start time of the terminal

session leader for tty-based timestamps or the start time of the
parent process for ppid-based timestamps.  Idea from Duncan Overbruck.
This commit is contained in:
Todd C. Miller
2017-12-16 05:53:05 -07:00
parent 5cec5734cc
commit 1709dc7f77
8 changed files with 379 additions and 68 deletions

View File

@@ -515,6 +515,7 @@ plugins/sudoers/set_perms.c
plugins/sudoers/solaris_audit.c plugins/sudoers/solaris_audit.c
plugins/sudoers/solaris_audit.h plugins/sudoers/solaris_audit.h
plugins/sudoers/sssd.c plugins/sudoers/sssd.c
plugins/sudoers/starttime.c
plugins/sudoers/sudo_nss.c plugins/sudoers/sudo_nss.c
plugins/sudoers/sudo_nss.h plugins/sudoers/sudo_nss.h
plugins/sudoers/sudo_printf.c plugins/sudoers/sudo_printf.c

View File

@@ -2772,19 +2772,21 @@ SSEECCUURRIITTYY NNOOTTEESS
with a date greater than current_time + 2 * TIMEOUT will be ignored and with a date greater than current_time + 2 * TIMEOUT will be ignored and
ssuuddooeerrss will log and complain. ssuuddooeerrss will log and complain.
Since time stamp files live in the file system, they can outlive a user's If the _t_i_m_e_s_t_a_m_p___t_y_p_e option is set to "tty", the time stamp record
login session. As a result, a user may be able to login, run a command includes the device number of the terminal the user authenticated with.
with ssuuddoo after authenticating, logout, login again, and run ssuuddoo without This provides per-terminal granularity but time stamp records may still
authenticating so long as the record's time stamp is within 5 minutes (or outlive the user's session.
whatever value the timeout is set to in the _s_u_d_o_e_r_s file). When the
_t_t_y___t_i_c_k_e_t_s option is enabled, the time stamp record includes the device Unless the _t_i_m_e_s_t_a_m_p___t_y_p_e option is set to "global", the time stamp
number of the terminal the user authenticated with. This provides per- record also includes the session ID of the process that last
tty granularity but time stamp records still may outlive the user's authenticated. This prevents processes in different terminal sessions
session. The time stamp record also includes the session ID of the from using the same time stamp record. On systems where a process's
process that last authenticated. This prevents processes in different start time can be queried, the start time of the session leader is
terminal sessions from using the same time stamp record. It also helps recorded in the time stamp record. If no terminal is present or the
reduce the chance that a user will be able to run ssuuddoo without entering a _t_i_m_e_s_t_a_m_p___t_y_p_e option is set to "ppid", the start time of the parent
password when logging out and back in again on the same terminal. 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.
DDEEBBUUGGGGIINNGG DDEEBBUUGGGGIINNGG
Versions 1.8.4 and higher of the ssuuddooeerrss plugin support a flexible Versions 1.8.4 and higher of the ssuuddooeerrss plugin support a flexible
@@ -2886,4 +2888,4 @@ DDIISSCCLLAAIIMMEERR
file distributed with ssuuddoo or https://www.sudo.ws/license.html for file distributed with ssuuddoo or https://www.sudo.ws/license.html for
complete details. complete details.
Sudo 1.8.22 December 11, 2017 Sudo 1.8.22 Sudo 1.8.22 December 15, 2017 Sudo 1.8.22

View File

@@ -21,7 +21,7 @@
.\" Agency (DARPA) and Air Force Research Laboratory, Air Force .\" Agency (DARPA) and Air Force Research Laboratory, Air Force
.\" Materiel Command, USAF, under agreement number F39502-99-1-0512. .\" 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 .nh
.if n .ad l .if n .ad l
.SH "NAME" .SH "NAME"
@@ -5497,31 +5497,33 @@ will be ignored and
\fBsudoers\fR \fBsudoers\fR
will log and complain. will log and complain.
.PP .PP
Since time stamp files live in the file system, they can outlive a If the
user's login session. \fItimestamp_type\fR
As a result, a user may be able to login, run a command with option is set to
\fBsudo\fR \(Lqtty\(Rq,
after authenticating, logout, login again, and run the time stamp record includes the device number of the terminal
\fBsudo\fR the user authenticated with.
without authenticating so long as the record's time stamp is within This provides per-terminal granularity but time stamp records may still
\fR@timeout@\fR outlive the user's session.
minutes (or whatever value the timeout is set to in the .PP
\fIsudoers\fR Unless the
file). \fItimestamp_type\fR
When the option is set to
\fItty_tickets\fR \(Lqglobal\(Rq,
option is enabled, the time stamp record includes the device the time stamp record also includes the session ID of the process
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. that last authenticated.
This prevents processes in different terminal sessions from using This prevents processes in different terminal sessions from using
the same time stamp record. the same time stamp record.
It also helps reduce the chance that a user will be able to run On systems where a process's start time can be queried,
\fBsudo\fR the start time of the session leader
without entering a password when logging out and back in again is recorded in the time stamp record.
on the same terminal. 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" .SH "DEBUGGING"
Versions 1.8.4 and higher of the Versions 1.8.4 and higher of the
\fBsudoers\fR \fBsudoers\fR

View File

@@ -19,7 +19,7 @@
.\" Agency (DARPA) and Air Force Research Laboratory, Air Force .\" Agency (DARPA) and Air Force Research Laboratory, Air Force
.\" Materiel Command, USAF, under agreement number F39502-99-1-0512. .\" Materiel Command, USAF, under agreement number F39502-99-1-0512.
.\" .\"
.Dd December 11, 2017 .Dd December 15, 2017
.Dt SUDOERS @mansectform@ .Dt SUDOERS @mansectform@
.Os Sudo @PACKAGE_VERSION@ .Os Sudo @PACKAGE_VERSION@
.Sh NAME .Sh NAME
@@ -5089,31 +5089,33 @@ will be ignored and
.Nm sudoers .Nm sudoers
will log and complain. will log and complain.
.Pp .Pp
Since time stamp files live in the file system, they can outlive a If the
user's login session. .Em timestamp_type
As a result, a user may be able to login, run a command with option is set to
.Nm sudo .Dq tty ,
after authenticating, logout, login again, and run the time stamp record includes the device number of the terminal
.Nm sudo the user authenticated with.
without authenticating so long as the record's time stamp is within This provides per-terminal granularity but time stamp records may still
.Li @timeout@ outlive the user's session.
minutes (or whatever value the timeout is set to in the .Pp
.Em sudoers Unless the
file). .Em timestamp_type
When the option is set to
.Em tty_tickets .Dq global ,
option is enabled, the time stamp record includes the device the time stamp record also includes the session ID of the process
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. that last authenticated.
This prevents processes in different terminal sessions from using This prevents processes in different terminal sessions from using
the same time stamp record. the same time stamp record.
It also helps reduce the chance that a user will be able to run On systems where a process's start time can be queried,
.Nm sudo the start time of the session leader
without entering a password when logging out and back in again is recorded in the time stamp record.
on the same terminal. 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 .Sh DEBUGGING
Versions 1.8.4 and higher of the Versions 1.8.4 and higher of the
.Nm .Nm

View 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 \ env_pattern.lo find_path.lo gc.lo goodpath.lo group_plugin.lo \
interfaces.lo iolog.lo iolog_path.lo locale.lo logging.lo \ interfaces.lo iolog.lo iolog_path.lo locale.lo logging.lo \
logwrap.lo mkdir_parents.lo parse.lo policy.lo prompt.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_OBJS = editor.o find_path.o goodpath.o locale.o sudo_printf.o visudo.o \
visudo_json.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 $(top_builddir)/pathnames.h
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(ASAN_CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(srcdir)/regress/parser/check_addr.c $(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 \ 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 $(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 \ check_digest.o: $(srcdir)/regress/parser/check_digest.c \
$(incdir)/compat/stdbool.h $(incdir)/sudo_compat.h \ $(incdir)/compat/stdbool.h $(incdir)/sudo_compat.h \
$(incdir)/sudo_fatal.h $(incdir)/sudo_queue.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 $(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 \ check_env_pattern.o: $(srcdir)/regress/env_match/check_env_pattern.c \
$(devdir)/def_data.h $(incdir)/compat/stdbool.h \ $(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 \ check_gentime.o: $(srcdir)/regress/parser/check_gentime.c \
$(incdir)/compat/stdbool.h $(incdir)/sudo_compat.h \ $(incdir)/compat/stdbool.h $(incdir)/sudo_compat.h \
$(incdir)/sudo_debug.h $(incdir)/sudo_queue.h \ $(incdir)/sudo_debug.h $(incdir)/sudo_queue.h \
$(srcdir)/parse.h $(srcdir)/sudoers_debug.h \ $(incdir)/sudo_util.h $(srcdir)/parse.h \
$(top_builddir)/config.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 $(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 \ 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 $(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 \ check_iolog_path.o: $(srcdir)/regress/iolog_path/check_iolog_path.c \
$(devdir)/def_data.c $(devdir)/def_data.h \ $(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 \ $(srcdir)/sudoers_debug.h $(top_builddir)/config.h \
$(top_builddir)/pathnames.h $(top_builddir)/pathnames.h
$(LIBTOOL) $(LTFLAGS) --mode=compile $(CC) -c $(CPPFLAGS) $(CFLAGS) $(ASAN_CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(srcdir)/sssd.c $(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 \ sudo_auth.lo: $(authdir)/sudo_auth.c $(devdir)/def_data.h \
$(incdir)/compat/stdbool.h $(incdir)/sudo_compat.h \ $(incdir)/compat/stdbool.h $(incdir)/sudo_compat.h \
$(incdir)/sudo_conf.h $(incdir)/sudo_debug.h \ $(incdir)/sudo_conf.h $(incdir)/sudo_debug.h \

View File

@@ -34,7 +34,7 @@
* records. Each record starts with a 16-bit version number and a 16-bit * 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. * record size. Multiple record types can coexist in the same file.
*/ */
#define TS_VERSION 1 #define TS_VERSION 2
/* Time stamp entry types */ /* Time stamp entry types */
#define TS_GLOBAL 0x01 #define TS_GLOBAL 0x01
@@ -46,7 +46,6 @@
#define TS_DISABLED 0x01 /* entry disabled */ #define TS_DISABLED 0x01 /* entry disabled */
#define TS_ANYUID 0x02 /* ignore uid, only valid in the key */ #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 { struct timestamp_entry {
unsigned short version; /* version number */ unsigned short version; /* version number */
unsigned short size; /* entry size */ unsigned short size; /* entry size */
@@ -54,6 +53,7 @@ struct timestamp_entry {
unsigned short flags; /* TS_DISABLED, TS_ANYUID */ unsigned short flags; /* TS_DISABLED, TS_ANYUID */
uid_t auth_uid; /* uid to authenticate as */ uid_t auth_uid; /* uid to authenticate as */
pid_t sid; /* session ID associated with tty/ppid */ pid_t sid; /* session ID associated with tty/ppid */
struct timespec start_time; /* session/ppid start time */
struct timespec ts; /* timestamp (CLOCK_MONOTONIC) */ struct timespec ts; /* timestamp (CLOCK_MONOTONIC) */
union { union {
dev_t ttydev; /* tty device number */ 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_lock(void *vcookie, struct passwd *pw);
bool timestamp_update(void *vcookie, struct passwd *pw); bool timestamp_update(void *vcookie, struct passwd *pw);
int timestamp_status(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); bool already_lectured(int status);
int set_lectured(void); int set_lectured(void);

282
plugins/sudoers/starttime.c Normal file
View File

@@ -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

View File

@@ -90,10 +90,15 @@ ts_match_record(struct timestamp_entry *key, struct timestamp_entry *entry)
/* verify parent pid */ /* verify parent pid */
if (entry->u.ppid != key->u.ppid) if (entry->u.ppid != key->u.ppid)
debug_return_bool(false); debug_return_bool(false);
if (sudo_timespeccmp(&entry->start_time, &key->start_time, !=))
debug_return_bool(false);
break;
break; break;
case TS_TTY: case TS_TTY:
if (entry->u.ttydev != key->u.ttydev) if (entry->u.ttydev != key->u.ttydev)
debug_return_bool(false); debug_return_bool(false);
if (sudo_timespeccmp(&entry->start_time, &key->start_time, !=))
debug_return_bool(false);
break; break;
default: default:
/* unknown record type, ignore it */ /* 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 */ /* tty-based time stamp */
entry->type = TS_TTY; entry->type = TS_TTY;
entry->u.ttydev = sb.st_rdev; entry->u.ttydev = sb.st_rdev;
if (entry->sid != -1)
get_starttime(entry->sid, &entry->start_time);
break; break;
} }
/* FALLTHROUGH */ /* FALLTHROUGH */
@@ -341,6 +348,7 @@ ts_init_key(struct timestamp_entry *entry, struct passwd *pw, int flags,
/* ppid-based time stamp */ /* ppid-based time stamp */
entry->type = TS_PPID; entry->type = TS_PPID;
entry->u.ppid = getppid(); entry->u.ppid = getppid();
get_starttime(entry->u.ppid, &entry->start_time);
break; break;
default: default:
/* global time stamp */ /* global time stamp */