Merge, update from OpenBSD

This commit is contained in:
katakk 2017-01-24 01:12:56 +09:00
parent a15e6ed35c
commit 453102110a
6 changed files with 278 additions and 188 deletions

4
doas.1
View file

@ -88,7 +88,7 @@ It may fail for one of the following reasons:
.Bl -bullet -compact .Bl -bullet -compact
.It .It
The config file The config file
.Pa /etc/doas.conf .Pa /usr/local/etc/doas.conf
could not be parsed. could not be parsed.
.It .It
The user attempted to run a command which is not permitted. The user attempted to run a command which is not permitted.
@ -106,4 +106,4 @@ The
command first appeared in command first appeared in
.Ox 5.8 . .Ox 5.8 .
.Sh AUTHORS .Sh AUTHORS
.An Ted Unangst Aq Mt tedu@openbsd.org .An Ted Unangst Aq Mt tedu@openbsd.org

118
doas.c
View file

@ -17,6 +17,7 @@
#include <sys/types.h> #include <sys/types.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/ioctl.h>
#if defined(HAVE_INTTYPES_H) #if defined(HAVE_INTTYPES_H)
#include <inttypes.h> #include <inttypes.h>
@ -39,6 +40,7 @@
#include <grp.h> #include <grp.h>
#include <syslog.h> #include <syslog.h>
#include <errno.h> #include <errno.h>
#include <fcntl.h>
#if defined(HAVE_LOGIN_CAP_H) #if defined(HAVE_LOGIN_CAP_H)
#include <login_cap.h> #include <login_cap.h>
@ -83,18 +85,6 @@ errc(int eval, int code, const char *format)
} }
#endif #endif
size_t
arraylen(const char **arr)
{
size_t cnt = 0;
while (*arr) {
cnt++;
arr++;
}
return cnt;
}
static int static int
parseuid(const char *s, uid_t *uid) parseuid(const char *s, uid_t *uid)
{ {
@ -254,6 +244,54 @@ checkconfig(const char *confpath, int argc, char **argv,
} }
} }
#if defined(USE_BSD_AUTH)
static void
authuser(char *myname, char *login_style, int persist)
{
char *challenge = NULL, *response, rbuf[1024], cbuf[128];
auth_session_t *as;
int fd = -1;
if (persist)
fd = open("/dev/tty", O_RDWR);
if (fd != -1) {
if (ioctl(fd, TIOCCHKVERAUTH) == 0)
goto good;
}
if (!(as = auth_userchallenge(myname, login_style, "auth-doas",
&challenge)))
errx(1, "Authorization failed");
if (!challenge) {
char host[HOST_NAME_MAX + 1];
if (gethostname(host, sizeof(host)))
snprintf(host, sizeof(host), "?");
snprintf(cbuf, sizeof(cbuf),
"\rdoas (%.32s@%.32s) password: ", myname, host);
challenge = cbuf;
}
response = readpassphrase(challenge, rbuf, sizeof(rbuf),
RPP_REQUIRE_TTY);
if (response == NULL && errno == ENOTTY) {
syslog(LOG_AUTHPRIV | LOG_NOTICE,
"tty required for %s", myname);
errx(1, "a tty is required");
}
if (!auth_userresponse(as, response, 0)) {
syslog(LOG_AUTHPRIV | LOG_NOTICE,
"failed auth for %s", myname);
errc(1, EPERM, NULL);
}
explicit_bzero(rbuf, sizeof(rbuf));
good:
if (fd != -1) {
int secs = 5 * 60;
ioctl(fd, TIOCSETVERAUTH, &secs);
close(fd);
}
}
#endif
int int
main(int argc, char **argv) main(int argc, char **argv)
{ {
@ -283,11 +321,6 @@ main(int argc, char **argv)
setprogname("doas"); setprogname("doas");
#endif #endif
/*
if (pledge("stdio rpath getpw tty proc exec id", NULL) == -1)
err(1, "pledge");
*/
#ifndef linux #ifndef linux
closefrom(STDERR_FILENO + 1); closefrom(STDERR_FILENO + 1);
#endif #endif
@ -295,6 +328,7 @@ main(int argc, char **argv)
uid = getuid(); uid = getuid();
while ((ch = getopt(argc, argv, "a:C:nsu:")) != -1) { while ((ch = getopt(argc, argv, "a:C:nsu:")) != -1) {
/* while ((ch = getopt(argc, argv, "a:C:Lnsu:")) != -1) { */
switch (ch) { switch (ch) {
case 'a': case 'a':
login_style = optarg; login_style = optarg;
@ -302,6 +336,12 @@ main(int argc, char **argv)
case 'C': case 'C':
confpath = optarg; confpath = optarg;
break; break;
/* case 'L':
i = open("/dev/tty", O_RDWR);
if (i != -1)
ioctl(i, TIOCCLRVERAUTH);
exit(i != -1);
*/
case 'u': case 'u':
if (parseuid(optarg, &target) != 0) if (parseuid(optarg, &target) != 0)
errx(1, "unknown user"); errx(1, "unknown user");
@ -343,9 +383,11 @@ main(int argc, char **argv)
if (sflag) { if (sflag) {
sh = getenv("SHELL"); sh = getenv("SHELL");
if (sh == NULL || *sh == '\0') if (sh == NULL || *sh == '\0') {
shargv[0] = pw->pw_shell; shargv[0] = strdup(pw->pw_shell);
else if (shargv[0] == NULL)
err(1, NULL);
} else
shargv[0] = sh; shargv[0] = sh;
argv = shargv; argv = shargv;
argc = 1; argc = 1;
@ -357,11 +399,14 @@ main(int argc, char **argv)
exit(1); /* fail safe */ exit(1); /* fail safe */
} }
if (geteuid())
errx(1, "not installed setuid");
parseconfig(DOAS_CONF, 1); parseconfig(DOAS_CONF, 1);
/* cmdline is used only for logging, no need to abort on truncate */ /* cmdline is used only for logging, no need to abort on truncate */
#ifndef linux #ifndef linux
(void) strlcpy(cmdline, argv[0], sizeof(cmdline)); (void)strlcpy(cmdline, argv[0], sizeof(cmdline));
for (i = 1; i < argc; i++) { for (i = 1; i < argc; i++) {
if (strlcat(cmdline, " ", sizeof(cmdline)) >= sizeof(cmdline)) if (strlcat(cmdline, " ", sizeof(cmdline)) >= sizeof(cmdline))
break; break;
@ -379,7 +424,7 @@ main(int argc, char **argv)
cmd = argv[0]; cmd = argv[0];
if (!permit(uid, groups, ngroups, &rule, target, cmd, if (!permit(uid, groups, ngroups, &rule, target, cmd,
(const char**)argv + 1)) { (const char **)argv + 1)) {
syslog(LOG_AUTHPRIV | LOG_NOTICE, syslog(LOG_AUTHPRIV | LOG_NOTICE,
"failed command for %s: %s", myname, cmdline); "failed command for %s: %s", myname, cmdline);
errc(1, EPERM, NULL); errc(1, EPERM, NULL);
@ -387,36 +432,10 @@ main(int argc, char **argv)
if (!(rule->options & NOPASS)) { if (!(rule->options & NOPASS)) {
#if defined(USE_BSD_AUTH) #if defined(USE_BSD_AUTH)
char *challenge = NULL, *response, rbuf[1024], cbuf[128];
auth_session_t *as;
if (nflag) if (nflag)
errx(1, "Authorization required"); errx(1, "Authorization required");
if (!(as = auth_userchallenge(myname, login_style, "auth-doas", authuser(myname, login_style, rule->options & PERSIST);
&challenge)))
errx(1, "Authorization failed");
if (!challenge) {
char host[MAXHOSTNAME + 1];
if (gethostname(host, sizeof(host)))
snprintf(host, sizeof(host), "?");
snprintf(cbuf, sizeof(cbuf),
"\rdoas (%.32s@%.32s) password: ", myname, host);
challenge = cbuf;
}
response = readpassphrase(challenge, rbuf, sizeof(rbuf),
RPP_REQUIRE_TTY);
if (response == NULL && errno == ENOTTY) {
syslog(LOG_AUTHPRIV | LOG_NOTICE,
"tty required for %s", myname);
errx(1, "a tty is required");
}
if (!auth_userresponse(as, response, 0)) {
syslog(LOG_AUTHPRIV | LOG_NOTICE,
"failed auth for %s", myname);
errc(1, EPERM, NULL);
}
explicit_bzero(rbuf, sizeof(rbuf));
#elif defined(USE_PAM) #elif defined(USE_PAM)
#define PAM_END(msg) do { \ #define PAM_END(msg) do { \
syslog(LOG_ERR, "%s: %s", msg, pam_strerror(pamh, pam_err)); \ syslog(LOG_ERR, "%s: %s", msg, pam_strerror(pamh, pam_err)); \
@ -518,7 +537,6 @@ main(int argc, char **argv)
#else #else
#error No auth module! #error No auth module!
#endif #endif
} }
/* /*

View file

@ -1,4 +1,4 @@
.\" $OpenBSD: doas.conf.5,v 1.26 2016/06/11 17:17:10 tedu Exp $ .\" $OpenBSD: doas.conf.5,v 1.31 2016/12/05 10:58:07 schwarze Exp $
.\" .\"
.\"Copyright (c) 2015 Ted Unangst <tedu@openbsd.org> .\"Copyright (c) 2015 Ted Unangst <tedu@openbsd.org>
.\" .\"
@ -13,7 +13,7 @@
.\"WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN .\"WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
.\"ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF .\"ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
.\"OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\"OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
.Dd $Mdocdate: June 11 2016 $ .Dd $Mdocdate: December 5 2016 $
.Dt DOAS.CONF 5 .Dt DOAS.CONF 5
.Os .Os
.Sh NAME .Sh NAME
@ -35,7 +35,7 @@ The rules have the following format:
.Op Ar options .Op Ar options
.Ar identity .Ar identity
.Op Ic as Ar target .Op Ic as Ar target
.Op Ic cmd Ar command Op Ic args ... .Op Ic cmd Ar command Op Ic args No ...
.Ed .Ed
.Pp .Pp
Rules consist of the following parts: Rules consist of the following parts:
@ -47,6 +47,9 @@ Options are:
.Bl -tag -width keepenv .Bl -tag -width keepenv
.It Ic nopass .It Ic nopass
The user is not required to enter a password. The user is not required to enter a password.
.It Ic persist
After the user successfully authenticates, do not ask for a password
again for some time.
.It Ic keepenv .It Ic keepenv
The user's environment is maintained. The user's environment is maintained.
The default is to reset the environment, except for the variables The default is to reset the environment, except for the variables
@ -59,9 +62,18 @@ The default is to reset the environment, except for the variables
.Ev USER .Ev USER
and and
.Ev USERNAME . .Ev USERNAME .
.It Ic keepenv { Oo Ar variable ... Oc Ic } .It Ic setenv { Oo Ar variable ... Oc Oo Ar variable=value ... Oc Ic }
In addition to the variables mentioned above, keep the space-separated In addition to the variables mentioned above, keep the space-separated
specified variables. specified variables.
Variables may also be removed with a leading
.Sq -
or set using the latter syntax.
If the first character of
.Ar value
is a
.Ql $
then the value to be set is taken from the existing environment
variable of the same name.
.El .El
.It Ar identity .It Ar identity
The username to match. The username to match.
@ -78,7 +90,7 @@ Be advised that it is best to specify absolute paths.
If a relative path is specified, only a restricted If a relative path is specified, only a restricted
.Ev PATH .Ev PATH
will be searched. will be searched.
.It Ic args ... .It Ic args Op Ar argument ...
Arguments to command. Arguments to command.
The command arguments provided by the user need to match those specified. The command arguments provided by the user need to match those specified.
The keyword The keyword
@ -109,25 +121,27 @@ If quotes or backslashes are used in a word,
it is not considered a keyword. it is not considered a keyword.
.El .El
.Sh EXAMPLES .Sh EXAMPLES
The following example permits users in group wsrc to build ports, The following example permits users in group wsrc to build ports;
wheel to execute commands as any user while keeping the environment wheel to execute commands as any user while keeping the environment
variables variables
.Ev ENV , .Ev PS1
.Ev PS1 ,
and and
.Ev SSH_AUTH_SOCK , .Ev SSH_AUTH_SOCK
permits tedu to run procmap as root without a password, and
unsetting
.Ev ENV ;
permits tedu to run procmap as root without a password;
and additionally permits root to run unrestricted commands as itself. and additionally permits root to run unrestricted commands as itself.
.Bd -literal -offset indent .Bd -literal -offset indent
# Non-exhaustive list of variables needed to # Non-exhaustive list of variables needed to
# build release(8) and ports(7) # build release(8) and ports(7)
permit nopass keepenv { \e permit nopass setenv { \e
FTPMODE PKG_CACHE PKG_PATH SM_PATH SSH_AUTH_SOCK \e FTPMODE PKG_CACHE PKG_PATH SM_PATH SSH_AUTH_SOCK \e
DESTDIR DISTDIR FETCH_CMD FLAVOR GROUP MAKE MAKECONF \e DESTDIR DISTDIR FETCH_CMD FLAVOR GROUP MAKE MAKECONF \e
MULTI_PACKAGES NOMAN OKAY_FILES OWNER PKG_DBDIR \e MULTI_PACKAGES NOMAN OKAY_FILES OWNER PKG_DBDIR \e
PKG_DESTDIR PKG_TMPDIR PORTSDIR RELEASEDIR SHARED_ONLY \e PKG_DESTDIR PKG_TMPDIR PORTSDIR RELEASEDIR SHARED_ONLY \e
SUBPACKAGE WRKOBJDIR SUDO_PORT_V1 } :wsrc SUBPACKAGE WRKOBJDIR SUDO_PORT_V1 } :wsrc
permit nopass keepenv { ENV PS1 SSH_AUTH_SOCK } :wheel permit setenv { -ENV PS1=$DOAS_PS1 SSH_AUTH_SOCK } :wheel
permit nopass tedu as root cmd /usr/sbin/procmap permit nopass tedu as root cmd /usr/sbin/procmap
permit nopass keepenv root as root permit nopass keepenv root as root
.Ed .Ed

23
doas.h
View file

@ -1,4 +1,20 @@
/* $OpenBSD: doas.h,v 1.8 2016/06/19 19:29:43 martijn Exp $ */ /* $OpenBSD: doas.h,v 1.12 2016/10/05 17:40:25 tedu Exp $ */
/*
* Copyright (c) 2015 Ted Unangst <tedu@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
struct rule { struct rule {
int action; int action;
int options; int options;
@ -10,11 +26,9 @@ struct rule {
}; };
extern struct rule **rules; extern struct rule **rules;
extern int nrules, maxrules; extern int nrules;
extern int parse_errors; extern int parse_errors;
size_t arraylen(const char **);
char **prepenv(struct rule *); char **prepenv(struct rule *);
#define PERMIT 1 #define PERMIT 1
@ -22,6 +36,7 @@ char **prepenv(struct rule *);
#define NOPASS 0x1 #define NOPASS 0x1
#define KEEPENV 0x2 #define KEEPENV 0x2
#define PERSIST 0x4
#ifndef UID_MAX #ifndef UID_MAX
#define UID_MAX 65535 #define UID_MAX 65535

191
env.c
View file

@ -1,4 +1,4 @@
/* $OpenBSD: env.c,v 1.2 2016/06/19 19:29:43 martijn Exp $ */ /* $OpenBSD: env.c,v 1.5 2016/09/15 00:58:23 deraadt Exp $ */
/* /*
* Copyright (c) 2016 Ted Unangst <tedu@openbsd.org> * Copyright (c) 2016 Ted Unangst <tedu@openbsd.org>
* *
@ -51,12 +51,31 @@ envcmp(struct envnode *a, struct envnode *b)
} }
RB_GENERATE_STATIC(envtree, envnode, node, envcmp) RB_GENERATE_STATIC(envtree, envnode, node, envcmp)
struct env *createenv(char **); static struct envnode *
struct env *filterenv(struct env *, struct rule *); createnode(const char *key, const char *value)
char **flattenenv(struct env *); {
struct envnode *node;
struct env * node = malloc(sizeof(*node));
createenv(char **envp) if (!node)
err(1, NULL);
node->key = strdup(key);
node->value = strdup(value);
if (!node->key || !node->value)
err(1, NULL);
return node;
}
static void
freenode(struct envnode *node)
{
free((char *)node->key);
free((char *)node->value);
free(node);
}
static struct env *
createenv(struct rule *rule)
{ {
struct env *env; struct env *env;
u_int i; u_int i;
@ -67,41 +86,47 @@ createenv(char **envp)
RB_INIT(&env->root); RB_INIT(&env->root);
env->count = 0; env->count = 0;
for (i = 0; envp[i] != NULL; i++) { if (rule->options & KEEPENV) {
struct envnode *node; extern const char **environ;
const char *e, *eq;
e = envp[i]; for (i = 0; environ[i] != NULL; i++) {
struct envnode *node;
const char *e, *eq;
size_t len;
char name[1024];
if ((eq = strchr(e, '=')) == NULL || eq == e) e = environ[i];
continue;
node = malloc(sizeof(*node)); /* ignore invalid or overlong names */
if (!node) if ((eq = strchr(e, '=')) == NULL || eq == e)
err(1, NULL); continue;
node->key = strndup(envp[i], eq - e); len = eq - e;
node->value = strdup(eq + 1); if (len > sizeof(name) - 1)
if (!node->key || !node->value) continue;
err(1, NULL); memcpy(name, e, len);
if (RB_FIND(envtree, &env->root, node)) { name[len] = '\0';
free((char *)node->key);
free((char *)node->value); node = createnode(name, eq + 1);
free(node); if (RB_INSERT(envtree, &env->root, node)) {
} else { /* ignore any later duplicates */
RB_INSERT(envtree, &env->root, node); freenode(node);
env->count++; } else {
env->count++;
}
} }
} }
return env; return env;
} }
char ** static char **
flattenenv(struct env *env) flattenenv(struct env *env)
{ {
char **envp; char **envp;
struct envnode *node; struct envnode *node;
u_int i; u_int i;
envp = reallocarray(NULL, (env->count + 1), sizeof(char *)); envp = reallocarray(NULL, env->count + 1, sizeof(char *));
if (!envp) if (!envp)
err(1, NULL); err(1, NULL);
i = 0; i = 0;
@ -115,72 +140,74 @@ flattenenv(struct env *env)
} }
static void static void
copyenv(struct env *orig, struct env *copy, const char **envlist) fillenv(struct env *env, const char **envlist)
{ {
struct envnode *node, key; struct envnode *node, key;
const char *e, *eq;
const char *val;
char name[1024];
u_int i; u_int i;
size_t len;
for (i = 0; envlist[i]; i++) { for (i = 0; envlist[i]; i++) {
key.key = envlist[i]; e = envlist[i];
if ((node = RB_FIND(envtree, &orig->root, &key))) {
RB_REMOVE(envtree, &orig->root, node); /* parse out env name */
orig->count--; if ((eq = strchr(e, '=')) == NULL)
RB_INSERT(envtree, &copy->root, node); len = strlen(e);
copy->count++; else
len = eq - e;
if (len > sizeof(name) - 1)
continue;
memcpy(name, e, len);
name[len] = '\0';
/* delete previous copies */
key.key = name;
if (*name == '-')
key.key = name + 1;
if ((node = RB_FIND(envtree, &env->root, &key))) {
RB_REMOVE(envtree, &env->root, node);
freenode(node);
env->count--;
}
if (*name == '-')
continue;
/* assign value or inherit from environ */
if (eq) {
val = eq + 1;
if (*val == '$')
val = getenv(val + 1);
} else {
val = getenv(name);
}
/* at last, we have something to insert */
if (val) {
node = createnode(name, val);
RB_INSERT(envtree, &env->root, node);
env->count++;
} }
} }
} }
struct env *
filterenv(struct env *orig, struct rule *rule)
{
const char *safeset[] = {
"DISPLAY", "HOME", "LOGNAME", "MAIL",
"PATH", "TERM", "USER", "USERNAME",
NULL
};
const char *badset[] = {
"ENV",
NULL
};
struct env *copy;
struct envnode *node, key;
u_int i;
if ((rule->options & KEEPENV) && !rule->envlist) {
for (i = 0; badset[i]; i++) {
key.key = badset[i];
if ((node = RB_FIND(envtree, &orig->root, &key))) {
RB_REMOVE(envtree, &orig->root, node);
free((char *)node->key);
free((char *)node->value);
free(node);
orig->count--;
}
}
return orig;
}
copy = malloc(sizeof(*copy));
if (!copy)
err(1, NULL);
RB_INIT(&copy->root);
copy->count = 0;
if (rule->envlist)
copyenv(orig, copy, rule->envlist);
copyenv(orig, copy, safeset);
return copy;
}
char ** char **
prepenv(struct rule *rule) prepenv(struct rule *rule)
{ {
extern char **environ; static const char *safeset[] = {
"DISPLAY", "HOME", "LOGNAME", "MAIL",
"PATH", "TERM", "USER", "USERNAME",
NULL
};
struct env *env; struct env *env;
env = createenv(environ); env = createenv(rule);
env = filterenv(env, rule);
/* if we started with blank, fill some defaults then apply rules */
if (!(rule->options & KEEPENV))
fillenv(env, safeset);
if (rule->envlist)
fillenv(env, rule->envlist);
return flattenenv(env); return flattenenv(env);
} }

92
parse.y
View file

@ -1,4 +1,4 @@
/* $OpenBSD: parse.y,v 1.18 2016/06/07 16:49:23 tedu Exp $ */ /* $OpenBSD: parse.y,v 1.26 2017/01/02 01:40:20 tedu Exp $ */
/* /*
* Copyright (c) 2015 Ted Unangst <tedu@openbsd.org> * Copyright (c) 2015 Ted Unangst <tedu@openbsd.org>
* *
@ -22,7 +22,6 @@
#include <stdint.h> #include <stdint.h>
#include <stdarg.h> #include <stdarg.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h>
#include <string.h> #include <string.h>
#include <err.h> #include <err.h>
@ -37,6 +36,7 @@ typedef struct {
const char **cmdargs; const char **cmdargs;
const char **envlist; const char **envlist;
}; };
const char **strlist;
const char *str; const char *str;
}; };
int lineno; int lineno;
@ -47,17 +47,30 @@ typedef struct {
FILE *yyfp; FILE *yyfp;
struct rule **rules; struct rule **rules;
int nrules, maxrules; int nrules;
static int maxrules;
int parse_errors = 0; int parse_errors = 0;
void yyerror(const char *, ...); static void yyerror(const char *, ...);
int yylex(void); static int yylex(void);
int yyparse(void);
static size_t
arraylen(const char **arr)
{
size_t cnt = 0;
while (*arr) {
cnt++;
arr++;
}
return cnt;
}
%} %}
%token TPERMIT TDENY TAS TCMD TARGS %token TPERMIT TDENY TAS TCMD TARGS
%token TNOPASS TKEEPENV %token TNOPASS TPERSIST TKEEPENV TSETENV
%token TSTRING %token TSTRING
%% %%
@ -98,15 +111,23 @@ action: TPERMIT options {
$$.envlist = $2.envlist; $$.envlist = $2.envlist;
} | TDENY { } | TDENY {
$$.action = DENY; $$.action = DENY;
$$.options = 0;
$$.envlist = NULL;
} ; } ;
options: /* none */ options: /* none */ {
| options option { $$.options = 0;
$$.envlist = NULL;
} | options option {
$$.options = $1.options | $2.options; $$.options = $1.options | $2.options;
$$.envlist = $1.envlist; $$.envlist = $1.envlist;
if (($$.options & (NOPASS|PERSIST)) == (NOPASS|PERSIST)) {
yyerror("can't combine nopass and persist");
YYERROR;
}
if ($2.envlist) { if ($2.envlist) {
if ($$.envlist) { if ($$.envlist) {
yyerror("can't have two keepenv sections"); yyerror("can't have two setenv sections");
YYERROR; YYERROR;
} else } else
$$.envlist = $2.envlist; $$.envlist = $2.envlist;
@ -114,24 +135,29 @@ options: /* none */
} ; } ;
option: TNOPASS { option: TNOPASS {
$$.options = NOPASS; $$.options = NOPASS;
$$.envlist = NULL;
} | TPERSIST {
$$.options = PERSIST;
$$.envlist = NULL;
} | TKEEPENV { } | TKEEPENV {
$$.options = KEEPENV; $$.options = KEEPENV;
} | TKEEPENV '{' envlist '}' { $$.envlist = NULL;
$$.options = KEEPENV; } | TSETENV '{' strlist '}' {
$$.envlist = $3.envlist; $$.options = 0;
$$.envlist = $3.strlist;
} ; } ;
envlist: /* empty */ { strlist: /* empty */ {
if (!($$.envlist = calloc(1, sizeof(char *)))) if (!($$.strlist = calloc(1, sizeof(char *))))
errx(1, "can't allocate envlist"); errx(1, "can't allocate strlist");
} | envlist TSTRING { } | strlist TSTRING {
int nenv = arraylen($1.envlist); int nstr = arraylen($1.strlist);
if (!($$.envlist = reallocarray($1.envlist, nenv + 2, if (!($$.strlist = reallocarray($1.strlist, nstr + 2,
sizeof(char *)))) sizeof(char *))))
errx(1, "can't allocate envlist"); errx(1, "can't allocate strlist");
$$.envlist[nenv] = $2.str; $$.strlist[nstr] = $2.str;
$$.envlist[nenv + 1] = NULL; $$.strlist[nstr + 1] = NULL;
} } ;
ident: TSTRING { ident: TSTRING {
@ -154,20 +180,8 @@ cmd: /* optional */ {
args: /* empty */ { args: /* empty */ {
$$.cmdargs = NULL; $$.cmdargs = NULL;
} | TARGS argslist { } | TARGS strlist {
$$.cmdargs = $2.cmdargs; $$.cmdargs = $2.strlist;
} ;
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;
} ; } ;
%% %%
@ -185,7 +199,7 @@ yyerror(const char *fmt, ...)
parse_errors++; parse_errors++;
} }
struct keyword { static struct keyword {
const char *word; const char *word;
int token; int token;
} keywords[] = { } keywords[] = {
@ -195,7 +209,9 @@ struct keyword {
{ "cmd", TCMD }, { "cmd", TCMD },
{ "args", TARGS }, { "args", TARGS },
{ "nopass", TNOPASS }, { "nopass", TNOPASS },
{ "persist", TPERSIST },
{ "keepenv", TKEEPENV }, { "keepenv", TKEEPENV },
{ "setenv", TSETENV },
}; };
int int