Skip to content

Commit

Permalink
add --with-confdir feature
Browse files Browse the repository at this point in the history
This adds support for an /etc/doas.d configuration directory as discussed in Duncaen#61. It is disabled by default.
  • Loading branch information
kaniini committed Aug 4, 2021
1 parent 9a25a6d commit 1157d84
Show file tree
Hide file tree
Showing 5 changed files with 95 additions and 3 deletions.
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,3 +61,12 @@ similar to sudo.

See the comment block in `timestamp.c` for an in-depth description on how
timestamps are created and checked to be as safe as possible.

### `--with-doas-confdir`

An optional feature can be enabled which will result in `doas` reading configuration
snippets from `/etc/doas.d`. These configuration snippets have the same requirements
as `/etc/doas.conf` (owned by root, not world-writable).

If this feature is enabled, only the `/etc/doas.d` directory is read, and the historical
`/etc/doas.conf` file is ignored.
8 changes: 8 additions & 0 deletions configure
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ usage: configure [options]
--without-shadow disable shadow support
--with-timestamp enable timestamp support
--with-doas-confdir enable configuration directory support
--uid-max=NUM set UID_MAX (default 65535)
--gid-max=NUM set GID_MAX (default 65535)
Expand All @@ -38,6 +39,7 @@ EOF

# defaults
WITHOUT_TIMESTAMP=yes
WITHOUT_CONFDIR=yes
UID_MAX=65535
GID_MAX=65535

Expand All @@ -56,6 +58,8 @@ for x; do
--target) TARGET=$var ;;
--enable-debug) DEBUG=yes ;;
--enable-static) BUILD_STATIC=yes ;;
--with-doas-confdir) WITHOUT_CONFDIR= ;;
--without-doas-confdir) WITHOUT_CONFDIR=yes ;;
--with-pam) WITHOUT_PAM=; WITHOUT_SHADOW=yes ;;
--with-shadow) WITHOUT_SHADOW=; WITHOUT_PAM=yes ;;
--without-pam) WITHOUT_PAM=yes ;;
Expand Down Expand Up @@ -558,4 +562,8 @@ fi

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

if [ -z "$WITHOUT_CONFDIR" ]; then
printf '#define DOAS_CONFDIR "%s/doas.d"\n' "${SYSCONFDIR}" >>$CONFIG_H
fi

printf '\n#endif /* CONFIG_H */\n' >>$CONFIG_H
73 changes: 71 additions & 2 deletions doas.c
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
#include <syslog.h>
#include <errno.h>
#include <fcntl.h>
#include <dirent.h>

#include "openbsd.h"
#include "doas.h"
Expand Down Expand Up @@ -155,15 +156,15 @@ permit(uid_t uid, gid_t *groups, int ngroups, const struct rule **lastr,
static void
parseconfig(const char *filename, int checkperms)
{
extern FILE *yyfp;
extern int yyparse(void);
struct stat sb;

yyfp = fopen(filename, "r");
if (!yyfp)
err(1, checkperms ? "doas is not enabled, %s" :
"could not open config file %s", filename);

yyfn = filename;

if (checkperms) {
if (fstat(fileno(yyfp), &sb) != 0)
err(1, "fstat(\"%s\")", filename);
Expand All @@ -174,11 +175,67 @@ parseconfig(const char *filename, int checkperms)
}

yyparse();
yyfn = NULL;

fclose(yyfp);
if (parse_errors)
exit(1);
}

#ifdef DOAS_CONFDIR
static int
isconfdir(const char *dirpath)
{
struct stat sb;

if (lstat(dirpath, &sb) != 0)
err(1, "lstat(\"%s\")", dirpath);

if ((sb.st_mode & (S_IFMT)) == S_IFDIR)
return 1;

errno = ENOTDIR;
return 0;
}

static void
parseconfdir(const char *dirpath, int checkperms)
{
struct dirent **dirent_table;
size_t i, dirent_count;
char pathbuf[PATH_MAX];

if (!isconfdir(dirpath))
err(1, checkperms ? "doas is not enabled, %s" :
"could not open config directory %s", dirpath);

dirent_count = scandir(dirpath, &dirent_table, NULL, alphasort);

for (i = 0; i < dirent_count; i++)
{
struct stat sb;
size_t pathlen;

pathlen = snprintf(pathbuf, sizeof pathbuf, "%s/%s", dirpath, dirent_table[i]->d_name);
free(dirent_table[i]);

/* make sure path ends in .conf */
if (strcmp(pathbuf + (pathlen - 5), ".conf"))
continue;

if (stat(pathbuf, &sb) != 0)
err(1, "stat(\"%s\")", pathbuf);

if ((sb.st_mode & (S_IFMT)) != S_IFREG)
continue;

parseconfig(pathbuf, checkperms);
}

free(dirent_table);
}
#endif

static void __dead
checkconfig(const char *confpath, int argc, char **argv,
uid_t uid, gid_t *groups, int ngroups, uid_t target)
Expand All @@ -188,7 +245,13 @@ checkconfig(const char *confpath, int argc, char **argv,
if (setresuid(uid, uid, uid) != 0)
err(1, "setresuid");

#ifdef DOAS_CONFDIR
if (isconfdir(confpath))
parseconfdir(confpath, 0);
else
#else
parseconfig(confpath, 0);
#endif
if (!argc)
exit(0);

Expand Down Expand Up @@ -330,7 +393,13 @@ main(int argc, char **argv)
if (geteuid())
errx(1, "not installed setuid");

#ifdef DOAS_CONFDIR
if (isconfdir(DOAS_CONFDIR))
parseconfdir(DOAS_CONFDIR, 1);
else
#else
parseconfig(DOAS_CONF, 1);
#endif

/* cmdline is used only for logging, no need to abort on truncate */
(void)strlcpy(cmdline, argv[0], sizeof(cmdline));
Expand Down
5 changes: 5 additions & 0 deletions doas.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ struct rule {
const char **envlist;
};

extern const char *yyfn;
extern FILE *yyfp;

extern struct rule **rules;
extern size_t nrules;
extern int parse_errors;
Expand All @@ -33,6 +36,8 @@ extern const char *formerpath;

struct passwd;

extern int yyparse(void);

char **prepenv(const struct rule *, const struct passwd *,
const struct passwd *);

Expand Down
3 changes: 2 additions & 1 deletion parse.y
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ typedef struct {
} yystype;
#define YYSTYPE yystype

const char *yyfn;
FILE *yyfp;

struct rule **rules;
Expand Down Expand Up @@ -203,7 +204,7 @@ yyerror(const char *fmt, ...)
va_start(va, fmt);
vfprintf(stderr, fmt, va);
va_end(va);
fprintf(stderr, " at line %d\n", yylval.lineno + 1);
fprintf(stderr, " at %s, line %d\n", yyfn, yylval.lineno + 1);
parse_errors++;
}

Expand Down

0 comments on commit 1157d84

Please sign in to comment.