Skip to content

Commit 7de1d45

Browse files
committed
add --with-confdir feature
This adds support for an /etc/doas.d configuration directory as discussed in #61. It is disabled by default.
1 parent 9a25a6d commit 7de1d45

File tree

4 files changed

+91
-1
lines changed

4 files changed

+91
-1
lines changed

README.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,3 +61,12 @@ similar to sudo.
6161

6262
See the comment block in `timestamp.c` for an in-depth description on how
6363
timestamps are created and checked to be as safe as possible.
64+
65+
### `--with-doas-confdir`
66+
67+
An optional feature can be enabled which will result in `doas` reading configuration
68+
snippets from `/etc/doas.d`. These configuration snippets have the same requirements
69+
as `/etc/doas.conf` (owned by root, not world-writable).
70+
71+
If this feature is enabled, only the `/etc/doas.d` directory is read, and the historical
72+
`/etc/doas.conf` file is ignored.

configure

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ usage: configure [options]
2727
--without-shadow disable shadow support
2828
2929
--with-timestamp enable timestamp support
30+
--with-doas-confdir enable configuration directory support
3031
3132
--uid-max=NUM set UID_MAX (default 65535)
3233
--gid-max=NUM set GID_MAX (default 65535)
@@ -38,6 +39,7 @@ EOF
3839

3940
# defaults
4041
WITHOUT_TIMESTAMP=yes
42+
WITHOUT_CONFDIR=yes
4143
UID_MAX=65535
4244
GID_MAX=65535
4345

@@ -56,6 +58,8 @@ for x; do
5658
--target) TARGET=$var ;;
5759
--enable-debug) DEBUG=yes ;;
5860
--enable-static) BUILD_STATIC=yes ;;
61+
--with-doas-confdir) WITHOUT_CONFDIR= ;;
62+
--without-doas-confdir) WITHOUT_CONFDIR=yes ;;
5963
--with-pam) WITHOUT_PAM=; WITHOUT_SHADOW=yes ;;
6064
--with-shadow) WITHOUT_SHADOW=; WITHOUT_PAM=yes ;;
6165
--without-pam) WITHOUT_PAM=yes ;;
@@ -558,4 +562,8 @@ fi
558562

559563
printf '#define DOAS_CONF "%s/doas.conf"\n' "${SYSCONFDIR}" >>$CONFIG_H
560564

565+
if [ -z "$WITHOUT_CONFDIR" ]; then
566+
printf '#define DOAS_CONFDIR "%s/doas.d"\n' "${SYSCONFDIR}" >>$CONFIG_H
567+
fi
568+
561569
printf '\n#endif /* CONFIG_H */\n' >>$CONFIG_H

doas.c

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
#include <syslog.h>
3636
#include <errno.h>
3737
#include <fcntl.h>
38+
#include <dirent.h>
3839

3940
#include "openbsd.h"
4041
#include "doas.h"
@@ -155,6 +156,7 @@ permit(uid_t uid, gid_t *groups, int ngroups, const struct rule **lastr,
155156
static void
156157
parseconfig(const char *filename, int checkperms)
157158
{
159+
extern const char *yyfn;
158160
extern FILE *yyfp;
159161
extern int yyparse(void);
160162
struct stat sb;
@@ -164,6 +166,8 @@ parseconfig(const char *filename, int checkperms)
164166
err(1, checkperms ? "doas is not enabled, %s" :
165167
"could not open config file %s", filename);
166168

169+
yyfn = filename;
170+
167171
if (checkperms) {
168172
if (fstat(fileno(yyfp), &sb) != 0)
169173
err(1, "fstat(\"%s\")", filename);
@@ -174,11 +178,67 @@ parseconfig(const char *filename, int checkperms)
174178
}
175179

176180
yyparse();
181+
yyfn = NULL;
182+
177183
fclose(yyfp);
178184
if (parse_errors)
179185
exit(1);
180186
}
181187

188+
#ifdef DOAS_CONFDIR
189+
static int
190+
isconfdir(const char *dirpath)
191+
{
192+
struct stat sb;
193+
194+
if (lstat(dirpath, &sb) != 0)
195+
err(1, "lstat(\"%s\")", dirpath);
196+
197+
if ((sb.st_mode & (S_IFMT)) == S_IFDIR)
198+
return 1;
199+
200+
errno = ENOTDIR;
201+
return 0;
202+
}
203+
204+
static void
205+
parseconfdir(const char *dirpath, int checkperms)
206+
{
207+
struct dirent **dirent_table;
208+
size_t i, dirent_count;
209+
char pathbuf[PATH_MAX];
210+
211+
if (!isconfdir(dirpath))
212+
err(1, checkperms ? "doas is not enabled, %s" :
213+
"could not open config directory %s", dirpath);
214+
215+
dirent_count = scandir(dirpath, &dirent_table, NULL, alphasort);
216+
217+
for (i = 0; i < dirent_count; i++)
218+
{
219+
struct stat sb;
220+
size_t pathlen;
221+
222+
pathlen = snprintf(pathbuf, sizeof pathbuf, "%s/%s", dirpath, dirent_table[i]->d_name);
223+
free(dirent_table[i]);
224+
225+
/* make sure path ends in .conf */
226+
if (strcmp(pathbuf + (pathlen - 5), ".conf"))
227+
continue;
228+
229+
if (stat(pathbuf, &sb) != 0)
230+
err(1, "stat(\"%s\")", pathbuf);
231+
232+
if ((sb.st_mode & (S_IFMT)) != S_IFREG)
233+
continue;
234+
235+
parseconfig(pathbuf, checkperms);
236+
}
237+
238+
free(dirent_table);
239+
}
240+
#endif
241+
182242
static void __dead
183243
checkconfig(const char *confpath, int argc, char **argv,
184244
uid_t uid, gid_t *groups, int ngroups, uid_t target)
@@ -188,7 +248,13 @@ checkconfig(const char *confpath, int argc, char **argv,
188248
if (setresuid(uid, uid, uid) != 0)
189249
err(1, "setresuid");
190250

251+
#ifdef DOAS_CONFDIR
252+
if (isconfdir(confpath))
253+
parseconfdir(confpath, 0);
254+
else
255+
#else
191256
parseconfig(confpath, 0);
257+
#endif
192258
if (!argc)
193259
exit(0);
194260

@@ -330,7 +396,13 @@ main(int argc, char **argv)
330396
if (geteuid())
331397
errx(1, "not installed setuid");
332398

399+
#ifdef DOAS_CONFDIR
400+
if (isconfdir(DOAS_CONFDIR))
401+
parseconfdir(DOAS_CONFDIR, 1);
402+
else
403+
#else
333404
parseconfig(DOAS_CONF, 1);
405+
#endif
334406

335407
/* cmdline is used only for logging, no need to abort on truncate */
336408
(void)strlcpy(cmdline, argv[0], sizeof(cmdline));

parse.y

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ typedef struct {
4949
} yystype;
5050
#define YYSTYPE yystype
5151

52+
const char *yyfn;
5253
FILE *yyfp;
5354

5455
struct rule **rules;
@@ -203,7 +204,7 @@ yyerror(const char *fmt, ...)
203204
va_start(va, fmt);
204205
vfprintf(stderr, fmt, va);
205206
va_end(va);
206-
fprintf(stderr, " at line %d\n", yylval.lineno + 1);
207+
fprintf(stderr, " at %s, line %d\n", yyfn, yylval.lineno + 1);
207208
parse_errors++;
208209
}
209210

0 commit comments

Comments
 (0)