opendoas

A portable version of the OpenBSD `doas` command
git clone https://pi.duncano.de/git/opendoas.git
Log | Files | Refs | README | LICENSE

commit 26edde87e46f2601656361736472e216e8acf707
parent edf4632491d75d7e56c5293de3c33dc2de74231d
Author: Vadim Zhukov <zhuk@openbsd.org>
Date:   Sun, 26 Jul 2015 17:24:02 +0000

Implement command matching without execution. This just extends
functionality of the -C flag, so we are not introducing more garbage.

Input and okay from jmc@ (documentation) and tedu@ (everything).

Diffstat:
doas.1 | 22+++++++++++++++++++++-
doas.c | 63+++++++++++++++++++++++++++++++++++++++++++++++----------------
2 files changed, 68 insertions(+), 17 deletions(-)

diff --git a/doas.1 b/doas.1 @@ -1,4 +1,4 @@ -.\" $OpenBSD: doas.1,v 1.9 2015/07/21 16:12:04 tedu Exp $ +.\" $OpenBSD: doas.1,v 1.10 2015/07/21 17:49:33 jmc Exp $ .\" .\"Copyright (c) 2015 Ted Unangst <tedu@openbsd.org> .\" @@ -30,6 +30,13 @@ The .Nm utility executes the given command as another user. +The +.Ar command +argument is mandatory unless +.Fl C +or +.Fl s +is specified. .Pp The options are as follows: .Bl -tag -width tenletters @@ -37,6 +44,19 @@ The options are as follows: Parse and check the configuration file .Ar config , then exit. +If +.Ar command +is supplied, +.Nm +will also perform command matching. +In the latter case +either +.Sq permit , +.Sq permit nopass +or +.Sq deny +will be printed on standard output, depending on command +matching results. No command is executed. .It Fl s Execute the shell from diff --git a/doas.c b/doas.c @@ -1,4 +1,4 @@ -/* $OpenBSD: doas.c,v 1.20 2015/07/22 16:35:03 zhuk Exp $ */ +/* $OpenBSD: doas.c,v 1.21 2015/07/24 06:36:42 zhuk Exp $ */ /* * Copyright (c) 2015 Ted Unangst <tedu@openbsd.org> * @@ -152,7 +152,7 @@ permit(uid_t uid, gid_t *groups, int ngroups, struct rule **lastr, } static void -parseconfig(const char *filename) +parseconfig(const char *filename, int checkperms) { extern FILE *yyfp; extern int yyparse(void); @@ -160,16 +160,21 @@ parseconfig(const char *filename) yyfp = fopen(filename, "r"); if (!yyfp) { - fprintf(stderr, "doas is not enabled.\n"); + if (checkperms) + fprintf(stderr, "doas is not enabled.\n"); + else + warn("could not open config file"); exit(1); } - if (fstat(fileno(yyfp), &sb) != 0) - err(1, "fstat(\"%s\")", filename); - if ((sb.st_mode & (S_IWGRP|S_IWOTH)) != 0) - errx(1, "%s is writable by group or other", filename); - if (sb.st_uid != 0) - errx(1, "%s is not owned by root", filename); + if (checkperms) { + if (fstat(fileno(yyfp), &sb) != 0) + err(1, "fstat(\"%s\")", filename); + if ((sb.st_mode & (S_IWGRP|S_IWOTH)) != 0) + errx(1, "%s is writable by group or other", filename); + if (sb.st_uid != 0) + errx(1, "%s is not owned by root", filename); + } yyparse(); fclose(yyfp); @@ -277,11 +282,32 @@ fail(void) exit(1); } +static int +checkconfig(const char *confpath, int argc, char **argv, + uid_t uid, gid_t *groups, int ngroups, uid_t target) { + struct rule *rule; + + setresuid(uid, uid, uid); + parseconfig(confpath, 0); + if (!argc) + exit(0); + + if (permit(uid, groups, ngroups, &rule, target, argv[0], + (const char **)argv + 1)) { + printf("permit%s\n", (rule->options & NOPASS) ? " nopass" : ""); + return 1; + } else { + printf("deny\n"); + return 0; + } +} + int main(int argc, char **argv, char **envp) { 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 *cmd; @@ -296,13 +322,11 @@ main(int argc, char **argv, char **envp) int i, ch; int sflag = 0; - uid = getuid(); while ((ch = getopt(argc, argv, "C:su:")) != -1) { switch (ch) { case 'C': - setresuid(uid, uid, uid); - parseconfig(optarg); - exit(0); + confpath = optarg; + break; case 'u': if (parseuid(optarg, &target) != 0) errx(1, "unknown user"); @@ -318,11 +342,13 @@ main(int argc, char **argv, char **envp) argv += optind; argc -= optind; - if ((!sflag && !argc) || (sflag && argc)) + if (confpath) { + if (sflag) + usage(); + } else if ((!sflag && !argc) || (sflag && argc)) usage(); - parseconfig("/etc/doas.conf"); - + uid = getuid(); pw = getpwuid(uid); if (!pw) err(1, "getpwuid failed"); @@ -343,6 +369,11 @@ main(int argc, char **argv, char **envp) argc = 1; } + if (confpath) + exit(!checkconfig(confpath, argc, argv, uid, groups, ngroups, + target)); + parseconfig("/etc/doas.conf", 1); + cmd = argv[0]; if (strlcpy(cmdline, argv[0], sizeof(cmdline)) >= sizeof(cmdline)) errx(1, "command line too long");