From e355b1d04349731687a4eb6222a91d9b2dcf7e6f Mon Sep 17 00:00:00 2001 From: Vadim Zhukov Date: Tue, 21 Jul 2015 11:04:06 +0000 Subject: [PATCH] Add argument matching support to doas. Input and generic support from many. Final okay from tedu@. --- doas.1 | 4 +++- doas.c | 28 +++++++++++++++++++++------- doas.conf.5 | 14 ++++++++++++-- doas.h | 3 ++- parse.y | 35 +++++++++++++++++++++++++++++------ 5 files changed, 67 insertions(+), 17 deletions(-) diff --git a/doas.1 b/doas.1 index cd076ea..351d5fd 100644 --- a/doas.1 +++ b/doas.1 @@ -1,4 +1,4 @@ -.\" $OpenBSD: doas.1,v 1.6 2015/07/19 16:42:31 espie Exp $ +.\" $OpenBSD: doas.1,v 1.7 2015/07/19 17:00:22 jmc Exp $ .\" .\"Copyright (c) 2015 Ted Unangst .\" @@ -55,6 +55,8 @@ could not be parsed. The user attempted to run a command which is not permitted. .It The password was incorrect. +.It +The actual program is absent or not executable. .El .Sh SEE ALSO .Xr doas.conf 5 diff --git a/doas.c b/doas.c index a712bc3..b77e17f 100644 --- a/doas.c +++ b/doas.c @@ -1,4 +1,4 @@ -/* $OpenBSD: doas.c,v 1.13 2015/07/20 01:00:48 tedu Exp $ */ +/* $OpenBSD: doas.c,v 1.14 2015/07/20 01:04:37 tedu Exp $ */ /* * Copyright (c) 2015 Ted Unangst * @@ -97,7 +97,7 @@ strtogid(const char *s) static int match(uid_t uid, gid_t *groups, int ngroups, uid_t target, const char *cmd, - struct rule *r) + const char **cmdargs, struct rule *r) { int i; @@ -117,20 +117,33 @@ match(uid_t uid, gid_t *groups, int ngroups, uid_t target, const char *cmd, } if (r->target && uidcheck(r->target, target) != 0) return 0; - if (r->cmd && strcmp(r->cmd, cmd) != 0) - return 0; + if (r->cmd) { + if (strcmp(r->cmd, cmd)) + return 0; + if (r->cmdargs) { + /* if arguments were given, they should match explicitly */ + for (i = 0; r->cmdargs[i]; i++) { + if (!cmdargs[i]) + return 0; + if (strcmp(r->cmdargs[i], cmdargs[i])) + return 0; + } + if (cmdargs[i]) + return 0; + } + } return 1; } static int permit(uid_t uid, gid_t *groups, int ngroups, struct rule **lastr, - uid_t target, const char *cmd) + uid_t target, const char *cmd, const char **cmdargs) { int i; *lastr = NULL; for (i = 0; i < nrules; i++) { - if (match(uid, groups, ngroups, target, cmd, rules[i])) + if (match(uid, groups, ngroups, target, cmd, cmdargs, rules[i])) *lastr = rules[i]; } if (!*lastr) @@ -334,7 +347,8 @@ main(int argc, char **argv, char **envp) errx(1, "command line too long"); } - if (!permit(uid, groups, ngroups, &rule, target, cmd)) { + if (!permit(uid, groups, ngroups, &rule, target, cmd, + (const char**)argv + 1)) { syslog(LOG_AUTHPRIV | LOG_NOTICE, "failed command for %s: %s", myname, cmdline); fail(); diff --git a/doas.conf.5 b/doas.conf.5 index 8d3cd1e..7510bd0 100644 --- a/doas.conf.5 +++ b/doas.conf.5 @@ -1,4 +1,4 @@ -.\" $OpenBSD: doas.conf.5,v 1.6 2015/07/20 07:43:27 jmc Exp $ +.\" $OpenBSD: doas.conf.5,v 1.7 2015/07/20 20:18:45 tedu Exp $ .\" .\"Copyright (c) 2015 Ted Unangst .\" @@ -33,7 +33,9 @@ The rules have the following format: .Op Ar options .Ar identity .Op Ic as Ar target -.Op Ic cmd Ar command +.Oo +.Ic cmd Ar command Op Ic args ... +.Oc .Ed .Pp Rules consist of the following parts: @@ -71,6 +73,14 @@ The default is root. The command the user is allowed or denied to run. The default is all commands. Be advised that it's best to specify absolute paths. +.It Ic args Op ... +Arguments to command. +If specified, the command arguments set provided by user and +the command arguments set in rule should be the same for successful +rule match. +Specifying +.Ic args +alone means that command should be run without any arguments. .El .Pp The last matching rule determines the action taken. diff --git a/doas.h b/doas.h index 8576e41..9f0e5e4 100644 --- a/doas.h +++ b/doas.h @@ -1,4 +1,4 @@ -/* $OpenBSD$ */ +/* $OpenBSD: doas.h,v 1.2 2015/07/18 07:49:16 bcallah Exp $ */ struct rule { int action; @@ -6,6 +6,7 @@ struct rule { const char *ident; const char *target; const char *cmd; + const char **cmdargs; const char **envlist; }; diff --git a/parse.y b/parse.y index f1e90ab..7dc1816 100644 --- a/parse.y +++ b/parse.y @@ -1,4 +1,4 @@ -/* $OpenBSD: parse.y,v 1.5 2015/07/19 22:09:08 benno Exp $ */ +/* $OpenBSD: parse.y,v 1.6 2015/07/19 22:11:41 benno Exp $ */ /* * Copyright (c) 2015 Ted Unangst * @@ -32,6 +32,8 @@ typedef struct { struct { int action; int options; + const char *cmd; + const char **cmdargs; const char **envlist; }; const char *str; @@ -50,7 +52,7 @@ int yyparse(void); %} -%token TPERMIT TDENY TAS TCMD +%token TPERMIT TDENY TAS TCMD TARGS %token TNOPASS TKEEPENV %token TSTRING @@ -71,7 +73,8 @@ rule: action ident target cmd { r->envlist = $1.envlist; r->ident = $2.str; r->target = $3.str; - r->cmd = $4.str; + r->cmd = $4.cmd; + r->cmdargs = $4.cmdargs; if (nrules == maxrules) { if (maxrules == 0) maxrules = 63; @@ -136,9 +139,28 @@ target: /* optional */ { } ; cmd: /* optional */ { - $$.str = NULL; - } | TCMD TSTRING { - $$.str = $2.str; + $$.cmd = NULL; + $$.cmdargs = NULL; + } | TCMD TSTRING args { + $$.cmd = $2.str; + $$.cmdargs = $3.cmdargs; + } ; + +args: /* empty */ { + $$.cmdargs = NULL; + } | TARGS argslist { + $$.cmdargs = $2.cmdargs; + } ; + +argslist: /* empty */ { + if (!($$.cmdargs = calloc(1, sizeof(char *)))) + errx(1, "can't allocate args"); + } | argslist TSTRING { + int nargs = arraylen($1.cmdargs); + if (!($$.cmdargs = reallocarray($1.cmdargs, nargs + 2, sizeof(char *)))) + errx(1, "can't allocate args"); + $$.cmdargs[nargs] = $2.str; + $$.cmdargs[nargs + 1] = NULL; } ; %% @@ -160,6 +182,7 @@ struct keyword { { "permit", TPERMIT }, { "as", TAS }, { "cmd", TCMD }, + { "args", TARGS }, { "nopass", TNOPASS }, { "keepenv", TKEEPENV }, }; -- 2.39.2