0014-doas-Port-to-linux-musl.patch (14338B)
- From cca6c84b472255c4d015d7d97225791796f61392 Mon Sep 17 00:00:00 2001
- From: Michael Forney <mforney@mforney.org>
- Date: Sun, 26 Feb 2017 16:50:55 -0800
- Subject: [PATCH] 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.
- Remove unveil/pledge since they aren't supported on Linux.
- Simplify handling of PATH in the environment since we don't have
- login.conf with per-user default PATH.
- ---
- usr.bin/doas/doas.1 | 9 ---
- usr.bin/doas/doas.c | 168 +++++++++++++----------------------------
- usr.bin/doas/doas.h | 6 +-
- usr.bin/doas/env.c | 17 ++---
- usr.bin/doas/parse.y | 1 +
- usr.bin/doas/persist.c | 133 ++++++++++++++++++++++++++++++++
- 6 files changed, 198 insertions(+), 136 deletions(-)
- create mode 100644 usr.bin/doas/persist.c
- diff --git a/usr.bin/doas/doas.1 b/usr.bin/doas/doas.1
- index 25827cc7104..3542680faf5 100644
- --- a/usr.bin/doas/doas.1
- +++ b/usr.bin/doas/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/usr.bin/doas/doas.c b/usr.bin/doas/doas.c
- index 3999b2e2f64..32532359267 100644
- --- a/usr.bin/doas/doas.c
- +++ b/usr.bin/doas/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 [arg ...]\n");
- exit(1);
- }
- @@ -203,16 +210,28 @@ checkconfig(const char *confpath, int argc, char **argv,
- }
- static int
- -authuser_checkpass(char *myname, char *login_style)
- +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 int
- +authuser_checkpass(char *myname)
- {
- char *challenge = NULL, *response, rbuf[1024], cbuf[128];
- - auth_session_t *as;
- - if (!(as = auth_userchallenge(myname, login_style, "auth-doas",
- - &challenge))) {
- - warnx("Authentication failed");
- - return AUTH_FAILED;
- - }
- if (!challenge) {
- char host[HOST_NAME_MAX + 1];
- @@ -225,14 +244,12 @@ authuser_checkpass(char *myname, char *login_style)
- 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);
- warnx("Authentication failed");
- return AUTH_FAILED;
- }
- @@ -241,79 +258,36 @@ authuser_checkpass(char *myname, char *login_style)
- }
- static void
- -authuser(char *myname, char *login_style, int persist)
- +authuser(char *myname, int persist)
- {
- - int i, fd = -1;
- + int i, 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;
- }
- for (i = 0; i < AUTH_RETRIES; i++) {
- - if (authuser_checkpass(myname, login_style) == AUTH_OK)
- + if (authuser_checkpass(myname) == AUTH_OK)
- goto good;
- }
- exit(1);
- good:
- if (fd != -1) {
- - int secs = 5 * 60;
- - ioctl(fd, TIOCSETVERAUTH, &secs);
- + setpersist(fd);
- close(fd);
- }
- }
- -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);
- -}
- -
- int
- main(int argc, char **argv)
- {
- - const char *safepath = "/bin:/sbin:/usr/bin:/usr/sbin:"
- - "/usr/local/bin:/usr/local/sbin";
- 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;
- @@ -326,28 +300,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");
- @@ -418,50 +384,30 @@ main(int argc, char **argv)
- rv = permit(uid, groups, ngroups, &rule, target, cmd,
- (const char **)argv + 1);
- if (rv != 0) {
- - syslog(LOG_AUTHPRIV | LOG_NOTICE,
- - "command not permitted for %s: %s", mypw->pw_name, cmdline);
- - errc(1, EPERM, NULL);
- + syslog(LOG_NOTICE, "command not permitted for %s: %s", mypw->pw_name, cmdline);
- + errno = EPERM;
- + err(1, NULL);
- }
- if (!(rule->options & NOPASS)) {
- if (nflag)
- errx(1, "Authentication required");
- - authuser(mypw->pw_name, login_style, rule->options & PERSIST);
- + authuser(mypw->pw_name, rule->options & PERSIST);
- }
- - if ((p = getenv("PATH")) != NULL)
- - formerpath = strdup(p);
- - if (formerpath == NULL)
- - formerpath = "";
- -
- - if (unveil(_PATH_LOGIN_CONF, "r") == -1)
- - err(1, "unveil %s", _PATH_LOGIN_CONF);
- - if (unveil(_PATH_LOGIN_CONF ".db", "r") == -1)
- - err(1, "unveil %s.db", _PATH_LOGIN_CONF);
- - if (unveil(_PATH_LOGIN_CONF_D, "r") == -1)
- - err(1, "unveil %s", _PATH_LOGIN_CONF_D);
- - if (rule->cmd) {
- - if (setenv("PATH", safepath, 1) == -1)
- - err(1, "failed to set PATH '%s'", safepath);
- - }
- - if (unveilcommands(getenv("PATH"), cmd) == 0)
- - goto fail;
- -
- - if (pledge("stdio rpath getpw exec id", NULL) == -1)
- - err(1, "pledge");
- -
- rv = getpwuid_r(target, &targpwstore, targpwbuf, sizeof(targpwbuf), &targpw);
- if (rv != 0)
- err(1, "getpwuid_r failed");
- 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 | LOGIN_SETENV | LOGIN_SETRTABLE) != 0)
- - errx(1, "failed to set user context for target");
- + if (initgroups(targpw->pw_name, targpw->pw_gid) == -1)
- + err(1, "initgroups");
- + if (setgid(targpw->pw_gid) == -1)
- + err(1, "setgid");
- + if (setuid(targpw->pw_uid) == -1)
- + err(1, "setuid");
- if (pledge("stdio rpath exec", NULL) == -1)
- err(1, "pledge");
- @@ -475,23 +421,17 @@ main(int argc, char **argv)
- err(1, "pledge");
- if (!(rule->options & NOLOG)) {
- - 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);
- - /* setusercontext set path for the next process, so reset it for us */
- if (rule->cmd) {
- 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);
- -fail:
- if (errno == ENOENT)
- errx(1, "%s: command not found", cmd);
- err(1, "%s", cmd);
- diff --git a/usr.bin/doas/doas.h b/usr.bin/doas/doas.h
- index ce6a03618ac..363e2626c23 100644
- --- a/usr.bin/doas/doas.h
- +++ b/usr.bin/doas/doas.h
- @@ -29,13 +29,17 @@ extern struct rule **rules;
- extern size_t nrules;
- extern int parse_error;
- -extern const char *formerpath;
- +extern const char *safepath;
- 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/usr.bin/doas/env.c b/usr.bin/doas/env.c
- index 2d93a4089b6..dc9be691955 100644
- --- a/usr.bin/doas/env.c
- +++ b/usr.bin/doas/env.c
- @@ -28,7 +28,7 @@
- #include "doas.h"
- -const char *formerpath;
- +const char *safepath = "/bin";
- struct envnode {
- RB_ENTRY(envnode) node;
- @@ -103,7 +103,7 @@ createenv(const struct rule *rule, const struct passwd *mypw,
- 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, "PATH", safepath);
- addnode(env, "SHELL", targpw->pw_shell);
- addnode(env, "USER", targpw->pw_name);
- @@ -200,17 +200,10 @@ fillenv(struct env *env, const char **envlist)
- /* assign value or inherit from environ */
- if (eq) {
- val = eq + 1;
- - if (*val == '$') {
- - if (strcmp(val + 1, "PATH") == 0)
- - val = formerpath;
- - else
- - val = getenv(val + 1);
- - }
- + if (*val == '$')
- + val = getenv(val + 1);
- } else {
- - if (strcmp(name, "PATH") == 0)
- - val = formerpath;
- - else
- - val = getenv(name);
- + val = getenv(name);
- }
- /* at last, we have something to insert */
- if (val) {
- diff --git a/usr.bin/doas/parse.y b/usr.bin/doas/parse.y
- index 604becb5445..e5fc912a9c4 100644
- --- a/usr.bin/doas/parse.y
- +++ b/usr.bin/doas/parse.y
- @@ -20,6 +20,7 @@
- #include <ctype.h>
- #include <limits.h>
- #include <unistd.h>
- +#include <stdlib.h>
- #include <stdint.h>
- #include <stdarg.h>
- #include <stdio.h>
- diff --git a/usr.bin/doas/persist.c b/usr.bin/doas/persist.c
- new file mode 100644
- index 00000000000..4ad1bf1efbf
- --- /dev/null
- +++ b/usr.bin/doas/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;
- +}
- --
- 2.49.0