From 861d30f4b90c5312b33358caea08e8e26fc24818 Mon Sep 17 00:00:00 2001 From: Duncaen Date: Fri, 6 May 2016 03:00:16 +0200 Subject: [PATCH 001/198] Add configure script --- .gitignore | 2 + LICENSE | 1 + Makefile | 4 - bsd.prog.mk | 20 +-- configure | 300 ++++++++++++++++++++++++++++++++++++ doas.c | 2 +- includes.h | 22 +++ libopenbsd/auth_userokay.c | 5 +- libopenbsd/execvpe.c | 2 + libopenbsd/openbsd.h | 42 ++++- libopenbsd/setusercontext.c | 3 +- pam.d__doas__linux | 10 ++ 12 files changed, 392 insertions(+), 21 deletions(-) create mode 100755 configure create mode 100644 includes.h create mode 100644 pam.d__doas__linux diff --git a/.gitignore b/.gitignore index 8d285f1..5ec596b 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,5 @@ version.h *.swp *.swo + +config.mk diff --git a/LICENSE b/LICENSE index 0c16551..2494fc0 100644 --- a/LICENSE +++ b/LICENSE @@ -1,5 +1,6 @@ portions copyright (c) 2015 Nathan Holstein portions copyright (c) 2015 Ted Unangst +portions copyright (c) 2016 Duncan Overbruck To the best of my knowledge, everything is released under the BSD license. diff --git a/Makefile b/Makefile index 9e1dbf3..3c8f8c5 100644 --- a/Makefile +++ b/Makefile @@ -16,7 +16,3 @@ LDFLAGS+= -lpam include bsd.prog.mk doas.o: version.h - -/etc/pam.d/doas: pam.d__doas - cp $< $@ -install: /etc/pam.d/doas diff --git a/bsd.prog.mk b/bsd.prog.mk index 80d3231..6c70ad7 100644 --- a/bsd.prog.mk +++ b/bsd.prog.mk @@ -1,12 +1,9 @@ # Copyright 2015 Nathan Holstein -BINDIR?=/usr/bin -MANDIR?=/usr/share/man - default: ${PROG} -OPENBSD:=reallocarray.c strtonum.c execvpe.c setresuid.c \ - auth_userokay.c setusercontext.c explicit_bzero.c +include config.mk + OPENBSD:=$(addprefix libopenbsd/,${OPENBSD:.c=.o}) libopenbsd.a: ${OPENBSD} ${AR} -r $@ $? @@ -21,15 +18,18 @@ ${PROG}: ${OBJS} libopenbsd.a .%.chmod: % cp $< $@ - chmod ${BINMODE} $@ chown ${BINOWN}:${BINGRP} $@ + chmod ${BINMODE} $@ -${BINDIR}: +${DESTRDIR}${BINDIR} ${DESTRDIR}${PAMDIR}: mkdir -pm 0755 $@ -${BINDIR}/${PROG}: .${PROG}.chmod ${BINDIR} +${DESTDIR}${BINDIR}/${PROG}: .${PROG}.chmod ${BINDIR} mv $< $@ +${DESTDIR}${PAMDIR}/doas: ${PAM_DOAS} + cp $< $@ + VERSION:=\#define VERSION "$(shell git describe --dirty --tags --long --always)" OLDVERSION:=$(shell [ -f version.h ] && cat version.h) version.h: ; @echo '$(VERSION)' > $@ @@ -37,10 +37,10 @@ ifneq ($(VERSION),$(OLDVERSION)) .PHONY: version.h endif -MAN:=$(join $(addprefix ${MANDIR}/man,$(patsubst .%,%/,$(suffix ${MAN}))),${MAN}) +MAN:=$(join $(addprefix ${DESTDIR}${MANDIR}/man,$(patsubst .%,%/,$(suffix ${MAN}))),${MAN}) $(foreach M,${MAN},$(eval $M: $(notdir $M); cp $$< $$@)) -install: ${BINDIR}/${PROG} ${MAN} +install: ${DESTDIR}${BINDIR}/${PROG} ${DESTDIR}${PAMDIR}/doas ${MAN} clean: rm -f version.h diff --git a/configure b/configure new file mode 100755 index 0000000..73d56cd --- /dev/null +++ b/configure @@ -0,0 +1,300 @@ +#!/bin/sh + +for x; do + opt=${x%%=*} + var=${x#*=} + case "$opt" in + --enable-debug) DEBUG=yes;; + --prefix) PREFIX=$var;; + --exec-prefix) EPREFIX=$var;; + --bindir) BINDIR=$var;; + --mandir) MANDIR=$var;; + --datadir) SHAREDIR=$var;; + --build) BUILD=$var;; + --host) HOST=$var;; + --target) TARGET=$var;; + --includedir) INCLUDEDIR=$var;; + --sysconfdir) SYSCONFDIR=$var;; + --pamdir) PAMDIR=$var;; + --localstatedir) LOCALSTATEDIR=$var;; + --libdir) LIBDIR=$var;; + --datadir|--infodir) ;; # ignore autotools + --verbose) unset SILENT;; + --pkgconfigdir) PKGCONFIGDIR=$var;; + --enable-static) BUILD_STATIC=yes;; + --enable-seccomp) BUILD_SECCOMP=yes;; + --help) usage;; + *) echo "$0: WARNING: unknown option $opt" >&2;; + esac +done + +CONFIG_MK=config.mk +rm -f "$CONFIG_MK" + +cat <>$CONFIG_MK +DESTDIR ?= / +PREFIX ?= ${PREFIX:="/usr"} +EPREFIX ?= ${EPREFIX:="${PREFIX}"} +SHAREDIR ?= ${SHAREDIR:="${PREFIX}/share"} +BINDIR ?= ${BINDIR:="${PREFIX}/bin"} +MANDIR ?= ${MANDIR:="${SHAREDIR}/man"} +SYSCONFDIR?= ${SYSCONFDIR:="/etc"} +PAMDIR ?= ${PAMDIR:="${SYSCONFDIR}/pam.d"} +EOF + +if [ -z "$BUILD" ]; then + BUILD="$(uname -m)-unknown-$(uname -s | tr '[:upper:]' '[:lower:]')" +fi +if [ -z "$HOST" ]; then + [ -z "$TARGET" ] && TARGET=$BUILD + HOST=$TARGET +fi +if [ -z "$TARGET" ]; then + [ -z "$HOST" ] && HOST=$BUILD + TARGET=$HOST +fi + +if [ -z "$OS" ]; then + # Derive OS from cpu-manufacturer-os-kernel + CPU=${TARGET%%-*} + REST=${TARGET#*-} + MANU=${REST%%-*} + REST=${REST#*-} + OS=${REST%%-*} + REST=${REST#*-} + KERNEL=${REST%%-*} +fi + +case "$OS" in + linux) + OS_CFLAGS="-D_DEFAULT_SOURCE -D_GNU_SOURCE -DUID_MAX=60000 -DGID_MAX=60000" + printf 'CURDIR := .\n' >>$CONFIG_MK + printf 'PAM_DOAS = pam.d__doas__linux\n' >>$CONFIG_MK + ;; +esac + +[ -n "$OS_CFLAGS" ] && \ + printf 'CFLAGS += %s\n' "$OS_CFLAGS" >>$CONFIG_MK + +# Add CPPFLAGS/CFLAGS/LDFLAGS to CC for testing features +XCC="${CC:=clang} $CFLAGS $OS_CFLAGS $CPPFLAGS $LDFLAGS" +# Make sure to disable --as-needed for CC tests. +XCC="$XCC -Wl,--no-as-needed" + +check_func() { + func="$1"; src="$2"; shift 2 + printf 'Checking for %-14s\t\t' "$func ..." + printf '%s\n' "$src" >"_$func.c" + if $XCC "_$func.c" -o "_$func" 2>/dev/null; then + printf 'yes.\n' + upperfunc="$(printf '%s\n' "$func" | tr '[[:lower:]]' '[[:upper:]]')" + printf 'CFLAGS += -DHAVE_%s\n' "$upperfunc" >>$CONFIG_MK + else + printf 'no.\n' + fi + rm -f "_$func.c" "_$func" +} + +src=' +#include +int main(void) { + explicit_bzero(NULL, 0); + return 0; +}' +check_func "explicit_bzero" "$src" || { + printf 'OPENBSD += explicit_bzero.c\n' >>$CONFIG_MK +} + + +# +# Check for strlcat(). +# +src=' +#include +int main(void) { + const char s1[] = "foo"; + char s2[10]; + strlccat(s2, s1, sizeof(s2)); + return 0; +}' +check_func "strlcat" "$src" || { + printf 'OPENBSD += strlcat.c\n' >>$CONFIG_MK +} + +# +# Check for strlcpy(). +# +src=' +#include +int main(void) { + const char s1[] = "foo"; + char s2[10]; + strlcpy(s2, s1, sizeof(s2)); + return 0; +}' +check_func "strlcpy" "$src" || { + printf 'OPENBSD += strlcpy.c\n' >>$CONFIG_MK +} + +# +# Check for errc(). +# +src=' +#include +int main(void) { + errc(0, 0, ""); + return 0; +}' +check_func "errc" "$src" || { + printf 'OPENBSD += errc.c\n' >>$CONFIG_MK +} + +# +# Check for verrc(). +# +src=' +#include +int main(void) { + verrc(0, 0, ""); + return 0; +}' +check_func "verrc" "$src" || { + printf 'OPENBSD += verrc.c\n' >>$CONFIG_MK +} + +# +# Check for setprogname(). +# +src=' +#include +int main(void) { + setprogname(""); + return 0; +}' +check_func "setprogname" "$src" || { + printf 'OPENBSD += progname.c\n' >>$CONFIG_MK +} + +# +# Check for readpassphrase(). +# +src=' +#include +int main(void) { + char buf[12]; + readpassphrase("", buf, sizeof(buf), 0); + return 0; +}' +check_func "readpassphrase" "$src" || { + printf 'OPENBSD += readpassphrase.c\n' >>$CONFIG_MK +} + +# +# Check for strtonum(). +# +src=' +#include +int main(void) { + const char *errstr; + strtonum("", 1, 64, &errstr); + return 0; +}' +check_func "strtonum" "$src" || { + printf 'OPENBSD += strtonum.c\n' >>$CONFIG_MK +} + +# +# Check for reallocarray(). +# +src=' +#include +int main(void) { + reallocarray(NULL, 0, 0); + return 0; +}' +check_func "reallocarray" "$src" || { + printf 'OPENBSD += reallocarray.c\n' >>$CONFIG_MK +} + +# +# Check for bsd_auth.h. +# +src=' +#include +int main(void) { + return 0; +}' +check_func "bsd_auth_h" "$src" || { + printf 'OPENBSD += auth_userokay.c\n' >>$CONFIG_MK +} + +# +# Check for login_cap.h. +# +src=' +#include +int main(void) { + return 0; +}' +check_func "login_cap_h" "$src" || { + printf 'OPENBSD += setusercontext.c\n' >>$CONFIG_MK +} + +# +# Check for execvpe(). +# +src=' +#include +int main(void) { + const char *p = { "", NULL }; + execvpe("", p, p); + return 0; +}' +check_func "execvpe" "$src" || { + printf 'OPENBSD += execvpe.c\n' >>$CONFIG_MK +} + +# +# Check for setresuid(). +# +src=' +#include +int main(void) { + setresuid(0, 0, 0); + return 0; +}' +check_func "setresuid" "$src" || { + printf 'OPENBSD += setresuid.c\n' >>$CONFIG_MK +} + +# +# Check for pledge(). +# +src=' +#include +int main(void) { + pledge("", NULL); + return 0; +}' +check_func "pledge" "$src" && { + have_pledge=1 +} + +# +# Check for seccomp.h +# +src=' +#include +#include +#include +int main(void) { + prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, NULL); + return 0; +}' +if [ -n "$have_pledge" -a -n "$BUILD_SECCOMP" ]; then + check_func "seccomp_h" "$src" && { + printf 'OPENBSD += pledge-seccomp.c\n' >>$CONFIG_MK + } +elif [ -n "$have_pledge" ]; then + printf 'OPENBSD += pledge-noop.c\n' >>$CONFIG_MK +fi diff --git a/doas.c b/doas.c index 1457925..04affe7 100644 --- a/doas.c +++ b/doas.c @@ -29,7 +29,7 @@ #include #include -#include "openbsd.h" +#include "includes.h" #include "doas.h" #include "version.h" diff --git a/includes.h b/includes.h new file mode 100644 index 0000000..b08e93c --- /dev/null +++ b/includes.h @@ -0,0 +1,22 @@ +#ifndef INCLUDES_H +#define INCLUDES_H + +#ifndef __UNUSED +# define __UNUSED __attribute__ ((unused)) +#endif + +#ifndef __dead +# define __dead +#endif + +#ifndef _PATH_TTY +# define _PATH_TTY "/dev/tty" +#endif + +#ifdef HAVE_READPASSPHRASE_H +# include +#endif + +#include "openbsd.h" + +#endif /* INCLUDES_H */ diff --git a/libopenbsd/auth_userokay.c b/libopenbsd/auth_userokay.c index 5565146..465cb1c 100644 --- a/libopenbsd/auth_userokay.c +++ b/libopenbsd/auth_userokay.c @@ -18,19 +18,16 @@ #include #include #include -#include #include #include #include #include -#include "openbsd.h" +#include "includes.h" #define PAM_SERVICE "doas" -#define __UNUSED __attribute__ ((unused)) - static char * pam_prompt(const char *msg, int echo_on, int *pam) { diff --git a/libopenbsd/execvpe.c b/libopenbsd/execvpe.c index f080148..4ddad3e 100644 --- a/libopenbsd/execvpe.c +++ b/libopenbsd/execvpe.c @@ -40,6 +40,8 @@ #include #include +#include "includes.h" + int execvpe(const char *name, char *const *argv, char *const *envp) { diff --git a/libopenbsd/openbsd.h b/libopenbsd/openbsd.h index 1fa73af..1e5ff97 100644 --- a/libopenbsd/openbsd.h +++ b/libopenbsd/openbsd.h @@ -1,14 +1,21 @@ #ifndef _LIB_OPENBSD_H_ #define _LIB_OPENBSD_H_ +#include #include +#include + +#include "readpassphrase.h" /* API definitions lifted from OpenBSD src/include */ /* bsd_auth.h */ +#ifndef HAVE_BSD_AUTH_H int auth_userokay(char *, char *, char *, char *); +#endif /* !HAVE_BSD_AUTH_H */ /* login_cap.h */ +#ifndef HAVE_LOGIN_CAP_H #define LOGIN_SETGROUP 0x0001 /* Set group */ #define LOGIN_SETLOGIN 0x0002 /* Set login */ #define LOGIN_SETPATH 0x0004 /* Set path */ @@ -22,20 +29,53 @@ int auth_userokay(char *, char *, char *, char *); typedef struct login_cap login_cap_t; struct passwd; int setusercontext(login_cap_t *, struct passwd *, uid_t, unsigned int); +#endif /* !HAVE_LOGIN_CAP_H */ /* pwd.h */ #define _PW_NAME_LEN 63 /* stdlib.h */ +#ifndef HAVE_REALLOCARRAY void * reallocarray(void *optr, size_t nmemb, size_t size); +#endif /* HAVE_REALLOCARRAY */ +#ifndef HAVE_STRTONUM long long strtonum(const char *numstr, long long minval, long long maxval, const char **errstrp); +#endif /* !HAVE_STRTONUM */ /* string.h */ +#ifndef HAVE_EXPLICIT_BZERO void explicit_bzero(void *, size_t); +#endif +#ifndef HAVE_STRLCAT +size_t strlcat(char *dst, const char *src, size_t dsize); +#endif /* !HAVE_STRLCAT */ +#ifndef HAVE_STRLCPY +size_t strlcpy(char *dst, const char *src, size_t dsize); +#endif /* !HAVE_STRLCPY */ /* unistd.h */ +#ifndef HAVE_EXECVPE int execvpe(const char *, char *const *, char *const *); +#endif /* !HAVE_EXECVPE */ +#ifndef HAVE_SETRESUID int setresuid(uid_t, uid_t, uid_t); +#endif /* !HAVE_SETRESUID */ +#ifndef HAVE_PLEDGE +int pledge(const char *promises, const char *paths[]); +#endif /* !HAVE_PLEDGE */ -#endif +/* err.h */ +#ifndef HAVE_VERRC +void verrc(int eval, int code, const char *fmt, va_list ap); +#endif /* !HAVE_VERRC */ +#ifndef HAVE_ERRC +void errc(int eval, int code, const char *fmt, ...); +#endif /* !HAVE_ERRC */ + +#ifndef HAVE_SETPROGNAME +const char * getprogname(void); +void setprogname(const char *progname); +#endif /* !HAVE_SETPROGNAME */ + +#endif /* _LIB_OPENBSD_H_ */ diff --git a/libopenbsd/setusercontext.c b/libopenbsd/setusercontext.c index a6a9aef..6b05dd5 100644 --- a/libopenbsd/setusercontext.c +++ b/libopenbsd/setusercontext.c @@ -21,8 +21,9 @@ #include #include #include +#include -#include "openbsd.h" +#include "includes.h" int setusercontext(login_cap_t *lc, struct passwd *pw, uid_t uid, unsigned int flags) diff --git a/pam.d__doas__linux b/pam.d__doas__linux new file mode 100644 index 0000000..9781fb2 --- /dev/null +++ b/pam.d__doas__linux @@ -0,0 +1,10 @@ +#%PAM-1.0 +auth sufficient pam_timestamp.so timestamp_timeout=300 verbose debug +auth sufficient pam_rootok.so +auth required pam_unix.so +account required pam_unix.so +session optional pam_xauth.so +session optional pam_umask.so usergroups umask=022 +session optional pam_timestamp.so timestamp_timeout=300 debug +session required pam_env.so +session required pam_unix.so From a424761ab8312a14f52e42e32ae11654d55d722b Mon Sep 17 00:00:00 2001 From: Duncaen Date: Fri, 6 May 2016 03:35:18 +0200 Subject: [PATCH 002/198] Sync doas.c --- doas.c | 100 ++++++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 78 insertions(+), 22 deletions(-) diff --git a/doas.c b/doas.c index 04affe7..643d3d8 100644 --- a/doas.c +++ b/doas.c @@ -1,4 +1,4 @@ -/* $OpenBSD: doas.c,v 1.33 2015/07/30 17:04:33 tedu Exp $ */ +/* $OpenBSD: doas.c,v 1.52 2016/04/28 04:48:56 tedu Exp $ */ /* * Copyright (c) 2015 Ted Unangst * @@ -44,7 +44,8 @@ version(void) static void __dead usage(void) { - fprintf(stderr, "usage: doas [-nsv] [-C config] [-u user] command [args]\n"); + fprintf(stderr, "usage: doas [-nsv] [-a style] [-C config] [-u user]" + " command [args]\n"); exit(1); } @@ -171,10 +172,9 @@ parseconfig(const char *filename, int checkperms) struct stat sb; yyfp = fopen(filename, "r"); - if (!yyfp) { - warn("could not open config file"); - exit(1); - } + if (!yyfp) + err(1, checkperms ? "doas is not enabled, %s" : + "could not open config file %s", filename); if (checkperms) { if (fstat(fileno(yyfp), &sb) != 0) @@ -290,13 +290,6 @@ copyenv(const char **oldenvp, struct rule *rule) return envp; } -static void __dead -fail(void) -{ - fprintf(stderr, "Permission denied\n"); - exit(1); -} - static void __dead checkconfig(const char *confpath, int argc, char **argv, uid_t uid, gid_t *groups, int ngroups, uid_t target) @@ -339,11 +332,24 @@ main(int argc, char **argv, char **envp) int sflag = 0; int nflag = 0; int vflag = 0; + char cwdpath[PATH_MAX]; + const char *cwd; + char *login_style = NULL; + + setprogname("doas"); + + if (pledge("stdio rpath getpw tty proc exec id", NULL) == -1) + err(1, "pledge"); + + /* closefrom(STDERR_FILENO + 1); */ uid = getuid(); - while ((ch = getopt(argc, argv, "C:nsu:v")) != -1) { + while ((ch = getopt(argc, argv, "a:C:nsu:v")) != -1) { switch (ch) { + case 'a': + login_style = optarg; + break; case 'C': confpath = optarg; break; @@ -419,32 +425,82 @@ main(int argc, char **argv, char **envp) (const char**)argv + 1)) { syslog(LOG_AUTHPRIV | LOG_NOTICE, "failed command for %s: %s", myname, cmdline); - fail(); + errc(1, EPERM, NULL); } if (!(rule->options & NOPASS)) { if (nflag) errx(1, "Authorization required"); + +#ifdef HAVE_BSD_AUTH_H + char *challenge = NULL, *response, rbuf[1024], cbuf[128]; + auth_session_t *as; + + if (!(as = auth_userchallenge(myname, login_style, "auth-doas", + &challenge))) + errx(1, "Authorization failed"); + if (!challenge) { + char host[HOST_NAME_MAX + 1]; + if (gethostname(host, sizeof(host))) + snprintf(host, sizeof(host), "?"); + snprintf(cbuf, sizeof(cbuf), + "\rdoas (%.32s@%.32s) password: ", myname, host); + challenge = cbuf; + } + response = readpassphrase(challenge, rbuf, sizeof(rbuf), + RPP_REQUIRE_TTY); + if (response == NULL && errno == ENOTTY) { + syslog(LOG_AUTHPRIV | LOG_NOTICE, + "tty required for %s", myname); + errx(1, "a tty is required"); + } + if (!auth_userresponse(as, response, 0)) { + syslog(LOG_AUTHPRIV | LOG_NOTICE, + "failed auth for %s", myname); + errc(1, EPERM, NULL); + } + explicit_bzero(rbuf, sizeof(rbuf)); +#else if (!auth_userokay(myname, NULL, NULL, NULL)) { syslog(LOG_AUTHPRIV | LOG_NOTICE, - "failed password for %s", myname); - fail(); + "failed auth for %s", myname); + errc(1, EPERM, NULL); } +#endif /* HAVE_BSD_AUTH_H */ } - envp = copyenv((const char **)envp, rule); + + if (pledge("stdio rpath getpw exec id", NULL) == -1) + err(1, "pledge"); pw = getpwuid(target); if (!pw) errx(1, "no passwd entry for target"); + if (setusercontext(NULL, pw, target, LOGIN_SETGROUP | LOGIN_SETPRIORITY | LOGIN_SETRESOURCES | LOGIN_SETUMASK | LOGIN_SETUSER) != 0) errx(1, "failed to set user context for target"); - syslog(LOG_AUTHPRIV | LOG_INFO, "%s ran command as %s: %s", - myname, pw->pw_name, cmdline); - if (setenv("PATH", safepath, 1) == -1) - err(1, "failed to set PATH '%s'", safepath); + if (pledge("stdio rpath exec", NULL) == -1) + err(1, "pledge"); + + if (getcwd(cwdpath, sizeof(cwdpath)) == NULL) + cwd = "(failed)"; + else + cwd = cwdpath; + + if (pledge("stdio exec", NULL) == -1) + err(1, "pledge"); + + syslog(LOG_AUTHPRIV | LOG_INFO, "%s ran command %s as %s from %s", + myname, cmdline, pw->pw_name, cwd); + + envp = copyenv((const char **)envp, rule); + + if (rule->cmd) { + if (setenv("PATH", safepath, 1) == -1) + err(1, "failed to set PATH '%s'", safepath); + } execvpe(cmd, argv, envp); if (errno == ENOENT) errx(1, "%s: command not found", cmd); From e0dd9ee353048757566541f18e04a96e2c76a479 Mon Sep 17 00:00:00 2001 From: Duncaen Date: Fri, 6 May 2016 03:37:58 +0200 Subject: [PATCH 003/198] Actually open pam sessions --- libopenbsd/auth_userokay.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/libopenbsd/auth_userokay.c b/libopenbsd/auth_userokay.c index 465cb1c..6a9841b 100644 --- a/libopenbsd/auth_userokay.c +++ b/libopenbsd/auth_userokay.c @@ -26,7 +26,7 @@ #include "includes.h" -#define PAM_SERVICE "doas" +#define PAM_SERVICE_NAME "doas" static char * pam_prompt(const char *msg, int echo_on, int *pam) @@ -93,17 +93,20 @@ auth_userokay(char *name, char *style, char *type, char *password) if (style || type || password) errx(1, "auth_userokay(name, NULL, NULL, NULL)!\n"); - ret = pam_start(PAM_SERVICE, name, &conv, &pamh); + ret = pam_start(PAM_SERVICE_NAME, name, &conv, &pamh); if (ret != PAM_SUCCESS) errx(1, "pam_start(\"%s\", \"%s\", ?, ?): failed\n", - PAM_SERVICE, name); + PAM_SERVICE_NAME, name); auth = pam_authenticate(pamh, 0); + ret = pam_open_session(pamh, 0); + if (ret != PAM_SUCCESS) + errx(1, "pam_open_session(): %s\n", pam_strerror(pamh, ret)); + ret = pam_close_session(pamh, 0); if (ret != PAM_SUCCESS) errx(1, "pam_close_session(): %s\n", pam_strerror(pamh, ret)); return auth == PAM_SUCCESS; } - From 2d34633b1527cfc264a746b6a04b326cbffe8797 Mon Sep 17 00:00:00 2001 From: Duncaen Date: Fri, 6 May 2016 03:40:33 +0200 Subject: [PATCH 004/198] Add more compatibility functions for linux support --- libopenbsd/errc.c | 44 ++++++++ libopenbsd/pledge-noop.c | 7 ++ libopenbsd/progname.c | 68 ++++++++++++ libopenbsd/readpassphrase.c | 213 ++++++++++++++++++++++++++++++++++++ libopenbsd/readpassphrase.h | 44 ++++++++ libopenbsd/strlcat.c | 55 ++++++++++ libopenbsd/strlcpy.c | 50 +++++++++ libopenbsd/verrc.c | 49 +++++++++ 8 files changed, 530 insertions(+) create mode 100644 libopenbsd/errc.c create mode 100644 libopenbsd/pledge-noop.c create mode 100644 libopenbsd/progname.c create mode 100644 libopenbsd/readpassphrase.c create mode 100644 libopenbsd/readpassphrase.h create mode 100644 libopenbsd/strlcat.c create mode 100644 libopenbsd/strlcpy.c create mode 100644 libopenbsd/verrc.c diff --git a/libopenbsd/errc.c b/libopenbsd/errc.c new file mode 100644 index 0000000..8e8474b --- /dev/null +++ b/libopenbsd/errc.c @@ -0,0 +1,44 @@ +/* $OpenBSD: errc.c,v 1.2 2015/08/31 02:53:57 guenther Exp $ */ +/*- + * Copyright (c) 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include + +#include "openbsd.h" + +__dead void +errc(int eval, int code, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + verrc(eval, code, fmt, ap); + va_end(ap); +} diff --git a/libopenbsd/pledge-noop.c b/libopenbsd/pledge-noop.c new file mode 100644 index 0000000..0a1b610 --- /dev/null +++ b/libopenbsd/pledge-noop.c @@ -0,0 +1,7 @@ +#include "openbsd.h" + +int +pledge(__UNUSED const char *promises, __UNUSED const char *paths[]) +{ + return 0; +} diff --git a/libopenbsd/progname.c b/libopenbsd/progname.c new file mode 100644 index 0000000..10c3701 --- /dev/null +++ b/libopenbsd/progname.c @@ -0,0 +1,68 @@ +/* + * Copyright © 2006 Robert Millan + * Copyright © 2010-2012 Guillem Jover + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Rejected in glibc + * . + */ + +#include +#include +#include + +#ifdef HAVE___PROGNAME +extern const char *__progname; +#else +static const char *__progname = NULL; +#endif + +const char * +getprogname(void) +{ +#if defined(HAVE_PROGRAM_INVOCATION_SHORT_NAME) + if (__progname == NULL) + __progname = program_invocation_short_name; +#elif defined(HAVE_GETEXECNAME) + /* getexecname(3) returns an absolute pathname, normalize it. */ + if (__progname == NULL) + setprogname(getexecname()); +#endif + + return __progname; +} + +void +setprogname(const char *progname) +{ + const char *last_slash; + + last_slash = strrchr(progname, '/'); + if (last_slash == NULL) + __progname = progname; + else + __progname = last_slash + 1; +} diff --git a/libopenbsd/readpassphrase.c b/libopenbsd/readpassphrase.c new file mode 100644 index 0000000..d63cdf2 --- /dev/null +++ b/libopenbsd/readpassphrase.c @@ -0,0 +1,213 @@ +/* $OpenBSD: readpassphrase.c,v 1.22 2010/01/13 10:20:54 dtucker Exp $ */ + +/* + * Copyright (c) 2000-2002, 2007 Todd C. Miller + * + * 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. + * + * Sponsored in part by the Defense Advanced Research Projects + * Agency (DARPA) and Air Force Research Laboratory, Air Force + * Materiel Command, USAF, under agreement number F39502-99-1-0512. + */ + +/* OPENBSD ORIGINAL: lib/libc/gen/readpassphrase.c */ + +#include "includes.h" + +#ifndef HAVE_READPASSPHRASE + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef TCSASOFT +# define _T_FLUSH (TCSAFLUSH|TCSASOFT) +#else +# define _T_FLUSH (TCSAFLUSH) +#endif + +/* SunOS 4.x which lacks _POSIX_VDISABLE, but has VDISABLE */ +#if !defined(_POSIX_VDISABLE) && defined(VDISABLE) +# define _POSIX_VDISABLE VDISABLE +#endif + +#ifndef _NSIG +# ifdef NSIG +# define _NSIG NSIG +# else +# define _NSIG 128 +# endif +#endif + +static volatile sig_atomic_t signo[_NSIG]; + +static void handler(int); + +char * +readpassphrase(const char *prompt, char *buf, size_t bufsiz, int flags) +{ + ssize_t nr; + int input, output, save_errno, i, need_restart; + char ch, *p, *end; + struct termios term, oterm; + struct sigaction sa, savealrm, saveint, savehup, savequit, saveterm; + struct sigaction savetstp, savettin, savettou, savepipe; + + /* I suppose we could alloc on demand in this case (XXX). */ + if (bufsiz == 0) { + errno = EINVAL; + return(NULL); + } + +restart: + for (i = 0; i < _NSIG; i++) + signo[i] = 0; + nr = -1; + save_errno = 0; + need_restart = 0; + /* + * Read and write to /dev/tty if available. If not, read from + * stdin and write to stderr unless a tty is required. + */ + if ((flags & RPP_STDIN) || + (input = output = open(_PATH_TTY, O_RDWR)) == -1) { + if (flags & RPP_REQUIRE_TTY) { + errno = ENOTTY; + return(NULL); + } + input = STDIN_FILENO; + output = STDERR_FILENO; + } + + /* + * Catch signals that would otherwise cause the user to end + * up with echo turned off in the shell. Don't worry about + * things like SIGXCPU and SIGVTALRM for now. + */ + sigemptyset(&sa.sa_mask); + sa.sa_flags = 0; /* don't restart system calls */ + sa.sa_handler = handler; + (void)sigaction(SIGALRM, &sa, &savealrm); + (void)sigaction(SIGHUP, &sa, &savehup); + (void)sigaction(SIGINT, &sa, &saveint); + (void)sigaction(SIGPIPE, &sa, &savepipe); + (void)sigaction(SIGQUIT, &sa, &savequit); + (void)sigaction(SIGTERM, &sa, &saveterm); + (void)sigaction(SIGTSTP, &sa, &savetstp); + (void)sigaction(SIGTTIN, &sa, &savettin); + (void)sigaction(SIGTTOU, &sa, &savettou); + + /* Turn off echo if possible. */ + if (input != STDIN_FILENO && tcgetattr(input, &oterm) == 0) { + memcpy(&term, &oterm, sizeof(term)); + if (!(flags & RPP_ECHO_ON)) + term.c_lflag &= ~(ECHO | ECHONL); +#ifdef VSTATUS + if (term.c_cc[VSTATUS] != _POSIX_VDISABLE) + term.c_cc[VSTATUS] = _POSIX_VDISABLE; +#endif + (void)tcsetattr(input, _T_FLUSH, &term); + } else { + memset(&term, 0, sizeof(term)); + term.c_lflag |= ECHO; + memset(&oterm, 0, sizeof(oterm)); + oterm.c_lflag |= ECHO; + } + + /* No I/O if we are already backgrounded. */ + if (signo[SIGTTOU] != 1 && signo[SIGTTIN] != 1) { + if (!(flags & RPP_STDIN)) + (void)write(output, prompt, strlen(prompt)); + end = buf + bufsiz - 1; + p = buf; + while ((nr = read(input, &ch, 1)) == 1 && ch != '\n' && ch != '\r') { + if (p < end) { + if ((flags & RPP_SEVENBIT)) + ch &= 0x7f; + if (isalpha(ch)) { + if ((flags & RPP_FORCELOWER)) + ch = (char)tolower(ch); + if ((flags & RPP_FORCEUPPER)) + ch = (char)toupper(ch); + } + *p++ = ch; + } + } + *p = '\0'; + save_errno = errno; + if (!(term.c_lflag & ECHO)) + (void)write(output, "\n", 1); + } + + /* Restore old terminal settings and signals. */ + if (memcmp(&term, &oterm, sizeof(term)) != 0) { + while (tcsetattr(input, _T_FLUSH, &oterm) == -1 && + errno == EINTR) + continue; + } + (void)sigaction(SIGALRM, &savealrm, NULL); + (void)sigaction(SIGHUP, &savehup, NULL); + (void)sigaction(SIGINT, &saveint, NULL); + (void)sigaction(SIGQUIT, &savequit, NULL); + (void)sigaction(SIGPIPE, &savepipe, NULL); + (void)sigaction(SIGTERM, &saveterm, NULL); + (void)sigaction(SIGTSTP, &savetstp, NULL); + (void)sigaction(SIGTTIN, &savettin, NULL); + (void)sigaction(SIGTTOU, &savettou, NULL); + if (input != STDIN_FILENO) + (void)close(input); + + /* + * If we were interrupted by a signal, resend it to ourselves + * now that we have restored the signal handlers. + */ + for (i = 0; i < _NSIG; i++) { + if (signo[i]) { + kill(getpid(), i); + switch (i) { + case SIGTSTP: + case SIGTTIN: + case SIGTTOU: + need_restart = 1; + } + } + } + if (need_restart) + goto restart; + + if (save_errno) + errno = save_errno; + return(nr == -1 ? NULL : buf); +} + +#if 0 +char * +getpass(const char *prompt) +{ + static char buf[_PASSWORD_LEN + 1]; + + return(readpassphrase(prompt, buf, sizeof(buf), RPP_ECHO_OFF)); +} +#endif + +static void handler(int s) +{ + + signo[s] = 1; +} +#endif /* HAVE_READPASSPHRASE */ diff --git a/libopenbsd/readpassphrase.h b/libopenbsd/readpassphrase.h new file mode 100644 index 0000000..5fd7c5d --- /dev/null +++ b/libopenbsd/readpassphrase.h @@ -0,0 +1,44 @@ +/* $OpenBSD: readpassphrase.h,v 1.5 2003/06/17 21:56:23 millert Exp $ */ + +/* + * Copyright (c) 2000, 2002 Todd C. Miller + * + * 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. + * + * Sponsored in part by the Defense Advanced Research Projects + * Agency (DARPA) and Air Force Research Laboratory, Air Force + * Materiel Command, USAF, under agreement number F39502-99-1-0512. + */ + +/* OPENBSD ORIGINAL: include/readpassphrase.h */ + +#ifndef _READPASSPHRASE_H_ +#define _READPASSPHRASE_H_ + +#include "includes.h" + +#ifndef HAVE_READPASSPHRASE + +#define RPP_ECHO_OFF 0x00 /* Turn off echo (default). */ +#define RPP_ECHO_ON 0x01 /* Leave echo on. */ +#define RPP_REQUIRE_TTY 0x02 /* Fail if there is no tty. */ +#define RPP_FORCELOWER 0x04 /* Force input to lower case. */ +#define RPP_FORCEUPPER 0x08 /* Force input to upper case. */ +#define RPP_SEVENBIT 0x10 /* Strip the high bit from input. */ +#define RPP_STDIN 0x20 /* Read from stdin, not /dev/tty */ + +char * readpassphrase(const char *, char *, size_t, int); + +#endif /* HAVE_READPASSPHRASE */ + +#endif /* !_READPASSPHRASE_H_ */ diff --git a/libopenbsd/strlcat.c b/libopenbsd/strlcat.c new file mode 100644 index 0000000..2596420 --- /dev/null +++ b/libopenbsd/strlcat.c @@ -0,0 +1,55 @@ +/* $OpenBSD: strlcat.c,v 1.16 2015/08/31 02:53:57 guenther Exp $ */ + +/* + * Copyright (c) 1998, 2015 Todd C. Miller + * + * 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 +#include + +/* + * Appends src to string dst of size dsize (unlike strncat, dsize is the + * full size of dst, not space left). At most dsize-1 characters + * will be copied. Always NUL terminates (unless dsize <= strlen(dst)). + * Returns strlen(src) + MIN(dsize, strlen(initial dst)). + * If retval >= dsize, truncation occurred. + */ +size_t +strlcat(char *dst, const char *src, size_t dsize) +{ + const char *odst = dst; + const char *osrc = src; + size_t n = dsize; + size_t dlen; + + /* Find the end of dst and adjust bytes left but don't go past end. */ + while (n-- != 0 && *dst != '\0') + dst++; + dlen = dst - odst; + n = dsize - dlen; + + if (n-- == 0) + return(dlen + strlen(src)); + while (*src != '\0') { + if (n != 0) { + *dst++ = *src; + n--; + } + src++; + } + *dst = '\0'; + + return(dlen + (src - osrc)); /* count does not include NUL */ +} diff --git a/libopenbsd/strlcpy.c b/libopenbsd/strlcpy.c new file mode 100644 index 0000000..6301674 --- /dev/null +++ b/libopenbsd/strlcpy.c @@ -0,0 +1,50 @@ +/* $OpenBSD: strlcpy.c,v 1.13 2015/08/31 02:53:57 guenther Exp $ */ + +/* + * Copyright (c) 1998, 2015 Todd C. Miller + * + * 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 +#include + +/* + * Copy string src to buffer dst of size dsize. At most dsize-1 + * chars will be copied. Always NUL terminates (unless dsize == 0). + * Returns strlen(src); if retval >= dsize, truncation occurred. + */ +size_t +strlcpy(char *dst, const char *src, size_t dsize) +{ + const char *osrc = src; + size_t nleft = dsize; + + /* Copy as many bytes as will fit. */ + if (nleft != 0) { + while (--nleft != 0) { + if ((*dst++ = *src++) == '\0') + break; + } + } + + /* Not enough room in dst, add NUL and traverse rest of src. */ + if (nleft == 0) { + if (dsize != 0) + *dst = '\0'; /* NUL-terminate dst */ + while (*src++) + ; + } + + return(src - osrc - 1); /* count does not include NUL */ +} diff --git a/libopenbsd/verrc.c b/libopenbsd/verrc.c new file mode 100644 index 0000000..e00fcd1 --- /dev/null +++ b/libopenbsd/verrc.c @@ -0,0 +1,49 @@ +/* $OpenBSD: verrc.c,v 1.3 2016/03/13 18:34:20 guenther Exp $ */ +/*- + * Copyright (c) 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include + +#include "openbsd.h" + +__dead void +verrc(int eval, int code, const char *fmt, va_list ap) +{ + (void)fprintf(stderr, "%s: ", getprogname()); + if (fmt != NULL) { + (void)vfprintf(stderr, fmt, ap); + (void)fprintf(stderr, ": "); + } + (void)fprintf(stderr, "%s\n", strerror(code)); + exit(eval); +} From 8558e5d1f9162b66bf09820ae1281522700fe283 Mon Sep 17 00:00:00 2001 From: Duncaen Date: Fri, 6 May 2016 03:41:13 +0200 Subject: [PATCH 005/198] Testing only seccomp pledge --- libopenbsd/pledge-seccomp.c | 411 ++++++++++++++++++++++++++++++++++++ libopenbsd/pledge-seccomp.h | 49 +++++ 2 files changed, 460 insertions(+) create mode 100644 libopenbsd/pledge-seccomp.c create mode 100644 libopenbsd/pledge-seccomp.h diff --git a/libopenbsd/pledge-seccomp.c b/libopenbsd/pledge-seccomp.c new file mode 100644 index 0000000..71d8a7d --- /dev/null +++ b/libopenbsd/pledge-seccomp.c @@ -0,0 +1,411 @@ +/* $OpenBSD: kern_pledge.c,v 1.165 2016/04/28 14:25:08 beck Exp $ */ + +/* + * Copyright (c) 2015 Nicholas Marriott + * Copyright (c) 2015 Theo de Raadt + * + * 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 +#include +#include +#include +#include +#include +#include +#include + +#include "openbsd.h" +#include "pledge.h" + +#define SYS_MAXSYSCALL 1000 + +/* + * Ordered in blocks starting with least risky and most required. + */ +const uint64_t pledge_syscalls[SYS_MAXSYSCALL] = { + /* + * Minimum required + */ + [SYS_exit] = PLEDGE_ALWAYS, + // [SYS_kbind] = PLEDGE_ALWAYS, + // [SYS___get_tcb] = PLEDGE_ALWAYS, + // [SYS_pledge] = PLEDGE_ALWAYS, + // [SYS_sendsyslog] = PLEDGE_ALWAYS, /* stack protector reporting */ + // [SYS_osendsyslog] = PLEDGE_ALWAYS, /* obsolete sendsyslog */ + // [SYS_thrkill] = PLEDGE_ALWAYS, /* raise, abort, stack pro */ + // [SYS_utrace] = PLEDGE_ALWAYS, /* ltrace(1) from ld.so */ + + /* "getting" information about self is considered safe */ + [SYS_getuid] = PLEDGE_STDIO, + [SYS_geteuid] = PLEDGE_STDIO, + [SYS_getresuid] = PLEDGE_STDIO, + [SYS_getgid] = PLEDGE_STDIO, + [SYS_getegid] = PLEDGE_STDIO, + [SYS_getresgid] = PLEDGE_STDIO, + [SYS_getgroups] = PLEDGE_STDIO, + // [SYS_getlogin59] = PLEDGE_STDIO, + // [SYS_getlogin_r] = PLEDGE_STDIO, + [SYS_getpgrp] = PLEDGE_STDIO, + [SYS_getpgid] = PLEDGE_STDIO, + [SYS_getppid] = PLEDGE_STDIO, + [SYS_getsid] = PLEDGE_STDIO, + // [SYS_getthrid] = PLEDGE_STDIO, + [SYS_getrlimit] = PLEDGE_STDIO, + [SYS_gettimeofday] = PLEDGE_STDIO, + // [SYS_getdtablecount] = PLEDGE_STDIO, + [SYS_getrusage] = PLEDGE_STDIO, + // [SYS_issetugid] = PLEDGE_STDIO, + [SYS_clock_getres] = PLEDGE_STDIO, + [SYS_clock_gettime] = PLEDGE_STDIO, + [SYS_getpid] = PLEDGE_STDIO, + + /* + * Almost exclusively read-only, Very narrow subset. + * Use of "route", "inet", "dns", "ps", or "vminfo" + * expands access. + */ + // [SYS_sysctl] = PLEDGE_STDIO, + + /* Support for malloc(3) family of operations */ + // [SYS_getentropy] = PLEDGE_STDIO, + [SYS_madvise] = PLEDGE_STDIO, + // [SYS_minherit] = PLEDGE_STDIO, + [SYS_mmap] = PLEDGE_STDIO, + [SYS_mprotect] = PLEDGE_STDIO, + // [SYS_mquery] = PLEDGE_STDIO, + [SYS_munmap] = PLEDGE_STDIO, + [SYS_msync] = PLEDGE_STDIO, + // [SYS_break] = PLEDGE_STDIO, + + [SYS_umask] = PLEDGE_STDIO, + + /* read/write operations */ + [SYS_read] = PLEDGE_STDIO, + [SYS_readv] = PLEDGE_STDIO, + // [SYS_pread] = PLEDGE_STDIO, + [SYS_preadv] = PLEDGE_STDIO, + [SYS_write] = PLEDGE_STDIO, + [SYS_writev] = PLEDGE_STDIO, + // [SYS_pwrite] = PLEDGE_STDIO, + [SYS_pwritev] = PLEDGE_STDIO, + [SYS_recvmsg] = PLEDGE_STDIO, + [SYS_recvfrom] = PLEDGE_STDIO | PLEDGE_YPACTIVE, + [SYS_ftruncate] = PLEDGE_STDIO, + [SYS_lseek] = PLEDGE_STDIO, + // [SYS_fpathconf] = PLEDGE_STDIO, + + /* + * Address selection required a network pledge ("inet", + * "unix", "dns". + */ + [SYS_sendto] = PLEDGE_STDIO | PLEDGE_YPACTIVE, + + /* + * Address specification required a network pledge ("inet", + * "unix", "dns". SCM_RIGHTS requires "sendfd" or "recvfd". + */ + [SYS_sendmsg] = PLEDGE_STDIO, + + /* Common signal operations */ + [SYS_nanosleep] = PLEDGE_STDIO, + [SYS_sigaltstack] = PLEDGE_STDIO, + // [SYS_sigprocmask] = PLEDGE_STDIO, + // [SYS_sigsuspend] = PLEDGE_STDIO, + // [SYS_sigaction] = PLEDGE_STDIO, + // [SYS_sigreturn] = PLEDGE_STDIO, + // [SYS_sigpending] = PLEDGE_STDIO, + [SYS_getitimer] = PLEDGE_STDIO, + [SYS_setitimer] = PLEDGE_STDIO, + + /* + * To support event driven programming. + */ + [SYS_poll] = PLEDGE_STDIO, + [SYS_ppoll] = PLEDGE_STDIO, + // [SYS_kevent] = PLEDGE_STDIO, + // [SYS_kqueue] = PLEDGE_STDIO, + [SYS_select] = PLEDGE_STDIO, + // [SYS_pselect] = PLEDGE_STDIO, + [SYS_pselect6] = PLEDGE_STDIO, + [SYS_epoll_create] = PLEDGE_STDIO, + [SYS_epoll_create1] = PLEDGE_STDIO, + [SYS_epoll_ctl] = PLEDGE_STDIO, + [SYS_epoll_pwait] = PLEDGE_STDIO, + [SYS_epoll_wait] = PLEDGE_STDIO, + [SYS_eventfd] = PLEDGE_STDIO, + [SYS_eventfd2] = PLEDGE_STDIO, + + [SYS_fstat] = PLEDGE_STDIO, + [SYS_fsync] = PLEDGE_STDIO, + + [SYS_setsockopt] = PLEDGE_STDIO, /* narrow whitelist */ + [SYS_getsockopt] = PLEDGE_STDIO, /* narrow whitelist */ + + /* F_SETOWN requires PLEDGE_PROC */ + [SYS_fcntl] = PLEDGE_STDIO, + + [SYS_close] = PLEDGE_STDIO, + [SYS_dup] = PLEDGE_STDIO, + [SYS_dup2] = PLEDGE_STDIO, + [SYS_dup3] = PLEDGE_STDIO, + // [SYS_closefrom] = PLEDGE_STDIO, + [SYS_shutdown] = PLEDGE_STDIO, + [SYS_fchdir] = PLEDGE_STDIO, /* XXX consider tightening */ + + [SYS_pipe] = PLEDGE_STDIO, + [SYS_pipe2] = PLEDGE_STDIO, + [SYS_socketpair] = PLEDGE_STDIO, + + [SYS_wait4] = PLEDGE_STDIO, + + /* + * Can kill self with "stdio". Killing another pid + * requires "proc" + */ + // [SYS_o58_kill] = PLEDGE_STDIO, + [SYS_kill] = PLEDGE_STDIO, + + /* + * FIONREAD/FIONBIO for "stdio" + * A few non-tty ioctl available using "ioctl" + * tty-centric ioctl available using "tty" + */ + [SYS_ioctl] = PLEDGE_STDIO, + + /* + * Path access/creation calls encounter many extensive + * checks are done during namei() + */ + [SYS_open] = PLEDGE_STDIO, + [SYS_stat] = PLEDGE_STDIO, + [SYS_access] = PLEDGE_STDIO, + [SYS_readlink] = PLEDGE_STDIO, + + // [SYS_adjtime] = PLEDGE_STDIO, /* setting requires "settime" */ + // [SYS_adjfreq] = PLEDGE_SETTIME, + [SYS_settimeofday] = PLEDGE_SETTIME, + + /* + * Needed by threaded programs + * XXX should we have a new "threads"? + */ + // [SYS___tfork] = PLEDGE_STDIO, + [SYS_sched_yield] = PLEDGE_STDIO, + // [SYS___thrsleep] = PLEDGE_STDIO, + // [SYS___thrwakeup] = PLEDGE_STDIO, + // [SYS___threxit] = PLEDGE_STDIO, + // [SYS___thrsigdivert] = PLEDGE_STDIO, + + [SYS_fork] = PLEDGE_PROC, + [SYS_vfork] = PLEDGE_PROC, + [SYS_setpgid] = PLEDGE_PROC, + [SYS_setsid] = PLEDGE_PROC, + + [SYS_setrlimit] = PLEDGE_PROC | PLEDGE_ID, + [SYS_getpriority] = PLEDGE_PROC | PLEDGE_ID, + + [SYS_setpriority] = PLEDGE_PROC | PLEDGE_ID, + + [SYS_setuid] = PLEDGE_ID, + // [SYS_seteuid] = PLEDGE_ID, + [SYS_setreuid] = PLEDGE_ID, + [SYS_setresuid] = PLEDGE_ID, + [SYS_setgid] = PLEDGE_ID, + // [SYS_setegid] = PLEDGE_ID, + [SYS_setregid] = PLEDGE_ID, + [SYS_setresgid] = PLEDGE_ID, + [SYS_setgroups] = PLEDGE_ID, + // [SYS_setlogin] = PLEDGE_ID, + + [SYS_execve] = PLEDGE_EXEC, + + [SYS_chdir] = PLEDGE_RPATH, + [SYS_openat] = PLEDGE_RPATH | PLEDGE_WPATH, + // [SYS_fstatat] = PLEDGE_RPATH | PLEDGE_WPATH, + [SYS_newfstatat] = PLEDGE_RPATH | PLEDGE_WPATH, + [SYS_faccessat] = PLEDGE_RPATH | PLEDGE_WPATH, + [SYS_readlinkat] = PLEDGE_RPATH | PLEDGE_WPATH, + [SYS_lstat] = PLEDGE_RPATH | PLEDGE_WPATH | PLEDGE_TMPPATH, + [SYS_truncate] = PLEDGE_WPATH, + [SYS_rename] = PLEDGE_CPATH, + [SYS_rmdir] = PLEDGE_CPATH, + [SYS_renameat] = PLEDGE_CPATH, + [SYS_link] = PLEDGE_CPATH, + [SYS_linkat] = PLEDGE_CPATH, + [SYS_symlink] = PLEDGE_CPATH, + [SYS_unlink] = PLEDGE_CPATH | PLEDGE_TMPPATH, + [SYS_unlinkat] = PLEDGE_CPATH, + [SYS_mkdir] = PLEDGE_CPATH, + [SYS_mkdirat] = PLEDGE_CPATH, + + // [SYS_mkfifo] = PLEDGE_DPATH, + [SYS_mknod] = PLEDGE_DPATH, + + [SYS_chroot] = PLEDGE_ID, /* also requires PLEDGE_PROC */ + + // [SYS_revoke] = PLEDGE_TTY, /* also requires PLEDGE_RPATH */ + + /* + * Classify as RPATH|WPATH, because of path information leakage. + * WPATH due to unknown use of mk*temp(3) on non-/tmp paths.. + */ + // [SYS___getcwd] = PLEDGE_RPATH | PLEDGE_WPATH, + [SYS_getcwd] = PLEDGE_RPATH | PLEDGE_WPATH, + + /* Classify as RPATH, because these leak path information */ + [SYS_getdents] = PLEDGE_RPATH, + // [SYS_getfsstat] = PLEDGE_RPATH, + [SYS_statfs] = PLEDGE_RPATH, + [SYS_fstatfs] = PLEDGE_RPATH, + // [SYS_pathconf] = PLEDGE_RPATH, + + [SYS_utimes] = PLEDGE_FATTR, + // [SYS_futimes] = PLEDGE_FATTR, + [SYS_futimesat] = PLEDGE_FATTR, + [SYS_utimensat] = PLEDGE_FATTR, + // [SYS_futimens] = PLEDGE_FATTR, + [SYS_chmod] = PLEDGE_FATTR, + [SYS_fchmod] = PLEDGE_FATTR, + [SYS_fchmodat] = PLEDGE_FATTR, + // [SYS_chflags] = PLEDGE_FATTR, + //[SYS_chflagsat] = PLEDGE_FATTR, + // [SYS_fchflags] = PLEDGE_FATTR, + [SYS_chown] = PLEDGE_FATTR, + [SYS_fchownat] = PLEDGE_FATTR, + [SYS_lchown] = PLEDGE_FATTR, + [SYS_fchown] = PLEDGE_FATTR, + + [SYS_socket] = PLEDGE_INET | PLEDGE_UNIX | PLEDGE_DNS | PLEDGE_YPACTIVE, + [SYS_connect] = PLEDGE_INET | PLEDGE_UNIX | PLEDGE_DNS | PLEDGE_YPACTIVE, + [SYS_bind] = PLEDGE_INET | PLEDGE_UNIX | PLEDGE_DNS | PLEDGE_YPACTIVE, + [SYS_getsockname] = PLEDGE_INET | PLEDGE_UNIX | PLEDGE_DNS | PLEDGE_YPACTIVE, + + [SYS_listen] = PLEDGE_INET | PLEDGE_UNIX, + [SYS_accept4] = PLEDGE_INET | PLEDGE_UNIX, + [SYS_accept] = PLEDGE_INET | PLEDGE_UNIX, + [SYS_getpeername] = PLEDGE_INET | PLEDGE_UNIX, + + [SYS_flock] = PLEDGE_FLOCK | PLEDGE_YPACTIVE, + + // [SYS_swapctl] = PLEDGE_VMINFO, /* XXX should limit to "get" operations */ +}; + + +static const struct { + char *name; + int flags; +} pledgereq[] = { + { "audio", PLEDGE_AUDIO }, + { "cpath", PLEDGE_CPATH }, + { "disklabel", PLEDGE_DISKLABEL }, + { "dns", PLEDGE_DNS }, + { "dpath", PLEDGE_DPATH }, + { "drm", PLEDGE_DRM }, + { "exec", PLEDGE_EXEC }, + { "fattr", PLEDGE_FATTR }, + { "flock", PLEDGE_FLOCK }, + { "getpw", PLEDGE_GETPW }, + { "id", PLEDGE_ID }, + { "inet", PLEDGE_INET }, + { "ioctl", PLEDGE_IOCTL }, + { "mcast", PLEDGE_MCAST }, + { "pf", PLEDGE_PF }, + { "proc", PLEDGE_PROC }, + { "prot_exec", PLEDGE_PROTEXEC }, + { "ps", PLEDGE_PS }, + { "recvfd", PLEDGE_RECVFD }, + { "route", PLEDGE_ROUTE }, + { "rpath", PLEDGE_RPATH }, + { "sendfd", PLEDGE_SENDFD }, + { "settime", PLEDGE_SETTIME }, + { "stdio", PLEDGE_STDIO }, + { "tmppath", PLEDGE_TMPPATH }, + { "tty", PLEDGE_TTY }, + { "unix", PLEDGE_UNIX }, + { "vminfo", PLEDGE_VMINFO }, + { "vmm", PLEDGE_VMM }, + { "wpath", PLEDGE_WPATH }, +}; + +scmp_filter_ctx scmp_ctx = NULL; + +/* bsearch over pledgereq. return flags value if found, 0 else */ +static int +pledgereq_flags(const char *req_name) +{ + int base = 0, cmp, i, lim; + + for (lim = nitems(pledgereq); lim != 0; lim >>= 1) { + i = base + (lim >> 1); + cmp = strcmp(req_name, pledgereq[i].name); + if (cmp == 0) + return (pledgereq[i].flags); + if (cmp > 0) { /* not found before, move right */ + base = i + 1; + lim--; + } /* else move left */ + } + return (0); +} + +/* whitelists syscalls returns -1 on error */ +int +pledge(const char *promises, __UNUSED const char *paths[]) +{ + char *buf, *p; + int rv = 0; + uint64_t flags = 0; + + if (scmp_ctx == NULL) { + /* inintialize new seccomp whitelist */ + if ((scmp_ctx = seccomp_init(SCMP_ACT_KILL)) == NULL) + err(1, "seccomp_init"); + } else { + /* reset previous rules */ + if (seccomp_reset(scmp_ctx, SCMP_ACT_KILL) < 0) + err(1, "seccomp_reset"); + } + + /* make flags from prmises string */ + buf = strdup(promises); + for (p = strtok(buf, " "); p; + p = strtok(NULL, " ")) { + flags |= pledgereq_flags(p); + } + + for (int i = 0; i < SYS_MAXSYSCALL; i++) { + /* skip not defined syscalls */ + if (pledge_syscalls[i] == 0) + continue; + + /* skip not matching syscalls */ + if (!(pledge_syscalls[i] & flags)) + continue; + + /* seccomp whitelist syscall */ + if((rv = seccomp_rule_add_exact(scmp_ctx, SCMP_ACT_ALLOW, i, 0)) != 0) + goto out; + } + + /* seccomp_export_pfc(scmp_ctx, STDERR_FILENO); */ + +out: + free(buf); + free(p); + + /* seccomp_release(scmp_ctx); */ + + return (rv == 0 ? 0 : -1); +} diff --git a/libopenbsd/pledge-seccomp.h b/libopenbsd/pledge-seccomp.h new file mode 100644 index 0000000..28086c4 --- /dev/null +++ b/libopenbsd/pledge-seccomp.h @@ -0,0 +1,49 @@ + +#ifndef nitems +#define nitems(_a) (sizeof((_a)) / sizeof((_a)[0])) +#endif + +/* + * pledge(2) requests + */ +#define PLEDGE_ALWAYS 0xffffffffffffffffULL +#define PLEDGE_RPATH 0x0000000000000001ULL /* allow open for read */ +#define PLEDGE_WPATH 0x0000000000000002ULL /* allow open for write */ +#define PLEDGE_CPATH 0x0000000000000004ULL /* allow creat, mkdir, unlink etc */ +#define PLEDGE_STDIO 0x0000000000000008ULL /* operate on own pid */ +#define PLEDGE_TMPPATH 0x0000000000000010ULL /* for mk*temp() */ +#define PLEDGE_DNS 0x0000000000000020ULL /* DNS services */ +#define PLEDGE_INET 0x0000000000000040ULL /* AF_INET/AF_INET6 sockets */ +#define PLEDGE_FLOCK 0x0000000000000080ULL /* file locking */ +#define PLEDGE_UNIX 0x0000000000000100ULL /* AF_UNIX sockets */ +#define PLEDGE_ID 0x0000000000000200ULL /* allow setuid, setgid, etc */ +#define PLEDGE_IOCTL 0x0000000000000400ULL /* Select ioctl */ +#define PLEDGE_GETPW 0x0000000000000800ULL /* YP enables if ypbind.lock */ +#define PLEDGE_PROC 0x0000000000001000ULL /* fork, waitpid, etc */ +#define PLEDGE_SETTIME 0x0000000000002000ULL /* able to set/adj time/freq */ +#define PLEDGE_FATTR 0x0000000000004000ULL /* allow explicit file st_* mods */ +#define PLEDGE_PROTEXEC 0x0000000000008000ULL /* allow use of PROT_EXEC */ +#define PLEDGE_TTY 0x0000000000010000ULL /* tty setting */ +#define PLEDGE_SENDFD 0x0000000000020000ULL /* AF_UNIX CMSG fd sending */ +#define PLEDGE_RECVFD 0x0000000000040000ULL /* AF_UNIX CMSG fd receiving */ +#define PLEDGE_EXEC 0x0000000000080000ULL /* execve, child is free of pledge */ +#define PLEDGE_ROUTE 0x0000000000100000ULL /* routing lookups */ +#define PLEDGE_MCAST 0x0000000000200000ULL /* multicast joins */ +#define PLEDGE_VMINFO 0x0000000000400000ULL /* vminfo listings */ +#define PLEDGE_PS 0x0000000000800000ULL /* ps listings */ +#define PLEDGE_DISKLABEL 0x0000000002000000ULL /* disklabels */ +#define PLEDGE_PF 0x0000000004000000ULL /* pf ioctls */ +#define PLEDGE_AUDIO 0x0000000008000000ULL /* audio ioctls */ +#define PLEDGE_DPATH 0x0000000010000000ULL /* mknod & mkfifo */ +#define PLEDGE_DRM 0x0000000020000000ULL /* drm ioctls */ +#define PLEDGE_VMM 0x0000000040000000ULL /* vmm ioctls */ + + +/* + * Bits outside PLEDGE_USERSET are used by the kernel itself + * to track program behaviours which have been observed. + */ +#define PLEDGE_USERSET 0x0fffffffffffffffULL +#define PLEDGE_STATLIE 0x4000000000000000ULL +#define PLEDGE_YPACTIVE 0x8000000000000000ULL /* YP use detected and allowed */ + From 673f187858a3b10aae4e41b190ca04f6f85c99f1 Mon Sep 17 00:00:00 2001 From: Duncaen Date: Sat, 7 May 2016 19:00:42 +0200 Subject: [PATCH 006/198] Fix typos and configure pledge detection --- bsd.prog.mk | 2 +- configure | 20 +++++++++++++------- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/bsd.prog.mk b/bsd.prog.mk index 6c70ad7..d2caff4 100644 --- a/bsd.prog.mk +++ b/bsd.prog.mk @@ -21,7 +21,7 @@ ${PROG}: ${OBJS} libopenbsd.a chown ${BINOWN}:${BINGRP} $@ chmod ${BINMODE} $@ -${DESTRDIR}${BINDIR} ${DESTRDIR}${PAMDIR}: +${DESTDIR}${BINDIR} ${DESTDIR}${PAMDIR}: mkdir -pm 0755 $@ ${DESTDIR}${BINDIR}/${PROG}: .${PROG}.chmod ${BINDIR} diff --git a/configure b/configure index 73d56cd..98699da 100755 --- a/configure +++ b/configure @@ -85,14 +85,18 @@ check_func() { func="$1"; src="$2"; shift 2 printf 'Checking for %-14s\t\t' "$func ..." printf '%s\n' "$src" >"_$func.c" - if $XCC "_$func.c" -o "_$func" 2>/dev/null; then + $XCC "_$func.c" -o "_$func" 2>/dev/null + ret=$? + rm -f "_$func.c" "_$func" + if [ $ret -eq 0 ]; then printf 'yes.\n' upperfunc="$(printf '%s\n' "$func" | tr '[[:lower:]]' '[[:upper:]]')" printf 'CFLAGS += -DHAVE_%s\n' "$upperfunc" >>$CONFIG_MK + return 0 else printf 'no.\n' + return 1 fi - rm -f "_$func.c" "_$func" } src=' @@ -114,7 +118,7 @@ src=' int main(void) { const char s1[] = "foo"; char s2[10]; - strlccat(s2, s1, sizeof(s2)); + strlcat(s2, s1, sizeof(s2)); return 0; }' check_func "strlcat" "$src" || { @@ -291,10 +295,12 @@ int main(void) { prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, NULL); return 0; }' -if [ -n "$have_pledge" -a -n "$BUILD_SECCOMP" ]; then - check_func "seccomp_h" "$src" && { +[ -z "$have_pledge" -a -n "$BUILD_SECCOMP" ] && \ + check_func "seccomp_h" "$src" && \ + { + have_pledge=1 printf 'OPENBSD += pledge-seccomp.c\n' >>$CONFIG_MK } -elif [ -n "$have_pledge" ]; then + +[ -z "$have_pledge" ] && \ printf 'OPENBSD += pledge-noop.c\n' >>$CONFIG_MK -fi From 5af624acf77da9592443bcfb095b21afaa984404 Mon Sep 17 00:00:00 2001 From: Duncaen Date: Sat, 7 May 2016 19:02:55 +0200 Subject: [PATCH 007/198] Enable style option only if bsd_auth.h is available --- doas.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/doas.c b/doas.c index 643d3d8..8094aad 100644 --- a/doas.c +++ b/doas.c @@ -334,7 +334,9 @@ main(int argc, char **argv, char **envp) int vflag = 0; char cwdpath[PATH_MAX]; const char *cwd; +#ifdef HAVE_BSD_AUTH_H char *login_style = NULL; +#endif setprogname("doas"); @@ -345,11 +347,19 @@ main(int argc, char **argv, char **envp) uid = getuid(); - while ((ch = getopt(argc, argv, "a:C:nsu:v")) != -1) { +#ifdef HAVE_BSD_AUTH_H +# define OPTSTRING "a:C:nsu:v" +#else +# define OPTSTRING "C:nsu:v" +#endif + + while ((ch = getopt(argc, argv, OPTSTRING)) != -1) { switch (ch) { +#ifdef HAVE_BSD_AUTH_H case 'a': login_style = optarg; break; +#endif case 'C': confpath = optarg; break; From 33e3630671ba6d2ec591fb45ba41af7e09fecf0a Mon Sep 17 00:00:00 2001 From: Duncaen Date: Sun, 8 May 2016 16:15:58 +0200 Subject: [PATCH 008/198] Add proper pam session handling --- configure | 18 ++- doas.c | 17 +-- doas_pam.c | 244 +++++++++++++++++++++++++++++++++++++ includes.h | 4 + libopenbsd/auth_userokay.c | 112 ----------------- libopenbsd/openbsd.h | 5 - 6 files changed, 273 insertions(+), 127 deletions(-) create mode 100644 doas_pam.c delete mode 100644 libopenbsd/auth_userokay.c diff --git a/configure b/configure index 98699da..16d37ee 100755 --- a/configure +++ b/configure @@ -228,9 +228,21 @@ src=' int main(void) { return 0; }' -check_func "bsd_auth_h" "$src" || { - printf 'OPENBSD += auth_userokay.c\n' >>$CONFIG_MK -} +check_func "bsd_auth_h" "$src" && \ + have_bsd_auth_h=1 + +# +# Check for pam_appl.h. +# +src=' +#include +int main(void) { + return 0; +}' +[ -z "$have_bsd_auth_h" ] && \ + check_func "pam_appl_h" "$src" && { + printf 'SRCS += doas_pam.c\n' >>$CONFIG_MK + } # # Check for login_cap.h. diff --git a/doas.c b/doas.c index 8094aad..cceeac1 100644 --- a/doas.c +++ b/doas.c @@ -438,11 +438,11 @@ main(int argc, char **argv, char **envp) errc(1, EPERM, NULL); } +#ifdef HAVE_BSD_AUTH_H if (!(rule->options & NOPASS)) { if (nflag) errx(1, "Authorization required"); -#ifdef HAVE_BSD_AUTH_H char *challenge = NULL, *response, rbuf[1024], cbuf[128]; auth_session_t *as; @@ -470,14 +470,17 @@ main(int argc, char **argv, char **envp) errc(1, EPERM, NULL); } explicit_bzero(rbuf, sizeof(rbuf)); + } +#elif HAVE_PAM_APPL_H + if (!doas_pam(myname, !nflag, rule->options & NOPASS)) { + syslog(LOG_AUTHPRIV | LOG_NOTICE, + "failed auth for %s", myname); + errc(1, EPERM, NULL); + } #else - if (!auth_userokay(myname, NULL, NULL, NULL)) { - syslog(LOG_AUTHPRIV | LOG_NOTICE, - "failed auth for %s", myname); - errc(1, EPERM, NULL); - } + if (!(rule->options & NOPASS)) { + errx(1, "Authorization required"); #endif /* HAVE_BSD_AUTH_H */ - } if (pledge("stdio rpath getpw exec id", NULL) == -1) err(1, "pledge"); diff --git a/doas_pam.c b/doas_pam.c new file mode 100644 index 0000000..df6a097 --- /dev/null +++ b/doas_pam.c @@ -0,0 +1,244 @@ +/* + * Copyright (c) 2015 Nathan Holstein + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "doas.h" +#include "includes.h" + +#define PAM_SERVICE_NAME "doas" + +static pam_handle_t *pamh = NULL; +static sig_atomic_t volatile caught_signal = 0; + +static char * +prompt(const char *msg, int echo_on, int *pam) +{ + char buf[PAM_MAX_RESP_SIZE]; + int flags = RPP_REQUIRE_TTY | (echo_on ? RPP_ECHO_ON : RPP_ECHO_OFF); + char *ret = readpassphrase(msg, buf, sizeof(buf), flags); + if (!ret) + *pam = PAM_CONV_ERR; + else if (!(ret = strdup(ret))) + *pam = PAM_BUF_ERR; + explicit_bzero(buf, sizeof(buf)); + return ret; +} + +static int +doas_pam_conv(int nmsgs, const struct pam_message **msgs, + struct pam_response **rsps, __UNUSED void *ptr) +{ + struct pam_response *rsp; + int i, style; + int ret = PAM_SUCCESS; + + if (!(rsp = calloc(nmsgs, sizeof(struct pam_response)))) + errx(1, "couldn't malloc pam_response"); + + for (i = 0; i < nmsgs; i++) { + switch (style = msgs[i]->msg_style) { + case PAM_PROMPT_ECHO_OFF: + case PAM_PROMPT_ECHO_ON: + rsp[i].resp = prompt(msgs[i]->msg, style == PAM_PROMPT_ECHO_ON, &ret); + if (ret != PAM_SUCCESS) + goto fail; + break; + + case PAM_ERROR_MSG: + case PAM_TEXT_INFO: + if (fprintf(style == PAM_ERROR_MSG ? stderr : stdout, + "%s\n", msgs[i]->msg) < 0) + goto fail; + break; + + default: + errx(1, "invalid PAM msg_style %d", style); + } + } + + *rsps = rsp; + rsp = NULL; + + return PAM_SUCCESS; + +fail: + /* overwrite and free response buffers */ + for (i = 0; i < nmsgs; i++) { + if (rsp[i].resp == NULL) + continue; + switch (style = msgs[i]->msg_style) { + case PAM_PROMPT_ECHO_OFF: + case PAM_PROMPT_ECHO_ON: + explicit_bzero(rsp[i].resp, strlen(rsp[i].resp)); + free(rsp[i].resp); + } + rsp[i].resp = NULL; + } + + return PAM_CONV_ERR; +} + +static void +catchsig(int sig) +{ + caught_signal = sig; +} + +int +doas_pam(char *name, int interactive, int nopass) +{ + static const struct pam_conv conv = { + .conv = doas_pam_conv, + .appdata_ptr = NULL, + }; + pid_t child; + int ret; + + if (!name) + return 0; + + ret = pam_start(PAM_SERVICE_NAME, name, &conv, &pamh); + if (ret != PAM_SUCCESS) + errx(1, "pam_start(\"%s\", \"%s\", ?, ?): failed\n", + PAM_SERVICE_NAME, name); + + if (!nopass) { + if (!interactive) + errx(1, "Authorization required"); + /* authenticate */ + ret = pam_authenticate(pamh, 0); + if (ret != PAM_SUCCESS) { + ret = pam_end(pamh, ret); + if (ret != PAM_SUCCESS) + errx(1, "pam_end(): %s\n", pam_strerror(pamh, ret)); + return 1; + } + } + + ret = pam_setcred(pamh, PAM_ESTABLISH_CRED); + if (ret != PAM_SUCCESS) + errx(1, "pam_setcred(?, PAM_ESTABLISH_CRED): %s\n", + pam_strerror(pamh, ret)); + + ret = pam_acct_mgmt(pamh, 0); + if (ret != PAM_SUCCESS) + errx(1, "pam_setcred(): %s\n", pam_strerror(pamh, ret)); + + /* open session */ + ret = pam_open_session(pamh, 0); + if (ret != PAM_SUCCESS) + errx(1, "pam_open_session(): %s\n", pam_strerror(pamh, ret)); + + if ((child = fork()) == -1) { + ret = pam_close_session(pamh, 0); + if (ret != PAM_SUCCESS) + errx(1, "pam_close_session(): %s\n", pam_strerror(pamh, ret)); + + ret = pam_end(pamh, PAM_ABORT); + if (ret != PAM_SUCCESS) + errx(1, "pam_end(): %s\n", pam_strerror(pamh, ret)); + + errx(1, "fork()"); + } + + /* return as child */ + if (child == 0) { + return 1; + } + + /* parent watches for signals and closes session */ + sigset_t sigs; + struct sigaction act, oldact; + int status; + + /* block signals */ + sigfillset(&sigs); + if (sigprocmask(SIG_BLOCK, &sigs, NULL)) { + errx(1, "sigprocmask()"); + } + + /* setup signal handler */ + act.sa_handler = catchsig; + sigemptyset(&act.sa_mask); + act.sa_flags = 0; + sigemptyset(&sigs); + + /* unblock SIGTERM and SIGALRM to catch them */ + if(sigaddset(&sigs, SIGTERM) || + sigaddset(&sigs, SIGALRM) || + sigaction(SIGTERM, &act, &oldact) || + sigprocmask(SIG_UNBLOCK, &sigs, NULL)) { + errx(1, "failed to set signal handler"); + } + + /* wait for child to be terminated */ + if (waitpid(child, &status, 0) != -1) { + if (WIFSIGNALED(status)) { + fprintf(stderr, "%s%s\n", strsignal(WTERMSIG(status)), + WCOREDUMP(status) ? " (core dumped)" : ""); + status = WTERMSIG(status) + 128; + } else { + status = WEXITSTATUS(status); + } + } + else if (caught_signal) + status = caught_signal + 128; + else + status = 1; + + if (caught_signal) { + fprintf(stderr, "\nSession terminated, killing shell\n"); + kill(child, SIGTERM); + } + + /* close session */ + ret = pam_close_session(pamh, 0); + if (ret != PAM_SUCCESS) + errx(1, "pam_close_session(): %s\n", pam_strerror(pamh, ret)); + + ret = pam_end(pamh, PAM_SUCCESS); + if (ret != PAM_SUCCESS) + errx(1, "pam_end(): %s\n", pam_strerror(pamh, ret)); + + if (caught_signal) { + /* kill child */ + sleep(2); + kill(child, SIGKILL); + fprintf(stderr, " ...killed.\n"); + + /* unblock cached signal and resend */ + sigaction(SIGTERM, &oldact, NULL); + if (caught_signal != SIGTERM) + caught_signal = SIGKILL; + kill(getpid(), caught_signal); + } + + exit(status); + + return 0; +} diff --git a/includes.h b/includes.h index b08e93c..e9ebf63 100644 --- a/includes.h +++ b/includes.h @@ -19,4 +19,8 @@ #include "openbsd.h" +#ifdef HAVE_PAM_APPL_H +int doas_pam(char *name, int interactive, int nopass); +#endif + #endif /* INCLUDES_H */ diff --git a/libopenbsd/auth_userokay.c b/libopenbsd/auth_userokay.c deleted file mode 100644 index 6a9841b..0000000 --- a/libopenbsd/auth_userokay.c +++ /dev/null @@ -1,112 +0,0 @@ -/* - * Copyright (c) 2015 Nathan Holstein - * - * 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 -#include -#include -#include -#include -#include -#include - -#include - -#include "includes.h" - -#define PAM_SERVICE_NAME "doas" - -static char * -pam_prompt(const char *msg, int echo_on, int *pam) -{ - char buf[PAM_MAX_RESP_SIZE]; - int flags = RPP_REQUIRE_TTY | (echo_on ? RPP_ECHO_ON : RPP_ECHO_OFF); - char *ret = readpassphrase(msg, buf, sizeof(buf), flags); - if (!ret) - *pam = PAM_CONV_ERR; - else if (!(ret = strdup(ret))) - *pam = PAM_BUF_ERR; - explicit_bzero(buf, sizeof(buf)); - return ret; -} - -static int -pam_conv(int nmsgs, const struct pam_message **msgs, - struct pam_response **rsps, __UNUSED void *ptr) -{ - struct pam_response *rsp; - int i, style; - int pam = PAM_SUCCESS; - - if (!(rsp = calloc(nmsgs, sizeof(struct pam_response)))) - errx(1, "couldn't malloc pam_response"); - *rsps = rsp; - - for (i = 0; i < nmsgs; i++) { - switch (style = msgs[i]->msg_style) { - case PAM_PROMPT_ECHO_OFF: - case PAM_PROMPT_ECHO_ON: - rsp[i].resp = pam_prompt(msgs[i]->msg, - style == PAM_PROMPT_ECHO_ON, &pam); - break; - - case PAM_ERROR_MSG: - case PAM_TEXT_INFO: - if (fprintf(style == PAM_ERROR_MSG ? stderr : stdout, - "%s\n", msgs[i]->msg) < 0) - pam = PAM_CONV_ERR; - break; - - default: - errx(1, "invalid PAM msg_style %d", style); - } - } - - return PAM_SUCCESS; -} - -int -auth_userokay(char *name, char *style, char *type, char *password) -{ - static const struct pam_conv conv = { - .conv = pam_conv, - .appdata_ptr = NULL, - }; - - int ret, auth; - pam_handle_t *pamh = NULL; - - if (!name) - return 0; - if (style || type || password) - errx(1, "auth_userokay(name, NULL, NULL, NULL)!\n"); - - ret = pam_start(PAM_SERVICE_NAME, name, &conv, &pamh); - if (ret != PAM_SUCCESS) - errx(1, "pam_start(\"%s\", \"%s\", ?, ?): failed\n", - PAM_SERVICE_NAME, name); - - auth = pam_authenticate(pamh, 0); - - ret = pam_open_session(pamh, 0); - if (ret != PAM_SUCCESS) - errx(1, "pam_open_session(): %s\n", pam_strerror(pamh, ret)); - - ret = pam_close_session(pamh, 0); - if (ret != PAM_SUCCESS) - errx(1, "pam_close_session(): %s\n", pam_strerror(pamh, ret)); - - return auth == PAM_SUCCESS; -} diff --git a/libopenbsd/openbsd.h b/libopenbsd/openbsd.h index 1e5ff97..f0eec3d 100644 --- a/libopenbsd/openbsd.h +++ b/libopenbsd/openbsd.h @@ -9,11 +9,6 @@ /* API definitions lifted from OpenBSD src/include */ -/* bsd_auth.h */ -#ifndef HAVE_BSD_AUTH_H -int auth_userokay(char *, char *, char *, char *); -#endif /* !HAVE_BSD_AUTH_H */ - /* login_cap.h */ #ifndef HAVE_LOGIN_CAP_H #define LOGIN_SETGROUP 0x0001 /* Set group */ From e246f9e2fee02046072b69a9e8c29767db7f4652 Mon Sep 17 00:00:00 2001 From: Svyatoslav Mishyn Date: Sun, 8 May 2016 11:26:32 +0300 Subject: [PATCH 009/198] configure: tune up a little bit * add usage() function * remove unused options * stop parsing option(s) if unknown was found * set up "--enable-debug" and "--enable-static" options Closes: #1 [via git-merge-pr] --- configure | 58 +++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 44 insertions(+), 14 deletions(-) diff --git a/configure b/configure index 16d37ee..66589b9 100755 --- a/configure +++ b/configure @@ -1,30 +1,54 @@ #!/bin/sh +die() { + printf "$1\n" >&2 + exit 1 +} + +usage() { + cat <&2;; + --enable-static) BUILD_STATIC=yes;; + --help|-h) usage;; + *) die "Error: unknown option $opt";; esac done @@ -35,8 +59,8 @@ cat <>$CONFIG_MK DESTDIR ?= / PREFIX ?= ${PREFIX:="/usr"} EPREFIX ?= ${EPREFIX:="${PREFIX}"} -SHAREDIR ?= ${SHAREDIR:="${PREFIX}/share"} BINDIR ?= ${BINDIR:="${PREFIX}/bin"} +SHAREDIR ?= ${SHAREDIR:="${PREFIX}/share"} MANDIR ?= ${MANDIR:="${SHAREDIR}/man"} SYSCONFDIR?= ${SYSCONFDIR:="/etc"} PAMDIR ?= ${PAMDIR:="${SYSCONFDIR}/pam.d"} @@ -76,6 +100,12 @@ esac [ -n "$OS_CFLAGS" ] && \ printf 'CFLAGS += %s\n' "$OS_CFLAGS" >>$CONFIG_MK +[ -n "$DEBUG" ] && \ + printf 'CFLAGS += -O0 -g\n' >>$CONFIG_MK + +[ -n "$BUILD_STATIC" ] && \ + printf 'CFLAGS += -static\n' >>$CONFIG_MK + # Add CPPFLAGS/CFLAGS/LDFLAGS to CC for testing features XCC="${CC:=clang} $CFLAGS $OS_CFLAGS $CPPFLAGS $LDFLAGS" # Make sure to disable --as-needed for CC tests. From c387f2c31718d4f34c77203dae6bb8c4bc0abc94 Mon Sep 17 00:00:00 2001 From: Duncaen Date: Sun, 8 May 2016 19:38:18 +0200 Subject: [PATCH 010/198] set PAM_USER, PAM_RUSER and PAM_TTY if available --- doas_pam.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/doas_pam.c b/doas_pam.c index df6a097..5c6fb28 100644 --- a/doas_pam.c +++ b/doas_pam.c @@ -116,6 +116,7 @@ doas_pam(char *name, int interactive, int nopass) .conv = doas_pam_conv, .appdata_ptr = NULL, }; + const char *ttydev, *tty; pid_t child; int ret; @@ -127,6 +128,28 @@ doas_pam(char *name, int interactive, int nopass) errx(1, "pam_start(\"%s\", \"%s\", ?, ?): failed\n", PAM_SERVICE_NAME, name); + ret = pam_set_item(pamh, PAM_USER, name); + if (ret != PAM_SUCCESS) + errx(1, "pam_set_item(?, PAM_USER, \"%s\"): %s\n", + name, pam_strerror(pamh, ret)); + + ret = pam_set_item(pamh, PAM_RUSER, name); + if (ret != PAM_SUCCESS) + errx(1, "pam_set_item(?, PAM_RUSER, \"%s\"): %s\n", + name, pam_strerror(pamh, ret)); + + if (isatty(0) && (ttydev = ttyname(0)) != NULL) { + if (strncmp(ttydev, "/dev/", 5)) + tty = ttydev + 5; + else + tty = ttydev; + + ret = pam_set_item(pamh, PAM_TTY, tty); + if (ret != PAM_SUCCESS) + errx(1, "pam_set_item(?, PAM_TTY, \"%s\"): %s\n", + tty, pam_strerror(pamh, ret)); + } + if (!nopass) { if (!interactive) errx(1, "Authorization required"); From 09843fcf8876df1ab1728e9c2c49de4836b25626 Mon Sep 17 00:00:00 2001 From: Duncaen Date: Sun, 8 May 2016 20:01:25 +0200 Subject: [PATCH 011/198] Simply install and move version to configure script --- Makefile | 2 -- bsd.prog.mk | 36 +++++++++++------------------------- configure | 3 +++ doas.c | 1 - 4 files changed, 14 insertions(+), 28 deletions(-) diff --git a/Makefile b/Makefile index 3c8f8c5..f2277ab 100644 --- a/Makefile +++ b/Makefile @@ -14,5 +14,3 @@ COPTS+= -Wall -Wextra -Werror -pedantic -std=c11 LDFLAGS+= -lpam include bsd.prog.mk - -doas.o: version.h diff --git a/bsd.prog.mk b/bsd.prog.mk index d2caff4..c759f72 100644 --- a/bsd.prog.mk +++ b/bsd.prog.mk @@ -16,31 +16,17 @@ OBJS:=${OBJS:.c=.o} ${PROG}: ${OBJS} libopenbsd.a ${CC} ${CFLAGS} ${LDFLAGS} $^ -o $@ -.%.chmod: % - cp $< $@ - chown ${BINOWN}:${BINGRP} $@ - chmod ${BINMODE} $@ - -${DESTDIR}${BINDIR} ${DESTDIR}${PAMDIR}: - mkdir -pm 0755 $@ - -${DESTDIR}${BINDIR}/${PROG}: .${PROG}.chmod ${BINDIR} - mv $< $@ - -${DESTDIR}${PAMDIR}/doas: ${PAM_DOAS} - cp $< $@ - -VERSION:=\#define VERSION "$(shell git describe --dirty --tags --long --always)" -OLDVERSION:=$(shell [ -f version.h ] && cat version.h) -version.h: ; @echo '$(VERSION)' > $@ -ifneq ($(VERSION),$(OLDVERSION)) -.PHONY: version.h -endif - -MAN:=$(join $(addprefix ${DESTDIR}${MANDIR}/man,$(patsubst .%,%/,$(suffix ${MAN}))),${MAN}) -$(foreach M,${MAN},$(eval $M: $(notdir $M); cp $$< $$@)) - -install: ${DESTDIR}${BINDIR}/${PROG} ${DESTDIR}${PAMDIR}/doas ${MAN} +install: ${PROG} ${PAM_DOAS} + mkdir -p -m 0755 ${DESTDIR}${BINDIR} + mkdir -p -m 0755 ${DESTDIR}${PAMDIR} + mkdir -p -m 0755 ${DESTDIR}${MANDIR}/man{1,5} + cp -f ${PROG} ${DESTDIR}${BINDIR} + chown ${BINOWN}:${BINGRP} ${DESTDIR}${BINDIR}/${PROG} + chmod ${BINMODE} ${DESTDIR}${BINDIR}/${PROG} + cp ${PAM_DOAS} ${DESTDIR}${PAMDIR}/doas + chmod 0644 ${DESTDIR}${PAMDIR}/doas + cp -f doas.1 ${DESTDIR}${MANDIR}/man1 + cp -f doas.conf.5 ${DESTDIR}${MANDIR}/man5 clean: rm -f version.h diff --git a/configure b/configure index 66589b9..7328547 100755 --- a/configure +++ b/configure @@ -55,6 +55,8 @@ done CONFIG_MK=config.mk rm -f "$CONFIG_MK" +: ${VERSION:="$(git describe --dirty --tags --long --always)"} + cat <>$CONFIG_MK DESTDIR ?= / PREFIX ?= ${PREFIX:="/usr"} @@ -64,6 +66,7 @@ SHAREDIR ?= ${SHAREDIR:="${PREFIX}/share"} MANDIR ?= ${MANDIR:="${SHAREDIR}/man"} SYSCONFDIR?= ${SYSCONFDIR:="/etc"} PAMDIR ?= ${PAMDIR:="${SYSCONFDIR}/pam.d"} +CFLAGS += -DVERSION="\"${VERSION}\"" EOF if [ -z "$BUILD" ]; then diff --git a/doas.c b/doas.c index cceeac1..6f644ec 100644 --- a/doas.c +++ b/doas.c @@ -32,7 +32,6 @@ #include "includes.h" #include "doas.h" -#include "version.h" static void __dead version(void) From 9972a8ee4233ba9e48f70b7d21dbf1fb395f136e Mon Sep 17 00:00:00 2001 From: Duncaen Date: Sun, 8 May 2016 20:03:04 +0200 Subject: [PATCH 012/198] Fix horrible mistake --- doas_pam.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doas_pam.c b/doas_pam.c index 5c6fb28..473e1fd 100644 --- a/doas_pam.c +++ b/doas_pam.c @@ -159,7 +159,7 @@ doas_pam(char *name, int interactive, int nopass) ret = pam_end(pamh, ret); if (ret != PAM_SUCCESS) errx(1, "pam_end(): %s\n", pam_strerror(pamh, ret)); - return 1; + return 0; } } From ba41f893e86153ac1788e971ce15b5e395247874 Mon Sep 17 00:00:00 2001 From: Svyatoslav Mishyn Date: Sun, 8 May 2016 22:10:01 +0300 Subject: [PATCH 013/198] fix make install man{1,5} is not expanded set default DESTDIR to an empty string `mkdir -p //usr/bin` - it creates dir, but looks not very nice also remove "[settings]" from configure usage --- bsd.prog.mk | 3 ++- configure | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/bsd.prog.mk b/bsd.prog.mk index c759f72..5769588 100644 --- a/bsd.prog.mk +++ b/bsd.prog.mk @@ -19,7 +19,8 @@ ${PROG}: ${OBJS} libopenbsd.a install: ${PROG} ${PAM_DOAS} mkdir -p -m 0755 ${DESTDIR}${BINDIR} mkdir -p -m 0755 ${DESTDIR}${PAMDIR} - mkdir -p -m 0755 ${DESTDIR}${MANDIR}/man{1,5} + mkdir -p -m 0755 ${DESTDIR}${MANDIR}/man1 + mkdir -p -m 0755 ${DESTDIR}${MANDIR}/man5 cp -f ${PROG} ${DESTDIR}${BINDIR} chown ${BINOWN}:${BINGRP} ${DESTDIR}${BINDIR}/${PROG} chmod ${BINMODE} ${DESTDIR}${BINDIR}/${PROG} diff --git a/configure b/configure index 7328547..ea6ce79 100755 --- a/configure +++ b/configure @@ -7,7 +7,7 @@ die() { usage() { cat <>$CONFIG_MK -DESTDIR ?= / +DESTDIR ?= PREFIX ?= ${PREFIX:="/usr"} EPREFIX ?= ${EPREFIX:="${PREFIX}"} BINDIR ?= ${BINDIR:="${PREFIX}/bin"} From eb33da16ec8b0203cd48cd06f85bb4e9cf19d250 Mon Sep 17 00:00:00 2001 From: Duncaen Date: Sun, 8 May 2016 21:32:55 +0200 Subject: [PATCH 014/198] More configure and make cleanup --- Makefile | 1 - bsd.prog.mk | 19 +++++++++---------- configure | 34 ++++++++++++++++++---------------- 3 files changed, 27 insertions(+), 27 deletions(-) diff --git a/Makefile b/Makefile index f2277ab..5dbb817 100644 --- a/Makefile +++ b/Makefile @@ -11,6 +11,5 @@ BINMODE=4511 CFLAGS+= -I${CURDIR} COPTS+= -Wall -Wextra -Werror -pedantic -std=c11 -LDFLAGS+= -lpam include bsd.prog.mk diff --git a/bsd.prog.mk b/bsd.prog.mk index 5769588..39d35f1 100644 --- a/bsd.prog.mk +++ b/bsd.prog.mk @@ -2,21 +2,21 @@ default: ${PROG} +CFLAGS += -I${CURDIR}/libopenbsd ${COPTS} -MD -MP + include config.mk -OPENBSD:=$(addprefix libopenbsd/,${OPENBSD:.c=.o}) +OPENBSD := $(addprefix libopenbsd/,${OPENBSD}) +OBJS := ${SRCS:.y=.c} +OBJS := ${OBJS:.c=.o} + libopenbsd.a: ${OPENBSD} ${AR} -r $@ $? -CFLAGS:=${CFLAGS} -I${CURDIR}/libopenbsd ${COPTS} -MD -MP - -OBJS:=${SRCS:.y=.c} -OBJS:=${OBJS:.c=.o} - ${PROG}: ${OBJS} libopenbsd.a ${CC} ${CFLAGS} ${LDFLAGS} $^ -o $@ -install: ${PROG} ${PAM_DOAS} +install: ${PROG} ${PAM_DOAS} ${MAN} mkdir -p -m 0755 ${DESTDIR}${BINDIR} mkdir -p -m 0755 ${DESTDIR}${PAMDIR} mkdir -p -m 0755 ${DESTDIR}${MANDIR}/man1 @@ -38,7 +38,6 @@ clean: rm -f ${OBJS:.o=.d} rm -f ${PROG} --include ${objs:.o=.d} ${OPENBSD:.o=.d} +-include ${OBJS:.o=.d} ${OPENBSD:.o=.d} -.PHONY: default clean install man -.INTERMEDIATE: .${PROG}.chmod +.PHONY: default clean install diff --git a/configure b/configure index ea6ce79..5f896c4 100755 --- a/configure +++ b/configure @@ -58,7 +58,6 @@ rm -f "$CONFIG_MK" : ${VERSION:="$(git describe --dirty --tags --long --always)"} cat <>$CONFIG_MK -DESTDIR ?= PREFIX ?= ${PREFIX:="/usr"} EPREFIX ?= ${EPREFIX:="${PREFIX}"} BINDIR ?= ${BINDIR:="${PREFIX}/bin"} @@ -92,9 +91,11 @@ if [ -z "$OS" ]; then KERNEL=${REST%%-*} fi +OS_FLAGS="-D__${OS}__" + case "$OS" in linux) - OS_CFLAGS="-D_DEFAULT_SOURCE -D_GNU_SOURCE -DUID_MAX=60000 -DGID_MAX=60000" + OS_CFLAGS+=" -D_DEFAULT_SOURCE -D_GNU_SOURCE -DUID_MAX=60000 -DGID_MAX=60000" printf 'CURDIR := .\n' >>$CONFIG_MK printf 'PAM_DOAS = pam.d__doas__linux\n' >>$CONFIG_MK ;; @@ -139,7 +140,7 @@ int main(void) { return 0; }' check_func "explicit_bzero" "$src" || { - printf 'OPENBSD += explicit_bzero.c\n' >>$CONFIG_MK + printf 'OPENBSD += explicit_bzero.o\n' >>$CONFIG_MK } @@ -155,7 +156,7 @@ int main(void) { return 0; }' check_func "strlcat" "$src" || { - printf 'OPENBSD += strlcat.c\n' >>$CONFIG_MK + printf 'OPENBSD += strlcat.o\n' >>$CONFIG_MK } # @@ -170,7 +171,7 @@ int main(void) { return 0; }' check_func "strlcpy" "$src" || { - printf 'OPENBSD += strlcpy.c\n' >>$CONFIG_MK + printf 'OPENBSD += strlcpy.o\n' >>$CONFIG_MK } # @@ -183,7 +184,7 @@ int main(void) { return 0; }' check_func "errc" "$src" || { - printf 'OPENBSD += errc.c\n' >>$CONFIG_MK + printf 'OPENBSD += errc.o\n' >>$CONFIG_MK } # @@ -196,7 +197,7 @@ int main(void) { return 0; }' check_func "verrc" "$src" || { - printf 'OPENBSD += verrc.c\n' >>$CONFIG_MK + printf 'OPENBSD += verrc.o\n' >>$CONFIG_MK } # @@ -209,7 +210,7 @@ int main(void) { return 0; }' check_func "setprogname" "$src" || { - printf 'OPENBSD += progname.c\n' >>$CONFIG_MK + printf 'OPENBSD += progname.o\n' >>$CONFIG_MK } # @@ -223,7 +224,7 @@ int main(void) { return 0; }' check_func "readpassphrase" "$src" || { - printf 'OPENBSD += readpassphrase.c\n' >>$CONFIG_MK + printf 'OPENBSD += readpassphrase.o\n' >>$CONFIG_MK } # @@ -237,7 +238,7 @@ int main(void) { return 0; }' check_func "strtonum" "$src" || { - printf 'OPENBSD += strtonum.c\n' >>$CONFIG_MK + printf 'OPENBSD += strtonum.o\n' >>$CONFIG_MK } # @@ -250,7 +251,7 @@ int main(void) { return 0; }' check_func "reallocarray" "$src" || { - printf 'OPENBSD += reallocarray.c\n' >>$CONFIG_MK + printf 'OPENBSD += reallocarray.o\n' >>$CONFIG_MK } # @@ -275,6 +276,7 @@ int main(void) { [ -z "$have_bsd_auth_h" ] && \ check_func "pam_appl_h" "$src" && { printf 'SRCS += doas_pam.c\n' >>$CONFIG_MK + printf 'LDFLAGS += -lpam\n' >>$CONFIG_MK } # @@ -286,7 +288,7 @@ int main(void) { return 0; }' check_func "login_cap_h" "$src" || { - printf 'OPENBSD += setusercontext.c\n' >>$CONFIG_MK + printf 'OPENBSD += setusercontext.o\n' >>$CONFIG_MK } # @@ -300,7 +302,7 @@ int main(void) { return 0; }' check_func "execvpe" "$src" || { - printf 'OPENBSD += execvpe.c\n' >>$CONFIG_MK + printf 'OPENBSD += execvpe.o\n' >>$CONFIG_MK } # @@ -313,7 +315,7 @@ int main(void) { return 0; }' check_func "setresuid" "$src" || { - printf 'OPENBSD += setresuid.c\n' >>$CONFIG_MK + printf 'OPENBSD += setresuid.o\n' >>$CONFIG_MK } # @@ -344,8 +346,8 @@ int main(void) { check_func "seccomp_h" "$src" && \ { have_pledge=1 - printf 'OPENBSD += pledge-seccomp.c\n' >>$CONFIG_MK + printf 'OPENBSD += pledge-seccomp.o\n' >>$CONFIG_MK } [ -z "$have_pledge" ] && \ - printf 'OPENBSD += pledge-noop.c\n' >>$CONFIG_MK + printf 'OPENBSD += pledge-noop.o\n' >>$CONFIG_MK From cbae406f55e6b1da7ec40417f24a5beceefd655d Mon Sep 17 00:00:00 2001 From: Duncaen Date: Sun, 8 May 2016 22:23:26 +0200 Subject: [PATCH 015/198] Make pam session handling more failsafe --- doas_pam.c | 61 +++++++++++++++++++++++++++--------------------------- 1 file changed, 30 insertions(+), 31 deletions(-) diff --git a/doas_pam.c b/doas_pam.c index 473e1fd..c051255 100644 --- a/doas_pam.c +++ b/doas_pam.c @@ -163,29 +163,27 @@ doas_pam(char *name, int interactive, int nopass) } } + ret = pam_acct_mgmt(pamh, 0); + if (ret == PAM_NEW_AUTHTOK_REQD) + ret = pam_chauthtok(pamh, PAM_CHANGE_EXPIRED_AUTHTOK); + + /* account not vaild or changing the auth token failed */ + if (ret != PAM_SUCCESS) + return 0; + ret = pam_setcred(pamh, PAM_ESTABLISH_CRED); if (ret != PAM_SUCCESS) errx(1, "pam_setcred(?, PAM_ESTABLISH_CRED): %s\n", pam_strerror(pamh, ret)); - ret = pam_acct_mgmt(pamh, 0); - if (ret != PAM_SUCCESS) - errx(1, "pam_setcred(): %s\n", pam_strerror(pamh, ret)); - /* open session */ ret = pam_open_session(pamh, 0); if (ret != PAM_SUCCESS) errx(1, "pam_open_session(): %s\n", pam_strerror(pamh, ret)); if ((child = fork()) == -1) { - ret = pam_close_session(pamh, 0); - if (ret != PAM_SUCCESS) - errx(1, "pam_close_session(): %s\n", pam_strerror(pamh, ret)); - - ret = pam_end(pamh, PAM_ABORT); - if (ret != PAM_SUCCESS) - errx(1, "pam_end(): %s\n", pam_strerror(pamh, ret)); - + pam_close_session(pamh, 0); + pam_end(pamh, PAM_ABORT); errx(1, "fork()"); } @@ -202,37 +200,41 @@ doas_pam(char *name, int interactive, int nopass) /* block signals */ sigfillset(&sigs); if (sigprocmask(SIG_BLOCK, &sigs, NULL)) { - errx(1, "sigprocmask()"); + warn("failed to block signals"); + caught_signal = 1; } /* setup signal handler */ act.sa_handler = catchsig; sigemptyset(&act.sa_mask); act.sa_flags = 0; - sigemptyset(&sigs); /* unblock SIGTERM and SIGALRM to catch them */ + sigemptyset(&sigs); if(sigaddset(&sigs, SIGTERM) || sigaddset(&sigs, SIGALRM) || sigaction(SIGTERM, &act, &oldact) || sigprocmask(SIG_UNBLOCK, &sigs, NULL)) { - errx(1, "failed to set signal handler"); + warn("failed to set signal handler"); + caught_signal = 1; } - /* wait for child to be terminated */ - if (waitpid(child, &status, 0) != -1) { - if (WIFSIGNALED(status)) { - fprintf(stderr, "%s%s\n", strsignal(WTERMSIG(status)), - WCOREDUMP(status) ? " (core dumped)" : ""); - status = WTERMSIG(status) + 128; - } else { - status = WEXITSTATUS(status); + if (!caught_signal) { + /* wait for child to be terminated */ + if (waitpid(child, &status, 0) != -1) { + if (WIFSIGNALED(status)) { + fprintf(stderr, "%s%s\n", strsignal(WTERMSIG(status)), + WCOREDUMP(status) ? " (core dumped)" : ""); + status = WTERMSIG(status) + 128; + } else { + status = WEXITSTATUS(status); + } } + else if (caught_signal) + status = caught_signal + 128; + else + status = 1; } - else if (caught_signal) - status = caught_signal + 128; - else - status = 1; if (caught_signal) { fprintf(stderr, "\nSession terminated, killing shell\n"); @@ -244,9 +246,7 @@ doas_pam(char *name, int interactive, int nopass) if (ret != PAM_SUCCESS) errx(1, "pam_close_session(): %s\n", pam_strerror(pamh, ret)); - ret = pam_end(pamh, PAM_SUCCESS); - if (ret != PAM_SUCCESS) - errx(1, "pam_end(): %s\n", pam_strerror(pamh, ret)); + pam_end(pamh, PAM_SUCCESS); if (caught_signal) { /* kill child */ @@ -262,6 +262,5 @@ doas_pam(char *name, int interactive, int nopass) } exit(status); - return 0; } From 1200408d46bfd352b568cbb7c6c9cbf7e8584de3 Mon Sep 17 00:00:00 2001 From: Duncaen Date: Sun, 8 May 2016 22:55:52 +0200 Subject: [PATCH 016/198] Add doas style prompt for pam authentication The doas style prompt "doas ($USER@$HOST) password:" is used if pam gives back a prompt that matches "Password:[ ]" in other cases the prompt provided by pam is used. --- doas_pam.c | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/doas_pam.c b/doas_pam.c index c051255..f8b6e63 100644 --- a/doas_pam.c +++ b/doas_pam.c @@ -24,6 +24,9 @@ #include #include #include +#ifdef __linux__ +#include +#endif #include @@ -34,17 +37,28 @@ static pam_handle_t *pamh = NULL; static sig_atomic_t volatile caught_signal = 0; +static char doas_prompt[128]; static char * prompt(const char *msg, int echo_on, int *pam) { - char buf[PAM_MAX_RESP_SIZE]; + const char *prompt; + char *ret, buf[PAM_MAX_RESP_SIZE]; int flags = RPP_REQUIRE_TTY | (echo_on ? RPP_ECHO_ON : RPP_ECHO_OFF); - char *ret = readpassphrase(msg, buf, sizeof(buf), flags); + + /* overwrite default prompt if it matches "Password:[ ]" */ + if (strncmp(msg,"Password:", 9) == 0 && + (msg[9] == '\0' || (msg[9] == ' ' && msg[10] == '\0'))) + prompt = doas_prompt; + else + prompt = msg; + + ret = readpassphrase(prompt, buf, sizeof(buf), flags); if (!ret) *pam = PAM_CONV_ERR; else if (!(ret = strdup(ret))) *pam = PAM_BUF_ERR; + explicit_bzero(buf, sizeof(buf)); return ret; } @@ -153,6 +167,14 @@ doas_pam(char *name, int interactive, int nopass) if (!nopass) { if (!interactive) errx(1, "Authorization required"); + + /* doas style prompt for pam */ + char host[HOST_NAME_MAX + 1]; + if (gethostname(host, sizeof(host))) + snprintf(host, sizeof(host), "?"); + snprintf(doas_prompt, sizeof(doas_prompt), + "\rdoas (%.32s@%.32s) password: ", name, host); + /* authenticate */ ret = pam_authenticate(pamh, 0); if (ret != PAM_SUCCESS) { From 82fa79900dba3618d9b13c14cecb74ed2c6428b7 Mon Sep 17 00:00:00 2001 From: Svyatoslav Mishyn Date: Mon, 9 May 2016 12:23:55 +0300 Subject: [PATCH 017/198] bsd.prog.mk: add "uninstall" target --- bsd.prog.mk | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/bsd.prog.mk b/bsd.prog.mk index 39d35f1..50bbcb3 100644 --- a/bsd.prog.mk +++ b/bsd.prog.mk @@ -29,6 +29,12 @@ install: ${PROG} ${PAM_DOAS} ${MAN} cp -f doas.1 ${DESTDIR}${MANDIR}/man1 cp -f doas.conf.5 ${DESTDIR}${MANDIR}/man5 +uninstall: + rm -f ${DESTDIR}${BINDIR}/${PROG} + rm -f ${DESTDIR}${PAMDIR}/doas + rm -f ${DESTDIR}${MANDIR}/man1/doas.1 + rm -f ${DESTDIR}${MANDIR}/man5/doas.conf.5 + clean: rm -f version.h rm -f libopenbsd.a @@ -40,4 +46,4 @@ clean: -include ${OBJS:.o=.d} ${OPENBSD:.o=.d} -.PHONY: default clean install +.PHONY: default clean install uninstall From 0f13894a2f9b5fa258d40a3575aaf5dcdf41256b Mon Sep 17 00:00:00 2001 From: Svyatoslav Mishyn Date: Mon, 9 May 2016 12:32:20 +0300 Subject: [PATCH 018/198] fix configure script * "+=" is not working in shell * fix a typo (OS_FLAGS => OS_CFLAGS) --- configure | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/configure b/configure index 5f896c4..0e3fd39 100755 --- a/configure +++ b/configure @@ -91,11 +91,11 @@ if [ -z "$OS" ]; then KERNEL=${REST%%-*} fi -OS_FLAGS="-D__${OS}__" +OS_CFLAGS="-D__${OS}__" case "$OS" in linux) - OS_CFLAGS+=" -D_DEFAULT_SOURCE -D_GNU_SOURCE -DUID_MAX=60000 -DGID_MAX=60000" + OS_CFLAGS="$OS_CFLAGS -D_DEFAULT_SOURCE -D_GNU_SOURCE -DUID_MAX=60000 -DGID_MAX=60000" printf 'CURDIR := .\n' >>$CONFIG_MK printf 'PAM_DOAS = pam.d__doas__linux\n' >>$CONFIG_MK ;; From e60457f5429605e5200c121bbc5ac1cac30c3712 Mon Sep 17 00:00:00 2001 From: Duncaen Date: Thu, 2 Jun 2016 16:27:31 +0200 Subject: [PATCH 019/198] remove nonstandard sys/cdefs.h --- libopenbsd/openbsd.h | 1 - 1 file changed, 1 deletion(-) diff --git a/libopenbsd/openbsd.h b/libopenbsd/openbsd.h index f0eec3d..87be1c0 100644 --- a/libopenbsd/openbsd.h +++ b/libopenbsd/openbsd.h @@ -3,7 +3,6 @@ #include #include -#include #include "readpassphrase.h" From e4bf599cc2019f436f6251b6dbd8aac2876f20b8 Mon Sep 17 00:00:00 2001 From: Duncaen Date: Thu, 2 Jun 2016 16:29:01 +0200 Subject: [PATCH 020/198] check return value of setresuid --- doas.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/doas.c b/doas.c index 6f644ec..3e15824 100644 --- a/doas.c +++ b/doas.c @@ -295,7 +295,9 @@ checkconfig(const char *confpath, int argc, char **argv, { struct rule *rule; - setresuid(uid, uid, uid); + if (setresuid(uid, uid, uid) != 0) + err(1, "setresuid"); + parseconfig(confpath, 0); if (!argc) exit(0); From a55cefe3d13a0e6c66d4d58352bb09e1e8f5282b Mon Sep 17 00:00:00 2001 From: Duncaen Date: Sun, 5 Jun 2016 13:29:58 +0200 Subject: [PATCH 021/198] remove version.h and define VERSION in configure script --- bsd.prog.mk | 1 - configure | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/bsd.prog.mk b/bsd.prog.mk index 50bbcb3..0256856 100644 --- a/bsd.prog.mk +++ b/bsd.prog.mk @@ -36,7 +36,6 @@ uninstall: rm -f ${DESTDIR}${MANDIR}/man5/doas.conf.5 clean: - rm -f version.h rm -f libopenbsd.a rm -f ${OPENBSD} rm -f ${OPENBSD:.o=.d} diff --git a/configure b/configure index 0e3fd39..2039151 100755 --- a/configure +++ b/configure @@ -56,6 +56,7 @@ CONFIG_MK=config.mk rm -f "$CONFIG_MK" : ${VERSION:="$(git describe --dirty --tags --long --always)"} +# : ${VERSION:="0.1"} cat <>$CONFIG_MK PREFIX ?= ${PREFIX:="/usr"} From 7f11114f0f07c653e0ea3d4ae093d7dcdda4a4ef Mon Sep 17 00:00:00 2001 From: Duncaen Date: Sun, 5 Jun 2016 13:33:36 +0200 Subject: [PATCH 022/198] sync with upstream (setenv) add a doas.conf setenv directive that allows setting environment variables explicitly and by copying existing environment variables of a different name. E.g. permit nopass setenv { PS1=$SUDO_PS1 FOO=bar } keepenv :wheel ok tedu@ benno@ --- doas.c | 79 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++- doas.h | 4 ++- parse.y | 72 +++++++++++++++++++++++++++++++++++++++++++++------- 3 files changed, 144 insertions(+), 11 deletions(-) diff --git a/doas.c b/doas.c index 3e15824..c3b520f 100644 --- a/doas.c +++ b/doas.c @@ -1,4 +1,4 @@ -/* $OpenBSD: doas.c,v 1.52 2016/04/28 04:48:56 tedu Exp $ */ +/* $OpenBSD: doas.c,v 1.53 2016/06/05 00:46:34 djm Exp $ */ /* * Copyright (c) 2015 Ted Unangst * @@ -289,6 +289,81 @@ copyenv(const char **oldenvp, struct rule *rule) return envp; } +/* find index of 'name' in environment envp */ +static int +findenv(const char **envp, const char *name, size_t namelen) +{ + int i; + + for (i = 0 ; envp[i] != NULL; i++) { + if (strlen(envp[i]) < namelen + 1) + continue; + if (strncmp(envp[i], name, namelen) == 0 && + envp[i][namelen] == '=') + return i; + } + return -1; +} + +/* merge rule->setenvlist into environment list; frees oldenvp */ +static char ** +dosetenv(char **oldenvp, struct rule *rule) +{ + size_t n, i, nset, nold; + char **envp, *cp, *cp2; + int found; + + if (!(rule->options & SETENV)) + return oldenvp; + + nset = arraylen(rule->setenvlist); + nold = arraylen((const char**)oldenvp); + + /* insert new variables */ + n = 0; + envp = NULL; + for (i = 0; i < nset; i++) { + if ((cp = strchr(rule->setenvlist[i], '=')) == NULL) + errx(1, "invalid setenv"); /* shouldn't happen */ + if (cp[1] == '\0' || cp - rule->setenvlist[i] > INT_MAX) + continue; /* skip variables with empty values */ + if ((envp = reallocarray(envp, n + 2, sizeof(*envp))) == NULL) + errx(1, "reallocarray failed"); + if (cp[1] == '$') { + /* FOO=$BAR: lookup and copy */ + if ((cp2 = getenv(cp + 2)) == NULL) + continue; /* not found; skip */ + if (asprintf(&(envp[n++]), "%.*s=%s", + (int)(cp - rule->setenvlist[i]), + rule->setenvlist[i], cp2) == -1) + errx(1, "asprintf failed"); + continue; + } else { + /* plain setenv */ + if ((envp[n++] = strdup(rule->setenvlist[i])) == NULL) + errx(1, "strdup failed"); + } + } + /* move old variables, dropping ones already set */ + for (i = 0; i < nold; i++) { + if ((cp = strchr(oldenvp[i], '=')) == NULL) + errx(1, "invalid env"); /* shouldn't happen */ + found = findenv(rule->setenvlist, oldenvp[i], cp - oldenvp[i]); + if (found != -1) + free(oldenvp[i]); /* discard */ + else { + if ((envp = reallocarray(envp, n + 2, + sizeof(*envp))) == NULL) + errx(1, "reallocarray failed"); + envp[n++] = oldenvp[i]; /* move */ + } + } + free(oldenvp); + if (n > 0) + envp[n] = NULL; + return envp; +} + static void __dead checkconfig(const char *confpath, int argc, char **argv, uid_t uid, gid_t *groups, int ngroups, uid_t target) @@ -511,6 +586,8 @@ main(int argc, char **argv, char **envp) envp = copyenv((const char **)envp, rule); + envp = dosetenv(envp, rule); + if (rule->cmd) { if (setenv("PATH", safepath, 1) == -1) err(1, "failed to set PATH '%s'", safepath); diff --git a/doas.h b/doas.h index 235df9f..70005ee 100644 --- a/doas.h +++ b/doas.h @@ -1,4 +1,4 @@ -/* $OpenBSD: doas.h,v 1.3 2015/07/21 11:04:06 zhuk Exp $ */ +/* $OpenBSD: doas.h,v 1.5 2016/06/05 00:46:34 djm Exp $ */ struct rule { int action; @@ -8,6 +8,7 @@ struct rule { const char *cmd; const char **cmdargs; const char **envlist; + const char **setenvlist; }; extern struct rule **rules; @@ -21,3 +22,4 @@ size_t arraylen(const char **); #define NOPASS 0x1 #define KEEPENV 0x2 +#define SETENV 0x4 diff --git a/parse.y b/parse.y index 0307b0f..6166cee 100644 --- a/parse.y +++ b/parse.y @@ -1,4 +1,4 @@ -/* $OpenBSD: parse.y,v 1.10 2015/07/24 06:36:42 zhuk Exp $ */ +/* $OpenBSD: parse.y,v 1.16 2016/06/05 00:46:34 djm Exp $ */ /* * Copyright (c) 2015 Ted Unangst * @@ -18,13 +18,13 @@ %{ #include #include -#include +#include +#include #include #include -#include #include #include -#include +#include #include "openbsd.h" @@ -38,6 +38,7 @@ typedef struct { const char *cmd; const char **cmdargs; const char **envlist; + const char **setenvlist; }; const char *str; }; @@ -59,7 +60,7 @@ int yyparse(void); %} %token TPERMIT TDENY TAS TCMD TARGS -%token TNOPASS TKEEPENV +%token TNOPASS TKEEPENV TSETENV %token TSTRING %% @@ -78,6 +79,7 @@ rule: action ident target cmd { r->action = $1.action; r->options = $1.options; r->envlist = $1.envlist; + r->setenvlist = $1.setenvlist; r->ident = $2.str; r->target = $3.str; r->cmd = $4.cmd; @@ -98,6 +100,7 @@ action: TPERMIT options { $$.action = PERMIT; $$.options = $2.options; $$.envlist = $2.envlist; + $$.setenvlist = $2.setenvlist; } | TDENY { $$.action = DENY; } ; @@ -115,6 +118,14 @@ options: /* none */ { } else $$.envlist = $2.envlist; } + $$.setenvlist = $1.setenvlist; + if ($2.setenvlist) { + if ($$.setenvlist) { + yyerror("can't have two setenv sections"); + YYERROR; + } else + $$.setenvlist = $2.setenvlist; + } } ; option: TNOPASS { $$.options = NOPASS; @@ -125,10 +136,16 @@ option: TNOPASS { } | TKEEPENV '{' envlist '}' { $$.options = KEEPENV; $$.envlist = $3.envlist; + } | TSETENV '{' setenvlist '}' { + $$.options = SETENV; + $$.setenvlist = NULL; + $$.setenvlist = $3.setenvlist; } ; envlist: /* empty */ { $$.envlist = NULL; + if (!($$.envlist = calloc(1, sizeof(char *)))) + errx(1, "can't allocate envlist"); } | envlist TSTRING { int nenv = arraylen($1.envlist); if (!($$.envlist = reallocarray($1.envlist, nenv + 2, @@ -138,6 +155,28 @@ envlist: /* empty */ { $$.envlist[nenv + 1] = NULL; } +setenvlist: /* empty */ { + if (!($$.setenvlist = calloc(1, sizeof(char *)))) + errx(1, "can't allocate setenvlist"); + } | setenvlist TSTRING '=' TSTRING { + int nenv = arraylen($1.setenvlist); + char *cp = NULL; + + if (*$2.str == '\0' || strchr($2.str, '=') != NULL) { + yyerror("invalid setenv expression"); + YYERROR; + } + if (!($$.setenvlist = reallocarray($1.setenvlist, + nenv + 2, sizeof(char *)))) + errx(1, "can't allocate envlist"); + $$.setenvlist[nenv] = NULL; + if (asprintf(&cp, "%s=%s", $2.str, $4.str) <= 0 || + cp == NULL) + errx(1,"asprintf failed"); + $$.setenvlist[nenv] = cp; + $$.setenvlist[nenv + 1] = NULL; + } + ident: TSTRING { $$.str = $1.str; @@ -165,6 +204,8 @@ args: /* empty */ { argslist: /* empty */ { $$.cmdargs = NULL; + if (!($$.cmdargs = calloc(1, sizeof(char *)))) + errx(1, "can't allocate args"); } | argslist TSTRING { int nargs = arraylen($1.cmdargs); if (!($$.cmdargs = reallocarray($1.cmdargs, nargs + 2, @@ -181,6 +222,7 @@ yyerror(const char *fmt, ...) { va_list va; + fprintf(stderr, "doas: "); va_start(va, fmt); vfprintf(stderr, fmt, va); va_end(va); @@ -199,6 +241,7 @@ struct keyword { { "args", TARGS }, { "nopass", TNOPASS }, { "keepenv", TKEEPENV }, + { "setenv", TSETENV }, }; int @@ -223,17 +266,18 @@ repeat: /* FALLTHROUGH */ case '{': case '}': + case '=': return c; case '#': /* skip comments; NUL is allowed; no continuation */ while ((c = getc(yyfp)) != '\n') if (c == EOF) - return 0; + goto eof; yylval.colno = 0; yylval.lineno++; return c; case EOF: - return 0; + goto eof; } /* parsing next word */ @@ -256,6 +300,8 @@ repeat: if (escape) { nonkw = 1; escape = 0; + yylval.colno = 0; + yylval.lineno++; continue; } goto eow; @@ -273,6 +319,7 @@ repeat: case '#': case ' ': case '\t': + case '=': if (!escape && !quotes) goto eow; break; @@ -287,8 +334,10 @@ repeat: } } *p++ = c; - if (p == ebuf) + if (p == ebuf) { yyerror("too long line"); + p = buf; + } escape = 0; } @@ -303,7 +352,7 @@ eow: * the main loop. */ if (c == EOF) - return 0; + goto eof; else if (qpos == -1) /* accept, e.g., empty args: cmd foo args "" */ goto repeat; } @@ -318,4 +367,9 @@ eow: err(1, "strdup"); yylval.str = str; return TSTRING; + +eof: + if (ferror(yyfp)) + yyerror("input error reading config"); + return 0; } From e939687b7a7aa2258ec27b819b87e33e80a7fa20 Mon Sep 17 00:00:00 2001 From: Duncaen Date: Sun, 5 Jun 2016 13:42:30 +0200 Subject: [PATCH 023/198] fix ld and cflags --- bsd.prog.mk | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bsd.prog.mk b/bsd.prog.mk index 0256856..f5b3efe 100644 --- a/bsd.prog.mk +++ b/bsd.prog.mk @@ -2,7 +2,7 @@ default: ${PROG} -CFLAGS += -I${CURDIR}/libopenbsd ${COPTS} -MD -MP +CFLAGS += -I${CURDIR}/libopenbsd ${COPTS} -MD -MP -Wno-unused-result include config.mk @@ -14,7 +14,7 @@ libopenbsd.a: ${OPENBSD} ${AR} -r $@ $? ${PROG}: ${OBJS} libopenbsd.a - ${CC} ${CFLAGS} ${LDFLAGS} $^ -o $@ + ${CC} ${CFLAGS} $^ -o $@ ${LDFLAGS} install: ${PROG} ${PAM_DOAS} ${MAN} mkdir -p -m 0755 ${DESTDIR}${BINDIR} From 5c50281be4c0624a78844e82d30e608e5d2593da Mon Sep 17 00:00:00 2001 From: Duncaen Date: Sun, 5 Jun 2016 13:58:30 +0200 Subject: [PATCH 024/198] add more restrictive permissions and root:root as owner for binary --- Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 5dbb817..66792eb 100644 --- a/Makefile +++ b/Makefile @@ -6,8 +6,8 @@ PROG= doas MAN= doas.1 doas.conf.5 BINOWN= root -BINGRP= wheel -BINMODE=4511 +BINGRP= root +BINMODE=4111 CFLAGS+= -I${CURDIR} COPTS+= -Wall -Wextra -Werror -pedantic -std=c11 From 63a642ef530e6421f2bc3648833611f2048f7638 Mon Sep 17 00:00:00 2001 From: Duncaen Date: Sun, 5 Jun 2016 14:01:31 +0200 Subject: [PATCH 025/198] bump version to 0.2 --- LICENSE | 19 +++++++++++++++---- configure | 4 ++-- 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/LICENSE b/LICENSE index 2494fc0..8dc155d 100644 --- a/LICENSE +++ b/LICENSE @@ -1,8 +1,19 @@ -portions copyright (c) 2015 Nathan Holstein -portions copyright (c) 2015 Ted Unangst -portions copyright (c) 2016 Duncan Overbruck +Copyright (c) 2015 Ted Unangst +Copyright (c) 2015 Nathan Holstein +Copyright (c) 2016 Duncan Overbruck + +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. -To the best of my knowledge, everything is released under the BSD license. Additional bits copyright of their respective authors, see individual files for details. diff --git a/configure b/configure index 2039151..cc23132 100755 --- a/configure +++ b/configure @@ -55,8 +55,8 @@ done CONFIG_MK=config.mk rm -f "$CONFIG_MK" -: ${VERSION:="$(git describe --dirty --tags --long --always)"} -# : ${VERSION:="0.1"} +# : ${VERSION:="$(git describe --dirty --tags --long --always)"} +: ${VERSION:="0.2"} cat <>$CONFIG_MK PREFIX ?= ${PREFIX:="/usr"} From 21c6e427af5275a1879cd027b5534e63528e1349 Mon Sep 17 00:00:00 2001 From: Duncaen Date: Wed, 8 Jun 2016 13:41:25 +0200 Subject: [PATCH 026/198] Revert "sync with upstream (setenv)" This reverts commit 7f11114f0f07c653e0ea3d4ae093d7dcdda4a4ef. --- doas.c | 79 +-------------------------------------------------------- doas.h | 4 +-- parse.y | 72 +++++++--------------------------------------------- 3 files changed, 11 insertions(+), 144 deletions(-) diff --git a/doas.c b/doas.c index c3b520f..3e15824 100644 --- a/doas.c +++ b/doas.c @@ -1,4 +1,4 @@ -/* $OpenBSD: doas.c,v 1.53 2016/06/05 00:46:34 djm Exp $ */ +/* $OpenBSD: doas.c,v 1.52 2016/04/28 04:48:56 tedu Exp $ */ /* * Copyright (c) 2015 Ted Unangst * @@ -289,81 +289,6 @@ copyenv(const char **oldenvp, struct rule *rule) return envp; } -/* find index of 'name' in environment envp */ -static int -findenv(const char **envp, const char *name, size_t namelen) -{ - int i; - - for (i = 0 ; envp[i] != NULL; i++) { - if (strlen(envp[i]) < namelen + 1) - continue; - if (strncmp(envp[i], name, namelen) == 0 && - envp[i][namelen] == '=') - return i; - } - return -1; -} - -/* merge rule->setenvlist into environment list; frees oldenvp */ -static char ** -dosetenv(char **oldenvp, struct rule *rule) -{ - size_t n, i, nset, nold; - char **envp, *cp, *cp2; - int found; - - if (!(rule->options & SETENV)) - return oldenvp; - - nset = arraylen(rule->setenvlist); - nold = arraylen((const char**)oldenvp); - - /* insert new variables */ - n = 0; - envp = NULL; - for (i = 0; i < nset; i++) { - if ((cp = strchr(rule->setenvlist[i], '=')) == NULL) - errx(1, "invalid setenv"); /* shouldn't happen */ - if (cp[1] == '\0' || cp - rule->setenvlist[i] > INT_MAX) - continue; /* skip variables with empty values */ - if ((envp = reallocarray(envp, n + 2, sizeof(*envp))) == NULL) - errx(1, "reallocarray failed"); - if (cp[1] == '$') { - /* FOO=$BAR: lookup and copy */ - if ((cp2 = getenv(cp + 2)) == NULL) - continue; /* not found; skip */ - if (asprintf(&(envp[n++]), "%.*s=%s", - (int)(cp - rule->setenvlist[i]), - rule->setenvlist[i], cp2) == -1) - errx(1, "asprintf failed"); - continue; - } else { - /* plain setenv */ - if ((envp[n++] = strdup(rule->setenvlist[i])) == NULL) - errx(1, "strdup failed"); - } - } - /* move old variables, dropping ones already set */ - for (i = 0; i < nold; i++) { - if ((cp = strchr(oldenvp[i], '=')) == NULL) - errx(1, "invalid env"); /* shouldn't happen */ - found = findenv(rule->setenvlist, oldenvp[i], cp - oldenvp[i]); - if (found != -1) - free(oldenvp[i]); /* discard */ - else { - if ((envp = reallocarray(envp, n + 2, - sizeof(*envp))) == NULL) - errx(1, "reallocarray failed"); - envp[n++] = oldenvp[i]; /* move */ - } - } - free(oldenvp); - if (n > 0) - envp[n] = NULL; - return envp; -} - static void __dead checkconfig(const char *confpath, int argc, char **argv, uid_t uid, gid_t *groups, int ngroups, uid_t target) @@ -586,8 +511,6 @@ main(int argc, char **argv, char **envp) envp = copyenv((const char **)envp, rule); - envp = dosetenv(envp, rule); - if (rule->cmd) { if (setenv("PATH", safepath, 1) == -1) err(1, "failed to set PATH '%s'", safepath); diff --git a/doas.h b/doas.h index 70005ee..235df9f 100644 --- a/doas.h +++ b/doas.h @@ -1,4 +1,4 @@ -/* $OpenBSD: doas.h,v 1.5 2016/06/05 00:46:34 djm Exp $ */ +/* $OpenBSD: doas.h,v 1.3 2015/07/21 11:04:06 zhuk Exp $ */ struct rule { int action; @@ -8,7 +8,6 @@ struct rule { const char *cmd; const char **cmdargs; const char **envlist; - const char **setenvlist; }; extern struct rule **rules; @@ -22,4 +21,3 @@ size_t arraylen(const char **); #define NOPASS 0x1 #define KEEPENV 0x2 -#define SETENV 0x4 diff --git a/parse.y b/parse.y index 6166cee..0307b0f 100644 --- a/parse.y +++ b/parse.y @@ -1,4 +1,4 @@ -/* $OpenBSD: parse.y,v 1.16 2016/06/05 00:46:34 djm Exp $ */ +/* $OpenBSD: parse.y,v 1.10 2015/07/24 06:36:42 zhuk Exp $ */ /* * Copyright (c) 2015 Ted Unangst * @@ -18,13 +18,13 @@ %{ #include #include -#include -#include +#include #include #include +#include #include #include -#include +#include #include "openbsd.h" @@ -38,7 +38,6 @@ typedef struct { const char *cmd; const char **cmdargs; const char **envlist; - const char **setenvlist; }; const char *str; }; @@ -60,7 +59,7 @@ int yyparse(void); %} %token TPERMIT TDENY TAS TCMD TARGS -%token TNOPASS TKEEPENV TSETENV +%token TNOPASS TKEEPENV %token TSTRING %% @@ -79,7 +78,6 @@ rule: action ident target cmd { r->action = $1.action; r->options = $1.options; r->envlist = $1.envlist; - r->setenvlist = $1.setenvlist; r->ident = $2.str; r->target = $3.str; r->cmd = $4.cmd; @@ -100,7 +98,6 @@ action: TPERMIT options { $$.action = PERMIT; $$.options = $2.options; $$.envlist = $2.envlist; - $$.setenvlist = $2.setenvlist; } | TDENY { $$.action = DENY; } ; @@ -118,14 +115,6 @@ options: /* none */ { } else $$.envlist = $2.envlist; } - $$.setenvlist = $1.setenvlist; - if ($2.setenvlist) { - if ($$.setenvlist) { - yyerror("can't have two setenv sections"); - YYERROR; - } else - $$.setenvlist = $2.setenvlist; - } } ; option: TNOPASS { $$.options = NOPASS; @@ -136,16 +125,10 @@ option: TNOPASS { } | TKEEPENV '{' envlist '}' { $$.options = KEEPENV; $$.envlist = $3.envlist; - } | TSETENV '{' setenvlist '}' { - $$.options = SETENV; - $$.setenvlist = NULL; - $$.setenvlist = $3.setenvlist; } ; envlist: /* empty */ { $$.envlist = NULL; - if (!($$.envlist = calloc(1, sizeof(char *)))) - errx(1, "can't allocate envlist"); } | envlist TSTRING { int nenv = arraylen($1.envlist); if (!($$.envlist = reallocarray($1.envlist, nenv + 2, @@ -155,28 +138,6 @@ envlist: /* empty */ { $$.envlist[nenv + 1] = NULL; } -setenvlist: /* empty */ { - if (!($$.setenvlist = calloc(1, sizeof(char *)))) - errx(1, "can't allocate setenvlist"); - } | setenvlist TSTRING '=' TSTRING { - int nenv = arraylen($1.setenvlist); - char *cp = NULL; - - if (*$2.str == '\0' || strchr($2.str, '=') != NULL) { - yyerror("invalid setenv expression"); - YYERROR; - } - if (!($$.setenvlist = reallocarray($1.setenvlist, - nenv + 2, sizeof(char *)))) - errx(1, "can't allocate envlist"); - $$.setenvlist[nenv] = NULL; - if (asprintf(&cp, "%s=%s", $2.str, $4.str) <= 0 || - cp == NULL) - errx(1,"asprintf failed"); - $$.setenvlist[nenv] = cp; - $$.setenvlist[nenv + 1] = NULL; - } - ident: TSTRING { $$.str = $1.str; @@ -204,8 +165,6 @@ args: /* empty */ { argslist: /* empty */ { $$.cmdargs = NULL; - if (!($$.cmdargs = calloc(1, sizeof(char *)))) - errx(1, "can't allocate args"); } | argslist TSTRING { int nargs = arraylen($1.cmdargs); if (!($$.cmdargs = reallocarray($1.cmdargs, nargs + 2, @@ -222,7 +181,6 @@ yyerror(const char *fmt, ...) { va_list va; - fprintf(stderr, "doas: "); va_start(va, fmt); vfprintf(stderr, fmt, va); va_end(va); @@ -241,7 +199,6 @@ struct keyword { { "args", TARGS }, { "nopass", TNOPASS }, { "keepenv", TKEEPENV }, - { "setenv", TSETENV }, }; int @@ -266,18 +223,17 @@ repeat: /* FALLTHROUGH */ case '{': case '}': - case '=': return c; case '#': /* skip comments; NUL is allowed; no continuation */ while ((c = getc(yyfp)) != '\n') if (c == EOF) - goto eof; + return 0; yylval.colno = 0; yylval.lineno++; return c; case EOF: - goto eof; + return 0; } /* parsing next word */ @@ -300,8 +256,6 @@ repeat: if (escape) { nonkw = 1; escape = 0; - yylval.colno = 0; - yylval.lineno++; continue; } goto eow; @@ -319,7 +273,6 @@ repeat: case '#': case ' ': case '\t': - case '=': if (!escape && !quotes) goto eow; break; @@ -334,10 +287,8 @@ repeat: } } *p++ = c; - if (p == ebuf) { + if (p == ebuf) yyerror("too long line"); - p = buf; - } escape = 0; } @@ -352,7 +303,7 @@ eow: * the main loop. */ if (c == EOF) - goto eof; + return 0; else if (qpos == -1) /* accept, e.g., empty args: cmd foo args "" */ goto repeat; } @@ -367,9 +318,4 @@ eow: err(1, "strdup"); yylval.str = str; return TSTRING; - -eof: - if (ferror(yyfp)) - yyerror("input error reading config"); - return 0; } From 4f7ed3854a4db241401f5b84cbb6246a06a17561 Mon Sep 17 00:00:00 2001 From: Duncaen Date: Wed, 8 Jun 2016 17:50:28 +0200 Subject: [PATCH 027/198] open pam sessions with right user and remove setusercontext shim before this change the sessions were opened as the user running doas. Now it sets its uid to root and then opens a pam session for the target user. The setusercontext shim was removed, because pam handles all this and its easier to just call setresuid and setresgid instead. --- configure | 12 ------- doas.c | 22 ++++++++---- doas_pam.c | 67 +++++++++++++++++++------------------ includes.h | 2 +- libopenbsd/openbsd.h | 17 ---------- libopenbsd/setusercontext.c | 66 ------------------------------------ pam.d__doas__linux | 5 ++- 7 files changed, 52 insertions(+), 139 deletions(-) delete mode 100644 libopenbsd/setusercontext.c diff --git a/configure b/configure index cc23132..3e34695 100755 --- a/configure +++ b/configure @@ -280,18 +280,6 @@ int main(void) { printf 'LDFLAGS += -lpam\n' >>$CONFIG_MK } -# -# Check for login_cap.h. -# -src=' -#include -int main(void) { - return 0; -}' -check_func "login_cap_h" "$src" || { - printf 'OPENBSD += setusercontext.o\n' >>$CONFIG_MK -} - # # Check for execvpe(). # diff --git a/doas.c b/doas.c index 3e15824..f1b2ec0 100644 --- a/doas.c +++ b/doas.c @@ -439,6 +439,10 @@ main(int argc, char **argv, char **envp) errc(1, EPERM, NULL); } + pw = getpwuid(target); + if (!pw) + errx(1, "no passwd entry for target"); + #ifdef HAVE_BSD_AUTH_H if (!(rule->options & NOPASS)) { if (nflag) @@ -473,9 +477,8 @@ main(int argc, char **argv, char **envp) explicit_bzero(rbuf, sizeof(rbuf)); } #elif HAVE_PAM_APPL_H - if (!doas_pam(myname, !nflag, rule->options & NOPASS)) { - syslog(LOG_AUTHPRIV | LOG_NOTICE, - "failed auth for %s", myname); + if (!doas_pam(pw->pw_name, myname, !nflag, rule->options & NOPASS)) { + syslog(LOG_AUTHPRIV | LOG_NOTICE, "failed auth for %s", myname); errc(1, EPERM, NULL); } #else @@ -486,14 +489,19 @@ main(int argc, char **argv, char **envp) if (pledge("stdio rpath getpw exec id", NULL) == -1) err(1, "pledge"); - pw = getpwuid(target); - if (!pw) - errx(1, "no passwd entry for target"); - +#ifdef HAVE_BSD_AUTH_H if (setusercontext(NULL, pw, target, LOGIN_SETGROUP | LOGIN_SETPRIORITY | LOGIN_SETRESOURCES | LOGIN_SETUMASK | LOGIN_SETUSER) != 0) errx(1, "failed to set user context for target"); +#else + if (setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) != 0) + errx(1, "setgid"); + if (initgroups(pw->pw_name, pw->pw_gid) != 0) + errx(1, "initgroups"); + if (setresuid(target, target, target) != 0) + errx(1, "setuid"); +#endif if (pledge("stdio rpath exec", NULL) == -1) err(1, "pledge"); diff --git a/doas_pam.c b/doas_pam.c index f8b6e63..4f2731d 100644 --- a/doas_pam.c +++ b/doas_pam.c @@ -39,6 +39,12 @@ static pam_handle_t *pamh = NULL; static sig_atomic_t volatile caught_signal = 0; static char doas_prompt[128]; +static void +catchsig(int sig) +{ + caught_signal = sig; +} + static char * prompt(const char *msg, int echo_on, int *pam) { @@ -48,7 +54,7 @@ prompt(const char *msg, int echo_on, int *pam) /* overwrite default prompt if it matches "Password:[ ]" */ if (strncmp(msg,"Password:", 9) == 0 && - (msg[9] == '\0' || (msg[9] == ' ' && msg[10] == '\0'))) + (msg[9] == '\0' || (msg[9] == ' ' && msg[10] == '\0'))) prompt = doas_prompt; else prompt = msg; @@ -86,7 +92,7 @@ doas_pam_conv(int nmsgs, const struct pam_message **msgs, case PAM_ERROR_MSG: case PAM_TEXT_INFO: if (fprintf(style == PAM_ERROR_MSG ? stderr : stdout, - "%s\n", msgs[i]->msg) < 0) + "%s\n", msgs[i]->msg) < 0) goto fail; break; @@ -117,14 +123,8 @@ doas_pam_conv(int nmsgs, const struct pam_message **msgs, return PAM_CONV_ERR; } -static void -catchsig(int sig) -{ - caught_signal = sig; -} - int -doas_pam(char *name, int interactive, int nopass) +doas_pam(const char *user, const char* ruser, int interactive, int nopass) { static const struct pam_conv conv = { .conv = doas_pam_conv, @@ -134,23 +134,22 @@ doas_pam(char *name, int interactive, int nopass) pid_t child; int ret; - if (!name) + if (!user || !ruser) return 0; - ret = pam_start(PAM_SERVICE_NAME, name, &conv, &pamh); - if (ret != PAM_SUCCESS) - errx(1, "pam_start(\"%s\", \"%s\", ?, ?): failed\n", - PAM_SERVICE_NAME, name); + /* pam needs the real root */ + if(setuid(0)) + errx(1, "setuid"); - ret = pam_set_item(pamh, PAM_USER, name); + ret = pam_start(PAM_SERVICE_NAME, ruser, &conv, &pamh); if (ret != PAM_SUCCESS) - errx(1, "pam_set_item(?, PAM_USER, \"%s\"): %s\n", - name, pam_strerror(pamh, ret)); + errx(1, "pam_start(\"%s\", \"%s\", ?, ?): failed\n", + PAM_SERVICE_NAME, ruser); - ret = pam_set_item(pamh, PAM_RUSER, name); + ret = pam_set_item(pamh, PAM_RUSER, ruser); if (ret != PAM_SUCCESS) - errx(1, "pam_set_item(?, PAM_RUSER, \"%s\"): %s\n", - name, pam_strerror(pamh, ret)); + warn("pam_set_item(?, PAM_RUSER, \"%s\"): %s\n", + pam_strerror(pamh, ret), ruser); if (isatty(0) && (ttydev = ttyname(0)) != NULL) { if (strncmp(ttydev, "/dev/", 5)) @@ -160,8 +159,8 @@ doas_pam(char *name, int interactive, int nopass) ret = pam_set_item(pamh, PAM_TTY, tty); if (ret != PAM_SUCCESS) - errx(1, "pam_set_item(?, PAM_TTY, \"%s\"): %s\n", - tty, pam_strerror(pamh, ret)); + warn("pam_set_item(?, PAM_TTY, \"%s\"): %s\n", + tty, pam_strerror(pamh, ret)); } if (!nopass) { @@ -173,14 +172,12 @@ doas_pam(char *name, int interactive, int nopass) if (gethostname(host, sizeof(host))) snprintf(host, sizeof(host), "?"); snprintf(doas_prompt, sizeof(doas_prompt), - "\rdoas (%.32s@%.32s) password: ", name, host); + "\rdoas (%.32s@%.32s) password: ", ruser, host); /* authenticate */ ret = pam_authenticate(pamh, 0); if (ret != PAM_SUCCESS) { - ret = pam_end(pamh, ret); - if (ret != PAM_SUCCESS) - errx(1, "pam_end(): %s\n", pam_strerror(pamh, ret)); + pam_end(pamh, ret); return 0; } } @@ -193,10 +190,14 @@ doas_pam(char *name, int interactive, int nopass) if (ret != PAM_SUCCESS) return 0; + ret = pam_set_item(pamh, PAM_USER, user); + if (ret != PAM_SUCCESS) + warn("pam_set_item(?, PAM_USER, \"%s\"): %s\n", user, + pam_strerror(pamh, ret)); + ret = pam_setcred(pamh, PAM_ESTABLISH_CRED); if (ret != PAM_SUCCESS) - errx(1, "pam_setcred(?, PAM_ESTABLISH_CRED): %s\n", - pam_strerror(pamh, ret)); + warn("pam_setcred(?, PAM_ESTABLISH_CRED): %s\n", pam_strerror(pamh, ret)); /* open session */ ret = pam_open_session(pamh, 0); @@ -233,10 +234,10 @@ doas_pam(char *name, int interactive, int nopass) /* unblock SIGTERM and SIGALRM to catch them */ sigemptyset(&sigs); - if(sigaddset(&sigs, SIGTERM) || - sigaddset(&sigs, SIGALRM) || - sigaction(SIGTERM, &act, &oldact) || - sigprocmask(SIG_UNBLOCK, &sigs, NULL)) { + if (sigaddset(&sigs, SIGTERM) || + sigaddset(&sigs, SIGALRM) || + sigaction(SIGTERM, &act, &oldact) || + sigprocmask(SIG_UNBLOCK, &sigs, NULL)) { warn("failed to set signal handler"); caught_signal = 1; } @@ -246,7 +247,7 @@ doas_pam(char *name, int interactive, int nopass) if (waitpid(child, &status, 0) != -1) { if (WIFSIGNALED(status)) { fprintf(stderr, "%s%s\n", strsignal(WTERMSIG(status)), - WCOREDUMP(status) ? " (core dumped)" : ""); + WCOREDUMP(status) ? " (core dumped)" : ""); status = WTERMSIG(status) + 128; } else { status = WEXITSTATUS(status); diff --git a/includes.h b/includes.h index e9ebf63..776a940 100644 --- a/includes.h +++ b/includes.h @@ -20,7 +20,7 @@ #include "openbsd.h" #ifdef HAVE_PAM_APPL_H -int doas_pam(char *name, int interactive, int nopass); +int doas_pam(const char *user, const char *ruser, int interactive, int nopass); #endif #endif /* INCLUDES_H */ diff --git a/libopenbsd/openbsd.h b/libopenbsd/openbsd.h index 87be1c0..0586844 100644 --- a/libopenbsd/openbsd.h +++ b/libopenbsd/openbsd.h @@ -8,23 +8,6 @@ /* API definitions lifted from OpenBSD src/include */ -/* login_cap.h */ -#ifndef HAVE_LOGIN_CAP_H -#define LOGIN_SETGROUP 0x0001 /* Set group */ -#define LOGIN_SETLOGIN 0x0002 /* Set login */ -#define LOGIN_SETPATH 0x0004 /* Set path */ -#define LOGIN_SETPRIORITY 0x0008 /* Set priority */ -#define LOGIN_SETRESOURCES 0x0010 /* Set resource limits */ -#define LOGIN_SETUMASK 0x0020 /* Set umask */ -#define LOGIN_SETUSER 0x0040 /* Set user */ -#define LOGIN_SETENV 0x0080 /* Set environment */ -#define LOGIN_SETALL 0x00ff /* Set all. */ - -typedef struct login_cap login_cap_t; -struct passwd; -int setusercontext(login_cap_t *, struct passwd *, uid_t, unsigned int); -#endif /* !HAVE_LOGIN_CAP_H */ - /* pwd.h */ #define _PW_NAME_LEN 63 diff --git a/libopenbsd/setusercontext.c b/libopenbsd/setusercontext.c deleted file mode 100644 index 6b05dd5..0000000 --- a/libopenbsd/setusercontext.c +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright (c) 2015 Nathan Holstein - * - * 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 -#include -#include -#include -#include -#include -#include -#include - -#include "includes.h" - -int -setusercontext(login_cap_t *lc, struct passwd *pw, uid_t uid, unsigned int flags) -{ - int ret; - - if (lc != NULL || pw == NULL || - (flags & ~(LOGIN_SETGROUP | LOGIN_SETPRIORITY | - LOGIN_SETRESOURCES | LOGIN_SETUMASK | - LOGIN_SETUSER)) != 0) { - errno = EINVAL; - return -1; - } - - if (flags & LOGIN_SETGROUP) { - if ((ret = setgid(pw->pw_gid)) != 0) - return ret; - if ((ret = initgroups(pw->pw_name, pw->pw_gid)) != 0) - return ret; - } - - if (flags & LOGIN_SETPRIORITY) { - if ((ret = setpriority(PRIO_PROCESS, getpid(), 0)) != 0) - return ret; - if ((ret = setpriority(PRIO_USER, uid, 0)) != 0) - return ret; - } - - if (flags & LOGIN_SETRESOURCES) { - } - - if (flags & LOGIN_SETUMASK) - umask(S_IWGRP | S_IWOTH); - - if (flags & LOGIN_SETUSER) - return setuid(uid); - - return 0; -} - diff --git a/pam.d__doas__linux b/pam.d__doas__linux index 9781fb2..813a0e4 100644 --- a/pam.d__doas__linux +++ b/pam.d__doas__linux @@ -1,10 +1,9 @@ #%PAM-1.0 -auth sufficient pam_timestamp.so timestamp_timeout=300 verbose debug -auth sufficient pam_rootok.so +auth sufficient pam_timestamp.so timestamp_timeout=300 auth required pam_unix.so account required pam_unix.so session optional pam_xauth.so session optional pam_umask.so usergroups umask=022 -session optional pam_timestamp.so timestamp_timeout=300 debug +session optional pam_timestamp.so timestamp_timeout=300 session required pam_env.so session required pam_unix.so From f577047ea6e6f0e4e4225200504fd694681dbed2 Mon Sep 17 00:00:00 2001 From: Duncaen Date: Wed, 8 Jun 2016 18:01:25 +0200 Subject: [PATCH 028/198] remove pledge seccomp shim This will never work, seccomp can't filter for paths (pointer) and all rules are inherited by child processes. pledge does not limit processes executed by execve. --- configure | 27 +-- libopenbsd/pledge-seccomp.c | 411 ------------------------------------ libopenbsd/pledge-seccomp.h | 49 ----- 3 files changed, 2 insertions(+), 485 deletions(-) delete mode 100644 libopenbsd/pledge-seccomp.c delete mode 100644 libopenbsd/pledge-seccomp.h diff --git a/configure b/configure index 3e34695..d854321 100755 --- a/configure +++ b/configure @@ -22,7 +22,6 @@ usage: configure [options] --target=target-alias the machine that CC will produce code for --enable-debug enable debugging - --enable-seccomp enable seccomp --enable-static prepare for static build --help, -h display this help and exit @@ -45,7 +44,6 @@ for x; do --host) HOST=$var;; --target) TARGET=$var;; --enable-debug) DEBUG=yes;; - --enable-seccomp) BUILD_SECCOMP=yes;; --enable-static) BUILD_STATIC=yes;; --help|-h) usage;; *) die "Error: unknown option $opt";; @@ -316,27 +314,6 @@ int main(void) { pledge("", NULL); return 0; }' -check_func "pledge" "$src" && { - have_pledge=1 -} - -# -# Check for seccomp.h -# -src=' -#include -#include -#include -int main(void) { - prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, NULL); - return 0; -}' -[ -z "$have_pledge" -a -n "$BUILD_SECCOMP" ] && \ - check_func "seccomp_h" "$src" && \ - { - have_pledge=1 - printf 'OPENBSD += pledge-seccomp.o\n' >>$CONFIG_MK - } - -[ -z "$have_pledge" ] && \ +check_func "pledge" "$src" || { printf 'OPENBSD += pledge-noop.o\n' >>$CONFIG_MK +} diff --git a/libopenbsd/pledge-seccomp.c b/libopenbsd/pledge-seccomp.c deleted file mode 100644 index 71d8a7d..0000000 --- a/libopenbsd/pledge-seccomp.c +++ /dev/null @@ -1,411 +0,0 @@ -/* $OpenBSD: kern_pledge.c,v 1.165 2016/04/28 14:25:08 beck Exp $ */ - -/* - * Copyright (c) 2015 Nicholas Marriott - * Copyright (c) 2015 Theo de Raadt - * - * 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 -#include -#include -#include -#include -#include -#include -#include - -#include "openbsd.h" -#include "pledge.h" - -#define SYS_MAXSYSCALL 1000 - -/* - * Ordered in blocks starting with least risky and most required. - */ -const uint64_t pledge_syscalls[SYS_MAXSYSCALL] = { - /* - * Minimum required - */ - [SYS_exit] = PLEDGE_ALWAYS, - // [SYS_kbind] = PLEDGE_ALWAYS, - // [SYS___get_tcb] = PLEDGE_ALWAYS, - // [SYS_pledge] = PLEDGE_ALWAYS, - // [SYS_sendsyslog] = PLEDGE_ALWAYS, /* stack protector reporting */ - // [SYS_osendsyslog] = PLEDGE_ALWAYS, /* obsolete sendsyslog */ - // [SYS_thrkill] = PLEDGE_ALWAYS, /* raise, abort, stack pro */ - // [SYS_utrace] = PLEDGE_ALWAYS, /* ltrace(1) from ld.so */ - - /* "getting" information about self is considered safe */ - [SYS_getuid] = PLEDGE_STDIO, - [SYS_geteuid] = PLEDGE_STDIO, - [SYS_getresuid] = PLEDGE_STDIO, - [SYS_getgid] = PLEDGE_STDIO, - [SYS_getegid] = PLEDGE_STDIO, - [SYS_getresgid] = PLEDGE_STDIO, - [SYS_getgroups] = PLEDGE_STDIO, - // [SYS_getlogin59] = PLEDGE_STDIO, - // [SYS_getlogin_r] = PLEDGE_STDIO, - [SYS_getpgrp] = PLEDGE_STDIO, - [SYS_getpgid] = PLEDGE_STDIO, - [SYS_getppid] = PLEDGE_STDIO, - [SYS_getsid] = PLEDGE_STDIO, - // [SYS_getthrid] = PLEDGE_STDIO, - [SYS_getrlimit] = PLEDGE_STDIO, - [SYS_gettimeofday] = PLEDGE_STDIO, - // [SYS_getdtablecount] = PLEDGE_STDIO, - [SYS_getrusage] = PLEDGE_STDIO, - // [SYS_issetugid] = PLEDGE_STDIO, - [SYS_clock_getres] = PLEDGE_STDIO, - [SYS_clock_gettime] = PLEDGE_STDIO, - [SYS_getpid] = PLEDGE_STDIO, - - /* - * Almost exclusively read-only, Very narrow subset. - * Use of "route", "inet", "dns", "ps", or "vminfo" - * expands access. - */ - // [SYS_sysctl] = PLEDGE_STDIO, - - /* Support for malloc(3) family of operations */ - // [SYS_getentropy] = PLEDGE_STDIO, - [SYS_madvise] = PLEDGE_STDIO, - // [SYS_minherit] = PLEDGE_STDIO, - [SYS_mmap] = PLEDGE_STDIO, - [SYS_mprotect] = PLEDGE_STDIO, - // [SYS_mquery] = PLEDGE_STDIO, - [SYS_munmap] = PLEDGE_STDIO, - [SYS_msync] = PLEDGE_STDIO, - // [SYS_break] = PLEDGE_STDIO, - - [SYS_umask] = PLEDGE_STDIO, - - /* read/write operations */ - [SYS_read] = PLEDGE_STDIO, - [SYS_readv] = PLEDGE_STDIO, - // [SYS_pread] = PLEDGE_STDIO, - [SYS_preadv] = PLEDGE_STDIO, - [SYS_write] = PLEDGE_STDIO, - [SYS_writev] = PLEDGE_STDIO, - // [SYS_pwrite] = PLEDGE_STDIO, - [SYS_pwritev] = PLEDGE_STDIO, - [SYS_recvmsg] = PLEDGE_STDIO, - [SYS_recvfrom] = PLEDGE_STDIO | PLEDGE_YPACTIVE, - [SYS_ftruncate] = PLEDGE_STDIO, - [SYS_lseek] = PLEDGE_STDIO, - // [SYS_fpathconf] = PLEDGE_STDIO, - - /* - * Address selection required a network pledge ("inet", - * "unix", "dns". - */ - [SYS_sendto] = PLEDGE_STDIO | PLEDGE_YPACTIVE, - - /* - * Address specification required a network pledge ("inet", - * "unix", "dns". SCM_RIGHTS requires "sendfd" or "recvfd". - */ - [SYS_sendmsg] = PLEDGE_STDIO, - - /* Common signal operations */ - [SYS_nanosleep] = PLEDGE_STDIO, - [SYS_sigaltstack] = PLEDGE_STDIO, - // [SYS_sigprocmask] = PLEDGE_STDIO, - // [SYS_sigsuspend] = PLEDGE_STDIO, - // [SYS_sigaction] = PLEDGE_STDIO, - // [SYS_sigreturn] = PLEDGE_STDIO, - // [SYS_sigpending] = PLEDGE_STDIO, - [SYS_getitimer] = PLEDGE_STDIO, - [SYS_setitimer] = PLEDGE_STDIO, - - /* - * To support event driven programming. - */ - [SYS_poll] = PLEDGE_STDIO, - [SYS_ppoll] = PLEDGE_STDIO, - // [SYS_kevent] = PLEDGE_STDIO, - // [SYS_kqueue] = PLEDGE_STDIO, - [SYS_select] = PLEDGE_STDIO, - // [SYS_pselect] = PLEDGE_STDIO, - [SYS_pselect6] = PLEDGE_STDIO, - [SYS_epoll_create] = PLEDGE_STDIO, - [SYS_epoll_create1] = PLEDGE_STDIO, - [SYS_epoll_ctl] = PLEDGE_STDIO, - [SYS_epoll_pwait] = PLEDGE_STDIO, - [SYS_epoll_wait] = PLEDGE_STDIO, - [SYS_eventfd] = PLEDGE_STDIO, - [SYS_eventfd2] = PLEDGE_STDIO, - - [SYS_fstat] = PLEDGE_STDIO, - [SYS_fsync] = PLEDGE_STDIO, - - [SYS_setsockopt] = PLEDGE_STDIO, /* narrow whitelist */ - [SYS_getsockopt] = PLEDGE_STDIO, /* narrow whitelist */ - - /* F_SETOWN requires PLEDGE_PROC */ - [SYS_fcntl] = PLEDGE_STDIO, - - [SYS_close] = PLEDGE_STDIO, - [SYS_dup] = PLEDGE_STDIO, - [SYS_dup2] = PLEDGE_STDIO, - [SYS_dup3] = PLEDGE_STDIO, - // [SYS_closefrom] = PLEDGE_STDIO, - [SYS_shutdown] = PLEDGE_STDIO, - [SYS_fchdir] = PLEDGE_STDIO, /* XXX consider tightening */ - - [SYS_pipe] = PLEDGE_STDIO, - [SYS_pipe2] = PLEDGE_STDIO, - [SYS_socketpair] = PLEDGE_STDIO, - - [SYS_wait4] = PLEDGE_STDIO, - - /* - * Can kill self with "stdio". Killing another pid - * requires "proc" - */ - // [SYS_o58_kill] = PLEDGE_STDIO, - [SYS_kill] = PLEDGE_STDIO, - - /* - * FIONREAD/FIONBIO for "stdio" - * A few non-tty ioctl available using "ioctl" - * tty-centric ioctl available using "tty" - */ - [SYS_ioctl] = PLEDGE_STDIO, - - /* - * Path access/creation calls encounter many extensive - * checks are done during namei() - */ - [SYS_open] = PLEDGE_STDIO, - [SYS_stat] = PLEDGE_STDIO, - [SYS_access] = PLEDGE_STDIO, - [SYS_readlink] = PLEDGE_STDIO, - - // [SYS_adjtime] = PLEDGE_STDIO, /* setting requires "settime" */ - // [SYS_adjfreq] = PLEDGE_SETTIME, - [SYS_settimeofday] = PLEDGE_SETTIME, - - /* - * Needed by threaded programs - * XXX should we have a new "threads"? - */ - // [SYS___tfork] = PLEDGE_STDIO, - [SYS_sched_yield] = PLEDGE_STDIO, - // [SYS___thrsleep] = PLEDGE_STDIO, - // [SYS___thrwakeup] = PLEDGE_STDIO, - // [SYS___threxit] = PLEDGE_STDIO, - // [SYS___thrsigdivert] = PLEDGE_STDIO, - - [SYS_fork] = PLEDGE_PROC, - [SYS_vfork] = PLEDGE_PROC, - [SYS_setpgid] = PLEDGE_PROC, - [SYS_setsid] = PLEDGE_PROC, - - [SYS_setrlimit] = PLEDGE_PROC | PLEDGE_ID, - [SYS_getpriority] = PLEDGE_PROC | PLEDGE_ID, - - [SYS_setpriority] = PLEDGE_PROC | PLEDGE_ID, - - [SYS_setuid] = PLEDGE_ID, - // [SYS_seteuid] = PLEDGE_ID, - [SYS_setreuid] = PLEDGE_ID, - [SYS_setresuid] = PLEDGE_ID, - [SYS_setgid] = PLEDGE_ID, - // [SYS_setegid] = PLEDGE_ID, - [SYS_setregid] = PLEDGE_ID, - [SYS_setresgid] = PLEDGE_ID, - [SYS_setgroups] = PLEDGE_ID, - // [SYS_setlogin] = PLEDGE_ID, - - [SYS_execve] = PLEDGE_EXEC, - - [SYS_chdir] = PLEDGE_RPATH, - [SYS_openat] = PLEDGE_RPATH | PLEDGE_WPATH, - // [SYS_fstatat] = PLEDGE_RPATH | PLEDGE_WPATH, - [SYS_newfstatat] = PLEDGE_RPATH | PLEDGE_WPATH, - [SYS_faccessat] = PLEDGE_RPATH | PLEDGE_WPATH, - [SYS_readlinkat] = PLEDGE_RPATH | PLEDGE_WPATH, - [SYS_lstat] = PLEDGE_RPATH | PLEDGE_WPATH | PLEDGE_TMPPATH, - [SYS_truncate] = PLEDGE_WPATH, - [SYS_rename] = PLEDGE_CPATH, - [SYS_rmdir] = PLEDGE_CPATH, - [SYS_renameat] = PLEDGE_CPATH, - [SYS_link] = PLEDGE_CPATH, - [SYS_linkat] = PLEDGE_CPATH, - [SYS_symlink] = PLEDGE_CPATH, - [SYS_unlink] = PLEDGE_CPATH | PLEDGE_TMPPATH, - [SYS_unlinkat] = PLEDGE_CPATH, - [SYS_mkdir] = PLEDGE_CPATH, - [SYS_mkdirat] = PLEDGE_CPATH, - - // [SYS_mkfifo] = PLEDGE_DPATH, - [SYS_mknod] = PLEDGE_DPATH, - - [SYS_chroot] = PLEDGE_ID, /* also requires PLEDGE_PROC */ - - // [SYS_revoke] = PLEDGE_TTY, /* also requires PLEDGE_RPATH */ - - /* - * Classify as RPATH|WPATH, because of path information leakage. - * WPATH due to unknown use of mk*temp(3) on non-/tmp paths.. - */ - // [SYS___getcwd] = PLEDGE_RPATH | PLEDGE_WPATH, - [SYS_getcwd] = PLEDGE_RPATH | PLEDGE_WPATH, - - /* Classify as RPATH, because these leak path information */ - [SYS_getdents] = PLEDGE_RPATH, - // [SYS_getfsstat] = PLEDGE_RPATH, - [SYS_statfs] = PLEDGE_RPATH, - [SYS_fstatfs] = PLEDGE_RPATH, - // [SYS_pathconf] = PLEDGE_RPATH, - - [SYS_utimes] = PLEDGE_FATTR, - // [SYS_futimes] = PLEDGE_FATTR, - [SYS_futimesat] = PLEDGE_FATTR, - [SYS_utimensat] = PLEDGE_FATTR, - // [SYS_futimens] = PLEDGE_FATTR, - [SYS_chmod] = PLEDGE_FATTR, - [SYS_fchmod] = PLEDGE_FATTR, - [SYS_fchmodat] = PLEDGE_FATTR, - // [SYS_chflags] = PLEDGE_FATTR, - //[SYS_chflagsat] = PLEDGE_FATTR, - // [SYS_fchflags] = PLEDGE_FATTR, - [SYS_chown] = PLEDGE_FATTR, - [SYS_fchownat] = PLEDGE_FATTR, - [SYS_lchown] = PLEDGE_FATTR, - [SYS_fchown] = PLEDGE_FATTR, - - [SYS_socket] = PLEDGE_INET | PLEDGE_UNIX | PLEDGE_DNS | PLEDGE_YPACTIVE, - [SYS_connect] = PLEDGE_INET | PLEDGE_UNIX | PLEDGE_DNS | PLEDGE_YPACTIVE, - [SYS_bind] = PLEDGE_INET | PLEDGE_UNIX | PLEDGE_DNS | PLEDGE_YPACTIVE, - [SYS_getsockname] = PLEDGE_INET | PLEDGE_UNIX | PLEDGE_DNS | PLEDGE_YPACTIVE, - - [SYS_listen] = PLEDGE_INET | PLEDGE_UNIX, - [SYS_accept4] = PLEDGE_INET | PLEDGE_UNIX, - [SYS_accept] = PLEDGE_INET | PLEDGE_UNIX, - [SYS_getpeername] = PLEDGE_INET | PLEDGE_UNIX, - - [SYS_flock] = PLEDGE_FLOCK | PLEDGE_YPACTIVE, - - // [SYS_swapctl] = PLEDGE_VMINFO, /* XXX should limit to "get" operations */ -}; - - -static const struct { - char *name; - int flags; -} pledgereq[] = { - { "audio", PLEDGE_AUDIO }, - { "cpath", PLEDGE_CPATH }, - { "disklabel", PLEDGE_DISKLABEL }, - { "dns", PLEDGE_DNS }, - { "dpath", PLEDGE_DPATH }, - { "drm", PLEDGE_DRM }, - { "exec", PLEDGE_EXEC }, - { "fattr", PLEDGE_FATTR }, - { "flock", PLEDGE_FLOCK }, - { "getpw", PLEDGE_GETPW }, - { "id", PLEDGE_ID }, - { "inet", PLEDGE_INET }, - { "ioctl", PLEDGE_IOCTL }, - { "mcast", PLEDGE_MCAST }, - { "pf", PLEDGE_PF }, - { "proc", PLEDGE_PROC }, - { "prot_exec", PLEDGE_PROTEXEC }, - { "ps", PLEDGE_PS }, - { "recvfd", PLEDGE_RECVFD }, - { "route", PLEDGE_ROUTE }, - { "rpath", PLEDGE_RPATH }, - { "sendfd", PLEDGE_SENDFD }, - { "settime", PLEDGE_SETTIME }, - { "stdio", PLEDGE_STDIO }, - { "tmppath", PLEDGE_TMPPATH }, - { "tty", PLEDGE_TTY }, - { "unix", PLEDGE_UNIX }, - { "vminfo", PLEDGE_VMINFO }, - { "vmm", PLEDGE_VMM }, - { "wpath", PLEDGE_WPATH }, -}; - -scmp_filter_ctx scmp_ctx = NULL; - -/* bsearch over pledgereq. return flags value if found, 0 else */ -static int -pledgereq_flags(const char *req_name) -{ - int base = 0, cmp, i, lim; - - for (lim = nitems(pledgereq); lim != 0; lim >>= 1) { - i = base + (lim >> 1); - cmp = strcmp(req_name, pledgereq[i].name); - if (cmp == 0) - return (pledgereq[i].flags); - if (cmp > 0) { /* not found before, move right */ - base = i + 1; - lim--; - } /* else move left */ - } - return (0); -} - -/* whitelists syscalls returns -1 on error */ -int -pledge(const char *promises, __UNUSED const char *paths[]) -{ - char *buf, *p; - int rv = 0; - uint64_t flags = 0; - - if (scmp_ctx == NULL) { - /* inintialize new seccomp whitelist */ - if ((scmp_ctx = seccomp_init(SCMP_ACT_KILL)) == NULL) - err(1, "seccomp_init"); - } else { - /* reset previous rules */ - if (seccomp_reset(scmp_ctx, SCMP_ACT_KILL) < 0) - err(1, "seccomp_reset"); - } - - /* make flags from prmises string */ - buf = strdup(promises); - for (p = strtok(buf, " "); p; - p = strtok(NULL, " ")) { - flags |= pledgereq_flags(p); - } - - for (int i = 0; i < SYS_MAXSYSCALL; i++) { - /* skip not defined syscalls */ - if (pledge_syscalls[i] == 0) - continue; - - /* skip not matching syscalls */ - if (!(pledge_syscalls[i] & flags)) - continue; - - /* seccomp whitelist syscall */ - if((rv = seccomp_rule_add_exact(scmp_ctx, SCMP_ACT_ALLOW, i, 0)) != 0) - goto out; - } - - /* seccomp_export_pfc(scmp_ctx, STDERR_FILENO); */ - -out: - free(buf); - free(p); - - /* seccomp_release(scmp_ctx); */ - - return (rv == 0 ? 0 : -1); -} diff --git a/libopenbsd/pledge-seccomp.h b/libopenbsd/pledge-seccomp.h deleted file mode 100644 index 28086c4..0000000 --- a/libopenbsd/pledge-seccomp.h +++ /dev/null @@ -1,49 +0,0 @@ - -#ifndef nitems -#define nitems(_a) (sizeof((_a)) / sizeof((_a)[0])) -#endif - -/* - * pledge(2) requests - */ -#define PLEDGE_ALWAYS 0xffffffffffffffffULL -#define PLEDGE_RPATH 0x0000000000000001ULL /* allow open for read */ -#define PLEDGE_WPATH 0x0000000000000002ULL /* allow open for write */ -#define PLEDGE_CPATH 0x0000000000000004ULL /* allow creat, mkdir, unlink etc */ -#define PLEDGE_STDIO 0x0000000000000008ULL /* operate on own pid */ -#define PLEDGE_TMPPATH 0x0000000000000010ULL /* for mk*temp() */ -#define PLEDGE_DNS 0x0000000000000020ULL /* DNS services */ -#define PLEDGE_INET 0x0000000000000040ULL /* AF_INET/AF_INET6 sockets */ -#define PLEDGE_FLOCK 0x0000000000000080ULL /* file locking */ -#define PLEDGE_UNIX 0x0000000000000100ULL /* AF_UNIX sockets */ -#define PLEDGE_ID 0x0000000000000200ULL /* allow setuid, setgid, etc */ -#define PLEDGE_IOCTL 0x0000000000000400ULL /* Select ioctl */ -#define PLEDGE_GETPW 0x0000000000000800ULL /* YP enables if ypbind.lock */ -#define PLEDGE_PROC 0x0000000000001000ULL /* fork, waitpid, etc */ -#define PLEDGE_SETTIME 0x0000000000002000ULL /* able to set/adj time/freq */ -#define PLEDGE_FATTR 0x0000000000004000ULL /* allow explicit file st_* mods */ -#define PLEDGE_PROTEXEC 0x0000000000008000ULL /* allow use of PROT_EXEC */ -#define PLEDGE_TTY 0x0000000000010000ULL /* tty setting */ -#define PLEDGE_SENDFD 0x0000000000020000ULL /* AF_UNIX CMSG fd sending */ -#define PLEDGE_RECVFD 0x0000000000040000ULL /* AF_UNIX CMSG fd receiving */ -#define PLEDGE_EXEC 0x0000000000080000ULL /* execve, child is free of pledge */ -#define PLEDGE_ROUTE 0x0000000000100000ULL /* routing lookups */ -#define PLEDGE_MCAST 0x0000000000200000ULL /* multicast joins */ -#define PLEDGE_VMINFO 0x0000000000400000ULL /* vminfo listings */ -#define PLEDGE_PS 0x0000000000800000ULL /* ps listings */ -#define PLEDGE_DISKLABEL 0x0000000002000000ULL /* disklabels */ -#define PLEDGE_PF 0x0000000004000000ULL /* pf ioctls */ -#define PLEDGE_AUDIO 0x0000000008000000ULL /* audio ioctls */ -#define PLEDGE_DPATH 0x0000000010000000ULL /* mknod & mkfifo */ -#define PLEDGE_DRM 0x0000000020000000ULL /* drm ioctls */ -#define PLEDGE_VMM 0x0000000040000000ULL /* vmm ioctls */ - - -/* - * Bits outside PLEDGE_USERSET are used by the kernel itself - * to track program behaviours which have been observed. - */ -#define PLEDGE_USERSET 0x0fffffffffffffffULL -#define PLEDGE_STATLIE 0x4000000000000000ULL -#define PLEDGE_YPACTIVE 0x8000000000000000ULL /* YP use detected and allowed */ - From 2a5702a91039473d001e4ac8b9f624d6b1f33fa5 Mon Sep 17 00:00:00 2001 From: tedu Date: Sat, 11 Jun 2016 04:56:16 +0000 Subject: [PATCH 029/198] specify that default is deny if no rule matches --- doas.conf.5 | 1 + 1 file changed, 1 insertion(+) diff --git a/doas.conf.5 b/doas.conf.5 index 65d5bef..212e0bb 100644 --- a/doas.conf.5 +++ b/doas.conf.5 @@ -83,6 +83,7 @@ alone means that command should be run without any arguments. .El .Pp The last matching rule determines the action taken. +If no rule matches, the action is denied. .Pp Comments can be put anywhere in the file using a hash mark .Pq Sq # , From 268a2845e4009af47dbd2bb272853028bee92fc7 Mon Sep 17 00:00:00 2001 From: tedu Date: Sat, 11 Jun 2016 05:04:03 +0000 Subject: [PATCH 030/198] clarify some wording --- doas.conf.5 | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/doas.conf.5 b/doas.conf.5 index 212e0bb..8110628 100644 --- a/doas.conf.5 +++ b/doas.conf.5 @@ -72,14 +72,17 @@ The default is all users. .It Ic cmd Ar command The command the user is allowed or denied to run. The default is all commands. -Be advised that it's best to specify absolute paths. +Be advised that it is best to specify absolute paths. +If a relative path is specified, only a restricted +.Ev PATH +will be searched. .It Ic args ... Arguments to command. If specified, the command arguments provided by the user -need to match for the command to be successful. -Specifying +need to match those specified. +The keyword .Ic args -alone means that command should be run without any arguments. +alone means that command must be run without any arguments. .El .Pp The last matching rule determines the action taken. From 9278ac53a4c5cce85b1721c170bbb4ce960e7ff6 Mon Sep 17 00:00:00 2001 From: tedu Date: Sat, 11 Jun 2016 17:17:10 +0000 Subject: [PATCH 031/198] don't use specified twice in a sentence, noticed by jmc --- doas.conf.5 | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/doas.conf.5 b/doas.conf.5 index 8110628..1dfeaee 100644 --- a/doas.conf.5 +++ b/doas.conf.5 @@ -78,8 +78,7 @@ If a relative path is specified, only a restricted will be searched. .It Ic args ... Arguments to command. -If specified, the command arguments provided by the user -need to match those specified. +The command arguments provided by the user need to match those specified. The keyword .Ic args alone means that command must be run without any arguments. From 1a0ed98a5cb619824028193ecff946f209da81fb Mon Sep 17 00:00:00 2001 From: tedu Date: Thu, 16 Jun 2016 17:40:30 +0000 Subject: [PATCH 032/198] the environment handling code was showing its age. just because environ is a char** array doesn't mean we must exclusively operate on such. convert to a red-black tree, manipulate as desired, then flatten to array. potentially overkill for the current operations, but reading the tea leaves i see that more manipulations are desired. ok tb (and some thought provoking disagreement from martijn) --- Makefile | 2 +- doas.c | 104 ++----------------------------------- doas.h | 19 +++++++ env.c | 153 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 177 insertions(+), 101 deletions(-) create mode 100644 env.c diff --git a/Makefile b/Makefile index 66792eb..fa69b8c 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ # $OpenBSD: Makefile,v 1.9 2014/01/13 01:41:00 tedu Exp $ -SRCS= parse.y doas.c +SRCS= parse.y doas.c env.c PROG= doas MAN= doas.1 doas.conf.5 diff --git a/doas.c b/doas.c index f1b2ec0..eef8955 100644 --- a/doas.c +++ b/doas.c @@ -190,105 +190,6 @@ parseconfig(const char *filename, int checkperms) exit(1); } -/* - * Copy the environment variables in safeset from oldenvp to envp. - */ -static int -copyenvhelper(const char **oldenvp, const char **safeset, size_t nsafe, - char **envp, int ei) -{ - size_t i; - - for (i = 0; i < nsafe; i++) { - const char **oe = oldenvp; - while (*oe) { - size_t len = strlen(safeset[i]); - if (strncmp(*oe, safeset[i], len) == 0 && - (*oe)[len] == '=') { - if (!(envp[ei++] = strdup(*oe))) - err(1, "strdup"); - break; - } - oe++; - } - } - return ei; -} - -static char ** -copyenv(const char **oldenvp, struct rule *rule) -{ - const char *safeset[] = { - "DISPLAY", "HOME", "LOGNAME", "MAIL", - "PATH", "TERM", "USER", "USERNAME", - NULL - }; - const char *badset[] = { - "ENV", - NULL - }; - char **envp; - const char **extra; - int ei; - size_t nsafe, nbad; - size_t nextras = 0; - - /* if there was no envvar whitelist, pass all except badset ones */ - nbad = arraylen(badset); - if ((rule->options & KEEPENV) && !rule->envlist) { - size_t iold, inew; - size_t oldlen = arraylen(oldenvp); - envp = reallocarray(NULL, oldlen + 1, sizeof(char *)); - if (!envp) - err(1, "reallocarray"); - for (inew = iold = 0; iold < oldlen; iold++) { - size_t ibad; - for (ibad = 0; ibad < nbad; ibad++) { - size_t len = strlen(badset[ibad]); - if (strncmp(oldenvp[iold], badset[ibad], len) == 0 && - oldenvp[iold][len] == '=') { - break; - } - } - if (ibad == nbad) { - if (!(envp[inew] = strdup(oldenvp[iold]))) - err(1, "strdup"); - inew++; - } - } - envp[inew] = NULL; - return envp; - } - - nsafe = arraylen(safeset); - if ((extra = rule->envlist)) { - size_t isafe; - nextras = arraylen(extra); - for (isafe = 0; isafe < nsafe; isafe++) { - size_t iextras; - for (iextras = 0; iextras < nextras; iextras++) { - if (strcmp(extra[iextras], safeset[isafe]) == 0) { - nextras--; - extra[iextras] = extra[nextras]; - extra[nextras] = NULL; - iextras--; - } - } - } - } - - envp = reallocarray(NULL, nsafe + nextras + 1, sizeof(char *)); - if (!envp) - err(1, "can't allocate new environment"); - - ei = 0; - ei = copyenvhelper(oldenvp, safeset, nsafe, envp, ei); - ei = copyenvhelper(oldenvp, rule->envlist, nextras, envp, ei); - envp[ei] = NULL; - - return envp; -} - static void __dead checkconfig(const char *confpath, int argc, char **argv, uid_t uid, gid_t *groups, int ngroups, uid_t target) @@ -321,6 +222,7 @@ main(int argc, char **argv, char **envp) char *shargv[] = { NULL, NULL }; char *sh; const char *cmd; + struct env *env; char cmdline[LINE_MAX]; char myname[_PW_NAME_LEN + 1]; struct passwd *pw; @@ -517,7 +419,9 @@ main(int argc, char **argv, char **envp) syslog(LOG_AUTHPRIV | LOG_INFO, "%s ran command %s as %s from %s", myname, cmdline, pw->pw_name, cwd); - envp = copyenv((const char **)envp, rule); + env = createenv(envp); + env = filterenv(env, rule); + envp = flattenenv(env); if (rule->cmd) { if (setenv("PATH", safepath, 1) == -1) diff --git a/doas.h b/doas.h index 235df9f..88b2223 100644 --- a/doas.h +++ b/doas.h @@ -1,5 +1,20 @@ /* $OpenBSD: doas.h,v 1.3 2015/07/21 11:04:06 zhuk Exp $ */ +#include + +struct envnode { + RB_ENTRY(envnode) node; + const char *key; + const char *value; +}; + +struct env { + RB_HEAD(envtree, envnode) root; + u_int count; +}; + +RB_PROTOTYPE(envtree, envnode, node, envcmp) + struct rule { int action; int options; @@ -16,6 +31,10 @@ extern int parse_errors; size_t arraylen(const char **); +struct env *createenv(char **); +struct env *filterenv(struct env *, struct rule *); +char **flattenenv(struct env *); + #define PERMIT 1 #define DENY 2 diff --git a/env.c b/env.c new file mode 100644 index 0000000..cf51e67 --- /dev/null +++ b/env.c @@ -0,0 +1,153 @@ +/* $OpenBSD$ */ +/* + * Copyright (c) 2016 Ted Unangst + * + * 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 + +#include +#include +#include +#include +#include +#include + +#include "doas.h" + +int +envcmp(struct envnode *a, struct envnode *b) +{ + return strcmp(a->key, b->key); +} +RB_GENERATE(envtree, envnode, node, envcmp) + +struct env * +createenv(char **envp) +{ + struct env *env; + u_int i; + + env = malloc(sizeof(*env)); + if (!env) + err(1, NULL); + RB_INIT(&env->root); + env->count = 0; + + for (i = 0; envp[i] != NULL; i++) { + struct envnode *node; + const char *e, *eq; + + e = envp[i]; + + if ((eq = strchr(e, '=')) == NULL || eq == e) + continue; + node = malloc(sizeof(*node)); + if (!node) + err(1, NULL); + node->key = strndup(envp[i], eq - e); + node->value = strdup(eq + 1); + if (!node->key || !node->value) + err(1, NULL); + if (RB_FIND(envtree, &env->root, node)) { + free((char *)node->key); + free((char *)node->value); + free(node); + } else { + RB_INSERT(envtree, &env->root, node); + env->count++; + } + } + return env; +} + +char ** +flattenenv(struct env *env) +{ + char **envp; + struct envnode *node; + u_int i; + + envp = reallocarray(NULL, env->count + 1, sizeof(char *)); + if (!envp) + err(1, NULL); + i = 0; + RB_FOREACH(node, envtree, &env->root) { + if (asprintf(&envp[i], "%s=%s", node->key, node->value) == -1) + err(1, NULL); + i++; + } + envp[i] = NULL; + return envp; +} + +static void +copyenv(struct env *orig, struct env *copy, const char **envlist) +{ + struct envnode *node, key; + u_int i; + + for (i = 0; envlist[i]; i++) { + key.key = envlist[i]; + if ((node = RB_FIND(envtree, &orig->root, &key))) { + RB_REMOVE(envtree, &orig->root, node); + orig->count--; + RB_INSERT(envtree, ©->root, node); + copy->count++; + } + } +} + +struct env * +filterenv(struct env *orig, struct rule *rule) +{ + const char *safeset[] = { + "DISPLAY", "HOME", "LOGNAME", "MAIL", + "PATH", "TERM", "USER", "USERNAME", + NULL + }; + const char *badset[] = { + "ENV", + NULL + }; + struct env *copy; + struct envnode *node, key; + u_int i; + + if ((rule->options & KEEPENV) && !rule->envlist) { + for (i = 0; badset[i]; i++) { + key.key = badset[i]; + if ((node = RB_FIND(envtree, &orig->root, &key))) { + RB_REMOVE(envtree, &orig->root, node); + free((char *)node->key); + free((char *)node->value); + free(node); + orig->count--; + } + } + return orig; + } + + copy = malloc(sizeof(*copy)); + if (!copy) + err(1, NULL); + RB_INIT(©->root); + copy->count = 0; + + if (rule->envlist) + copyenv(orig, copy, rule->envlist); + copyenv(orig, copy, safeset); + + return copy; +} From a3ceebbcdde17d0fbfb0a334ad88cc4b4f73f533 Mon Sep 17 00:00:00 2001 From: martijn Date: Sun, 19 Jun 2016 19:29:43 +0000 Subject: [PATCH 033/198] Move the RB_ code from doas.h to env.c, and limit the environment interface to a simple prepenv function. OK tedu@ --- doas.c | 8 +++----- doas.h | 22 ++-------------------- env.c | 29 ++++++++++++++++++++++++++++- 3 files changed, 33 insertions(+), 26 deletions(-) diff --git a/doas.c b/doas.c index eef8955..ee1b341 100644 --- a/doas.c +++ b/doas.c @@ -214,7 +214,7 @@ checkconfig(const char *confpath, int argc, char **argv, } int -main(int argc, char **argv, char **envp) +main(int argc, char **argv) { const char *safepath = "/bin:/sbin:/usr/bin:/usr/sbin:" "/usr/local/bin:/usr/local/sbin"; @@ -222,7 +222,6 @@ main(int argc, char **argv, char **envp) char *shargv[] = { NULL, NULL }; char *sh; const char *cmd; - struct env *env; char cmdline[LINE_MAX]; char myname[_PW_NAME_LEN + 1]; struct passwd *pw; @@ -237,6 +236,7 @@ main(int argc, char **argv, char **envp) int vflag = 0; char cwdpath[PATH_MAX]; const char *cwd; + char **envp; #ifdef HAVE_BSD_AUTH_H char *login_style = NULL; #endif @@ -419,9 +419,7 @@ main(int argc, char **argv, char **envp) syslog(LOG_AUTHPRIV | LOG_INFO, "%s ran command %s as %s from %s", myname, cmdline, pw->pw_name, cwd); - env = createenv(envp); - env = filterenv(env, rule); - envp = flattenenv(env); + envp = prepenv(rule); if (rule->cmd) { if (setenv("PATH", safepath, 1) == -1) diff --git a/doas.h b/doas.h index 88b2223..2f95310 100644 --- a/doas.h +++ b/doas.h @@ -1,20 +1,4 @@ -/* $OpenBSD: doas.h,v 1.3 2015/07/21 11:04:06 zhuk Exp $ */ - -#include - -struct envnode { - RB_ENTRY(envnode) node; - const char *key; - const char *value; -}; - -struct env { - RB_HEAD(envtree, envnode) root; - u_int count; -}; - -RB_PROTOTYPE(envtree, envnode, node, envcmp) - +/* $OpenBSD$ */ struct rule { int action; int options; @@ -31,9 +15,7 @@ extern int parse_errors; size_t arraylen(const char **); -struct env *createenv(char **); -struct env *filterenv(struct env *, struct rule *); -char **flattenenv(struct env *); +char **prepenv(struct rule *); #define PERMIT 1 #define DENY 2 diff --git a/env.c b/env.c index cf51e67..77b2434 100644 --- a/env.c +++ b/env.c @@ -16,6 +16,7 @@ */ #include +#include #include #include @@ -26,12 +27,27 @@ #include "doas.h" +struct envnode { + RB_ENTRY(envnode) node; + const char *key; + const char *value; +}; + +struct env { + RB_HEAD(envtree, envnode) root; + u_int count; +}; + int envcmp(struct envnode *a, struct envnode *b) { return strcmp(a->key, b->key); } -RB_GENERATE(envtree, envnode, node, envcmp) +RB_GENERATE_STATIC(envtree, envnode, node, envcmp) + +struct env *createenv(char **); +struct env *filterenv(struct env *, struct rule *); +char **flattenenv(struct env *); struct env * createenv(char **envp) @@ -151,3 +167,14 @@ filterenv(struct env *orig, struct rule *rule) return copy; } + +char ** +prepenv(struct rule *rule) +{ + extern char **environ; + struct env *env; + + env = createenv(environ); + env = filterenv(env, rule); + return flattenenv(env); +} From 03b3cb7f6291300fd10a5af519c7fece104f1a59 Mon Sep 17 00:00:00 2001 From: Duncaen Date: Fri, 24 Jun 2016 16:33:42 +0200 Subject: [PATCH 034/198] import sys-tree.h from openssh-portable --- configure | 10 + env.c | 5 + libopenbsd/sys-tree.h | 754 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 769 insertions(+) create mode 100644 libopenbsd/sys-tree.h diff --git a/configure b/configure index d854321..c5981a5 100755 --- a/configure +++ b/configure @@ -264,6 +264,16 @@ int main(void) { check_func "bsd_auth_h" "$src" && \ have_bsd_auth_h=1 +# +# Check for sys/tree.h. +# +src=' +#include +int main(void) { + return 0; +}' +check_func "sys_tree_h" "$src" + # # Check for pam_appl.h. # diff --git a/env.c b/env.c index 77b2434..9baf4a5 100644 --- a/env.c +++ b/env.c @@ -16,7 +16,11 @@ */ #include +#ifdef HAVE_SYS_TREE_H #include +#else +#include "sys-tree.h" +#endif #include #include @@ -26,6 +30,7 @@ #include #include "doas.h" +#include "includes.h" struct envnode { RB_ENTRY(envnode) node; diff --git a/libopenbsd/sys-tree.h b/libopenbsd/sys-tree.h new file mode 100644 index 0000000..6700e14 --- /dev/null +++ b/libopenbsd/sys-tree.h @@ -0,0 +1,754 @@ +/* $OpenBSD: tree.h,v 1.13 2011/07/09 00:19:45 pirofti Exp $ */ +/* + * Copyright 2002 Niels Provos + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* OPENBSD ORIGINAL: sys/sys/tree.h */ + +#ifdef NO_ATTRIBUTE_ON_RETURN_TYPE +# define __attribute__(x) +#endif + +#ifndef _SYS_TREE_H_ +#define _SYS_TREE_H_ + +/* + * This file defines data structures for different types of trees: + * splay trees and red-black trees. + * + * A splay tree is a self-organizing data structure. Every operation + * on the tree causes a splay to happen. The splay moves the requested + * node to the root of the tree and partly rebalances it. + * + * This has the benefit that request locality causes faster lookups as + * the requested nodes move to the top of the tree. On the other hand, + * every lookup causes memory writes. + * + * The Balance Theorem bounds the total access time for m operations + * and n inserts on an initially empty tree as O((m + n)lg n). The + * amortized cost for a sequence of m accesses to a splay tree is O(lg n); + * + * A red-black tree is a binary search tree with the node color as an + * extra attribute. It fulfills a set of conditions: + * - every search path from the root to a leaf consists of the + * same number of black nodes, + * - each red node (except for the root) has a black parent, + * - each leaf node is black. + * + * Every operation on a red-black tree is bounded as O(lg n). + * The maximum height of a red-black tree is 2lg (n+1). + */ + +#define SPLAY_HEAD(name, type) \ +struct name { \ + struct type *sph_root; /* root of the tree */ \ +} + +#define SPLAY_INITIALIZER(root) \ + { NULL } + +#define SPLAY_INIT(root) do { \ + (root)->sph_root = NULL; \ +} while (0) + +#define SPLAY_ENTRY(type) \ +struct { \ + struct type *spe_left; /* left element */ \ + struct type *spe_right; /* right element */ \ +} + +#define SPLAY_LEFT(elm, field) (elm)->field.spe_left +#define SPLAY_RIGHT(elm, field) (elm)->field.spe_right +#define SPLAY_ROOT(head) (head)->sph_root +#define SPLAY_EMPTY(head) (SPLAY_ROOT(head) == NULL) + +/* SPLAY_ROTATE_{LEFT,RIGHT} expect that tmp hold SPLAY_{RIGHT,LEFT} */ +#define SPLAY_ROTATE_RIGHT(head, tmp, field) do { \ + SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(tmp, field); \ + SPLAY_RIGHT(tmp, field) = (head)->sph_root; \ + (head)->sph_root = tmp; \ +} while (0) + +#define SPLAY_ROTATE_LEFT(head, tmp, field) do { \ + SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(tmp, field); \ + SPLAY_LEFT(tmp, field) = (head)->sph_root; \ + (head)->sph_root = tmp; \ +} while (0) + +#define SPLAY_LINKLEFT(head, tmp, field) do { \ + SPLAY_LEFT(tmp, field) = (head)->sph_root; \ + tmp = (head)->sph_root; \ + (head)->sph_root = SPLAY_LEFT((head)->sph_root, field); \ +} while (0) + +#define SPLAY_LINKRIGHT(head, tmp, field) do { \ + SPLAY_RIGHT(tmp, field) = (head)->sph_root; \ + tmp = (head)->sph_root; \ + (head)->sph_root = SPLAY_RIGHT((head)->sph_root, field); \ +} while (0) + +#define SPLAY_ASSEMBLE(head, node, left, right, field) do { \ + SPLAY_RIGHT(left, field) = SPLAY_LEFT((head)->sph_root, field); \ + SPLAY_LEFT(right, field) = SPLAY_RIGHT((head)->sph_root, field);\ + SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(node, field); \ + SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(node, field); \ +} while (0) + +/* Generates prototypes and inline functions */ + +#define SPLAY_PROTOTYPE(name, type, field, cmp) \ +void name##_SPLAY(struct name *, struct type *); \ +void name##_SPLAY_MINMAX(struct name *, int); \ +struct type *name##_SPLAY_INSERT(struct name *, struct type *); \ +struct type *name##_SPLAY_REMOVE(struct name *, struct type *); \ + \ +/* Finds the node with the same key as elm */ \ +static __inline struct type * \ +name##_SPLAY_FIND(struct name *head, struct type *elm) \ +{ \ + if (SPLAY_EMPTY(head)) \ + return(NULL); \ + name##_SPLAY(head, elm); \ + if ((cmp)(elm, (head)->sph_root) == 0) \ + return (head->sph_root); \ + return (NULL); \ +} \ + \ +static __inline struct type * \ +name##_SPLAY_NEXT(struct name *head, struct type *elm) \ +{ \ + name##_SPLAY(head, elm); \ + if (SPLAY_RIGHT(elm, field) != NULL) { \ + elm = SPLAY_RIGHT(elm, field); \ + while (SPLAY_LEFT(elm, field) != NULL) { \ + elm = SPLAY_LEFT(elm, field); \ + } \ + } else \ + elm = NULL; \ + return (elm); \ +} \ + \ +static __inline struct type * \ +name##_SPLAY_MIN_MAX(struct name *head, int val) \ +{ \ + name##_SPLAY_MINMAX(head, val); \ + return (SPLAY_ROOT(head)); \ +} + +/* Main splay operation. + * Moves node close to the key of elm to top + */ +#define SPLAY_GENERATE(name, type, field, cmp) \ +struct type * \ +name##_SPLAY_INSERT(struct name *head, struct type *elm) \ +{ \ + if (SPLAY_EMPTY(head)) { \ + SPLAY_LEFT(elm, field) = SPLAY_RIGHT(elm, field) = NULL; \ + } else { \ + int __comp; \ + name##_SPLAY(head, elm); \ + __comp = (cmp)(elm, (head)->sph_root); \ + if(__comp < 0) { \ + SPLAY_LEFT(elm, field) = SPLAY_LEFT((head)->sph_root, field);\ + SPLAY_RIGHT(elm, field) = (head)->sph_root; \ + SPLAY_LEFT((head)->sph_root, field) = NULL; \ + } else if (__comp > 0) { \ + SPLAY_RIGHT(elm, field) = SPLAY_RIGHT((head)->sph_root, field);\ + SPLAY_LEFT(elm, field) = (head)->sph_root; \ + SPLAY_RIGHT((head)->sph_root, field) = NULL; \ + } else \ + return ((head)->sph_root); \ + } \ + (head)->sph_root = (elm); \ + return (NULL); \ +} \ + \ +struct type * \ +name##_SPLAY_REMOVE(struct name *head, struct type *elm) \ +{ \ + struct type *__tmp; \ + if (SPLAY_EMPTY(head)) \ + return (NULL); \ + name##_SPLAY(head, elm); \ + if ((cmp)(elm, (head)->sph_root) == 0) { \ + if (SPLAY_LEFT((head)->sph_root, field) == NULL) { \ + (head)->sph_root = SPLAY_RIGHT((head)->sph_root, field);\ + } else { \ + __tmp = SPLAY_RIGHT((head)->sph_root, field); \ + (head)->sph_root = SPLAY_LEFT((head)->sph_root, field);\ + name##_SPLAY(head, elm); \ + SPLAY_RIGHT((head)->sph_root, field) = __tmp; \ + } \ + return (elm); \ + } \ + return (NULL); \ +} \ + \ +void \ +name##_SPLAY(struct name *head, struct type *elm) \ +{ \ + struct type __node, *__left, *__right, *__tmp; \ + int __comp; \ +\ + SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL;\ + __left = __right = &__node; \ +\ + while ((__comp = (cmp)(elm, (head)->sph_root))) { \ + if (__comp < 0) { \ + __tmp = SPLAY_LEFT((head)->sph_root, field); \ + if (__tmp == NULL) \ + break; \ + if ((cmp)(elm, __tmp) < 0){ \ + SPLAY_ROTATE_RIGHT(head, __tmp, field); \ + if (SPLAY_LEFT((head)->sph_root, field) == NULL)\ + break; \ + } \ + SPLAY_LINKLEFT(head, __right, field); \ + } else if (__comp > 0) { \ + __tmp = SPLAY_RIGHT((head)->sph_root, field); \ + if (__tmp == NULL) \ + break; \ + if ((cmp)(elm, __tmp) > 0){ \ + SPLAY_ROTATE_LEFT(head, __tmp, field); \ + if (SPLAY_RIGHT((head)->sph_root, field) == NULL)\ + break; \ + } \ + SPLAY_LINKRIGHT(head, __left, field); \ + } \ + } \ + SPLAY_ASSEMBLE(head, &__node, __left, __right, field); \ +} \ + \ +/* Splay with either the minimum or the maximum element \ + * Used to find minimum or maximum element in tree. \ + */ \ +void name##_SPLAY_MINMAX(struct name *head, int __comp) \ +{ \ + struct type __node, *__left, *__right, *__tmp; \ +\ + SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL;\ + __left = __right = &__node; \ +\ + while (1) { \ + if (__comp < 0) { \ + __tmp = SPLAY_LEFT((head)->sph_root, field); \ + if (__tmp == NULL) \ + break; \ + if (__comp < 0){ \ + SPLAY_ROTATE_RIGHT(head, __tmp, field); \ + if (SPLAY_LEFT((head)->sph_root, field) == NULL)\ + break; \ + } \ + SPLAY_LINKLEFT(head, __right, field); \ + } else if (__comp > 0) { \ + __tmp = SPLAY_RIGHT((head)->sph_root, field); \ + if (__tmp == NULL) \ + break; \ + if (__comp > 0) { \ + SPLAY_ROTATE_LEFT(head, __tmp, field); \ + if (SPLAY_RIGHT((head)->sph_root, field) == NULL)\ + break; \ + } \ + SPLAY_LINKRIGHT(head, __left, field); \ + } \ + } \ + SPLAY_ASSEMBLE(head, &__node, __left, __right, field); \ +} + +#define SPLAY_NEGINF -1 +#define SPLAY_INF 1 + +#define SPLAY_INSERT(name, x, y) name##_SPLAY_INSERT(x, y) +#define SPLAY_REMOVE(name, x, y) name##_SPLAY_REMOVE(x, y) +#define SPLAY_FIND(name, x, y) name##_SPLAY_FIND(x, y) +#define SPLAY_NEXT(name, x, y) name##_SPLAY_NEXT(x, y) +#define SPLAY_MIN(name, x) (SPLAY_EMPTY(x) ? NULL \ + : name##_SPLAY_MIN_MAX(x, SPLAY_NEGINF)) +#define SPLAY_MAX(name, x) (SPLAY_EMPTY(x) ? NULL \ + : name##_SPLAY_MIN_MAX(x, SPLAY_INF)) + +#define SPLAY_FOREACH(x, name, head) \ + for ((x) = SPLAY_MIN(name, head); \ + (x) != NULL; \ + (x) = SPLAY_NEXT(name, head, x)) + +/* Macros that define a red-black tree */ +#define RB_HEAD(name, type) \ +struct name { \ + struct type *rbh_root; /* root of the tree */ \ +} + +#define RB_INITIALIZER(root) \ + { NULL } + +#define RB_INIT(root) do { \ + (root)->rbh_root = NULL; \ +} while (0) + +#define RB_BLACK 0 +#define RB_RED 1 +#define RB_ENTRY(type) \ +struct { \ + struct type *rbe_left; /* left element */ \ + struct type *rbe_right; /* right element */ \ + struct type *rbe_parent; /* parent element */ \ + int rbe_color; /* node color */ \ +} + +#define RB_LEFT(elm, field) (elm)->field.rbe_left +#define RB_RIGHT(elm, field) (elm)->field.rbe_right +#define RB_PARENT(elm, field) (elm)->field.rbe_parent +#define RB_COLOR(elm, field) (elm)->field.rbe_color +#define RB_ROOT(head) (head)->rbh_root +#define RB_EMPTY(head) (RB_ROOT(head) == NULL) + +#define RB_SET(elm, parent, field) do { \ + RB_PARENT(elm, field) = parent; \ + RB_LEFT(elm, field) = RB_RIGHT(elm, field) = NULL; \ + RB_COLOR(elm, field) = RB_RED; \ +} while (0) + +#define RB_SET_BLACKRED(black, red, field) do { \ + RB_COLOR(black, field) = RB_BLACK; \ + RB_COLOR(red, field) = RB_RED; \ +} while (0) + +#ifndef RB_AUGMENT +#define RB_AUGMENT(x) do {} while (0) +#endif + +#define RB_ROTATE_LEFT(head, elm, tmp, field) do { \ + (tmp) = RB_RIGHT(elm, field); \ + if ((RB_RIGHT(elm, field) = RB_LEFT(tmp, field))) { \ + RB_PARENT(RB_LEFT(tmp, field), field) = (elm); \ + } \ + RB_AUGMENT(elm); \ + if ((RB_PARENT(tmp, field) = RB_PARENT(elm, field))) { \ + if ((elm) == RB_LEFT(RB_PARENT(elm, field), field)) \ + RB_LEFT(RB_PARENT(elm, field), field) = (tmp); \ + else \ + RB_RIGHT(RB_PARENT(elm, field), field) = (tmp); \ + } else \ + (head)->rbh_root = (tmp); \ + RB_LEFT(tmp, field) = (elm); \ + RB_PARENT(elm, field) = (tmp); \ + RB_AUGMENT(tmp); \ + if ((RB_PARENT(tmp, field))) \ + RB_AUGMENT(RB_PARENT(tmp, field)); \ +} while (0) + +#define RB_ROTATE_RIGHT(head, elm, tmp, field) do { \ + (tmp) = RB_LEFT(elm, field); \ + if ((RB_LEFT(elm, field) = RB_RIGHT(tmp, field))) { \ + RB_PARENT(RB_RIGHT(tmp, field), field) = (elm); \ + } \ + RB_AUGMENT(elm); \ + if ((RB_PARENT(tmp, field) = RB_PARENT(elm, field))) { \ + if ((elm) == RB_LEFT(RB_PARENT(elm, field), field)) \ + RB_LEFT(RB_PARENT(elm, field), field) = (tmp); \ + else \ + RB_RIGHT(RB_PARENT(elm, field), field) = (tmp); \ + } else \ + (head)->rbh_root = (tmp); \ + RB_RIGHT(tmp, field) = (elm); \ + RB_PARENT(elm, field) = (tmp); \ + RB_AUGMENT(tmp); \ + if ((RB_PARENT(tmp, field))) \ + RB_AUGMENT(RB_PARENT(tmp, field)); \ +} while (0) + +/* Generates prototypes and inline functions */ +#define RB_PROTOTYPE(name, type, field, cmp) \ + RB_PROTOTYPE_INTERNAL(name, type, field, cmp,) +#define RB_PROTOTYPE_STATIC(name, type, field, cmp) \ + RB_PROTOTYPE_INTERNAL(name, type, field, cmp, __attribute__((__unused__)) static) +#define RB_PROTOTYPE_INTERNAL(name, type, field, cmp, attr) \ +attr void name##_RB_INSERT_COLOR(struct name *, struct type *); \ +attr void name##_RB_REMOVE_COLOR(struct name *, struct type *, struct type *);\ +attr struct type *name##_RB_REMOVE(struct name *, struct type *); \ +attr struct type *name##_RB_INSERT(struct name *, struct type *); \ +attr struct type *name##_RB_FIND(struct name *, struct type *); \ +attr struct type *name##_RB_NFIND(struct name *, struct type *); \ +attr struct type *name##_RB_NEXT(struct type *); \ +attr struct type *name##_RB_PREV(struct type *); \ +attr struct type *name##_RB_MINMAX(struct name *, int); \ + \ + +/* Main rb operation. + * Moves node close to the key of elm to top + */ +#define RB_GENERATE(name, type, field, cmp) \ + RB_GENERATE_INTERNAL(name, type, field, cmp,) +#define RB_GENERATE_STATIC(name, type, field, cmp) \ + RB_GENERATE_INTERNAL(name, type, field, cmp, __attribute__((__unused__)) static) +#define RB_GENERATE_INTERNAL(name, type, field, cmp, attr) \ +attr void \ +name##_RB_INSERT_COLOR(struct name *head, struct type *elm) \ +{ \ + struct type *parent, *gparent, *tmp; \ + while ((parent = RB_PARENT(elm, field)) && \ + RB_COLOR(parent, field) == RB_RED) { \ + gparent = RB_PARENT(parent, field); \ + if (parent == RB_LEFT(gparent, field)) { \ + tmp = RB_RIGHT(gparent, field); \ + if (tmp && RB_COLOR(tmp, field) == RB_RED) { \ + RB_COLOR(tmp, field) = RB_BLACK; \ + RB_SET_BLACKRED(parent, gparent, field);\ + elm = gparent; \ + continue; \ + } \ + if (RB_RIGHT(parent, field) == elm) { \ + RB_ROTATE_LEFT(head, parent, tmp, field);\ + tmp = parent; \ + parent = elm; \ + elm = tmp; \ + } \ + RB_SET_BLACKRED(parent, gparent, field); \ + RB_ROTATE_RIGHT(head, gparent, tmp, field); \ + } else { \ + tmp = RB_LEFT(gparent, field); \ + if (tmp && RB_COLOR(tmp, field) == RB_RED) { \ + RB_COLOR(tmp, field) = RB_BLACK; \ + RB_SET_BLACKRED(parent, gparent, field);\ + elm = gparent; \ + continue; \ + } \ + if (RB_LEFT(parent, field) == elm) { \ + RB_ROTATE_RIGHT(head, parent, tmp, field);\ + tmp = parent; \ + parent = elm; \ + elm = tmp; \ + } \ + RB_SET_BLACKRED(parent, gparent, field); \ + RB_ROTATE_LEFT(head, gparent, tmp, field); \ + } \ + } \ + RB_COLOR(head->rbh_root, field) = RB_BLACK; \ +} \ + \ +attr void \ +name##_RB_REMOVE_COLOR(struct name *head, struct type *parent, struct type *elm) \ +{ \ + struct type *tmp; \ + while ((elm == NULL || RB_COLOR(elm, field) == RB_BLACK) && \ + elm != RB_ROOT(head)) { \ + if (RB_LEFT(parent, field) == elm) { \ + tmp = RB_RIGHT(parent, field); \ + if (RB_COLOR(tmp, field) == RB_RED) { \ + RB_SET_BLACKRED(tmp, parent, field); \ + RB_ROTATE_LEFT(head, parent, tmp, field);\ + tmp = RB_RIGHT(parent, field); \ + } \ + if ((RB_LEFT(tmp, field) == NULL || \ + RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) &&\ + (RB_RIGHT(tmp, field) == NULL || \ + RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK)) {\ + RB_COLOR(tmp, field) = RB_RED; \ + elm = parent; \ + parent = RB_PARENT(elm, field); \ + } else { \ + if (RB_RIGHT(tmp, field) == NULL || \ + RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK) {\ + struct type *oleft; \ + if ((oleft = RB_LEFT(tmp, field)))\ + RB_COLOR(oleft, field) = RB_BLACK;\ + RB_COLOR(tmp, field) = RB_RED; \ + RB_ROTATE_RIGHT(head, tmp, oleft, field);\ + tmp = RB_RIGHT(parent, field); \ + } \ + RB_COLOR(tmp, field) = RB_COLOR(parent, field);\ + RB_COLOR(parent, field) = RB_BLACK; \ + if (RB_RIGHT(tmp, field)) \ + RB_COLOR(RB_RIGHT(tmp, field), field) = RB_BLACK;\ + RB_ROTATE_LEFT(head, parent, tmp, field);\ + elm = RB_ROOT(head); \ + break; \ + } \ + } else { \ + tmp = RB_LEFT(parent, field); \ + if (RB_COLOR(tmp, field) == RB_RED) { \ + RB_SET_BLACKRED(tmp, parent, field); \ + RB_ROTATE_RIGHT(head, parent, tmp, field);\ + tmp = RB_LEFT(parent, field); \ + } \ + if ((RB_LEFT(tmp, field) == NULL || \ + RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) &&\ + (RB_RIGHT(tmp, field) == NULL || \ + RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK)) {\ + RB_COLOR(tmp, field) = RB_RED; \ + elm = parent; \ + parent = RB_PARENT(elm, field); \ + } else { \ + if (RB_LEFT(tmp, field) == NULL || \ + RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) {\ + struct type *oright; \ + if ((oright = RB_RIGHT(tmp, field)))\ + RB_COLOR(oright, field) = RB_BLACK;\ + RB_COLOR(tmp, field) = RB_RED; \ + RB_ROTATE_LEFT(head, tmp, oright, field);\ + tmp = RB_LEFT(parent, field); \ + } \ + RB_COLOR(tmp, field) = RB_COLOR(parent, field);\ + RB_COLOR(parent, field) = RB_BLACK; \ + if (RB_LEFT(tmp, field)) \ + RB_COLOR(RB_LEFT(tmp, field), field) = RB_BLACK;\ + RB_ROTATE_RIGHT(head, parent, tmp, field);\ + elm = RB_ROOT(head); \ + break; \ + } \ + } \ + } \ + if (elm) \ + RB_COLOR(elm, field) = RB_BLACK; \ +} \ + \ +attr struct type * \ +name##_RB_REMOVE(struct name *head, struct type *elm) \ +{ \ + struct type *child, *parent, *old = elm; \ + int color; \ + if (RB_LEFT(elm, field) == NULL) \ + child = RB_RIGHT(elm, field); \ + else if (RB_RIGHT(elm, field) == NULL) \ + child = RB_LEFT(elm, field); \ + else { \ + struct type *left; \ + elm = RB_RIGHT(elm, field); \ + while ((left = RB_LEFT(elm, field))) \ + elm = left; \ + child = RB_RIGHT(elm, field); \ + parent = RB_PARENT(elm, field); \ + color = RB_COLOR(elm, field); \ + if (child) \ + RB_PARENT(child, field) = parent; \ + if (parent) { \ + if (RB_LEFT(parent, field) == elm) \ + RB_LEFT(parent, field) = child; \ + else \ + RB_RIGHT(parent, field) = child; \ + RB_AUGMENT(parent); \ + } else \ + RB_ROOT(head) = child; \ + if (RB_PARENT(elm, field) == old) \ + parent = elm; \ + (elm)->field = (old)->field; \ + if (RB_PARENT(old, field)) { \ + if (RB_LEFT(RB_PARENT(old, field), field) == old)\ + RB_LEFT(RB_PARENT(old, field), field) = elm;\ + else \ + RB_RIGHT(RB_PARENT(old, field), field) = elm;\ + RB_AUGMENT(RB_PARENT(old, field)); \ + } else \ + RB_ROOT(head) = elm; \ + RB_PARENT(RB_LEFT(old, field), field) = elm; \ + if (RB_RIGHT(old, field)) \ + RB_PARENT(RB_RIGHT(old, field), field) = elm; \ + if (parent) { \ + left = parent; \ + do { \ + RB_AUGMENT(left); \ + } while ((left = RB_PARENT(left, field))); \ + } \ + goto color; \ + } \ + parent = RB_PARENT(elm, field); \ + color = RB_COLOR(elm, field); \ + if (child) \ + RB_PARENT(child, field) = parent; \ + if (parent) { \ + if (RB_LEFT(parent, field) == elm) \ + RB_LEFT(parent, field) = child; \ + else \ + RB_RIGHT(parent, field) = child; \ + RB_AUGMENT(parent); \ + } else \ + RB_ROOT(head) = child; \ +color: \ + if (color == RB_BLACK) \ + name##_RB_REMOVE_COLOR(head, parent, child); \ + return (old); \ +} \ + \ +/* Inserts a node into the RB tree */ \ +attr struct type * \ +name##_RB_INSERT(struct name *head, struct type *elm) \ +{ \ + struct type *tmp; \ + struct type *parent = NULL; \ + int comp = 0; \ + tmp = RB_ROOT(head); \ + while (tmp) { \ + parent = tmp; \ + comp = (cmp)(elm, parent); \ + if (comp < 0) \ + tmp = RB_LEFT(tmp, field); \ + else if (comp > 0) \ + tmp = RB_RIGHT(tmp, field); \ + else \ + return (tmp); \ + } \ + RB_SET(elm, parent, field); \ + if (parent != NULL) { \ + if (comp < 0) \ + RB_LEFT(parent, field) = elm; \ + else \ + RB_RIGHT(parent, field) = elm; \ + RB_AUGMENT(parent); \ + } else \ + RB_ROOT(head) = elm; \ + name##_RB_INSERT_COLOR(head, elm); \ + return (NULL); \ +} \ + \ +/* Finds the node with the same key as elm */ \ +attr struct type * \ +name##_RB_FIND(struct name *head, struct type *elm) \ +{ \ + struct type *tmp = RB_ROOT(head); \ + int comp; \ + while (tmp) { \ + comp = cmp(elm, tmp); \ + if (comp < 0) \ + tmp = RB_LEFT(tmp, field); \ + else if (comp > 0) \ + tmp = RB_RIGHT(tmp, field); \ + else \ + return (tmp); \ + } \ + return (NULL); \ +} \ + \ +/* Finds the first node greater than or equal to the search key */ \ +attr struct type * \ +name##_RB_NFIND(struct name *head, struct type *elm) \ +{ \ + struct type *tmp = RB_ROOT(head); \ + struct type *res = NULL; \ + int comp; \ + while (tmp) { \ + comp = cmp(elm, tmp); \ + if (comp < 0) { \ + res = tmp; \ + tmp = RB_LEFT(tmp, field); \ + } \ + else if (comp > 0) \ + tmp = RB_RIGHT(tmp, field); \ + else \ + return (tmp); \ + } \ + return (res); \ +} \ + \ +/* ARGSUSED */ \ +attr struct type * \ +name##_RB_NEXT(struct type *elm) \ +{ \ + if (RB_RIGHT(elm, field)) { \ + elm = RB_RIGHT(elm, field); \ + while (RB_LEFT(elm, field)) \ + elm = RB_LEFT(elm, field); \ + } else { \ + if (RB_PARENT(elm, field) && \ + (elm == RB_LEFT(RB_PARENT(elm, field), field))) \ + elm = RB_PARENT(elm, field); \ + else { \ + while (RB_PARENT(elm, field) && \ + (elm == RB_RIGHT(RB_PARENT(elm, field), field)))\ + elm = RB_PARENT(elm, field); \ + elm = RB_PARENT(elm, field); \ + } \ + } \ + return (elm); \ +} \ + \ +/* ARGSUSED */ \ +attr struct type * \ +name##_RB_PREV(struct type *elm) \ +{ \ + if (RB_LEFT(elm, field)) { \ + elm = RB_LEFT(elm, field); \ + while (RB_RIGHT(elm, field)) \ + elm = RB_RIGHT(elm, field); \ + } else { \ + if (RB_PARENT(elm, field) && \ + (elm == RB_RIGHT(RB_PARENT(elm, field), field))) \ + elm = RB_PARENT(elm, field); \ + else { \ + while (RB_PARENT(elm, field) && \ + (elm == RB_LEFT(RB_PARENT(elm, field), field)))\ + elm = RB_PARENT(elm, field); \ + elm = RB_PARENT(elm, field); \ + } \ + } \ + return (elm); \ +} \ + \ +attr struct type * \ +name##_RB_MINMAX(struct name *head, int val) \ +{ \ + struct type *tmp = RB_ROOT(head); \ + struct type *parent = NULL; \ + while (tmp) { \ + parent = tmp; \ + if (val < 0) \ + tmp = RB_LEFT(tmp, field); \ + else \ + tmp = RB_RIGHT(tmp, field); \ + } \ + return (parent); \ +} + +#define RB_NEGINF -1 +#define RB_INF 1 + +#define RB_INSERT(name, x, y) name##_RB_INSERT(x, y) +#define RB_REMOVE(name, x, y) name##_RB_REMOVE(x, y) +#define RB_FIND(name, x, y) name##_RB_FIND(x, y) +#define RB_NFIND(name, x, y) name##_RB_NFIND(x, y) +#define RB_NEXT(name, x, y) name##_RB_NEXT(y) +#define RB_PREV(name, x, y) name##_RB_PREV(y) +#define RB_MIN(name, x) name##_RB_MINMAX(x, RB_NEGINF) +#define RB_MAX(name, x) name##_RB_MINMAX(x, RB_INF) + +#define RB_FOREACH(x, name, head) \ + for ((x) = RB_MIN(name, head); \ + (x) != NULL; \ + (x) = name##_RB_NEXT(x)) + +#define RB_FOREACH_SAFE(x, name, head, y) \ + for ((x) = RB_MIN(name, head); \ + ((x) != NULL) && ((y) = name##_RB_NEXT(x), 1); \ + (x) = (y)) + +#define RB_FOREACH_REVERSE(x, name, head) \ + for ((x) = RB_MAX(name, head); \ + (x) != NULL; \ + (x) = name##_RB_PREV(x)) + +#define RB_FOREACH_REVERSE_SAFE(x, name, head, y) \ + for ((x) = RB_MAX(name, head); \ + ((x) != NULL) && ((y) = name##_RB_PREV(x), 1); \ + (x) = (y)) + +#endif /* _SYS_TREE_H_ */ From 0473a9be20f7413f2c57680eb05fb91a3b1bb5f5 Mon Sep 17 00:00:00 2001 From: Duncaen Date: Fri, 24 Jun 2016 16:50:17 +0200 Subject: [PATCH 035/198] rename doas_pam.c to pam.c --- configure | 2 +- doas_pam.c => pam.c | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename doas_pam.c => pam.c (100%) diff --git a/configure b/configure index c5981a5..1bb827f 100755 --- a/configure +++ b/configure @@ -284,7 +284,7 @@ int main(void) { }' [ -z "$have_bsd_auth_h" ] && \ check_func "pam_appl_h" "$src" && { - printf 'SRCS += doas_pam.c\n' >>$CONFIG_MK + printf 'SRCS += pam.c\n' >>$CONFIG_MK printf 'LDFLAGS += -lpam\n' >>$CONFIG_MK } diff --git a/doas_pam.c b/pam.c similarity index 100% rename from doas_pam.c rename to pam.c From f4a7d3640c8a8da587abd5d1023b14cc6620261d Mon Sep 17 00:00:00 2001 From: Duncaen Date: Sat, 25 Jun 2016 17:37:49 +0200 Subject: [PATCH 036/198] some more cleanup and refactoring of pam code --- doas.c | 2 +- includes.h | 2 +- pam.c | 232 +++++++++++++++++++++++++++++------------------------ 3 files changed, 128 insertions(+), 108 deletions(-) diff --git a/doas.c b/doas.c index ee1b341..d955417 100644 --- a/doas.c +++ b/doas.c @@ -379,7 +379,7 @@ main(int argc, char **argv) explicit_bzero(rbuf, sizeof(rbuf)); } #elif HAVE_PAM_APPL_H - if (!doas_pam(pw->pw_name, myname, !nflag, rule->options & NOPASS)) { + if (!pamauth(pw->pw_name, myname, !nflag, rule->options & NOPASS)) { syslog(LOG_AUTHPRIV | LOG_NOTICE, "failed auth for %s", myname); errc(1, EPERM, NULL); } diff --git a/includes.h b/includes.h index 776a940..1be024a 100644 --- a/includes.h +++ b/includes.h @@ -20,7 +20,7 @@ #include "openbsd.h" #ifdef HAVE_PAM_APPL_H -int doas_pam(const char *user, const char *ruser, int interactive, int nopass); +int pamauth(const char *, const char *, int, int); #endif #endif /* INCLUDES_H */ diff --git a/pam.c b/pam.c index 4f2731d..7842c9a 100644 --- a/pam.c +++ b/pam.c @@ -36,8 +36,10 @@ #define PAM_SERVICE_NAME "doas" static pam_handle_t *pamh = NULL; -static sig_atomic_t volatile caught_signal = 0; static char doas_prompt[128]; +static sig_atomic_t volatile caught_signal = 0; +static int session_opened = 0; +static int cred_established = 0; static void catchsig(int sig) @@ -46,10 +48,10 @@ catchsig(int sig) } static char * -prompt(const char *msg, int echo_on, int *pam) +pamprompt(const char *msg, int echo_on, int *ret) { const char *prompt; - char *ret, buf[PAM_MAX_RESP_SIZE]; + char *pass, buf[PAM_MAX_RESP_SIZE]; int flags = RPP_REQUIRE_TTY | (echo_on ? RPP_ECHO_ON : RPP_ECHO_OFF); /* overwrite default prompt if it matches "Password:[ ]" */ @@ -59,23 +61,25 @@ prompt(const char *msg, int echo_on, int *pam) else prompt = msg; - ret = readpassphrase(prompt, buf, sizeof(buf), flags); - if (!ret) - *pam = PAM_CONV_ERR; - else if (!(ret = strdup(ret))) - *pam = PAM_BUF_ERR; + pass = readpassphrase(prompt, buf, sizeof(buf), flags); + if (!pass) + *ret = PAM_CONV_ERR; + else if (!(pass = strdup(pass))) + *ret = PAM_BUF_ERR; + else + *ret = PAM_SUCCESS; explicit_bzero(buf, sizeof(buf)); - return ret; + return pass; } static int -doas_pam_conv(int nmsgs, const struct pam_message **msgs, +pamconv(int nmsgs, const struct pam_message **msgs, struct pam_response **rsps, __UNUSED void *ptr) { struct pam_response *rsp; int i, style; - int ret = PAM_SUCCESS; + int ret; if (!(rsp = calloc(nmsgs, sizeof(struct pam_response)))) errx(1, "couldn't malloc pam_response"); @@ -84,7 +88,7 @@ doas_pam_conv(int nmsgs, const struct pam_message **msgs, switch (style = msgs[i]->msg_style) { case PAM_PROMPT_ECHO_OFF: case PAM_PROMPT_ECHO_ON: - rsp[i].resp = prompt(msgs[i]->msg, style == PAM_PROMPT_ECHO_ON, &ret); + rsp[i].resp = pamprompt(msgs[i]->msg, style == PAM_PROMPT_ECHO_ON, &ret); if (ret != PAM_SUCCESS) goto fail; break; @@ -123,44 +127,125 @@ doas_pam_conv(int nmsgs, const struct pam_message **msgs, return PAM_CONV_ERR; } +void +pamcleanup(int ret) +{ + if (session_opened) + ret = pam_close_session(pamh, 0); + if (ret != PAM_SUCCESS) + errx(1, "pam_close_session: %s", pam_strerror(pamh, ret)); + + if (cred_established) + ret = pam_setcred(pamh, PAM_DELETE_CRED | PAM_SILENT); + if (ret != PAM_SUCCESS) + warn("pam_setcred(?, PAM_DELETE_CRED | PAM_SILENT): %s", + pam_strerror(pamh, ret)); + + pam_end(pamh, ret); +} + +void +watchsession(pid_t child) +{ + sigset_t sigs; + struct sigaction act, oldact; + int status; + + /* block signals */ + sigfillset(&sigs); + if (sigprocmask(SIG_BLOCK, &sigs, NULL)) { + warn("failed to block signals"); + caught_signal = 1; + goto close; + } + + /* setup signal handler */ + act.sa_handler = catchsig; + sigemptyset(&act.sa_mask); + act.sa_flags = 0; + + /* unblock SIGTERM and SIGALRM to catch them */ + sigemptyset(&sigs); + if (sigaddset(&sigs, SIGTERM) || + sigaddset(&sigs, SIGALRM) || + sigaddset(&sigs, SIGTSTP) || + sigaction(SIGTERM, &act, &oldact) || + sigprocmask(SIG_UNBLOCK, &sigs, NULL)) { + warn("failed to set signal handler"); + caught_signal = 1; + goto close; + } + + /* wait for child to be terminated */ + if (waitpid(child, &status, 0) != -1) { + if (WIFSIGNALED(status)) { + fprintf(stderr, "%s%s\n", strsignal(WTERMSIG(status)), + WCOREDUMP(status) ? " (core dumped)" : ""); + status = WTERMSIG(status) + 128; + } else + status = WEXITSTATUS(status); + } + else if (caught_signal) + status = caught_signal + 128; + else + status = 1; + +close: + if (caught_signal) { + fprintf(stderr, "\nSession terminated, killing shell\n"); + kill(child, SIGTERM); + } + + pamcleanup(PAM_SUCCESS); + + if (caught_signal) { + /* kill child */ + sleep(2); + kill(child, SIGKILL); + fprintf(stderr, " ...killed.\n"); + + /* unblock cached signal and resend */ + sigaction(SIGTERM, &oldact, NULL); + if (caught_signal != SIGTERM) + caught_signal = SIGKILL; + kill(getpid(), caught_signal); + } + + exit(status); +} + int -doas_pam(const char *user, const char* ruser, int interactive, int nopass) +pamauth(const char *user, const char* ruser, int interactive, int nopass) { static const struct pam_conv conv = { - .conv = doas_pam_conv, + .conv = pamconv, .appdata_ptr = NULL, }; - const char *ttydev, *tty; + const char *ttydev; pid_t child; int ret; if (!user || !ruser) return 0; - /* pam needs the real root */ - if(setuid(0)) - errx(1, "setuid"); - ret = pam_start(PAM_SERVICE_NAME, ruser, &conv, &pamh); if (ret != PAM_SUCCESS) - errx(1, "pam_start(\"%s\", \"%s\", ?, ?): failed\n", + errx(1, "pam_start(\"%s\", \"%s\", ?, ?): failed", PAM_SERVICE_NAME, ruser); ret = pam_set_item(pamh, PAM_RUSER, ruser); if (ret != PAM_SUCCESS) - warn("pam_set_item(?, PAM_RUSER, \"%s\"): %s\n", + warn("pam_set_item(?, PAM_RUSER, \"%s\"): %s", pam_strerror(pamh, ret), ruser); if (isatty(0) && (ttydev = ttyname(0)) != NULL) { - if (strncmp(ttydev, "/dev/", 5)) - tty = ttydev + 5; - else - tty = ttydev; + if (strncmp(ttydev, "/dev/", 5) == 0) + ttydev += 5; - ret = pam_set_item(pamh, PAM_TTY, tty); + ret = pam_set_item(pamh, PAM_TTY, ttydev); if (ret != PAM_SUCCESS) - warn("pam_set_item(?, PAM_TTY, \"%s\"): %s\n", - tty, pam_strerror(pamh, ret)); + warn("pam_set_item(?, PAM_TTY, \"%s\"): %s", + ttydev, pam_strerror(pamh, ret)); } if (!nopass) { @@ -177,7 +262,7 @@ doas_pam(const char *user, const char* ruser, int interactive, int nopass) /* authenticate */ ret = pam_authenticate(pamh, 0); if (ret != PAM_SUCCESS) { - pam_end(pamh, ret); + pamcleanup(ret); return 0; } } @@ -190,100 +275,35 @@ doas_pam(const char *user, const char* ruser, int interactive, int nopass) if (ret != PAM_SUCCESS) return 0; + /* set PAM_USER to the user we want to be */ ret = pam_set_item(pamh, PAM_USER, user); if (ret != PAM_SUCCESS) - warn("pam_set_item(?, PAM_USER, \"%s\"): %s\n", user, + warn("pam_set_item(?, PAM_USER, \"%s\"): %s", user, pam_strerror(pamh, ret)); ret = pam_setcred(pamh, PAM_ESTABLISH_CRED); if (ret != PAM_SUCCESS) - warn("pam_setcred(?, PAM_ESTABLISH_CRED): %s\n", pam_strerror(pamh, ret)); + warn("pam_setcred(?, PAM_ESTABLISH_CRED): %s", pam_strerror(pamh, ret)); + else + cred_established = 1; /* open session */ ret = pam_open_session(pamh, 0); if (ret != PAM_SUCCESS) - errx(1, "pam_open_session(): %s\n", pam_strerror(pamh, ret)); + errx(1, "pam_open_session: %s", pam_strerror(pamh, ret)); + else + session_opened = 1; if ((child = fork()) == -1) { - pam_close_session(pamh, 0); - pam_end(pamh, PAM_ABORT); - errx(1, "fork()"); + pamcleanup(PAM_ABORT); + errx(1, "fork"); } /* return as child */ - if (child == 0) { + if (child == 0) return 1; - } - - /* parent watches for signals and closes session */ - sigset_t sigs; - struct sigaction act, oldact; - int status; - - /* block signals */ - sigfillset(&sigs); - if (sigprocmask(SIG_BLOCK, &sigs, NULL)) { - warn("failed to block signals"); - caught_signal = 1; - } - - /* setup signal handler */ - act.sa_handler = catchsig; - sigemptyset(&act.sa_mask); - act.sa_flags = 0; - - /* unblock SIGTERM and SIGALRM to catch them */ - sigemptyset(&sigs); - if (sigaddset(&sigs, SIGTERM) || - sigaddset(&sigs, SIGALRM) || - sigaction(SIGTERM, &act, &oldact) || - sigprocmask(SIG_UNBLOCK, &sigs, NULL)) { - warn("failed to set signal handler"); - caught_signal = 1; - } - - if (!caught_signal) { - /* wait for child to be terminated */ - if (waitpid(child, &status, 0) != -1) { - if (WIFSIGNALED(status)) { - fprintf(stderr, "%s%s\n", strsignal(WTERMSIG(status)), - WCOREDUMP(status) ? " (core dumped)" : ""); - status = WTERMSIG(status) + 128; - } else { - status = WEXITSTATUS(status); - } - } - else if (caught_signal) - status = caught_signal + 128; - else - status = 1; - } - - if (caught_signal) { - fprintf(stderr, "\nSession terminated, killing shell\n"); - kill(child, SIGTERM); - } - - /* close session */ - ret = pam_close_session(pamh, 0); - if (ret != PAM_SUCCESS) - errx(1, "pam_close_session(): %s\n", pam_strerror(pamh, ret)); - pam_end(pamh, PAM_SUCCESS); + watchsession(child); - if (caught_signal) { - /* kill child */ - sleep(2); - kill(child, SIGKILL); - fprintf(stderr, " ...killed.\n"); - - /* unblock cached signal and resend */ - sigaction(SIGTERM, &oldact, NULL); - if (caught_signal != SIGTERM) - caught_signal = SIGKILL; - kill(getpid(), caught_signal); - } - - exit(status); return 0; } From 788dd4b64a25b2705f962c37962363c22d9c49f9 Mon Sep 17 00:00:00 2001 From: Duncaen Date: Sat, 25 Jun 2016 17:41:04 +0200 Subject: [PATCH 037/198] fix err messages --- doas.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doas.c b/doas.c index d955417..e74881b 100644 --- a/doas.c +++ b/doas.c @@ -398,11 +398,11 @@ main(int argc, char **argv) errx(1, "failed to set user context for target"); #else if (setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) != 0) - errx(1, "setgid"); + errx(1, "setresgid"); if (initgroups(pw->pw_name, pw->pw_gid) != 0) errx(1, "initgroups"); if (setresuid(target, target, target) != 0) - errx(1, "setuid"); + errx(1, "setresuid"); #endif if (pledge("stdio rpath exec", NULL) == -1) From e88a009620a604d57d85bd53b971cfa77e44315e Mon Sep 17 00:00:00 2001 From: Duncaen Date: Sun, 26 Jun 2016 23:10:37 +0200 Subject: [PATCH 038/198] add --without-pam configure option to allow passwd/shadow auth --- bsd.prog.mk | 6 +++--- configure | 19 +++++++++++++++++-- doas.c | 52 +++++++++++++++++++++++++++++++++++++++++++++++----- 3 files changed, 67 insertions(+), 10 deletions(-) diff --git a/bsd.prog.mk b/bsd.prog.mk index f5b3efe..03dcfb8 100644 --- a/bsd.prog.mk +++ b/bsd.prog.mk @@ -18,14 +18,14 @@ ${PROG}: ${OBJS} libopenbsd.a install: ${PROG} ${PAM_DOAS} ${MAN} mkdir -p -m 0755 ${DESTDIR}${BINDIR} - mkdir -p -m 0755 ${DESTDIR}${PAMDIR} + [ -n "${PAM_DOAS}" ] && mkdir -p -m 0755 ${DESTDIR}${PAMDIR} mkdir -p -m 0755 ${DESTDIR}${MANDIR}/man1 mkdir -p -m 0755 ${DESTDIR}${MANDIR}/man5 cp -f ${PROG} ${DESTDIR}${BINDIR} chown ${BINOWN}:${BINGRP} ${DESTDIR}${BINDIR}/${PROG} chmod ${BINMODE} ${DESTDIR}${BINDIR}/${PROG} - cp ${PAM_DOAS} ${DESTDIR}${PAMDIR}/doas - chmod 0644 ${DESTDIR}${PAMDIR}/doas + [ -n "${PAM_DOAS}" ] && cp ${PAM_DOAS} ${DESTDIR}${PAMDIR}/doas || true + [ -n "${PAM_DOAS}" ] && chmod 0644 ${DESTDIR}${PAMDIR}/doas cp -f doas.1 ${DESTDIR}${MANDIR}/man1 cp -f doas.conf.5 ${DESTDIR}${MANDIR}/man5 diff --git a/configure b/configure index 1bb827f..9387d30 100755 --- a/configure +++ b/configure @@ -45,6 +45,7 @@ for x; do --target) TARGET=$var;; --enable-debug) DEBUG=yes;; --enable-static) BUILD_STATIC=yes;; + --without-pam) WITHOUT_PAM=yes;; --help|-h) usage;; *) die "Error: unknown option $opt";; esac @@ -96,7 +97,8 @@ case "$OS" in linux) OS_CFLAGS="$OS_CFLAGS -D_DEFAULT_SOURCE -D_GNU_SOURCE -DUID_MAX=60000 -DGID_MAX=60000" printf 'CURDIR := .\n' >>$CONFIG_MK - printf 'PAM_DOAS = pam.d__doas__linux\n' >>$CONFIG_MK + [ -z "$WITHOUT_PAM" ] && \ + printf 'PAM_DOAS = pam.d__doas__linux\n' >>$CONFIG_MK ;; esac @@ -282,12 +284,25 @@ src=' int main(void) { return 0; }' -[ -z "$have_bsd_auth_h" ] && \ +[ -z "$WITHOUT_PAM" -a -z "$have_bsd_auth_h" ] && \ check_func "pam_appl_h" "$src" && { printf 'SRCS += pam.c\n' >>$CONFIG_MK printf 'LDFLAGS += -lpam\n' >>$CONFIG_MK } +# +# Check for shadow.h. +# +src=' +#include +int main(void) { + return 0; +}' +[ -z "$WITHOUT_PAM" -a -z "$have_bsd_auth_h" ] || \ + check_func "shadow_h" "$src" && { + printf 'LDFLAGS += -lcrypt\n' >>$CONFIG_MK + } + # # Check for execvpe(). # diff --git a/doas.c b/doas.c index e74881b..33be571 100644 --- a/doas.c +++ b/doas.c @@ -28,6 +28,9 @@ #include #include #include +#if HAVE_SHADOW_H +#include +#endif #include "includes.h" @@ -341,10 +344,6 @@ main(int argc, char **argv) errc(1, EPERM, NULL); } - pw = getpwuid(target); - if (!pw) - errx(1, "no passwd entry for target"); - #ifdef HAVE_BSD_AUTH_H if (!(rule->options & NOPASS)) { if (nflag) @@ -379,24 +378,67 @@ main(int argc, char **argv) explicit_bzero(rbuf, sizeof(rbuf)); } #elif HAVE_PAM_APPL_H + pw = getpwuid(target); + if (!pw) + errx(1, "no passwd entry for target"); + if (!pamauth(pw->pw_name, myname, !nflag, rule->options & NOPASS)) { syslog(LOG_AUTHPRIV | LOG_NOTICE, "failed auth for %s", myname); errc(1, EPERM, NULL); } -#else +#elif HAVE_SHADOW_H + const char *pass; + if (!(rule->options & NOPASS)) { + if (nflag) errx(1, "Authorization required"); + + pass = pw->pw_passwd; + if (pass[0] == 'x' && pass[1] == '\0') { + struct spwd *sp; + if (!(sp = getspnam(myname))) + errx(1, "Authorization failed"); + pass = sp->sp_pwdp; + } + + char *challenge, *response, rbuf[1024], cbuf[128], host[HOST_NAME_MAX + 1]; + if (gethostname(host, sizeof(host))) + snprintf(host, sizeof(host), "?"); + snprintf(cbuf, sizeof(cbuf), + "\rdoas (%.32s@%.32s) password: ", myname, host); + challenge = cbuf; + + response = readpassphrase(challenge, rbuf, sizeof(rbuf), RPP_REQUIRE_TTY); + if (response == NULL && errno == ENOTTY) { + syslog(LOG_AUTHPRIV | LOG_NOTICE, + "tty required for %s", myname); + errx(1, "a tty is required"); + } + if (strcmp(crypt(response, pass), pass) != 0) { + syslog(LOG_AUTHPRIV | LOG_NOTICE, "failed auth for %s", myname); + errc(1, EPERM, NULL); + } + explicit_bzero(rbuf, sizeof(rbuf)); + } +#else + if (!(rule->options & NOPASS)) + errx(1, "Authorization required"); #endif /* HAVE_BSD_AUTH_H */ if (pledge("stdio rpath getpw exec id", NULL) == -1) err(1, "pledge"); + pw = getpwuid(target); + if (!pw) + errx(1, "no passwd entry for target"); + #ifdef HAVE_BSD_AUTH_H if (setusercontext(NULL, pw, target, LOGIN_SETGROUP | LOGIN_SETPRIORITY | LOGIN_SETRESOURCES | LOGIN_SETUMASK | LOGIN_SETUSER) != 0) errx(1, "failed to set user context for target"); #else + warn(pw->pw_name); if (setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) != 0) errx(1, "setresgid"); if (initgroups(pw->pw_name, pw->pw_gid) != 0) From 3f6bcbafcddabe79049de65d47cc83414ea2f7ce Mon Sep 17 00:00:00 2001 From: Duncaen Date: Wed, 8 Jun 2016 13:42:17 +0200 Subject: [PATCH 039/198] bump version 0.3 --- configure | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure b/configure index 9387d30..f5474ed 100755 --- a/configure +++ b/configure @@ -55,7 +55,7 @@ CONFIG_MK=config.mk rm -f "$CONFIG_MK" # : ${VERSION:="$(git describe --dirty --tags --long --always)"} -: ${VERSION:="0.2"} +: ${VERSION:="0.3"} cat <>$CONFIG_MK PREFIX ?= ${PREFIX:="/usr"} From e0c0b370ea6a9d0efc642b059fced85f7f5249a5 Mon Sep 17 00:00:00 2001 From: Duncaen Date: Sun, 26 Jun 2016 23:22:27 +0200 Subject: [PATCH 040/198] remove unnecessary warning output --- doas.c | 1 - 1 file changed, 1 deletion(-) diff --git a/doas.c b/doas.c index 33be571..b5f8049 100644 --- a/doas.c +++ b/doas.c @@ -438,7 +438,6 @@ main(int argc, char **argv) LOGIN_SETUSER) != 0) errx(1, "failed to set user context for target"); #else - warn(pw->pw_name); if (setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) != 0) errx(1, "setresgid"); if (initgroups(pw->pw_name, pw->pw_gid) != 0) From 1606730a38b9adacfc8c52f8eeedd5076b08c2f6 Mon Sep 17 00:00:00 2001 From: Duncaen Date: Sun, 26 Jun 2016 23:22:52 +0200 Subject: [PATCH 041/198] remove pam_timestamp from pam config --- pam.d__doas__linux | 2 -- 1 file changed, 2 deletions(-) diff --git a/pam.d__doas__linux b/pam.d__doas__linux index 813a0e4..5e7885a 100644 --- a/pam.d__doas__linux +++ b/pam.d__doas__linux @@ -1,9 +1,7 @@ #%PAM-1.0 -auth sufficient pam_timestamp.so timestamp_timeout=300 auth required pam_unix.so account required pam_unix.so session optional pam_xauth.so session optional pam_umask.so usergroups umask=022 -session optional pam_timestamp.so timestamp_timeout=300 session required pam_env.so session required pam_unix.so From f30e68c675405fed00b8d5a7bf26ce440b634e7c Mon Sep 17 00:00:00 2001 From: Duncaen Date: Sun, 26 Jun 2016 23:23:30 +0200 Subject: [PATCH 042/198] bump version 0.3.1 --- configure | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure b/configure index f5474ed..b5cbf80 100755 --- a/configure +++ b/configure @@ -55,7 +55,7 @@ CONFIG_MK=config.mk rm -f "$CONFIG_MK" # : ${VERSION:="$(git describe --dirty --tags --long --always)"} -: ${VERSION:="0.3"} +: ${VERSION:="0.3.1"} cat <>$CONFIG_MK PREFIX ?= ${PREFIX:="/usr"} From aedbe76e8443757d8553a35fdc217722dcd1470b Mon Sep 17 00:00:00 2001 From: Duncaen Date: Mon, 27 Jun 2016 18:18:42 +0200 Subject: [PATCH 043/198] fix sys/tree.h test --- configure | 22 ++++++++++++---------- env.c | 4 ---- 2 files changed, 12 insertions(+), 14 deletions(-) diff --git a/configure b/configure index b5cbf80..48a515a 100755 --- a/configure +++ b/configure @@ -266,16 +266,6 @@ int main(void) { check_func "bsd_auth_h" "$src" && \ have_bsd_auth_h=1 -# -# Check for sys/tree.h. -# -src=' -#include -int main(void) { - return 0; -}' -check_func "sys_tree_h" "$src" - # # Check for pam_appl.h. # @@ -342,3 +332,15 @@ int main(void) { check_func "pledge" "$src" || { printf 'OPENBSD += pledge-noop.o\n' >>$CONFIG_MK } + +# +# +# +src=' +#include +int main(void){return 0;} +__attribute__((__unused__)) static void foo(void){return;} +' +check_func "__attribute__" "$src" || { + printf 'CFLAGS += -DNO_ATTRIBUTE_ON_RETURN_TYPE=1\n' >>$CONFIG_MK +} diff --git a/env.c b/env.c index 9baf4a5..f25d21f 100644 --- a/env.c +++ b/env.c @@ -16,11 +16,7 @@ */ #include -#ifdef HAVE_SYS_TREE_H -#include -#else #include "sys-tree.h" -#endif #include #include From c05e559c4c473655da25d0fcae207aa3345b029b Mon Sep 17 00:00:00 2001 From: Duncaen Date: Mon, 27 Jun 2016 18:19:31 +0200 Subject: [PATCH 044/198] fix pamcleanup --- pam.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pam.c b/pam.c index 7842c9a..ec89d04 100644 --- a/pam.c +++ b/pam.c @@ -130,17 +130,17 @@ pamconv(int nmsgs, const struct pam_message **msgs, void pamcleanup(int ret) { - if (session_opened) + if (session_opened != 0) { ret = pam_close_session(pamh, 0); if (ret != PAM_SUCCESS) errx(1, "pam_close_session: %s", pam_strerror(pamh, ret)); - - if (cred_established) + } + if (cred_established != 0) { ret = pam_setcred(pamh, PAM_DELETE_CRED | PAM_SILENT); if (ret != PAM_SUCCESS) warn("pam_setcred(?, PAM_DELETE_CRED | PAM_SILENT): %s", pam_strerror(pamh, ret)); - + } pam_end(pamh, ret); } From 45d57dad74fc9bfea1597a4903ce902eaf76529a Mon Sep 17 00:00:00 2001 From: Duncaen Date: Mon, 27 Jun 2016 18:47:24 +0200 Subject: [PATCH 045/198] fix --with(out)-pam configure option --- configure | 44 ++++++++++++++++++++++++-------------------- 1 file changed, 24 insertions(+), 20 deletions(-) diff --git a/configure b/configure index 48a515a..6c35a92 100755 --- a/configure +++ b/configure @@ -24,6 +24,8 @@ usage: configure [options] --enable-debug enable debugging --enable-static prepare for static build + --without-pam disable pam support + --help, -h display this help and exit EOF exit 0 @@ -33,21 +35,22 @@ for x; do opt=${x%%=*} var=${x#*=} case "$opt" in - --prefix) PREFIX=$var;; - --exec-prefix) EPREFIX=$var;; - --bindir) BINDIR=$var;; - --datadir) SHAREDIR=$var;; - --mandir) MANDIR=$var;; - --sysconfdir) SYSCONFDIR=$var;; - --pamdir) PAMDIR=$var;; - --build) BUILD=$var;; - --host) HOST=$var;; - --target) TARGET=$var;; - --enable-debug) DEBUG=yes;; - --enable-static) BUILD_STATIC=yes;; - --without-pam) WITHOUT_PAM=yes;; - --help|-h) usage;; - *) die "Error: unknown option $opt";; + --prefix) PREFIX=$var ;; + --exec-prefix) EPREFIX=$var ;; + --bindir) BINDIR=$var ;; + --datadir) SHAREDIR=$var ;; + --mandir) MANDIR=$var ;; + --sysconfdir) SYSCONFDIR=$var ;; + --pamdir) PAMDIR=$var ;; + --build) BUILD=$var ;; + --host) HOST=$var ;; + --target) TARGET=$var ;; + --enable-debug) DEBUG=yes ;; + --enable-static) BUILD_STATIC=yes ;; + --with-pam) WITHOUT_PAM= ;; + --without-pam) WITHOUT_PAM=yes ;; + --help|-h) usage ;; + *) die "Error: unknown option $opt" ;; esac done @@ -274,11 +277,12 @@ src=' int main(void) { return 0; }' -[ -z "$WITHOUT_PAM" -a -z "$have_bsd_auth_h" ] && \ - check_func "pam_appl_h" "$src" && { +check_func "pam_appl_h" "$src" && { + [ -z "$WITHOUT_PAM" -a -z "$have_bsd_auth_h" ] && { printf 'SRCS += pam.c\n' >>$CONFIG_MK printf 'LDFLAGS += -lpam\n' >>$CONFIG_MK } +} # # Check for shadow.h. @@ -288,10 +292,10 @@ src=' int main(void) { return 0; }' -[ -z "$WITHOUT_PAM" -a -z "$have_bsd_auth_h" ] || \ - check_func "shadow_h" "$src" && { +check_func "shadow_h" "$src" && { + [ -n "$WITHOUT_PAM" -a -z "$have_bsd_auth_h" ] && \ printf 'LDFLAGS += -lcrypt\n' >>$CONFIG_MK - } +} # # Check for execvpe(). From c88a56ca3e159ac6782e8b44ff36a657f94d1d68 Mon Sep 17 00:00:00 2001 From: Duncaen Date: Mon, 27 Jun 2016 18:50:34 +0200 Subject: [PATCH 046/198] bump to version v0.3.2 --- configure | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure b/configure index 6c35a92..e15e509 100755 --- a/configure +++ b/configure @@ -58,7 +58,7 @@ CONFIG_MK=config.mk rm -f "$CONFIG_MK" # : ${VERSION:="$(git describe --dirty --tags --long --always)"} -: ${VERSION:="0.3.1"} +: ${VERSION:="0.3.2"} cat <>$CONFIG_MK PREFIX ?= ${PREFIX:="/usr"} From 154b849c750be60d4c76cdcac56314b28c8a2790 Mon Sep 17 00:00:00 2001 From: Svyatoslav Mishyn Date: Mon, 27 Jun 2016 22:05:56 +0300 Subject: [PATCH 047/198] configure: fix usage() formatting --- configure | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure b/configure index e15e509..e6a842a 100755 --- a/configure +++ b/configure @@ -24,7 +24,7 @@ usage: configure [options] --enable-debug enable debugging --enable-static prepare for static build - --without-pam disable pam support + --without-pam disable pam support --help, -h display this help and exit EOF From 7c37e228b05e21488634da336449ece27b3894b6 Mon Sep 17 00:00:00 2001 From: Svyatoslav Mishyn Date: Mon, 27 Jun 2016 22:33:32 +0300 Subject: [PATCH 048/198] fix make install --- bsd.prog.mk | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bsd.prog.mk b/bsd.prog.mk index 03dcfb8..93032d2 100644 --- a/bsd.prog.mk +++ b/bsd.prog.mk @@ -18,14 +18,14 @@ ${PROG}: ${OBJS} libopenbsd.a install: ${PROG} ${PAM_DOAS} ${MAN} mkdir -p -m 0755 ${DESTDIR}${BINDIR} - [ -n "${PAM_DOAS}" ] && mkdir -p -m 0755 ${DESTDIR}${PAMDIR} + [ -n "${PAM_DOAS}" ] && mkdir -p -m 0755 ${DESTDIR}${PAMDIR} || true mkdir -p -m 0755 ${DESTDIR}${MANDIR}/man1 mkdir -p -m 0755 ${DESTDIR}${MANDIR}/man5 cp -f ${PROG} ${DESTDIR}${BINDIR} chown ${BINOWN}:${BINGRP} ${DESTDIR}${BINDIR}/${PROG} chmod ${BINMODE} ${DESTDIR}${BINDIR}/${PROG} [ -n "${PAM_DOAS}" ] && cp ${PAM_DOAS} ${DESTDIR}${PAMDIR}/doas || true - [ -n "${PAM_DOAS}" ] && chmod 0644 ${DESTDIR}${PAMDIR}/doas + [ -n "${PAM_DOAS}" ] && chmod 0644 ${DESTDIR}${PAMDIR}/doas || true cp -f doas.1 ${DESTDIR}${MANDIR}/man1 cp -f doas.conf.5 ${DESTDIR}${MANDIR}/man5 From 555da719ab036a43d3ec8636840f5e74e1ff97e0 Mon Sep 17 00:00:00 2001 From: tedu Date: Fri, 24 Jun 2016 20:49:56 +0000 Subject: [PATCH 049/198] move a space to the correct spot --- doas.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doas.c b/doas.c index b5f8049..c1b822b 100644 --- a/doas.c +++ b/doas.c @@ -328,7 +328,7 @@ main(int argc, char **argv) parseconfig("/etc/doas.conf", 1); /* cmdline is used only for logging, no need to abort on truncate */ - (void) strlcpy(cmdline, argv[0], sizeof(cmdline)); + (void)strlcpy(cmdline, argv[0], sizeof(cmdline)); for (i = 1; i < argc; i++) { if (strlcat(cmdline, " ", sizeof(cmdline)) >= sizeof(cmdline)) break; @@ -338,7 +338,7 @@ main(int argc, char **argv) cmd = argv[0]; if (!permit(uid, groups, ngroups, &rule, target, cmd, - (const char**)argv + 1)) { + (const char **)argv + 1)) { syslog(LOG_AUTHPRIV | LOG_NOTICE, "failed command for %s: %s", myname, cmdline); errc(1, EPERM, NULL); From 5e9d76849fdad47f59b11993ee8ca319bd11414d Mon Sep 17 00:00:00 2001 From: tedu Date: Mon, 27 Jun 2016 15:41:17 +0000 Subject: [PATCH 050/198] revise environment handling. Add a setenv keyword for manipulating the environment. keepenv now means only retain everything. (for one release, the old use of keepenv will still work.) Allow setting variables to new or existing values, and also removing vars when keepenv is used. ok djm martijn tb --- doas.conf.5 | 24 +++++-- env.c | 185 ++++++++++++++++++++++++++++++---------------------- parse.y | 17 ++++- 3 files changed, 137 insertions(+), 89 deletions(-) diff --git a/doas.conf.5 b/doas.conf.5 index 1dfeaee..6273e72 100644 --- a/doas.conf.5 +++ b/doas.conf.5 @@ -57,9 +57,16 @@ The default is to reset the environment, except for the variables .Ev USER and .Ev USERNAME . -.It Ic keepenv { Oo Ar variable ... Oc Ic } +.It Ic setenv { Oo Ar variable ... Oc Ic Oo Ar variable=value ... Oc Ic } In addition to the variables mentioned above, keep the space-separated specified variables. +Variables may also be removed with a leading - or set using the latter syntax. +If the first character of +.Ar value +is a +.Ql $ +then the value to be set is taken from the existing environment +variable of the same name. .El .It Ar identity The username to match. @@ -110,21 +117,24 @@ it isn't considered a keyword. The following example permits users in group wsrc to build ports, wheel to execute commands as any user while keeping the environment variables -.Ev ENV , -.Ev PS1 , +.Ev PS1 +and +.Ev SSH_AUTH_SOCK and -.Ev SSH_AUTH_SOCK , -and additionally permits tedu to run procmap as root without a password. +unsetting +.Ev ENV , +permits tedu to run procmap as root without a password, +and additionally permits root to run unrestricted commands as itself. .Bd -literal -offset indent # Non-exhaustive list of variables needed to # build release(8) and ports(7) -permit nopass keepenv { \e +permit nopass setenv { \e FTPMODE PKG_CACHE PKG_PATH SM_PATH SSH_AUTH_SOCK \e DESTDIR DISTDIR FETCH_CMD FLAVOR GROUP MAKE MAKECONF \e MULTI_PACKAGES NOMAN OKAY_FILES OWNER PKG_DBDIR \e PKG_DESTDIR PKG_TMPDIR PORTSDIR RELEASEDIR SHARED_ONLY \e SUBPACKAGE WRKOBJDIR SUDO_PORT_V1 } :wsrc -permit nopass keepenv { ENV PS1 SSH_AUTH_SOCK } :wheel +permit nopass setenv { -ENV PS1=$DOAS_PS1 SSH_AUTH_SOCK } :wheel permit nopass tedu as root cmd /usr/sbin/procmap .Ed .Sh SEE ALSO diff --git a/env.c b/env.c index f25d21f..42a0d2e 100644 --- a/env.c +++ b/env.c @@ -39,19 +39,38 @@ struct env { u_int count; }; -int +static int envcmp(struct envnode *a, struct envnode *b) { return strcmp(a->key, b->key); } RB_GENERATE_STATIC(envtree, envnode, node, envcmp) -struct env *createenv(char **); -struct env *filterenv(struct env *, struct rule *); -char **flattenenv(struct env *); +static struct envnode * +createnode(const char *key, const char *value) +{ + struct envnode *node; -struct env * -createenv(char **envp) + node = malloc(sizeof(*node)); + if (!node) + err(1, NULL); + node->key = strdup(key); + node->value = strdup(value); + if (!node->key || !node->value) + err(1, NULL); + return node; +} + +static void +freenode(struct envnode *node) +{ + free((char *)node->key); + free((char *)node->value); + free(node); +} + +static struct env * +createenv(struct rule *rule) { struct env *env; u_int i; @@ -62,34 +81,40 @@ createenv(char **envp) RB_INIT(&env->root); env->count = 0; - for (i = 0; envp[i] != NULL; i++) { - struct envnode *node; - const char *e, *eq; - - e = envp[i]; - - if ((eq = strchr(e, '=')) == NULL || eq == e) - continue; - node = malloc(sizeof(*node)); - if (!node) - err(1, NULL); - node->key = strndup(envp[i], eq - e); - node->value = strdup(eq + 1); - if (!node->key || !node->value) - err(1, NULL); - if (RB_FIND(envtree, &env->root, node)) { - free((char *)node->key); - free((char *)node->value); - free(node); - } else { - RB_INSERT(envtree, &env->root, node); - env->count++; + if (rule->options & KEEPENV) { + extern const char **environ; + + for (i = 0; environ[i] != NULL; i++) { + struct envnode *node; + const char *e, *eq; + size_t len; + char keybuf[1024]; + + e = environ[i]; + + /* ignore invalid or overlong names */ + if ((eq = strchr(e, '=')) == NULL || eq == e) + continue; + len = eq - e; + if (len > sizeof(keybuf) - 1) + continue; + memcpy(keybuf, e, len); + keybuf[len] = '\0'; + + node = createnode(keybuf, eq + 1); + if (RB_INSERT(envtree, &env->root, node)) { + /* ignore any later duplicates */ + freenode(node); + } else { + env->count++; + } } } + return env; } -char ** +static char ** flattenenv(struct env *env) { char **envp; @@ -110,72 +135,74 @@ flattenenv(struct env *env) } static void -copyenv(struct env *orig, struct env *copy, const char **envlist) +fillenv(struct env *env, const char **envlist) { struct envnode *node, key; + const char *e, *eq; + const char *val; + char name[1024]; u_int i; + size_t len; for (i = 0; envlist[i]; i++) { - key.key = envlist[i]; - if ((node = RB_FIND(envtree, &orig->root, &key))) { - RB_REMOVE(envtree, &orig->root, node); - orig->count--; - RB_INSERT(envtree, ©->root, node); - copy->count++; + e = envlist[i]; + + /* parse out env name */ + if ((eq = strchr(e, '=')) == NULL) + len = strlen(e); + else + len = eq - e; + if (len > sizeof(name) - 1) + continue; + memcpy(name, e, len); + name[len] = '\0'; + + /* delete previous copies */ + key.key = name; + if (*name == '-') + key.key = name + 1; + if ((node = RB_FIND(envtree, &env->root, &key))) { + RB_REMOVE(envtree, &env->root, node); + freenode(node); + env->count--; + } + if (*name == '-') + continue; + + /* assign value or inherit from environ */ + if (eq) { + val = eq + 1; + if (*val == '$') + val = getenv(val + 1); + } else { + val = getenv(name); + } + /* at last, we have something to insert */ + if (val) { + node = createnode(name, val); + RB_INSERT(envtree, &env->root, node); + env->count++; } } } -struct env * -filterenv(struct env *orig, struct rule *rule) +char ** +prepenv(struct rule *rule) { - const char *safeset[] = { + static const char *safeset[] = { "DISPLAY", "HOME", "LOGNAME", "MAIL", "PATH", "TERM", "USER", "USERNAME", NULL }; - const char *badset[] = { - "ENV", - NULL - }; - struct env *copy; - struct envnode *node, key; - u_int i; - - if ((rule->options & KEEPENV) && !rule->envlist) { - for (i = 0; badset[i]; i++) { - key.key = badset[i]; - if ((node = RB_FIND(envtree, &orig->root, &key))) { - RB_REMOVE(envtree, &orig->root, node); - free((char *)node->key); - free((char *)node->value); - free(node); - orig->count--; - } - } - return orig; - } - - copy = malloc(sizeof(*copy)); - if (!copy) - err(1, NULL); - RB_INIT(©->root); - copy->count = 0; + struct env *env; + + env = createenv(rule); + /* if we started with blank, fill some defaults then apply rules */ + if (!(rule->options & KEEPENV)) + fillenv(env, safeset); if (rule->envlist) - copyenv(orig, copy, rule->envlist); - copyenv(orig, copy, safeset); - - return copy; -} + fillenv(env, rule->envlist); -char ** -prepenv(struct rule *rule) -{ - extern char **environ; - struct env *env; - - env = createenv(environ); - env = filterenv(env, rule); return flattenenv(env); } diff --git a/parse.y b/parse.y index 0307b0f..506eb68 100644 --- a/parse.y +++ b/parse.y @@ -51,6 +51,7 @@ FILE *yyfp; struct rule **rules; int nrules, maxrules; int parse_errors = 0; +int obsolete_warned = 0; void yyerror(const char *, ...); int yylex(void); @@ -59,7 +60,7 @@ int yyparse(void); %} %token TPERMIT TDENY TAS TCMD TARGS -%token TNOPASS TKEEPENV +%token TNOPASS TKEEPENV TSETENV %token TSTRING %% @@ -100,6 +101,8 @@ action: TPERMIT options { $$.envlist = $2.envlist; } | TDENY { $$.action = DENY; + $$.options = 0; + $$.envlist = NULL; } ; options: /* none */ { @@ -110,7 +113,7 @@ options: /* none */ { $$.envlist = $1.envlist; if ($2.envlist) { if ($$.envlist) { - yyerror("can't have two keepenv sections"); + yyerror("can't have two setenv sections"); YYERROR; } else $$.envlist = $2.envlist; @@ -123,7 +126,14 @@ option: TNOPASS { $$.options = KEEPENV; $$.envlist = NULL; } | TKEEPENV '{' envlist '}' { - $$.options = KEEPENV; + $$.options = 0; + if (!obsolete_warned) { + warnx("keepenv with list is obsolete"); + obsolete_warned = 1; + } + $$.envlist = $3.envlist; + } | TSETENV '{' envlist '}' { + $$.options = 0; $$.envlist = $3.envlist; } ; @@ -199,6 +209,7 @@ struct keyword { { "args", TARGS }, { "nopass", TNOPASS }, { "keepenv", TKEEPENV }, + { "setenv", TSETENV }, }; int From 3460a3c722774871d697ab2223479bddc79bb209 Mon Sep 17 00:00:00 2001 From: tedu Date: Mon, 27 Jun 2016 15:47:38 +0000 Subject: [PATCH 051/198] somehow nopass snuck onto the :wheel example. i think it's better without. --- doas.conf.5 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doas.conf.5 b/doas.conf.5 index 6273e72..46fa4bf 100644 --- a/doas.conf.5 +++ b/doas.conf.5 @@ -134,7 +134,7 @@ permit nopass setenv { \e MULTI_PACKAGES NOMAN OKAY_FILES OWNER PKG_DBDIR \e PKG_DESTDIR PKG_TMPDIR PORTSDIR RELEASEDIR SHARED_ONLY \e SUBPACKAGE WRKOBJDIR SUDO_PORT_V1 } :wsrc -permit nopass setenv { -ENV PS1=$DOAS_PS1 SSH_AUTH_SOCK } :wheel +permit setenv { -ENV PS1=$DOAS_PS1 SSH_AUTH_SOCK } :wheel permit nopass tedu as root cmd /usr/sbin/procmap .Ed .Sh SEE ALSO From 7bdae50597be8eb0920e6f5b2a61aa712f5c398d Mon Sep 17 00:00:00 2001 From: jmc Date: Mon, 27 Jun 2016 17:36:33 +0000 Subject: [PATCH 052/198] minor tweaks; ok tedu --- doas.conf.5 | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/doas.conf.5 b/doas.conf.5 index 46fa4bf..e6c115b 100644 --- a/doas.conf.5 +++ b/doas.conf.5 @@ -57,10 +57,12 @@ The default is to reset the environment, except for the variables .Ev USER and .Ev USERNAME . -.It Ic setenv { Oo Ar variable ... Oc Ic Oo Ar variable=value ... Oc Ic } +.It Ic setenv { Oo Ar variable ... Oc Oo Ar variable=value ... Oc Ic } In addition to the variables mentioned above, keep the space-separated specified variables. -Variables may also be removed with a leading - or set using the latter syntax. +Variables may also be removed with a leading +.Sq - +or set using the latter syntax. If the first character of .Ar value is a @@ -114,7 +116,7 @@ If quotes or backslashes are used in a word, it isn't considered a keyword. .El .Sh EXAMPLES -The following example permits users in group wsrc to build ports, +The following example permits users in group wsrc to build ports; wheel to execute commands as any user while keeping the environment variables .Ev PS1 @@ -122,8 +124,8 @@ and .Ev SSH_AUTH_SOCK and unsetting -.Ev ENV , -permits tedu to run procmap as root without a password, +.Ev ENV ; +permits tedu to run procmap as root without a password; and additionally permits root to run unrestricted commands as itself. .Bd -literal -offset indent # Non-exhaustive list of variables needed to From 03b7619e47e684f240da58fb8ea93edccfee6c98 Mon Sep 17 00:00:00 2001 From: Duncaen Date: Mon, 27 Jun 2016 21:45:26 +0200 Subject: [PATCH 053/198] minor tweaks --- env.c | 2 +- pam.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/env.c b/env.c index 42a0d2e..e4e1c43 100644 --- a/env.c +++ b/env.c @@ -82,7 +82,7 @@ createenv(struct rule *rule) env->count = 0; if (rule->options & KEEPENV) { - extern const char **environ; + extern char **environ; for (i = 0; environ[i] != NULL; i++) { struct envnode *node; diff --git a/pam.c b/pam.c index ec89d04..af038dd 100644 --- a/pam.c +++ b/pam.c @@ -149,7 +149,7 @@ watchsession(pid_t child) { sigset_t sigs; struct sigaction act, oldact; - int status; + int status = 1; /* block signals */ sigfillset(&sigs); From 6ed45e5ffe674eae211814a9c4cfdba38f62ebdc Mon Sep 17 00:00:00 2001 From: Duncaen Date: Thu, 30 Jun 2016 01:33:08 +0200 Subject: [PATCH 054/198] use posix correct optstring --- doas.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doas.c b/doas.c index c1b822b..05d2b93 100644 --- a/doas.c +++ b/doas.c @@ -256,7 +256,7 @@ main(int argc, char **argv) #ifdef HAVE_BSD_AUTH_H # define OPTSTRING "a:C:nsu:v" #else -# define OPTSTRING "C:nsu:v" +# define OPTSTRING "+C:nsu:v" #endif while ((ch = getopt(argc, argv, OPTSTRING)) != -1) { From 6ec218cb321404af01a1bec505f782e98c5ec7a3 Mon Sep 17 00:00:00 2001 From: semarie Date: Tue, 12 Jul 2016 12:10:42 +0000 Subject: [PATCH 055/198] add "recvfd" to doas(1) for use with skey. ok tb@ deraadt@ --- doas.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doas.c b/doas.c index 05d2b93..8691559 100644 --- a/doas.c +++ b/doas.c @@ -246,7 +246,7 @@ main(int argc, char **argv) setprogname("doas"); - if (pledge("stdio rpath getpw tty proc exec id", NULL) == -1) + if (pledge("stdio rpath getpw tty recvfd proc exec id", NULL) == -1) err(1, "pledge"); /* closefrom(STDERR_FILENO + 1); */ From 24c98415f578823a1132be03279f18189d41c241 Mon Sep 17 00:00:00 2001 From: zhuk Date: Mon, 18 Jul 2016 16:46:30 +0000 Subject: [PATCH 056/198] The string with path to shell could be taken directly from struct passwd. At some point later the data it points to is overridden by getpwuid() call, resulting in garbage. The problem could be easily demonstreated by double doas call: $ doas doas -su _sndio doas: mpty: command not found The fix is easy: just strdup() the pw_shell field value. okay tedu@, tweaks from & okay natano@ --- doas.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/doas.c b/doas.c index 8691559..fa0eafb 100644 --- a/doas.c +++ b/doas.c @@ -311,9 +311,11 @@ main(int argc, char **argv) if (sflag) { sh = getenv("SHELL"); - if (sh == NULL || *sh == '\0') - shargv[0] = pw->pw_shell; - else + if (sh == NULL || *sh == '\0') { + shargv[0] = strdup(pw->pw_shell); + if (shargv[0] == NULL) + err(1, NULL); + } else shargv[0] = sh; argv = shargv; argc = 1; From b3a6a29fd0df8d1db25bec755893935065a18472 Mon Sep 17 00:00:00 2001 From: Duncaen Date: Fri, 2 Sep 2016 20:41:37 +0200 Subject: [PATCH 057/198] configure: error out if no authentication found and fix default CC --- configure | 109 ++++++++++++++++++++++++++++++++---------------------- 1 file changed, 65 insertions(+), 44 deletions(-) diff --git a/configure b/configure index e6a842a..e33a14c 100755 --- a/configure +++ b/configure @@ -25,6 +25,7 @@ usage: configure [options] --enable-static prepare for static build --without-pam disable pam support + --without-pam disable shadow support --help, -h display this help and exit EOF @@ -48,7 +49,9 @@ for x; do --enable-debug) DEBUG=yes ;; --enable-static) BUILD_STATIC=yes ;; --with-pam) WITHOUT_PAM= ;; + --with-shadow) WITHOUT_SHADOW= ;; --without-pam) WITHOUT_PAM=yes ;; + --without-shadow) WITHOUT_SHADOW=yes ;; --help|-h) usage ;; *) die "Error: unknown option $opt" ;; esac @@ -115,28 +118,78 @@ esac printf 'CFLAGS += -static\n' >>$CONFIG_MK # Add CPPFLAGS/CFLAGS/LDFLAGS to CC for testing features -XCC="${CC:=clang} $CFLAGS $OS_CFLAGS $CPPFLAGS $LDFLAGS" +XCC="${CC:=cc} $CFLAGS $OS_CFLAGS $CPPFLAGS $LDFLAGS" # Make sure to disable --as-needed for CC tests. XCC="$XCC -Wl,--no-as-needed" check_func() { func="$1"; src="$2"; shift 2 - printf 'Checking for %-14s\t\t' "$func ..." + printf 'Checking for %-14s\t\t' "$func ..." >&2 printf '%s\n' "$src" >"_$func.c" $XCC "_$func.c" -o "_$func" 2>/dev/null ret=$? rm -f "_$func.c" "_$func" if [ $ret -eq 0 ]; then - printf 'yes.\n' + printf 'yes.\n' >&2 upperfunc="$(printf '%s\n' "$func" | tr '[[:lower:]]' '[[:upper:]]')" printf 'CFLAGS += -DHAVE_%s\n' "$upperfunc" >>$CONFIG_MK return 0 else - printf 'no.\n' + printf 'no.\n' >&2 return 1 fi } +authmethod() { + # + # Check for bsd_auth.h. + # + src=' +#include +int main(void) { + return 0; +}' + check_func "bsd_auth_h" "$src" && { + have_bsd_auth_h=1 + printf 'bsd\n' + return 0 + } + + # + # Check for pam_appl.h. + # + src=' +#include +int main(void) { + return 0; +}' + [ -z "$WITHOUT_PAM" ] && check_func "pam_appl_h" "$src" && { + printf 'SRCS += pam.c\n' >>$CONFIG_MK + printf 'LDFLAGS += -lpam\n' >>$CONFIG_MK + printf 'pam\n' + return 0 + } + + # + # Check for shadow.h. + # + src=' +#include +int main(void) { + return 0; +}' + [ -z "$WITHOUT_SHADOW" ] && check_func "shadow_h" "$src" && { + printf 'LDFLAGS += -lcrypt\n' >>$CONFIG_MK + printf 'shadow\n' + return 0 + } + + return 1 +} + +# +# Check for explicit_bzero(). +# src=' #include int main(void) { @@ -147,7 +200,6 @@ check_func "explicit_bzero" "$src" || { printf 'OPENBSD += explicit_bzero.o\n' >>$CONFIG_MK } - # # Check for strlcat(). # @@ -258,45 +310,6 @@ check_func "reallocarray" "$src" || { printf 'OPENBSD += reallocarray.o\n' >>$CONFIG_MK } -# -# Check for bsd_auth.h. -# -src=' -#include -int main(void) { - return 0; -}' -check_func "bsd_auth_h" "$src" && \ - have_bsd_auth_h=1 - -# -# Check for pam_appl.h. -# -src=' -#include -int main(void) { - return 0; -}' -check_func "pam_appl_h" "$src" && { - [ -z "$WITHOUT_PAM" -a -z "$have_bsd_auth_h" ] && { - printf 'SRCS += pam.c\n' >>$CONFIG_MK - printf 'LDFLAGS += -lpam\n' >>$CONFIG_MK - } -} - -# -# Check for shadow.h. -# -src=' -#include -int main(void) { - return 0; -}' -check_func "shadow_h" "$src" && { - [ -n "$WITHOUT_PAM" -a -z "$have_bsd_auth_h" ] && \ - printf 'LDFLAGS += -lcrypt\n' >>$CONFIG_MK -} - # # Check for execvpe(). # @@ -348,3 +361,11 @@ __attribute__((__unused__)) static void foo(void){return;} check_func "__attribute__" "$src" || { printf 'CFLAGS += -DNO_ATTRIBUTE_ON_RETURN_TYPE=1\n' >>$CONFIG_MK } + +auth=$(authmethod) +if [ $? -eq 0 ]; then + printf 'Using auth method\t\t\t%s.\n' "$auth" >&2 +else + printf 'Error auth method\t\t\n' >&2 + exit 1 +fi From 36110508c0e1ca46a4674ebf1937beeeafb5daa0 Mon Sep 17 00:00:00 2001 From: Duncaen Date: Sat, 3 Sep 2016 23:02:49 +0200 Subject: [PATCH 058/198] minor configure tweaks --- configure | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/configure b/configure index e33a14c..c5a4c39 100755 --- a/configure +++ b/configure @@ -48,8 +48,8 @@ for x; do --target) TARGET=$var ;; --enable-debug) DEBUG=yes ;; --enable-static) BUILD_STATIC=yes ;; - --with-pam) WITHOUT_PAM= ;; - --with-shadow) WITHOUT_SHADOW= ;; + --with-pam) WITHOUT_PAM=; WITHOUT_SHADOW=yes ;; + --with-shadow) WITHOUT_SHADOW=; WITHOUT_PAM=yes ;; --without-pam) WITHOUT_PAM=yes ;; --without-shadow) WITHOUT_SHADOW=yes ;; --help|-h) usage ;; From fc4df78fd381731e8125ddd7afa251bd00975e60 Mon Sep 17 00:00:00 2001 From: Philip K Date: Mon, 5 Sep 2016 18:26:24 +0200 Subject: [PATCH 059/198] Print -a flag in usage() only if HAVE_BSD_AUTH_H Closes: #11 [via git-merge-pr] --- doas.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/doas.c b/doas.c index fa0eafb..7494b07 100644 --- a/doas.c +++ b/doas.c @@ -46,8 +46,11 @@ version(void) static void __dead usage(void) { - fprintf(stderr, "usage: doas [-nsv] [-a style] [-C config] [-u user]" - " command [args]\n"); + fprintf(stderr, "usage: doas [-nsv] " +#ifdef HAVE_BSD_AUTH_H + "[-a style] " +#endif + "[-C config] [-u user] command [args]\n"); exit(1); } From 01a8fd6567f520a5e8b0d6262f05c645f02e69a1 Mon Sep 17 00:00:00 2001 From: Duncaen Date: Tue, 6 Sep 2016 02:56:34 +0200 Subject: [PATCH 060/198] Add closefrom(2) from openssh-portable --- configure | 109 +++++++++++++++++++++++++++++++++++++++++ doas.c | 2 +- libopenbsd/closefrom.c | 109 +++++++++++++++++++++++++++++++++++++++++ libopenbsd/openbsd.h | 3 ++ 4 files changed, 222 insertions(+), 1 deletion(-) create mode 100644 libopenbsd/closefrom.c diff --git a/configure b/configure index c5a4c39..44879d4 100755 --- a/configure +++ b/configure @@ -350,6 +350,115 @@ check_func "pledge" "$src" || { printf 'OPENBSD += pledge-noop.o\n' >>$CONFIG_MK } +# +# Check for closefrom(). +# +src=' +#include +int main(void) { + closefrom(0); + return 0; +}' +check_func "closefrom" "$src" || { + printf 'OPENBSD += closefrom.o\n' >>$CONFIG_MK +} + +# +# Check for sysconf(). +# +src=' +#include +int main(void) { + (void)sysconf(0); + return 0; +}' +check_func "sysconf" "$src" + +# +# Check for /proc/$PID. +# +printf 'Checking for %-14s\t\t' "/proc/\$PID ..." >&2 +if test -d /proc/$$; then + printf 'yes.\n' >&2 + printf 'CFLAGS += -DHAVE_%s\n' "PROC_PID" >>$CONFIG_MK +else + printf 'no.\n' >&2 +fi + +# +# Check for dirfd(). +# +src=' +#include +int main(void) { + (void)dirfd(0); + return 0; +}' +check_func "dirfd" "$src" + +# +# Check for fcntl.h. +# +src=' +#include +int main(void) { + return 0; +}' +check_func "fcntl_h" "$src" + +# +# Check for F_CLOSEM. +# +src=' +#include +#ifndef F_CLOSEM +#error no F_CLOSEM +#endif +int main(void) { + return 0; +}' +check_func "F_CLOSEM" "$src" + +# +# Check for dirent.h. +# +src=' +#include +int main(void) { + return 0; +}' +check_func "dirent_h" "$src" + +# +# Check for sys/ndir.h. +# +src=' +#include +int main(void) { + return 0; +}' +check_func "sys_ndir_h" "$src" + +# +# Check for sys/dir.h. +# +src=' +#include +int main(void) { + return 0; +}' +check_func "sys_dir_h" "$src" + +# +# Check for ndir.h. +# +src=' +#include +int main(void) { + return 0; +}' +check_func "ndir_h" "$src" + # # # diff --git a/doas.c b/doas.c index 7494b07..d4d87cb 100644 --- a/doas.c +++ b/doas.c @@ -252,7 +252,7 @@ main(int argc, char **argv) if (pledge("stdio rpath getpw tty recvfd proc exec id", NULL) == -1) err(1, "pledge"); - /* closefrom(STDERR_FILENO + 1); */ + closefrom(STDERR_FILENO + 1); uid = getuid(); diff --git a/libopenbsd/closefrom.c b/libopenbsd/closefrom.c new file mode 100644 index 0000000..9380b33 --- /dev/null +++ b/libopenbsd/closefrom.c @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2004-2005 Todd C. Miller + * + * 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 "includes.h" + +#ifndef HAVE_CLOSEFROM + +#include +#include +#include +#include +#ifdef HAVE_FCNTL_H +# include +#endif +#include +#include +#include +#include +#include +#ifdef HAVE_DIRENT_H +# include +# define NAMLEN(dirent) strlen((dirent)->d_name) +#else +# define dirent direct +# define NAMLEN(dirent) (dirent)->d_namlen +# ifdef HAVE_SYS_NDIR_H +# include +# endif +# ifdef HAVE_SYS_DIR_H +# include +# endif +# ifdef HAVE_NDIR_H +# include +# endif +#endif + +#ifndef OPEN_MAX +# define OPEN_MAX 256 +#endif + +#if 0 +__unused static const char rcsid[] = "$Sudo: closefrom.c,v 1.11 2006/08/17 15:26:54 millert Exp $"; +#endif /* lint */ + +/* + * Close all file descriptors greater than or equal to lowfd. + */ +#ifdef HAVE_FCNTL_CLOSEM +void +closefrom(int lowfd) +{ + (void) fcntl(lowfd, F_CLOSEM, 0); +} +#else +void +closefrom(int lowfd) +{ + long fd, maxfd; +#if defined(HAVE_DIRFD) && defined(HAVE_PROC_PID) + char fdpath[PATH_MAX], *endp; + struct dirent *dent; + DIR *dirp; + int len; + + /* Check for a /proc/$$/fd directory. */ + len = snprintf(fdpath, sizeof(fdpath), "/proc/%ld/fd", (long)getpid()); + if (len > 0 && (size_t)len <= sizeof(fdpath) && (dirp = opendir(fdpath))) { + while ((dent = readdir(dirp)) != NULL) { + fd = strtol(dent->d_name, &endp, 10); + if (dent->d_name != endp && *endp == '\0' && + fd >= 0 && fd < INT_MAX && fd >= lowfd && fd != dirfd(dirp)) + (void) close((int) fd); + } + (void) closedir(dirp); + } else +#endif + { + /* + * Fall back on sysconf() or getdtablesize(). We avoid checking + * resource limits since it is possible to open a file descriptor + * and then drop the rlimit such that it is below the open fd. + */ +#ifdef HAVE_SYSCONF + maxfd = sysconf(_SC_OPEN_MAX); +#else + maxfd = getdtablesize(); +#endif /* HAVE_SYSCONF */ + if (maxfd < 0) + maxfd = OPEN_MAX; + + for (fd = lowfd; fd < maxfd; fd++) + (void) close((int) fd); + } +} +#endif /* !HAVE_FCNTL_CLOSEM */ +#endif /* HAVE_CLOSEFROM */ diff --git a/libopenbsd/openbsd.h b/libopenbsd/openbsd.h index 0586844..d9d3c99 100644 --- a/libopenbsd/openbsd.h +++ b/libopenbsd/openbsd.h @@ -41,6 +41,9 @@ int setresuid(uid_t, uid_t, uid_t); #ifndef HAVE_PLEDGE int pledge(const char *promises, const char *paths[]); #endif /* !HAVE_PLEDGE */ +#ifndef HAVE_CLOSEFROM +void closefrom(int); +#endif /* !HAVE_CLOSEFROM */ /* err.h */ #ifndef HAVE_VERRC From 5a7014dc013d1ab90886732b578875e04c0ede72 Mon Sep 17 00:00:00 2001 From: Duncaen Date: Tue, 6 Sep 2016 02:58:42 +0200 Subject: [PATCH 061/198] bump version to v6.0 --- configure | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure b/configure index 44879d4..f82f1e5 100755 --- a/configure +++ b/configure @@ -61,7 +61,7 @@ CONFIG_MK=config.mk rm -f "$CONFIG_MK" # : ${VERSION:="$(git describe --dirty --tags --long --always)"} -: ${VERSION:="0.3.2"} +: ${VERSION:="6.0"} cat <>$CONFIG_MK PREFIX ?= ${PREFIX:="/usr"} From 33d4bf7af0c80cf1b8b66bfe33d76a46b8a7fea0 Mon Sep 17 00:00:00 2001 From: tedu Date: Thu, 1 Sep 2016 13:16:38 +0000 Subject: [PATCH 062/198] move the authentication code to a function --- doas.c | 150 +++++++++++++++++++++++++++++++-------------------------- 1 file changed, 83 insertions(+), 67 deletions(-) diff --git a/doas.c b/doas.c index d4d87cb..a63f2e7 100644 --- a/doas.c +++ b/doas.c @@ -219,6 +219,85 @@ checkconfig(const char *confpath, int argc, char **argv, } } +#ifdef HAVE_BSD_AUTH_H +static void +authuser(const char *myname, const char *login_style) +{ + char *challenge = NULL, *response, rbuf[1024], cbuf[128]; + auth_session_t *as; + + if (!(as = auth_userchallenge(myname, login_style, "auth-doas", + &challenge))) + errx(1, "Authorization failed"); + if (!challenge) { + char host[HOST_NAME_MAX + 1]; + if (gethostname(host, sizeof(host))) + snprintf(host, sizeof(host), "?"); + snprintf(cbuf, sizeof(cbuf), + "\rdoas (%.32s@%.32s) password: ", myname, host); + challenge = cbuf; + } + response = readpassphrase(challenge, rbuf, sizeof(rbuf), + RPP_REQUIRE_TTY); + if (response == NULL && errno == ENOTTY) { + syslog(LOG_AUTHPRIV | LOG_NOTICE, + "tty required for %s", myname); + errx(1, "a tty is required"); + } + if (!auth_userresponse(as, response, 0)) { + syslog(LOG_AUTHPRIV | LOG_NOTICE, + "failed auth for %s", myname); + errc(1, EPERM, NULL); + } + explicit_bzero(rbuf, sizeof(rbuf)); +} +#elif HAVE_SHADOW_H +static void +authuser(const char *myname, const char *login_style) +{ + const char *hash; + char *encrypted; + struct passwd *pw; + + (void)login_style; + (void)persist; + + if (!(pw = getpwnam(myname))) + err(1, "getpwnam"); + + hash = pw->pw_passwd; + if (hash[0] == 'x' && hash[1] == '\0') { + struct spwd *sp; + if (!(sp = getspnam(myname))) + errx(1, "Authorization failed"); + hash = sp->sp_pwdp; + } else if (hash[0] != '*') { + errx(1, "Authorization failed"); + } + + char *challenge, *response, rbuf[1024], cbuf[128], host[HOST_NAME_MAX + 1]; + if (gethostname(host, sizeof(host))) + snprintf(host, sizeof(host), "?"); + snprintf(cbuf, sizeof(cbuf), + "\rdoas (%.32s@%.32s) password: ", myname, host); + challenge = cbuf; + + response = readpassphrase(challenge, rbuf, sizeof(rbuf), RPP_REQUIRE_TTY); + if (response == NULL && errno == ENOTTY) { + syslog(LOG_AUTHPRIV | LOG_NOTICE, + "tty required for %s", myname); + errx(1, "a tty is required"); + } + if (!(encrypted = crypt(response, hash))) + errx(1, "crypt"); + if (strcmp(encrypted, hash) != 0) { + syslog(LOG_AUTHPRIV | LOG_NOTICE, "failed auth for %s", myname); + errx(1, "Authorization failed"); + } + explicit_bzero(rbuf, sizeof(rbuf)); +} +#endif /* HAVE_BSD_AUTH_H */ + int main(int argc, char **argv) { @@ -243,9 +322,7 @@ main(int argc, char **argv) char cwdpath[PATH_MAX]; const char *cwd; char **envp; -#ifdef HAVE_BSD_AUTH_H char *login_style = NULL; -#endif setprogname("doas"); @@ -349,38 +426,12 @@ main(int argc, char **argv) errc(1, EPERM, NULL); } -#ifdef HAVE_BSD_AUTH_H +#if defined(HAVE_BSD_AUTH_H) || defined(HAVE_SHADOW_H) if (!(rule->options & NOPASS)) { if (nflag) errx(1, "Authorization required"); - char *challenge = NULL, *response, rbuf[1024], cbuf[128]; - auth_session_t *as; - - if (!(as = auth_userchallenge(myname, login_style, "auth-doas", - &challenge))) - errx(1, "Authorization failed"); - if (!challenge) { - char host[HOST_NAME_MAX + 1]; - if (gethostname(host, sizeof(host))) - snprintf(host, sizeof(host), "?"); - snprintf(cbuf, sizeof(cbuf), - "\rdoas (%.32s@%.32s) password: ", myname, host); - challenge = cbuf; - } - response = readpassphrase(challenge, rbuf, sizeof(rbuf), - RPP_REQUIRE_TTY); - if (response == NULL && errno == ENOTTY) { - syslog(LOG_AUTHPRIV | LOG_NOTICE, - "tty required for %s", myname); - errx(1, "a tty is required"); - } - if (!auth_userresponse(as, response, 0)) { - syslog(LOG_AUTHPRIV | LOG_NOTICE, - "failed auth for %s", myname); - errc(1, EPERM, NULL); - } - explicit_bzero(rbuf, sizeof(rbuf)); + authuser(myname, login_style); } #elif HAVE_PAM_APPL_H pw = getpwuid(target); @@ -389,45 +440,10 @@ main(int argc, char **argv) if (!pamauth(pw->pw_name, myname, !nflag, rule->options & NOPASS)) { syslog(LOG_AUTHPRIV | LOG_NOTICE, "failed auth for %s", myname); - errc(1, EPERM, NULL); - } -#elif HAVE_SHADOW_H - const char *pass; - - if (!(rule->options & NOPASS)) { - if (nflag) - errx(1, "Authorization required"); - - pass = pw->pw_passwd; - if (pass[0] == 'x' && pass[1] == '\0') { - struct spwd *sp; - if (!(sp = getspnam(myname))) - errx(1, "Authorization failed"); - pass = sp->sp_pwdp; - } - - char *challenge, *response, rbuf[1024], cbuf[128], host[HOST_NAME_MAX + 1]; - if (gethostname(host, sizeof(host))) - snprintf(host, sizeof(host), "?"); - snprintf(cbuf, sizeof(cbuf), - "\rdoas (%.32s@%.32s) password: ", myname, host); - challenge = cbuf; - - response = readpassphrase(challenge, rbuf, sizeof(rbuf), RPP_REQUIRE_TTY); - if (response == NULL && errno == ENOTTY) { - syslog(LOG_AUTHPRIV | LOG_NOTICE, - "tty required for %s", myname); - errx(1, "a tty is required"); - } - if (strcmp(crypt(response, pass), pass) != 0) { - syslog(LOG_AUTHPRIV | LOG_NOTICE, "failed auth for %s", myname); - errc(1, EPERM, NULL); - } - explicit_bzero(rbuf, sizeof(rbuf)); + errx(1, "Authorization failed"); } #else - if (!(rule->options & NOPASS)) - errx(1, "Authorization required"); +#error "No authentication method" #endif /* HAVE_BSD_AUTH_H */ if (pledge("stdio rpath getpw exec id", NULL) == -1) From 3d4da2df4672135c3fe89bb7083f07a3a1849452 Mon Sep 17 00:00:00 2001 From: tedu Date: Thu, 1 Sep 2016 17:30:52 +0000 Subject: [PATCH 063/198] unconst these parameters; i won't be changing bsd auth today. --- doas.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doas.c b/doas.c index a63f2e7..3474a7c 100644 --- a/doas.c +++ b/doas.c @@ -221,7 +221,7 @@ checkconfig(const char *confpath, int argc, char **argv, #ifdef HAVE_BSD_AUTH_H static void -authuser(const char *myname, const char *login_style) +authuser(char *myname, char *login_style) { char *challenge = NULL, *response, rbuf[1024], cbuf[128]; auth_session_t *as; From 27235dd398ab05cf7f992efe2027efc70fa0da0c Mon Sep 17 00:00:00 2001 From: tedu Date: Fri, 2 Sep 2016 18:12:30 +0000 Subject: [PATCH 064/198] add support for the verified auth ioctls using 'persist' rules. ok deraadt henning --- doas.1 | 6 ++++-- doas.c | 37 ++++++++++++++++++++++++++++--------- doas.conf.5 | 3 +++ doas.h | 1 + parse.y | 6 +++++- 5 files changed, 41 insertions(+), 12 deletions(-) diff --git a/doas.1 b/doas.1 index dd98081..16983aa 100644 --- a/doas.1 +++ b/doas.1 @@ -21,7 +21,7 @@ .Nd execute commands as another user .Sh SYNOPSIS .Nm doas -.Op Fl ns +.Op Fl Lns .Op Fl C Ar config .Op Fl u Ar user .Ar command @@ -57,7 +57,9 @@ or .Sq deny will be printed on standard output, depending on command matching results. -In either case, no command is executed. +No command is executed. +.It Fl L +Clear any persisted authorizations from previous invocations. .It Fl n Non interactive mode, fail if .Nm diff --git a/doas.c b/doas.c index 3474a7c..f1a7c34 100644 --- a/doas.c +++ b/doas.c @@ -17,6 +17,7 @@ #include #include +#include #include #include @@ -46,7 +47,7 @@ version(void) static void __dead usage(void) { - fprintf(stderr, "usage: doas [-nsv] " + fprintf(stderr, "usage: doas [-Lnsv] " #ifdef HAVE_BSD_AUTH_H "[-a style] " #endif @@ -221,10 +222,18 @@ checkconfig(const char *confpath, int argc, char **argv, #ifdef HAVE_BSD_AUTH_H static void -authuser(char *myname, char *login_style) +authuser(char *myname, char *login_style, int persist) { char *challenge = NULL, *response, rbuf[1024], cbuf[128]; auth_session_t *as; + int fd = -1; + + if (persist) + fd = open("/dev/tty", O_RDWR); + if (fd != -1) { + if (ioctl(fd, TIOCCHKVERAUTH) == 0) + goto good; + } if (!(as = auth_userchallenge(myname, login_style, "auth-doas", &challenge))) @@ -250,10 +259,16 @@ authuser(char *myname, char *login_style) errc(1, EPERM, NULL); } explicit_bzero(rbuf, sizeof(rbuf)); +good: + if (fd != -1) { + int secs = 10 * 60; + ioctl(fd, TIOCSETVERAUTH, &secs); + close(fd); + } } #elif HAVE_SHADOW_H static void -authuser(const char *myname, const char *login_style) +authuser(const char *myname, const char *login_style, int persist) { const char *hash; char *encrypted; @@ -326,17 +341,14 @@ main(int argc, char **argv) setprogname("doas"); - if (pledge("stdio rpath getpw tty recvfd proc exec id", NULL) == -1) - err(1, "pledge"); - closefrom(STDERR_FILENO + 1); uid = getuid(); #ifdef HAVE_BSD_AUTH_H -# define OPTSTRING "a:C:nsu:v" +# define OPTSTRING "a:C:Lnsu:v" #else -# define OPTSTRING "+C:nsu:v" +# define OPTSTRING "+C:Lnsu:v" #endif while ((ch = getopt(argc, argv, OPTSTRING)) != -1) { @@ -349,6 +361,13 @@ main(int argc, char **argv) case 'C': confpath = optarg; break; + case 'L': +#ifdef TIOCCLRVERAUTH + i = open("/dev/tty", O_RDWR); + if (i != -1) + ioctl(i, TIOCCLRVERAUTH); + exit(i == -1); +#endif case 'u': if (parseuid(optarg, &target) != 0) errx(1, "unknown user"); @@ -431,7 +450,7 @@ main(int argc, char **argv) if (nflag) errx(1, "Authorization required"); - authuser(myname, login_style); + authuser(myname, login_style, rule->options & PERSIST); } #elif HAVE_PAM_APPL_H pw = getpwuid(target); diff --git a/doas.conf.5 b/doas.conf.5 index e6c115b..cfe1cf3 100644 --- a/doas.conf.5 +++ b/doas.conf.5 @@ -45,6 +45,9 @@ Options are: .Bl -tag -width keepenv .It Ic nopass The user is not required to enter a password. +.It Ic persist +After the user successfully authenticates, do not ask for a password +again for some time. .It Ic keepenv The user's environment is maintained. The default is to reset the environment, except for the variables diff --git a/doas.h b/doas.h index 2f95310..062c387 100644 --- a/doas.h +++ b/doas.h @@ -22,3 +22,4 @@ char **prepenv(struct rule *); #define NOPASS 0x1 #define KEEPENV 0x2 +#define PERSIST 0x4 diff --git a/parse.y b/parse.y index 506eb68..43732a6 100644 --- a/parse.y +++ b/parse.y @@ -60,7 +60,7 @@ int yyparse(void); %} %token TPERMIT TDENY TAS TCMD TARGS -%token TNOPASS TKEEPENV TSETENV +%token TNOPASS TPERSIST TKEEPENV TSETENV %token TSTRING %% @@ -122,6 +122,9 @@ options: /* none */ { option: TNOPASS { $$.options = NOPASS; $$.envlist = NULL; + } | TPERSIST { + $$.options = PERSIST; + $$.envlist = NULL; } | TKEEPENV { $$.options = KEEPENV; $$.envlist = NULL; @@ -208,6 +211,7 @@ struct keyword { { "cmd", TCMD }, { "args", TARGS }, { "nopass", TNOPASS }, + { "persist", TPERSIST }, { "keepenv", TKEEPENV }, { "setenv", TSETENV }, }; From f9b39a6d16b27ac064c6791dbc641df11573b6ee Mon Sep 17 00:00:00 2001 From: tedu Date: Fri, 2 Sep 2016 20:38:05 +0000 Subject: [PATCH 065/198] clarify that -L will exit without running a command. --- doas.1 | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/doas.1 b/doas.1 index 16983aa..2db822b 100644 --- a/doas.1 +++ b/doas.1 @@ -59,7 +59,9 @@ will be printed on standard output, depending on command matching results. No command is executed. .It Fl L -Clear any persisted authorizations from previous invocations. +Clear any persisted authorizations from previous invocations, +then immediately exit. +No command is executed. .It Fl n Non interactive mode, fail if .Nm From 30bd833bc0118a182eaa609c815a533ae16a4cee Mon Sep 17 00:00:00 2001 From: tedu Date: Sat, 3 Sep 2016 11:03:18 +0000 Subject: [PATCH 066/198] the sudo timeout was 5 minutes i believe, so we'll match that. --- doas.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doas.c b/doas.c index f1a7c34..7757f49 100644 --- a/doas.c +++ b/doas.c @@ -261,7 +261,7 @@ authuser(char *myname, char *login_style, int persist) explicit_bzero(rbuf, sizeof(rbuf)); good: if (fd != -1) { - int secs = 10 * 60; + int secs = 5 * 60; ioctl(fd, TIOCSETVERAUTH, &secs); close(fd); } From f5f27a8e03bf039b7d90dd605175046bcc3fe0f8 Mon Sep 17 00:00:00 2001 From: tedu Date: Sun, 4 Sep 2016 15:11:13 +0000 Subject: [PATCH 067/198] don't allow combining nopass and persist in a single rule --- parse.y | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/parse.y b/parse.y index 43732a6..8a33364 100644 --- a/parse.y +++ b/parse.y @@ -111,6 +111,10 @@ options: /* none */ { } | options option { $$.options = $1.options | $2.options; $$.envlist = $1.envlist; + if (($$.options & (NOPASS|PERSIST)) == (NOPASS|PERSIST)) { + yyerror("can't combine nopass and persist"); + YYERROR; + } if ($2.envlist) { if ($$.envlist) { yyerror("can't have two setenv sections"); From 380865b7ba94280a10cf6b9f55b289b5caab0c3e Mon Sep 17 00:00:00 2001 From: tedu Date: Sun, 4 Sep 2016 15:20:37 +0000 Subject: [PATCH 068/198] -L means no command --- doas.1 | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/doas.1 b/doas.1 index 2db822b..26d63df 100644 --- a/doas.1 +++ b/doas.1 @@ -33,7 +33,8 @@ utility executes the given command as another user. The .Ar command argument is mandatory unless -.Fl C +.Fl C , +.Fl L , or .Fl s is specified. From b7e6671371e489fb6607f9ab883ddeb03895e51d Mon Sep 17 00:00:00 2001 From: deraadt Date: Thu, 15 Sep 2016 00:58:23 +0000 Subject: [PATCH 069/198] use static in the right places to seperate modules better ok tedu --- doas.c | 1 - doas.h | 20 +++++++++++++++++++- env.c | 2 +- parse.y | 13 +++++++------ 4 files changed, 27 insertions(+), 9 deletions(-) diff --git a/doas.c b/doas.c index 7757f49..7feaafc 100644 --- a/doas.c +++ b/doas.c @@ -174,7 +174,6 @@ static void parseconfig(const char *filename, int checkperms) { extern FILE *yyfp; - extern int yyparse(void); struct stat sb; yyfp = fopen(filename, "r"); diff --git a/doas.h b/doas.h index 062c387..5de73cc 100644 --- a/doas.h +++ b/doas.h @@ -1,4 +1,20 @@ /* $OpenBSD$ */ +/* + * Copyright (c) 2015 Ted Unangst + * + * 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. + */ + struct rule { int action; int options; @@ -10,13 +26,15 @@ struct rule { }; extern struct rule **rules; -extern int nrules, maxrules; +extern int nrules; extern int parse_errors; size_t arraylen(const char **); char **prepenv(struct rule *); +int yyparse(void); + #define PERMIT 1 #define DENY 2 diff --git a/env.c b/env.c index e4e1c43..23a95f2 100644 --- a/env.c +++ b/env.c @@ -195,7 +195,7 @@ prepenv(struct rule *rule) NULL }; struct env *env; - + env = createenv(rule); /* if we started with blank, fill some defaults then apply rules */ diff --git a/parse.y b/parse.y index 8a33364..5e77679 100644 --- a/parse.y +++ b/parse.y @@ -49,13 +49,14 @@ typedef struct { FILE *yyfp; struct rule **rules; -int nrules, maxrules; +int nrules; +static int maxrules; + int parse_errors = 0; -int obsolete_warned = 0; +static int obsolete_warned = 0; -void yyerror(const char *, ...); -int yylex(void); -int yyparse(void); +static void yyerror(const char *, ...); +static int yylex(void); %} @@ -205,7 +206,7 @@ yyerror(const char *fmt, ...) parse_errors++; } -struct keyword { +static struct keyword { const char *word; int token; } keywords[] = { From be6639289c38a69b5edbb4d40d0bc3c56360379c Mon Sep 17 00:00:00 2001 From: tedu Date: Wed, 5 Oct 2016 17:36:53 +0000 Subject: [PATCH 070/198] as a result of the env rework, arraylen() is only used in parse.y. move it there and make it static. --- doas.c | 14 -------------- doas.h | 2 -- parse.y | 12 ++++++++++++ 3 files changed, 12 insertions(+), 16 deletions(-) diff --git a/doas.c b/doas.c index 7feaafc..6c1b14d 100644 --- a/doas.c +++ b/doas.c @@ -55,20 +55,6 @@ usage(void) exit(1); } -size_t -arraylen(const char **arr) -{ - size_t cnt = 0; - - if (arr) { - while (*arr) { - cnt++; - arr++; - } - } - return cnt; -} - static int parseuid(const char *s, uid_t *uid) { diff --git a/doas.h b/doas.h index 5de73cc..0f96d1e 100644 --- a/doas.h +++ b/doas.h @@ -29,8 +29,6 @@ extern struct rule **rules; extern int nrules; extern int parse_errors; -size_t arraylen(const char **); - char **prepenv(struct rule *); int yyparse(void); diff --git a/parse.y b/parse.y index 5e77679..e89fb1c 100644 --- a/parse.y +++ b/parse.y @@ -58,6 +58,18 @@ static int obsolete_warned = 0; static void yyerror(const char *, ...); static int yylex(void); +static size_t +arraylen(const char **arr) +{ + size_t cnt = 0; + + while (*arr) { + cnt++; + arr++; + } + return cnt; +} + %} %token TPERMIT TDENY TAS TCMD TARGS From a6f4fdf684c40fecb091229e82995bf66e666fdf Mon Sep 17 00:00:00 2001 From: tedu Date: Wed, 5 Oct 2016 17:40:25 +0000 Subject: [PATCH 071/198] move yyparse decl next to yyfp --- doas.c | 2 +- doas.h | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/doas.c b/doas.c index 6c1b14d..a4686ef 100644 --- a/doas.c +++ b/doas.c @@ -160,6 +160,7 @@ static void parseconfig(const char *filename, int checkperms) { extern FILE *yyfp; + extern int yyparse(void); struct stat sb; yyfp = fopen(filename, "r"); @@ -176,7 +177,6 @@ parseconfig(const char *filename, int checkperms) errx(1, "%s is not owned by root", filename); } - yyparse(); fclose(yyfp); if (parse_errors) exit(1); diff --git a/doas.h b/doas.h index 0f96d1e..a371eb6 100644 --- a/doas.h +++ b/doas.h @@ -31,8 +31,6 @@ extern int parse_errors; char **prepenv(struct rule *); -int yyparse(void); - #define PERMIT 1 #define DENY 2 From 25cd40416e98dd79c786ff01eda308fb5ef4a980 Mon Sep 17 00:00:00 2001 From: tb Date: Wed, 5 Oct 2016 23:28:28 +0000 Subject: [PATCH 072/198] Add back the call to yyparse() that was accidentally dropped in the previous commit. Fortunately, doas fails closed... ok tedu --- doas.c | 1 + 1 file changed, 1 insertion(+) diff --git a/doas.c b/doas.c index a4686ef..0a23595 100644 --- a/doas.c +++ b/doas.c @@ -177,6 +177,7 @@ parseconfig(const char *filename, int checkperms) errx(1, "%s is not owned by root", filename); } + yyparse(); fclose(yyfp); if (parse_errors) exit(1); From 293637bb93a707d5c7c1304071757586592f0278 Mon Sep 17 00:00:00 2001 From: tedu Date: Thu, 10 Nov 2016 16:00:40 +0000 Subject: [PATCH 073/198] missing semicolon at end of rule. yacc doesn't seem to mind, though. from Edakawa --- parse.y | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/parse.y b/parse.y index e89fb1c..0863d75 100644 --- a/parse.y +++ b/parse.y @@ -166,7 +166,7 @@ envlist: /* empty */ { errx(1, "can't allocate envlist"); $$.envlist[nenv] = $2.str; $$.envlist[nenv + 1] = NULL; - } + } ; ident: TSTRING { From 8150cd43ccf59dcb235036ba1f5ee387cfd42c70 Mon Sep 17 00:00:00 2001 From: schwarze Date: Mon, 5 Dec 2016 10:58:07 +0000 Subject: [PATCH 074/198] Be more explicit about the "args" syntax. In part from a patch from Anton dot Lindqvist at gmail dot com. OK tedu@ --- doas.conf.5 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doas.conf.5 b/doas.conf.5 index cfe1cf3..2c0b690 100644 --- a/doas.conf.5 +++ b/doas.conf.5 @@ -33,7 +33,7 @@ The rules have the following format: .Op Ar options .Ar identity .Op Ic as Ar target -.Op Ic cmd Ar command Op Ic args ... +.Op Ic cmd Ar command Op Ic args No ... .Ed .Pp Rules consist of the following parts: @@ -88,7 +88,7 @@ Be advised that it is best to specify absolute paths. If a relative path is specified, only a restricted .Ev PATH will be searched. -.It Ic args ... +.It Ic args Op Ar argument ... Arguments to command. The command arguments provided by the user need to match those specified. The keyword From 75ec7b4adb302d32577a555206147c9865d62374 Mon Sep 17 00:00:00 2001 From: tedu Date: Thu, 29 Dec 2016 19:12:42 +0000 Subject: [PATCH 075/198] it has been six months and two days... remove keepenv { obsolete } syntax --- parse.y | 8 -------- 1 file changed, 8 deletions(-) diff --git a/parse.y b/parse.y index 0863d75..fd161e6 100644 --- a/parse.y +++ b/parse.y @@ -53,7 +53,6 @@ int nrules; static int maxrules; int parse_errors = 0; -static int obsolete_warned = 0; static void yyerror(const char *, ...); static int yylex(void); @@ -145,13 +144,6 @@ option: TNOPASS { } | TKEEPENV { $$.options = KEEPENV; $$.envlist = NULL; - } | TKEEPENV '{' envlist '}' { - $$.options = 0; - if (!obsolete_warned) { - warnx("keepenv with list is obsolete"); - obsolete_warned = 1; - } - $$.envlist = $3.envlist; } | TSETENV '{' envlist '}' { $$.options = 0; $$.envlist = $3.envlist; From ed8c643225540f735a8a1f9db0660c3c14a2993f Mon Sep 17 00:00:00 2001 From: tedu Date: Mon, 2 Jan 2017 01:40:20 +0000 Subject: [PATCH 076/198] envlist and arglist are both string lists; simplify ok benno --- parse.y | 37 ++++++++++++++----------------------- 1 file changed, 14 insertions(+), 23 deletions(-) diff --git a/parse.y b/parse.y index fd161e6..475c0d5 100644 --- a/parse.y +++ b/parse.y @@ -39,6 +39,7 @@ typedef struct { const char **cmdargs; const char **envlist; }; + const char **strlist; const char *str; }; int lineno; @@ -144,20 +145,21 @@ option: TNOPASS { } | TKEEPENV { $$.options = KEEPENV; $$.envlist = NULL; - } | TSETENV '{' envlist '}' { + } | TSETENV '{' strlist '}' { $$.options = 0; - $$.envlist = $3.envlist; + $$.envlist = $3.strlist; } ; -envlist: /* empty */ { - $$.envlist = NULL; - } | envlist TSTRING { - int nenv = arraylen($1.envlist); - if (!($$.envlist = reallocarray($1.envlist, nenv + 2, +strlist: /* empty */ { + if (!($$.strlist = calloc(1, sizeof(char *)))) + errx(1, "can't allocate strlist"); + } | strlist TSTRING { + int nstr = arraylen($1.strlist); + if (!($$.strlist = reallocarray($1.strlist, nstr + 2, sizeof(char *)))) - errx(1, "can't allocate envlist"); - $$.envlist[nenv] = $2.str; - $$.envlist[nenv + 1] = NULL; + errx(1, "can't allocate strlist"); + $$.strlist[nstr] = $2.str; + $$.strlist[nstr + 1] = NULL; } ; @@ -181,19 +183,8 @@ cmd: /* optional */ { args: /* empty */ { $$.cmdargs = NULL; - } | TARGS argslist { - $$.cmdargs = $2.cmdargs; - } ; - -argslist: /* empty */ { - $$.cmdargs = NULL; - } | argslist TSTRING { - int nargs = arraylen($1.cmdargs); - if (!($$.cmdargs = reallocarray($1.cmdargs, nargs + 2, - sizeof(char *)))) - errx(1, "can't allocate args"); - $$.cmdargs[nargs] = $2.str; - $$.cmdargs[nargs + 1] = NULL; + } | TARGS strlist { + $$.cmdargs = $2.strlist; } ; %% From 97d12a583b38551d7cdcb301f984988eab766256 Mon Sep 17 00:00:00 2001 From: tedu Date: Sat, 14 Jan 2017 18:51:24 +0000 Subject: [PATCH 077/198] add a geteuid check to make sure we're root before plowing into setauth. spare some debugging effort in case doas is not installed setuid. --- doas.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/doas.c b/doas.c index 0a23595..d30f73d 100644 --- a/doas.c +++ b/doas.c @@ -412,6 +412,9 @@ main(int argc, char **argv) exit(1); /* fail safe */ } + if (geteuid()) + errx(1, "not installed setuid"); + parseconfig("/etc/doas.conf", 1); /* cmdline is used only for logging, no need to abort on truncate */ From 998d49046c5da55449ace95a7bb0ba4522a8c029 Mon Sep 17 00:00:00 2001 From: tedu Date: Mon, 20 Mar 2017 14:35:06 +0000 Subject: [PATCH 078/198] simplify example. list of ports variables was non-exahustive, which means what exactly? there should be a better place for such lists. --- doas.conf.5 | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/doas.conf.5 b/doas.conf.5 index 2c0b690..43d8e20 100644 --- a/doas.conf.5 +++ b/doas.conf.5 @@ -119,8 +119,9 @@ If quotes or backslashes are used in a word, it isn't considered a keyword. .El .Sh EXAMPLES -The following example permits users in group wsrc to build ports; -wheel to execute commands as any user while keeping the environment +The following example permits user aja to install packages +from a preferred mirror; +group wheel to execute commands as any user while keeping the environment variables .Ev PS1 and @@ -131,14 +132,7 @@ unsetting permits tedu to run procmap as root without a password; and additionally permits root to run unrestricted commands as itself. .Bd -literal -offset indent -# Non-exhaustive list of variables needed to -# build release(8) and ports(7) -permit nopass setenv { \e - FTPMODE PKG_CACHE PKG_PATH SM_PATH SSH_AUTH_SOCK \e - DESTDIR DISTDIR FETCH_CMD FLAVOR GROUP MAKE MAKECONF \e - MULTI_PACKAGES NOMAN OKAY_FILES OWNER PKG_DBDIR \e - PKG_DESTDIR PKG_TMPDIR PORTSDIR RELEASEDIR SHARED_ONLY \e - SUBPACKAGE WRKOBJDIR SUDO_PORT_V1 } :wsrc +permit persist setenv { PKG_CACHE PKG_PATH } aja cmd pkg_add permit setenv { -ENV PS1=$DOAS_PS1 SSH_AUTH_SOCK } :wheel permit nopass tedu as root cmd /usr/sbin/procmap .Ed From 7413704e222d69771ba3c70356695d0f15857f66 Mon Sep 17 00:00:00 2001 From: tedu Date: Thu, 6 Apr 2017 21:12:06 +0000 Subject: [PATCH 079/198] prepenv can take a const rule --- doas.h | 2 +- env.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/doas.h b/doas.h index a371eb6..48069db 100644 --- a/doas.h +++ b/doas.h @@ -29,7 +29,7 @@ extern struct rule **rules; extern int nrules; extern int parse_errors; -char **prepenv(struct rule *); +char **prepenv(const struct rule *); #define PERMIT 1 #define DENY 2 diff --git a/env.c b/env.c index 23a95f2..3e8b95d 100644 --- a/env.c +++ b/env.c @@ -70,7 +70,7 @@ freenode(struct envnode *node) } static struct env * -createenv(struct rule *rule) +createenv(const struct rule *rule) { struct env *env; u_int i; @@ -187,7 +187,7 @@ fillenv(struct env *env, const char **envlist) } char ** -prepenv(struct rule *rule) +prepenv(const struct rule *rule) { static const char *safeset[] = { "DISPLAY", "HOME", "LOGNAME", "MAIL", From f0fa08cd6ba71c84495fa93fede52a7536f047c8 Mon Sep 17 00:00:00 2001 From: tedu Date: Thu, 6 Apr 2017 21:14:12 +0000 Subject: [PATCH 080/198] a little const here and there to prevent rules from changing --- doas.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doas.c b/doas.c index d30f73d..b6a7bc1 100644 --- a/doas.c +++ b/doas.c @@ -140,7 +140,7 @@ match(uid_t uid, gid_t *groups, int ngroups, uid_t target, const char *cmd, } static int -permit(uid_t uid, gid_t *groups, int ngroups, struct rule **lastr, +permit(uid_t uid, gid_t *groups, int ngroups, const struct rule **lastr, uid_t target, const char *cmd, const char **cmdargs) { int i; @@ -187,7 +187,7 @@ static void __dead checkconfig(const char *confpath, int argc, char **argv, uid_t uid, gid_t *groups, int ngroups, uid_t target) { - struct rule *rule; + const struct rule *rule; if (setresuid(uid, uid, uid) != 0) err(1, "setresuid"); @@ -311,7 +311,7 @@ main(int argc, char **argv) char cmdline[LINE_MAX]; char myname[_PW_NAME_LEN + 1]; struct passwd *pw; - struct rule *rule; + const struct rule *rule; uid_t uid; uid_t target = 0; gid_t groups[NGROUPS_MAX + 1]; From d8c2180a36392d73b0d00de704d8811af0bf2e5c Mon Sep 17 00:00:00 2001 From: tedu Date: Sat, 27 May 2017 09:51:07 +0000 Subject: [PATCH 081/198] for password failure, print Authorization failed instead of EPERM. will make things less confusing with commands rejected by config file. --- doas.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doas.c b/doas.c index b6a7bc1..b7248f0 100644 --- a/doas.c +++ b/doas.c @@ -242,7 +242,7 @@ authuser(char *myname, char *login_style, int persist) if (!auth_userresponse(as, response, 0)) { syslog(LOG_AUTHPRIV | LOG_NOTICE, "failed auth for %s", myname); - errc(1, EPERM, NULL); + errx(1, "Authorization failed"); } explicit_bzero(rbuf, sizeof(rbuf)); good: From 6d258086f25fdba0f0d2c3e99fbf29051a01f95a Mon Sep 17 00:00:00 2001 From: espie Date: Mon, 3 Jul 2017 22:21:47 +0000 Subject: [PATCH 082/198] no need to generate y.tab.h if nothing uses it, set YFLAGS to nothing instead of CLEANFILES += y.tab.h okay millert@ --- Makefile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index fa69b8c..9cca8ca 100644 --- a/Makefile +++ b/Makefile @@ -10,6 +10,7 @@ BINGRP= root BINMODE=4111 CFLAGS+= -I${CURDIR} -COPTS+= -Wall -Wextra -Werror -pedantic -std=c11 +COPTS+= -Wall -Wextra -Werror -pedantic +YFLAGS= include bsd.prog.mk From 1aa26defa1c2021c89b1138756cf63bb099e7909 Mon Sep 17 00:00:00 2001 From: jmc Date: Thu, 13 Jul 2017 19:16:33 +0000 Subject: [PATCH 083/198] man pages with pseudo synopses which list filenames end up creating very ugly output in man -k; after some discussion with ingo, we feel the simplest fix is to remove such SYNOPSIS sections: the info is hardly helpful at page top, is contained already in FILES, and there are sufficiently few that just zapping them is simple; ok schwarze, who also helpfully ran things through a build to check output; --- doas.conf.5 | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/doas.conf.5 b/doas.conf.5 index 43d8e20..78bb16f 100644 --- a/doas.conf.5 +++ b/doas.conf.5 @@ -118,6 +118,11 @@ as a result, comments may not be extended over multiple lines. If quotes or backslashes are used in a word, it isn't considered a keyword. .El +.Sh FILES +.Bl -tag -width "/etc/doas.conf" +.It Pa /etc/doas.conf +Doas configuration file. +.El .Sh EXAMPLES The following example permits user aja to install packages from a preferred mirror; From d8236832f0a8773903eeed9c1b512ac0f682195d Mon Sep 17 00:00:00 2001 From: Duncaen Date: Mon, 11 Dec 2017 15:44:52 +0100 Subject: [PATCH 084/198] configure: fix usage --- configure | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure b/configure index f82f1e5..4b6b0d4 100755 --- a/configure +++ b/configure @@ -25,7 +25,7 @@ usage: configure [options] --enable-static prepare for static build --without-pam disable pam support - --without-pam disable shadow support + --without-shadow disable shadow support --help, -h display this help and exit EOF From 4dc52ebd1dc0b2e619ea3cdbb14b46a3f3286732 Mon Sep 17 00:00:00 2001 From: Duncaen Date: Mon, 11 Dec 2017 15:45:05 +0100 Subject: [PATCH 085/198] configure: update version --- configure | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure b/configure index 4b6b0d4..83fa3f1 100755 --- a/configure +++ b/configure @@ -61,7 +61,7 @@ CONFIG_MK=config.mk rm -f "$CONFIG_MK" # : ${VERSION:="$(git describe --dirty --tags --long --always)"} -: ${VERSION:="6.0"} +: ${VERSION:="6.2"} cat <>$CONFIG_MK PREFIX ?= ${PREFIX:="/usr"} From 1899c37ea48dd1932edd913c510411ee4c9c242f Mon Sep 17 00:00:00 2001 From: Duncaen Date: Mon, 11 Dec 2017 20:20:57 +0100 Subject: [PATCH 086/198] add initial timestamp file support, disabled by default and only with shadow auth --- configure | 24 +++++ doas.c | 19 ++++ includes.h | 6 ++ persist_timestamp.c | 217 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 266 insertions(+) create mode 100644 persist_timestamp.c diff --git a/configure b/configure index 83fa3f1..95d8a56 100755 --- 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 index b7248f0..7c682cf 100644 --- 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 index 1be024a..dd4bc63 100644 --- 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 new file mode 100644 index 0000000..68c9e9f --- /dev/null +++ b/persist_timestamp.c @@ -0,0 +1,217 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#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; +} From ec70ae5c5eb35b66b4ab0d9676de02bcfce93bf6 Mon Sep 17 00:00:00 2001 From: Duncaen Date: Tue, 12 Dec 2017 02:08:30 +0100 Subject: [PATCH 087/198] persist_timestamp: use /proc/self/stat to get tty_nr --- persist_timestamp.c | 103 ++++++++++++++++++++++++++++++++++++-------- 1 file changed, 85 insertions(+), 18 deletions(-) diff --git a/persist_timestamp.c b/persist_timestamp.c index 68c9e9f..d69e7da 100644 --- a/persist_timestamp.c +++ b/persist_timestamp.c @@ -8,10 +8,14 @@ #include #include #include +#include +#include #include #include +#include "includes.h" + #ifndef TIMESTAMP_DIR # define TIMESTAMP_DIR "/tmp/doas" #endif @@ -33,23 +37,84 @@ } \ } while (0) -static char * -tspath() + +#ifdef __linux__ +/* Use tty_nr from /proc/self/stat instead of using + * ttyname(3), stdin, stdout and stderr are user + * controllable and would allow to reuse timestamps + * from another writable terminal. + * See https://www.sudo.ws/alerts/tty_tickets.html + */ +static int +ttynr() { - 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; + char buf[1024]; + char *p, *p1, *saveptr; + const char *errstr; + int fd, n; + + p = buf; + + if ((fd = open("/proc/self/stat", O_RDONLY)) == -1) + return -1; + + while ((n = read(fd, p, buf + sizeof buf - p)) != 0) { + if (n == -1) { + if (errno == EAGAIN || errno == EINTR) + continue; + else + break; + } + p += n; + if (p >= buf + sizeof buf) + break; + } + close(fd); + /* error if it contains NULL bytes */ + if (n != 0 || memchr(buf, '\0', p - buf)) + return -1; + + /* Get the 7th field, 5 fields after the last ')', + * because the 5th field 'comm' can include spaces + * and closing paranthesis too. + * See https://www.sudo.ws/alerts/linux_tty.html + */ + if ((p = strrchr(buf, ')')) == NULL) + return -1; + for ((p1 = strtok_r(p, " ", &saveptr)), n = 0; p1; + (p1 = strtok_r(NULL, " ", &saveptr)), n++) + if (n == 5) + break; + if (p1 == NULL || n != 5) + return -1; + + n = strtonum(p1, INT_MIN, INT_MAX, &errstr); + if (errstr) + return -1; + + return n; +} +#else +#error "ttynr not implemented" +#endif + +static char pathbuf[PATH_MAX]; + +static int +tspath(const char **path) +{ + int tty; + pid_t ppid; + if (*pathbuf == '\0') { + if ((tty = ttynr()) == -1) + errx(1, "failed to get tty number"); + ppid = getppid(); + if (snprintf(pathbuf, sizeof pathbuf, "%s/.%d_%d", + TIMESTAMP_DIR, tty, ppid) == -1) + return -1; + } + *path = pathbuf; + return 0; } static int @@ -167,7 +232,8 @@ persist_open(int *valid, int secs) gid_t gid; const char *path; - path = tspath(); + if (tspath(&path) == -1) + errx(1, "failed to get timestamp path"); if (checktsdir(path)) errx(1, "checktsdir"); @@ -210,7 +276,8 @@ int persist_clear() { const char *path; - path = tspath(); + if (tspath(&path) == -1) + errx(1, "failed to get timestamp path"); if (unlink(path) == -1 && errno != ENOENT) return -1; return 0; From 7c364fd80021864ff69cf01e130eebeb734ea6f5 Mon Sep 17 00:00:00 2001 From: Duncaen Date: Tue, 12 Dec 2017 02:13:29 +0100 Subject: [PATCH 088/198] persist_timestamp: cleanup --- persist_timestamp.c | 34 ++++++++++++---------------------- 1 file changed, 12 insertions(+), 22 deletions(-) diff --git a/persist_timestamp.c b/persist_timestamp.c index d69e7da..d4ffdd3 100644 --- a/persist_timestamp.c +++ b/persist_timestamp.c @@ -160,33 +160,24 @@ checktsdir(const char *path) 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 (read(fd, &ts_mono, sizeof ts_mono) != sizeof ts_mono || + read(fd, &ts_real, sizeof ts_real) != sizeof ts_mono) + err(1, "read"); + if (!timespecisset(&ts_mono) || !timespecisset(&ts_real)) + errx(1, "timespecisset"); - 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 (clock_gettime(CLOCK_MONOTONIC, &mono) == -1 || + clock_gettime(CLOCK_REALTIME, &real) == -1) + err(1, "clock_gettime"); if (timespeccmp(&mono, &ts_mono, >) || timespeccmp(&real, &ts_real, >)) - return 1; + return -1; memset(&timeout, 0, sizeof timeout); timeout.tv_sec = secs; @@ -205,10 +196,9 @@ 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, ?)"); + if (clock_gettime(CLOCK_MONOTONIC, &mono) == -1 || + clock_gettime(CLOCK_REALTIME, &real) == -1) + err(1, "clock_gettime"); memset(&timeout, 0, sizeof timeout); timeout.tv_sec = secs; From ef627e6d9df4eac6de11de4bcf4669823a424ad4 Mon Sep 17 00:00:00 2001 From: Duncaen Date: Tue, 12 Dec 2017 02:17:09 +0100 Subject: [PATCH 089/198] persist_timestamp: don't allow og+rwx permission for timestamp directory --- persist_timestamp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/persist_timestamp.c b/persist_timestamp.c index d4ffdd3..c608dca 100644 --- a/persist_timestamp.c +++ b/persist_timestamp.c @@ -147,7 +147,7 @@ checktsdir(const char *path) 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) + if ((st.st_mode & (S_IWGRP|S_IRGRP|S_IXGRP|S_IWOTH|S_IROTH|S_IXOTH)) != 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"); From 492b5e3952ed05a8340d08c9167687b406736b76 Mon Sep 17 00:00:00 2001 From: Duncaen Date: Tue, 12 Dec 2017 03:07:52 +0100 Subject: [PATCH 090/198] persist_timestamp: use CLOCK_MONOTONIC_RAW --- persist_timestamp.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/persist_timestamp.c b/persist_timestamp.c index c608dca..c8f4bb5 100644 --- a/persist_timestamp.c +++ b/persist_timestamp.c @@ -171,7 +171,7 @@ persist_check(int fd, int secs) if (!timespecisset(&ts_mono) || !timespecisset(&ts_real)) errx(1, "timespecisset"); - if (clock_gettime(CLOCK_MONOTONIC, &mono) == -1 || + if (clock_gettime(CLOCK_MONOTONIC_RAW, &mono) == -1 || clock_gettime(CLOCK_REALTIME, &real) == -1) err(1, "clock_gettime"); @@ -196,7 +196,7 @@ persist_set(int fd, int secs) { struct timespec mono, real, ts_mono, ts_real, timeout; - if (clock_gettime(CLOCK_MONOTONIC, &mono) == -1 || + if (clock_gettime(CLOCK_MONOTONIC_RAW, &mono) == -1 || clock_gettime(CLOCK_REALTIME, &real) == -1) err(1, "clock_gettime"); From c745626cdc84ce04ae3e9839a3f34d491b4985a3 Mon Sep 17 00:00:00 2001 From: Duncaen Date: Tue, 12 Dec 2017 14:57:50 +0100 Subject: [PATCH 091/198] persist_timestamp: make tmpfs requirement optional and only available on linux --- persist_timestamp.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/persist_timestamp.c b/persist_timestamp.c index c8f4bb5..3f2ee18 100644 --- a/persist_timestamp.c +++ b/persist_timestamp.c @@ -17,10 +17,13 @@ #include "includes.h" #ifndef TIMESTAMP_DIR -# define TIMESTAMP_DIR "/tmp/doas" +# define TIMESTAMP_DIR "/tmp/doas" #endif -#ifndef TMPFS_MAGIC -# define TMPFS_MAGIC 0x01021994 + +#if defined(TIMESTAMP_TMPFS) && defined(__linux__) +# ifndef TMPFS_MAGIC +# define TMPFS_MAGIC 0x01021994 +# endif #endif #define timespecisset(tsp) ((tsp)->tv_sec || (tsp)->tv_nsec) @@ -153,8 +156,11 @@ checktsdir(const char *path) errx(1, "timestamp directory is not owned by root"); if (statfs(dir, &sf) == -1) err(1, "statfs"); + +#if defined(TIMESTAMP_TMPFS) && defined(__linux__) if (sf.f_type != TMPFS_MAGIC) errx(1, "timestamp directory not on tmpfs"); +#endif free(buf); return 0; From 619c319edfd6997dd321333c53a93329528f800d Mon Sep 17 00:00:00 2001 From: Duncaen Date: Tue, 12 Dec 2017 15:29:03 +0100 Subject: [PATCH 092/198] persist_timestamp: add session id to timestamps --- persist_timestamp.c | 49 +++++++++++++++++++++------------------------ 1 file changed, 23 insertions(+), 26 deletions(-) diff --git a/persist_timestamp.c b/persist_timestamp.c index 3f2ee18..ac16c8f 100644 --- a/persist_timestamp.c +++ b/persist_timestamp.c @@ -52,7 +52,7 @@ static int ttynr() { char buf[1024]; - char *p, *p1, *saveptr; + char *p, *saveptr; const char *errstr; int fd, n; @@ -65,14 +65,14 @@ ttynr() if (n == -1) { if (errno == EAGAIN || errno == EINTR) continue; - else - break; + break; } p += n; if (p >= buf + sizeof buf) break; } close(fd); + /* error if it contains NULL bytes */ if (n != 0 || memchr(buf, '\0', p - buf)) return -1; @@ -84,14 +84,13 @@ ttynr() */ if ((p = strrchr(buf, ')')) == NULL) return -1; - for ((p1 = strtok_r(p, " ", &saveptr)), n = 0; p1; - (p1 = strtok_r(NULL, " ", &saveptr)), n++) - if (n == 5) - break; - if (p1 == NULL || n != 5) + for ((p = strtok_r(p, " ", &saveptr)), n = 0; p && n < 5; + (p = strtok_r(NULL, " ", &saveptr)), n++) + ; + if (p == NULL || n != 5) return -1; - n = strtonum(p1, INT_MIN, INT_MAX, &errstr); + n = strtonum(p, INT_MIN, INT_MAX, &errstr); if (errstr) return -1; @@ -101,23 +100,21 @@ ttynr() #error "ttynr not implemented" #endif -static char pathbuf[PATH_MAX]; - -static int -tspath(const char **path) +static const char * +tspath() { + static char pathbuf[PATH_MAX]; int tty; - pid_t ppid; - if (*pathbuf == '\0') { - if ((tty = ttynr()) == -1) - errx(1, "failed to get tty number"); - ppid = getppid(); - if (snprintf(pathbuf, sizeof pathbuf, "%s/.%d_%d", - TIMESTAMP_DIR, tty, ppid) == -1) - return -1; - } - *path = pathbuf; - return 0; + pid_t ppid, sid; + if ((tty = ttynr()) == -1) + errx(1, "failed to get tty number"); + ppid = getppid(); + if ((sid = getsid(0)) == -1) + err(1, "getsid"); + if (snprintf(pathbuf, sizeof pathbuf, "%s/.%d_%d_%d", + TIMESTAMP_DIR, tty, ppid, sid) == -1) + return NULL; + return pathbuf; } static int @@ -228,7 +225,7 @@ persist_open(int *valid, int secs) gid_t gid; const char *path; - if (tspath(&path) == -1) + if ((path = tspath()) == NULL) errx(1, "failed to get timestamp path"); if (checktsdir(path)) errx(1, "checktsdir"); @@ -272,7 +269,7 @@ int persist_clear() { const char *path; - if (tspath(&path) == -1) + if ((path = tspath()) == NULL) errx(1, "failed to get timestamp path"); if (unlink(path) == -1 && errno != ENOENT) return -1; From 76940688071a083230e26faba613e57661c64f83 Mon Sep 17 00:00:00 2001 From: Duncaen Date: Tue, 12 Dec 2017 16:25:33 +0100 Subject: [PATCH 093/198] persist_timestamp: use open directory fd to check and work with timestamp files --- persist_timestamp.c | 144 ++++++++++++++++++++++++++------------------ 1 file changed, 85 insertions(+), 59 deletions(-) diff --git a/persist_timestamp.c b/persist_timestamp.c index ac16c8f..1ca1e14 100644 --- a/persist_timestamp.c +++ b/persist_timestamp.c @@ -101,9 +101,9 @@ ttynr() #endif static const char * -tspath() +tsname() { - static char pathbuf[PATH_MAX]; + static char buf[128]; int tty; pid_t ppid, sid; if ((tty = ttynr()) == -1) @@ -111,55 +111,85 @@ tspath() ppid = getppid(); if ((sid = getsid(0)) == -1) err(1, "getsid"); - if (snprintf(pathbuf, sizeof pathbuf, "%s/.%d_%d_%d", - TIMESTAMP_DIR, tty, ppid, sid) == -1) + if (snprintf(buf, sizeof buf, ".%d_%d_%d", tty, ppid, sid) == -1) return NULL; - return pathbuf; + return buf; } static int -checktsdir(const char *path) +checktsdir(int fd) { - char *dir, *buf; struct stat st; + + if (fstat(fd, &st) == -1) + err(1, "fstatat"); + + if ((st.st_mode & S_IFMT) != S_IFDIR) + errx(0, "timestamp directory is not a directory"); + if ((st.st_mode & (S_IWGRP|S_IRGRP|S_IXGRP|S_IWOTH|S_IROTH|S_IXOTH)) != 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 defined(TIMESTAMP_TMPFS) && defined(__linux__) struct statfs sf; - gid_t gid; + if (fstatfs(fd, &sf) == -1) + err(1, "statfs"); - if (!(buf = strdup(path))) - err(1, "strdup"); - dir = dirname(buf); + if (sf.f_type != TMPFS_MAGIC) + errx(1, "timestamp directory not on tmpfs"); +#endif -check: - if (lstat(dir, &st) == -1) { + return 0; +} + +static int +opentsdir() +{ + gid_t gid; + int fd; + +reopen: + if ((fd = open(TIMESTAMP_DIR, O_RDONLY | O_DIRECTORY)) == -1) { if (errno == ENOENT) { gid = getegid(); if (setegid(0) != 0) err(1, "setegid"); - if (mkdir(dir, (S_IRUSR|S_IWUSR|S_IXUSR)) != 0) + if (mkdir(TIMESTAMP_DIR, (S_IRUSR|S_IWUSR|S_IXUSR)) != 0) err(1, "mkdir"); if (setegid(gid) != 0) err(1, "setegid"); - goto check; + goto reopen; } else { - err(1, "stat"); + err(1, "failed to open timestamp directory: %s", TIMESTAMP_DIR); } } - if ((st.st_mode & S_IFMT) != S_IFDIR) - errx(1, "timestamp directory is not a directory"); + if (checktsdir(fd) != 0) + return -1; + + return fd; +} + +static int +checktsfile(int fd, size_t *tssize) +{ + struct stat st; + gid_t gid; + + 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 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"); + errx(1, "timestamp permissions wrong"); -#if defined(TIMESTAMP_TMPFS) && defined(__linux__) - if (sf.f_type != TMPFS_MAGIC) - errx(1, "timestamp directory not on tmpfs"); -#endif + gid = getegid(); + if (st.st_uid != 0 || st.st_gid != gid) + errx(1, "timestamp has wrong owner"); + + *tssize = st.st_size; - free(buf); return 0; } @@ -220,58 +250,54 @@ persist_set(int fd, int secs) int persist_open(int *valid, int secs) { - struct stat st; - int fd; - gid_t gid; - const char *path; + int dirfd, fd; + const char *name; - if ((path = tspath()) == NULL) - errx(1, "failed to get timestamp path"); - if (checktsdir(path)) - errx(1, "checktsdir"); + if ((name = tsname()) == NULL) + errx(1, "failed to get timestamp name"); + if ((dirfd = opentsdir()) == -1) + errx(1, "opentsdir"); - if ((fd = open(path, (O_RDWR), (S_IRUSR|S_IWUSR))) == -1) + if ((fd = openat(dirfd, name, (O_RDWR), (S_IRUSR|S_IWUSR))) == -1) if (errno != ENOENT) - err(1, "open: %s", path); + err(1, "open: %s", name); if (fd == -1) { - if ((fd = open(path, (O_RDWR|O_CREAT|O_EXCL), (S_IRUSR|S_IWUSR))) == -1) - err(1, "open: %s", path); + if ((fd = openat(dirfd, name, (O_RDWR|O_CREAT|O_EXCL), (S_IRUSR|S_IWUSR))) == -1) + err(1, "open: %s", name); *valid = 0; - return fd; + goto ret; } - 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"); + size_t tssize; + if (checktsfile(fd, &tssize) == -1) + err(1, "checktsfile"); - if (st.st_size == 0) { + if (tssize == 0) { *valid = 0; - return fd; + goto ret; } - if (st.st_size != sizeof(struct timespec) * 2) + if (tssize != sizeof(struct timespec) * 2) errx(1, "corrupt timestamp file"); *valid = persist_check(fd, secs) == 0; - +ret: + close(dirfd); return fd; } int persist_clear() { - const char *path; - if ((path = tspath()) == NULL) - errx(1, "failed to get timestamp path"); - if (unlink(path) == -1 && errno != ENOENT) + const char *name; + int dirfd; + if ((name = tsname()) == NULL) + errx(1, "failed to get timestamp name"); + if ((dirfd = opentsdir()) == -1) + errx(1, "opentsdir"); + if (unlinkat(dirfd, name, 0) == -1 && errno != ENOENT) return -1; + close(dirfd); return 0; } From 89c2c8ede00aedc3426fc89761a9e8fc7cf43572 Mon Sep 17 00:00:00 2001 From: Duncaen Date: Tue, 12 Dec 2017 16:36:35 +0100 Subject: [PATCH 094/198] persist_timestamp: persist_check was only used internally, make it static --- persist_timestamp.c | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/persist_timestamp.c b/persist_timestamp.c index 1ca1e14..19e27aa 100644 --- a/persist_timestamp.c +++ b/persist_timestamp.c @@ -180,9 +180,9 @@ checktsfile(int fd, size_t *tssize) if (fstat(fd, &st) == -1) err(1, "stat"); if ((st.st_mode & S_IFMT) != S_IFREG) - errx(1, "timestamp is not a file"); + errx(1, "timestamp is not a regular file"); if ((st.st_mode & (S_IWGRP|S_IRGRP|S_IXGRP|S_IWOTH|S_IROTH|S_IXOTH)) != 0) - errx(1, "timestamp permissions wrong"); + errx(1, "timestamp has wrong permissions"); gid = getegid(); if (st.st_uid != 0 || st.st_gid != gid) @@ -193,13 +193,13 @@ checktsfile(int fd, size_t *tssize) return 0; } -int -persist_check(int fd, int secs) +static int +validts(int fd, int secs) { struct timespec mono, real, ts_mono, ts_real, timeout; if (read(fd, &ts_mono, sizeof ts_mono) != sizeof ts_mono || - read(fd, &ts_real, sizeof ts_real) != sizeof ts_mono) + read(fd, &ts_real, sizeof ts_real) != sizeof ts_real) err(1, "read"); if (!timespecisset(&ts_mono) || !timespecisset(&ts_real)) errx(1, "timespecisset"); @@ -253,6 +253,8 @@ persist_open(int *valid, int secs) int dirfd, fd; const char *name; + *valid = 0; + if ((name = tsname()) == NULL) errx(1, "failed to get timestamp name"); if ((dirfd = opentsdir()) == -1) @@ -265,23 +267,24 @@ persist_open(int *valid, int secs) if (fd == -1) { if ((fd = openat(dirfd, name, (O_RDWR|O_CREAT|O_EXCL), (S_IRUSR|S_IWUSR))) == -1) err(1, "open: %s", name); - *valid = 0; - goto ret; } size_t tssize; if (checktsfile(fd, &tssize) == -1) err(1, "checktsfile"); - if (tssize == 0) { - *valid = 0; + /* The timestamp size is 0 if its a new file or a + * timestamp that was never set, its not valid but + * can be used to write the new timestamp. + * If the size does not match the expected size it + * is incomplete and should never be used + */ + if (tssize == 0) goto ret; - } - - if (tssize != sizeof(struct timespec) * 2) + else if (tssize != sizeof(struct timespec) * 2) errx(1, "corrupt timestamp file"); - *valid = persist_check(fd, secs) == 0; + *valid = validts(fd, secs) == 0; ret: close(dirfd); return fd; From 5054c7a52d950e0f66a088689a36ca5999987c97 Mon Sep 17 00:00:00 2001 From: Duncaen Date: Tue, 12 Dec 2017 16:38:08 +0100 Subject: [PATCH 095/198] persist_timestamp: remove goto from persist_open --- persist_timestamp.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/persist_timestamp.c b/persist_timestamp.c index 19e27aa..c30b8f3 100644 --- a/persist_timestamp.c +++ b/persist_timestamp.c @@ -279,13 +279,11 @@ persist_open(int *valid, int secs) * If the size does not match the expected size it * is incomplete and should never be used */ - if (tssize == 0) - goto ret; - else if (tssize != sizeof(struct timespec) * 2) + if (tssize == sizeof(struct timespec) * 2) + *valid = validts(fd, secs) == 0; + else if (tssize != 0) errx(1, "corrupt timestamp file"); - *valid = validts(fd, secs) == 0; -ret: close(dirfd); return fd; } From 6cf64c20542022569d468403b951065eea99b20b Mon Sep 17 00:00:00 2001 From: Duncaen Date: Tue, 12 Dec 2017 16:42:11 +0100 Subject: [PATCH 096/198] persist_timestamp: create timestamp file with O_NOFOLLOW and don't leak the name --- persist_timestamp.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/persist_timestamp.c b/persist_timestamp.c index c30b8f3..81ea273 100644 --- a/persist_timestamp.c +++ b/persist_timestamp.c @@ -262,11 +262,12 @@ persist_open(int *valid, int secs) if ((fd = openat(dirfd, name, (O_RDWR), (S_IRUSR|S_IWUSR))) == -1) if (errno != ENOENT) - err(1, "open: %s", name); + err(1, "open timestamp file"); if (fd == -1) { - if ((fd = openat(dirfd, name, (O_RDWR|O_CREAT|O_EXCL), (S_IRUSR|S_IWUSR))) == -1) - err(1, "open: %s", name); + if ((fd = openat(dirfd, name, (O_RDWR|O_CREAT|O_EXCL|O_NOFOLLOW), + (S_IRUSR|S_IWUSR))) == -1) + err(1, "open timestamp file"); } size_t tssize; From c04c64a28fc1e223ba3744c9074c60ec610b4e5c Mon Sep 17 00:00:00 2001 From: Duncaen Date: Tue, 12 Dec 2017 17:14:45 +0100 Subject: [PATCH 097/198] persist_timestamp: move timespec macros to libopenbsd --- libopenbsd/sys-time.h | 60 +++++++++++++++++++++++++++++++++++++++++++ persist_timestamp.c | 32 ++++++++--------------- 2 files changed, 70 insertions(+), 22 deletions(-) create mode 100644 libopenbsd/sys-time.h diff --git a/libopenbsd/sys-time.h b/libopenbsd/sys-time.h new file mode 100644 index 0000000..2a40dc5 --- /dev/null +++ b/libopenbsd/sys-time.h @@ -0,0 +1,60 @@ +/* $OpenBSD: time.h,v 1.37 2017/12/11 23:31:16 jca Exp $ */ +/* $NetBSD: time.h,v 1.18 1996/04/23 10:29:33 mycroft Exp $ */ + +/* + * Copyright (c) 1982, 1986, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)time.h 8.2 (Berkeley) 7/10/94 + */ + +#ifndef _SYS_TIME_H_ +#define _SYS_TIME_H_ + +/* Operations on timespecs. */ +#ifndef timespecisset +#define timespecisset(tsp) ((tsp)->tv_sec || (tsp)->tv_nsec) +#endif +#ifndef timespeccmp +#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)) +#endif +#ifndef timespecadd +#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) +#endif + +#endif /* !_SYS_TIME_H_ */ diff --git a/persist_timestamp.c b/persist_timestamp.c index 81ea273..bd32fe5 100644 --- a/persist_timestamp.c +++ b/persist_timestamp.c @@ -1,18 +1,21 @@ +#include +#include + +#if !defined(timespecisset) || \ + !defined(timespeccmp) || \ + !defined(timespecadd) +# include "sys-time.h" +#endif + #include #include #include #include -#include +#include #include -#include #include #include #include -#include -#include - -#include -#include #include "includes.h" @@ -26,21 +29,6 @@ # endif #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) - - #ifdef __linux__ /* Use tty_nr from /proc/self/stat instead of using * ttyname(3), stdin, stdout and stderr are user From 3018a665160b917f70176e0d623fdd0afb10cb3d Mon Sep 17 00:00:00 2001 From: Duncaen Date: Fri, 6 Apr 2018 18:10:26 +0200 Subject: [PATCH 098/198] persist_timestamp: add start time and document implementation details --- persist_timestamp.c | 107 ++++++++++++++++++++++++++++++++++++-------- 1 file changed, 88 insertions(+), 19 deletions(-) diff --git a/persist_timestamp.c b/persist_timestamp.c index bd32fe5..99b5378 100644 --- a/persist_timestamp.c +++ b/persist_timestamp.c @@ -1,3 +1,48 @@ +/* + * 1) Timestamp files and directories + * + * Timestamp files MUST NOT be accessible to users other than root, + * this includes the name, metadata and the content of timestamp files + * and directories. + * + * Symlinks can be used to create, manipulate or delete wrong files + * and directories. The Implementation MUST reject any symlinks for + * timestamp files or directories. + * + * To avoid race conditions the implementation MUST use the same + * file descriptor for permission checks and do read or write + * write operations after the permission checks. + * + * The timestamp files MUST be opened with openat(2) using the + * timestamp directory file descriptor. Permissions of the directory + * MUST be checked before opening the timestamp file descriptor. + * + * 2) Clock sources for timestamps + * + * Timestamp files MUST NOT rely on only one clock source, using the + * wall clock would allow to reset the clock to an earlier point in + * time to reuse a timestamp. + * + * The timestamp MUST consist of multiple clocks and MUST reject the + * timestamp if there is a change to any clock because there is no way + * to differentiate between malicious and legitimate clock changes. + * + * 3) Timestamp lifetime + * + * The implementation MUST NOT use the user controlled stdin, stdout + * and stderr file descriptors to determine the controlling terminal. + * On linux the /proc/$pid/stat file MUST be used to get the terminal + * number. + * + * There is no reliable way to determine the lifetime of a tty/pty. + * The start time of the session leader MUST be used as part of the + * timestamp to determine if the tty is still the same. + * If the start time of the session leader changed the timestamp MUST + * be rejected. + * + */ + +#include #include #include @@ -13,6 +58,7 @@ #include #include #include +#include #include #include #include @@ -37,16 +83,20 @@ * See https://www.sudo.ws/alerts/tty_tickets.html */ static int -ttynr() +proc_info(pid_t pid, int *ttynr, unsigned long long *starttime) { + char path[128]; char buf[1024]; - char *p, *saveptr; + char *p, *saveptr, *ep; const char *errstr; int fd, n; p = buf; - if ((fd = open("/proc/self/stat", O_RDONLY)) == -1) + if (snprintf(path, sizeof path, "/proc/%d/stat", pid) == -1) + return -1; + + if ((fd = open(path, O_RDONLY)) == -1) return -1; while ((n = read(fd, p, buf + sizeof buf - p)) != 0) { @@ -66,40 +116,59 @@ ttynr() return -1; /* Get the 7th field, 5 fields after the last ')', - * because the 5th field 'comm' can include spaces - * and closing paranthesis too. + * (2th field) because the 5th field 'comm' can include + * spaces and closing paranthesis too. * See https://www.sudo.ws/alerts/linux_tty.html */ if ((p = strrchr(buf, ')')) == NULL) return -1; - for ((p = strtok_r(p, " ", &saveptr)), n = 0; p && n < 5; - (p = strtok_r(NULL, " ", &saveptr)), n++) - ; - if (p == NULL || n != 5) - return -1; - n = strtonum(p, INT_MIN, INT_MAX, &errstr); - if (errstr) - return -1; + n = 2; + for ((p = strtok_r(p, " ", &saveptr)); p; + (p = strtok_r(NULL, " ", &saveptr))) { + switch (n++) { + case 7: + *ttynr = strtonum(p, INT_MIN, INT_MAX, &errstr); + if (errstr) + return -1; + break; + case 22: + errno = 0; + *starttime = strtoull(p, &ep, 10); + if (p == ep || + (errno == ERANGE && *starttime == ULLONG_MAX)) + return -1; + break; + } + if (n == 23) + break; + } - return n; + return 0; } #else -#error "ttynr not implemented" +#error "proc_info not implemented" #endif static const char * tsname() { static char buf[128]; - int tty; - pid_t ppid, sid; - if ((tty = ttynr()) == -1) + int tty, fd; + unsigned long long starttime; + pid_t ppid, sid, leader; + if ((fd = open("/dev/tty", O_RDONLY)) == -1) + err(1, "open: /dev/tty"); + if (ioctl(fd, TIOCGSID, &leader) == -1) + err(1, "ioctl: failed to get session leader"); + close(fd); + if (proc_info(leader, &tty, &starttime) == -1) errx(1, "failed to get tty number"); ppid = getppid(); if ((sid = getsid(0)) == -1) err(1, "getsid"); - if (snprintf(buf, sizeof buf, ".%d_%d_%d", tty, ppid, sid) == -1) + if (snprintf(buf, sizeof buf, ".%d_%d_%llu_%d_%d", + tty, leader, starttime, ppid, sid) == -1) return NULL; return buf; } From 4b1b24b7efeda7ca04db707a0a1e89a8868ca7d8 Mon Sep 17 00:00:00 2001 From: Duncaen Date: Fri, 6 Apr 2018 18:16:30 +0200 Subject: [PATCH 099/198] pam: check watch child pid --- pam.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/pam.c b/pam.c index af038dd..3e8fab5 100644 --- a/pam.c +++ b/pam.c @@ -191,7 +191,7 @@ watchsession(pid_t child) status = 1; close: - if (caught_signal) { + if (caught_signal && child != (pid_t)-1) { fprintf(stderr, "\nSession terminated, killing shell\n"); kill(child, SIGTERM); } @@ -199,10 +199,12 @@ watchsession(pid_t child) pamcleanup(PAM_SUCCESS); if (caught_signal) { - /* kill child */ - sleep(2); - kill(child, SIGKILL); - fprintf(stderr, " ...killed.\n"); + if (child != (pid_t)-1) { + /* kill child */ + sleep(2); + kill(child, SIGKILL); + fprintf(stderr, " ...killed.\n"); + } /* unblock cached signal and resend */ sigaction(SIGTERM, &oldact, NULL); From badba30fd041965a10fbdfaa702cc3a2568c6213 Mon Sep 17 00:00:00 2001 From: tedu Date: Wed, 7 Feb 2018 05:05:46 +0000 Subject: [PATCH 100/198] not necessarily the same name, but the indicated name --- doas.conf.5 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doas.conf.5 b/doas.conf.5 index 78bb16f..e16ab6f 100644 --- a/doas.conf.5 +++ b/doas.conf.5 @@ -71,7 +71,7 @@ If the first character of is a .Ql $ then the value to be set is taken from the existing environment -variable of the same name. +variable of the indicated name. .El .It Ar identity The username to match. From 3f3c502351b672bb8ce1a5cc72b0078b1b102ffb Mon Sep 17 00:00:00 2001 From: tedu Date: Wed, 7 Feb 2018 05:13:57 +0000 Subject: [PATCH 101/198] lowercase doas ee cummings style --- doas.conf.5 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doas.conf.5 b/doas.conf.5 index e16ab6f..95daf4c 100644 --- a/doas.conf.5 +++ b/doas.conf.5 @@ -121,7 +121,7 @@ it isn't considered a keyword. .Sh FILES .Bl -tag -width "/etc/doas.conf" .It Pa /etc/doas.conf -Doas configuration file. +doas configuration file. .El .Sh EXAMPLES The following example permits user aja to install packages From 9d63eb3e713bfe178f79a111ff389b9c7020472c Mon Sep 17 00:00:00 2001 From: Ivy Foster Date: Wed, 30 Jan 2019 13:23:40 -0600 Subject: [PATCH 102/198] doas.c: put login_style in ifdef to compile on Linux Closes: #23 [via git-merge-pr] --- doas.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doas.c b/doas.c index 7c682cf..8fa6176 100644 --- a/doas.c +++ b/doas.c @@ -340,7 +340,9 @@ main(int argc, char **argv) char cwdpath[PATH_MAX]; const char *cwd; char **envp; +#ifdef HAVE_BSD_AUTH_H char *login_style = NULL; +#endif setprogname("doas"); From dbac3f3cfda4559cdf356313575fac33208afae7 Mon Sep 17 00:00:00 2001 From: gsoares Date: Wed, 30 Jan 2019 20:49:19 +0100 Subject: [PATCH 103/198] adjust yyerror() to precede with "progname: " the error message string OK tedu@ phessler@ --- parse.y | 1 + 1 file changed, 1 insertion(+) diff --git a/parse.y b/parse.y index 475c0d5..c906d9f 100644 --- a/parse.y +++ b/parse.y @@ -194,6 +194,7 @@ yyerror(const char *fmt, ...) { va_list va; + fprintf(stderr, "doas: "); va_start(va, fmt); vfprintf(stderr, fmt, va); va_end(va); From 612315084e72327418904d204b709a1db543ece8 Mon Sep 17 00:00:00 2001 From: Duncaen Date: Wed, 30 Jan 2019 20:59:40 +0100 Subject: [PATCH 104/198] libopenbsd/readpassphrase: update to latest version from openssh-portable --- libopenbsd/readpassphrase.c | 105 ++++++++++++++++++------------------ 1 file changed, 51 insertions(+), 54 deletions(-) diff --git a/libopenbsd/readpassphrase.c b/libopenbsd/readpassphrase.c index d63cdf2..6862a5e 100644 --- a/libopenbsd/readpassphrase.c +++ b/libopenbsd/readpassphrase.c @@ -1,7 +1,8 @@ -/* $OpenBSD: readpassphrase.c,v 1.22 2010/01/13 10:20:54 dtucker Exp $ */ +/* $OpenBSD: readpassphrase.c,v 1.26 2016/10/18 12:47:18 millert Exp $ */ /* - * Copyright (c) 2000-2002, 2007 Todd C. Miller + * Copyright (c) 2000-2002, 2007, 2010 + * Todd C. Miller * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -35,10 +36,9 @@ #include #include -#ifdef TCSASOFT -# define _T_FLUSH (TCSAFLUSH|TCSASOFT) -#else -# define _T_FLUSH (TCSAFLUSH) +#ifndef TCSASOFT +/* If we don't have TCSASOFT define it so that ORing it it below is a no-op. */ +# define TCSASOFT 0 #endif /* SunOS 4.x which lacks _POSIX_VDISABLE, but has VDISABLE */ @@ -46,14 +46,6 @@ # define _POSIX_VDISABLE VDISABLE #endif -#ifndef _NSIG -# ifdef NSIG -# define _NSIG NSIG -# else -# define _NSIG 128 -# endif -#endif - static volatile sig_atomic_t signo[_NSIG]; static void handler(int); @@ -94,6 +86,27 @@ readpassphrase(const char *prompt, char *buf, size_t bufsiz, int flags) output = STDERR_FILENO; } + /* + * Turn off echo if possible. + * If we are using a tty but are not the foreground pgrp this will + * generate SIGTTOU, so do it *before* installing the signal handlers. + */ + if (input != STDIN_FILENO && tcgetattr(input, &oterm) == 0) { + memcpy(&term, &oterm, sizeof(term)); + if (!(flags & RPP_ECHO_ON)) + term.c_lflag &= ~(ECHO | ECHONL); +#ifdef VSTATUS + if (term.c_cc[VSTATUS] != _POSIX_VDISABLE) + term.c_cc[VSTATUS] = _POSIX_VDISABLE; +#endif + (void)tcsetattr(input, TCSAFLUSH|TCSASOFT, &term); + } else { + memset(&term, 0, sizeof(term)); + term.c_lflag |= ECHO; + memset(&oterm, 0, sizeof(oterm)); + oterm.c_lflag |= ECHO; + } + /* * Catch signals that would otherwise cause the user to end * up with echo turned off in the shell. Don't worry about @@ -112,53 +125,37 @@ readpassphrase(const char *prompt, char *buf, size_t bufsiz, int flags) (void)sigaction(SIGTTIN, &sa, &savettin); (void)sigaction(SIGTTOU, &sa, &savettou); - /* Turn off echo if possible. */ - if (input != STDIN_FILENO && tcgetattr(input, &oterm) == 0) { - memcpy(&term, &oterm, sizeof(term)); - if (!(flags & RPP_ECHO_ON)) - term.c_lflag &= ~(ECHO | ECHONL); -#ifdef VSTATUS - if (term.c_cc[VSTATUS] != _POSIX_VDISABLE) - term.c_cc[VSTATUS] = _POSIX_VDISABLE; -#endif - (void)tcsetattr(input, _T_FLUSH, &term); - } else { - memset(&term, 0, sizeof(term)); - term.c_lflag |= ECHO; - memset(&oterm, 0, sizeof(oterm)); - oterm.c_lflag |= ECHO; - } - - /* No I/O if we are already backgrounded. */ - if (signo[SIGTTOU] != 1 && signo[SIGTTIN] != 1) { - if (!(flags & RPP_STDIN)) - (void)write(output, prompt, strlen(prompt)); - end = buf + bufsiz - 1; - p = buf; - while ((nr = read(input, &ch, 1)) == 1 && ch != '\n' && ch != '\r') { - if (p < end) { - if ((flags & RPP_SEVENBIT)) - ch &= 0x7f; - if (isalpha(ch)) { - if ((flags & RPP_FORCELOWER)) - ch = (char)tolower(ch); - if ((flags & RPP_FORCEUPPER)) - ch = (char)toupper(ch); - } - *p++ = ch; + if (!(flags & RPP_STDIN)) + (void)write(output, prompt, strlen(prompt)); + end = buf + bufsiz - 1; + p = buf; + while ((nr = read(input, &ch, 1)) == 1 && ch != '\n' && ch != '\r') { + if (p < end) { + if ((flags & RPP_SEVENBIT)) + ch &= 0x7f; + if (isalpha((unsigned char)ch)) { + if ((flags & RPP_FORCELOWER)) + ch = (char)tolower((unsigned char)ch); + if ((flags & RPP_FORCEUPPER)) + ch = (char)toupper((unsigned char)ch); } + *p++ = ch; } - *p = '\0'; - save_errno = errno; - if (!(term.c_lflag & ECHO)) - (void)write(output, "\n", 1); } + *p = '\0'; + save_errno = errno; + if (!(term.c_lflag & ECHO)) + (void)write(output, "\n", 1); /* Restore old terminal settings and signals. */ if (memcmp(&term, &oterm, sizeof(term)) != 0) { - while (tcsetattr(input, _T_FLUSH, &oterm) == -1 && - errno == EINTR) + const int sigttou = signo[SIGTTOU]; + + /* Ignore SIGTTOU generated when we are not the fg pgrp. */ + while (tcsetattr(input, TCSAFLUSH|TCSASOFT, &oterm) == -1 && + errno == EINTR && !signo[SIGTTOU]) continue; + signo[SIGTTOU] = sigttou; } (void)sigaction(SIGALRM, &savealrm, NULL); (void)sigaction(SIGHUP, &savehup, NULL); From 365f92247952a8be6d8e40b6770589dbe169825c Mon Sep 17 00:00:00 2001 From: Duncaen Date: Wed, 30 Jan 2019 21:06:15 +0100 Subject: [PATCH 105/198] libopenbsd/closefrom: correctly handle snprintf truncation --- libopenbsd/closefrom.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libopenbsd/closefrom.c b/libopenbsd/closefrom.c index 9380b33..b56476a 100644 --- a/libopenbsd/closefrom.c +++ b/libopenbsd/closefrom.c @@ -77,7 +77,7 @@ closefrom(int lowfd) /* Check for a /proc/$$/fd directory. */ len = snprintf(fdpath, sizeof(fdpath), "/proc/%ld/fd", (long)getpid()); - if (len > 0 && (size_t)len <= sizeof(fdpath) && (dirp = opendir(fdpath))) { + if (len > 0 && (size_t)len < sizeof(fdpath) && (dirp = opendir(fdpath))) { while ((dent = readdir(dirp)) != NULL) { fd = strtol(dent->d_name, &endp, 10); if (dent->d_name != endp && *endp == '\0' && From 77e474fb8dbb860668900984f44377492ce007e8 Mon Sep 17 00:00:00 2001 From: Duncaen Date: Wed, 30 Jan 2019 21:19:37 +0100 Subject: [PATCH 106/198] doas: remove v flag, not neccessary, upstream doesn't have it and __DATE__ is bad for reproducible builds --- doas.c | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/doas.c b/doas.c index 8fa6176..cee1aec 100644 --- a/doas.c +++ b/doas.c @@ -37,17 +37,10 @@ #include "doas.h" -static void __dead -version(void) -{ - fprintf(stderr, "doas: version %s built %s\n", VERSION, __DATE__); - exit(1); -} - static void __dead usage(void) { - fprintf(stderr, "usage: doas [-Lnsv] " + fprintf(stderr, "usage: doas [-Lns] " #ifdef HAVE_BSD_AUTH_H "[-a style] " #endif @@ -336,7 +329,6 @@ main(int argc, char **argv) int i, ch; int sflag = 0; int nflag = 0; - int vflag = 0; char cwdpath[PATH_MAX]; const char *cwd; char **envp; @@ -385,9 +377,6 @@ main(int argc, char **argv) case 's': sflag = 1; break; - case 'v': - vflag = 1; - break; default: usage(); break; @@ -396,9 +385,6 @@ main(int argc, char **argv) argv += optind; argc -= optind; - if (vflag) - version(); - if (confpath) { if (sflag) usage(); From 3df794793ea3db2a6a8abfeb46803b9c5b80502a Mon Sep 17 00:00:00 2001 From: Duncaen Date: Wed, 30 Jan 2019 22:28:22 +0100 Subject: [PATCH 107/198] doas: remove unnecessary configure checks, move shadow to its own file --- configure | 30 ++---------- doas.c | 132 +++++++++++++++++++---------------------------------- includes.h | 9 ++-- pam.c | 15 +++--- shadow.c | 83 +++++++++++++++++++++++++++++++++ 5 files changed, 146 insertions(+), 123 deletions(-) create mode 100644 shadow.c diff --git a/configure b/configure index 95d8a56..1266444 100755 --- a/configure +++ b/configure @@ -148,20 +148,6 @@ check_func() { } authmethod() { - # - # Check for bsd_auth.h. - # - src=' -#include -int main(void) { - return 0; -}' - check_func "bsd_auth_h" "$src" && { - have_bsd_auth_h=1 - printf 'bsd\n' - return 0 - } - # # Check for pam_appl.h. # @@ -173,6 +159,7 @@ int main(void) { [ -z "$WITHOUT_PAM" ] && check_func "pam_appl_h" "$src" && { printf 'SRCS += pam.c\n' >>$CONFIG_MK printf 'LDFLAGS += -lpam\n' >>$CONFIG_MK + printf 'CPPFLAGS += -DUSE_PAM\n' >>$CONFIG_MK printf 'pam\n' return 0 } @@ -186,7 +173,9 @@ int main(void) { return 0; }' [ -z "$WITHOUT_SHADOW" ] && check_func "shadow_h" "$src" && { + printf 'SRCS += shadow.c\n' >>$CONFIG_MK printf 'LDFLAGS += -lcrypt\n' >>$CONFIG_MK + printf 'CPPFLAGS += -DUSE_SHADOW\n' >>$CONFIG_MK printf 'shadow\n' return 0 } @@ -354,19 +343,6 @@ check_func "setresuid" "$src" || { printf 'OPENBSD += setresuid.o\n' >>$CONFIG_MK } -# -# Check for pledge(). -# -src=' -#include -int main(void) { - pledge("", NULL); - return 0; -}' -check_func "pledge" "$src" || { - printf 'OPENBSD += pledge-noop.o\n' >>$CONFIG_MK -} - # # Check for closefrom(). # diff --git a/doas.c b/doas.c index cee1aec..2bdaac2 100644 --- a/doas.c +++ b/doas.c @@ -20,6 +20,10 @@ #include #include +#if __OpenBSD__ +# include +# include +#endif #include #include #include @@ -29,9 +33,6 @@ #include #include #include -#if HAVE_SHADOW_H -#include -#endif #include "includes.h" @@ -41,7 +42,7 @@ static void __dead usage(void) { fprintf(stderr, "usage: doas [-Lns] " -#ifdef HAVE_BSD_AUTH_H +#ifdef __OpenBSD__ "[-a style] " #endif "[-C config] [-u user] command [args]\n"); @@ -199,7 +200,7 @@ checkconfig(const char *confpath, int argc, char **argv, } } -#ifdef HAVE_BSD_AUTH_H +#ifdef USE_BSD_AUTH static void authuser(char *myname, char *login_style, int persist) { @@ -245,69 +246,7 @@ authuser(char *myname, char *login_style, int persist) close(fd); } } -#elif HAVE_SHADOW_H -static void -authuser(const char *myname, const char *login_style, int persist) -{ - const char *hash; - char *encrypted; - 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"); - - hash = pw->pw_passwd; - if (hash[0] == 'x' && hash[1] == '\0') { - struct spwd *sp; - if (!(sp = getspnam(myname))) - errx(1, "Authorization failed"); - hash = sp->sp_pwdp; - } else if (hash[0] != '*') { - errx(1, "Authorization failed"); - } - - char *challenge, *response, rbuf[1024], cbuf[128], host[HOST_NAME_MAX + 1]; - if (gethostname(host, sizeof(host))) - snprintf(host, sizeof(host), "?"); - snprintf(cbuf, sizeof(cbuf), - "\rdoas (%.32s@%.32s) password: ", myname, host); - challenge = cbuf; - - response = readpassphrase(challenge, rbuf, sizeof(rbuf), RPP_REQUIRE_TTY); - if (response == NULL && errno == ENOTTY) { - syslog(LOG_AUTHPRIV | LOG_NOTICE, - "tty required for %s", myname); - errx(1, "a tty is required"); - } - if (!(encrypted = crypt(response, hash))) - errx(1, "crypt"); - if (strcmp(encrypted, hash) != 0) { - syslog(LOG_AUTHPRIV | LOG_NOTICE, "failed auth for %s", myname); - 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 */ int main(int argc, char **argv) @@ -332,7 +271,7 @@ main(int argc, char **argv) char cwdpath[PATH_MAX]; const char *cwd; char **envp; -#ifdef HAVE_BSD_AUTH_H +#ifdef USE_BSD_AUTH char *login_style = NULL; #endif @@ -342,15 +281,15 @@ main(int argc, char **argv) uid = getuid(); -#ifdef HAVE_BSD_AUTH_H -# define OPTSTRING "a:C:Lnsu:v" +#ifdef USE_BSD_AUTH +# define OPTSTRING "a:C:Lnsu:" #else -# define OPTSTRING "+C:Lnsu:v" +# define OPTSTRING "+C:Lnsu:" #endif while ((ch = getopt(argc, argv, OPTSTRING)) != -1) { switch (ch) { -#ifdef HAVE_BSD_AUTH_H +#ifdef USE_BSD_AUTH case 'a': login_style = optarg; break; @@ -441,57 +380,78 @@ main(int argc, char **argv) errc(1, EPERM, NULL); } -#if defined(HAVE_BSD_AUTH_H) || defined(HAVE_SHADOW_H) +#if defined(__OpenBSD__) if (!(rule->options & NOPASS)) { if (nflag) errx(1, "Authorization required"); authuser(myname, login_style, rule->options & PERSIST); } -#elif HAVE_PAM_APPL_H + + if (pledge("stdio rpath getpw exec id", NULL) == -1) + err(1, "pledge"); + pw = getpwuid(target); if (!pw) errx(1, "no passwd entry for target"); - if (!pamauth(pw->pw_name, myname, !nflag, rule->options & NOPASS)) { - syslog(LOG_AUTHPRIV | LOG_NOTICE, "failed auth for %s", myname); - errx(1, "Authorization failed"); +#elif defined(USE_SHADOW) + if (!(rule->options & NOPASS)) { + if (nflag) + errx(1, "Authorization required"); + + shadowauth(myname, rule->options & PERSIST); } -#else -#error "No authentication method" -#endif /* HAVE_BSD_AUTH_H */ - if (pledge("stdio rpath getpw exec id", NULL) == -1) - err(1, "pledge"); + pw = getpwuid(target); + if (!pw) + errx(1, "no passwd entry for target"); +#elif defined(USE_PAM) pw = getpwuid(target); if (!pw) errx(1, "no passwd entry for target"); -#ifdef HAVE_BSD_AUTH_H + if (!pamauth(pw->pw_name, myname, !nflag, rule->options & NOPASS)) { + syslog(LOG_AUTHPRIV | LOG_NOTICE, "failed auth for %s", myname); + errx(1, "Authorization failed"); + } + +#else + (void) nflag; + if (!(rule->options & NOPASS)) { + errx(1, "Authorization required"); + } +#endif + +#ifdef HAVE_SETUSERCONTEXT if (setusercontext(NULL, pw, target, LOGIN_SETGROUP | LOGIN_SETPRIORITY | LOGIN_SETRESOURCES | LOGIN_SETUMASK | LOGIN_SETUSER) != 0) errx(1, "failed to set user context for target"); #else if (setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) != 0) - errx(1, "setresgid"); + err(1, "setresgid"); if (initgroups(pw->pw_name, pw->pw_gid) != 0) - errx(1, "initgroups"); + err(1, "initgroups"); if (setresuid(target, target, target) != 0) - errx(1, "setresuid"); + err(1, "setresuid"); #endif +#ifdef __OpenBSD__ if (pledge("stdio rpath exec", NULL) == -1) err(1, "pledge"); +#endif if (getcwd(cwdpath, sizeof(cwdpath)) == NULL) cwd = "(failed)"; else cwd = cwdpath; +#ifdef __OpenBSD__ if (pledge("stdio exec", NULL) == -1) err(1, "pledge"); +#endif syslog(LOG_AUTHPRIV | LOG_INFO, "%s ran command %s as %s from %s", myname, cmdline, pw->pw_name, cwd); diff --git a/includes.h b/includes.h index dd4bc63..7083674 100644 --- a/includes.h +++ b/includes.h @@ -13,16 +13,17 @@ # define _PATH_TTY "/dev/tty" #endif -#ifdef HAVE_READPASSPHRASE_H -# include -#endif #include "openbsd.h" -#ifdef HAVE_PAM_APPL_H +#ifdef USE_PAM int pamauth(const char *, const char *, int, int); #endif +#ifdef USE_SHADOW +void shadowauth(const char *, int); +#endif + #ifdef PERSIST_TIMESTAMP int persist_open(int *, int); int persist_set(int, int); diff --git a/pam.c b/pam.c index 3e8fab5..6be44b7 100644 --- a/pam.c +++ b/pam.c @@ -15,22 +15,25 @@ */ #include + #include #include +#include #include +#ifdef HAVE_READPASSPHRASE_H +# include +#else +# include "readpassphrase.h" +#endif +#include #include #include #include -#include #include -#include -#ifdef __linux__ -#include -#endif +#include #include -#include "doas.h" #include "includes.h" #define PAM_SERVICE_NAME "doas" diff --git a/shadow.c b/shadow.c new file mode 100644 index 0000000..a775b2b --- /dev/null +++ b/shadow.c @@ -0,0 +1,83 @@ +#if HAVE_CRYPT_H +# include +#endif +#include +#include +#include +#include +#ifdef HAVE_READPASSPHRASE_H +# include +#else +# include "readpassphrase.h" +#endif +#include +#include +#include +#include +#include + +#include "openbsd.h" + +void +shadowauth(const char *myname, int persist) +{ + const char *hash; + char *encrypted; + struct passwd *pw; + char *challenge, *response, rbuf[1024], cbuf[128]; + +#ifdef USE_TIMESTAMP + int fd = -1; + int valid = 0; + + if (persist) + fd = timestamp_open(&valid, 5 * 60); + if (fd != -1 && valid == 1) + goto good; +#else + (void) persist; +#endif + + if ((pw = getpwnam(myname)) == NULL) + err(1, "getpwnam"); + + hash = pw->pw_passwd; + if (hash[0] == 'x' && hash[1] == '\0') { + struct spwd *sp; + if ((sp = getspnam(myname)) == NULL) + errx(1, "Authorization failed"); + hash = sp->sp_pwdp; + } else if (hash[0] != '*') { + errx(1, "Authorization failed"); + } + + char host[HOST_NAME_MAX + 1]; + if (gethostname(host, sizeof(host))) + snprintf(host, sizeof(host), "?"); + snprintf(cbuf, sizeof(cbuf), + "\rdoas (%.32s@%.32s) password: ", myname, host); + challenge = cbuf; + + response = readpassphrase(challenge, rbuf, sizeof(rbuf), RPP_REQUIRE_TTY); + if (response == NULL && errno == ENOTTY) { + syslog(LOG_AUTHPRIV | LOG_NOTICE, + "tty required for %s", myname); + errx(1, "a tty is required"); + } + if ((encrypted = crypt(response, hash)) == NULL) + err(1, "crypt"); + if (strcmp(encrypted, hash) != 0) { + syslog(LOG_AUTHPRIV | LOG_NOTICE, "failed auth for %s", myname); + errx(1, "Authorization failed"); + } + + explicit_bzero(rbuf, sizeof(rbuf)); + +#ifdef USE_TIMESTAMP +good: + if (fd != -1) { + timestamp_set(fd, 5 * 60); + close(fd); + } +#endif +} From bf8b7becf2167e13a5b43d8f61b7e16913de2ead Mon Sep 17 00:00:00 2001 From: Duncaen Date: Wed, 30 Jan 2019 22:29:11 +0100 Subject: [PATCH 108/198] libopenbsd: minor cleanup --- libopenbsd/closefrom.c | 2 +- libopenbsd/errc.c | 4 ++-- libopenbsd/execvpe.c | 6 +++--- libopenbsd/openbsd.h | 2 -- libopenbsd/pledge-noop.c | 7 ------- libopenbsd/verrc.c | 4 ++-- 6 files changed, 8 insertions(+), 17 deletions(-) delete mode 100644 libopenbsd/pledge-noop.c diff --git a/libopenbsd/closefrom.c b/libopenbsd/closefrom.c index b56476a..ef7d611 100644 --- a/libopenbsd/closefrom.c +++ b/libopenbsd/closefrom.c @@ -69,7 +69,7 @@ void closefrom(int lowfd) { long fd, maxfd; -#if defined(HAVE_DIRFD) && defined(HAVE_PROC_PID) +#if defined(HAVE_PROC_PID) char fdpath[PATH_MAX], *endp; struct dirent *dent; DIR *dirp; diff --git a/libopenbsd/errc.c b/libopenbsd/errc.c index 8e8474b..37eff0f 100644 --- a/libopenbsd/errc.c +++ b/libopenbsd/errc.c @@ -28,11 +28,11 @@ * SUCH DAMAGE. */ +#include "includes.h" + #include #include -#include "openbsd.h" - __dead void errc(int eval, int code, const char *fmt, ...) { diff --git a/libopenbsd/execvpe.c b/libopenbsd/execvpe.c index 4ddad3e..c3238ea 100644 --- a/libopenbsd/execvpe.c +++ b/libopenbsd/execvpe.c @@ -1,4 +1,4 @@ -/* $OpenBSD: exec.c,v 1.20 2013/01/08 02:26:09 deraadt Exp $ */ +/* $OpenBSD: exec.c,v 1.23 2016/03/13 18:34:20 guenther Exp $ */ /*- * Copyright (c) 1991, 1993 * The Regents of the University of California. All rights reserved. @@ -28,6 +28,8 @@ * SUCH DAMAGE. */ +#include "includes.h" + #include #include @@ -40,8 +42,6 @@ #include #include -#include "includes.h" - int execvpe(const char *name, char *const *argv, char *const *envp) { diff --git a/libopenbsd/openbsd.h b/libopenbsd/openbsd.h index d9d3c99..793d9bf 100644 --- a/libopenbsd/openbsd.h +++ b/libopenbsd/openbsd.h @@ -4,8 +4,6 @@ #include #include -#include "readpassphrase.h" - /* API definitions lifted from OpenBSD src/include */ /* pwd.h */ diff --git a/libopenbsd/pledge-noop.c b/libopenbsd/pledge-noop.c deleted file mode 100644 index 0a1b610..0000000 --- a/libopenbsd/pledge-noop.c +++ /dev/null @@ -1,7 +0,0 @@ -#include "openbsd.h" - -int -pledge(__UNUSED const char *promises, __UNUSED const char *paths[]) -{ - return 0; -} diff --git a/libopenbsd/verrc.c b/libopenbsd/verrc.c index e00fcd1..15400bc 100644 --- a/libopenbsd/verrc.c +++ b/libopenbsd/verrc.c @@ -28,14 +28,14 @@ * SUCH DAMAGE. */ +#include "includes.h" + #include #include #include #include #include -#include "openbsd.h" - __dead void verrc(int eval, int code, const char *fmt, va_list ap) { From 331dda05d1d06fcb12763be9c34c82b5e280b3bc Mon Sep 17 00:00:00 2001 From: Duncaen Date: Wed, 30 Jan 2019 22:31:47 +0100 Subject: [PATCH 109/198] timestamp: rename and simplify --- configure | 4 +- doas.c | 8 +- includes.h | 8 +- persist_timestamp.c => timestamp.c | 199 +++++++++++++++++------------ 4 files changed, 129 insertions(+), 90 deletions(-) rename persist_timestamp.c => timestamp.c (77%) diff --git a/configure b/configure index 1266444..1f3e2cb 100755 --- a/configure +++ b/configure @@ -185,8 +185,8 @@ int main(void) { persistmethod() { [ -z "$WITHOUT_TIMESTAMP" ] && { - printf 'CFLAGS += -DPERSIST_TIMESTAMP\n' >>$CONFIG_MK - printf 'SRCS += persist_timestamp.c\n' >>$CONFIG_MK + printf 'CPPFLAGS += -DUSE_TIMESTAMP\n' >>$CONFIG_MK + printf 'SRCS += timestamp.c\n' >>$CONFIG_MK printf 'timestamp\n' return 0 } diff --git a/doas.c b/doas.c index 2bdaac2..0782f87 100644 --- a/doas.c +++ b/doas.c @@ -298,13 +298,15 @@ main(int argc, char **argv) confpath = optarg; break; case 'L': -#ifdef TIOCCLRVERAUTH +#if defined(USE_BSD_AUTH) i = open("/dev/tty", O_RDWR); if (i != -1) ioctl(i, TIOCCLRVERAUTH); exit(i == -1); -#elif PERSIST_TIMESTAMP - exit(persist_clear() != 0); +#elif defined(USE_TIMESTAMP) + exit(timestamp_clear() == -1); +#else + exit(0); #endif case 'u': if (parseuid(optarg, &target) != 0) diff --git a/includes.h b/includes.h index 7083674..d3d4e39 100644 --- a/includes.h +++ b/includes.h @@ -24,10 +24,10 @@ int pamauth(const char *, const char *, int, int); void shadowauth(const char *, int); #endif -#ifdef PERSIST_TIMESTAMP -int persist_open(int *, int); -int persist_set(int, int); -int persist_clear(); +#ifdef USE_TIMESTAMP +int timestamp_open(int *, int); +int timestamp_set(int, int); +int timestamp_clear(void); #endif #endif /* INCLUDES_H */ diff --git a/persist_timestamp.c b/timestamp.c similarity index 77% rename from persist_timestamp.c rename to timestamp.c index 99b5378..0d8f9c3 100644 --- a/persist_timestamp.c +++ b/timestamp.c @@ -53,6 +53,7 @@ #endif #include +#include #include #include #include @@ -66,7 +67,7 @@ #include "includes.h" #ifndef TIMESTAMP_DIR -# define TIMESTAMP_DIR "/tmp/doas" +# define TIMESTAMP_DIR "/run/doas" #endif #if defined(TIMESTAMP_TMPFS) && defined(__linux__) @@ -103,7 +104,8 @@ proc_info(pid_t pid, int *ttynr, unsigned long long *starttime) if (n == -1) { if (errno == EAGAIN || errno == EINTR) continue; - break; + close(fd); + return -1; } p += n; if (p >= buf + sizeof buf) @@ -150,108 +152,107 @@ proc_info(pid_t pid, int *ttynr, unsigned long long *starttime) #error "proc_info not implemented" #endif +/* The session prefix is the tty number, the pid + * of the session leader and the start time of the + * session leader. + */ static const char * -tsname() +session_prefix() { - static char buf[128]; + static char prefix[128]; int tty, fd; unsigned long long starttime; - pid_t ppid, sid, leader; + pid_t leader; + if ((fd = open("/dev/tty", O_RDONLY)) == -1) err(1, "open: /dev/tty"); if (ioctl(fd, TIOCGSID, &leader) == -1) err(1, "ioctl: failed to get session leader"); close(fd); + if (proc_info(leader, &tty, &starttime) == -1) errx(1, "failed to get tty number"); - ppid = getppid(); - if ((sid = getsid(0)) == -1) - err(1, "getsid"); - if (snprintf(buf, sizeof buf, ".%d_%d_%llu_%d_%d", - tty, leader, starttime, ppid, sid) == -1) + if (snprintf(prefix, sizeof prefix, ".%d_%d_%llu_", + tty, leader, starttime) == -1) return NULL; - return buf; + return prefix; } -static int -checktsdir(int fd) +static const char * +timestamp_name() { - struct stat st; - - if (fstat(fd, &st) == -1) - err(1, "fstatat"); - - if ((st.st_mode & S_IFMT) != S_IFDIR) - errx(0, "timestamp directory is not a directory"); - if ((st.st_mode & (S_IWGRP|S_IRGRP|S_IXGRP|S_IWOTH|S_IROTH|S_IXOTH)) != 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 defined(TIMESTAMP_TMPFS) && defined(__linux__) - struct statfs sf; - if (fstatfs(fd, &sf) == -1) - err(1, "statfs"); - - if (sf.f_type != TMPFS_MAGIC) - errx(1, "timestamp directory not on tmpfs"); -#endif + static char name[128]; + pid_t ppid, sid; + const char *prefix; - return 0; + ppid = getppid(); + if ((sid = getsid(0)) == -1) + err(1, "getsid"); + if ((prefix = session_prefix()) == NULL) + return NULL; + if (snprintf(name, sizeof name, "%s_%d_%d", prefix, ppid, sid) == -1) + return NULL; + return name; } static int opentsdir() { - gid_t gid; + struct stat st; int fd; + int isnew; + + fd = -1; + isnew = 0; reopen: if ((fd = open(TIMESTAMP_DIR, O_RDONLY | O_DIRECTORY)) == -1) { - if (errno == ENOENT) { - gid = getegid(); - if (setegid(0) != 0) - err(1, "setegid"); + if (errno == ENOENT && isnew == 0) { if (mkdir(TIMESTAMP_DIR, (S_IRUSR|S_IWUSR|S_IXUSR)) != 0) err(1, "mkdir"); - if (setegid(gid) != 0) - err(1, "setegid"); + isnew = 1; goto reopen; } else { err(1, "failed to open timestamp directory: %s", TIMESTAMP_DIR); } } - if (checktsdir(fd) != 0) - return -1; - - return fd; -} - -static int -checktsfile(int fd, size_t *tssize) -{ - struct stat st; - gid_t gid; - +recheck: if (fstat(fd, &st) == -1) - err(1, "stat"); - if ((st.st_mode & S_IFMT) != S_IFREG) - errx(1, "timestamp is not a regular file"); + err(1, "fstatat"); + + 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_IXGRP|S_IWOTH|S_IROTH|S_IXOTH)) != 0) - errx(1, "timestamp has wrong permissions"); + errx(1, "timestamp directory has wrong permissions"); + + if (isnew == 1) { + if (fchown(fd, 0, 0) == -1) + err(1, "fchown"); + isnew = 0; + goto recheck; + } else { + if (st.st_uid != 0 || st.st_gid != 0) + errx(1, "timestamp directory has wrong owner or group"); + } - gid = getegid(); - if (st.st_uid != 0 || st.st_gid != gid) - errx(1, "timestamp has wrong owner"); +#if defined(TIMESTAMP_TMPFS) && defined(__linux__) + struct statfs sf; + if (fstatfs(fd, &sf) == -1) + err(1, "statfs"); - *tssize = st.st_size; + if (sf.f_type != TMPFS_MAGIC) + errx(1, "timestamp directory not on tmpfs"); +#endif - return 0; + return fd; } +/* + * Returns 1 if the timestamp is valid, 0 if its invalid + */ static int -validts(int fd, int secs) +timestamp_valid(int fd, int secs) { struct timespec mono, real, ts_mono, ts_real, timeout; @@ -267,7 +268,7 @@ validts(int fd, int secs) if (timespeccmp(&mono, &ts_mono, >) || timespeccmp(&real, &ts_real, >)) - return -1; + return 0; memset(&timeout, 0, sizeof timeout); timeout.tv_sec = secs; @@ -278,11 +279,11 @@ validts(int fd, int secs) timespeccmp(&real, &ts_real, <)) errx(1, "timestamp is too far in the future"); - return 0; + return 1; } int -persist_set(int fd, int secs) +timestamp_set(int fd, int secs) { struct timespec mono, real, ts_mono, ts_real, timeout; @@ -297,6 +298,8 @@ persist_set(int fd, int secs) if (lseek(fd, 0, SEEK_SET) == -1) err(1, "lseek"); + if (ftruncate(fd, 0) == -1) + err(1, "ftruncate"); 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"); @@ -305,14 +308,16 @@ persist_set(int fd, int secs) } int -persist_open(int *valid, int secs) +timestamp_open(int *valid, int secs) { + struct stat st; int dirfd, fd; + gid_t gid; const char *name; *valid = 0; - if ((name = tsname()) == NULL) + if ((name = timestamp_name()) == NULL) errx(1, "failed to get timestamp name"); if ((dirfd = opentsdir()) == -1) errx(1, "opentsdir"); @@ -327,9 +332,16 @@ persist_open(int *valid, int secs) err(1, "open timestamp file"); } - size_t tssize; - if (checktsfile(fd, &tssize) == -1) - err(1, "checktsfile"); + if (fstat(fd, &st) == -1) + err(1, "stat"); + if ((st.st_mode & S_IFMT) != S_IFREG) + errx(1, "timestamp is not a regular file"); + if ((st.st_mode & (S_IWGRP|S_IRGRP|S_IXGRP|S_IWOTH|S_IROTH|S_IXOTH)) != 0) + errx(1, "timestamp file has wrong permissions"); + + gid = getegid(); + if (st.st_uid != 0 || st.st_gid != gid) + errx(1, "timestamp file has wrong owner or group"); /* The timestamp size is 0 if its a new file or a * timestamp that was never set, its not valid but @@ -337,26 +349,51 @@ persist_open(int *valid, int secs) * If the size does not match the expected size it * is incomplete and should never be used */ - if (tssize == sizeof(struct timespec) * 2) - *valid = validts(fd, secs) == 0; - else if (tssize != 0) + if (st.st_size == sizeof (struct timespec) * 2) { + *valid = timestamp_valid(fd, secs); + } else if (st.st_size == 0) { + *valid = 0; + } else { errx(1, "corrupt timestamp file"); + } close(dirfd); return fd; } int -persist_clear() +timestamp_clear() { - const char *name; + struct dirent *ent; + DIR *dp; + const char *prefix; int dirfd; - if ((name = tsname()) == NULL) - errx(1, "failed to get timestamp name"); + int ret = 0; + size_t plen; + + if ((prefix = session_prefix()) == NULL) + errx(1, "failed to get timestamp session prefix"); + plen = strlen(prefix); + if ((dirfd = opentsdir()) == -1) errx(1, "opentsdir"); - if (unlinkat(dirfd, name, 0) == -1 && errno != ENOENT) - return -1; + if ((dp = fdopendir(dirfd)) == NULL) + err(1, "fopendir"); + + /* + * Delete all files in the timestamp directory + * with the same session prefix. + */ + while ((ent = readdir(dp)) != NULL) { + if (ent->d_type != DT_REG) + continue; + if (strncmp(prefix, ent->d_name, plen) != 0) + continue; + if (unlinkat(dirfd, ent->d_name, 0) == -1) + ret = -1; + } + closedir(dp); close(dirfd); - return 0; + + return ret; } From ed7fb0a2f40f2f51304f676963a459f2986f5ea0 Mon Sep 17 00:00:00 2001 From: Duncaen Date: Wed, 30 Jan 2019 23:07:19 +0100 Subject: [PATCH 110/198] pam: add timestamp support --- doas.c | 6 ++-- includes.h | 2 +- pam.c | 86 ++++++++++++++++++++++++++++++++++-------------------- 3 files changed, 57 insertions(+), 37 deletions(-) diff --git a/doas.c b/doas.c index 0782f87..a327136 100644 --- a/doas.c +++ b/doas.c @@ -414,10 +414,8 @@ main(int argc, char **argv) if (!pw) errx(1, "no passwd entry for target"); - if (!pamauth(pw->pw_name, myname, !nflag, rule->options & NOPASS)) { - syslog(LOG_AUTHPRIV | LOG_NOTICE, "failed auth for %s", myname); - errx(1, "Authorization failed"); - } + pamauth(pw->pw_name, myname, !nflag, rule->options & NOPASS, + rule->options & PERSIST); #else (void) nflag; diff --git a/includes.h b/includes.h index d3d4e39..d4967e0 100644 --- a/includes.h +++ b/includes.h @@ -17,7 +17,7 @@ #include "openbsd.h" #ifdef USE_PAM -int pamauth(const char *, const char *, int, int); +void pamauth(const char *, const char *, int, int, int); #endif #ifdef USE_SHADOW diff --git a/pam.c b/pam.c index 6be44b7..e0f17a5 100644 --- a/pam.c +++ b/pam.c @@ -15,6 +15,7 @@ */ #include +#include #include #include @@ -29,7 +30,7 @@ #include #include #include -#include +#include #include #include @@ -41,8 +42,6 @@ static pam_handle_t *pamh = NULL; static char doas_prompt[128]; static sig_atomic_t volatile caught_signal = 0; -static int session_opened = 0; -static int cred_established = 0; static void catchsig(int sig) @@ -85,7 +84,7 @@ pamconv(int nmsgs, const struct pam_message **msgs, int ret; if (!(rsp = calloc(nmsgs, sizeof(struct pam_response)))) - errx(1, "couldn't malloc pam_response"); + err(1, "could not allocate pam_response"); for (i = 0; i < nmsgs; i++) { switch (style = msgs[i]->msg_style) { @@ -131,14 +130,14 @@ pamconv(int nmsgs, const struct pam_message **msgs, } void -pamcleanup(int ret) +pamcleanup(int ret, int sess, int cred) { - if (session_opened != 0) { + if (sess) { ret = pam_close_session(pamh, 0); if (ret != PAM_SUCCESS) errx(1, "pam_close_session: %s", pam_strerror(pamh, ret)); } - if (cred_established != 0) { + if (cred) { ret = pam_setcred(pamh, PAM_DELETE_CRED | PAM_SILENT); if (ret != PAM_SUCCESS) warn("pam_setcred(?, PAM_DELETE_CRED | PAM_SILENT): %s", @@ -148,7 +147,7 @@ pamcleanup(int ret) } void -watchsession(pid_t child) +watchsession(pid_t child, int sess, int cred) { sigset_t sigs; struct sigaction act, oldact; @@ -199,7 +198,7 @@ watchsession(pid_t child) kill(child, SIGTERM); } - pamcleanup(PAM_SUCCESS); + pamcleanup(PAM_SUCCESS, sess, cred); if (caught_signal) { if (child != (pid_t)-1) { @@ -219,8 +218,8 @@ watchsession(pid_t child) exit(status); } -int -pamauth(const char *user, const char* ruser, int interactive, int nopass) +void +pamauth(const char *user, const char *myname, int interactive, int nopass, int persist) { static const struct pam_conv conv = { .conv = pamconv, @@ -228,20 +227,27 @@ pamauth(const char *user, const char* ruser, int interactive, int nopass) }; const char *ttydev; pid_t child; - int ret; + int ret, sess = 0, cred = 0; - if (!user || !ruser) - return 0; +#ifdef USE_TIMESTAMP + int fd = -1; + int valid = 0; +#else + (void) persist; +#endif + + if (!user || !myname) + errx(1, "Authorization failed"); - ret = pam_start(PAM_SERVICE_NAME, ruser, &conv, &pamh); + ret = pam_start(PAM_SERVICE_NAME, myname, &conv, &pamh); if (ret != PAM_SUCCESS) errx(1, "pam_start(\"%s\", \"%s\", ?, ?): failed", - PAM_SERVICE_NAME, ruser); + PAM_SERVICE_NAME, myname); - ret = pam_set_item(pamh, PAM_RUSER, ruser); + ret = pam_set_item(pamh, PAM_RUSER, myname); if (ret != PAM_SUCCESS) warn("pam_set_item(?, PAM_RUSER, \"%s\"): %s", - pam_strerror(pamh, ret), ruser); + pam_strerror(pamh, ret), myname); if (isatty(0) && (ttydev = ttyname(0)) != NULL) { if (strncmp(ttydev, "/dev/", 5) == 0) @@ -253,6 +259,14 @@ pamauth(const char *user, const char* ruser, int interactive, int nopass) ttydev, pam_strerror(pamh, ret)); } + +#ifdef USE_TIMESTAMP + if (persist) + fd = timestamp_open(&valid, 5 * 60); + if (fd != -1 && valid == 1) + nopass = 1; +#endif + if (!nopass) { if (!interactive) errx(1, "Authorization required"); @@ -262,23 +276,28 @@ pamauth(const char *user, const char* ruser, int interactive, int nopass) if (gethostname(host, sizeof(host))) snprintf(host, sizeof(host), "?"); snprintf(doas_prompt, sizeof(doas_prompt), - "\rdoas (%.32s@%.32s) password: ", ruser, host); + "\rdoas (%.32s@%.32s) password: ", myname, host); /* authenticate */ ret = pam_authenticate(pamh, 0); if (ret != PAM_SUCCESS) { - pamcleanup(ret); - return 0; + pamcleanup(ret, sess, cred); + syslog(LOG_AUTHPRIV | LOG_NOTICE, "failed auth for %s", myname); + errx(1, "Authorization failed"); } } + ret = pam_acct_mgmt(pamh, 0); if (ret == PAM_NEW_AUTHTOK_REQD) ret = pam_chauthtok(pamh, PAM_CHANGE_EXPIRED_AUTHTOK); /* account not vaild or changing the auth token failed */ - if (ret != PAM_SUCCESS) - return 0; + if (ret != PAM_SUCCESS) { + pamcleanup(ret, sess, cred); + syslog(LOG_AUTHPRIV | LOG_NOTICE, "failed auth for %s", myname); + errx(1, "Authorization failed"); + } /* set PAM_USER to the user we want to be */ ret = pam_set_item(pamh, PAM_USER, user); @@ -290,25 +309,28 @@ pamauth(const char *user, const char* ruser, int interactive, int nopass) if (ret != PAM_SUCCESS) warn("pam_setcred(?, PAM_ESTABLISH_CRED): %s", pam_strerror(pamh, ret)); else - cred_established = 1; + cred = 1; /* open session */ ret = pam_open_session(pamh, 0); if (ret != PAM_SUCCESS) errx(1, "pam_open_session: %s", pam_strerror(pamh, ret)); - else - session_opened = 1; + sess = 1; if ((child = fork()) == -1) { - pamcleanup(PAM_ABORT); - errx(1, "fork"); + pamcleanup(PAM_ABORT, sess, cred); + err(1, "fork"); } /* return as child */ if (child == 0) - return 1; - - watchsession(child); + return; - return 0; +#ifdef USE_TIMESTAMP + if (fd != -1) { + timestamp_set(fd, 5 * 60); + close(fd); + } +#endif + watchsession(child, sess, cred); } From 39c5d01f30a99d94dc89411a8e1ab103e2fc3074 Mon Sep 17 00:00:00 2001 From: krw Date: Wed, 11 Jul 2018 07:39:22 +0000 Subject: [PATCH 111/198] Do for most running out of memory err() what was done for most running out of memory log_warn(). i.e. ("%s", __func__) instead of manual function names and redundant verbiage about which wrapper detected the out of memory condition. ok henning@ --- parse.y | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/parse.y b/parse.y index c906d9f..e4a041a 100644 --- a/parse.y +++ b/parse.y @@ -331,7 +331,7 @@ eow: } } if ((str = strdup(buf)) == NULL) - err(1, "strdup"); + err(1, "%s", __func__); yylval.str = str; return TSTRING; } From 37bd6612bdffabe6d8a588b391bd353c39497abb Mon Sep 17 00:00:00 2001 From: tedu Date: Thu, 17 Jan 2019 05:35:35 +0000 Subject: [PATCH 112/198] clear the password even after a mismatch --- doas.c | 1 + 1 file changed, 1 insertion(+) diff --git a/doas.c b/doas.c index a327136..6223aff 100644 --- a/doas.c +++ b/doas.c @@ -234,6 +234,7 @@ authuser(char *myname, char *login_style, int persist) errx(1, "a tty is required"); } if (!auth_userresponse(as, response, 0)) { + explicit_bzero(rbuf, sizeof(rbuf)); syslog(LOG_AUTHPRIV | LOG_NOTICE, "failed auth for %s", myname); errx(1, "Authorization failed"); From a283d2f0e2569329bd13f97d2d61f7556ea51b30 Mon Sep 17 00:00:00 2001 From: Duncaen Date: Wed, 30 Jan 2019 23:17:49 +0100 Subject: [PATCH 113/198] shadow: clear the password even after a mismatch --- shadow.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/shadow.c b/shadow.c index a775b2b..53877d5 100644 --- a/shadow.c +++ b/shadow.c @@ -64,9 +64,14 @@ shadowauth(const char *myname, int persist) "tty required for %s", myname); errx(1, "a tty is required"); } - if ((encrypted = crypt(response, hash)) == NULL) - err(1, "crypt"); + if (response == NULL) + err(1, "readpassphrase"); + if ((encrypted = crypt(response, hash)) == NULL) { + explicit_bzero(rbuf, sizeof(rbuf)); + errx(1, "Authorization failed"); + } if (strcmp(encrypted, hash) != 0) { + explicit_bzero(rbuf, sizeof(rbuf)); syslog(LOG_AUTHPRIV | LOG_NOTICE, "failed auth for %s", myname); errx(1, "Authorization failed"); } From 8872767adddd36c58d2fd3ec5f2d1b35fe37c399 Mon Sep 17 00:00:00 2001 From: Ivy Foster Date: Wed, 30 Jan 2019 13:35:14 -0600 Subject: [PATCH 114/198] configure: list --with-timestamp in help, since without is default --- configure | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure b/configure index 1f3e2cb..4dfd287 100755 --- a/configure +++ b/configure @@ -27,7 +27,7 @@ usage: configure [options] --without-pam disable pam support --without-shadow disable shadow support - --without-timestamp disable timestamp support + --with-timestamp enable timestamp support --help, -h display this help and exit EOF From 55c5e6bdb8fb360553420550133775422204a55b Mon Sep 17 00:00:00 2001 From: Ivy Foster Date: Wed, 30 Jan 2019 13:39:50 -0600 Subject: [PATCH 115/198] Add generated file parse.c to .gitignore and 'make clean' Closes: #24 [via git-merge-pr] --- .gitignore | 1 + bsd.prog.mk | 1 + 2 files changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index 5ec596b..fe3a7aa 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ doas +parse.c version.h *.a diff --git a/bsd.prog.mk b/bsd.prog.mk index 93032d2..df8f514 100644 --- a/bsd.prog.mk +++ b/bsd.prog.mk @@ -42,6 +42,7 @@ clean: rm -f ${OBJS} rm -f ${OBJS:.o=.d} rm -f ${PROG} + rm -f parse.c -include ${OBJS:.o=.d} ${OPENBSD:.o=.d} From 38e072b353f5b1325bbf52dfb759fe49ff6ef0f7 Mon Sep 17 00:00:00 2001 From: Duncaen Date: Wed, 30 Jan 2019 23:35:25 +0100 Subject: [PATCH 116/198] shadow: clear phassphrase earlier --- shadow.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/shadow.c b/shadow.c index 53877d5..71c71c6 100644 --- a/shadow.c +++ b/shadow.c @@ -70,14 +70,12 @@ shadowauth(const char *myname, int persist) explicit_bzero(rbuf, sizeof(rbuf)); errx(1, "Authorization failed"); } + explicit_bzero(rbuf, sizeof(rbuf)); if (strcmp(encrypted, hash) != 0) { - explicit_bzero(rbuf, sizeof(rbuf)); syslog(LOG_AUTHPRIV | LOG_NOTICE, "failed auth for %s", myname); errx(1, "Authorization failed"); } - explicit_bzero(rbuf, sizeof(rbuf)); - #ifdef USE_TIMESTAMP good: if (fd != -1) { From 8b2a776ddd47b74bb814a00ea7af3cc970063db7 Mon Sep 17 00:00:00 2001 From: Duncaen Date: Wed, 30 Jan 2019 23:43:12 +0100 Subject: [PATCH 117/198] pam: close timestamp fd in both both processes --- pam.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/pam.c b/pam.c index e0f17a5..c5a3001 100644 --- a/pam.c +++ b/pam.c @@ -323,8 +323,13 @@ pamauth(const char *user, const char *myname, int interactive, int nopass, int p } /* return as child */ - if (child == 0) + if (child == 0) { +#ifdef USE_TIMESTAMP + if (fd != -1) + close(fd); +#endif return; + } #ifdef USE_TIMESTAMP if (fd != -1) { From fe5ec5717fe414e9f1e12bbdf0794c18af3238fb Mon Sep 17 00:00:00 2001 From: Duncan Overbruck Date: Fri, 26 Jul 2019 16:39:36 +0200 Subject: [PATCH 118/198] timestamp: error out if fstat and lstat st_ino and st_dev are not the same --- timestamp.c | 44 ++++++++++++++++++++++++++++++++++---------- 1 file changed, 34 insertions(+), 10 deletions(-) diff --git a/timestamp.c b/timestamp.c index 0d8f9c3..baa713e 100644 --- a/timestamp.c +++ b/timestamp.c @@ -198,14 +198,21 @@ timestamp_name() static int opentsdir() { - struct stat st; + struct stat st, stl; int fd; int isnew; - fd = -1; isnew = 0; + reopen: + if (lstat(TIMESTAMP_DIR, &stl) == -1) { + if (!(errno == ENOENT && isnew == 0)) + err(1, "lstat: %s", TIMESTAMP_DIR); + } else if ((stl.st_mode & S_IFMT) != S_IFDIR) { + errx(1, "%s: not a directory", TIMESTAMP_DIR); + } + if ((fd = open(TIMESTAMP_DIR, O_RDONLY | O_DIRECTORY)) == -1) { if (errno == ENOENT && isnew == 0) { if (mkdir(TIMESTAMP_DIR, (S_IRUSR|S_IWUSR|S_IXUSR)) != 0) @@ -221,8 +228,9 @@ opentsdir() if (fstat(fd, &st) == -1) err(1, "fstatat"); - if ((st.st_mode & S_IFMT) != S_IFDIR) - errx(1, "timestamp directory is not a directory"); + if (stl.st_ino != st.st_ino || stl.st_dev != st.st_dev) + errx(1, "timestamp directory lstat and fstat are different files"); + if ((st.st_mode & (S_IWGRP|S_IRGRP|S_IXGRP|S_IWOTH|S_IROTH|S_IXOTH)) != 0) errx(1, "timestamp directory has wrong permissions"); @@ -310,7 +318,7 @@ timestamp_set(int fd, int secs) int timestamp_open(int *valid, int secs) { - struct stat st; + struct stat st, stl; int dirfd, fd; gid_t gid; const char *name; @@ -322,20 +330,36 @@ timestamp_open(int *valid, int secs) if ((dirfd = opentsdir()) == -1) errx(1, "opentsdir"); + if (fstatat(dirfd, name, &stl, AT_SYMLINK_NOFOLLOW) == -1) { + if (errno != ENOENT) + err(1, "fstatat"); + } else if ((stl.st_mode & S_IFMT) != S_IFREG) { + errx(1, "timestamp: not a regular file"); + } + if ((fd = openat(dirfd, name, (O_RDWR), (S_IRUSR|S_IWUSR))) == -1) if (errno != ENOENT) err(1, "open timestamp file"); - if (fd == -1) { + /* + * If the file was opened, check if fstat and lstat results are + * the same file. + * If the file doesn't exist and we create it with O_CREAT|O_EXCL, + * it is already known that the file is a regular file. + */ + if (fd != -1) { + if (fstat(fd, &st) == -1) + err(1, "stat"); + if (stl.st_ino != st.st_ino || stl.st_dev != st.st_dev) + errx(1, "timestamp file lstat and fstat are different files"); + } else { if ((fd = openat(dirfd, name, (O_RDWR|O_CREAT|O_EXCL|O_NOFOLLOW), (S_IRUSR|S_IWUSR))) == -1) err(1, "open timestamp file"); + if (fstat(fd, &st) == -1) + err(1, "stat"); } - if (fstat(fd, &st) == -1) - err(1, "stat"); - if ((st.st_mode & S_IFMT) != S_IFREG) - errx(1, "timestamp is not a regular file"); if ((st.st_mode & (S_IWGRP|S_IRGRP|S_IXGRP|S_IWOTH|S_IROTH|S_IXOTH)) != 0) errx(1, "timestamp file has wrong permissions"); From 8cba47c99d1efa726801fac41c478f1712ab481a Mon Sep 17 00:00:00 2001 From: Duncan Overbruck Date: Fri, 26 Jul 2019 17:01:54 +0200 Subject: [PATCH 119/198] libopenbsd/closefrom.c: sync with sudo --- libopenbsd/closefrom.c | 160 ++++++++++++++++++++++++----------------- 1 file changed, 94 insertions(+), 66 deletions(-) diff --git a/libopenbsd/closefrom.c b/libopenbsd/closefrom.c index ef7d611..8f66084 100644 --- a/libopenbsd/closefrom.c +++ b/libopenbsd/closefrom.c @@ -1,5 +1,8 @@ /* - * Copyright (c) 2004-2005 Todd C. Miller + * SPDX-License-Identifier: ISC + * + * Copyright (c) 2004-2005, 2007, 2010, 2012-2015, 2017-2018 + * Todd C. Miller * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -14,96 +17,121 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include "includes.h" +/* + * 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 #ifndef HAVE_CLOSEFROM #include -#include #include #include -#ifdef HAVE_FCNTL_H -# include -#endif -#include #include -#include -#include -#include -#ifdef HAVE_DIRENT_H -# include -# define NAMLEN(dirent) strlen((dirent)->d_name) +#include +#include +#ifdef HAVE_PSTAT_GETPROC +# include #else -# define dirent direct -# define NAMLEN(dirent) (dirent)->d_namlen -# ifdef HAVE_SYS_NDIR_H -# include -# endif -# ifdef HAVE_SYS_DIR_H -# include -# endif -# ifdef HAVE_NDIR_H -# include -# endif +# include #endif -#ifndef OPEN_MAX -# define OPEN_MAX 256 -#endif +#include "openbsd.h" -#if 0 -__unused static const char rcsid[] = "$Sudo: closefrom.c,v 1.11 2006/08/17 15:26:54 millert Exp $"; -#endif /* lint */ +#ifndef _POSIX_OPEN_MAX +# define _POSIX_OPEN_MAX 20 +#endif /* * Close all file descriptors greater than or equal to lowfd. + * This is the expensive (fallback) method. */ -#ifdef HAVE_FCNTL_CLOSEM -void -closefrom(int lowfd) +static void +closefrom_fallback(int lowfd) { - (void) fcntl(lowfd, F_CLOSEM, 0); -} + long fd, maxfd; + + /* + * Fall back on sysconf(_SC_OPEN_MAX). We avoid checking + * resource limits since it is possible to open a file descriptor + * and then drop the rlimit such that it is below the open fd. + */ + maxfd = sysconf(_SC_OPEN_MAX); + if (maxfd < 0) + maxfd = _POSIX_OPEN_MAX; + + for (fd = lowfd; fd < maxfd; fd++) { +#ifdef __APPLE__ + /* Avoid potential libdispatch crash when we close its fds. */ + (void) fcntl((int) fd, F_SETFD, FD_CLOEXEC); #else + (void) close((int) fd); +#endif + } +} + +/* + * Close all file descriptors greater than or equal to lowfd. + * We try the fast way first, falling back on the slow method. + */ void closefrom(int lowfd) { - long fd, maxfd; -#if defined(HAVE_PROC_PID) - char fdpath[PATH_MAX], *endp; - struct dirent *dent; +#if defined(HAVE_PSTAT_GETPROC) + struct pst_status pstat; +#elif defined(HAVE_DIRFD) + const char *path; DIR *dirp; - int len; +#endif - /* Check for a /proc/$$/fd directory. */ - len = snprintf(fdpath, sizeof(fdpath), "/proc/%ld/fd", (long)getpid()); - if (len > 0 && (size_t)len < sizeof(fdpath) && (dirp = opendir(fdpath))) { + /* Try the fast method first, if possible. */ +#if defined(HAVE_FCNTL_CLOSEM) + if (fcntl(lowfd, F_CLOSEM, 0) != -1) + return; +#endif +#if defined(HAVE_PSTAT_GETPROC) + /* + * EOVERFLOW is not a fatal error for the fields we use. + * See the "EOVERFLOW Error" section of pstat_getvminfo(3). + */ + if (pstat_getproc(&pstat, sizeof(pstat), 0, getpid()) != -1 || + errno == EOVERFLOW) { + int fd; + + for (fd = lowfd; fd <= pstat.pst_highestfd; fd++) + (void) close(fd); + return; + } +#elif defined(HAVE_DIRFD) + /* Use /proc/self/fd (or /dev/fd on macOS) if it exists. */ +# ifdef __APPLE__ + path = _PATH_DEV "fd"; +# else + path = "/proc/self/fd"; +# endif + if ((dirp = opendir(path)) != NULL) { + struct dirent *dent; while ((dent = readdir(dirp)) != NULL) { - fd = strtol(dent->d_name, &endp, 10); - if (dent->d_name != endp && *endp == '\0' && - fd >= 0 && fd < INT_MAX && fd >= lowfd && fd != dirfd(dirp)) - (void) close((int) fd); + const char *errstr; + int fd = strtonum(dent->d_name, lowfd, INT_MAX, &errstr); + if (errstr == NULL && fd != dirfd(dirp)) { +# ifdef __APPLE__ + /* Avoid potential libdispatch crash when we close its fds. */ + (void) fcntl(fd, F_SETFD, FD_CLOEXEC); +# else + (void) close(fd); +# endif + } } (void) closedir(dirp); - } else -#endif - { - /* - * Fall back on sysconf() or getdtablesize(). We avoid checking - * resource limits since it is possible to open a file descriptor - * and then drop the rlimit such that it is below the open fd. - */ -#ifdef HAVE_SYSCONF - maxfd = sysconf(_SC_OPEN_MAX); -#else - maxfd = getdtablesize(); -#endif /* HAVE_SYSCONF */ - if (maxfd < 0) - maxfd = OPEN_MAX; - - for (fd = lowfd; fd < maxfd; fd++) - (void) close((int) fd); + return; } +#endif /* HAVE_DIRFD */ + + /* Do things the slow way. */ + closefrom_fallback(lowfd); } -#endif /* !HAVE_FCNTL_CLOSEM */ + #endif /* HAVE_CLOSEFROM */ From 346e58e98596142ac2f3059814bbc7708b6824fd Mon Sep 17 00:00:00 2001 From: Duncan Overbruck Date: Fri, 26 Jul 2019 17:13:55 +0200 Subject: [PATCH 120/198] libopenbsd: remove MacOSX compat functions, its not supported anyways --- configure | 8 +-- libopenbsd/execvpe.c | 158 ----------------------------------------- libopenbsd/setresuid.c | 36 ---------- 3 files changed, 2 insertions(+), 200 deletions(-) delete mode 100644 libopenbsd/execvpe.c delete mode 100644 libopenbsd/setresuid.c diff --git a/configure b/configure index 4dfd287..95df243 100755 --- a/configure +++ b/configure @@ -326,9 +326,7 @@ int main(void) { execvpe("", p, p); return 0; }' -check_func "execvpe" "$src" || { - printf 'OPENBSD += execvpe.o\n' >>$CONFIG_MK -} +check_func "execvpe" "$src" || die "system has no execvpe(3): not supported" # # Check for setresuid(). @@ -339,9 +337,7 @@ int main(void) { setresuid(0, 0, 0); return 0; }' -check_func "setresuid" "$src" || { - printf 'OPENBSD += setresuid.o\n' >>$CONFIG_MK -} +check_func "setresuid" "$src" || die "system has no setresuid(2): not supported" # # Check for closefrom(). diff --git a/libopenbsd/execvpe.c b/libopenbsd/execvpe.c deleted file mode 100644 index c3238ea..0000000 --- a/libopenbsd/execvpe.c +++ /dev/null @@ -1,158 +0,0 @@ -/* $OpenBSD: exec.c,v 1.23 2016/03/13 18:34:20 guenther Exp $ */ -/*- - * Copyright (c) 1991, 1993 - * The Regents of the University of California. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#include "includes.h" - -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -int -execvpe(const char *name, char *const *argv, char *const *envp) -{ - char **memp; - int cnt; - size_t lp, ln, len; - char *p; - int eacces = 0; - char *bp, *cur, *path, buf[PATH_MAX]; - - /* - * Do not allow null name - */ - if (name == NULL || *name == '\0') { - errno = ENOENT; - return (-1); - } - - /* If it's an absolute or relative path name, it's easy. */ - if (strchr(name, '/')) { - bp = (char *)name; - cur = path = NULL; - goto retry; - } - bp = buf; - - /* Get the path we're searching. */ - if (!(path = getenv("PATH"))) - path = _PATH_DEFPATH; - len = strlen(path) + 1; - cur = alloca(len); - if (cur == NULL) { - errno = ENOMEM; - return (-1); - } - strlcpy(cur, path, len); - path = cur; - while ((p = strsep(&cur, ":"))) { - /* - * It's a SHELL path -- double, leading and trailing colons - * mean the current directory. - */ - if (!*p) { - p = "."; - lp = 1; - } else - lp = strlen(p); - ln = strlen(name); - - /* - * If the path is too long complain. This is a possible - * security issue; given a way to make the path too long - * the user may execute the wrong program. - */ - if (lp + ln + 2 > sizeof(buf)) { - struct iovec iov[3]; - - iov[0].iov_base = "execvp: "; - iov[0].iov_len = 8; - iov[1].iov_base = p; - iov[1].iov_len = lp; - iov[2].iov_base = ": path too long\n"; - iov[2].iov_len = 16; - (void)writev(STDERR_FILENO, iov, 3); - continue; - } - bcopy(p, buf, lp); - buf[lp] = '/'; - bcopy(name, buf + lp + 1, ln); - buf[lp + ln + 1] = '\0'; - -retry: (void)execve(bp, argv, envp); - switch(errno) { - case E2BIG: - goto done; - case EISDIR: - case ELOOP: - case ENAMETOOLONG: - case ENOENT: - break; - case ENOEXEC: - for (cnt = 0; argv[cnt]; ++cnt) - ; - memp = alloca((cnt + 2) * sizeof(char *)); - if (memp == NULL) - goto done; - memp[0] = "sh"; - memp[1] = bp; - bcopy(argv + 1, memp + 2, cnt * sizeof(char *)); - (void)execve(_PATH_BSHELL, memp, envp); - goto done; - case ENOMEM: - goto done; - case ENOTDIR: - break; - case ETXTBSY: - /* - * We used to retry here, but sh(1) doesn't. - */ - goto done; - case EACCES: - eacces = 1; - break; - default: - goto done; - } - } - if (eacces) - errno = EACCES; - else if (!errno) - errno = ENOENT; -done: - return (-1); -} diff --git a/libopenbsd/setresuid.c b/libopenbsd/setresuid.c deleted file mode 100644 index a62b19a..0000000 --- a/libopenbsd/setresuid.c +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (c) 2015 Nathan Holstein - * - * 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 -#include -#include - -/* I don't think we can actually mimic the right semantics? */ -int -setresuid(uid_t ruid, uid_t euid, uid_t suid) -{ - int ret; - if (suid != ruid) { - errno = EPERM; - return -1; - } - if ((ret = setuid(ruid)) != 0) - return ret; - if ((ret = seteuid(euid)) != 0) - return ret; - return 0; -} - From a1d5a98e134a410db728df2400b6c4be102318f0 Mon Sep 17 00:00:00 2001 From: Duncan Overbruck Date: Fri, 26 Jul 2019 17:39:27 +0200 Subject: [PATCH 121/198] README.md: update the readme to match the current state --- README.md | 54 ++++++++++++++++++++++-------------------------------- 1 file changed, 22 insertions(+), 32 deletions(-) diff --git a/README.md b/README.md index 3b54f18..20ef9f2 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,3 @@ -![sandwich](https://cloud.githubusercontent.com/assets/13654546/9128676/a583cd0a-3c9a-11e5-9b4f-e03ab0ba37d7.png) - -Apologies to [Randall Monroe](http://www.xkcd.org/149/). - # OpenDoas: a portable version of OpenBSD's `doas` command `doas` is a minimal replacement for the venerable `sudo`. It was @@ -9,47 +5,41 @@ initially [written by Ted Unangst](http://www.tedunangst.com/flak/post/doas) of the OpenBSD project to provide 95% of the features of `sudo` with a fraction of the codebase. -This is still a work in progress! Please do not deploy yet in a critical -environment! Of note, `doas` semantics may yet change, and I haven't -performed even a trivial security audit of my additions. +At the moment only linux with GLIBC or musl libc is supported and tested. ## Building and installing -Building `doas` should be just a simple `make` away. - -The included makefile also has an installation target. Installation -requires root access to properly set the executable permissions. You'll -also need to install a `doas.conf` file: - ``` -make && sudo make install -echo "permit :admin" | sudo tee /etc/doas.conf +$ ./configure +$ make +# make install ``` -Oh the irony, using `sudo` to install `doas`! - ## About the port +This is not an official port/project from OpenBSD! + As much as possible I've attempted to stick to `doas` as tedu desired it. As things stand it's essentially just code lifted from OpenBSD with -PAM based authentication glommed on to it. +PAM or shadow based authentication glommed on to it. + +Compatibility functions in libopenbsd come from openbsd directly +(`strtonum.c`, `reallocarray.c`, `strlcpy.c`, `strlcat.c`), +from openssh (`readpassphrase.c`) or from sudo (`closefrom.c`). -I've used cvsync and git-cvsimport to retain the history of the core -source files. I may choose to go back and do the same with some of the -compatibility functions (such as reallocarray.c). +The PAM and shadow authentication code does not come from the OpenBSD project. -I have found it necessary to make some fixes to the codebase. One was -a segfault due to differences in yacc/bison, others were just minor -fixes to warnings. Once this appears stable, I may try to upstream some -of these. +### Perist/Timestamp/Timeout -Currently, this is only tested on MacOSX 10.10 with Clang. My next goal -is support for Fedora Linux as well. Contributions gladly accepted. ;-) +The persist feature is disabled by default and can be enabled with the configure +flag `--with-timestamp`. -## Copyright +This feature is new and potentially dangerous, in the original doas, a kernel API +is used to set and clear timeouts. This API is openbsd specific and no similar API +is available on other operating systems. -All code from OpenBSD is licensed under the BSD license, please see -individual files for details as the specific text varies from file to -file. +As a workaround, the persist feature is implemented using timestamp files +similar to sudo. -All code I've written is licensed with the 2-clause BSD. +See the comment block in `timestamp.c` for an in-depth description on how +timestamps are created and checked to be as safe as possible. From 9a9495d672fb426010a6b52d637c30a0dbeb619f Mon Sep 17 00:00:00 2001 From: Duncan Overbruck Date: Fri, 26 Jul 2019 17:46:17 +0200 Subject: [PATCH 122/198] libopenbsd/closefrom.c: remove config.h include --- libopenbsd/closefrom.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/libopenbsd/closefrom.c b/libopenbsd/closefrom.c index 8f66084..0d39353 100644 --- a/libopenbsd/closefrom.c +++ b/libopenbsd/closefrom.c @@ -22,10 +22,6 @@ * PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com */ -#include - -#ifndef HAVE_CLOSEFROM - #include #include #include @@ -133,5 +129,3 @@ closefrom(int lowfd) /* Do things the slow way. */ closefrom_fallback(lowfd); } - -#endif /* HAVE_CLOSEFROM */ From dd136587ed181ac3a6eb5c3fcdfa2f99f42e020d Mon Sep 17 00:00:00 2001 From: tedu Date: Mon, 10 Jun 2019 18:11:27 +0000 Subject: [PATCH 123/198] use getpwuid_r to avoid problems with hidden static storage. ok deraadt lteo martijn --- doas.c | 93 ++++++++++++++++++++++++++++++++++++---------------------- 1 file changed, 58 insertions(+), 35 deletions(-) diff --git a/doas.c b/doas.c index 6223aff..beb707a 100644 --- a/doas.c +++ b/doas.c @@ -260,13 +260,19 @@ main(int argc, char **argv) const char *cmd; char cmdline[LINE_MAX]; char myname[_PW_NAME_LEN + 1]; - struct passwd *pw; +#ifdef __OpenBSD__ + char mypwbuf[_PW_BUF_LEN], targpwbuf[_PW_BUF_LEN]; +#else + char *mypwbuf = NULL, *targpwbuf = NULL; +#endif + struct passwd mypwstore, targpwstore; + struct passwd *mypw, *targpw; const struct rule *rule; uid_t uid; uid_t target = 0; gid_t groups[NGROUPS_MAX + 1]; int ngroups; - int i, ch; + int i, ch, rv; int sflag = 0; int nflag = 0; char cwdpath[PATH_MAX]; @@ -333,10 +339,23 @@ main(int argc, char **argv) } else if ((!sflag && !argc) || (sflag && argc)) usage(); - pw = getpwuid(uid); - if (!pw) - err(1, "getpwuid failed"); - if (strlcpy(myname, pw->pw_name, sizeof(myname)) >= sizeof(myname)) +#ifdef __OpenBSD__ + rv = getpwuid_r(uid, &mypwstore, mypwbuf, sizeof(mypwbuf), &mypw); + if (rv != 0 || mypw == NULL) + err(1, "getpwuid_r failed"); +#else + for (size_t sz = 1024; sz <= 16*1024; sz *= 2) { + mypwbuf = reallocarray(mypwbuf, sz, sizeof (char)); + if (mypwbuf == NULL) + errx(1, "can't allocate mypwbuf"); + rv = getpwuid_r(uid, &mypwstore, mypwbuf, sz, &mypw); + if (rv != ERANGE) + break; + } + if (rv != 0 || mypw == NULL) + err(1, "getpwuid_r failed"); +#endif + if (strlcpy(myname, mypw->pw_name, sizeof(myname)) >= sizeof(myname)) errx(1, "pw_name too long"); ngroups = getgroups(NGROUPS_MAX, groups); if (ngroups == -1) @@ -346,7 +365,7 @@ main(int argc, char **argv) if (sflag) { sh = getenv("SHELL"); if (sh == NULL || *sh == '\0') { - shargv[0] = strdup(pw->pw_shell); + shargv[0] = strdup(mypw->pw_shell); if (shargv[0] == NULL) err(1, NULL); } else @@ -383,57 +402,61 @@ main(int argc, char **argv) errc(1, EPERM, NULL); } -#if defined(__OpenBSD__) +#if defined(__OpenBSD__) || defined(USE_SHADOW) if (!(rule->options & NOPASS)) { if (nflag) errx(1, "Authorization required"); +# ifdef __OpenBSD__ authuser(myname, login_style, rule->options & PERSIST); +# else + shadowauth(myname, rule->options & PERSIST); +# endif } +# ifdef __OpenBSD__ if (pledge("stdio rpath getpw exec id", NULL) == -1) err(1, "pledge"); +# endif - pw = getpwuid(target); - if (!pw) - errx(1, "no passwd entry for target"); - -#elif defined(USE_SHADOW) +#elif !defined(USE_PAM) + (void) nflag; if (!(rule->options & NOPASS)) { - if (nflag) - errx(1, "Authorization required"); - - shadowauth(myname, rule->options & PERSIST); + errx(1, "Authorization required"); } +#endif /* !(__OpenBSD__ || USE_SHADOW) && !USE_PAM */ - pw = getpwuid(target); - if (!pw) - errx(1, "no passwd entry for target"); - -#elif defined(USE_PAM) - pw = getpwuid(target); - if (!pw) +#ifdef __OpenBSD__ + rv = getpwuid_r(target, &targpwstore, targpwbuf, sizeof(targpwbuf), &targpw); + if (rv != 0 || targpw == NULL) errx(1, "no passwd entry for target"); - - pamauth(pw->pw_name, myname, !nflag, rule->options & NOPASS, - rule->options & PERSIST); - #else - (void) nflag; - if (!(rule->options & NOPASS)) { - errx(1, "Authorization required"); + for (size_t sz = 1024; sz <= 16*1024; sz *= 2) { + targpwbuf = reallocarray(targpwbuf, sz, sizeof (char)); + if (targpwbuf == NULL) + errx(1, "can't allocate targpwbuf"); + rv = getpwuid_r(target, &targpwstore, targpwbuf, sz, &targpw); + if (rv != ERANGE) + break; } + if (rv != 0 || targpw == NULL) + err(1, "getpwuid_r failed"); +#endif + +#if defined(USE_PAM) + pamauth(targpw->pw_name, myname, !nflag, rule->options & NOPASS, + rule->options & PERSIST); #endif #ifdef HAVE_SETUSERCONTEXT - if (setusercontext(NULL, pw, target, LOGIN_SETGROUP | + if (setusercontext(NULL, targpw, target, LOGIN_SETGROUP | LOGIN_SETPRIORITY | LOGIN_SETRESOURCES | LOGIN_SETUMASK | LOGIN_SETUSER) != 0) errx(1, "failed to set user context for target"); #else - if (setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) != 0) + if (setresgid(targpw->pw_gid, targpw->pw_gid, targpw->pw_gid) != 0) err(1, "setresgid"); - if (initgroups(pw->pw_name, pw->pw_gid) != 0) + if (initgroups(targpw->pw_name, targpw->pw_gid) != 0) err(1, "initgroups"); if (setresuid(target, target, target) != 0) err(1, "setresuid"); @@ -455,7 +478,7 @@ main(int argc, char **argv) #endif syslog(LOG_AUTHPRIV | LOG_INFO, "%s ran command %s as %s from %s", - myname, cmdline, pw->pw_name, cwd); + myname, cmdline, targpw->pw_name, cwd); envp = prepenv(rule); From dbc7d06b5bbf01652744423bd8825ea7b5e92f73 Mon Sep 17 00:00:00 2001 From: tedu Date: Wed, 12 Jun 2019 02:50:29 +0000 Subject: [PATCH 124/198] a few cleanups and simplifications possible now that static pw is gone. noted by martijn. ok martijn. --- doas.c | 29 ++++++++++++++--------------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/doas.c b/doas.c index beb707a..1fd0e9a 100644 --- a/doas.c +++ b/doas.c @@ -259,7 +259,6 @@ main(int argc, char **argv) char *sh; const char *cmd; char cmdline[LINE_MAX]; - char myname[_PW_NAME_LEN + 1]; #ifdef __OpenBSD__ char mypwbuf[_PW_BUF_LEN], targpwbuf[_PW_BUF_LEN]; #else @@ -341,7 +340,7 @@ main(int argc, char **argv) #ifdef __OpenBSD__ rv = getpwuid_r(uid, &mypwstore, mypwbuf, sizeof(mypwbuf), &mypw); - if (rv != 0 || mypw == NULL) + if (rv != 0) err(1, "getpwuid_r failed"); #else for (size_t sz = 1024; sz <= 16*1024; sz *= 2) { @@ -352,11 +351,11 @@ main(int argc, char **argv) if (rv != ERANGE) break; } - if (rv != 0 || mypw == NULL) + if (rv != 0) err(1, "getpwuid_r failed"); #endif - if (strlcpy(myname, mypw->pw_name, sizeof(myname)) >= sizeof(myname)) - errx(1, "pw_name too long"); + if (mypw == NULL) + errx(1, "no passwd entry for self"); ngroups = getgroups(NGROUPS_MAX, groups); if (ngroups == -1) err(1, "can't get groups"); @@ -365,9 +364,7 @@ main(int argc, char **argv) if (sflag) { sh = getenv("SHELL"); if (sh == NULL || *sh == '\0') { - shargv[0] = strdup(mypw->pw_shell); - if (shargv[0] == NULL) - err(1, NULL); + shargv[0] = mypw->pw_shell; } else shargv[0] = sh; argv = shargv; @@ -398,7 +395,7 @@ main(int argc, char **argv) if (!permit(uid, groups, ngroups, &rule, target, cmd, (const char **)argv + 1)) { syslog(LOG_AUTHPRIV | LOG_NOTICE, - "failed command for %s: %s", myname, cmdline); + "failed command for %s: %s", mypw->pw_name, cmdline); errc(1, EPERM, NULL); } @@ -408,9 +405,9 @@ main(int argc, char **argv) errx(1, "Authorization required"); # ifdef __OpenBSD__ - authuser(myname, login_style, rule->options & PERSIST); + authuser(mypw->pw_name, login_style, rule->options & PERSIST); # else - shadowauth(myname, rule->options & PERSIST); + shadowauth(mypw->pw_name, rule->options & PERSIST); # endif } @@ -428,7 +425,7 @@ main(int argc, char **argv) #ifdef __OpenBSD__ rv = getpwuid_r(target, &targpwstore, targpwbuf, sizeof(targpwbuf), &targpw); - if (rv != 0 || targpw == NULL) + if (rv != 0) errx(1, "no passwd entry for target"); #else for (size_t sz = 1024; sz <= 16*1024; sz *= 2) { @@ -439,12 +436,14 @@ main(int argc, char **argv) if (rv != ERANGE) break; } - if (rv != 0 || targpw == NULL) + if (rv != 0) err(1, "getpwuid_r failed"); #endif + if (targpw == NULL) + err(1, "getpwuid_r failed"); #if defined(USE_PAM) - pamauth(targpw->pw_name, myname, !nflag, rule->options & NOPASS, + pamauth(targpw->pw_name, mypw->pw_name, !nflag, rule->options & NOPASS, rule->options & PERSIST); #endif @@ -478,7 +477,7 @@ main(int argc, char **argv) #endif syslog(LOG_AUTHPRIV | LOG_INFO, "%s ran command %s as %s from %s", - myname, cmdline, targpw->pw_name, cwd); + mypw->pw_name, cmdline, targpw->pw_name, cwd); envp = prepenv(rule); From 01c658f8c45cb92a343be5f32aa6da70b2032168 Mon Sep 17 00:00:00 2001 From: tedu Date: Sun, 16 Jun 2019 18:16:34 +0000 Subject: [PATCH 125/198] redo the environment inheritance to not inherit. it was intended to make life easier, but it can be surprising or even unsafe. instead, reset just about everything to the target user's values. ok deraadt martijn Thanks to Sander Bos in particular for pointing out some nasty edge cases. --- doas.c | 4 +++- doas.conf.5 | 10 ++-------- doas.h | 5 ++++- env.c | 45 +++++++++++++++++++++++++++++++++------------ 4 files changed, 42 insertions(+), 22 deletions(-) diff --git a/doas.c b/doas.c index 1fd0e9a..5396df0 100644 --- a/doas.c +++ b/doas.c @@ -449,6 +449,7 @@ main(int argc, char **argv) #ifdef HAVE_SETUSERCONTEXT if (setusercontext(NULL, targpw, target, LOGIN_SETGROUP | + LOGIN_SETPATH | LOGIN_SETPRIORITY | LOGIN_SETRESOURCES | LOGIN_SETUMASK | LOGIN_SETUSER) != 0) errx(1, "failed to set user context for target"); @@ -479,9 +480,10 @@ main(int argc, char **argv) syslog(LOG_AUTHPRIV | LOG_INFO, "%s ran command %s as %s from %s", mypw->pw_name, cmdline, targpw->pw_name, cwd); - envp = prepenv(rule); + envp = prepenv(rule, mypw, targpw); if (rule->cmd) { + /* do this again after setusercontext reset it */ if (setenv("PATH", safepath, 1) == -1) err(1, "failed to set PATH '%s'", safepath); } diff --git a/doas.conf.5 b/doas.conf.5 index 95daf4c..8fd700b 100644 --- a/doas.conf.5 +++ b/doas.conf.5 @@ -51,15 +51,9 @@ again for some time. .It Ic keepenv The user's environment is maintained. The default is to reset the environment, except for the variables -.Ev DISPLAY , -.Ev HOME , -.Ev LOGNAME , -.Ev MAIL , -.Ev PATH , -.Ev TERM , -.Ev USER +.Ev DISPLAY and -.Ev USERNAME . +.Ev TERM . .It Ic setenv { Oo Ar variable ... Oc Oo Ar variable=value ... Oc Ic } In addition to the variables mentioned above, keep the space-separated specified variables. diff --git a/doas.h b/doas.h index 48069db..3831dc7 100644 --- a/doas.h +++ b/doas.h @@ -29,7 +29,10 @@ extern struct rule **rules; extern int nrules; extern int parse_errors; -char **prepenv(const struct rule *); +struct passwd; + +char **prepenv(const struct rule *, const struct passwd *, + const struct passwd *); #define PERMIT 1 #define DENY 2 diff --git a/env.c b/env.c index 3e8b95d..c0f3837 100644 --- a/env.c +++ b/env.c @@ -24,6 +24,7 @@ #include #include #include +#include #include "doas.h" #include "includes.h" @@ -39,6 +40,8 @@ struct env { u_int count; }; +static void fillenv(struct env *env, const char **envlist); + static int envcmp(struct envnode *a, struct envnode *b) { @@ -69,8 +72,19 @@ freenode(struct envnode *node) free(node); } +static void +addnode(struct env *env, const char *key, const char *value) +{ + struct envnode *node; + + node = createnode(key, value); + RB_INSERT(envtree, &env->root, node); + env->count++; +} + static struct env * -createenv(const struct rule *rule) +createenv(const struct rule *rule, const struct passwd *mypw, + const struct passwd *targpw) { struct env *env; u_int i; @@ -81,6 +95,8 @@ createenv(const struct rule *rule) RB_INIT(&env->root); env->count = 0; + addnode(env, "DOAS_USER", mypw->pw_name); + if (rule->options & KEEPENV) { extern char **environ; @@ -109,6 +125,19 @@ createenv(const struct rule *rule) env->count++; } } + } else { + static const char *copyset[] = { + "DISPLAY", "TERM", + NULL + }; + + addnode(env, "HOME", targpw->pw_dir); + addnode(env, "LOGNAME", targpw->pw_name); + addnode(env, "PATH", getenv("PATH")); + addnode(env, "SHELL", targpw->pw_shell); + addnode(env, "USER", targpw->pw_name); + + fillenv(env, copyset); } return env; @@ -187,20 +216,12 @@ fillenv(struct env *env, const char **envlist) } char ** -prepenv(const struct rule *rule) +prepenv(const struct rule *rule, const struct passwd *mypw, + const struct passwd *targpw) { - static const char *safeset[] = { - "DISPLAY", "HOME", "LOGNAME", "MAIL", - "PATH", "TERM", "USER", "USERNAME", - NULL - }; struct env *env; - env = createenv(rule); - - /* if we started with blank, fill some defaults then apply rules */ - if (!(rule->options & KEEPENV)) - fillenv(env, safeset); + env = createenv(rule, mypw, targpw); if (rule->envlist) fillenv(env, rule->envlist); From 55adb00203fcb35df767868a02d6bcaea86092f5 Mon Sep 17 00:00:00 2001 From: tedu Date: Mon, 17 Jun 2019 16:01:26 +0000 Subject: [PATCH 126/198] always reset the "su" variables, which is more consistent and predictable. ok martijn millert --- doas.conf.5 | 8 ++++++++ env.c | 24 +++++++++++------------- 2 files changed, 19 insertions(+), 13 deletions(-) diff --git a/doas.conf.5 b/doas.conf.5 index 8fd700b..c9ea1b8 100644 --- a/doas.conf.5 +++ b/doas.conf.5 @@ -54,6 +54,14 @@ The default is to reset the environment, except for the variables .Ev DISPLAY and .Ev TERM . +The variables +.Ev HOME , +.Ev LOGNAME , +.Ev PATH , +.Ev SHELL , +and +.Ev USER +are always reset. .It Ic setenv { Oo Ar variable ... Oc Oo Ar variable=value ... Oc Ic } In addition to the variables mentioned above, keep the space-separated specified variables. diff --git a/env.c b/env.c index c0f3837..f1fe45f 100644 --- a/env.c +++ b/env.c @@ -86,6 +86,10 @@ static struct env * createenv(const struct rule *rule, const struct passwd *mypw, const struct passwd *targpw) { + static const char *copyset[] = { + "DISPLAY", "TERM", + NULL + }; struct env *env; u_int i; @@ -96,6 +100,13 @@ createenv(const struct rule *rule, const struct passwd *mypw, env->count = 0; addnode(env, "DOAS_USER", mypw->pw_name); + addnode(env, "HOME", targpw->pw_dir); + addnode(env, "LOGNAME", targpw->pw_name); + addnode(env, "PATH", getenv("PATH")); + addnode(env, "SHELL", targpw->pw_shell); + addnode(env, "USER", targpw->pw_name); + + fillenv(env, copyset); if (rule->options & KEEPENV) { extern char **environ; @@ -125,19 +136,6 @@ createenv(const struct rule *rule, const struct passwd *mypw, env->count++; } } - } else { - static const char *copyset[] = { - "DISPLAY", "TERM", - NULL - }; - - addnode(env, "HOME", targpw->pw_dir); - addnode(env, "LOGNAME", targpw->pw_name); - addnode(env, "PATH", getenv("PATH")); - addnode(env, "SHELL", targpw->pw_shell); - addnode(env, "USER", targpw->pw_name); - - fillenv(env, copyset); } return env; From 78ab134cc3541d3dc9ad55044cbad5c0c8539147 Mon Sep 17 00:00:00 2001 From: tedu Date: Mon, 17 Jun 2019 18:44:44 +0000 Subject: [PATCH 127/198] mention environment resetting here as well. ok millert --- doas.1 | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/doas.1 b/doas.1 index 26d63df..0dad559 100644 --- a/doas.1 +++ b/doas.1 @@ -39,6 +39,23 @@ or .Fl s is specified. .Pp +By default, the environment is reset. +The variables +.Ev HOME , +.Ev LOGNAME , +.Ev PATH , +.Ev SHELL , +and +.Ev USER +are set to values appropriate for the target user. +The variables +.Ev DISPLAY +and +.Ev TERM +are inherited from the current environment. +This behavior may be modified by the config file. +The working directory is not changed. +.Pp The options are as follows: .Bl -tag -width tenletters .It Fl C Ar config From 2103dd548aaa63339fd9137a4c9bb1e041921c28 Mon Sep 17 00:00:00 2001 From: tedu Date: Mon, 17 Jun 2019 19:51:23 +0000 Subject: [PATCH 128/198] setusercontext resets PATH (which we want). but then it becomes impossible to access the old PATH. save a copy in case we need it later. bug report from espie. --- doas.c | 18 ++++++++++++++++++ doas.h | 2 ++ env.c | 10 ++++++++-- 3 files changed, 28 insertions(+), 2 deletions(-) diff --git a/doas.c b/doas.c index 5396df0..28954ec 100644 --- a/doas.c +++ b/doas.c @@ -257,6 +257,7 @@ main(int argc, char **argv) const char *confpath = NULL; char *shargv[] = { NULL, NULL }; char *sh; + const char *p; const char *cmd; char cmdline[LINE_MAX]; #ifdef __OpenBSD__ @@ -411,7 +412,24 @@ main(int argc, char **argv) # endif } + if ((p = getenv("PATH")) != NULL) + formerpath = strdup(p); + if (formerpath == NULL) + formerpath = ""; + +# ifdef __OpenBSD__ + if (unveil(_PATH_LOGIN_CONF, "r") == -1 || + unveil(_PATH_LOGIN_CONF ".db", "r") == -1) + err(1, "unveil"); +# endif + if (rule->cmd) { + if (setenv("PATH", safepath, 1) == -1) + err(1, "failed to set PATH '%s'", safepath); + } # ifdef __OpenBSD__ + if (unveilcommands(getenv("PATH"), cmd) == 0) + goto fail; + if (pledge("stdio rpath getpw exec id", NULL) == -1) err(1, "pledge"); # endif diff --git a/doas.h b/doas.h index 3831dc7..4a117be 100644 --- a/doas.h +++ b/doas.h @@ -29,6 +29,8 @@ extern struct rule **rules; extern int nrules; extern int parse_errors; +extern const char *formerpath; + struct passwd; char **prepenv(const struct rule *, const struct passwd *, diff --git a/env.c b/env.c index f1fe45f..2090897 100644 --- a/env.c +++ b/env.c @@ -29,6 +29,8 @@ #include "doas.h" #include "includes.h" +const char *formerpath; + struct envnode { RB_ENTRY(envnode) node; const char *key; @@ -199,8 +201,12 @@ fillenv(struct env *env, const char **envlist) /* assign value or inherit from environ */ if (eq) { val = eq + 1; - if (*val == '$') - val = getenv(val + 1); + if (*val == '$') { + if (strcmp(val + 1, "PATH") == 0) + val = formerpath; + else + val = getenv(val + 1); + } } else { val = getenv(name); } From 2da129d4aad6e092ee3e6c7ab3103860c30a3441 Mon Sep 17 00:00:00 2001 From: schwarze Date: Wed, 19 Jun 2019 09:50:13 +0000 Subject: [PATCH 129/198] mention that doas(1) resets the umask(2); OK tedu@ --- doas.1 | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doas.1 b/doas.1 index 0dad559..2ecc992 100644 --- a/doas.1 +++ b/doas.1 @@ -47,6 +47,8 @@ The variables .Ev SHELL , and .Ev USER +and the +.Xr umask 2 are set to values appropriate for the target user. The variables .Ev DISPLAY From 025db698803cbd722444ba2745ead9a5c51efcb4 Mon Sep 17 00:00:00 2001 From: schwarze Date: Wed, 19 Jun 2019 09:55:55 +0000 Subject: [PATCH 130/198] more precisely describe what happens to the environment without keepenv; OK tedu@ --- doas.conf.5 | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/doas.conf.5 b/doas.conf.5 index c9ea1b8..d4fb355 100644 --- a/doas.conf.5 +++ b/doas.conf.5 @@ -50,18 +50,18 @@ After the user successfully authenticates, do not ask for a password again for some time. .It Ic keepenv The user's environment is maintained. -The default is to reset the environment, except for the variables +The default is to retain the variables .Ev DISPLAY and -.Ev TERM . -The variables +.Ev TERM +from the invoking process, reset .Ev HOME , .Ev LOGNAME , .Ev PATH , .Ev SHELL , and .Ev USER -are always reset. +as appropriate for the target user, and discard the rest of the environment. .It Ic setenv { Oo Ar variable ... Oc Oo Ar variable=value ... Oc Ic } In addition to the variables mentioned above, keep the space-separated specified variables. From 45b802a030ade925dbff39af816671b3f9eedd81 Mon Sep 17 00:00:00 2001 From: tedu Date: Fri, 21 Jun 2019 17:02:27 +0000 Subject: [PATCH 131/198] tweak wording a bit. always talk about creating a new environment. also document DOAS_USER. ok deraadt jmc --- doas.1 | 5 ++++- doas.conf.5 | 20 +++++--------------- 2 files changed, 9 insertions(+), 16 deletions(-) diff --git a/doas.1 b/doas.1 index 2ecc992..81fa803 100644 --- a/doas.1 +++ b/doas.1 @@ -39,7 +39,7 @@ or .Fl s is specified. .Pp -By default, the environment is reset. +By default, a new environment is created. The variables .Ev HOME , .Ev LOGNAME , @@ -50,6 +50,9 @@ and and the .Xr umask 2 are set to values appropriate for the target user. +.Ev DOAS_USER +is set to the name of the user executing +.Nm . The variables .Ev DISPLAY and diff --git a/doas.conf.5 b/doas.conf.5 index d4fb355..592b4b6 100644 --- a/doas.conf.5 +++ b/doas.conf.5 @@ -49,22 +49,11 @@ The user is not required to enter a password. After the user successfully authenticates, do not ask for a password again for some time. .It Ic keepenv -The user's environment is maintained. -The default is to retain the variables -.Ev DISPLAY -and -.Ev TERM -from the invoking process, reset -.Ev HOME , -.Ev LOGNAME , -.Ev PATH , -.Ev SHELL , -and -.Ev USER -as appropriate for the target user, and discard the rest of the environment. +Environment variables other than those listed in +.Xr doas 1 +are retained when creating the environment for the new process. .It Ic setenv { Oo Ar variable ... Oc Oo Ar variable=value ... Oc Ic } -In addition to the variables mentioned above, keep the space-separated -specified variables. +Keep or set the space-separated specified variables. Variables may also be removed with a leading .Sq - or set using the latter syntax. @@ -74,6 +63,7 @@ is a .Ql $ then the value to be set is taken from the existing environment variable of the indicated name. +This option is processed after the default environment has been created. .El .It Ar identity The username to match. From 3f08ab43f6e987220ef9711705368f1b49bde1cc Mon Sep 17 00:00:00 2001 From: tedu Date: Mon, 24 Jun 2019 14:45:52 +0000 Subject: [PATCH 132/198] add an example hint that shows how original path can be retained --- doas.conf.5 | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/doas.conf.5 b/doas.conf.5 index 592b4b6..d7642bd 100644 --- a/doas.conf.5 +++ b/doas.conf.5 @@ -127,11 +127,13 @@ and unsetting .Ev ENV ; permits tedu to run procmap as root without a password; -and additionally permits root to run unrestricted commands as itself. +and additionally permits root to run unrestricted commands as itself +while retaining the original PATH. .Bd -literal -offset indent permit persist setenv { PKG_CACHE PKG_PATH } aja cmd pkg_add permit setenv { -ENV PS1=$DOAS_PS1 SSH_AUTH_SOCK } :wheel permit nopass tedu as root cmd /usr/sbin/procmap +permit nopass keepenv setenv { PATH=$PATH } root as root .Ed .Sh SEE ALSO .Xr doas 1 From 6d8f0e61e0449a0446ef820521e94f5093b1da41 Mon Sep 17 00:00:00 2001 From: tedu Date: Sat, 29 Jun 2019 22:35:37 +0000 Subject: [PATCH 133/198] fix some more fallout from setting path in setusercontext. restore previous behavior of using user PATH if no cmd restriction in the rule. run into by espie --- doas.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/doas.c b/doas.c index 28954ec..7588274 100644 --- a/doas.c +++ b/doas.c @@ -500,10 +500,13 @@ main(int argc, char **argv) envp = prepenv(rule, mypw, targpw); + /* setusercontext set path for the next process, so reset it for us */ if (rule->cmd) { - /* do this again after setusercontext reset it */ if (setenv("PATH", safepath, 1) == -1) err(1, "failed to set PATH '%s'", safepath); + } else { + if (setenv("PATH", formerpath, 1) == -1) + err(1, "failed to set PATH '%s'", formerpath); } execvpe(cmd, argv, envp); if (errno == ENOENT) From f94cf30ade3dcd20e74ffa347359e9bf78428e6e Mon Sep 17 00:00:00 2001 From: deraadt Date: Wed, 3 Jul 2019 03:24:02 +0000 Subject: [PATCH 134/198] snprintf/vsnprintf return < 0 on error, rather than -1. --- doas.c | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/doas.c b/doas.c index 7588274..71f955a 100644 --- a/doas.c +++ b/doas.c @@ -249,6 +249,46 @@ authuser(char *myname, char *login_style, int persist) } #endif +#ifdef __OpenBSD__ +int +unveilcommands(const char *ipath, const char *cmd) +{ + char *path = NULL, *p; + int unveils = 0; + + if (strchr(cmd, '/') != NULL) { + if (unveil(cmd, "x") != -1) + unveils++; + goto done; + } + + if (!ipath) { + errno = ENOENT; + goto done; + } + path = strdup(ipath); + if (!path) { + errno = ENOENT; + goto done; + } + for (p = path; p && *p; ) { + char buf[PATH_MAX]; + char *cp = strsep(&p, ":"); + + if (cp) { + int r = snprintf(buf, sizeof buf, "%s/%s", cp, cmd); + if (r >= 0 && r < sizeof buf) { + if (unveil(buf, "x") != -1) + unveils++; + } + } + } +done: + free(path); + return (unveils); +} +#endif + int main(int argc, char **argv) { From ae7c4bab7a4899d7bd9b71a21451729a58cb7af6 Mon Sep 17 00:00:00 2001 From: tedu Date: Thu, 4 Jul 2019 19:04:17 +0000 Subject: [PATCH 135/198] note that authentication is required, unless otherwise configured. ok sthen --- doas.1 | 3 +++ 1 file changed, 3 insertions(+) diff --git a/doas.1 b/doas.1 index 81fa803..7360be3 100644 --- a/doas.1 +++ b/doas.1 @@ -39,6 +39,9 @@ or .Fl s is specified. .Pp +The user will be required to authenticate by entering their password, +unless configured otherwise. +.Pp By default, a new environment is created. The variables .Ev HOME , From 4356cb6b4cefb142d182784c264ce936a1ec3626 Mon Sep 17 00:00:00 2001 From: tedu Date: Sun, 7 Jul 2019 19:21:28 +0000 Subject: [PATCH 136/198] fix one last edge case regarding PATH, allows simpler config. --- doas.conf.5 | 2 +- env.c | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/doas.conf.5 b/doas.conf.5 index d7642bd..54022ee 100644 --- a/doas.conf.5 +++ b/doas.conf.5 @@ -133,7 +133,7 @@ while retaining the original PATH. permit persist setenv { PKG_CACHE PKG_PATH } aja cmd pkg_add permit setenv { -ENV PS1=$DOAS_PS1 SSH_AUTH_SOCK } :wheel permit nopass tedu as root cmd /usr/sbin/procmap -permit nopass keepenv setenv { PATH=$PATH } root as root +permit nopass keepenv setenv { PATH } root as root .Ed .Sh SEE ALSO .Xr doas 1 diff --git a/env.c b/env.c index 2090897..12909c6 100644 --- a/env.c +++ b/env.c @@ -208,7 +208,10 @@ fillenv(struct env *env, const char **envlist) val = getenv(val + 1); } } else { - val = getenv(name); + if (strcmp(name, "PATH") == 0) + val = formerpath; + else + val = getenv(name); } /* at last, we have something to insert */ if (val) { From 3916903608b12ae310588c51466c46971977acb6 Mon Sep 17 00:00:00 2001 From: Duncan Overbruck Date: Sat, 19 Oct 2019 15:02:58 +0200 Subject: [PATCH 137/198] fixup unveil --- doas.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/doas.c b/doas.c index 71f955a..bc03abd 100644 --- a/doas.c +++ b/doas.c @@ -458,8 +458,7 @@ main(int argc, char **argv) formerpath = ""; # ifdef __OpenBSD__ - if (unveil(_PATH_LOGIN_CONF, "r") == -1 || - unveil(_PATH_LOGIN_CONF ".db", "r") == -1) + if (unveil(_PATH_LOGIN_CONF, "r") == -1) err(1, "unveil"); # endif if (rule->cmd) { From 96d78078f6fcddc07b57aa8edabf305e4c12ed68 Mon Sep 17 00:00:00 2001 From: semarie Date: Sat, 14 Sep 2019 17:47:00 +0000 Subject: [PATCH 138/198] correct some unveil(2) violations due to "login.conf.db" access (the .db version of "login.conf"), and stat(2) on _PATH_MASTERPASSWD_LOCK (via pw_mkdb(3)). problem initially noted by myself for passwd(1) millert@ reported similar problem on chpass(1), su(1), doas(1) and encrypt(1) mestre@ noted chpass(1) too ok mestre@ millert@ --- doas.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/doas.c b/doas.c index bc03abd..71f955a 100644 --- a/doas.c +++ b/doas.c @@ -458,7 +458,8 @@ main(int argc, char **argv) formerpath = ""; # ifdef __OpenBSD__ - if (unveil(_PATH_LOGIN_CONF, "r") == -1) + if (unveil(_PATH_LOGIN_CONF, "r") == -1 || + unveil(_PATH_LOGIN_CONF ".db", "r") == -1) err(1, "unveil"); # endif if (rule->cmd) { From 3dac6fb553b199cf799e5f5ff6172b97794d22d1 Mon Sep 17 00:00:00 2001 From: tedu Date: Fri, 18 Oct 2019 17:15:45 +0000 Subject: [PATCH 139/198] add some checks to avoid UID_MAX (-1) here. this is not problematic with the current code, but it's probably safer this way. ok deraadt --- doas.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/doas.c b/doas.c index 71f955a..70b255b 100644 --- a/doas.c +++ b/doas.c @@ -57,9 +57,11 @@ parseuid(const char *s, uid_t *uid) if ((pw = getpwnam(s)) != NULL) { *uid = pw->pw_uid; + if (*uid == UID_MAX) + return -1; return 0; } - *uid = strtonum(s, 0, UID_MAX, &errstr); + *uid = strtonum(s, 0, UID_MAX - 1, &errstr); if (errstr) return -1; return 0; @@ -85,9 +87,11 @@ parsegid(const char *s, gid_t *gid) if ((gr = getgrnam(s)) != NULL) { *gid = gr->gr_gid; + if (*gid == GID_MAX) + return -1; return 0; } - *gid = strtonum(s, 0, GID_MAX, &errstr); + *gid = strtonum(s, 0, GID_MAX - 1, &errstr); if (errstr) return -1; return 0; From ea76157909ba9919cd0eb7535a7cb91c4c3fcb77 Mon Sep 17 00:00:00 2001 From: Duncan Overbruck Date: Thu, 21 Nov 2019 17:14:43 +0100 Subject: [PATCH 140/198] configure: make {UID,GID}_MAX configurable --- configure | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/configure b/configure index 95df243..2d20f09 100755 --- a/configure +++ b/configure @@ -29,6 +29,9 @@ usage: configure [options] --with-timestamp enable timestamp support + --uid-max=NUM set UID_MAX (default 65535) + --gid-max=NUM set GID_MAX (default 65535) + --help, -h display this help and exit EOF exit 0 @@ -36,6 +39,8 @@ EOF # defaults WITHOUT_TIMESTAMP=yes +UID_MAX=65535 +GID_MAX=65535 for x; do opt=${x%%=*} @@ -59,6 +64,8 @@ for x; do --without-shadow) WITHOUT_SHADOW=yes ;; --with-timestamp) WITHOUT_TIMESTAMP= ;; --without-timestamp) WITHOUT_TIMESTAMP=yes ;; + --uid-max) UID_MAX=$var ;; + --gid-max) UID_MAX=$var ;; --help|-h) usage ;; *) die "Error: unknown option $opt" ;; esac @@ -108,7 +115,7 @@ OS_CFLAGS="-D__${OS}__" case "$OS" in linux) - OS_CFLAGS="$OS_CFLAGS -D_DEFAULT_SOURCE -D_GNU_SOURCE -DUID_MAX=60000 -DGID_MAX=60000" + OS_CFLAGS="$OS_CFLAGS -D_DEFAULT_SOURCE -D_GNU_SOURCE -DUID_MAX=${UID_MAX} -DGID_MAX=${GID_MAX}" printf 'CURDIR := .\n' >>$CONFIG_MK [ -z "$WITHOUT_PAM" ] && \ printf 'PAM_DOAS = pam.d__doas__linux\n' >>$CONFIG_MK @@ -474,3 +481,5 @@ if [ $? -eq 0 ]; then else printf 'Using persist method\t\t\tnone.\n' >&2 fi +printf 'Setting UID_MAX\t\t\t\t%d.\n' "$UID_MAX" >&2 +printf 'Setting GID_MAX\t\t\t\t%d.\n' "$GID_MAX" >&2 From 9be2d262af3d7f57697345fe4e3614fa87c5fa58 Mon Sep 17 00:00:00 2001 From: Duncan Overbruck Date: Thu, 21 Nov 2019 18:01:36 +0100 Subject: [PATCH 141/198] timestamp: simplify --- timestamp.c | 301 ++++++++++++++-------------------------------------- 1 file changed, 81 insertions(+), 220 deletions(-) diff --git a/timestamp.c b/timestamp.c index baa713e..e97c92f 100644 --- a/timestamp.c +++ b/timestamp.c @@ -94,7 +94,8 @@ proc_info(pid_t pid, int *ttynr, unsigned long long *starttime) p = buf; - if (snprintf(path, sizeof path, "/proc/%d/stat", pid) == -1) + n = snprintf(path, sizeof path, "/proc/%d/stat", pid); + if (n < 0 || n >= (int)sizeof path) return -1; if ((fd = open(path, O_RDONLY)) == -1) @@ -152,272 +153,132 @@ proc_info(pid_t pid, int *ttynr, unsigned long long *starttime) #error "proc_info not implemented" #endif -/* The session prefix is the tty number, the pid - * of the session leader and the start time of the - * session leader. - */ -static const char * -session_prefix() -{ - static char prefix[128]; - int tty, fd; - unsigned long long starttime; - pid_t leader; - - if ((fd = open("/dev/tty", O_RDONLY)) == -1) - err(1, "open: /dev/tty"); - if (ioctl(fd, TIOCGSID, &leader) == -1) - err(1, "ioctl: failed to get session leader"); - close(fd); - - if (proc_info(leader, &tty, &starttime) == -1) - errx(1, "failed to get tty number"); - if (snprintf(prefix, sizeof prefix, ".%d_%d_%llu_", - tty, leader, starttime) == -1) - return NULL; - return prefix; -} - -static const char * -timestamp_name() +static int +timestamp_path(char *buf, size_t len) { - static char name[128]; pid_t ppid, sid; - const char *prefix; + unsigned long long starttime; + int n, ttynr; ppid = getppid(); if ((sid = getsid(0)) == -1) - err(1, "getsid"); - if ((prefix = session_prefix()) == NULL) - return NULL; - if (snprintf(name, sizeof name, "%s_%d_%d", prefix, ppid, sid) == -1) - return NULL; - return name; + return -1; + if (proc_info(ppid, &ttynr, &starttime) == -1) + return -1; + n = snprintf(buf, len, TIMESTAMP_DIR "/%d-%d-%d-%llu-%d", + ppid, sid, ttynr, starttime, getuid()); + if (n < 0 || n >= (int)len) + return -1; + return 0; } -static int -opentsdir() +int +timestamp_set(int fd, int secs) { - struct stat st, stl; - int fd; - int isnew; - - isnew = 0; + struct timespec ts[2], timeout = { .tv_sec = secs, .tv_nsec = 0 }; + if (clock_gettime(CLOCK_BOOTTIME, &ts[0]) == -1 || + clock_gettime(CLOCK_REALTIME, &ts[1]) == -1) + return -1; -reopen: - if (lstat(TIMESTAMP_DIR, &stl) == -1) { - if (!(errno == ENOENT && isnew == 0)) - err(1, "lstat: %s", TIMESTAMP_DIR); - } else if ((stl.st_mode & S_IFMT) != S_IFDIR) { - errx(1, "%s: not a directory", TIMESTAMP_DIR); - } - - if ((fd = open(TIMESTAMP_DIR, O_RDONLY | O_DIRECTORY)) == -1) { - if (errno == ENOENT && isnew == 0) { - if (mkdir(TIMESTAMP_DIR, (S_IRUSR|S_IWUSR|S_IXUSR)) != 0) - err(1, "mkdir"); - isnew = 1; - goto reopen; - } else { - err(1, "failed to open timestamp directory: %s", TIMESTAMP_DIR); - } - } - -recheck: - if (fstat(fd, &st) == -1) - err(1, "fstatat"); - - if (stl.st_ino != st.st_ino || stl.st_dev != st.st_dev) - errx(1, "timestamp directory lstat and fstat are different files"); - - if ((st.st_mode & (S_IWGRP|S_IRGRP|S_IXGRP|S_IWOTH|S_IROTH|S_IXOTH)) != 0) - errx(1, "timestamp directory has wrong permissions"); - - if (isnew == 1) { - if (fchown(fd, 0, 0) == -1) - err(1, "fchown"); - isnew = 0; - goto recheck; - } else { - if (st.st_uid != 0 || st.st_gid != 0) - errx(1, "timestamp directory has wrong owner or group"); - } - -#if defined(TIMESTAMP_TMPFS) && defined(__linux__) - struct statfs sf; - if (fstatfs(fd, &sf) == -1) - err(1, "statfs"); - - if (sf.f_type != TMPFS_MAGIC) - errx(1, "timestamp directory not on tmpfs"); -#endif - - return fd; + timespecadd(&ts[0], &timeout, &ts[0]); + timespecadd(&ts[1], &timeout, &ts[1]); + return futimens(fd, ts); } /* * Returns 1 if the timestamp is valid, 0 if its invalid */ static int -timestamp_valid(int fd, int secs) +timestamp_check(int fd, int secs) { - struct timespec mono, real, ts_mono, ts_real, timeout; + struct timespec ts[2], timeout = { .tv_sec = secs, .tv_nsec = 0 }; + struct stat st; - if (read(fd, &ts_mono, sizeof ts_mono) != sizeof ts_mono || - read(fd, &ts_real, sizeof ts_real) != sizeof ts_real) - err(1, "read"); - if (!timespecisset(&ts_mono) || !timespecisset(&ts_real)) - errx(1, "timespecisset"); + if (fstat(fd, &st) == -1) + return 0; - if (clock_gettime(CLOCK_MONOTONIC_RAW, &mono) == -1 || - clock_gettime(CLOCK_REALTIME, &real) == -1) - err(1, "clock_gettime"); + if (!timespecisset(&st.st_atim) || !timespecisset(&st.st_mtim)) + return 0; - if (timespeccmp(&mono, &ts_mono, >) || - timespeccmp(&real, &ts_real, >)) + if (clock_gettime(CLOCK_BOOTTIME, &ts[0]) == -1 || + clock_gettime(CLOCK_REALTIME, &ts[1]) == -1) return 0; - memset(&timeout, 0, sizeof timeout); - timeout.tv_sec = secs; - timespecadd(&timeout, &mono, &mono); - timespecadd(&timeout, &real, &real); + /* check if timestamp is too old */ + if (timespeccmp(&st.st_atim, &ts[0], <) || + timespeccmp(&st.st_mtim, &ts[1], <)) + return 0; - if (timespeccmp(&mono, &ts_mono, <) || - timespeccmp(&real, &ts_real, <)) - errx(1, "timestamp is too far in the future"); + /* check if timestamp is too far in the future */ + timespecadd(&ts[0], &timeout, &ts[0]); + timespecadd(&ts[1], &timeout, &ts[1]); + if (timespeccmp(&st.st_atim, &ts[0], >) || + timespeccmp(&st.st_mtim, &ts[1], >)) + return 0; return 1; } -int -timestamp_set(int fd, int secs) -{ - struct timespec mono, real, ts_mono, ts_real, timeout; - - if (clock_gettime(CLOCK_MONOTONIC_RAW, &mono) == -1 || - clock_gettime(CLOCK_REALTIME, &real) == -1) - err(1, "clock_gettime"); - - 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 (ftruncate(fd, 0) == -1) - err(1, "ftruncate"); - 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 timestamp_open(int *valid, int secs) { - struct stat st, stl; - int dirfd, fd; - gid_t gid; - const char *name; + struct timespec ts[2] = {0}; + struct stat st; + int fd; + char path[256]; + int serrno = 0; *valid = 0; - if ((name = timestamp_name()) == NULL) - errx(1, "failed to get timestamp name"); - if ((dirfd = opentsdir()) == -1) - errx(1, "opentsdir"); - - if (fstatat(dirfd, name, &stl, AT_SYMLINK_NOFOLLOW) == -1) { + if (stat(TIMESTAMP_DIR, &st) == -1) { if (errno != ENOENT) - err(1, "fstatat"); - } else if ((stl.st_mode & S_IFMT) != S_IFREG) { - errx(1, "timestamp: not a regular file"); + return -1; + if (mkdir(TIMESTAMP_DIR, 0700) == -1) + return -1; + } else if (st.st_uid != 0 || st.st_mode != (S_IFDIR | 0700)) { + return -1; } - if ((fd = openat(dirfd, name, (O_RDWR), (S_IRUSR|S_IWUSR))) == -1) - if (errno != ENOENT) - err(1, "open timestamp file"); + if (timestamp_path(path, sizeof path) == -1) + return -1; - /* - * If the file was opened, check if fstat and lstat results are - * the same file. - * If the file doesn't exist and we create it with O_CREAT|O_EXCL, - * it is already known that the file is a regular file. - */ - if (fd != -1) { - if (fstat(fd, &st) == -1) - err(1, "stat"); - if (stl.st_ino != st.st_ino || stl.st_dev != st.st_dev) - errx(1, "timestamp file lstat and fstat are different files"); - } else { - if ((fd = openat(dirfd, name, (O_RDWR|O_CREAT|O_EXCL|O_NOFOLLOW), - (S_IRUSR|S_IWUSR))) == -1) - err(1, "open timestamp file"); - if (fstat(fd, &st) == -1) - err(1, "stat"); - } + if (stat(path, &st) != -1 && (st.st_uid != 0 || st.st_gid != getgid()|| st.st_mode != (S_IFREG | 0000))) + return -1; - if ((st.st_mode & (S_IWGRP|S_IRGRP|S_IXGRP|S_IWOTH|S_IROTH|S_IXOTH)) != 0) - errx(1, "timestamp file has wrong permissions"); + fd = open(path, O_RDONLY|O_NOFOLLOW); + if (fd == -1) { + char tmp[256]; + int n; - gid = getegid(); - if (st.st_uid != 0 || st.st_gid != gid) - errx(1, "timestamp file has wrong owner or group"); + n = snprintf(tmp, sizeof tmp, TIMESTAMP_DIR "/.tmp-%d", getpid()); + if (n < 0 || n >= (int)sizeof tmp) + return -1; - /* The timestamp size is 0 if its a new file or a - * timestamp that was never set, its not valid but - * can be used to write the new timestamp. - * If the size does not match the expected size it - * is incomplete and should never be used - */ - if (st.st_size == sizeof (struct timespec) * 2) { - *valid = timestamp_valid(fd, secs); - } else if (st.st_size == 0) { - *valid = 0; + fd = open(tmp, O_RDONLY|O_CREAT|O_EXCL|O_NOFOLLOW, 0000); + if (fd == -1) + return -1; + if (futimens(fd, ts) == -1 || rename(tmp, path) == -1) { + serrno = errno; + close(fd); + unlink(tmp); + errno = serrno; + return -1; + } } else { - errx(1, "corrupt timestamp file"); + *valid = timestamp_check(fd, secs); } - - close(dirfd); return fd; } int timestamp_clear() { - struct dirent *ent; - DIR *dp; - const char *prefix; - int dirfd; - int ret = 0; - size_t plen; - - if ((prefix = session_prefix()) == NULL) - errx(1, "failed to get timestamp session prefix"); - plen = strlen(prefix); - - if ((dirfd = opentsdir()) == -1) - errx(1, "opentsdir"); - if ((dp = fdopendir(dirfd)) == NULL) - err(1, "fopendir"); - - /* - * Delete all files in the timestamp directory - * with the same session prefix. - */ - while ((ent = readdir(dp)) != NULL) { - if (ent->d_type != DT_REG) - continue; - if (strncmp(prefix, ent->d_name, plen) != 0) - continue; - if (unlinkat(dirfd, ent->d_name, 0) == -1) - ret = -1; - } - closedir(dp); - close(dirfd); + char path[256]; - return ret; + if (timestamp_path(path, sizeof path) == -1) + return -1; + if (unlink(path) == -1 && errno != ENOENT) + return -1; + return 0; } From 74449f015ff7a72300be5a27680d2c70af309328 Mon Sep 17 00:00:00 2001 From: Duncan Overbruck Date: Sat, 23 Nov 2019 15:18:44 +0100 Subject: [PATCH 142/198] doas.c: remove dead ifdefs to unclutter code --- doas.c | 230 ++++++++++----------------------------------------------- 1 file changed, 40 insertions(+), 190 deletions(-) diff --git a/doas.c b/doas.c index 70b255b..cbc3657 100644 --- a/doas.c +++ b/doas.c @@ -20,10 +20,6 @@ #include #include -#if __OpenBSD__ -# include -# include -#endif #include #include #include @@ -33,19 +29,16 @@ #include #include #include +#include #include "includes.h" - #include "doas.h" static void __dead usage(void) { - fprintf(stderr, "usage: doas [-Lns] " -#ifdef __OpenBSD__ - "[-a style] " -#endif - "[-C config] [-u user] command [args]\n"); + fprintf(stderr, "usage: doas [-Lns] [-C config] [-u user]" + " command [args]\n"); exit(1); } @@ -204,94 +197,36 @@ checkconfig(const char *confpath, int argc, char **argv, } } -#ifdef USE_BSD_AUTH -static void -authuser(char *myname, char *login_style, int persist) -{ - char *challenge = NULL, *response, rbuf[1024], cbuf[128]; - auth_session_t *as; - int fd = -1; - - if (persist) - fd = open("/dev/tty", O_RDWR); - if (fd != -1) { - if (ioctl(fd, TIOCCHKVERAUTH) == 0) - goto good; - } - - if (!(as = auth_userchallenge(myname, login_style, "auth-doas", - &challenge))) - errx(1, "Authorization failed"); - if (!challenge) { - char host[HOST_NAME_MAX + 1]; - if (gethostname(host, sizeof(host))) - snprintf(host, sizeof(host), "?"); - snprintf(cbuf, sizeof(cbuf), - "\rdoas (%.32s@%.32s) password: ", myname, host); - challenge = cbuf; - } - response = readpassphrase(challenge, rbuf, sizeof(rbuf), - RPP_REQUIRE_TTY); - if (response == NULL && errno == ENOTTY) { - syslog(LOG_AUTHPRIV | LOG_NOTICE, - "tty required for %s", myname); - errx(1, "a tty is required"); - } - if (!auth_userresponse(as, response, 0)) { - explicit_bzero(rbuf, sizeof(rbuf)); - syslog(LOG_AUTHPRIV | LOG_NOTICE, - "failed auth for %s", myname); - errx(1, "Authorization failed"); - } - explicit_bzero(rbuf, sizeof(rbuf)); -good: - if (fd != -1) { - int secs = 5 * 60; - ioctl(fd, TIOCSETVERAUTH, &secs); - close(fd); - } -} -#endif - -#ifdef __OpenBSD__ int -unveilcommands(const char *ipath, const char *cmd) +mygetpwuid_r(uid_t uid, struct passwd *pwd, struct passwd **result) { - char *path = NULL, *p; - int unveils = 0; - - if (strchr(cmd, '/') != NULL) { - if (unveil(cmd, "x") != -1) - unveils++; - goto done; + int rv; + char *buf; + static long pwsz = 0; + size_t buflen; + + if (pwsz == 0) + pwsz = sysconf(_SC_GETPW_R_SIZE_MAX); + + buflen = pwsz > 0 ? pwsz : 1024; + + buf = malloc(buflen); + if (buf == NULL) + return errno; + + while ((rv = getpwuid_r(uid, pwd, buf, buflen, result)) == ERANGE) { + size_t newsz; + newsz = buflen * 2; + if (newsz < buflen) + return rv; + buflen = newsz; + buf = realloc(buf, buflen); + if (buf == NULL) + return errno; } - if (!ipath) { - errno = ENOENT; - goto done; - } - path = strdup(ipath); - if (!path) { - errno = ENOENT; - goto done; - } - for (p = path; p && *p; ) { - char buf[PATH_MAX]; - char *cp = strsep(&p, ":"); - - if (cp) { - int r = snprintf(buf, sizeof buf, "%s/%s", cp, cmd); - if (r >= 0 && r < sizeof buf) { - if (unveil(buf, "x") != -1) - unveils++; - } - } - } -done: - free(path); - return (unveils); + return rv; } -#endif int main(int argc, char **argv) @@ -304,11 +239,6 @@ main(int argc, char **argv) const char *p; const char *cmd; char cmdline[LINE_MAX]; -#ifdef __OpenBSD__ - char mypwbuf[_PW_BUF_LEN], targpwbuf[_PW_BUF_LEN]; -#else - char *mypwbuf = NULL, *targpwbuf = NULL; -#endif struct passwd mypwstore, targpwstore; struct passwd *mypw, *targpw; const struct rule *rule; @@ -322,9 +252,6 @@ main(int argc, char **argv) char cwdpath[PATH_MAX]; const char *cwd; char **envp; -#ifdef USE_BSD_AUTH - char *login_style = NULL; -#endif setprogname("doas"); @@ -332,29 +259,13 @@ main(int argc, char **argv) uid = getuid(); -#ifdef USE_BSD_AUTH -# define OPTSTRING "a:C:Lnsu:" -#else -# define OPTSTRING "+C:Lnsu:" -#endif - - while ((ch = getopt(argc, argv, OPTSTRING)) != -1) { + while ((ch = getopt(argc, argv, "+C:Lnsu:")) != -1) { switch (ch) { -#ifdef USE_BSD_AUTH - case 'a': - login_style = optarg; - break; -#endif case 'C': confpath = optarg; break; case 'L': -#if defined(USE_BSD_AUTH) - i = open("/dev/tty", O_RDWR); - if (i != -1) - ioctl(i, TIOCCLRVERAUTH); - exit(i == -1); -#elif defined(USE_TIMESTAMP) +#if defined(USE_TIMESTAMP) exit(timestamp_clear() == -1); #else exit(0); @@ -383,22 +294,9 @@ main(int argc, char **argv) } else if ((!sflag && !argc) || (sflag && argc)) usage(); -#ifdef __OpenBSD__ - rv = getpwuid_r(uid, &mypwstore, mypwbuf, sizeof(mypwbuf), &mypw); + rv = mygetpwuid_r(uid, &mypwstore, &mypw); if (rv != 0) err(1, "getpwuid_r failed"); -#else - for (size_t sz = 1024; sz <= 16*1024; sz *= 2) { - mypwbuf = reallocarray(mypwbuf, sz, sizeof (char)); - if (mypwbuf == NULL) - errx(1, "can't allocate mypwbuf"); - rv = getpwuid_r(uid, &mypwstore, mypwbuf, sz, &mypw); - if (rv != ERANGE) - break; - } - if (rv != 0) - err(1, "getpwuid_r failed"); -#endif if (mypw == NULL) errx(1, "no passwd entry for self"); ngroups = getgroups(NGROUPS_MAX, groups); @@ -444,101 +342,53 @@ main(int argc, char **argv) errc(1, EPERM, NULL); } -#if defined(__OpenBSD__) || defined(USE_SHADOW) +#if defined(USE_SHADOW) if (!(rule->options & NOPASS)) { if (nflag) errx(1, "Authorization required"); -# ifdef __OpenBSD__ - authuser(mypw->pw_name, login_style, rule->options & PERSIST); -# else shadowauth(mypw->pw_name, rule->options & PERSIST); -# endif } +#elif !defined(USE_PAM) + /* no authentication provider, only allow NOPASS rules */ + (void) nflag; + if (!(rule->options & NOPASS)) + errx(1, "Authorization required"); +#endif if ((p = getenv("PATH")) != NULL) formerpath = strdup(p); if (formerpath == NULL) formerpath = ""; -# ifdef __OpenBSD__ - if (unveil(_PATH_LOGIN_CONF, "r") == -1 || - unveil(_PATH_LOGIN_CONF ".db", "r") == -1) - err(1, "unveil"); -# endif if (rule->cmd) { if (setenv("PATH", safepath, 1) == -1) err(1, "failed to set PATH '%s'", safepath); } -# ifdef __OpenBSD__ - if (unveilcommands(getenv("PATH"), cmd) == 0) - goto fail; - if (pledge("stdio rpath getpw exec id", NULL) == -1) - err(1, "pledge"); -# endif - -#elif !defined(USE_PAM) - (void) nflag; - if (!(rule->options & NOPASS)) { - errx(1, "Authorization required"); - } -#endif /* !(__OpenBSD__ || USE_SHADOW) && !USE_PAM */ - -#ifdef __OpenBSD__ - rv = getpwuid_r(target, &targpwstore, targpwbuf, sizeof(targpwbuf), &targpw); - if (rv != 0) - errx(1, "no passwd entry for target"); -#else - for (size_t sz = 1024; sz <= 16*1024; sz *= 2) { - targpwbuf = reallocarray(targpwbuf, sz, sizeof (char)); - if (targpwbuf == NULL) - errx(1, "can't allocate targpwbuf"); - rv = getpwuid_r(target, &targpwstore, targpwbuf, sz, &targpw); - if (rv != ERANGE) - break; - } + rv = mygetpwuid_r(target, &targpwstore, &targpw); if (rv != 0) err(1, "getpwuid_r failed"); -#endif if (targpw == NULL) - err(1, "getpwuid_r failed"); + errx(1, "no passwd entry for target"); #if defined(USE_PAM) pamauth(targpw->pw_name, mypw->pw_name, !nflag, rule->options & NOPASS, rule->options & PERSIST); #endif -#ifdef HAVE_SETUSERCONTEXT - if (setusercontext(NULL, targpw, target, LOGIN_SETGROUP | - LOGIN_SETPATH | - LOGIN_SETPRIORITY | LOGIN_SETRESOURCES | LOGIN_SETUMASK | - LOGIN_SETUSER) != 0) - errx(1, "failed to set user context for target"); -#else if (setresgid(targpw->pw_gid, targpw->pw_gid, targpw->pw_gid) != 0) err(1, "setresgid"); if (initgroups(targpw->pw_name, targpw->pw_gid) != 0) err(1, "initgroups"); if (setresuid(target, target, target) != 0) err(1, "setresuid"); -#endif - -#ifdef __OpenBSD__ - if (pledge("stdio rpath exec", NULL) == -1) - err(1, "pledge"); -#endif if (getcwd(cwdpath, sizeof(cwdpath)) == NULL) cwd = "(failed)"; else cwd = cwdpath; -#ifdef __OpenBSD__ - if (pledge("stdio exec", NULL) == -1) - err(1, "pledge"); -#endif - syslog(LOG_AUTHPRIV | LOG_INFO, "%s ran command %s as %s from %s", mypw->pw_name, cmdline, targpw->pw_name, cwd); From 84ce5c7f600aa91f53db656c062ed269765146dd Mon Sep 17 00:00:00 2001 From: Duncan Overbruck Date: Sat, 23 Nov 2019 16:21:04 +0100 Subject: [PATCH 143/198] configure: remove version --- configure | 4 ---- 1 file changed, 4 deletions(-) diff --git a/configure b/configure index 2d20f09..4ae9b69 100755 --- a/configure +++ b/configure @@ -74,9 +74,6 @@ done CONFIG_MK=config.mk rm -f "$CONFIG_MK" -# : ${VERSION:="$(git describe --dirty --tags --long --always)"} -: ${VERSION:="6.2"} - cat <>$CONFIG_MK PREFIX ?= ${PREFIX:="/usr"} EPREFIX ?= ${EPREFIX:="${PREFIX}"} @@ -85,7 +82,6 @@ SHAREDIR ?= ${SHAREDIR:="${PREFIX}/share"} MANDIR ?= ${MANDIR:="${SHAREDIR}/man"} SYSCONFDIR?= ${SYSCONFDIR:="/etc"} PAMDIR ?= ${PAMDIR:="${SYSCONFDIR}/pam.d"} -CFLAGS += -DVERSION="\"${VERSION}\"" EOF if [ -z "$BUILD" ]; then From 1fae30ee0ec3b4c7431c291ca8f63abbf67bff89 Mon Sep 17 00:00:00 2001 From: Ivy Foster Date: Wed, 8 Jan 2020 11:33:51 -0600 Subject: [PATCH 144/198] Change binary permissions to 4755. Closes #26 The owner can be trusted to read and write their own files, and there's no reason not to let others read the file. --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 9cca8ca..c0ddb1a 100644 --- a/Makefile +++ b/Makefile @@ -7,7 +7,7 @@ MAN= doas.1 doas.conf.5 BINOWN= root BINGRP= root -BINMODE=4111 +BINMODE=4755 CFLAGS+= -I${CURDIR} COPTS+= -Wall -Wextra -Werror -pedantic From 5dc1cdecb149196c944c3a0983051eeae6c2b3a0 Mon Sep 17 00:00:00 2001 From: Duncan Overbruck Date: Fri, 6 Dec 2019 02:43:57 +0100 Subject: [PATCH 145/198] libopenbsd: define __dead as noreturn --- includes.h | 8 -------- libopenbsd/errc.c | 2 +- libopenbsd/openbsd.h | 12 ++++++++++-- libopenbsd/verrc.c | 2 +- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/includes.h b/includes.h index d4967e0..401a0e4 100644 --- a/includes.h +++ b/includes.h @@ -1,14 +1,6 @@ #ifndef INCLUDES_H #define INCLUDES_H -#ifndef __UNUSED -# define __UNUSED __attribute__ ((unused)) -#endif - -#ifndef __dead -# define __dead -#endif - #ifndef _PATH_TTY # define _PATH_TTY "/dev/tty" #endif diff --git a/libopenbsd/errc.c b/libopenbsd/errc.c index 37eff0f..b82d419 100644 --- a/libopenbsd/errc.c +++ b/libopenbsd/errc.c @@ -33,7 +33,7 @@ #include #include -__dead void +void __dead errc(int eval, int code, const char *fmt, ...) { va_list ap; diff --git a/libopenbsd/openbsd.h b/libopenbsd/openbsd.h index 793d9bf..da420de 100644 --- a/libopenbsd/openbsd.h +++ b/libopenbsd/openbsd.h @@ -4,6 +4,14 @@ #include #include +#ifndef __UNUSED +# define __UNUSED __attribute__ ((unused)) +#endif + +#ifndef __dead +# define __dead __attribute__ ((noreturn)) +#endif + /* API definitions lifted from OpenBSD src/include */ /* pwd.h */ @@ -45,10 +53,10 @@ void closefrom(int); /* err.h */ #ifndef HAVE_VERRC -void verrc(int eval, int code, const char *fmt, va_list ap); +void __dead verrc(int eval, int code, const char *fmt, va_list ap); #endif /* !HAVE_VERRC */ #ifndef HAVE_ERRC -void errc(int eval, int code, const char *fmt, ...); +__dead void errc(int eval, int code, const char *fmt, ...); #endif /* !HAVE_ERRC */ #ifndef HAVE_SETPROGNAME diff --git a/libopenbsd/verrc.c b/libopenbsd/verrc.c index 15400bc..c155a92 100644 --- a/libopenbsd/verrc.c +++ b/libopenbsd/verrc.c @@ -36,7 +36,7 @@ #include #include -__dead void +void __dead verrc(int eval, int code, const char *fmt, va_list ap) { (void)fprintf(stderr, "%s: ", getprogname()); From 22370cb3e35eca03455a38284c70ad1ad4eb2f4b Mon Sep 17 00:00:00 2001 From: Duncan Overbruck Date: Fri, 6 Dec 2019 02:44:46 +0100 Subject: [PATCH 146/198] doas.c: initialize mygetpwuid_r result This can't happen really happen, but makes scan-build happy. --- doas.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doas.c b/doas.c index cbc3657..e253905 100644 --- a/doas.c +++ b/doas.c @@ -205,6 +205,8 @@ mygetpwuid_r(uid_t uid, struct passwd *pwd, struct passwd **result) static long pwsz = 0; size_t buflen; + *result = NULL; + if (pwsz == 0) pwsz = sysconf(_SC_GETPW_R_SIZE_MAX); From 78c1c0f2a99df80678fbff77911b498c5311dcc0 Mon Sep 17 00:00:00 2001 From: Duncan Overbruck Date: Fri, 6 Dec 2019 02:45:22 +0100 Subject: [PATCH 147/198] timestamp.c: already return on 22th field of /proc/ppid/stat this is the last field we are interested in and if we didn't reach it, return an error. --- timestamp.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/timestamp.c b/timestamp.c index e97c92f..c90318d 100644 --- a/timestamp.c +++ b/timestamp.c @@ -141,13 +141,11 @@ proc_info(pid_t pid, int *ttynr, unsigned long long *starttime) if (p == ep || (errno == ERANGE && *starttime == ULLONG_MAX)) return -1; - break; + return 0; } - if (n == 23) - break; } - return 0; + return -1; } #else #error "proc_info not implemented" From 05f977774dd985b3aa5f5a6e43af0360493c007e Mon Sep 17 00:00:00 2001 From: Duncan Overbruck Date: Mon, 3 Feb 2020 22:11:34 +0100 Subject: [PATCH 148/198] timestamp.c: add some more error/warning messages This might help to identify bugs/misbehaving systems or attempts to mess with timestamp files. --- timestamp.c | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/timestamp.c b/timestamp.c index c90318d..38c89ff 100644 --- a/timestamp.c +++ b/timestamp.c @@ -98,13 +98,16 @@ proc_info(pid_t pid, int *ttynr, unsigned long long *starttime) if (n < 0 || n >= (int)sizeof path) return -1; - if ((fd = open(path, O_RDONLY)) == -1) + if ((fd = open(path, O_RDONLY)) == -1) { + warn("failed to open: %s", path); return -1; + } while ((n = read(fd, p, buf + sizeof buf - p)) != 0) { if (n == -1) { if (errno == EAGAIN || errno == EINTR) continue; + warn("read: %s", path); close(fd); return -1; } @@ -115,7 +118,8 @@ proc_info(pid_t pid, int *ttynr, unsigned long long *starttime) close(fd); /* error if it contains NULL bytes */ - if (n != 0 || memchr(buf, '\0', p - buf)) + if (n != 0 || memchr(buf, '\0', p - buf)) { + warn("NUL in: %s", path); return -1; /* Get the 7th field, 5 fields after the last ')', @@ -194,14 +198,18 @@ timestamp_check(int fd, int secs) struct stat st; if (fstat(fd, &st) == -1) - return 0; + err(1, "fstat"); - if (!timespecisset(&st.st_atim) || !timespecisset(&st.st_mtim)) + if (!timespecisset(&st.st_atim) || !timespecisset(&st.st_mtim)) { + warnx("timestamp atim or mtime not set"); return 0; + } if (clock_gettime(CLOCK_BOOTTIME, &ts[0]) == -1 || - clock_gettime(CLOCK_REALTIME, &ts[1]) == -1) + clock_gettime(CLOCK_REALTIME, &ts[1]) == -1) { + warn("clock_gettime"); return 0; + } /* check if timestamp is too old */ if (timespeccmp(&st.st_atim, &ts[0], <) || @@ -212,8 +220,10 @@ timestamp_check(int fd, int secs) timespecadd(&ts[0], &timeout, &ts[0]); timespecadd(&ts[1], &timeout, &ts[1]); if (timespeccmp(&st.st_atim, &ts[0], >) || - timespeccmp(&st.st_mtim, &ts[1], >)) + timespeccmp(&st.st_mtim, &ts[1], >)) { + warnx("timestamp too far in the future"); return 0; + } return 1; } @@ -249,6 +259,9 @@ timestamp_open(int *valid, int secs) char tmp[256]; int n; + if (errno != ENOENT) + err(1, "open: %s", path); + n = snprintf(tmp, sizeof tmp, TIMESTAMP_DIR "/.tmp-%d", getpid()); if (n < 0 || n >= (int)sizeof tmp) return -1; From 5debef098b7ebba67da5db9fbb020a7cd0f90a7f Mon Sep 17 00:00:00 2001 From: Duncan Overbruck Date: Mon, 3 Feb 2020 22:17:43 +0100 Subject: [PATCH 149/198] timestamp.c: correctly NUL terminate buffer read from /proc/pid/stat This solves buf #28. --- timestamp.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/timestamp.c b/timestamp.c index 38c89ff..75a3af7 100644 --- a/timestamp.c +++ b/timestamp.c @@ -98,12 +98,12 @@ proc_info(pid_t pid, int *ttynr, unsigned long long *starttime) if (n < 0 || n >= (int)sizeof path) return -1; - if ((fd = open(path, O_RDONLY)) == -1) { + if ((fd = open(path, O_RDONLY|O_NOFOLLOW)) == -1) { warn("failed to open: %s", path); return -1; } - while ((n = read(fd, p, buf + sizeof buf - p)) != 0) { + while ((n = read(fd, p, buf + (sizeof buf - 1) - p)) != 0) { if (n == -1) { if (errno == EAGAIN || errno == EINTR) continue; @@ -112,15 +112,18 @@ proc_info(pid_t pid, int *ttynr, unsigned long long *starttime) return -1; } p += n; - if (p >= buf + sizeof buf) + if (p >= buf + (sizeof buf - 1)) break; } close(fd); /* error if it contains NULL bytes */ - if (n != 0 || memchr(buf, '\0', p - buf)) { + if (n != 0 || memchr(buf, '\0', p - buf - 1) != NULL) { warn("NUL in: %s", path); return -1; + } + + *p = '\0'; /* Get the 7th field, 5 fields after the last ')', * (2th field) because the 5th field 'comm' can include From 31d95b9c3339af50f0e6758a1e3a66d846e5145d Mon Sep 17 00:00:00 2001 From: Duncan Overbruck Date: Mon, 3 Feb 2020 22:19:45 +0100 Subject: [PATCH 150/198] timestamp.c: check fstat(2) instead of separate stat(2) --- timestamp.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/timestamp.c b/timestamp.c index 75a3af7..c7edb56 100644 --- a/timestamp.c +++ b/timestamp.c @@ -202,6 +202,8 @@ timestamp_check(int fd, int secs) if (fstat(fd, &st) == -1) err(1, "fstat"); + if (st.st_uid != 0 || st.st_gid != getgid() || st.st_mode != (S_IFREG | 0000)) + errx(1, "timestamp uid, gid or mode wrong"); if (!timespecisset(&st.st_atim) || !timespecisset(&st.st_mtim)) { warnx("timestamp atim or mtime not set"); @@ -254,9 +256,6 @@ timestamp_open(int *valid, int secs) if (timestamp_path(path, sizeof path) == -1) return -1; - if (stat(path, &st) != -1 && (st.st_uid != 0 || st.st_gid != getgid()|| st.st_mode != (S_IFREG | 0000))) - return -1; - fd = open(path, O_RDONLY|O_NOFOLLOW); if (fd == -1) { char tmp[256]; From 50a47d3b7e587fabcce85bc5af10450a3707821a Mon Sep 17 00:00:00 2001 From: Duncan Overbruck Date: Mon, 3 Feb 2020 22:26:55 +0100 Subject: [PATCH 151/198] timestamp.c: remove warning for normal case --- timestamp.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/timestamp.c b/timestamp.c index c7edb56..6b4354c 100644 --- a/timestamp.c +++ b/timestamp.c @@ -205,10 +205,9 @@ timestamp_check(int fd, int secs) if (st.st_uid != 0 || st.st_gid != getgid() || st.st_mode != (S_IFREG | 0000)) errx(1, "timestamp uid, gid or mode wrong"); - if (!timespecisset(&st.st_atim) || !timespecisset(&st.st_mtim)) { - warnx("timestamp atim or mtime not set"); + /* this timestamp was created but never set, invalid but no error */ + if (!timespecisset(&st.st_atim) || !timespecisset(&st.st_mtim)) return 0; - } if (clock_gettime(CLOCK_BOOTTIME, &ts[0]) == -1 || clock_gettime(CLOCK_REALTIME, &ts[1]) == -1) { From 84ccfe0b9ac591199d0464ede10a4130c8a3482d Mon Sep 17 00:00:00 2001 From: Sunil Nimmagadda Date: Mon, 2 Nov 2020 11:54:29 +0530 Subject: [PATCH 152/198] Honor --sysconfdir option for doas.conf path. Some distributions may choose to place configuration files in a different directory than /etc. The configure script provides --sysconfdir option already, use it to find doas.conf path instead of hardcoding '/etc/doas.conf'. --- bsd.prog.mk | 2 ++ doas.c | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/bsd.prog.mk b/bsd.prog.mk index df8f514..ba6c141 100644 --- a/bsd.prog.mk +++ b/bsd.prog.mk @@ -6,6 +6,8 @@ CFLAGS += -I${CURDIR}/libopenbsd ${COPTS} -MD -MP -Wno-unused-result include config.mk +CFLAGS += -DDOAS_CONF="\"${SYSCONFDIR}/doas.conf\"" + OPENBSD := $(addprefix libopenbsd/,${OPENBSD}) OBJS := ${SRCS:.y=.c} OBJS := ${OBJS:.c=.o} diff --git a/doas.c b/doas.c index e253905..008132b 100644 --- a/doas.c +++ b/doas.c @@ -325,7 +325,7 @@ main(int argc, char **argv) if (geteuid()) errx(1, "not installed setuid"); - parseconfig("/etc/doas.conf", 1); + parseconfig(DOAS_CONF, 1); /* cmdline is used only for logging, no need to abort on truncate */ (void)strlcpy(cmdline, argv[0], sizeof(cmdline)); From b1ae418af40289ea4f40449a819df26ee3bdd399 Mon Sep 17 00:00:00 2001 From: Sunil Nimmagadda Date: Thu, 5 Nov 2020 12:33:09 +0530 Subject: [PATCH 153/198] Fallback definition for HOST_NAME_MAX. On some platforms(NetBSD) where HOST_NAME_MAX is not defined, provide a fallback definition to _POSIX_HOST_NAME_MAX. --- pam.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pam.c b/pam.c index c5a3001..ccebd0f 100644 --- a/pam.c +++ b/pam.c @@ -271,6 +271,9 @@ pamauth(const char *user, const char *myname, int interactive, int nopass, int p if (!interactive) errx(1, "Authorization required"); +#ifndef HOST_NAME_MAX +#define HOST_NAME_MAX _POSIX_HOST_NAME_MAX +#endif /* doas style prompt for pam */ char host[HOST_NAME_MAX + 1]; if (gethostname(host, sizeof(host))) From 25b1f36e92c75983b5c02380836db8b97eb7237f Mon Sep 17 00:00:00 2001 From: schwarze Date: Mon, 10 Feb 2020 13:18:20 +0000 Subject: [PATCH 154/198] briefly mention /etc/examples/ in the FILES section of all the manual pages that document the corresponding configuration files; OK jmc@, and general direction discussed with many --- doas.conf.5 | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/doas.conf.5 b/doas.conf.5 index 54022ee..007dd30 100644 --- a/doas.conf.5 +++ b/doas.conf.5 @@ -111,9 +111,12 @@ If quotes or backslashes are used in a word, it isn't considered a keyword. .El .Sh FILES -.Bl -tag -width "/etc/doas.conf" +.Bl -tag -width /etc/examples/doas.conf -compact .It Pa /etc/doas.conf -doas configuration file. +.Xr doas 1 +configuration file +.It Pa /etc/examples/doas.conf +example configuration file .El .Sh EXAMPLES The following example permits user aja to install packages From 1ae55876daaf1d2d02e6261db7b6dd08c41b8fcb Mon Sep 17 00:00:00 2001 From: jmc Date: Sat, 16 May 2020 16:58:11 +0000 Subject: [PATCH 155/198] list example files in FILES with a short description: generally, "Example configuration file.", but occasionally something else fit better; at the same time, try to make the format for FILES more consistent; original diff from clematis --- doas.conf.5 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doas.conf.5 b/doas.conf.5 index 007dd30..a14e778 100644 --- a/doas.conf.5 +++ b/doas.conf.5 @@ -114,9 +114,9 @@ it isn't considered a keyword. .Bl -tag -width /etc/examples/doas.conf -compact .It Pa /etc/doas.conf .Xr doas 1 -configuration file +configuration file. .It Pa /etc/examples/doas.conf -example configuration file +Example configuration file. .El .Sh EXAMPLES The following example permits user aja to install packages From 7441dfc4da8c9474ee767464dabe73ca90ff42ae Mon Sep 17 00:00:00 2001 From: kn Date: Fri, 9 Oct 2020 00:04:05 +0000 Subject: [PATCH 156/198] Improve error message on missing permission In case "cmd" (and "args") in doas.conf(5) mismatch, the log syslog(3) message might be read as if the command was executed but failed, i.e. returned non-zero. Be unambiguous and help admins spot execution *attempts* as such: -Oct 9 01:05:20 eru doas: failed command for kn: echo bar +Oct 9 01:05:20 eru doas: command not permitted for kn: echo bar OK tedu deraadt --- doas.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doas.c b/doas.c index 008132b..8275fe1 100644 --- a/doas.c +++ b/doas.c @@ -340,7 +340,7 @@ main(int argc, char **argv) if (!permit(uid, groups, ngroups, &rule, target, cmd, (const char **)argv + 1)) { syslog(LOG_AUTHPRIV | LOG_NOTICE, - "failed command for %s: %s", mypw->pw_name, cmdline); + "command not permitted for %s: %s", mypw->pw_name, cmdline); errc(1, EPERM, NULL); } From 1530e7aa087276696db7d321a1b9164a49ddea4b Mon Sep 17 00:00:00 2001 From: kn Date: Fri, 9 Oct 2020 07:43:38 +0000 Subject: [PATCH 157/198] Add nolog option to avoid syslog(3) doas(1) unconditionally logs all executions but syslog.conf(5) provides no means to filter messages by user, target or command. Add the "nolog" option to doas.conf(5) such that syslog becomes an opt-out feature; this keeps configuration simple enough yet powerful since rule definition is the best place to decide whether to log commands or not on a per rule basis - this also aoids duplicating information or logic in any other log processing tool. OK tedu martijn --- doas.c | 7 +++++-- doas.conf.5 | 4 ++++ doas.h | 1 + parse.y | 6 +++++- 4 files changed, 15 insertions(+), 3 deletions(-) diff --git a/doas.c b/doas.c index 8275fe1..dea68f8 100644 --- a/doas.c +++ b/doas.c @@ -391,8 +391,11 @@ main(int argc, char **argv) else cwd = cwdpath; - syslog(LOG_AUTHPRIV | LOG_INFO, "%s ran command %s as %s from %s", - mypw->pw_name, cmdline, targpw->pw_name, cwd); + if (!(rule->options & NOLOG)) { + syslog(LOG_AUTHPRIV | LOG_INFO, + "%s ran command %s as %s from %s", + mypw->pw_name, cmdline, targpw->pw_name, cwd); + } envp = prepenv(rule, mypw, targpw); diff --git a/doas.conf.5 b/doas.conf.5 index a14e778..ce66565 100644 --- a/doas.conf.5 +++ b/doas.conf.5 @@ -45,6 +45,9 @@ Options are: .Bl -tag -width keepenv .It Ic nopass The user is not required to enter a password. +.It Ic nolog +Do not log successful command execution to +.Xr syslogd 8 . .It Ic persist After the user successfully authenticates, do not ask for a password again for some time. @@ -140,6 +143,7 @@ permit nopass keepenv setenv { PATH } root as root .Ed .Sh SEE ALSO .Xr doas 1 +.Xr syslogd 8 .Sh HISTORY The .Nm diff --git a/doas.h b/doas.h index 4a117be..de8dbe1 100644 --- a/doas.h +++ b/doas.h @@ -42,3 +42,4 @@ char **prepenv(const struct rule *, const struct passwd *, #define NOPASS 0x1 #define KEEPENV 0x2 #define PERSIST 0x4 +#define NOLOG 0x8 diff --git a/parse.y b/parse.y index e4a041a..15c00c1 100644 --- a/parse.y +++ b/parse.y @@ -73,7 +73,7 @@ arraylen(const char **arr) %} %token TPERMIT TDENY TAS TCMD TARGS -%token TNOPASS TPERSIST TKEEPENV TSETENV +%token TNOPASS TNOLOG TPERSIST TKEEPENV TSETENV %token TSTRING %% @@ -139,6 +139,9 @@ options: /* none */ { option: TNOPASS { $$.options = NOPASS; $$.envlist = NULL; + } | TNOLOG { + $$.options = NOLOG; + $$.envlist = NULL; } | TPERSIST { $$.options = PERSIST; $$.envlist = NULL; @@ -212,6 +215,7 @@ static struct keyword { { "cmd", TCMD }, { "args", TARGS }, { "nopass", TNOPASS }, + { "nolog", TNOLOG }, { "persist", TPERSIST }, { "keepenv", TKEEPENV }, { "setenv", TSETENV }, From bfea01b8d21291919d2a0e1f2568de1834e67fd0 Mon Sep 17 00:00:00 2001 From: jmc Date: Fri, 9 Oct 2020 10:24:33 +0000 Subject: [PATCH 158/198] fix SEE ALSO; --- doas.conf.5 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doas.conf.5 b/doas.conf.5 index ce66565..461ef3f 100644 --- a/doas.conf.5 +++ b/doas.conf.5 @@ -142,7 +142,7 @@ permit nopass tedu as root cmd /usr/sbin/procmap permit nopass keepenv setenv { PATH } root as root .Ed .Sh SEE ALSO -.Xr doas 1 +.Xr doas 1 , .Xr syslogd 8 .Sh HISTORY The From fd03103c045c958756f5f3f03f13604bd80eaa1d Mon Sep 17 00:00:00 2001 From: Duncan Overbruck Date: Thu, 5 Nov 2020 21:00:16 +0100 Subject: [PATCH 159/198] check for login_cap.h and use setusercontext if available --- configure | 11 +++++++++++ doas.c | 10 ++++++++++ 2 files changed, 21 insertions(+) diff --git a/configure b/configure index 4ae9b69..bd1b94f 100755 --- a/configure +++ b/configure @@ -451,6 +451,17 @@ int main(void) { }' check_func "ndir_h" "$src" +# +# Check for login_cap.h. +# +src=' +#include +#include +int main(void) { + return 0; +}' +check_func "login_cap_h" "$src" + # # # diff --git a/doas.c b/doas.c index dea68f8..c95dee3 100644 --- a/doas.c +++ b/doas.c @@ -20,6 +20,9 @@ #include #include +#ifdef HAVE_LOGIN_CAP_H +#include +#endif #include #include #include @@ -379,12 +382,19 @@ main(int argc, char **argv) rule->options & PERSIST); #endif +#ifdef HAVE_LOGIN_CAP_H + if (setusercontext(NULL, targpw, target, LOGIN_SETGROUP | + LOGIN_SETPRIORITY | LOGIN_SETRESOURCES | LOGIN_SETUMASK | + LOGIN_SETUSER) != 0) + errx(1, "failed to set user context for target"); +#else if (setresgid(targpw->pw_gid, targpw->pw_gid, targpw->pw_gid) != 0) err(1, "setresgid"); if (initgroups(targpw->pw_name, targpw->pw_gid) != 0) err(1, "initgroups"); if (setresuid(target, target, target) != 0) err(1, "setresuid"); +#endif if (getcwd(cwdpath, sizeof(cwdpath)) == NULL) cwd = "(failed)"; From 46a5abc4ac149c98f7a67ef953b5d48710fad110 Mon Sep 17 00:00:00 2001 From: Duncan Overbruck Date: Thu, 12 Nov 2020 14:11:05 +0100 Subject: [PATCH 160/198] move HOST_NAME_MAX to the top and add it to shadow.c --- pam.c | 7 ++++--- shadow.c | 4 ++++ 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/pam.c b/pam.c index ccebd0f..1fc6082 100644 --- a/pam.c +++ b/pam.c @@ -37,6 +37,10 @@ #include "includes.h" +#ifndef HOST_NAME_MAX +#define HOST_NAME_MAX _POSIX_HOST_NAME_MAX +#endif + #define PAM_SERVICE_NAME "doas" static pam_handle_t *pamh = NULL; @@ -271,9 +275,6 @@ pamauth(const char *user, const char *myname, int interactive, int nopass, int p if (!interactive) errx(1, "Authorization required"); -#ifndef HOST_NAME_MAX -#define HOST_NAME_MAX _POSIX_HOST_NAME_MAX -#endif /* doas style prompt for pam */ char host[HOST_NAME_MAX + 1]; if (gethostname(host, sizeof(host))) diff --git a/shadow.c b/shadow.c index 71c71c6..64cc1f7 100644 --- a/shadow.c +++ b/shadow.c @@ -18,6 +18,10 @@ #include "openbsd.h" +#ifndef HOST_NAME_MAX +#define HOST_NAME_MAX _POSIX_HOST_NAME_MAX +#endif + void shadowauth(const char *myname, int persist) { From 4daae4a9009b1340e4887d42974a0b0a50ae6c43 Mon Sep 17 00:00:00 2001 From: Sunil Nimmagadda Date: Thu, 12 Nov 2020 18:02:47 +0530 Subject: [PATCH 161/198] opendoas: Fallback for setresuid(2). This approach borrows from openssh-portable. The bsd-setres_id.c is adapted with openssh-portable specific bits (log.h inclusion and error() function) removed. Closes: #40 [via git-merge-pr] --- configure | 4 +- libopenbsd/bsd-setres_id.c | 79 ++++++++++++++++++++++++++++++++++++++ libopenbsd/openbsd.h | 7 ++++ 3 files changed, 89 insertions(+), 1 deletion(-) create mode 100644 libopenbsd/bsd-setres_id.c diff --git a/configure b/configure index bd1b94f..679fe68 100755 --- a/configure +++ b/configure @@ -340,7 +340,9 @@ int main(void) { setresuid(0, 0, 0); return 0; }' -check_func "setresuid" "$src" || die "system has no setresuid(2): not supported" +check_func "setresuid" "$src" || { + printf 'OPENBSD += bsd-setres_id.o\n' >>$CONFIG_MK +} # # Check for closefrom(). diff --git a/libopenbsd/bsd-setres_id.c b/libopenbsd/bsd-setres_id.c new file mode 100644 index 0000000..f36d3eb --- /dev/null +++ b/libopenbsd/bsd-setres_id.c @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2012 Darren Tucker (dtucker at zip com au). + * + * 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 "includes.h" + +#include + +#include +#include +#include +#include + +#if !defined(HAVE_SETRESGID) || defined(BROKEN_SETRESGID) +int +setresgid(gid_t rgid, gid_t egid, gid_t sgid) +{ + int ret = 0; + + if (rgid != sgid) { + errno = ENOSYS; + return -1; + } +#if defined(HAVE_SETREGID) && !defined(BROKEN_SETREGID) + if (setregid(rgid, egid) < 0) { + ret = -1; + } +#else + if (setegid(egid) < 0) { + ret = -1; + } + if (setgid(rgid) < 0) { + ret = -1; + } +#endif + return ret; +} +#endif + +#if !defined(HAVE_SETRESUID) || defined(BROKEN_SETRESUID) +int +setresuid(uid_t ruid, uid_t euid, uid_t suid) +{ + int ret = 0; + + if (ruid != suid) { + errno = ENOSYS; + return -1; + } +#if defined(HAVE_SETREUID) && !defined(BROKEN_SETREUID) + if (setreuid(ruid, euid) < 0) { + ret = -1; + } +#else + +# ifndef SETEUID_BREAKS_SETUID + if (seteuid(euid) < 0) { + ret = -1; + } +# endif + if (setuid(ruid) < 0) { + ret = -1; + } +#endif + return ret; +} +#endif diff --git a/libopenbsd/openbsd.h b/libopenbsd/openbsd.h index da420de..d3bdc0b 100644 --- a/libopenbsd/openbsd.h +++ b/libopenbsd/openbsd.h @@ -64,4 +64,11 @@ const char * getprogname(void); void setprogname(const char *progname); #endif /* !HAVE_SETPROGNAME */ +#ifndef HAVE_SETRESGID +int setresgid(gid_t, gid_t, gid_t); +#endif +#ifndef HAVE_SETRESUID +int setresuid(uid_t, uid_t, uid_t); +#endif + #endif /* _LIB_OPENBSD_H_ */ From a3264b9072fa61ac1a834445d6b2d80860f58435 Mon Sep 17 00:00:00 2001 From: Duncan Overbruck Date: Thu, 12 Nov 2020 16:11:40 +0100 Subject: [PATCH 162/198] use config.h and link objects instead of libopenbsd.a --- .gitignore | 1 + bsd.prog.mk | 13 ++----- configure | 70 ++++++++++++++++++++----------------- doas.c | 2 ++ env.c | 2 ++ libopenbsd/bsd-setres_id.c | 2 +- libopenbsd/closefrom.c | 2 ++ libopenbsd/errc.c | 4 ++- libopenbsd/explicit_bzero.c | 2 ++ libopenbsd/openbsd.h | 3 -- libopenbsd/progname.c | 2 ++ libopenbsd/readpassphrase.c | 2 +- libopenbsd/reallocarray.c | 2 ++ libopenbsd/strlcat.c | 2 ++ libopenbsd/strlcpy.c | 2 ++ libopenbsd/strtonum.c | 2 ++ libopenbsd/verrc.c | 4 ++- pam.c | 2 ++ parse.y | 2 ++ shadow.c | 18 ++++++++++ timestamp.c | 18 ++++++++++ 21 files changed, 106 insertions(+), 51 deletions(-) diff --git a/.gitignore b/.gitignore index fe3a7aa..a5224e9 100644 --- a/.gitignore +++ b/.gitignore @@ -11,3 +11,4 @@ version.h *.swo config.mk +config.h diff --git a/bsd.prog.mk b/bsd.prog.mk index ba6c141..6441fd8 100644 --- a/bsd.prog.mk +++ b/bsd.prog.mk @@ -6,16 +6,10 @@ CFLAGS += -I${CURDIR}/libopenbsd ${COPTS} -MD -MP -Wno-unused-result include config.mk -CFLAGS += -DDOAS_CONF="\"${SYSCONFDIR}/doas.conf\"" - -OPENBSD := $(addprefix libopenbsd/,${OPENBSD}) OBJS := ${SRCS:.y=.c} OBJS := ${OBJS:.c=.o} -libopenbsd.a: ${OPENBSD} - ${AR} -r $@ $? - -${PROG}: ${OBJS} libopenbsd.a +${PROG}: ${OBJS} ${CC} ${CFLAGS} $^ -o $@ ${LDFLAGS} install: ${PROG} ${PAM_DOAS} ${MAN} @@ -38,14 +32,11 @@ uninstall: rm -f ${DESTDIR}${MANDIR}/man5/doas.conf.5 clean: - rm -f libopenbsd.a - rm -f ${OPENBSD} - rm -f ${OPENBSD:.o=.d} rm -f ${OBJS} rm -f ${OBJS:.o=.d} rm -f ${PROG} rm -f parse.c --include ${OBJS:.o=.d} ${OPENBSD:.o=.d} +-include ${OBJS:.o=.d} .PHONY: default clean install uninstall diff --git a/configure b/configure index 679fe68..f9c2754 100755 --- a/configure +++ b/configure @@ -72,7 +72,14 @@ for x; do done CONFIG_MK=config.mk -rm -f "$CONFIG_MK" +CONFIG_H=config.h +rm -f "$CONFIG_MK" "$CONFIG_H" + +cat <$CONFIG_H +#ifndef CONFIG_H +#define CONFIG_H + +! cat <>$CONFIG_MK PREFIX ?= ${PREFIX:="/usr"} @@ -111,10 +118,12 @@ OS_CFLAGS="-D__${OS}__" case "$OS" in linux) - OS_CFLAGS="$OS_CFLAGS -D_DEFAULT_SOURCE -D_GNU_SOURCE -DUID_MAX=${UID_MAX} -DGID_MAX=${GID_MAX}" + printf 'Setting UID_MAX\t\t\t\t%d.\n' "$UID_MAX" >&2 + printf '#define UID_MAX %s\n' "$UID_MAX" >>$CONFIG_H + printf 'Setting GID_MAX\t\t\t\t%d.\n' "$GID_MAX" >&2 + printf '#define GID_MAX %s\n' "$GID_MAX" >>$CONFIG_H + OS_CFLAGS="$OS_CFLAGS -D_DEFAULT_SOURCE -D_GNU_SOURCE" printf 'CURDIR := .\n' >>$CONFIG_MK - [ -z "$WITHOUT_PAM" ] && \ - printf 'PAM_DOAS = pam.d__doas__linux\n' >>$CONFIG_MK ;; esac @@ -139,12 +148,13 @@ check_func() { $XCC "_$func.c" -o "_$func" 2>/dev/null ret=$? rm -f "_$func.c" "_$func" + upperfunc="$(printf '%s\n' "$func" | tr '[[:lower:]]' '[[:upper:]]')" if [ $ret -eq 0 ]; then printf 'yes.\n' >&2 - upperfunc="$(printf '%s\n' "$func" | tr '[[:lower:]]' '[[:upper:]]')" - printf 'CFLAGS += -DHAVE_%s\n' "$upperfunc" >>$CONFIG_MK + printf '#define HAVE_%s\n' "$upperfunc" >>$CONFIG_H return 0 else + printf '/* #define HAVE_%s */\n' "$upperfunc" >>$CONFIG_H printf 'no.\n' >&2 return 1 fi @@ -162,8 +172,11 @@ int main(void) { [ -z "$WITHOUT_PAM" ] && check_func "pam_appl_h" "$src" && { printf 'SRCS += pam.c\n' >>$CONFIG_MK printf 'LDFLAGS += -lpam\n' >>$CONFIG_MK - printf 'CPPFLAGS += -DUSE_PAM\n' >>$CONFIG_MK + printf '#define USE_PAM\n' >>$CONFIG_H printf 'pam\n' + + pam_file="pam.d__doas__${OS}" + [ -e "$pam_file" ] && printf 'PAM_DOAS = %s\n' "$pam_file" >>$CONFIG_MK return 0 } @@ -178,7 +191,7 @@ int main(void) { [ -z "$WITHOUT_SHADOW" ] && check_func "shadow_h" "$src" && { printf 'SRCS += shadow.c\n' >>$CONFIG_MK printf 'LDFLAGS += -lcrypt\n' >>$CONFIG_MK - printf 'CPPFLAGS += -DUSE_SHADOW\n' >>$CONFIG_MK + printf '#define USE_SHADOW\n' >>$CONFIG_H printf 'shadow\n' return 0 } @@ -188,7 +201,7 @@ int main(void) { persistmethod() { [ -z "$WITHOUT_TIMESTAMP" ] && { - printf 'CPPFLAGS += -DUSE_TIMESTAMP\n' >>$CONFIG_MK + printf '#define USE_TIMESTAMP\n' >>$CONFIG_H printf 'SRCS += timestamp.c\n' >>$CONFIG_MK printf 'timestamp\n' return 0 @@ -206,7 +219,7 @@ int main(void) { return 0; }' check_func "explicit_bzero" "$src" || { - printf 'OPENBSD += explicit_bzero.o\n' >>$CONFIG_MK + printf 'SRCS += libopenbsd/explicit_bzero.c\n' >>$CONFIG_MK } # @@ -221,7 +234,7 @@ int main(void) { return 0; }' check_func "strlcat" "$src" || { - printf 'OPENBSD += strlcat.o\n' >>$CONFIG_MK + printf 'SRCS += libopenbsd/strlcat.c\n' >>$CONFIG_MK } # @@ -236,7 +249,7 @@ int main(void) { return 0; }' check_func "strlcpy" "$src" || { - printf 'OPENBSD += strlcpy.o\n' >>$CONFIG_MK + printf 'SRCS += libopenbsd/strlcpy.c\n' >>$CONFIG_MK } # @@ -249,7 +262,7 @@ int main(void) { return 0; }' check_func "errc" "$src" || { - printf 'OPENBSD += errc.o\n' >>$CONFIG_MK + printf 'SRCS += libopenbsd/errc.c\n' >>$CONFIG_MK } # @@ -262,7 +275,7 @@ int main(void) { return 0; }' check_func "verrc" "$src" || { - printf 'OPENBSD += verrc.o\n' >>$CONFIG_MK + printf 'SRCS += libopenbsd/verrc.c\n' >>$CONFIG_MK } # @@ -275,7 +288,7 @@ int main(void) { return 0; }' check_func "setprogname" "$src" || { - printf 'OPENBSD += progname.o\n' >>$CONFIG_MK + printf 'SRCS += libopenbsd/progname.c\n' >>$CONFIG_MK } # @@ -289,7 +302,7 @@ int main(void) { return 0; }' check_func "readpassphrase" "$src" || { - printf 'OPENBSD += readpassphrase.o\n' >>$CONFIG_MK + printf 'SRCS += libopenbsd/readpassphrase.c\n' >>$CONFIG_MK } # @@ -303,7 +316,7 @@ int main(void) { return 0; }' check_func "strtonum" "$src" || { - printf 'OPENBSD += strtonum.o\n' >>$CONFIG_MK + printf 'SRCS += libopenbsd/strtonum.c\n' >>$CONFIG_MK } # @@ -316,7 +329,7 @@ int main(void) { return 0; }' check_func "reallocarray" "$src" || { - printf 'OPENBSD += reallocarray.o\n' >>$CONFIG_MK + printf 'SRCS += libopenbsd/reallocarray.c\n' >>$CONFIG_MK } # @@ -341,7 +354,7 @@ int main(void) { return 0; }' check_func "setresuid" "$src" || { - printf 'OPENBSD += bsd-setres_id.o\n' >>$CONFIG_MK + printf 'SRCS += libopenbsd/bsd-setres_id.c\n' >>$CONFIG_MK } # @@ -354,7 +367,7 @@ int main(void) { return 0; }' check_func "closefrom" "$src" || { - printf 'OPENBSD += closefrom.o\n' >>$CONFIG_MK + printf 'SRCS += libopenbsd/closefrom.c\n' >>$CONFIG_MK } # @@ -368,17 +381,6 @@ int main(void) { }' check_func "sysconf" "$src" -# -# Check for /proc/$PID. -# -printf 'Checking for %-14s\t\t' "/proc/\$PID ..." >&2 -if test -d /proc/$$; then - printf 'yes.\n' >&2 - printf 'CFLAGS += -DHAVE_%s\n' "PROC_PID" >>$CONFIG_MK -else - printf 'no.\n' >&2 -fi - # # Check for dirfd(). # @@ -490,5 +492,7 @@ if [ $? -eq 0 ]; then else printf 'Using persist method\t\t\tnone.\n' >&2 fi -printf 'Setting UID_MAX\t\t\t\t%d.\n' "$UID_MAX" >&2 -printf 'Setting GID_MAX\t\t\t\t%d.\n' "$GID_MAX" >&2 + +printf '#define DOAS_CONF "%s/doas.conf"\n' "${SYSCONFDIR}" >>$CONFIG_H + +printf '\n#endif /* CONFIG_H */\n' >>$CONFIG_H diff --git a/doas.c b/doas.c index c95dee3..5a969a0 100644 --- a/doas.c +++ b/doas.c @@ -15,6 +15,8 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include "config.h" + #include #include #include diff --git a/env.c b/env.c index 12909c6..e022787 100644 --- a/env.c +++ b/env.c @@ -15,6 +15,8 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include "config.h" + #include #include "sys-tree.h" diff --git a/libopenbsd/bsd-setres_id.c b/libopenbsd/bsd-setres_id.c index f36d3eb..c244b40 100644 --- a/libopenbsd/bsd-setres_id.c +++ b/libopenbsd/bsd-setres_id.c @@ -14,7 +14,7 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include "includes.h" +#include "config.h" #include diff --git a/libopenbsd/closefrom.c b/libopenbsd/closefrom.c index 0d39353..5df58b8 100644 --- a/libopenbsd/closefrom.c +++ b/libopenbsd/closefrom.c @@ -22,6 +22,8 @@ * PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com */ +#include "config.h" + #include #include #include diff --git a/libopenbsd/errc.c b/libopenbsd/errc.c index b82d419..cab3e84 100644 --- a/libopenbsd/errc.c +++ b/libopenbsd/errc.c @@ -28,11 +28,13 @@ * SUCH DAMAGE. */ -#include "includes.h" +#include "config.h" #include #include +#include "openbsd.h" + void __dead errc(int eval, int code, const char *fmt, ...) { diff --git a/libopenbsd/explicit_bzero.c b/libopenbsd/explicit_bzero.c index 9a646c6..2758e6c 100644 --- a/libopenbsd/explicit_bzero.c +++ b/libopenbsd/explicit_bzero.c @@ -4,6 +4,8 @@ * Written by Matthew Dempsky. */ +#include "config.h" + #include #define __UNUSED __attribute__ ((unused)) diff --git a/libopenbsd/openbsd.h b/libopenbsd/openbsd.h index d3bdc0b..1572b9c 100644 --- a/libopenbsd/openbsd.h +++ b/libopenbsd/openbsd.h @@ -14,9 +14,6 @@ /* API definitions lifted from OpenBSD src/include */ -/* pwd.h */ -#define _PW_NAME_LEN 63 - /* stdlib.h */ #ifndef HAVE_REALLOCARRAY void * reallocarray(void *optr, size_t nmemb, size_t size); diff --git a/libopenbsd/progname.c b/libopenbsd/progname.c index 10c3701..acf496f 100644 --- a/libopenbsd/progname.c +++ b/libopenbsd/progname.c @@ -30,6 +30,8 @@ * . */ +#include "config.h" + #include #include #include diff --git a/libopenbsd/readpassphrase.c b/libopenbsd/readpassphrase.c index 6862a5e..a283969 100644 --- a/libopenbsd/readpassphrase.c +++ b/libopenbsd/readpassphrase.c @@ -23,7 +23,7 @@ /* OPENBSD ORIGINAL: lib/libc/gen/readpassphrase.c */ -#include "includes.h" +#include "config.h" #ifndef HAVE_READPASSPHRASE diff --git a/libopenbsd/reallocarray.c b/libopenbsd/reallocarray.c index aa70686..7775fc6 100644 --- a/libopenbsd/reallocarray.c +++ b/libopenbsd/reallocarray.c @@ -15,6 +15,8 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include "config.h" + #include #include #include diff --git a/libopenbsd/strlcat.c b/libopenbsd/strlcat.c index 2596420..68e1de6 100644 --- a/libopenbsd/strlcat.c +++ b/libopenbsd/strlcat.c @@ -16,6 +16,8 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include "config.h" + #include #include diff --git a/libopenbsd/strlcpy.c b/libopenbsd/strlcpy.c index 6301674..985c9ef 100644 --- a/libopenbsd/strlcpy.c +++ b/libopenbsd/strlcpy.c @@ -16,6 +16,8 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include "config.h" + #include #include diff --git a/libopenbsd/strtonum.c b/libopenbsd/strtonum.c index 3725177..b7ff28d 100644 --- a/libopenbsd/strtonum.c +++ b/libopenbsd/strtonum.c @@ -17,6 +17,8 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include "config.h" + #include #include #include diff --git a/libopenbsd/verrc.c b/libopenbsd/verrc.c index c155a92..c27320d 100644 --- a/libopenbsd/verrc.c +++ b/libopenbsd/verrc.c @@ -28,7 +28,7 @@ * SUCH DAMAGE. */ -#include "includes.h" +#include "config.h" #include #include @@ -36,6 +36,8 @@ #include #include +#include "openbsd.h" + void __dead verrc(int eval, int code, const char *fmt, va_list ap) { diff --git a/pam.c b/pam.c index 1fc6082..ee02b0f 100644 --- a/pam.c +++ b/pam.c @@ -14,6 +14,8 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include "config.h" + #include #include diff --git a/parse.y b/parse.y index 15c00c1..f4309a3 100644 --- a/parse.y +++ b/parse.y @@ -16,6 +16,8 @@ */ %{ +#include "config.h" + #include #include #include diff --git a/shadow.c b/shadow.c index 64cc1f7..de53cea 100644 --- a/shadow.c +++ b/shadow.c @@ -1,3 +1,21 @@ +/* + * Copyright (c) 2020 Duncan Overbruck + * + * 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" + #if HAVE_CRYPT_H # include #endif diff --git a/timestamp.c b/timestamp.c index 6b4354c..c3606ee 100644 --- a/timestamp.c +++ b/timestamp.c @@ -1,3 +1,21 @@ +/* + * Copyright (c) 2020 Duncan Overbruck + * + * 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" + /* * 1) Timestamp files and directories * From 22b68970dd6a3f5018bbf3e68a5f9e35d5948c2e Mon Sep 17 00:00:00 2001 From: Duncan Overbruck Date: Thu, 12 Nov 2020 16:15:04 +0100 Subject: [PATCH 163/198] libopenbsd: clean up readpassphrase compat and fix ifdefs --- includes.h | 5 ----- libopenbsd/readpassphrase.c | 8 +++++++- libopenbsd/{readpassphrase.h => sys-readpassphrase.h} | 0 pam.c | 4 ++-- shadow.c | 4 ++-- 5 files changed, 11 insertions(+), 10 deletions(-) rename libopenbsd/{readpassphrase.h => sys-readpassphrase.h} (100%) diff --git a/includes.h b/includes.h index 401a0e4..911d903 100644 --- a/includes.h +++ b/includes.h @@ -1,11 +1,6 @@ #ifndef INCLUDES_H #define INCLUDES_H -#ifndef _PATH_TTY -# define _PATH_TTY "/dev/tty" -#endif - - #include "openbsd.h" #ifdef USE_PAM diff --git a/libopenbsd/readpassphrase.c b/libopenbsd/readpassphrase.c index a283969..87675aa 100644 --- a/libopenbsd/readpassphrase.c +++ b/libopenbsd/readpassphrase.c @@ -31,10 +31,16 @@ #include #include #include -#include #include #include #include +#include + +#include "sys-readpassphrase.h" + +#ifndef _PATH_TTY +#define _PATH_TTY "/dev/tty" +#endif #ifndef TCSASOFT /* If we don't have TCSASOFT define it so that ORing it it below is a no-op. */ diff --git a/libopenbsd/readpassphrase.h b/libopenbsd/sys-readpassphrase.h similarity index 100% rename from libopenbsd/readpassphrase.h rename to libopenbsd/sys-readpassphrase.h diff --git a/pam.c b/pam.c index ee02b0f..b921069 100644 --- a/pam.c +++ b/pam.c @@ -23,10 +23,10 @@ #include #include #include -#ifdef HAVE_READPASSPHRASE_H +#ifdef HAVE_READPASSPHRASE # include #else -# include "readpassphrase.h" +# include "sys-readpassphrase.h" #endif #include #include diff --git a/shadow.c b/shadow.c index de53cea..8489017 100644 --- a/shadow.c +++ b/shadow.c @@ -23,10 +23,10 @@ #include #include #include -#ifdef HAVE_READPASSPHRASE_H +#ifdef HAVE_READPASSPHRASE # include #else -# include "readpassphrase.h" +# include "sys-readpassphrase.h" #endif #include #include From 0a42dd6437c3c056e8e5c99096ce7e060cabcc0d Mon Sep 17 00:00:00 2001 From: Duncan Overbruck Date: Thu, 12 Nov 2020 16:17:42 +0100 Subject: [PATCH 164/198] remove includes.h and move the prototypes to doas.h --- doas.c | 2 +- doas.h | 14 ++++++++++++++ env.c | 2 +- includes.h | 20 -------------------- libopenbsd/sys-readpassphrase.h | 2 -- pam.c | 3 ++- shadow.c | 1 + timestamp.c | 3 ++- 8 files changed, 21 insertions(+), 26 deletions(-) delete mode 100644 includes.h diff --git a/doas.c b/doas.c index 5a969a0..a184650 100644 --- a/doas.c +++ b/doas.c @@ -36,7 +36,7 @@ #include #include -#include "includes.h" +#include "openbsd.h" #include "doas.h" static void __dead diff --git a/doas.h b/doas.h index de8dbe1..c38fca2 100644 --- a/doas.h +++ b/doas.h @@ -43,3 +43,17 @@ char **prepenv(const struct rule *, const struct passwd *, #define KEEPENV 0x2 #define PERSIST 0x4 #define NOLOG 0x8 + +#ifdef USE_PAM +void pamauth(const char *, const char *, int, int, int); +#endif + +#ifdef USE_SHADOW +void shadowauth(const char *, int); +#endif + +#ifdef USE_TIMESTAMP +int timestamp_open(int *, int); +int timestamp_set(int, int); +int timestamp_clear(void); +#endif diff --git a/env.c b/env.c index e022787..e2286fc 100644 --- a/env.c +++ b/env.c @@ -28,8 +28,8 @@ #include #include +#include "openbsd.h" #include "doas.h" -#include "includes.h" const char *formerpath; diff --git a/includes.h b/includes.h deleted file mode 100644 index 911d903..0000000 --- a/includes.h +++ /dev/null @@ -1,20 +0,0 @@ -#ifndef INCLUDES_H -#define INCLUDES_H - -#include "openbsd.h" - -#ifdef USE_PAM -void pamauth(const char *, const char *, int, int, int); -#endif - -#ifdef USE_SHADOW -void shadowauth(const char *, int); -#endif - -#ifdef USE_TIMESTAMP -int timestamp_open(int *, int); -int timestamp_set(int, int); -int timestamp_clear(void); -#endif - -#endif /* INCLUDES_H */ diff --git a/libopenbsd/sys-readpassphrase.h b/libopenbsd/sys-readpassphrase.h index 5fd7c5d..0c4a59e 100644 --- a/libopenbsd/sys-readpassphrase.h +++ b/libopenbsd/sys-readpassphrase.h @@ -25,8 +25,6 @@ #ifndef _READPASSPHRASE_H_ #define _READPASSPHRASE_H_ -#include "includes.h" - #ifndef HAVE_READPASSPHRASE #define RPP_ECHO_OFF 0x00 /* Turn off echo (default). */ diff --git a/pam.c b/pam.c index b921069..c6f63f2 100644 --- a/pam.c +++ b/pam.c @@ -37,7 +37,8 @@ #include -#include "includes.h" +#include "openbsd.h" +#include "doas.h" #ifndef HOST_NAME_MAX #define HOST_NAME_MAX _POSIX_HOST_NAME_MAX diff --git a/shadow.c b/shadow.c index 8489017..27053d3 100644 --- a/shadow.c +++ b/shadow.c @@ -35,6 +35,7 @@ #include #include "openbsd.h" +#include "doas.h" #ifndef HOST_NAME_MAX #define HOST_NAME_MAX _POSIX_HOST_NAME_MAX diff --git a/timestamp.c b/timestamp.c index c3606ee..bca260d 100644 --- a/timestamp.c +++ b/timestamp.c @@ -82,7 +82,8 @@ #include #include -#include "includes.h" +#include "openbsd.h" +#include "doas.h" #ifndef TIMESTAMP_DIR # define TIMESTAMP_DIR "/run/doas" From b38cfb8a3adbe1d62fd57e2fe3f909d191cbbb0c Mon Sep 17 00:00:00 2001 From: Duncan Overbruck Date: Thu, 12 Nov 2020 16:21:04 +0100 Subject: [PATCH 165/198] pam.c: remove dead assignment --- pam.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pam.c b/pam.c index c6f63f2..8a343e9 100644 --- a/pam.c +++ b/pam.c @@ -124,7 +124,7 @@ pamconv(int nmsgs, const struct pam_message **msgs, for (i = 0; i < nmsgs; i++) { if (rsp[i].resp == NULL) continue; - switch (style = msgs[i]->msg_style) { + switch (msgs[i]->msg_style) { case PAM_PROMPT_ECHO_OFF: case PAM_PROMPT_ECHO_ON: explicit_bzero(rsp[i].resp, strlen(rsp[i].resp)); From 46db70a5a455c713e55d39551335ab215f36b3a3 Mon Sep 17 00:00:00 2001 From: Duncan Overbruck Date: Thu, 12 Nov 2020 16:22:16 +0100 Subject: [PATCH 166/198] pam.c: free rsp in case of failure --- pam.c | 1 + 1 file changed, 1 insertion(+) diff --git a/pam.c b/pam.c index 8a343e9..8148380 100644 --- a/pam.c +++ b/pam.c @@ -132,6 +132,7 @@ pamconv(int nmsgs, const struct pam_message **msgs, } rsp[i].resp = NULL; } + free(rsp); return PAM_CONV_ERR; } From 13660d7c9902705c4b2e3f42b101d813599cd2af Mon Sep 17 00:00:00 2001 From: Duncan Overbruck Date: Thu, 12 Nov 2020 16:49:13 +0100 Subject: [PATCH 167/198] set _OPENBSD_SOURCE on NetBSD --- configure | 3 +++ 1 file changed, 3 insertions(+) diff --git a/configure b/configure index f9c2754..7d95062 100755 --- a/configure +++ b/configure @@ -125,6 +125,9 @@ case "$OS" in OS_CFLAGS="$OS_CFLAGS -D_DEFAULT_SOURCE -D_GNU_SOURCE" printf 'CURDIR := .\n' >>$CONFIG_MK ;; + netbsd) + OS_CFLAGS="$OS_CFLAGS -D_OPENBSD_SOURCE" + ;; esac [ -n "$OS_CFLAGS" ] && \ From ea3dfc90c0d1d584b5163e2aa128e6185a94ec24 Mon Sep 17 00:00:00 2001 From: Duncan Overbruck Date: Thu, 12 Nov 2020 16:55:16 +0100 Subject: [PATCH 168/198] link libutil for setusercontext on NetBSD --- configure | 1 + 1 file changed, 1 insertion(+) diff --git a/configure b/configure index 7d95062..9eaef72 100755 --- a/configure +++ b/configure @@ -127,6 +127,7 @@ case "$OS" in ;; netbsd) OS_CFLAGS="$OS_CFLAGS -D_OPENBSD_SOURCE" + printf 'LDFLAGS += -lutil\n' >>$CONFIG_MK ;; esac From af676f5abd261260f0d474bfd657dd5da65946f5 Mon Sep 17 00:00:00 2001 From: Duncan Overbruck Date: Thu, 12 Nov 2020 17:02:05 +0100 Subject: [PATCH 169/198] fix portability issues with configure script --- configure | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/configure b/configure index 9eaef72..2c88379 100755 --- a/configure +++ b/configure @@ -42,6 +42,14 @@ WITHOUT_TIMESTAMP=yes UID_MAX=65535 GID_MAX=65535 +: ${PREFIX:=/usr/local} +: ${EPREFIX:=${PREFIX}} +: ${BINDIR:=${PREFIX}/bin} +: ${SHAREDIR:=${PREFIX}/share} +: ${MANDIR:=${SHAREDIR}/man} +: ${SYSCONFDIR:=/etc} +: ${PAMDIR:=${SYSCONFDIR}/pam.d} + for x; do opt=${x%%=*} var=${x#*=} @@ -82,13 +90,13 @@ cat <$CONFIG_H ! cat <>$CONFIG_MK -PREFIX ?= ${PREFIX:="/usr"} -EPREFIX ?= ${EPREFIX:="${PREFIX}"} -BINDIR ?= ${BINDIR:="${PREFIX}/bin"} -SHAREDIR ?= ${SHAREDIR:="${PREFIX}/share"} -MANDIR ?= ${MANDIR:="${SHAREDIR}/man"} -SYSCONFDIR?= ${SYSCONFDIR:="/etc"} -PAMDIR ?= ${PAMDIR:="${SYSCONFDIR}/pam.d"} +PREFIX ?= ${PREFIX} +EPREFIX ?= ${EPREFIX} +BINDIR ?= ${BINDIR} +SHAREDIR ?= ${SHAREDIR} +MANDIR ?= ${MANDIR} +SYSCONFDIR?= ${SYSCONFDIR} +PAMDIR ?= ${PAMDIR} EOF if [ -z "$BUILD" ]; then From 9bfe647d25e27479c391492470e31dedef366238 Mon Sep 17 00:00:00 2001 From: Duncan Overbruck Date: Thu, 12 Nov 2020 17:11:09 +0100 Subject: [PATCH 170/198] configure: define CURDIR for all targets --- configure | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure b/configure index 2c88379..058a958 100755 --- a/configure +++ b/configure @@ -90,6 +90,7 @@ cat <$CONFIG_H ! cat <>$CONFIG_MK +CURDIR := . PREFIX ?= ${PREFIX} EPREFIX ?= ${EPREFIX} BINDIR ?= ${BINDIR} @@ -131,7 +132,6 @@ case "$OS" in printf 'Setting GID_MAX\t\t\t\t%d.\n' "$GID_MAX" >&2 printf '#define GID_MAX %s\n' "$GID_MAX" >>$CONFIG_H OS_CFLAGS="$OS_CFLAGS -D_DEFAULT_SOURCE -D_GNU_SOURCE" - printf 'CURDIR := .\n' >>$CONFIG_MK ;; netbsd) OS_CFLAGS="$OS_CFLAGS -D_OPENBSD_SOURCE" From b82ffa68a6436ce3f4c4b480bc9c12ac284b0d99 Mon Sep 17 00:00:00 2001 From: Duncan Overbruck Date: Thu, 12 Nov 2020 17:22:54 +0100 Subject: [PATCH 171/198] simplify makefile --- bsd.prog.mk => GNUmakefile | 24 +++++++++++---------- Makefile | 16 -------------- configure | 44 ++++++++++++++++++++++---------------- 3 files changed, 38 insertions(+), 46 deletions(-) rename bsd.prog.mk => GNUmakefile (75%) delete mode 100644 Makefile diff --git a/bsd.prog.mk b/GNUmakefile similarity index 75% rename from bsd.prog.mk rename to GNUmakefile index 6441fd8..cc64429 100644 --- a/bsd.prog.mk +++ b/GNUmakefile @@ -1,13 +1,18 @@ -# Copyright 2015 Nathan Holstein +PROG= doas +MAN= doas.1 doas.conf.5 -default: ${PROG} - -CFLAGS += -I${CURDIR}/libopenbsd ${COPTS} -MD -MP -Wno-unused-result +SRCS= parse.y doas.c env.c include config.mk -OBJS := ${SRCS:.y=.c} -OBJS := ${OBJS:.c=.o} +CFLAGS+= -I. -Ilibopenbsd ${COPTS} +COPTS+= -Wall -Wextra -Werror -pedantic +YFLAGS= + +all: ${PROG} + +OBJS:= ${SRCS:.y=.c} +OBJS:= ${OBJS:.c=.o} ${PROG}: ${OBJS} ${CC} ${CFLAGS} $^ -o $@ ${LDFLAGS} @@ -32,11 +37,8 @@ uninstall: rm -f ${DESTDIR}${MANDIR}/man5/doas.conf.5 clean: - rm -f ${OBJS} - rm -f ${OBJS:.o=.d} - rm -f ${PROG} - rm -f parse.c + rm -f ${PROG} ${OBJS} ${OBJS:.o=.d} parse.c -include ${OBJS:.o=.d} -.PHONY: default clean install uninstall +.PHONY: all clean install uninstall diff --git a/Makefile b/Makefile deleted file mode 100644 index c0ddb1a..0000000 --- a/Makefile +++ /dev/null @@ -1,16 +0,0 @@ -# $OpenBSD: Makefile,v 1.9 2014/01/13 01:41:00 tedu Exp $ - -SRCS= parse.y doas.c env.c - -PROG= doas -MAN= doas.1 doas.conf.5 - -BINOWN= root -BINGRP= root -BINMODE=4755 - -CFLAGS+= -I${CURDIR} -COPTS+= -Wall -Wextra -Werror -pedantic -YFLAGS= - -include bsd.prog.mk diff --git a/configure b/configure index 058a958..632c74a 100755 --- a/configure +++ b/configure @@ -42,14 +42,6 @@ WITHOUT_TIMESTAMP=yes UID_MAX=65535 GID_MAX=65535 -: ${PREFIX:=/usr/local} -: ${EPREFIX:=${PREFIX}} -: ${BINDIR:=${PREFIX}/bin} -: ${SHAREDIR:=${PREFIX}/share} -: ${MANDIR:=${SHAREDIR}/man} -: ${SYSCONFDIR:=/etc} -: ${PAMDIR:=${SYSCONFDIR}/pam.d} - for x; do opt=${x%%=*} var=${x#*=} @@ -89,17 +81,6 @@ cat <$CONFIG_H ! -cat <>$CONFIG_MK -CURDIR := . -PREFIX ?= ${PREFIX} -EPREFIX ?= ${EPREFIX} -BINDIR ?= ${BINDIR} -SHAREDIR ?= ${SHAREDIR} -MANDIR ?= ${MANDIR} -SYSCONFDIR?= ${SYSCONFDIR} -PAMDIR ?= ${PAMDIR} -EOF - if [ -z "$BUILD" ]; then BUILD="$(uname -m)-unknown-$(uname -s | tr '[:upper:]' '[:lower:]')" fi @@ -136,9 +117,34 @@ case "$OS" in netbsd) OS_CFLAGS="$OS_CFLAGS -D_OPENBSD_SOURCE" printf 'LDFLAGS += -lutil\n' >>$CONFIG_MK + : ${BINGRP:=wheel} ;; esac +: ${PREFIX:=/usr/local} +: ${EPREFIX:=${PREFIX}} +: ${BINDIR:=${PREFIX}/bin} +: ${SHAREDIR:=${PREFIX}/share} +: ${MANDIR:=${SHAREDIR}/man} +: ${SYSCONFDIR:=/etc} +: ${PAMDIR:=${SYSCONFDIR}/pam.d} +: ${BINMODE:=4755} +: ${BINOWN:=root} +: ${BINGRP:=root} + +cat <>$CONFIG_MK +PREFIX ?= ${PREFIX} +EPREFIX ?= ${EPREFIX} +BINDIR ?= ${BINDIR} +SHAREDIR ?= ${SHAREDIR} +MANDIR ?= ${MANDIR} +SYSCONFDIR?= ${SYSCONFDIR} +PAMDIR ?= ${PAMDIR} +BINMODE ?= ${BINMODE} +BINOWN ?= ${BINOWN} +BINGRP ?= ${BINGRP} +EOF + [ -n "$OS_CFLAGS" ] && \ printf 'CFLAGS += %s\n' "$OS_CFLAGS" >>$CONFIG_MK From 5310da93a5b7a6651e54f244363c4af88d3741a0 Mon Sep 17 00:00:00 2001 From: Duncan Overbruck Date: Thu, 12 Nov 2020 17:48:28 +0100 Subject: [PATCH 172/198] add back execvpe fallback --- configure | 4 +- libopenbsd/execvpe.c | 160 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 163 insertions(+), 1 deletion(-) create mode 100644 libopenbsd/execvpe.c diff --git a/configure b/configure index 632c74a..94ded41 100755 --- a/configure +++ b/configure @@ -360,7 +360,9 @@ int main(void) { execvpe("", p, p); return 0; }' -check_func "execvpe" "$src" || die "system has no execvpe(3): not supported" +check_func "execvpe" "$src" || { + printf 'SRCS += libopenbsd/execvpe.c\n' >>$CONFIG_MK +} # # Check for setresuid(). diff --git a/libopenbsd/execvpe.c b/libopenbsd/execvpe.c new file mode 100644 index 0000000..223daf4 --- /dev/null +++ b/libopenbsd/execvpe.c @@ -0,0 +1,160 @@ +/* $OpenBSD: exec.c,v 1.23 2016/03/13 18:34:20 guenther Exp $ */ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 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 +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "openbsd.h" + +int +execvpe(const char *name, char *const *argv, char *const *envp) +{ + char **memp; + int cnt; + size_t lp, ln, len; + char *p; + int eacces = 0; + char *bp, *cur, *path, buf[PATH_MAX]; + + /* + * Do not allow null name + */ + if (name == NULL || *name == '\0') { + errno = ENOENT; + return (-1); + } + + /* If it's an absolute or relative path name, it's easy. */ + if (strchr(name, '/')) { + bp = (char *)name; + cur = path = NULL; + goto retry; + } + bp = buf; + + /* Get the path we're searching. */ + if (!(path = getenv("PATH"))) + path = _PATH_DEFPATH; + len = strlen(path) + 1; + cur = alloca(len); + if (cur == NULL) { + errno = ENOMEM; + return (-1); + } + strlcpy(cur, path, len); + path = cur; + while ((p = strsep(&cur, ":"))) { + /* + * It's a SHELL path -- double, leading and trailing colons + * mean the current directory. + */ + if (!*p) { + p = "."; + lp = 1; + } else + lp = strlen(p); + ln = strlen(name); + + /* + * If the path is too long complain. This is a possible + * security issue; given a way to make the path too long + * the user may execute the wrong program. + */ + if (lp + ln + 2 > sizeof(buf)) { + struct iovec iov[3]; + + iov[0].iov_base = "execvp: "; + iov[0].iov_len = 8; + iov[1].iov_base = p; + iov[1].iov_len = lp; + iov[2].iov_base = ": path too long\n"; + iov[2].iov_len = 16; + (void)writev(STDERR_FILENO, iov, 3); + continue; + } + bcopy(p, buf, lp); + buf[lp] = '/'; + bcopy(name, buf + lp + 1, ln); + buf[lp + ln + 1] = '\0'; + +retry: (void)execve(bp, argv, envp); + switch(errno) { + case E2BIG: + goto done; + case EISDIR: + case ELOOP: + case ENAMETOOLONG: + case ENOENT: + break; + case ENOEXEC: + for (cnt = 0; argv[cnt]; ++cnt) + ; + memp = alloca((cnt + 2) * sizeof(char *)); + if (memp == NULL) + goto done; + memp[0] = "sh"; + memp[1] = bp; + bcopy(argv + 1, memp + 2, cnt * sizeof(char *)); + (void)execve(_PATH_BSHELL, memp, envp); + goto done; + case ENOMEM: + goto done; + case ENOTDIR: + break; + case ETXTBSY: + /* + * We used to retry here, but sh(1) doesn't. + */ + goto done; + case EACCES: + eacces = 1; + break; + default: + goto done; + } + } + if (eacces) + errno = EACCES; + else if (!errno) + errno = ENOENT; +done: + return (-1); +} From b324c300fd9b45ce0f90e77925ec85545eeec718 Mon Sep 17 00:00:00 2001 From: Duncan Overbruck Date: Thu, 12 Nov 2020 18:01:09 +0100 Subject: [PATCH 173/198] libopenbsd/closefrom.h: include path.h for _PATH_DEV on MacOSX --- libopenbsd/closefrom.c | 1 + 1 file changed, 1 insertion(+) diff --git a/libopenbsd/closefrom.c b/libopenbsd/closefrom.c index 5df58b8..fba73e7 100644 --- a/libopenbsd/closefrom.c +++ b/libopenbsd/closefrom.c @@ -30,6 +30,7 @@ #include #include #include +#include #ifdef HAVE_PSTAT_GETPROC # include #else From 049eedbf316fcc831a485f1fc53aa33035ba12da Mon Sep 17 00:00:00 2001 From: Duncan Overbruck Date: Thu, 12 Nov 2020 18:02:11 +0100 Subject: [PATCH 174/198] configure: don't set --no-as-needed on MacOSX while running checks --- configure | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/configure b/configure index 94ded41..f8d351b 100755 --- a/configure +++ b/configure @@ -157,7 +157,11 @@ EOF # Add CPPFLAGS/CFLAGS/LDFLAGS to CC for testing features XCC="${CC:=cc} $CFLAGS $OS_CFLAGS $CPPFLAGS $LDFLAGS" # Make sure to disable --as-needed for CC tests. -XCC="$XCC -Wl,--no-as-needed" + +case "$OS" in + darwin) ;; + *) XCC="$XCC -Wl,--no-as-needed" ;; +esac check_func() { func="$1"; src="$2"; shift 2 From 378157c652e44f521578b79867a76902e0a22cf6 Mon Sep 17 00:00:00 2001 From: Duncan Overbruck Date: Thu, 12 Nov 2020 18:20:34 +0100 Subject: [PATCH 175/198] use wheel group on MacOSX --- configure | 3 +++ 1 file changed, 3 insertions(+) diff --git a/configure b/configure index f8d351b..f5f3f47 100755 --- a/configure +++ b/configure @@ -119,6 +119,9 @@ case "$OS" in printf 'LDFLAGS += -lutil\n' >>$CONFIG_MK : ${BINGRP:=wheel} ;; + darwin) + : ${BINGRP:=wheel} + ;; esac : ${PREFIX:=/usr/local} From 790aab481bcc7ce668cc651004579b10840d4a13 Mon Sep 17 00:00:00 2001 From: Duncan Overbruck Date: Thu, 12 Nov 2020 18:29:08 +0100 Subject: [PATCH 176/198] add pam.d file for MacOSX --- pam.d__doas__darwin | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 pam.d__doas__darwin diff --git a/pam.d__doas__darwin b/pam.d__doas__darwin new file mode 100644 index 0000000..87551fb --- /dev/null +++ b/pam.d__doas__darwin @@ -0,0 +1,5 @@ +# sudo: auth account password session +auth required pam_opendirectory.so +account required pam_permit.so +password required pam_deny.so +session required pam_permit.so From 46d2543203501994e10bfdac24392653f2325657 Mon Sep 17 00:00:00 2001 From: Duncan Overbruck Date: Thu, 12 Nov 2020 21:19:55 +0100 Subject: [PATCH 177/198] configure: add freebsd support --- configure | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/configure b/configure index f5f3f47..d6c85af 100755 --- a/configure +++ b/configure @@ -119,6 +119,10 @@ case "$OS" in printf 'LDFLAGS += -lutil\n' >>$CONFIG_MK : ${BINGRP:=wheel} ;; + freebsd) + printf 'LDFLAGS += -lutil\n' >>$CONFIG_MK + : ${BINGRP:=wheel} + ;; darwin) : ${BINGRP:=wheel} ;; From 17629b9ce63dba6aa4146d483cd61448cddb1fa5 Mon Sep 17 00:00:00 2001 From: Duncan Overbruck Date: Thu, 12 Nov 2020 21:26:04 +0100 Subject: [PATCH 178/198] configure: add setresgid, setreuid and setregid checks --- configure | 42 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 40 insertions(+), 2 deletions(-) diff --git a/configure b/configure index d6c85af..0dfc649 100755 --- a/configure +++ b/configure @@ -384,9 +384,47 @@ int main(void) { setresuid(0, 0, 0); return 0; }' -check_func "setresuid" "$src" || { +check_func "setresuid" "$src" +have_setresuid=$? + +# +# Check for setresgid(). +# +src=' +#include +int main(void) { + setresgid(0, 0, 0); + return 0; +}' +check_func "setresgid" "$src" +have_setresgid=$? + +if [ $have_setresuid -eq 1 -o $have_setresgid -eq 1 ]; then printf 'SRCS += libopenbsd/bsd-setres_id.c\n' >>$CONFIG_MK -} +fi + +# +# Check for setreuid(). +# +src=' +#include +int main(void) { + setreuid(0, 0); + return 0; +}' +check_func "setreuid" "$src" + + +# +# Check for setregid(). +# +src=' +#include +int main(void) { + setregid(0, 0); + return 0; +}' +check_func "setregid" "$src" # # Check for closefrom(). From 29123f7c5b7cfb79419425ad4d2b7af153e06472 Mon Sep 17 00:00:00 2001 From: Duncan Overbruck Date: Sat, 14 Nov 2020 16:02:10 +0100 Subject: [PATCH 179/198] configure: fix verrc check --- configure | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/configure b/configure index 0dfc649..82b9331 100755 --- a/configure +++ b/configure @@ -298,9 +298,10 @@ check_func "errc" "$src" || { # Check for verrc(). # src=' +#include #include int main(void) { - verrc(0, 0, ""); + verrc(0, 0, "x", NULL); return 0; }' check_func "verrc" "$src" || { From d67caab6ab5dfa342159fef7cef5ea91ccf0aad3 Mon Sep 17 00:00:00 2001 From: Duncan Overbruck Date: Sat, 14 Nov 2020 16:13:16 +0100 Subject: [PATCH 180/198] configure: use LDLIBS instead of setting LDFLAGS --- GNUmakefile | 4 ++-- configure | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/GNUmakefile b/GNUmakefile index cc64429..9e524fc 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -6,7 +6,7 @@ SRCS= parse.y doas.c env.c include config.mk CFLAGS+= -I. -Ilibopenbsd ${COPTS} -COPTS+= -Wall -Wextra -Werror -pedantic +COPTS+= -Wall -Wextra -pedantic -O2 -D_FORTIFY_SOURCE=2 YFLAGS= all: ${PROG} @@ -15,7 +15,7 @@ OBJS:= ${SRCS:.y=.c} OBJS:= ${OBJS:.c=.o} ${PROG}: ${OBJS} - ${CC} ${CFLAGS} $^ -o $@ ${LDFLAGS} + ${CC} ${CFLAGS} $^ -o $@ ${LDFLAGS} ${LDLIBS} install: ${PROG} ${PAM_DOAS} ${MAN} mkdir -p -m 0755 ${DESTDIR}${BINDIR} diff --git a/configure b/configure index 82b9331..9592a30 100755 --- a/configure +++ b/configure @@ -116,11 +116,11 @@ case "$OS" in ;; netbsd) OS_CFLAGS="$OS_CFLAGS -D_OPENBSD_SOURCE" - printf 'LDFLAGS += -lutil\n' >>$CONFIG_MK + printf 'LDLIBS += -lutil\n' >>$CONFIG_MK : ${BINGRP:=wheel} ;; freebsd) - printf 'LDFLAGS += -lutil\n' >>$CONFIG_MK + printf 'LDLIBS += -lutil\n' >>$CONFIG_MK : ${BINGRP:=wheel} ;; darwin) @@ -161,8 +161,8 @@ EOF [ -n "$BUILD_STATIC" ] && \ printf 'CFLAGS += -static\n' >>$CONFIG_MK -# Add CPPFLAGS/CFLAGS/LDFLAGS to CC for testing features -XCC="${CC:=cc} $CFLAGS $OS_CFLAGS $CPPFLAGS $LDFLAGS" +# Add CPPFLAGS/CFLAGS/LDFLAGS/LDLIBS to CC for testing features +XCC="${CC:=cc} $CFLAGS $OS_CFLAGS $CPPFLAGS $LDFLAGS $LDLIBS" # Make sure to disable --as-needed for CC tests. case "$OS" in @@ -200,7 +200,7 @@ int main(void) { }' [ -z "$WITHOUT_PAM" ] && check_func "pam_appl_h" "$src" && { printf 'SRCS += pam.c\n' >>$CONFIG_MK - printf 'LDFLAGS += -lpam\n' >>$CONFIG_MK + printf 'LDLIBS += -lpam\n' >>$CONFIG_MK printf '#define USE_PAM\n' >>$CONFIG_H printf 'pam\n' @@ -219,7 +219,7 @@ int main(void) { }' [ -z "$WITHOUT_SHADOW" ] && check_func "shadow_h" "$src" && { printf 'SRCS += shadow.c\n' >>$CONFIG_MK - printf 'LDFLAGS += -lcrypt\n' >>$CONFIG_MK + printf 'LDLIBS += -lcrypt\n' >>$CONFIG_MK printf '#define USE_SHADOW\n' >>$CONFIG_H printf 'shadow\n' return 0 From dc56c2fe04e8f658a2481fba007c4422dd1cdc35 Mon Sep 17 00:00:00 2001 From: Duncan Overbruck Date: Sat, 14 Nov 2020 16:19:31 +0100 Subject: [PATCH 181/198] pam.d: include system-auth for auth, account and session --- pam.d__doas__linux | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/pam.d__doas__linux b/pam.d__doas__linux index 5e7885a..98707ef 100644 --- a/pam.d__doas__linux +++ b/pam.d__doas__linux @@ -1,7 +1,4 @@ #%PAM-1.0 -auth required pam_unix.so -account required pam_unix.so -session optional pam_xauth.so -session optional pam_umask.so usergroups umask=022 -session required pam_env.so -session required pam_unix.so +auth include system-auth +account include system-auth +session include system-auth From b3e966b7f3b921a686a7665f4b265b19c79c46f3 Mon Sep 17 00:00:00 2001 From: Duncan Overbruck Date: Sat, 14 Nov 2020 16:27:07 +0100 Subject: [PATCH 182/198] configure: respect environment and make CFLAGS --- GNUmakefile | 4 +--- configure | 4 ++-- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/GNUmakefile b/GNUmakefile index 9e524fc..9470202 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -5,9 +5,7 @@ SRCS= parse.y doas.c env.c include config.mk -CFLAGS+= -I. -Ilibopenbsd ${COPTS} -COPTS+= -Wall -Wextra -pedantic -O2 -D_FORTIFY_SOURCE=2 -YFLAGS= +override CFLAGS:=-I. -Ilibopenbsd -O2 -Wall -Wextra ${OS_CFLAGS} ${CFLAGS} all: ${PROG} diff --git a/configure b/configure index 9592a30..1c5d989 100755 --- a/configure +++ b/configure @@ -153,7 +153,7 @@ BINGRP ?= ${BINGRP} EOF [ -n "$OS_CFLAGS" ] && \ - printf 'CFLAGS += %s\n' "$OS_CFLAGS" >>$CONFIG_MK + printf 'OS_CFLAGS += %s\n' "$OS_CFLAGS" >>$CONFIG_MK [ -n "$DEBUG" ] && \ printf 'CFLAGS += -O0 -g\n' >>$CONFIG_MK @@ -545,7 +545,7 @@ int main(void){return 0;} __attribute__((__unused__)) static void foo(void){return;} ' check_func "__attribute__" "$src" || { - printf 'CFLAGS += -DNO_ATTRIBUTE_ON_RETURN_TYPE=1\n' >>$CONFIG_MK + printf 'OS_CFLAGS += -DNO_ATTRIBUTE_ON_RETURN_TYPE=1\n' >>$CONFIG_MK } auth=$(authmethod) From a1ab056bccfe66d4f03b96e3f83168a3732e56f4 Mon Sep 17 00:00:00 2001 From: Duncan Overbruck Date: Sat, 14 Nov 2020 16:28:27 +0100 Subject: [PATCH 183/198] pam: use PAM_REINITIALIZE_CRED Both work fine, PAM_REINITIALIZE_CRED is the more correct choice and is required on Solaris, see sudo bug #642; https://bugzilla.sudo.ws/show_bug.cgi?id=642 --- pam.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pam.c b/pam.c index 8148380..68294b2 100644 --- a/pam.c +++ b/pam.c @@ -313,9 +313,9 @@ pamauth(const char *user, const char *myname, int interactive, int nopass, int p warn("pam_set_item(?, PAM_USER, \"%s\"): %s", user, pam_strerror(pamh, ret)); - ret = pam_setcred(pamh, PAM_ESTABLISH_CRED); + ret = pam_setcred(pamh, PAM_REINITIALIZE_CRED); if (ret != PAM_SUCCESS) - warn("pam_setcred(?, PAM_ESTABLISH_CRED): %s", pam_strerror(pamh, ret)); + warn("pam_setcred(?, PAM_REINITIALIZE_CRED): %s", pam_strerror(pamh, ret)); else cred = 1; From 31abd37c26c26892ce5e0d538c51fbc38ff3e05a Mon Sep 17 00:00:00 2001 From: Duncan Overbruck Date: Sat, 14 Nov 2020 16:30:28 +0100 Subject: [PATCH 184/198] remove unused pam.d file --- pam.d__doas | 5 ----- 1 file changed, 5 deletions(-) delete mode 100644 pam.d__doas diff --git a/pam.d__doas b/pam.d__doas deleted file mode 100644 index 87551fb..0000000 --- a/pam.d__doas +++ /dev/null @@ -1,5 +0,0 @@ -# sudo: auth account password session -auth required pam_opendirectory.so -account required pam_permit.so -password required pam_deny.so -session required pam_permit.so From 36cc28e4446e77ab4d8d48055e9715f1ae26c68e Mon Sep 17 00:00:00 2001 From: mikeb Date: Tue, 1 Sep 2015 16:20:55 +0000 Subject: [PATCH 185/198] increment the line number after the line continuation; ok tedu --- parse.y | 2 ++ 1 file changed, 2 insertions(+) diff --git a/parse.y b/parse.y index f4309a3..9ddca34 100644 --- a/parse.y +++ b/parse.y @@ -278,6 +278,8 @@ repeat: if (escape) { nonkw = 1; escape = 0; + yylval.colno = 0; + yylval.lineno++; continue; } goto eow; From 01ac84109c99e94bf61c0a4039c76b8af289c499 Mon Sep 17 00:00:00 2001 From: tedu Date: Fri, 27 Nov 2015 21:10:17 +0000 Subject: [PATCH 186/198] after reading a too long line, restart at the beginning of the buffer so we don't keep writing past the end. (the perils of trying to recover from parse errors.) noticed by Jan Schreiber --- parse.y | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/parse.y b/parse.y index 9ddca34..c50378a 100644 --- a/parse.y +++ b/parse.y @@ -311,8 +311,10 @@ repeat: } } *p++ = c; - if (p == ebuf) + if (p == ebuf) { yyerror("too long line"); + p = buf; + } escape = 0; } From d5acd52e2a15c36a8e06f9103d35622933aa422d Mon Sep 17 00:00:00 2001 From: Duncan Overbruck Date: Thu, 28 Jan 2021 17:58:34 +0100 Subject: [PATCH 187/198] correctly reset path for rules without specific command This is a fixup for commit 01c658f8c45cb92a343be5f32aa6da70b2032168 where the behaviour was changed to not inherit the PATH variable by default. --- doas.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/doas.c b/doas.c index a184650..d312b24 100644 --- a/doas.c +++ b/doas.c @@ -386,6 +386,7 @@ main(int argc, char **argv) #ifdef HAVE_LOGIN_CAP_H if (setusercontext(NULL, targpw, target, LOGIN_SETGROUP | + LOGIN_SETPATH | LOGIN_SETPRIORITY | LOGIN_SETRESOURCES | LOGIN_SETUMASK | LOGIN_SETUSER) != 0) errx(1, "failed to set user context for target"); @@ -396,6 +397,8 @@ main(int argc, char **argv) err(1, "initgroups"); if (setresuid(target, target, target) != 0) err(1, "setresuid"); + if (setenv("PATH", safepath, 1) == -1) + err(1, "failed to set PATH '%s'", safepath); #endif if (getcwd(cwdpath, sizeof(cwdpath)) == NULL) From 6e3c6ba4af9c04ba7f59c859148864aa895851cc Mon Sep 17 00:00:00 2001 From: martijn Date: Fri, 15 Jan 2021 08:32:55 +0000 Subject: [PATCH 188/198] Be more explicit by stating that the -n flag is linked to the nopass option in doas.conf instead of a generic "would prompt for password", which could lead people into believing that persist could work with this option. OK tedu@ Feedback and OK kn@ --- doas.1 | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doas.1 b/doas.1 index 7360be3..2f72ca9 100644 --- a/doas.1 +++ b/doas.1 @@ -89,9 +89,9 @@ Clear any persisted authorizations from previous invocations, then immediately exit. No command is executed. .It Fl n -Non interactive mode, fail if -.Nm -would prompt for password. +Non interactive mode, fail if the matching rule doesn't have the +.Ic nopass +option. .It Fl s Execute the shell from .Ev SHELL From e8e8713b26723d2daf3de50b51d2ac209ad65716 Mon Sep 17 00:00:00 2001 From: martijn Date: Sat, 16 Jan 2021 09:18:41 +0000 Subject: [PATCH 189/198] s/authorization/authentication/g OK kn@ tedu@ --- doas.c | 4 ++-- pam.c | 8 ++++---- shadow.c | 8 ++++---- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/doas.c b/doas.c index d312b24..b47d2bb 100644 --- a/doas.c +++ b/doas.c @@ -352,7 +352,7 @@ main(int argc, char **argv) #if defined(USE_SHADOW) if (!(rule->options & NOPASS)) { if (nflag) - errx(1, "Authorization required"); + errx(1, "Authentication required"); shadowauth(mypw->pw_name, rule->options & PERSIST); } @@ -360,7 +360,7 @@ main(int argc, char **argv) /* no authentication provider, only allow NOPASS rules */ (void) nflag; if (!(rule->options & NOPASS)) - errx(1, "Authorization required"); + errx(1, "Authentication required"); #endif if ((p = getenv("PATH")) != NULL) diff --git a/pam.c b/pam.c index 68294b2..a9e2036 100644 --- a/pam.c +++ b/pam.c @@ -245,7 +245,7 @@ pamauth(const char *user, const char *myname, int interactive, int nopass, int p #endif if (!user || !myname) - errx(1, "Authorization failed"); + errx(1, "Authentication failed"); ret = pam_start(PAM_SERVICE_NAME, myname, &conv, &pamh); if (ret != PAM_SUCCESS) @@ -277,7 +277,7 @@ pamauth(const char *user, const char *myname, int interactive, int nopass, int p if (!nopass) { if (!interactive) - errx(1, "Authorization required"); + errx(1, "Authentication required"); /* doas style prompt for pam */ char host[HOST_NAME_MAX + 1]; @@ -291,7 +291,7 @@ pamauth(const char *user, const char *myname, int interactive, int nopass, int p if (ret != PAM_SUCCESS) { pamcleanup(ret, sess, cred); syslog(LOG_AUTHPRIV | LOG_NOTICE, "failed auth for %s", myname); - errx(1, "Authorization failed"); + errx(1, "Authentication failed"); } } @@ -304,7 +304,7 @@ pamauth(const char *user, const char *myname, int interactive, int nopass, int p if (ret != PAM_SUCCESS) { pamcleanup(ret, sess, cred); syslog(LOG_AUTHPRIV | LOG_NOTICE, "failed auth for %s", myname); - errx(1, "Authorization failed"); + errx(1, "Authentication failed"); } /* set PAM_USER to the user we want to be */ diff --git a/shadow.c b/shadow.c index 27053d3..2569b58 100644 --- a/shadow.c +++ b/shadow.c @@ -68,10 +68,10 @@ shadowauth(const char *myname, int persist) if (hash[0] == 'x' && hash[1] == '\0') { struct spwd *sp; if ((sp = getspnam(myname)) == NULL) - errx(1, "Authorization failed"); + errx(1, "Authentication failed"); hash = sp->sp_pwdp; } else if (hash[0] != '*') { - errx(1, "Authorization failed"); + errx(1, "Authentication failed"); } char host[HOST_NAME_MAX + 1]; @@ -91,12 +91,12 @@ shadowauth(const char *myname, int persist) err(1, "readpassphrase"); if ((encrypted = crypt(response, hash)) == NULL) { explicit_bzero(rbuf, sizeof(rbuf)); - errx(1, "Authorization failed"); + errx(1, "Authentication failed"); } explicit_bzero(rbuf, sizeof(rbuf)); if (strcmp(encrypted, hash) != 0) { syslog(LOG_AUTHPRIV | LOG_NOTICE, "failed auth for %s", myname); - errx(1, "Authorization failed"); + errx(1, "Authentication failed"); } #ifdef USE_TIMESTAMP From 2d7431ca9ec0b3204a5e9fd01d0fffd946c4d430 Mon Sep 17 00:00:00 2001 From: millert Date: Wed, 27 Jan 2021 17:02:50 +0000 Subject: [PATCH 190/198] Promote nrules/maxrules to size_t and make sure they can't overflow. reallocarray(3) will fail if nmemb * size would overflow. OK tb@ martijn@ --- doas.c | 2 +- doas.h | 2 +- parse.y | 16 ++++++++-------- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/doas.c b/doas.c index b47d2bb..ac3a42a 100644 --- a/doas.c +++ b/doas.c @@ -139,7 +139,7 @@ static int permit(uid_t uid, gid_t *groups, int ngroups, const struct rule **lastr, uid_t target, const char *cmd, const char **cmdargs) { - int i; + size_t i; *lastr = NULL; for (i = 0; i < nrules; i++) { diff --git a/doas.h b/doas.h index c38fca2..a8aa41b 100644 --- a/doas.h +++ b/doas.h @@ -26,7 +26,7 @@ struct rule { }; extern struct rule **rules; -extern int nrules; +extern size_t nrules; extern int parse_errors; extern const char *formerpath; diff --git a/parse.y b/parse.y index c50378a..85e20cc 100644 --- a/parse.y +++ b/parse.y @@ -52,8 +52,8 @@ typedef struct { FILE *yyfp; struct rule **rules; -int nrules; -static int maxrules; +size_t nrules; +static size_t maxrules; int parse_errors = 0; @@ -100,12 +100,12 @@ rule: action ident target cmd { r->cmdargs = $4.cmdargs; if (nrules == maxrules) { if (maxrules == 0) - maxrules = 63; - else - maxrules *= 2; - if (!(rules = reallocarray(rules, maxrules, - sizeof(*rules)))) + maxrules = 32; + rules = reallocarray(rules, maxrules, + 2 * sizeof(*rules)); + if (!rules) errx(1, "can't allocate rules"); + maxrules *= 2; } rules[nrules++] = r; } ; @@ -228,6 +228,7 @@ yylex(void) { char buf[1024], *ebuf, *p, *str; int c, quotes = 0, escape = 0, qpos = -1, nonkw = 0; + size_t i; p = buf; ebuf = buf + sizeof(buf); @@ -334,7 +335,6 @@ eow: goto repeat; } if (!nonkw) { - size_t i; for (i = 0; i < sizeof(keywords) / sizeof(keywords[0]); i++) { if (strcmp(buf, keywords[i].word) == 0) return keywords[i].token; From 454489f79baa3ee1c56498a1c35606b43822f138 Mon Sep 17 00:00:00 2001 From: tedu Date: Fri, 4 Dec 2015 09:41:49 +0000 Subject: [PATCH 191/198] espie reminds me that EOF can happen for errors as well, so check for that happening and print a message. --- parse.y | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/parse.y b/parse.y index 85e20cc..388c2a5 100644 --- a/parse.y +++ b/parse.y @@ -251,12 +251,12 @@ repeat: /* skip comments; NUL is allowed; no continuation */ while ((c = getc(yyfp)) != '\n') if (c == EOF) - return 0; + goto eof; yylval.colno = 0; yylval.lineno++; return c; case EOF: - return 0; + goto eof; } /* parsing next word */ @@ -330,7 +330,7 @@ eow: * the main loop. */ if (c == EOF) - return 0; + goto eof; else if (qpos == -1) /* accept, e.g., empty args: cmd foo args "" */ goto repeat; } @@ -344,4 +344,9 @@ eow: err(1, "%s", __func__); yylval.str = str; return TSTRING; + +eof: + if (ferror(yyfp)) + yyerror("input error reading config"); + return 0; } From 24b1a957cbe55363ca06a62b10c936e5c53e3423 Mon Sep 17 00:00:00 2001 From: Duncan Overbruck Date: Thu, 28 Jan 2021 20:12:48 +0100 Subject: [PATCH 192/198] apply missing man page changes --- doas.1 | 6 +++--- doas.conf.5 | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/doas.1 b/doas.1 index 2f72ca9..a91705e 100644 --- a/doas.1 +++ b/doas.1 @@ -1,4 +1,4 @@ -.\" $OpenBSD: doas.1,v 1.13 2015/07/26 23:00:15 tedu Exp $ +.\" $OpenBSD: doas.1,v 1.25 2021/01/16 09:18:41 martijn Exp $ .\" .\"Copyright (c) 2015 Ted Unangst .\" @@ -13,7 +13,7 @@ .\"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. -.Dd $Mdocdate: July 26 2015 $ +.Dd $Mdocdate: January 16 2021 $ .Dt DOAS 1 .Os .Sh NAME @@ -85,7 +85,7 @@ will be printed on standard output, depending on command matching results. No command is executed. .It Fl L -Clear any persisted authorizations from previous invocations, +Clear any persisted authentications from previous invocations, then immediately exit. No command is executed. .It Fl n diff --git a/doas.conf.5 b/doas.conf.5 index 461ef3f..e98bfbe 100644 --- a/doas.conf.5 +++ b/doas.conf.5 @@ -1,4 +1,4 @@ -.\" $OpenBSD: doas.conf.5,v 1.13 2015/07/27 21:44:11 tedu Exp $ +.\" $OpenBSD: doas.conf.5,v 1.45 2020/10/09 10:24:33 jmc Exp $ .\" .\"Copyright (c) 2015 Ted Unangst .\" @@ -13,7 +13,7 @@ .\"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. -.Dd $Mdocdate: July 27 2015 $ +.Dd $Mdocdate: October 9 2020 $ .Dt DOAS.CONF 5 .Os .Sh NAME @@ -111,7 +111,7 @@ escapes the next character, including new line characters, outside comments; as a result, comments may not be extended over multiple lines. .It If quotes or backslashes are used in a word, -it isn't considered a keyword. +it is not considered a keyword. .El .Sh FILES .Bl -tag -width /etc/examples/doas.conf -compact From cfa9f0d3b306d6c1287ec4f2aa42be29de66c9de Mon Sep 17 00:00:00 2001 From: Duncan Overbruck Date: Fri, 29 Jan 2021 00:00:23 +0100 Subject: [PATCH 193/198] remove pam.d configuration files pam configuration files are not portable, its the job of the package maintainer or user who builds opendoas themselves to configure pam in a safe and usable way. --- GNUmakefile | 5 +---- README.md | 12 ++++++++++++ configure | 7 ------- pam.d__doas__darwin | 5 ----- pam.d__doas__linux | 4 ---- 5 files changed, 13 insertions(+), 20 deletions(-) delete mode 100644 pam.d__doas__darwin delete mode 100644 pam.d__doas__linux diff --git a/GNUmakefile b/GNUmakefile index 9470202..2eef88e 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -15,16 +15,13 @@ OBJS:= ${OBJS:.c=.o} ${PROG}: ${OBJS} ${CC} ${CFLAGS} $^ -o $@ ${LDFLAGS} ${LDLIBS} -install: ${PROG} ${PAM_DOAS} ${MAN} +install: ${PROG} ${MAN} mkdir -p -m 0755 ${DESTDIR}${BINDIR} - [ -n "${PAM_DOAS}" ] && mkdir -p -m 0755 ${DESTDIR}${PAMDIR} || true mkdir -p -m 0755 ${DESTDIR}${MANDIR}/man1 mkdir -p -m 0755 ${DESTDIR}${MANDIR}/man5 cp -f ${PROG} ${DESTDIR}${BINDIR} chown ${BINOWN}:${BINGRP} ${DESTDIR}${BINDIR}/${PROG} chmod ${BINMODE} ${DESTDIR}${BINDIR}/${PROG} - [ -n "${PAM_DOAS}" ] && cp ${PAM_DOAS} ${DESTDIR}${PAMDIR}/doas || true - [ -n "${PAM_DOAS}" ] && chmod 0644 ${DESTDIR}${PAMDIR}/doas || true cp -f doas.1 ${DESTDIR}${MANDIR}/man1 cp -f doas.conf.5 ${DESTDIR}${MANDIR}/man5 diff --git a/README.md b/README.md index 20ef9f2..3498f1c 100644 --- a/README.md +++ b/README.md @@ -29,6 +29,18 @@ from openssh (`readpassphrase.c`) or from sudo (`closefrom.c`). The PAM and shadow authentication code does not come from the OpenBSD project. +### pam configuration + +I will not ship pam configuration files, they are distribution specific and +its simply not safe or productive to ship and install those files. + +If you want to use opendoas on your system and there is no package that +ships with a working pam configuration file, then you have to write and +test it yourself. + +A good starting point is probably the distribution maintained `/etc/pam.d/sudo` +file. + ### Perist/Timestamp/Timeout The persist feature is disabled by default and can be enabled with the configure diff --git a/configure b/configure index 1c5d989..1f92f01 100755 --- a/configure +++ b/configure @@ -15,7 +15,6 @@ usage: configure [options] --datadir=DIR architecture-independent data files [PREFIX/share] --mandir=DIR manual pages [DATADIR/man] --sysconfdir=DIR directory for configuration files [/etc] - --pamdir=DIR PAM directory [SYSCONFDIR/pam.d] --build=build-alias a cpu-vendor-opsys for the system where the application will be built --host=host-alias a cpu-vendor-opsys for the system where the application will run @@ -52,7 +51,6 @@ for x; do --datadir) SHAREDIR=$var ;; --mandir) MANDIR=$var ;; --sysconfdir) SYSCONFDIR=$var ;; - --pamdir) PAMDIR=$var ;; --build) BUILD=$var ;; --host) HOST=$var ;; --target) TARGET=$var ;; @@ -134,7 +132,6 @@ esac : ${SHAREDIR:=${PREFIX}/share} : ${MANDIR:=${SHAREDIR}/man} : ${SYSCONFDIR:=/etc} -: ${PAMDIR:=${SYSCONFDIR}/pam.d} : ${BINMODE:=4755} : ${BINOWN:=root} : ${BINGRP:=root} @@ -146,7 +143,6 @@ BINDIR ?= ${BINDIR} SHAREDIR ?= ${SHAREDIR} MANDIR ?= ${MANDIR} SYSCONFDIR?= ${SYSCONFDIR} -PAMDIR ?= ${PAMDIR} BINMODE ?= ${BINMODE} BINOWN ?= ${BINOWN} BINGRP ?= ${BINGRP} @@ -203,9 +199,6 @@ int main(void) { printf 'LDLIBS += -lpam\n' >>$CONFIG_MK printf '#define USE_PAM\n' >>$CONFIG_H printf 'pam\n' - - pam_file="pam.d__doas__${OS}" - [ -e "$pam_file" ] && printf 'PAM_DOAS = %s\n' "$pam_file" >>$CONFIG_MK return 0 } diff --git a/pam.d__doas__darwin b/pam.d__doas__darwin deleted file mode 100644 index 87551fb..0000000 --- a/pam.d__doas__darwin +++ /dev/null @@ -1,5 +0,0 @@ -# sudo: auth account password session -auth required pam_opendirectory.so -account required pam_permit.so -password required pam_deny.so -session required pam_permit.so diff --git a/pam.d__doas__linux b/pam.d__doas__linux deleted file mode 100644 index 98707ef..0000000 --- a/pam.d__doas__linux +++ /dev/null @@ -1,4 +0,0 @@ -#%PAM-1.0 -auth include system-auth -account include system-auth -session include system-auth From 9474e418d2184e86408f0dce09ca250e36138672 Mon Sep 17 00:00:00 2001 From: Duncan Overbruck Date: Fri, 29 Jan 2021 00:28:46 +0100 Subject: [PATCH 194/198] Replace build/installation instructions with discouragements --- README.md | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 3498f1c..e6f5749 100644 --- a/README.md +++ b/README.md @@ -5,15 +5,21 @@ initially [written by Ted Unangst](http://www.tedunangst.com/flak/post/doas) of the OpenBSD project to provide 95% of the features of `sudo` with a fraction of the codebase. -At the moment only linux with GLIBC or musl libc is supported and tested. - -## Building and installing - -``` -$ ./configure -$ make -# make install -``` +## Building and installation discouragements + +There are a few steps you have to carefully consider before building and installing +opendoas: + +* There are less eyes on random doas ports, just because sudo had a vulnerability + does not mean random doas ports are more secure if they are not reviewed + or pam is configured incorrectly. +* If you want to use pam; You have to [configure pam](#pam-configuration) + and failing to do so correctly might leave a big open door. +* Use the configure script to configure the opendoas. +* Use the default make target to build the software. +* If you really want to install a setuid binary that depends on + pam being correctly configured, use the make install target + to install the software. ## About the port From adeb56b455aacfb56f1819537f466de78974c0b4 Mon Sep 17 00:00:00 2001 From: Lukas Hannen Date: Wed, 3 Feb 2021 01:49:20 +0100 Subject: [PATCH 195/198] fixed typo in README.md Closes: #54 [via git-merge-pr] --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index e6f5749..5695803 100644 --- a/README.md +++ b/README.md @@ -47,7 +47,7 @@ test it yourself. A good starting point is probably the distribution maintained `/etc/pam.d/sudo` file. -### Perist/Timestamp/Timeout +### Persist/Timestamp/Timeout The persist feature is disabled by default and can be enabled with the configure flag `--with-timestamp`. From 9a25a6d7b6be3ed4ffb822c5a3fa178057d18329 Mon Sep 17 00:00:00 2001 From: Duncan Overbruck Date: Wed, 3 Feb 2021 20:55:44 +0100 Subject: [PATCH 196/198] fix some wording in README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 5695803..f15610c 100644 --- a/README.md +++ b/README.md @@ -15,8 +15,8 @@ opendoas: or pam is configured incorrectly. * If you want to use pam; You have to [configure pam](#pam-configuration) and failing to do so correctly might leave a big open door. -* Use the configure script to configure the opendoas. -* Use the default make target to build the software. +* Use the configure script. +* Use the default make target. * If you really want to install a setuid binary that depends on pam being correctly configured, use the make install target to install the software. From 6266763acfd5be984146c4b26f23ed31cdc7fd54 Mon Sep 17 00:00:00 2001 From: Mark Date: Mon, 17 Jan 2022 00:09:56 -0600 Subject: [PATCH 197/198] Fix: improve formatting and add Wikipedia reference. --- README.md | 41 +++++++++++++++++++++-------------------- 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index f15610c..3d532a2 100644 --- a/README.md +++ b/README.md @@ -1,27 +1,28 @@ # OpenDoas: a portable version of OpenBSD's `doas` command -`doas` is a minimal replacement for the venerable `sudo`. It was +[`doas`](https://en.wikipedia.org/wiki/Doas) is a minimal replacement for the venerable `sudo`. It was initially [written by Ted Unangst](http://www.tedunangst.com/flak/post/doas) of the OpenBSD project to provide 95% of the features of `sudo` with a fraction of the codebase. -## Building and installation discouragements +## Building and Installation Warnings There are a few steps you have to carefully consider before building and installing -opendoas: +OpenDoas: -* There are less eyes on random doas ports, just because sudo had a vulnerability +* There are fewer eyes on random `doas` ports, just because `sudo` had a vulnerability does not mean random doas ports are more secure if they are not reviewed - or pam is configured incorrectly. -* If you want to use pam; You have to [configure pam](#pam-configuration) - and failing to do so correctly might leave a big open door. -* Use the configure script. + or [PAM](https://en.wikipedia.org/wiki/Pluggable_authentication_module) is configured incorrectly. + * If you want to use PAM; You have to [configure PAM](#pam-configuration) + and failing to do so correctly might leave a big open door. + +* Use the `configure` script. * Use the default make target. * If you really want to install a setuid binary that depends on - pam being correctly configured, use the make install target + PAM being correctly configured, use the `make install` target to install the software. -## About the port +## About the OpenDoas Port This is not an official port/project from OpenBSD! @@ -29,19 +30,19 @@ As much as possible I've attempted to stick to `doas` as tedu desired it. As things stand it's essentially just code lifted from OpenBSD with PAM or shadow based authentication glommed on to it. -Compatibility functions in libopenbsd come from openbsd directly +Compatibility functions in libopenbsd come from OpenBSD directly (`strtonum.c`, `reallocarray.c`, `strlcpy.c`, `strlcat.c`), from openssh (`readpassphrase.c`) or from sudo (`closefrom.c`). The PAM and shadow authentication code does not come from the OpenBSD project. -### pam configuration +### PAM Configuration -I will not ship pam configuration files, they are distribution specific and +I will not ship PAM configuration files, they are distribution specific and its simply not safe or productive to ship and install those files. -If you want to use opendoas on your system and there is no package that -ships with a working pam configuration file, then you have to write and +If you want to use OpenDoas on your system and there is no package that +ships with a working PAM configuration file, then you have to write and test it yourself. A good starting point is probably the distribution maintained `/etc/pam.d/sudo` @@ -49,15 +50,15 @@ file. ### Persist/Timestamp/Timeout -The persist feature is disabled by default and can be enabled with the configure -flag `--with-timestamp`. +The persist feature is disabled by default and can be enabled with the +`--with-timestamp` configure flag. -This feature is new and potentially dangerous, in the original doas, a kernel API -is used to set and clear timeouts. This API is openbsd specific and no similar API +This feature is new and potentially dangerous, in the original `doas`, a kernel API +is used to set and clear timeouts. This API is OpenBSD specific and no similar API is available on other operating systems. As a workaround, the persist feature is implemented using timestamp files -similar to sudo. +similar to `sudo`. See the comment block in `timestamp.c` for an in-depth description on how timestamps are created and checked to be as safe as possible. From b96106b7e34ac591ae78b1684e9be3a265122463 Mon Sep 17 00:00:00 2001 From: Duncan Overbruck Date: Mon, 21 Feb 2022 22:01:24 +0100 Subject: [PATCH 198/198] pam: always print pam_conv messages to stderr Fixes #95 --- pam.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pam.c b/pam.c index a9e2036..fa483b8 100644 --- a/pam.c +++ b/pam.c @@ -104,8 +104,7 @@ pamconv(int nmsgs, const struct pam_message **msgs, case PAM_ERROR_MSG: case PAM_TEXT_INFO: - if (fprintf(style == PAM_ERROR_MSG ? stderr : stdout, - "%s\n", msgs[i]->msg) < 0) + if (fprintf(stderr, "%s\n", msgs[i]->msg) < 0) goto fail; break;