1 /* $OpenBSD: parse.y,v 1.16 2016/06/05 00:46:34 djm Exp $ */
3 * Copyright (c) 2015 Ted Unangst <tedu@openbsd.org>
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19 #include <sys/types.h>
41 const char **setenvlist;
48 #define YYSTYPE yystype
56 void yyerror(const char *, ...);
62 %token TPERMIT TDENY TAS TCMD TARGS
63 %token TNOPASS TKEEPENV TSETENV
74 rule: action ident target cmd {
76 r = calloc(1, sizeof(*r));
78 errx(1, "can't allocate rule");
79 r->action = $1.action;
80 r->options = $1.options;
81 r->envlist = $1.envlist;
82 r->setenvlist = $1.setenvlist;
86 r->cmdargs = $4.cmdargs;
87 if (nrules == maxrules) {
92 if (!(rules = reallocarray(rules, maxrules,
94 errx(1, "can't allocate rules");
99 action: TPERMIT options {
101 $$.options = $2.options;
102 $$.envlist = $2.envlist;
103 $$.setenvlist = $2.setenvlist;
108 options: /* none */ {
112 $$.options = $1.options | $2.options;
113 $$.envlist = $1.envlist;
116 yyerror("can't have two keepenv sections");
119 $$.envlist = $2.envlist;
121 $$.setenvlist = $1.setenvlist;
124 yyerror("can't have two setenv sections");
127 $$.setenvlist = $2.setenvlist;
134 $$.options = KEEPENV;
136 } | TKEEPENV '{' envlist '}' {
137 $$.options = KEEPENV;
138 $$.envlist = $3.envlist;
139 } | TSETENV '{' setenvlist '}' {
141 $$.setenvlist = NULL;
142 $$.setenvlist = $3.setenvlist;
145 envlist: /* empty */ {
147 if (!($$.envlist = calloc(1, sizeof(char *))))
148 errx(1, "can't allocate envlist");
149 } | envlist TSTRING {
150 int nenv = arraylen($1.envlist);
151 if (!($$.envlist = reallocarray($1.envlist, nenv + 2,
153 errx(1, "can't allocate envlist");
154 $$.envlist[nenv] = $2.str;
155 $$.envlist[nenv + 1] = NULL;
158 setenvlist: /* empty */ {
159 if (!($$.setenvlist = calloc(1, sizeof(char *))))
160 errx(1, "can't allocate setenvlist");
161 } | setenvlist TSTRING '=' TSTRING {
162 int nenv = arraylen($1.setenvlist);
165 if (*$2.str == '\0' || strchr($2.str, '=') != NULL) {
166 yyerror("invalid setenv expression");
169 if (!($$.setenvlist = reallocarray($1.setenvlist,
170 nenv + 2, sizeof(char *))))
171 errx(1, "can't allocate envlist");
172 $$.setenvlist[nenv] = NULL;
173 if (asprintf(&cp, "%s=%s", $2.str, $4.str) <= 0 ||
175 errx(1,"asprintf failed");
176 $$.setenvlist[nenv] = cp;
177 $$.setenvlist[nenv + 1] = NULL;
185 target: /* optional */ {
191 cmd: /* optional */ {
194 } | TCMD TSTRING args {
196 $$.cmdargs = $3.cmdargs;
202 $$.cmdargs = $2.cmdargs;
205 argslist: /* empty */ {
207 if (!($$.cmdargs = calloc(1, sizeof(char *))))
208 errx(1, "can't allocate args");
209 } | argslist TSTRING {
210 int nargs = arraylen($1.cmdargs);
211 if (!($$.cmdargs = reallocarray($1.cmdargs, nargs + 2,
213 errx(1, "can't allocate args");
214 $$.cmdargs[nargs] = $2.str;
215 $$.cmdargs[nargs + 1] = NULL;
221 yyerror(const char *fmt, ...)
225 fprintf(stderr, "doas: ");
227 vfprintf(stderr, fmt, va);
229 fprintf(stderr, " at line %d\n", yylval.lineno + 1);
238 { "permit", TPERMIT },
242 { "nopass", TNOPASS },
243 { "keepenv", TKEEPENV },
244 { "setenv", TSETENV },
250 char buf[1024], *ebuf, *p, *str;
251 int c, quotes = 0, escape = 0, qpos = -1, nonkw = 0;
254 ebuf = buf + sizeof(buf);
257 /* skip whitespace first */
258 for (c = getc(yyfp); c == ' ' || c == '\t'; c = getc(yyfp))
261 /* check for special one-character constructions */
272 /* skip comments; NUL is allowed; no continuation */
273 while ((c = getc(yyfp)) != '\n')
283 /* parsing next word */
284 for (;; c = getc(yyfp), yylval.colno++) {
287 yyerror("unallowed character NUL in column %d",
298 yyerror("unterminated quotes in column %d",
310 yyerror("unterminated escape in column %d",
313 yyerror("unterminated quotes in column %d",
323 if (!escape && !quotes)
338 yyerror("too long line");
350 * There could be a number of reasons for empty buffer,
351 * and we handle all of them here, to avoid cluttering
356 else if (qpos == -1) /* accept, e.g., empty args: cmd foo args "" */
361 for (i = 0; i < sizeof(keywords) / sizeof(keywords[0]); i++) {
362 if (strcmp(buf, keywords[i].word) == 0)
363 return keywords[i].token;
366 if ((str = strdup(buf)) == NULL)
373 yyerror("input error reading config");