summaryrefslogtreecommitdiff
path: root/pkg/openbsd/patch/0014-doas-Port-to-linux-musl.patch
diff options
context:
space:
mode:
authorMichael Forney <mforney@mforney.org>2025-04-29 16:35:44 -0700
committerMichael Forney <mforney@mforney.org>2025-04-29 18:08:11 -0700
commit06624ed7867c902e0ca44b5ca381e9d4dc157e14 (patch)
treeb599a63619db2f17915395b1b7c01025a6181159 /pkg/openbsd/patch/0014-doas-Port-to-linux-musl.patch
parentf14538ac33402854509d46e1a7bdaae091c2fb80 (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.patch581
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, &times[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
+