aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md54
-rwxr-xr-xconfigure23
-rw-r--r--doas.125
-rw-r--r--doas.c215
-rw-r--r--doas.conf.522
-rw-r--r--doas.h7
-rw-r--r--env.c58
-rw-r--r--libopenbsd/closefrom.c160
-rw-r--r--libopenbsd/execvpe.c158
-rw-r--r--libopenbsd/setresuid.c36
-rw-r--r--pam.c7
-rw-r--r--timestamp.c281
12 files changed, 373 insertions, 673 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.
diff --git a/configure b/configure
index 4dfd287..4ae9b69 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
@@ -67,9 +74,6 @@ done
CONFIG_MK=config.mk
rm -f "$CONFIG_MK"
-# : ${VERSION:="$(git describe --dirty --tags --long --always)"}
-: ${VERSION:="6.2"}
-
cat <<EOF >>$CONFIG_MK
PREFIX ?= ${PREFIX:="/usr"}
EPREFIX ?= ${EPREFIX:="${PREFIX}"}
@@ -78,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
@@ -108,7 +111,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
@@ -326,9 +329,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 +340,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().
@@ -478,3 +477,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
diff --git a/doas.1 b/doas.1
index 26d63df..7360be3 100644
--- a/doas.1
+++ b/doas.1
@@ -39,6 +39,31 @@ 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 ,
+.Ev LOGNAME ,
+.Ev PATH ,
+.Ev SHELL ,
+and
+.Ev USER
+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
+.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
diff --git a/doas.c b/doas.c
index 6223aff..cbc3657 100644
--- a/doas.c
+++ b/doas.c
@@ -20,10 +20,6 @@
#include <sys/ioctl.h>
#include <limits.h>
-#if __OpenBSD__
-# include <login_cap.h>
-# include <readpassphrase.h>
-#endif
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
@@ -33,19 +29,16 @@
#include <grp.h>
#include <syslog.h>
#include <errno.h>
+#include <fcntl.h>
#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);
}
@@ -57,9 +50,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 +80,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;
@@ -200,54 +197,36 @@ checkconfig(const char *confpath, int argc, char **argv,
}
}
-#ifdef USE_BSD_AUTH
-static void
-authuser(char *myname, char *login_style, int persist)
+int
+mygetpwuid_r(uid_t uid, struct passwd *pwd, struct passwd **result)
{
- 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;
+ 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 (!(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);
- }
+ return rv;
}
-#endif
int
main(int argc, char **argv)
@@ -257,24 +236,22 @@ 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];
- char myname[_PW_NAME_LEN + 1];
- struct passwd *pw;
+ 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];
const char *cwd;
char **envp;
-#ifdef USE_BSD_AUTH
- char *login_style = NULL;
-#endif
setprogname("doas");
@@ -282,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);
@@ -333,11 +294,11 @@ 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))
- errx(1, "pw_name too long");
+ rv = mygetpwuid_r(uid, &mypwstore, &mypw);
+ if (rv != 0)
+ err(1, "getpwuid_r failed");
+ if (mypw == NULL)
+ errx(1, "no passwd entry for self");
ngroups = getgroups(NGROUPS_MAX, groups);
if (ngroups == -1)
err(1, "can't get groups");
@@ -346,9 +307,7 @@ main(int argc, char **argv)
if (sflag) {
sh = getenv("SHELL");
if (sh == NULL || *sh == '\0') {
- shargv[0] = strdup(pw->pw_shell);
- if (shargv[0] == NULL)
- err(1, NULL);
+ shargv[0] = mypw->pw_shell;
} else
shargv[0] = sh;
argv = shargv;
@@ -379,89 +338,69 @@ 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);
}
-#if defined(__OpenBSD__)
+#if defined(USE_SHADOW)
if (!(rule->options & NOPASS)) {
if (nflag)
errx(1, "Authorization required");
- authuser(myname, login_style, rule->options & PERSIST);
+ shadowauth(mypw->pw_name, rule->options & PERSIST);
}
+#elif !defined(USE_PAM)
+ /* no authentication provider, only allow NOPASS rules */
+ (void) nflag;
+ if (!(rule->options & NOPASS))
+ errx(1, "Authorization required");
+#endif
- 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_SHADOW)
- if (!(rule->options & NOPASS)) {
- if (nflag)
- errx(1, "Authorization required");
+ if ((p = getenv("PATH")) != NULL)
+ formerpath