Add tsdump, a simple utility to dump a timestamp file. To build,

run "make tsdump" in the plugins/sudoers directory (it is not built
by default).  In order to map the tty device number to a name,
sudo_ttyname_dev() has been moved into libsudo_util.
This commit is contained in:
Todd C. Miller
2018-01-11 10:49:20 -07:00
parent d5f4fdb814
commit ff5ac3ef0e
12 changed files with 668 additions and 283 deletions

View File

@@ -178,6 +178,7 @@ lib/util/sudo_conf.c
lib/util/sudo_debug.c lib/util/sudo_debug.c
lib/util/sudo_dso.c lib/util/sudo_dso.c
lib/util/term.c lib/util/term.c
lib/util/ttyname_dev.c
lib/util/ttysize.c lib/util/ttysize.c
lib/util/util.exp.in lib/util/util.exp.in
lib/util/utimens.c lib/util/utimens.c
@@ -540,6 +541,7 @@ plugins/sudoers/toke.c
plugins/sudoers/toke.h plugins/sudoers/toke.h
plugins/sudoers/toke.l plugins/sudoers/toke.l
plugins/sudoers/toke_util.c plugins/sudoers/toke_util.c
plugins/sudoers/tsdump.c
plugins/sudoers/tsgetgrpw.c plugins/sudoers/tsgetgrpw.c
plugins/sudoers/tsgetgrpw.h plugins/sudoers/tsgetgrpw.h
plugins/sudoers/visudo.c plugins/sudoers/visudo.c

View File

@@ -195,6 +195,9 @@
don't. */ don't. */
#undef HAVE_DECL__SYS_SIGNAME #undef HAVE_DECL__SYS_SIGNAME
/* Define to 1 if you have the `devname' function. */
#undef HAVE_DEVNAME
/* Define to 1 if you have the <dirent.h> header file, and it defines `DIR'. /* Define to 1 if you have the <dirent.h> header file, and it defines `DIR'.
*/ */
#undef HAVE_DIRENT_H #undef HAVE_DIRENT_H

13
configure vendored
View File

@@ -19252,7 +19252,18 @@ if test "x$ac_cv_func_sysctl" = xyes; then :
cat >>confdefs.h <<_ACEOF cat >>confdefs.h <<_ACEOF
#define HAVE_SYSCTL 1 #define HAVE_SYSCTL 1
_ACEOF _ACEOF
ac_fn_c_check_member "$LINENO" "struct kinfo_proc" "ki_tdev" "ac_cv_member_struct_kinfo_proc_ki_tdev" " for ac_func in devname
do :
ac_fn_c_check_func "$LINENO" "devname" "ac_cv_func_devname"
if test "x$ac_cv_func_devname" = xyes; then :
cat >>confdefs.h <<_ACEOF
#define HAVE_DEVNAME 1
_ACEOF
fi
done
ac_fn_c_check_member "$LINENO" "struct kinfo_proc" "ki_tdev" "ac_cv_member_struct_kinfo_proc_ki_tdev" "
# include <sys/param.h> # include <sys/param.h>
# include <sys/sysctl.h> # include <sys/sysctl.h>
# include <sys/user.h> # include <sys/user.h>

View File

