opendoas

my fork of doas with custom prompt
git clone git://git.kocotian.pl/opendoas.git
Log | Files | Refs | README | LICENSE

commit 1899c37ea48dd1932edd913c510411ee4c9c242f
parent 4dc52ebd1dc0b2e619ea3cdbb14b46a3f3286732
Author: Duncaen <mail@duncano.de>
Date:   Mon, 11 Dec 2017 20:20:57 +0100

add initial timestamp file support, disabled by default and only with shadow auth

Diffstat:
Mconfigure | 24++++++++++++++++++++++++
Mdoas.c | 19+++++++++++++++++++
Mincludes.h | 6++++++
Apersist_timestamp.c | 217+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 266 insertions(+), 0 deletions(-)

diff --git a/configure b/configure @@ -27,11 +27,16 @@ usage: configure [options] --without-pam disable pam support --without-shadow disable shadow support + --without-timestamp disable timestamp support + --help, -h display this help and exit EOF exit 0 } +# defaults +WITHOUT_TIMESTAMP=yes + for x; do opt=${x%%=*} var=${x#*=} @@ -52,6 +57,8 @@ for x; do --with-shadow) WITHOUT_SHADOW=; WITHOUT_PAM=yes ;; --without-pam) WITHOUT_PAM=yes ;; --without-shadow) WITHOUT_SHADOW=yes ;; + --with-timestamp) WITHOUT_TIMESTAMP= ;; + --without-timestamp) WITHOUT_TIMESTAMP=yes ;; --help|-h) usage ;; *) die "Error: unknown option $opt" ;; esac @@ -187,6 +194,16 @@ int main(void) { return 1 } +persistmethod() { + [ -z "$WITHOUT_TIMESTAMP" ] && { + printf 'CFLAGS += -DPERSIST_TIMESTAMP\n' >>$CONFIG_MK + printf 'SRCS += persist_timestamp.c\n' >>$CONFIG_MK + printf 'timestamp\n' + return 0 + } + return 1 +} + # # Check for explicit_bzero(). # @@ -478,3 +495,10 @@ else printf 'Error auth method\t\t\n' >&2 exit 1 fi + +persist=$(persistmethod) +if [ $? -eq 0 ]; then + printf 'Using persist method\t\t\t%s.\n' "$persist" >&2 +else + printf 'Using persist method\t\t\tnone.\n' >&2 +fi diff --git a/doas.c b/doas.c @@ -261,7 +261,17 @@ authuser(const char *myname, const char *login_style, int persist) struct passwd *pw; (void)login_style; + +#ifdef PERSIST_TIMESTAMP + int fd = -1; + int valid; + if (persist) + fd = persist_open(&valid, 5 * 60); + if (fd != -1 && valid) + goto good; +#else (void)persist; +#endif if (!(pw = getpwnam(myname))) err(1, "getpwnam"); @@ -296,6 +306,13 @@ authuser(const char *myname, const char *login_style, int persist) errx(1, "Authorization failed"); } explicit_bzero(rbuf, sizeof(rbuf)); +#ifdef PERSIST_TIMESTAMP +good: + if (fd != -1) { + persist_set(fd, 5 * 60); + close(fd); + } +#endif } #endif /* HAVE_BSD_AUTH_H */ @@ -353,6 +370,8 @@ main(int argc, char **argv) if (i != -1) ioctl(i, TIOCCLRVERAUTH); exit(i == -1); +#elif PERSIST_TIMESTAMP + exit(persist_clear() != 0); #endif case 'u': if (parseuid(optarg, &target) != 0) diff --git a/includes.h b/includes.h @@ -23,4 +23,10 @@ int pamauth(const char *, const char *, int, int); #endif +#ifdef PERSIST_TIMESTAMP +int persist_open(int *, int); +int persist_set(int, int); +int persist_clear(); +#endif + #endif /* INCLUDES_H */ diff --git a/persist_timestamp.c b/persist_timestamp.c @@ -0,0 +1,217 @@ +#include <ctype.h> +#include <err.h> +#include <errno.h> +#include <fcntl.h> +#include <libgen.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <unistd.h> + +#include <sys/stat.h> +#include <sys/vfs.h> + +#ifndef TIMESTAMP_DIR +# define TIMESTAMP_DIR "/tmp/doas" +#endif +#ifndef TMPFS_MAGIC +# define TMPFS_MAGIC 0x01021994 +#endif + +#define timespecisset(tsp) ((tsp)->tv_sec || (tsp)->tv_nsec) +#define timespeccmp(tsp, usp, cmp) \ + (((tsp)->tv_sec == (usp)->tv_sec) ? \ + ((tsp)->tv_nsec cmp (usp)->tv_nsec) : \ + ((tsp)->tv_sec cmp (usp)->tv_sec)) +#define timespecadd(tsp, usp, vsp) do { \ + (vsp)->tv_sec = (tsp)->tv_sec + (usp)->tv_sec; \ + (vsp)->tv_nsec = (tsp)->tv_nsec + (usp)->tv_nsec; \ + if ((vsp)->tv_nsec >= 1000000000L) { \ + (vsp)->tv_sec++; \ + (vsp)->tv_nsec -= 1000000000L; \ + } \ + } while (0) + +static char * +tspath() +{ + char *path, *tty, *ttynorm, *p; + if (!(tty = ttyname(0)) + && !(tty = ttyname(1)) + && !(tty = ttyname(2))) + err(1, "ttyname"); + if (!(ttynorm = strdup(tty))) + err(1, "strdup"); + for (p = ttynorm; *p; p++) + if (!isalnum(*p)) + *p = '_'; + if (asprintf(&path, "%s/.%s_%d", TIMESTAMP_DIR, ttynorm, getppid()) == -1) + errx(1, "asprintf"); + free(ttynorm); + return path; +} + +static int +checktsdir(const char *path) +{ + char *dir, *buf; + struct stat st; + struct statfs sf; + gid_t gid; + + if (!(buf = strdup(path))) + err(1, "strdup"); + dir = dirname(buf); + +check: + if (lstat(dir, &st) == -1) { + if (errno == ENOENT) { + gid = getegid(); + if (setegid(0) != 0) + err(1, "setegid"); + if (mkdir(dir, (S_IRUSR|S_IWUSR|S_IXUSR)) != 0) + err(1, "mkdir"); + if (setegid(gid) != 0) + err(1, "setegid"); + goto check; + } else { + err(1, "stat"); + } + } + + if ((st.st_mode & S_IFMT) != S_IFDIR) + errx(1, "timestamp directory is not a directory"); + if ((st.st_mode & (S_IWGRP|S_IRGRP|S_IWOTH|S_IROTH)) != 0) + errx(1, "timestamp directory permissions wrong"); + if (st.st_uid != 0 || st.st_gid != 0) + errx(1, "timestamp directory is not owned by root"); + if (statfs(dir, &sf) == -1) + err(1, "statfs"); + if (sf.f_type != TMPFS_MAGIC) + errx(1, "timestamp directory not on tmpfs"); + + free(buf); + return 0; +} + +static int +timestamp_read(int fd, struct timespec *mono, struct timespec *real) +{ + if (read(fd, (void *)mono, sizeof *mono) != sizeof *mono || + read(fd, (void *)real, sizeof *real) != sizeof *mono) + err(1, "read"); + if (!timespecisset(mono) || !timespecisset(real)) + errx(1, "timespecisset"); + return 0; +} + +int +persist_check(int fd, int secs) +{ + struct timespec mono, real, ts_mono, ts_real, timeout; + + if (timestamp_read(fd, &ts_mono, &ts_real) != 0) + return 1; + + if (clock_gettime(CLOCK_MONOTONIC, &mono) != 0 || !timespecisset(&mono)) + err(1, "clock_gettime(CLOCK_MONOTONIC, ?)"); + if (clock_gettime(CLOCK_REALTIME, &real) != 0 || !timespecisset(&real)) + err(1, "clock_gettime(CLOCK_REALTIME, ?)"); + + if (timespeccmp(&mono, &ts_mono, >) || + timespeccmp(&real, &ts_real, >)) + return 1; + + memset(&timeout, 0, sizeof timeout); + timeout.tv_sec = secs; + timespecadd(&timeout, &mono, &mono); + timespecadd(&timeout, &real, &real); + + if (timespeccmp(&mono, &ts_mono, <) || + timespeccmp(&real, &ts_real, <)) + errx(1, "timestamp is too far in the future"); + + return 0; +} + +int +persist_set(int fd, int secs) +{ + struct timespec mono, real, ts_mono, ts_real, timeout; + + if (clock_gettime(CLOCK_MONOTONIC, &mono) != 0 || !timespecisset(&mono)) + err(1, "clock_gettime(XLOCK_MONOTONIC, ?)"); + if (clock_gettime(CLOCK_REALTIME, &real) != 0 || !timespecisset(&real)) + err(1, "clock_gettime(CLOCK_REALTIME, ?)"); + + memset(&timeout, 0, sizeof timeout); + timeout.tv_sec = secs; + timespecadd(&timeout, &mono, &ts_mono); + timespecadd(&timeout, &real, &ts_real); + + if (lseek(fd, 0, SEEK_SET) == -1) + err(1, "lseek"); + if (write(fd, (void *)&ts_mono, sizeof ts_mono) != sizeof ts_mono || + write(fd, (void *)&ts_real, sizeof ts_real) != sizeof ts_real) + err(1, "write"); + + return 0; +} + +int +persist_open(int *valid, int secs) +{ + struct stat st; + int fd; + gid_t gid; + const char *path; + + path = tspath(); + if (checktsdir(path)) + errx(1, "checktsdir"); + + if ((fd = open(path, (O_RDWR), (S_IRUSR|S_IWUSR))) == -1) + if (errno != ENOENT) + err(1, "open: %s", path); + + if (fd == -1) { + if ((fd = open(path, (O_RDWR|O_CREAT|O_EXCL), (S_IRUSR|S_IWUSR))) == -1) + err(1, "open: %s", path); + *valid = 0; + return fd; + } + + if (fstat(fd, &st) == -1) + err(1, "stat"); + if ((st.st_mode & S_IFMT) != S_IFREG) + errx(1, "timestamp is not a file"); + if ((st.st_mode & (S_IWGRP|S_IRGRP|S_IXGRP|S_IWOTH|S_IROTH|S_IXOTH)) != 0) + errx(1, "timestamp permissions wrong"); + + gid = getegid(); + if (st.st_uid != 0 || st.st_gid != gid) + errx(1, "timestamp has wrong owner"); + + if (st.st_size == 0) { + *valid = 0; + return fd; + } + + if (st.st_size != sizeof(struct timespec) * 2) + errx(1, "corrupt timestamp file"); + + *valid = persist_check(fd, secs) == 0; + + return fd; +} + +int +persist_clear() +{ + const char *path; + path = tspath(); + if (unlink(path) == -1 && errno != ENOENT) + return -1; + return 0; +}