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:
2
MANIFEST
2
MANIFEST
@@ -178,6 +178,7 @@ lib/util/sudo_conf.c
|
||||
lib/util/sudo_debug.c
|
||||
lib/util/sudo_dso.c
|
||||
lib/util/term.c
|
||||
lib/util/ttyname_dev.c
|
||||
lib/util/ttysize.c
|
||||
lib/util/util.exp.in
|
||||
lib/util/utimens.c
|
||||
@@ -540,6 +541,7 @@ plugins/sudoers/toke.c
|
||||
plugins/sudoers/toke.h
|
||||
plugins/sudoers/toke.l
|
||||
plugins/sudoers/toke_util.c
|
||||
plugins/sudoers/tsdump.c
|
||||
plugins/sudoers/tsgetgrpw.c
|
||||
plugins/sudoers/tsgetgrpw.h
|
||||
plugins/sudoers/visudo.c
|
||||
|
@@ -195,6 +195,9 @@
|
||||
don't. */
|
||||
#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'.
|
||||
*/
|
||||
#undef HAVE_DIRENT_H
|
||||
|
11
configure
vendored
11
configure
vendored
@@ -19252,6 +19252,17 @@ if test "x$ac_cv_func_sysctl" = xyes; then :
|
||||
cat >>confdefs.h <<_ACEOF
|
||||
#define HAVE_SYSCTL 1
|
||||
_ACEOF
|
||||
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/sysctl.h>
|
||||
|
@@ -2528,8 +2528,8 @@ if test "$utmp_style" = "LEGACY"; then
|
||||
AC_CHECK_FUNCS([fseeko])
|
||||
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_proc.p_tdev], [], [
|
||||
AC_CHECK_MEMBERS([struct kinfo_proc.kp_eproc.e_tdev], [], [], [
|
||||
|
@@ -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);
|
||||
#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 */
|
||||
__dso_public void sudo_get_ttysize_v1(int *rowp, int *colp);
|
||||
#define sudo_get_ttysize(_a, _b) sudo_get_ttysize_v1((_a), (_b))
|
||||
|
@@ -111,8 +111,8 @@ SHELL = @SHELL@
|
||||
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 \
|
||||
setgroups.lo strsplit.lo strtobool.lo strtoid.lo strtomode.lo \
|
||||
sudo_conf.lo sudo_debug.lo sudo_dso.lo term.lo ttysize.lo \
|
||||
@COMMON_OBJS@ @LTLIBOBJS@
|
||||
sudo_conf.lo sudo_debug.lo sudo_dso.lo term.lo ttyname_dev.lo \
|
||||
ttysize.lo @COMMON_OBJS@ @LTLIBOBJS@
|
||||
|
||||
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 \
|
||||
$(top_builddir)/config.h
|
||||
$(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 \
|
||||
$(incdir)/sudo_compat.h $(incdir)/sudo_debug.h \
|
||||
$(incdir)/sudo_queue.h $(incdir)/sudo_util.h \
|
||||
|
311
lib/util/ttyname_dev.c
Normal file
311
lib/util/ttyname_dev.c
Normal 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
|
@@ -60,6 +60,7 @@ sudo_fatal_callback_deregister_v1
|
||||
sudo_fatal_callback_register_v1
|
||||
sudo_fatal_nodebug_v1
|
||||
sudo_fatalx_nodebug_v1
|
||||
sudo_ttyname_dev_v1
|
||||
sudo_get_ttysize_v1
|
||||
sudo_gethostname_v1
|
||||
sudo_gettime_mono_v1
|
||||
|
@@ -172,6 +172,8 @@ REPLAY_OBJS = getdate.o sudoreplay.o
|
||||
TEST_OBJS = group_plugin.o interfaces.o locale.o net_ifs.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 \
|
||||
sudo_printf.o
|
||||
|
||||
@@ -245,6 +247,9 @@ sudoreplay: timestr.lo $(REPLAY_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)
|
||||
|
||||
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)
|
||||
$(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
|
||||
$(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 \
|
||||
$(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
|
||||
$(incdir)/compat/stdbool.h $(incdir)/sudo_compat.h \
|
||||
$(incdir)/sudo_fatal.h $(incdir)/sudo_util.h \
|
||||
$(srcdir)/check.h $(top_builddir)/config.h
|
||||
$(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 \
|
||||
$(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
|
||||
$(LIBTOOL) $(LTFLAGS) --mode=compile $(CC) -c $(CPPFLAGS) $(CFLAGS) $(ASAN_CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(srcdir)/toke_util.c
|
||||
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 \
|
||||
$(incdir)/compat/stdbool.h $(incdir)/sudo_compat.h \
|
||||
$(incdir)/sudo_conf.h $(incdir)/sudo_debug.h \
|
||||
|
@@ -46,6 +46,20 @@
|
||||
#define TS_DISABLED 0x01 /* entry disabled */
|
||||
#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 {
|
||||
unsigned short version; /* version number */
|
||||
unsigned short size; /* entry size */
|
||||
|
294
plugins/sudoers/tsdump.c
Normal file
294
plugins/sudoers/tsdump.c
Normal 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);
|
||||
}
|
270
src/ttyname.c
270
src/ttyname.c
@@ -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
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
@@ -43,10 +43,8 @@
|
||||
#include <limits.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)
|
||||
# include <sys/param.h> /* for makedev/major/minor */
|
||||
# include <sys/sysctl.h>
|
||||
#elif defined(HAVE_STRUCT_KINFO_PROC_KI_TDEV)
|
||||
# include <sys/param.h> /* for makedev/major/minor */
|
||||
# include <sys/sysctl.h>
|
||||
# include <sys/user.h>
|
||||
#endif
|
||||
@@ -56,7 +54,6 @@
|
||||
# include <sys/procfs.h>
|
||||
#endif
|
||||
#ifdef HAVE_PSTAT_GETPROC
|
||||
# include <sys/param.h> /* for makedev/major/minor */
|
||||
# include <sys/pstat.h>
|
||||
#endif
|
||||
|
||||
@@ -87,271 +84,6 @@
|
||||
# define sudo_kp_namelen 4
|
||||
#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)
|
||||
/*
|
||||
* Store the name of the tty to which the process is attached in name.
|
||||
|
Reference in New Issue
Block a user