summaryrefslogtreecommitdiffstats
path: root/plugins/sudoers/tsdump.c
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/sudoers/tsdump.c')
-rw-r--r--plugins/sudoers/tsdump.c316
1 files changed, 316 insertions, 0 deletions
diff --git a/plugins/sudoers/tsdump.c b/plugins/sudoers/tsdump.c
new file mode 100644
index 0000000..91a26ce
--- /dev/null
+++ b/plugins/sudoers/tsdump.c
@@ -0,0 +1,316 @@
+/*
+ * 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.
+ */
+
+/*
+ * This is an open source non-commercial project. Dear PVS-Studio, please check it.
+ * PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
+ */
+
+#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(struct timestamp_entry *entry, off_t pos);
+static bool valid_entry(union timestamp_entry_storage *u, off_t pos);
+static bool convert_entry(union timestamp_entry_storage *record, struct timespec *off);
+
+/*
+ * 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;
+ struct timespec now, timediff;
+ 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();
+ }
+
+ /* Calculate the difference between real time and mono time. */
+ if (sudo_gettime_real(&now) == -1)
+ sudo_fatal("unable to get current time");
+ if (sudo_gettime_mono(&timediff) == -1)
+ sudo_fatal("unable to read the clock");
+ sudo_timespecsub(&now, &timediff, &timediff);
+
+ if (fname == NULL) {
+ struct passwd *pw;
+
+ if (user == NULL) {
+ if ((pw = getpwuid(geteuid())) == NULL)
+ sudo_fatalx(U_("unknown uid: %u"), (unsigned 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) {
+ /* Convert entry to latest version as needed. */
+ if (!convert_entry(&cur, &timediff))
+ continue;
+ dump_entry(&cur.v2, 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",
+ (int)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.
+ * Also adjusts time stamps on Linux to be wallclock time.
+ */
+static bool
+convert_entry(union timestamp_entry_storage *record, struct timespec *off)
+{
+ union timestamp_entry_storage orig;
+ debug_decl(convert_entry, SUDOERS_DEBUG_UTIL)
+
+ if (record->common.version != TS_VERSION) {
+ 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));
+ }
+
+ /* On Linux, start time is relative to boot time, adjust to real time. */
+#ifdef __linux__
+ if (sudo_timespecisset(&record->v2.start_time))
+ sudo_timespecadd(&record->v2.start_time, off, &record->v2.start_time);
+#endif
+
+ /* Adjust time stamp from mono time to real time. */
+ if (sudo_timespecisset(&record->v2.ts))
+ sudo_timespecadd(&record->v2.ts, off, &record->v2.ts);
+
+ debug_return_bool(true);
+}
+
+static void
+dump_entry(struct timestamp_entry *entry, off_t pos)
+{
+ debug_decl(dump_entry, SUDOERS_DEBUG_UTIL)
+
+ 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);
+}