@@ -2528,8 +2528,8 @@ if test "$utmp_style" = "LEGACY"; then
AC_CHECK_FUNCS([fseeko]) AC_CHECK_FUNCS([fseeko])
fi fi
AC_CHECK_FUNCS([sysctl], [AC_CHECK_MEMBERS([struct kinfo_proc.ki_tdev], [], AC_CHECK_FUNCS([sysctl], [AC_CHECK_FUNCS([devname])
[ AC_CHECK_MEMBERS([struct kinfo_proc.ki_tdev], [], [
AC_CHECK_MEMBERS([struct kinfo_proc2.p_tdev], [], [ AC_CHECK_MEMBERS([struct kinfo_proc2.p_tdev], [], [
AC_CHECK_MEMBERS([struct kinfo_proc.p_tdev], [], [ AC_CHECK_MEMBERS([struct kinfo_proc.p_tdev], [], [
AC_CHECK_MEMBERS([struct kinfo_proc.kp_eproc.e_tdev], [], [], [ AC_CHECK_MEMBERS([struct kinfo_proc.kp_eproc.e_tdev], [], [], [

View File

@@ -248,6 +248,10 @@ __dso_public bool sudo_term_raw_v1(int fd, int isig);
__dso_public bool sudo_term_restore_v1(int fd, bool flush); __dso_public bool sudo_term_restore_v1(int fd, bool flush);
#define sudo_term_restore(_a, _b) sudo_term_restore_v1((_a), (_b)) #define sudo_term_restore(_a, _b) sudo_term_restore_v1((_a), (_b))
/* ttyname_dev.c */
__dso_public char *sudo_ttyname_dev_v1(dev_t tdev, char *name, size_t namelen);
#define sudo_ttyname_dev(_a, _b, _c) sudo_ttyname_dev_v1((_a), (_b), (_c))
/* ttysize.c */ /* ttysize.c */
__dso_public void sudo_get_ttysize_v1(int *rowp, int *colp); __dso_public void sudo_get_ttysize_v1(int *rowp, int *colp);
#define sudo_get_ttysize(_a, _b) sudo_get_ttysize_v1((_a), (_b)) #define sudo_get_ttysize(_a, _b) sudo_get_ttysize_v1((_a), (_b))

View File

@@ -111,8 +111,8 @@ SHELL = @SHELL@
LTOBJS = event.lo fatal.lo key_val.lo gethostname.lo gettime.lo \ LTOBJS = event.lo fatal.lo key_val.lo gethostname.lo gettime.lo \
gidlist.lo lbuf.lo locking.lo parseln.lo progname.lo secure_path.lo \ gidlist.lo lbuf.lo locking.lo parseln.lo progname.lo secure_path.lo \
setgroups.lo strsplit.lo strtobool.lo strtoid.lo strtomode.lo \ setgroups.lo strsplit.lo strtobool.lo strtoid.lo strtomode.lo \
sudo_conf.lo sudo_debug.lo sudo_dso.lo term.lo ttysize.lo \ sudo_conf.lo sudo_debug.lo sudo_dso.lo term.lo ttyname_dev.lo \
@COMMON_OBJS@ @LTLIBOBJS@ ttysize.lo @COMMON_OBJS@ @LTLIBOBJS@
ATOFOO_TEST_OBJS = atofoo_test.lo ATOFOO_TEST_OBJS = atofoo_test.lo
@@ -589,6 +589,12 @@ term.lo: $(srcdir)/term.c $(incdir)/compat/stdbool.h $(incdir)/sudo_compat.h \
$(incdir)/sudo_debug.h $(incdir)/sudo_queue.h $(incdir)/sudo_util.h \ $(incdir)/sudo_debug.h $(incdir)/sudo_queue.h $(incdir)/sudo_util.h \
$(top_builddir)/config.h $(top_builddir)/config.h
$(LIBTOOL) $(LTFLAGS) --mode=compile $(CC) -c -o $@ $(CPPFLAGS) $(CFLAGS) $(ASAN_CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(srcdir)/term.c $(LIBTOOL) $(LTFLAGS) --mode=compile $(CC) -c -o $@ $(CPPFLAGS) $(CFLAGS) $(ASAN_CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(srcdir)/term.c
ttyname_dev.lo: $(srcdir)/ttyname_dev.c $(incdir)/compat/stdbool.h \
$(incdir)/sudo_compat.h $(incdir)/sudo_conf.h \
$(incdir)/sudo_debug.h $(incdir)/sudo_queue.h \
$(incdir)/sudo_util.h $(top_builddir)/config.h \
$(top_builddir)/pathnames.h
$(LIBTOOL) $(LTFLAGS) --mode=compile $(CC) -c -o $@ $(CPPFLAGS) $(CFLAGS) $(ASAN_CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(srcdir)/ttyname_dev.c
ttysize.lo: $(srcdir)/ttysize.c $(incdir)/compat/stdbool.h \ ttysize.lo: $(srcdir)/ttysize.c $(incdir)/compat/stdbool.h \
$(incdir)/sudo_compat.h $(incdir)/sudo_debug.h \ $(incdir)/sudo_compat.h $(incdir)/sudo_debug.h \
$(incdir)/sudo_queue.h $(incdir)/sudo_util.h \ $(incdir)/sudo_queue.h $(incdir)/sudo_util.h \

311
lib/util/ttyname_dev.c Normal file
View File

@@ -0,0 +1,311 @@
/*
* Copyright (c) 2012-2018 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>
#include <sys/types.h>
#include <sys/stat.h>
#if defined(MAJOR_IN_MKDEV)
# include <sys/mkdev.h>
#elif defined(MAJOR_IN_SYSMACROS)
# include <sys/sysmacros.h>
#else
# include <sys/param.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 <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <dirent.h>
#include <pathnames.h>
#include "sudo_compat.h"
#include "sudo_debug.h"
#include "sudo_conf.h"
#include "sudo_util.h"
#if defined(HAVE_DEVNAME)
/*
* Like ttyname() but uses a dev_t instead of an open fd.
* Returns name on success and NULL on failure, setting errno.
* The BSD version uses devname().
*/
char *
sudo_ttyname_dev_v1(dev_t tdev, char *name, size_t namelen)
{
char *dev;
debug_decl(sudo_ttyname_dev, SUDO_DEBUG_UTIL)
/* Some versions of devname() return NULL on failure, others do not. */
dev = devname(tdev, S_IFCHR);
if (dev != NULL && *dev != '?' && *dev != '#') {
if (strlcpy(name, _PATH_DEV, namelen) < namelen &&
strlcat(name, dev, namelen) < namelen)
debug_return_str(name);
errno = ERANGE;
} else {
/* Not all versions of devname() set errno. */
errno = ENOENT;
}
debug_return_str(NULL);
}
#elif defined(HAVE__TTYNAME_DEV)
extern char *_ttyname_dev(dev_t rdev, char *buffer, size_t buflen);
/*
* Like ttyname() but uses a dev_t instead of an open fd.
* Returns name on success and NULL on failure, setting errno.
* This version is just a wrapper around _ttyname_dev().
*/
char *
sudo_ttyname_dev_v1(dev_t tdev, char *name, size_t namelen)
{
int serrno = errno;
debug_decl(sudo_ttyname_dev, SUDO_DEBUG_UTIL)
/*
* _ttyname_dev() sets errno to ERANGE if namelen is too small
* but does not modify it if tdev is not found.
*/
errno = ENOENT;
if (_ttyname_dev(tdev, name, namelen) == NULL)
debug_return_str(NULL);
errno = serrno;
debug_return_str(name);
}
#else
/*
* Device nodes to ignore.
*/
static const char *ignore_devs[] = {
_PATH_DEV "stdin",
_PATH_DEV "stdout",
_PATH_DEV "stderr",
NULL
};
/*
* Do a scan of a directory looking for the specified device.
* Does not descend into subdirectories.
* Returns name on success and NULL on failure, setting errno.
*/
static char *
sudo_ttyname_scan(const char *dir, dev_t rdev, char *name, size_t namelen)
{
size_t sdlen;
char pathbuf[PATH_MAX];
char *ret = NULL;
struct dirent *dp;
struct stat sb;
unsigned int i;
DIR *d = NULL;
debug_decl(sudo_ttyname_scan, SUDO_DEBUG_UTIL)
if (dir[0] == '\0') {
errno = ENOENT;
goto done;
}
if ((d = opendir(dir)) == NULL)
goto done;
if (fstat(dirfd(d), &sb) == -1) {
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
"unable to fstat %s", dir);
goto done;
}
if ((sb.st_mode & S_IWOTH) != 0) {
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
"ignoring world-writable directory %s", dir);
errno = ENOENT;
goto done;
}
sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO,
"scanning for dev %u in %s", (unsigned int)rdev, dir);
sdlen = strlen(dir);
while (sdlen > 0 && dir[sdlen - 1] == '/')
sdlen--;
if (sdlen + 1 >= sizeof(pathbuf)) {
errno = ERANGE;
goto done;
}
memcpy(pathbuf, dir, sdlen);
pathbuf[sdlen++] = '/';
while ((dp = readdir(d)) != NULL) {
struct stat sb;
/* Skip anything starting with "." */
if (dp->d_name[0] == '.')
continue;
pathbuf[sdlen] = '\0';
if (strlcat(pathbuf, dp->d_name, sizeof(pathbuf)) >= sizeof(pathbuf)) {
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
"%s%s is too big to fit in pathbuf", pathbuf, dp->d_name);
continue;
}
/* Ignore device nodes listed in ignore_devs[]. */
for (i = 0; ignore_devs[i] != NULL; i++) {
if (strcmp(pathbuf, ignore_devs[i]) == 0)
break;
}
if (ignore_devs[i] != NULL) {
sudo_debug_printf(SUDO_DEBUG_DEBUG|SUDO_DEBUG_LINENO,
"ignoring %s", pathbuf);
continue;
}
# if defined(HAVE_STRUCT_DIRENT_D_TYPE)
/*
* Avoid excessive stat() calls by checking dp->d_type.
*/
switch (dp->d_type) {
case DT_CHR:
case DT_LNK:
case DT_UNKNOWN:
break;
default:
/* Not a character device or link, skip it. */
sudo_debug_printf(SUDO_DEBUG_DEBUG|SUDO_DEBUG_LINENO,
"skipping non-device %s", pathbuf);
continue;
}
# endif
if (stat(pathbuf, &sb) == -1) {
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO|SUDO_DEBUG_ERRNO,
"unable to stat %s", pathbuf);
continue;
}
if (S_ISCHR(sb.st_mode) && sb.st_rdev == rdev) {
sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO,
"resolved dev %u as %s", (unsigned int)rdev, pathbuf);
if (strlcpy(name, pathbuf, namelen) < namelen) {
ret = name;
} else {
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
"unable to store %s, have %zu, need %zu",
pathbuf, namelen, strlen(pathbuf) + 1);
errno = ERANGE;
}
goto done;
}
}
done:
if (d != NULL)
closedir(d);
debug_return_str(ret);
}
static char *
sudo_dev_check(dev_t rdev, const char *devname, char *buf, size_t buflen)
{
struct stat sb;
debug_decl(sudo_dev_check, SUDO_DEBUG_UTIL)
if (stat(devname, &sb) == 0) {
if (S_ISCHR(sb.st_mode) && sb.st_rdev == rdev) {
sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO,
"comparing dev %u to %s: match!",
(unsigned int)rdev, devname);
if (strlcpy(buf, devname, buflen) < buflen)
debug_return_str(buf);
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
"unable to store %s, have %zu, need %zu",
devname, buflen, strlen(devname) + 1);
errno = ERANGE;
}
}
sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO,
"comparing dev %u to %s: no", (unsigned int)rdev, devname);
debug_return_str(NULL);
}
/*
* Like ttyname() but uses a dev_t instead of an open fd.
* Returns name on success and NULL on failure, setting errno.
* Generic version.
*/
char *
sudo_ttyname_dev_v1(dev_t rdev, char *buf, size_t buflen)
{
const char *devsearch, *devsearch_end;
char path[PATH_MAX], *ret;
const char *cp, *ep;
size_t len;
debug_decl(sudo_ttyname_dev, SUDO_DEBUG_UTIL)
/*
* First, check /dev/console.
*/
ret = sudo_dev_check(rdev, _PATH_DEV "console", buf, buflen);
if (ret != NULL)
goto done;
/*
* Then check the device search path.
*/
devsearch = sudo_conf_devsearch_path();
devsearch_end = devsearch + strlen(devsearch);
for (cp = sudo_strsplit(devsearch, devsearch_end, ":", &ep);
cp != NULL; cp = sudo_strsplit(NULL, devsearch_end, ":", &ep)) {
len = (size_t)(ep - cp);
if (len >= sizeof(path)) {
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
"devsearch entry %.*s too long", (int)len, cp);
continue;
}
memcpy(path, cp, len);
path[len] = '\0';
if (strcmp(path, _PATH_DEV "pts") == 0) {
/* Special case /dev/pts */
len = (size_t)snprintf(path, sizeof(path), "%spts/%u",
_PATH_DEV, (unsigned int)minor(rdev));
if (len >= sizeof(path)) {
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
"devsearch entry %spts/%u too long",
_PATH_DEV, (unsigned int)minor(rdev));
continue;
}
ret = sudo_dev_check(rdev, path, buf, buflen);
if (ret != NULL)
goto done;
} else {
/* Scan path, looking for rdev. */
ret = sudo_ttyname_scan(path, rdev, buf, buflen);
if (ret != NULL || errno == ENOMEM)
goto done;
}
}
done:
debug_return_str(ret);
}
#endif

View File

@@ -60,6 +60,7 @@ sudo_fatal_callback_deregister_v1
sudo_fatal_callback_register_v1 sudo_fatal_callback_register_v1
sudo_fatal_nodebug_v1 sudo_fatal_nodebug_v1
sudo_fatalx_nodebug_v1 sudo_fatalx_nodebug_v1
sudo_ttyname_dev_v1
sudo_get_ttysize_v1 sudo_get_ttysize_v1
sudo_gethostname_v1 sudo_gethostname_v1
sudo_gettime_mono_v1 sudo_gettime_mono_v1

View File

@@ -172,6 +172,8 @@ REPLAY_OBJS = getdate.o sudoreplay.o
TEST_OBJS = group_plugin.o interfaces.o locale.o net_ifs.o \ TEST_OBJS = group_plugin.o interfaces.o locale.o net_ifs.o \
sudo_printf.o testsudoers.o tsgetgrpw.o sudo_printf.o testsudoers.o tsgetgrpw.o
TSDUMP_OBJS = tsdump.o sudoers_debug.o locale.o
CHECK_ADDR_OBJS = check_addr.o interfaces.o match_addr.o sudoers_debug.o \ CHECK_ADDR_OBJS = check_addr.o interfaces.o match_addr.o sudoers_debug.o \
sudo_printf.o sudo_printf.o
@@ -245,6 +247,9 @@ sudoreplay: timestr.lo $(REPLAY_OBJS) $(LT_LIBS)
testsudoers: libparsesudoers.la $(TEST_OBJS) $(LT_LIBS) testsudoers: libparsesudoers.la $(TEST_OBJS) $(LT_LIBS)
$(LIBTOOL) $(LTFLAGS) --mode=link $(CC) -o $@ $(TEST_OBJS) $(LDFLAGS) $(ASAN_LDFLAGS) $(PIE_LDFLAGS) $(SSP_LDFLAGS) libparsesudoers.la $(LIBS) $(TESTSUDOERS_LIBS) $(LIBTOOL) $(LTFLAGS) --mode=link $(CC) -o $@ $(TEST_OBJS) $(LDFLAGS) $(ASAN_LDFLAGS) $(PIE_LDFLAGS) $(SSP_LDFLAGS) libparsesudoers.la $(LIBS) $(TESTSUDOERS_LIBS)
tsdump: $(TSDUMP_OBJS) $(LT_LIBS)
$(LIBTOOL) $(LTFLAGS) --mode=link $(CC) -o $@ $(TSDUMP_OBJS) $(LDFLAGS) $(ASAN_LDFLAGS) $(PIE_LDFLAGS) $(SSP_LDFLAGS) $(LIBS)
check_addr: $(CHECK_ADDR_OBJS) $(LT_LIBS) check_addr: $(CHECK_ADDR_OBJS) $(LT_LIBS)
$(LIBTOOL) $(LTFLAGS) --mode=link $(CC) -o $@ $(CHECK_ADDR_OBJS) $(LDFLAGS) $(ASAN_LDFLAGS) $(PIE_LDFLAGS) $(SSP_LDFLAGS) $(LIBS) $(NET_LIBS) $(LIBTOOL) $(LTFLAGS) --mode=link $(CC) -o $@ $(CHECK_ADDR_OBJS) $(LDFLAGS) $(ASAN_LDFLAGS) $(PIE_LDFLAGS) $(SSP_LDFLAGS) $(LIBS) $(NET_LIBS)
@@ -661,15 +666,9 @@ check_iolog_path.o: $(srcdir)/regress/iolog_path/check_iolog_path.c \
$(top_builddir)/config.h $(top_builddir)/pathnames.h $(top_builddir)/config.h $(top_builddir)/pathnames.h
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(ASAN_CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(srcdir)/regress/iolog_path/check_iolog_path.c $(CC) -c $(CPPFLAGS) $(CFLAGS) $(ASAN_CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(srcdir)/regress/iolog_path/check_iolog_path.c
check_starttime.o: $(srcdir)/regress/starttime/check_starttime.c \ check_starttime.o: $(srcdir)/regress/starttime/check_starttime.c \
$(devdir)/def_data.h $(incdir)/compat/stdbool.h \ $(incdir)/compat/stdbool.h $(incdir)/sudo_compat.h \
$(incdir)/sudo_compat.h $(incdir)/sudo_conf.h \ $(incdir)/sudo_fatal.h $(incdir)/sudo_util.h \
$(incdir)/sudo_debug.h $(incdir)/sudo_fatal.h \ $(srcdir)/check.h $(top_builddir)/config.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
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(ASAN_CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(srcdir)/regress/starttime/check_starttime.c $(CC) -c $(CPPFLAGS) $(CFLAGS) $(ASAN_CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(srcdir)/regress/starttime/check_starttime.c
check_symbols.o: $(srcdir)/regress/check_symbols/check_symbols.c \ check_symbols.o: $(srcdir)/regress/check_symbols/check_symbols.c \
$(incdir)/compat/stdbool.h $(incdir)/sudo_compat.h \ $(incdir)/compat/stdbool.h $(incdir)/sudo_compat.h \
@@ -1250,6 +1249,14 @@ toke_util.lo: $(srcdir)/toke_util.c $(devdir)/def_data.h $(devdir)/gram.h \
$(top_builddir)/config.h $(top_builddir)/pathnames.h $(top_builddir)/config.h $(top_builddir)/pathnames.h
$(LIBTOOL) $(LTFLAGS) --mode=compile $(CC) -c $(CPPFLAGS) $(CFLAGS) $(ASAN_CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(srcdir)/toke_util.c $(LIBTOOL) $(LTFLAGS) --mode=compile $(CC) -c $(CPPFLAGS) $(CFLAGS) $(ASAN_CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(srcdir)/toke_util.c
toke_util.o: toke_util.lo toke_util.o: toke_util.lo
tsdump.o: $(srcdir)/tsdump.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
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(ASAN_CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(srcdir)/tsdump.c
tsgetgrpw.o: $(srcdir)/tsgetgrpw.c $(devdir)/def_data.h \ tsgetgrpw.o: $(srcdir)/tsgetgrpw.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

@@ -46,6 +46,20 @@
#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 */
struct timestamp_entry_v1 {
unsigned short version; /* version number */
unsigned short size; /* entry size */
unsigned short type; /* TS_GLOBAL, TS_TTY, TS_PPID */
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 ts; /* time stamp (CLOCK_MONOTONIC) */
union {
dev_t ttydev; /* tty device number */
pid_t ppid; /* parent pid */
} u;
};
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 */

294
plugins/sudoers/tsdump.c Normal file
View File

@@ -0,0 +1,294 @@
/*
* Copyright (c) 2018 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.
* 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 <sys/types.h>
#include <sys/stat.h>
#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 <errno.h>
#include <fcntl.h>
#include <pwd.h>
#include <time.h>
#include <unistd.h>
#include "sudoers.h"
#include "check.h"
struct timestamp_entry_common {
unsigned short version; /* version number */
unsigned short size; /* entry size */
unsigned short type; /* TS_GLOBAL, TS_TTY, TS_PPID */
unsigned short flags; /* TS_DISABLED, TS_ANYUID */
};
union timestamp_entry_storage {
struct timestamp_entry_common common;
struct timestamp_entry_v1 v1;
struct timestamp_entry v2;
};
__dso_public int main(int argc, char *argv[]);
static void usage(void) __attribute__((__noreturn__));
static void dump_entry(union timestamp_entry_storage *u, off_t pos);
static bool valid_entry(union timestamp_entry_storage *u, off_t pos);
/*
* tsdump: a simple utility to dump the contents of a time stamp file.
* Unlock sudo, does not perform any locking of the time stamp file.
*/
int
main(int argc, char *argv[])
{
int ch, fd;
const char *user = NULL;
char *fname = NULL;
union timestamp_entry_storage cur;
debug_decl(main, SUDOERS_DEBUG_MAIN)
#if defined(SUDO_DEVEL) && defined(__OpenBSD__)
malloc_options = "S";
#endif
initprogname(argc > 0 ? argv[0] : "tsdump");
bindtextdomain("sudoers", LOCALEDIR);
textdomain("sudoers");
/* Initialize the debug subsystem. */
if (sudo_conf_read(NULL, SUDO_CONF_DEBUG) == -1)
exit(EXIT_FAILURE);
sudoers_debug_register(getprogname(), sudo_conf_debug_files(getprogname()));
while ((ch = getopt(argc, argv, "f:u:")) != -1) {
switch (ch) {
case 'f':
fname = optarg;
break;
case 'u':
user = optarg;
break;
default:
usage();
}
}
argc -= optind;
argv += optind;
if (fname != NULL && user != NULL) {
sudo_warnx("the -f and -u flags are mutually exclusive");
usage();
}
if (fname == NULL) {
struct passwd *pw;
if (user == NULL) {
if ((pw = getpwuid(geteuid())) == NULL)
sudo_fatalx(U_("unknown uid: %d"), (int)geteuid());
user = pw->pw_name;
}
if (asprintf(&fname, "%s/%s", _PATH_SUDO_TIMEDIR, user) == -1)
sudo_fatalx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
}
fd = open(fname, O_RDONLY);
if (fd == -1)
sudo_fatal(U_("unable to open %s"), fname);
for (;;) {
off_t pos = lseek(fd, 0, SEEK_CUR);
ssize_t nread;
bool valid;
if ((nread = read(fd, &cur, sizeof(cur))) == 0)
break;
if (nread == -1)
sudo_fatal(U_("unable to read %s"), fname);
valid = valid_entry(&cur, pos);
if (cur.common.size != 0 && cur.common.size != sizeof(cur)) {
off_t offset = (off_t)cur.common.size - (off_t)sizeof(cur);
if (lseek(fd, offset, SEEK_CUR) == -1)
sudo_fatal("unable to seek %d bytes", (int)offset);
}
if (valid)
dump_entry(&cur, pos);
}
return 0;
}
static bool
valid_entry(union timestamp_entry_storage *u, off_t pos)
{
struct timestamp_entry *entry = (struct timestamp_entry *)u;
debug_decl(valid_entry, SUDOERS_DEBUG_UTIL)
switch (entry->version) {
case 1:
if (entry->size != sizeof(struct timestamp_entry_v1)) {
printf("wrong sized v1 record @ %lld, got %hu, expected %zu\n",
(long long)pos, entry->size, sizeof(struct timestamp_entry_v1));
debug_return_bool(false);
}
break;
case 2:
if (entry->size != sizeof(struct timestamp_entry)) {
printf("wrong sized v2 record @ %lld, got %hu, expected %zu\n",
(long long)pos, entry->size, sizeof(struct timestamp_entry));
debug_return_bool(false);
}
break;
default:
printf("unknown time stamp entry version %d @ %lld\n",
entry->version, (long long)pos);
debug_return_bool(false);
break;
}
debug_return_bool(true);
}
static char *
type2string(int type)
{
static char name[64];
debug_decl(type2string, SUDOERS_DEBUG_UTIL)
switch (type) {
case TS_LOCKEXCL:
debug_return_str("TS_LOCKEXCL");
case TS_GLOBAL:
debug_return_str("TS_GLOBAL");
case TS_TTY:
debug_return_str("TS_TTY");
case TS_PPID:
debug_return_str("TS_PPID");
}
snprintf(name, sizeof(name), "UNKNOWN (0x%x)", type);
debug_return_str(name);
}
static void
print_flags(int flags)
{
bool first = true;
debug_decl(print_flags, SUDOERS_DEBUG_UTIL)
printf("flags: ");
if (ISSET(flags, TS_DISABLED)) {
printf("%sTS_DISABLED", first ? "" : ", ");
CLR(flags, TS_DISABLED);
first = false;
}
if (ISSET(flags, TS_ANYUID)) {
/* TS_ANYUID should never appear on disk. */
printf("%sTS_ANYUID", first ? "" : ", ");
CLR(flags, TS_ANYUID);
first = false;
}
if (flags != 0)
printf("%s0x%x", first ? "" : ", ", flags);
putchar('\n');
debug_return;
}
/*
* Convert an older entry to current.
*/
static bool
convert_entry(union timestamp_entry_storage *record)
{
union timestamp_entry_storage orig;
debug_decl(convert_entry, SUDOERS_DEBUG_UTIL)
if (record->common.version != 1) {
sudo_warnx("unexpected record version %hu", record->common.version);
debug_return_bool(false);
}
/* The first four fields are the same regardless of version. */
memcpy(&orig, record, sizeof(union timestamp_entry_storage));
record->v2.auth_uid = orig.v1.auth_uid;
record->v2.sid = orig.v1.sid;
sudo_timespecclear(&record->v2.start_time);
record->v2.ts = orig.v1.ts;
if (record->common.type == TS_TTY)
record->v2.u.ttydev = orig.v1.u.ttydev;
else if (record->common.type == TS_PPID)
record->v2.u.ppid = orig.v1.u.ppid;
else
memset(&record->v2.u, 0, sizeof(record->v2.u));
debug_return_bool(true);
}
static void
dump_entry(union timestamp_entry_storage *u, off_t pos)
{
struct timestamp_entry *entry = (struct timestamp_entry *)u;
debug_decl(dump_entry, SUDOERS_DEBUG_UTIL)
/* Convert to latest version as needed. */
if (u->common.version != TS_VERSION) {
if (!convert_entry(u))
debug_return;
}
printf("position: %lld\n", (long long)pos);
printf("version: %hu\n", entry->version);
printf("size: %hu\n", entry->size);
printf("type: %s\n", type2string(entry->type));
print_flags(entry->flags);
printf("auth uid: %d\n", (int)entry->auth_uid);
printf("session ID: %d\n", (int)entry->sid);
if (sudo_timespecisset(&entry->start_time))
printf("start time: %s", ctime(&entry->start_time.tv_sec));
if (sudo_timespecisset(&entry->ts))
printf("time stamp: %s", ctime(&entry->ts.tv_sec));
if (entry->type == TS_TTY) {
char tty[PATH_MAX];
if (sudo_ttyname_dev(entry->u.ttydev, tty, sizeof(tty)) == NULL)
printf("terminal: %d\n", (int)entry->u.ttydev);
else
printf("terminal: %s\n", tty);
} else if (entry->type == TS_PPID) {
printf("parent pid: %d\n", (int)entry->u.ppid);
}
printf("\n");
debug_return;
}
static void
usage(void)
{
fprintf(stderr, "usage: %s [-f timestamp_file] | [-u username]\n",
getprogname());
exit(1);
}

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2012-2017 Todd C. Miller <Todd.Miller@sudo.ws> * Copyright (c) 2012-2018 Todd C. Miller <Todd.Miller@sudo.ws>
* *
* Permission to use, copy, modify, and distribute this software for any * Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above * purpose with or without fee is hereby granted, provided that the above
@@ -43,10 +43,8 @@
#include <limits.h> #include <limits.h>
#include <dirent.h> #include <dirent.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) #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/param.h> /* for makedev/major/minor */
# include <sys/sysctl.h> # include <sys/sysctl.h>
#elif defined(HAVE_STRUCT_KINFO_PROC_KI_TDEV) #elif defined(HAVE_STRUCT_KINFO_PROC_KI_TDEV)
# include <sys/param.h> /* for makedev/major/minor */
# include <sys/sysctl.h> # include <sys/sysctl.h>
# include <sys/user.h> # include <sys/user.h>
#endif #endif
@@ -56,7 +54,6 @@
# include <sys/procfs.h> # include <sys/procfs.h>
#endif #endif
#ifdef HAVE_PSTAT_GETPROC #ifdef HAVE_PSTAT_GETPROC
# include <sys/param.h> /* for makedev/major/minor */
# include <sys/pstat.h> # include <sys/pstat.h>
#endif #endif
@@ -87,271 +84,6 @@
# define sudo_kp_namelen 4 # define sudo_kp_namelen 4
#endif #endif
#if defined(sudo_kp_tdev)
/*
* Like ttyname() but uses a dev_t instead of an open fd.
* Returns name on success and NULL on failure, setting errno.
* The BSD version uses devname().
*/
static char *
sudo_ttyname_dev(dev_t tdev, char *name, size_t namelen)
{
char *dev;
debug_decl(sudo_ttyname_dev, SUDO_DEBUG_UTIL)
/* Some versions of devname() return NULL on failure, others do not. */
dev = devname(tdev, S_IFCHR);
if (dev != NULL && *dev != '?' && *dev != '#') {
if (strlcpy(name, _PATH_DEV, namelen) < namelen &&
strlcat(name, dev, namelen) < namelen)
debug_return_str(name);
errno = ERANGE;
} else {
/* Not all versions of devname() set errno. */
errno = ENOENT;
}
debug_return_str(NULL);
}
#elif defined(HAVE__TTYNAME_DEV)
extern char *_ttyname_dev(dev_t rdev, char *buffer, size_t buflen);
/*
* Like ttyname() but uses a dev_t instead of an open fd.
* Returns name on success and NULL on failure, setting errno.
* This version is just a wrapper around _ttyname_dev().
*/
static char *
sudo_ttyname_dev(dev_t tdev, char *name, size_t namelen)
{
int serrno = errno;
debug_decl(sudo_ttyname_dev, SUDO_DEBUG_UTIL)
/*
* _ttyname_dev() sets errno to ERANGE if namelen is too small
* but does not modify it if tdev is not found.
*/
errno = ENOENT;
if (_ttyname_dev(tdev, name, namelen) == NULL)
debug_return_str(NULL);
errno = serrno;
debug_return_str(name);
}
#elif defined(HAVE_STRUCT_PSINFO_PR_TTYDEV) || defined(HAVE_PSTAT_GETPROC) || defined(__linux__)
/*
* Device nodes to ignore.
*/
static const char *ignore_devs[] = {
_PATH_DEV "stdin",
_PATH_DEV "stdout",
_PATH_DEV "stderr",
NULL
};
/*
* Do a scan of a directory looking for the specified device.
* Does not descend into subdirectories.
* Returns name on success and NULL on failure, setting errno.
*/
static char *
sudo_ttyname_scan(const char *dir, dev_t rdev, char *name, size_t namelen)
{
size_t sdlen;
char pathbuf[PATH_MAX];
char *ret = NULL;
struct dirent *dp;
struct stat sb;
unsigned int i;
DIR *d = NULL;
debug_decl(sudo_ttyname_scan, SUDO_DEBUG_UTIL)
if (dir[0] == '\0') {
errno = ENOENT;
goto done;
}
if ((d = opendir(dir)) == NULL)
goto done;
if (fstat(dirfd(d), &sb) == -1) {
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
"unable to fstat %s", dir);
goto done;
}
if ((sb.st_mode & S_IWOTH) != 0) {
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
"ignoring world-writable directory %s", dir);
errno = ENOENT;
goto done;
}
sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO,
"scanning for dev %u in %s", (unsigned int)rdev, dir);
sdlen = strlen(dir);
while (sdlen > 0 && dir[sdlen - 1] == '/')
sdlen--;
if (sdlen + 1 >= sizeof(pathbuf)) {
errno = ERANGE;
goto done;
}
memcpy(pathbuf, dir, sdlen);
pathbuf[sdlen++] = '/';
while ((dp = readdir(d)) != NULL) {
struct stat sb;
/* Skip anything starting with "." */
if (dp->d_name[0] == '.')
continue;
pathbuf[sdlen] = '\0';
if (strlcat(pathbuf, dp->d_name, sizeof(pathbuf)) >= sizeof(pathbuf)) {
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
"%s%s is too big to fit in pathbuf", pathbuf, dp->d_name);
continue;
}
/* Ignore device nodes listed in ignore_devs[]. */
for (i = 0; ignore_devs[i] != NULL; i++) {
if (strcmp(pathbuf, ignore_devs[i]) == 0)
break;
}
if (ignore_devs[i] != NULL) {
sudo_debug_printf(SUDO_DEBUG_DEBUG|SUDO_DEBUG_LINENO,
"ignoring %s", pathbuf);
continue;
}
# if defined(HAVE_STRUCT_DIRENT_D_TYPE)
/*
* Avoid excessive stat() calls by checking dp->d_type.
*/
switch (dp->d_type) {
case DT_CHR:
case DT_LNK:
case DT_UNKNOWN:
break;
default:
/* Not a character device or link, skip it. */
sudo_debug_printf(SUDO_DEBUG_DEBUG|SUDO_DEBUG_LINENO,
"skipping non-device %s", pathbuf);
continue;
}
# endif
if (stat(pathbuf, &sb) == -1) {
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO|SUDO_DEBUG_ERRNO,
"unable to stat %s", pathbuf);
continue;
}
if (S_ISCHR(sb.st_mode) && sb.st_rdev == rdev) {
sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO,
"resolved dev %u as %s", (unsigned int)rdev, pathbuf);
if (strlcpy(name, pathbuf, namelen) < namelen) {
ret = name;
} else {
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
"unable to store %s, have %zu, need %zu",
pathbuf, namelen, strlen(pathbuf) + 1);
errno = ERANGE;
}
goto done;
}
}
done:
if (d != NULL)
closedir(d);
debug_return_str(ret);
}
static char *
sudo_dev_check(dev_t rdev, const char *devname, char *buf, size_t buflen)
{
struct stat sb;
debug_decl(sudo_dev_check, SUDO_DEBUG_UTIL)
if (stat(devname, &sb) == 0) {
if (S_ISCHR(sb.st_mode) && sb.st_rdev == rdev) {
sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO,
"comparing dev %u to %s: match!",
(unsigned int)rdev, devname);
if (strlcpy(buf, devname, buflen) < buflen)
debug_return_str(buf);
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
"unable to store %s, have %zu, need %zu",
devname, buflen, strlen(devname) + 1);
errno = ERANGE;
}
}
sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO,
"comparing dev %u to %s: no", (unsigned int)rdev, devname);
debug_return_str(NULL);
}
/*
* Like ttyname() but uses a dev_t instead of an open fd.
* Returns name on success and NULL on failure, setting errno.
* Generic version.
*/
static char *
sudo_ttyname_dev(dev_t rdev, char *buf, size_t buflen)
{
const char *devsearch, *devsearch_end;
char path[PATH_MAX], *ret;
const char *cp, *ep;
size_t len;
debug_decl(sudo_ttyname_dev, SUDO_DEBUG_UTIL)
/*
* First, check /dev/console.
*/
ret = sudo_dev_check(rdev, _PATH_DEV "console", buf, buflen);
if (ret != NULL)
goto done;
/*
* Then check the device search path.
*/
devsearch = sudo_conf_devsearch_path();
devsearch_end = devsearch + strlen(devsearch);
for (cp = sudo_strsplit(devsearch, devsearch_end, ":", &ep);
cp != NULL; cp = sudo_strsplit(NULL, devsearch_end, ":", &ep)) {
len = (size_t)(ep - cp);
if (len >= sizeof(path)) {
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
"devsearch entry %.*s too long", (int)len, cp);
continue;
}
memcpy(path, cp, len);
path[len] = '\0';
if (strcmp(path, _PATH_DEV "pts") == 0) {
/* Special case /dev/pts */
len = (size_t)snprintf(path, sizeof(path), "%spts/%u",
_PATH_DEV, (unsigned int)minor(rdev));
if (len >= sizeof(path)) {
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
"devsearch entry %spts/%u too long",
_PATH_DEV, (unsigned int)minor(rdev));
continue;
}
ret = sudo_dev_check(rdev, path, buf, buflen);
if (ret != NULL)
goto done;
} else {
/* Scan path, looking for rdev. */
ret = sudo_ttyname_scan(path, rdev, buf, buflen);
if (ret != NULL || errno == ENOMEM)
goto done;
}
}
done:
debug_return_str(ret);
}
#endif
#if defined(sudo_kp_tdev) #if defined(sudo_kp_tdev)
/* /*
* Store the name of the tty to which the process is attached in name. * Store the name of the tty to which the process is attached in name.