Commit: 179d313d0e7a88486949edce106835e94daf2000
Parent: 7b14b4ce2394732cec6e88e9368b9c0768f1cbc9
Author: Michael Forney
Date: Sun, 26 Feb 2017 16:50:55 -0800
doas: Port to linux/musl
Remove -a login style option and BSD authentication. Instead, compare
against shadow file.
Use timestamp files in /run/doas instead of TIOC*VERAUTH to implement
persist.
Use initgroups/setgid/setuid instead of setusercontext.
Provide UID_MAX and GID_MAX defaults.
Use LOGIN_NAME_MAX instead of _PW_NAME_LEN.
Remove call to closefrom.
Replace calls to errc with err after setting errno.
Call openlog at start to set syslog identity.
Diffstat:
M | doas.1 | | | 9 | --------- |
M | doas.c | | | 97 | +++++++++++++++++++++++++++++++++++++++++++------------------------------------ |
M | doas.h | | | 4 | ++++ |
M | parse.y | | | 1 | + |
A | persist.c | | | 133 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
5 files changed, 191 insertions(+), 53 deletions(-)
diff --git a/doas.1 b/doas.1
@@ -22,7 +22,6 @@
.Sh SYNOPSIS
.Nm doas
.Op Fl Lns
-.Op Fl a Ar style
.Op Fl C Ar config
.Op Fl u Ar user
.Ar command
@@ -67,14 +66,6 @@ The working directory is not changed.
.Pp
The options are as follows:
.Bl -tag -width tenletters
-.It Fl a Ar style
-Use the specified authentication style when validating the user,
-as allowed by
-.Pa /etc/login.conf .
-A list of doas-specific authentication methods may be configured by adding an
-.Sq auth-doas
-entry in
-.Xr login.conf 5 .
.It Fl C Ar config
Parse and check the configuration file
.Ar config ,
diff --git a/doas.c b/doas.c
@@ -20,8 +20,6 @@
#include <sys/ioctl.h>
#include <limits.h>
-#include <login_cap.h>
-#include <bsd_auth.h>
#include <readpassphrase.h>
#include <string.h>
#include <stdio.h>
@@ -33,13 +31,22 @@
#include <syslog.h>
#include <errno.h>
#include <fcntl.h>
+#include <shadow.h>
#include "doas.h"
+#ifndef UID_MAX
+#define UID_MAX 65535
+#endif
+
+#ifndef GID_MAX
+#define GID_MAX 65535
+#endif
+
static void __dead
usage(void)
{
- fprintf(stderr, "usage: doas [-Lns] [-a style] [-C config] [-u user]"
+ fprintf(stderr, "usage: doas [-Lns] [-C config] [-u user]"
" command [args]\n");
exit(1);
}
@@ -193,23 +200,36 @@ checkconfig(const char *confpath, int argc, char **argv,
}
}
+static int
+verifypasswd(const char *user, const char *pass)
+{
+ struct spwd *sp;
+ char *p1, *p2;
+
+ sp = getspnam(user);
+ if (!sp)
+ return 0;
+ p1 = sp->sp_pwdp;
+ if (p1[0] == '!' || p1[0] == '*')
+ return 0;
+ p2 = crypt(pass, p1);
+ if (!p2)
+ return 0;
+ return strcmp(p1, p2) == 0;
+}
+
static void
-authuser(char *myname, char *login_style, int persist)
+authuser(char *myname, int persist)
{
char *challenge = NULL, *response, rbuf[1024], cbuf[128];
- auth_session_t *as;
- int fd = -1;
+ int fd = -1, valid = 0;
- if (persist)
- fd = open("/dev/tty", O_RDWR);
- if (fd != -1) {
- if (ioctl(fd, TIOCCHKVERAUTH) == 0)
+ if (persist) {
+ fd = openpersist(&valid);
+ if (valid)
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)))
@@ -221,21 +241,18 @@ authuser(char *myname, char *login_style, int persist)
response = readpassphrase(challenge, rbuf, sizeof(rbuf),
RPP_REQUIRE_TTY);
if (response == NULL && errno == ENOTTY) {
- syslog(LOG_AUTHPRIV | LOG_NOTICE,
- "tty required for %s", myname);
+ syslog(LOG_NOTICE, "tty required for %s", myname);
errx(1, "a tty is required");
}
- if (!auth_userresponse(as, response, 0)) {
+ if (!verifypasswd(myname, response)) {
explicit_bzero(rbuf, sizeof(rbuf));
- syslog(LOG_AUTHPRIV | LOG_NOTICE,
- "failed auth for %s", myname);
+ syslog(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);
+ setpersist(fd);
close(fd);
}
}
@@ -281,15 +298,14 @@ done:
int
main(int argc, char **argv)
{
- const char *safepath = "/bin:/sbin:/usr/bin:/usr/sbin:"
- "/usr/local/bin:/usr/local/sbin";
+ const char *safepath = "/bin";
const char *confpath = NULL;
char *shargv[] = { NULL, NULL };
char *sh;
const char *p;
const char *cmd;
char cmdline[LINE_MAX];
- char mypwbuf[_PW_BUF_LEN], targpwbuf[_PW_BUF_LEN];
+ char mypwbuf[1024], targpwbuf[1024];
struct passwd mypwstore, targpwstore;
struct passwd *mypw, *targpw;
const struct rule *rule;
@@ -302,28 +318,20 @@ main(int argc, char **argv)
int nflag = 0;
char cwdpath[PATH_MAX];
const char *cwd;
- char *login_style = NULL;
char **envp;
setprogname("doas");
-
- closefrom(STDERR_FILENO + 1);
+ openlog("doas", 0, LOG_AUTHPRIV);
uid = getuid();
- while ((ch = getopt(argc, argv, "a:C:Lnsu:")) != -1) {
+ while ((ch = getopt(argc, argv, "C:Lnsu:")) != -1) {
switch (ch) {
- case 'a':
- login_style = optarg;
- break;
case 'C':
confpath = optarg;
break;
case 'L':
- i = open("/dev/tty", O_RDWR);
- if (i != -1)
- ioctl(i, TIOCCLRVERAUTH);
- exit(i == -1);
+ exit(clearpersist() != 0);
case 'u':
if (parseuid(optarg, &target) != 0)
errx(1, "unknown user");
@@ -391,16 +399,16 @@ main(int argc, char **argv)
cmd = argv[0];
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);
- errc(1, EPERM, NULL);
+ syslog(LOG_NOTICE, "failed command for %s: %s", mypw->pw_name, cmdline);
+ errno = EPERM;
+ err(1, NULL);
}
if (!(rule->options & NOPASS)) {
if (nflag)
errx(1, "Authorization required");
- authuser(mypw->pw_name, login_style, rule->options & PERSIST);
+ authuser(mypw->pw_name, rule->options & PERSIST);
}
if ((p = getenv("PATH")) != NULL)
@@ -427,11 +435,12 @@ main(int argc, char **argv)
if (targpw == NULL)
errx(1, "no passwd entry for target");
- 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");
+ if (initgroups(targpw->pw_name, targpw->pw_gid) < 0)
+ err(1, "initgroups");
+ if (setgid(targpw->pw_gid) < 0)
+ err(1, "setgid");
+ if (setuid(targpw->pw_uid) < 0)
+ err(1, "setuid");
if (pledge("stdio rpath exec", NULL) == -1)
err(1, "pledge");
@@ -444,7 +453,7 @@ main(int argc, char **argv)
if (pledge("stdio exec", NULL) == -1)
err(1, "pledge");
- syslog(LOG_AUTHPRIV | LOG_INFO, "%s ran command %s as %s from %s",
+ syslog(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.h b/doas.h
@@ -36,6 +36,10 @@ struct passwd;
char **prepenv(const struct rule *, const struct passwd *,
const struct passwd *);
+int openpersist(int *valid);
+int setpersist(int fd);
+int clearpersist(void);
+
#define PERMIT 1
#define DENY 2
diff --git a/parse.y b/parse.y
@@ -19,6 +19,7 @@
#include <sys/types.h>
#include <ctype.h>
#include <unistd.h>
+#include <stdlib.h>
#include <stdint.h>
#include <stdarg.h>
#include <stdio.h>
diff --git a/persist.c b/persist.c
@@ -0,0 +1,133 @@
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "doas.h"
+
+#define PERSIST_DIR "/run/doas"
+#define PERSIST_TIMEOUT 5 * 60
+
+static int
+ttyid(dev_t *tty)
+{
+ int fd, i;
+ char buf[BUFSIZ], *p;
+ ssize_t n;
+
+ fd = open("/proc/self/stat", O_RDONLY);
+ if (fd == -1)
+ return -1;
+ n = read(fd, buf, sizeof(buf) - 1);
+ if (n >= 0)
+ buf[n] = '\0';
+ /* check that we read the whole file */
+ n = read(fd, buf, 1);
+ close(fd);
+ if (n != 0)
+ return -1;
+ p = strrchr(buf, ')');
+ if (!p)
+ return -1;
+ ++p;
+ /* ttr_nr is the 5th field after executable name, so skip the next 4 */
+ for (i = 0; i < 4; ++i) {
+ p = strchr(++p, ' ');
+ if (!p)
+ return -1;
+ }
+ *tty = strtol(p, &p, 10);
+ if (*p != ' ')
+ return -1;
+ return 0;
+}
+
+static int
+persistpath(char *buf, size_t len)
+{
+ dev_t tty;
+ int n;
+
+ if (ttyid(&tty) < 0)
+ return -1;
+ n = snprintf(buf, len, PERSIST_DIR "/%ju-%ju", (uintmax_t)getuid(), (uintmax_t)tty);
+ if (n < 0 || n >= (int)len)
+ return -1;
+ return 0;
+}
+
+int
+openpersist(int *valid)
+{
+ char path[256];
+ struct stat st;
+ struct timespec ts;
+ int fd;
+
+ if (stat(PERSIST_DIR, &st) < 0) {
+ if (errno != ENOENT)
+ return -1;
+ if (mkdir(PERSIST_DIR, 0700) < 0)
+ return -1;
+ } else if (st.st_uid != 0 || st.st_mode != (S_IFDIR | 0700)) {
+ return -1;
+ }
+ if (persistpath(path, sizeof(path)) < 0)
+ return -1;
+ fd = open(path, O_RDONLY);
+ if (fd == -1) {
+ char tmp[256];
+ struct timespec ts[2] = { { .tv_nsec = UTIME_OMIT }, { 0 } };
+ int n;
+
+ n = snprintf(tmp, sizeof(tmp), PERSIST_DIR "/.tmp-%d", getpid());
+ if (n < 0 || n >= (int)sizeof(tmp))
+ return -1;
+ fd = open(tmp, O_RDONLY | O_CREAT | O_EXCL, 0);
+ if (fd == -1)
+ return -1;
+ if (futimens(fd, ts) < 0 || rename(tmp, path) < 0) {
+ close(fd);
+ unlink(tmp);
+ return -1;
+ }
+ *valid = 0;
+ } else {
+ *valid = clock_gettime(CLOCK_BOOTTIME, &ts) == 0 &&
+ fstat(fd, &st) == 0 &&
+ (ts.tv_sec < st.st_mtim.tv_sec ||
+ ts.tv_sec == st.st_mtim.tv_sec && ts.tv_nsec < st.st_mtim.tv_nsec) &&
+ st.st_mtime - ts.tv_sec <= PERSIST_TIMEOUT;
+ }
+ return fd;
+}
+
+int
+setpersist(int fd)
+{
+ struct timespec times[2];
+
+ if (clock_gettime(CLOCK_BOOTTIME, ×[1]) < 0)
+ return -1;
+ times[0].tv_nsec = UTIME_OMIT;
+ times[1].tv_sec += PERSIST_TIMEOUT;
+ return futimens(fd, times);
+}
+
+int
+clearpersist(void)
+{
+ char path[256];
+
+ if (persistpath(path, sizeof(path)) < 0)
+ return -1;
+ if (unlink(path) < 0 && errno != ENOENT)
+ return -1;
+ return 0;
+}