swm_version: 1 base: distro_version: dev pins: {} mutations: - type: source_patch tarball: https://github.com/Duncaen/OpenDoas/archive/v6.8.2.tar.gz sha256: 6da058a0e70b7543bc60624389b0b00b686189ec933828c522bf8b2600495a67 patches: - "Patch-Source: https://github.com/Duncaen/OpenDoas/pull/71 (rebased + one extra commit)\n--\nFrom a6aa77d9f4b9ad4556e478d6779dbebd4143a98a Mon Sep 17 00:00:00 2001\nFrom: Ariadne Conill \nDate: Wed, 4 Aug 2021 04:47:04 -0600\nSubject: [PATCH] add --with-confdir feature\n\nThis adds support for an /etc/doas.d configuration directory as discussed in #61. It is disabled by default.\n\ndiff --git a/GNUmakefile b/GNUmakefile\nindex 9470202..22be971 100644\n--- a/GNUmakefile\n+++ b/GNUmakefile\n@@ -27,6 +27,7 @@ install: ${PROG} ${PAM_DOAS} ${MAN}\n \t[ -n \"${PAM_DOAS}\" ] && chmod 0644 ${DESTDIR}${PAMDIR}/doas || true\n \tcp -f doas.1 ${DESTDIR}${MANDIR}/man1\n \tcp -f doas.conf.5 ${DESTDIR}${MANDIR}/man5\n+\tcp -f doas.d.5 ${DESTDIR}${MANDIR}/man5\n \n uninstall:\n \trm -f ${DESTDIR}${BINDIR}/${PROG}\ndiff --git a/README.md b/README.md\nindex 20ef9f2..92acded 100644\n--- a/README.md\n+++ b/README.md\n@@ -43,3 +43,12 @@ similar to sudo.\n \n See the comment block in `timestamp.c` for an in-depth description on how\n timestamps are created and checked to be as safe as possible.\n+\n+### `--with-doas-confdir`\n+\n+An optional feature can be enabled which will result in `doas` reading configuration\n+snippets from `/etc/doas.d`. These configuration snippets have the same requirements\n+as `/etc/doas.conf` (owned by root, not world-writable).\n+\n+If this feature is enabled, only the `/etc/doas.d` directory is read, and the historical\n+`/etc/doas.conf` file is ignored.\n\\ No newline at end of file\ndiff --git a/configure b/configure\nindex 1c5d989..22a078e 100755\n--- a/configure\n+++ b/configure\n@@ -28,6 +28,7 @@ usage: configure [options]\n --without-shadow disable shadow support\n \n --with-timestamp enable timestamp support\n+ --with-doas-confdir enable configuration directory support\n \n --uid-max=NUM set UID_MAX (default 65535)\n --gid-max=NUM set GID_MAX (default 65535)\n@@ -39,6 +40,7 @@ EOF\n \n # defaults\n WITHOUT_TIMESTAMP=yes\n+WITHOUT_CONFDIR=yes\n UID_MAX=65535\n GID_MAX=65535\n \n@@ -58,6 +60,8 @@ for x; do\n \t--target) TARGET=$var ;;\n \t--enable-debug) DEBUG=yes ;;\n \t--enable-static) BUILD_STATIC=yes ;;\n+\t--with-doas-confdir) WITHOUT_CONFDIR= ;;\n+\t--without-doas-confdir) WITHOUT_CONFDIR=yes ;;\n \t--with-pam) WITHOUT_PAM=; WITHOUT_SHADOW=yes ;;\n \t--with-shadow) WITHOUT_SHADOW=; WITHOUT_PAM=yes ;;\n \t--without-pam) WITHOUT_PAM=yes ;;\n@@ -565,4 +569,8 @@ fi\n \n printf '#define DOAS_CONF \"%s/doas.conf\"\\n' \"${SYSCONFDIR}\" >>$CONFIG_H\n \n+if [ -z \"$WITHOUT_CONFDIR\" ]; then\n+\tprintf '#define DOAS_CONFDIR \"%s/doas.d\"\\n' \"${SYSCONFDIR}\" >>$CONFIG_H\n+fi\n+\n printf '\\n#endif /* CONFIG_H */\\n' >>$CONFIG_H\ndiff --git a/doas.c b/doas.c\nindex ac3a42a..d77186b 100644\n--- a/doas.c\n+++ b/doas.c\n@@ -35,6 +35,7 @@\n #include \n #include \n #include \n+#include \n \n #include \"openbsd.h\"\n #include \"doas.h\"\n@@ -155,6 +156,7 @@ permit(uid_t uid, gid_t *groups, int ngroups, const struct rule **lastr,\n static void\n parseconfig(const char *filename, int checkperms)\n {\n+\textern const char *yyfn;\n \textern FILE *yyfp;\n \textern int yyparse(void);\n \tstruct stat sb;\n@@ -164,6 +166,8 @@ parseconfig(const char *filename, int checkperms)\n \t\terr(1, checkperms ? \"doas is not enabled, %s\" :\n \t\t \"could not open config file %s\", filename);\n \n+\tyyfn = filename;\n+\n \tif (checkperms) {\n \t\tif (fstat(fileno(yyfp), &sb) != 0)\n \t\t\terr(1, \"fstat(\\\"%s\\\")\", filename);\n@@ -174,11 +178,82 @@ parseconfig(const char *filename, int checkperms)\n \t}\n \n \tyyparse();\n+\tyyfn = NULL;\n+\n \tfclose(yyfp);\n \tif (parse_errors)\n \t\texit(1);\n }\n \n+#ifdef DOAS_CONFDIR\n+static int\n+isconfdir(const char *dirpath)\n+{\n+\tstruct stat sb;\n+\n+\tif (lstat(dirpath, &sb) != 0) {\n+\t\tif (errno != ENOENT)\n+\t\t\terr(1, \"lstat(\\\"%s\\\")\", dirpath);\n+\n+\t\terrno = ENOTDIR;\n+\t\treturn 0;\n+\t}\n+\n+\tif ((sb.st_mode & (S_IFMT)) == S_IFDIR)\n+\t\treturn 1;\n+\n+\terrno = ENOTDIR;\n+\treturn 0;\n+}\n+\n+static void\n+parseconfdir(const char *dirpath, int checkperms)\n+{\n+\tstruct dirent **dirent_table;\n+\tint i, m, dirent_count;\n+\tchar pathbuf[PATH_MAX];\n+\n+\tif (!isconfdir(dirpath))\n+\t\terr(1, checkperms ? \"doas is not enabled, %s\" :\n+\t\t \"could not open config directory %s\", dirpath);\n+\n+\tdirent_count = scandir(dirpath, &dirent_table, NULL, alphasort);\n+\tif (dirent_count < 0)\n+\t\terr(1, checkperms ? \"doas is not enabled, %s\" :\n+\t\t \"could not open config directory %s\", dirpath);\n+\n+\tfor (i = 0, m = 0; i < dirent_count; i++)\n+\t{\n+\t\tstruct stat sb;\n+\t\tsize_t pathlen;\n+\n+\t\tpathlen = snprintf(pathbuf, sizeof pathbuf, \"%s/%s\", dirpath, dirent_table[i]->d_name);\n+\t\tfree(dirent_table[i]);\n+\n+\t\t/* make sure path ends in .conf */\n+\t\tif (pathlen < 6)\n+\t\t\tcontinue;\n+\n+\t\tif (strcmp(pathbuf + (pathlen - 5), \".conf\"))\n+\t\t\tcontinue;\n+\n+\t\tif (stat(pathbuf, &sb) != 0)\n+\t\t\terr(1, \"stat(\\\"%s\\\")\", pathbuf);\n+\n+\t\tif ((sb.st_mode & (S_IFMT)) != S_IFREG)\n+\t\t\tcontinue;\n+\n+\t\tparseconfig(pathbuf, checkperms);\n+\t\tm++;\n+\t}\n+\n+\tfree(dirent_table);\n+\n+\tif (!m)\n+\t\terrx(1, \"doas is not enabled, %s: no matching configuration files found\\n\", dirpath);\n+}\n+#endif\n+\n static void __dead\n checkconfig(const char *confpath, int argc, char **argv,\n uid_t uid, gid_t *groups, int ngroups, uid_t target)\n@@ -188,6 +263,11 @@ checkconfig(const char *confpath, int argc, char **argv,\n \tif (setresuid(uid, uid, uid) != 0)\n \t\terr(1, \"setresuid\");\n \n+#ifdef DOAS_CONFDIR\n+\tif (isconfdir(confpath))\n+\t\tparseconfdir(confpath, 0);\n+\telse\n+#endif\n \tparseconfig(confpath, 0);\n \tif (!argc)\n \t\texit(0);\n@@ -330,6 +410,11 @@ main(int argc, char **argv)\n \tif (geteuid())\n \t\terrx(1, \"not installed setuid\");\n \n+#ifdef DOAS_CONFDIR\n+\tif (isconfdir(DOAS_CONFDIR))\n+\t\tparseconfdir(DOAS_CONFDIR, 1);\n+\telse\n+#endif\n \tparseconfig(DOAS_CONF, 1);\n \n \t/* cmdline is used only for logging, no need to abort on truncate */\ndiff --git a/doas.conf.5 b/doas.conf.5\nindex e98bfbe..e90d512 100644\n--- a/doas.conf.5\n+++ b/doas.conf.5\n@@ -143,6 +143,7 @@ permit nopass keepenv setenv { PATH } root as root\n .Ed\n .Sh SEE ALSO\n .Xr doas 1 ,\n+.Xr doas.d 5 ,\n .Xr syslogd 8\n .Sh HISTORY\n The\ndiff --git a/doas.d.5 b/doas.d.5\nnew file mode 100644\nindex 0000000..c5eaa72\n--- /dev/null\n+++ b/doas.d.5\n@@ -0,0 +1,50 @@\n+.\\\"Copyright (c) 2021 Ariadne Conill \n+.\\\"\n+.\\\"Permission to use, copy, modify, and distribute this software for any\n+.\\\"purpose with or without fee is hereby granted, provided that the above\n+.\\\"copyright notice and this permission notice appear in all copies.\n+.\\\"\n+.\\\"THE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES\n+.\\\"WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF\n+.\\\"MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR\n+.\\\"ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES\n+.\\\"WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN\n+.\\\"ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF\n+.\\\"OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.\n+.Dd $Mdocdate: October 9 2020 $\n+.Dt DOAS.D 5\n+.Os\n+.Sh NAME\n+.Nm doas.d\n+.Nd doas configuration directory\n+.Sh DESCRIPTION\n+The\n+.Xr doas 1\n+utility executes commands as other users according to the rules\n+configured in either the configuration file or, optionally, the\n+configuration directory. The preference to use the configuration\n+file or configuration directory is determined at compile time,\n+.Xr doas 1\n+will only consult one or the other.\n+.Pp\n+Configuration snippets stored in the configuration directory\n+follow the same rules as the classic\n+.Xr doas 1\n+configuration file, documented in\n+.Xr doas.conf 5 .\n+They must end with the .conf extension, or they will be ignored.\n+.Pp\n+These snippets are read in alphabetical order and thus can be\n+ordered in the same way as other configuration directories.\n+.Sh FILES\n+.Bl -tag -width /etc/doas.d -compact\n+.It Pa /etc/doas.d\n+.Xr doas 1\n+configuration directory.\n+.Sh SEE ALSO\n+.Xr doas 1 ,\n+.Xr doas.conf 5\n+.Sh HISTORY\n+The\n+.Nm\n+configuration directory first appeared in OpenDoas.\ndiff --git a/parse.y b/parse.y\nindex 388c2a5..c6d7ebf 100644\n--- a/parse.y\n+++ b/parse.y\n@@ -49,6 +49,7 @@ typedef struct {\n } yystype;\n #define YYSTYPE yystype\n \n+const char *yyfn;\n FILE *yyfp;\n \n struct rule **rules;\n@@ -203,7 +204,7 @@ yyerror(const char *fmt, ...)\n \tva_start(va, fmt);\n \tvfprintf(stderr, fmt, va);\n \tva_end(va);\n-\tfprintf(stderr, \" at line %d\\n\", yylval.lineno + 1);\n+\tfprintf(stderr, \" at %s, line %d\\n\", yyfn, yylval.lineno + 1);\n \tparse_errors++;\n }\n \n--\nFrom c871cf723cc4cc1045ffc7de380f5271b4c29acf Mon Sep 17 00:00:00 2001\nFrom: Jakub Jirutka \nDate: Sat, 13 May 2023 22:38:23 +0200\nSubject: [PATCH 9/9] read both /etc/doas.conf and /etc/doas.d/*.conf if\n confdir is enabled\n\nThe current behaviour of the configuration directory was required by the\nupstream, but it doesn't conform to established conventions used by\nvirtually all programs on Linux that support modular configuration, and\nwhat the users naturally expect. Also, when someone used to vanilla\nOpenDoas or doas on BSD comes to Alpine, the way they're used to\nconfiguring it (via /etc/doas.conf) will no work without notice! It\nhas already caused some problems and confusion.\n\nThe current behaviour is: if /etc/doas.d exists, there must be at least one\n*.conf file and /etc/doas.conf is *ignored*.\n\nSince it doesn't look like the upstream will ever merge this, better to\nfix this patch to work as it should from the beginning...\n\nThis new behaviour: /etc/doas.conf must always exist (as in unpatched\nOpenDoas) and will be read first; if /etc/doas.d exists and there are any\n*.conf files, they will be loaded as well.\n---\n README.md | 7 +++----\n doas.c | 22 +++++-----------------\n doas.d.5 | 14 +++++---------\n 3 files changed, 13 insertions(+), 30 deletions(-)\n\ndiff --git a/README.md b/README.md\nindex 92acded..d5b2e72 100644\n--- a/README.md\n+++ b/README.md\n@@ -48,7 +48,6 @@ timestamps are created and checked to be as safe as possible.\n \n An optional feature can be enabled which will result in `doas` reading configuration\n snippets from `/etc/doas.d`. These configuration snippets have the same requirements\n-as `/etc/doas.conf` (owned by root, not world-writable).\n-\n-If this feature is enabled, only the `/etc/doas.d` directory is read, and the historical\n-`/etc/doas.conf` file is ignored.\n\\ No newline at end of file\n+as `/etc/doas.conf` (owned by root, not world-writable). The main configuration file\n+`/etc/doas.conf` is still required to exist and it is read before `/etc/doas.d`. It is\n+not an error if `/etc/doas.d` does not exist or no matching files are found there.\ndiff --git a/doas.c b/doas.c\nindex d77186b..affbe39 100644\n--- a/doas.c\n+++ b/doas.c\n@@ -210,19 +210,14 @@ static void\n parseconfdir(const char *dirpath, int checkperms)\n {\n \tstruct dirent **dirent_table;\n-\tint i, m, dirent_count;\n+\tint i, dirent_count;\n \tchar pathbuf[PATH_MAX];\n \n-\tif (!isconfdir(dirpath))\n-\t\terr(1, checkperms ? \"doas is not enabled, %s\" :\n-\t\t \"could not open config directory %s\", dirpath);\n-\n \tdirent_count = scandir(dirpath, &dirent_table, NULL, alphasort);\n \tif (dirent_count < 0)\n-\t\terr(1, checkperms ? \"doas is not enabled, %s\" :\n-\t\t \"could not open config directory %s\", dirpath);\n+\t\treturn;\n \n-\tfor (i = 0, m = 0; i < dirent_count; i++)\n+\tfor (i = 0; i < dirent_count; i++)\n \t{\n \t\tstruct stat sb;\n \t\tsize_t pathlen;\n@@ -244,13 +239,9 @@ parseconfdir(const char *dirpath, int checkperms)\n \t\t\tcontinue;\n \n \t\tparseconfig(pathbuf, checkperms);\n-\t\tm++;\n \t}\n \n \tfree(dirent_table);\n-\n-\tif (!m)\n-\t\terrx(1, \"doas is not enabled, %s: no matching configuration files found\\n\", dirpath);\n }\n #endif\n \n@@ -263,12 +254,11 @@ checkconfig(const char *confpath, int argc, char **argv,\n \tif (setresuid(uid, uid, uid) != 0)\n \t\terr(1, \"setresuid\");\n \n+\tparseconfig(confpath, 0);\n #ifdef DOAS_CONFDIR\n \tif (isconfdir(confpath))\n \t\tparseconfdir(confpath, 0);\n-\telse\n #endif\n-\tparseconfig(confpath, 0);\n \tif (!argc)\n \t\texit(0);\n \n@@ -410,13 +400,11 @@ main(int argc, char **argv)\n \tif (geteuid())\n \t\terrx(1, \"not installed setuid\");\n \n+\tparseconfig(DOAS_CONF, 1);\n #ifdef DOAS_CONFDIR\n \tif (isconfdir(DOAS_CONFDIR))\n \t\tparseconfdir(DOAS_CONFDIR, 1);\n-\telse\n #endif\n-\tparseconfig(DOAS_CONF, 1);\n-\n \t/* cmdline is used only for logging, no need to abort on truncate */\n \t(void)strlcpy(cmdline, argv[0], sizeof(cmdline));\n \tfor (i = 1; i < argc; i++) {\ndiff --git a/doas.d.5 b/doas.d.5\nindex c5eaa72..5911a12 100644\n--- a/doas.d.5\n+++ b/doas.d.5\n@@ -21,14 +21,9 @@\n The\n .Xr doas 1\n utility executes commands as other users according to the rules\n-configured in either the configuration file or, optionally, the\n-configuration directory. The preference to use the configuration\n-file or configuration directory is determined at compile time,\n-.Xr doas 1\n-will only consult one or the other.\n-.Pp\n-Configuration snippets stored in the configuration directory\n-follow the same rules as the classic\n+configured in the configuration file and, optionally, the\n+configuration directory. Configuration snippets stored in the\n+configuration directory follow the same rules as the classic\n .Xr doas 1\n configuration file, documented in\n .Xr doas.conf 5 .\n@@ -47,4 +42,5 @@ configuration directory.\n .Sh HISTORY\n The\n .Nm\n-configuration directory first appeared in OpenDoas.\n+configuration directory first appeared as a patch for doas on\n+Alpine Linux and it is not supported in upstream OpenDoas.\n" - | --- OpenDoas-6.8.1.orig/doas.conf.5 +++ OpenDoas-6.8.1/doas.conf.5 @@ -114,11 +114,11 @@ it is not considered a keyword. .El .Sh FILES -.Bl -tag -width /etc/examples/doas.conf -compact +.Bl -tag -width /usr/share/doc/doas/doas.conf.example -compact .It Pa /etc/doas.conf .Xr doas 1 configuration file. -.It Pa /etc/examples/doas.conf +.It Pa /usr/share/doc/doas/doas.conf.example Example configuration file. .El .Sh EXAMPLES - "From: Jakub Jirutka \nDate: Tue, 28 Jun 2022 22:36:16 +0200\nSubject: [PATCH] Change default PATH to the Alpine's default\n\nUse the same PATH as in openssh, sudo and our /etc/profile.\n\n--- a/doas.c\n+++ b/doas.c\n@@ -238,8 +238,8 @@\n int\n main(int argc, char **argv)\n {\n-\tconst char *safepath = \"/bin:/sbin:/usr/bin:/usr/sbin:\"\n-\t \"/usr/local/bin:/usr/local/sbin\";\n+\tconst char *safepath = \"/usr/local/sbin:/usr/local/bin:\"\n+\t \"/usr/sbin:/usr/bin:/sbin:/bin\";\n \tconst char *confpath = NULL;\n \tchar *shargv[] = { NULL, NULL };\n \tchar *sh;\n" - "This patch has been taken from OpenBSD upstream, it changes permit bits to make\nthem more rowhammer-resistent. A similar patch has also been committed to sudo.\n\nThe patch has not made its way into OpenDoas yet, but at the time of writting\nOpenDoas upstream is rather stale (last commit was done 2 years ago).\n\nSee:\n\n* https://github.com/openbsd/src/commit/38599afa1d1d1f14a897b01350e8ce94486e1788\n* https://github.com/sudo-project/sudo/commit/7873f8334c8d31031f8cfa83bd97ac6029309e4f\n\ndiff --git a/doas.c b/doas.c\nindex ac3a42a..93f0836 100644\n--- a/doas.c\n+++ b/doas.c\n@@ -148,8 +148,10 @@ permit(uid_t uid, gid_t *groups, int ngroups, const struct rule **lastr,\n \t\t\t*lastr = rules[i];\n \t}\n \tif (!*lastr)\n+\t\treturn -1;\n+\tif ((*lastr)->action == PERMIT)\n \t\treturn 0;\n-\treturn (*lastr)->action == PERMIT;\n+\treturn -1;\n }\n \n static void\n@@ -184,6 +186,7 @@ checkconfig(const char *confpath, int argc, char **argv,\n uid_t uid, gid_t *groups, int ngroups, uid_t target)\n {\n \tconst struct rule *rule;\n+\tint rv;\n \n \tif (setresuid(uid, uid, uid) != 0)\n \t\terr(1, \"setresuid\");\n@@ -191,9 +194,9 @@ checkconfig(const char *confpath, int argc, char **argv,\n \tparseconfig(confpath, 0);\n \tif (!argc)\n \t\texit(0);\n-\n-\tif (permit(uid, groups, ngroups, &rule, target, argv[0],\n-\t (const char **)argv + 1)) {\n+\trv = permit(uid, groups, ngroups, &rule, target, argv[0],\n+ \t (const char **)argv + 1);\n+\tif (rv == 0) {\n \t\tprintf(\"permit%s\\n\", (rule->options & NOPASS) ? \" nopass\" : \"\");\n \t\texit(0);\n \t} else {\n@@ -342,8 +345,9 @@ main(int argc, char **argv)\n \t}\n \n \tcmd = argv[0];\n-\tif (!permit(uid, groups, ngroups, &rule, target, cmd,\n-\t (const char **)argv + 1)) {\n+\trv = permit(uid, groups, ngroups, &rule, target, cmd,\n+\t (const char **)argv + 1);\n+\tif (rv != 0) {\n \t\tsyslog(LOG_AUTHPRIV | LOG_NOTICE,\n \t\t \"command not permitted for %s: %s\", mypw->pw_name, cmdline);\n \t\terrc(1, EPERM, NULL);\ndiff --git a/doas.h b/doas.h\nindex a8aa41b..591816f 100644\n--- a/doas.h\n+++ b/doas.h\n@@ -36,7 +36,7 @@ struct passwd;\n char **prepenv(const struct rule *, const struct passwd *,\n const struct passwd *);\n \n-#define PERMIT\t1\n+#define PERMIT\t-1\n #define DENY\t2\n \n #define NOPASS\t\t0x1\n" build: compiler: zig-cc target: x86_64-linux-musl link: static flags: [] phases: compile: "_abuild_phase() {\n./configure \\\n\t\t--prefix=/usr \\\n\t\t--without-pam \\\n\t\t--with-timestamp \\\n\t\t--with-doas-confdir\n\tmake\n}\n_abuild_phase\n" install: "_abuild_phase() {\n\tinstall -D -m 755 doas \"/out\"/usr/bin/doas\n\tinstall -D -m 644 doas.1 \"/out\"/usr/share/man/man1/doas.1 2>/dev/null || true\n\tinstall -D -m 644 doas.conf.5 \"/out\"/usr/share/man/man5/doas.conf.5 2>/dev/null || true\n}\n_abuild_phase\n" strip_debug: true target_bin: /usr/bin/doas expected_hash: b3:28bc42c24008f73d9541f407562cd4ed81e3788b95ab72fa00b5369ad606064a deps: build: - binutils - bison - make runtime: []