diff options
| author | Michael Forney <mforney@mforney.org> | 2025-04-29 16:35:44 -0700 |
|---|---|---|
| committer | Michael Forney <mforney@mforney.org> | 2025-04-29 18:08:11 -0700 |
| commit | 06624ed7867c902e0ca44b5ca381e9d4dc157e14 (patch) | |
| tree | b599a63619db2f17915395b1b7c01025a6181159 /pkg/openbsd/patch/0014-doas-Port-to-linux-musl.patch | |
| parent | f14538ac33402854509d46e1a7bdaae091c2fb80 (diff) | |
openbsd: Update to 7.7
Diffstat (limited to 'pkg/openbsd/patch/0014-doas-Port-to-linux-musl.patch')
| -rw-r--r-- | pkg/openbsd/patch/0014-doas-Port-to-linux-musl.patch | 581 |
1 files changed, 581 insertions, 0 deletions
diff --git a/pkg/openbsd/patch/0014-doas-Port-to-linux-musl.patch b/pkg/openbsd/patch/0014-doas-Port-to-linux-musl.patch new file mode 100644 index 00000000..853345af --- /dev/null +++ b/pkg/openbsd/patch/0014-doas-Port-to-linux-musl.patch @@ -0,0 +1,581 @@ +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 + |
