1*ce8e0faaSderaadt /* $OpenBSD: parse.y,v 1.31 2022/03/22 20:36:49 deraadt Exp $ */
27bfbda14Stedu /*
37bfbda14Stedu * Copyright (c) 2015 Ted Unangst <tedu@openbsd.org>
47bfbda14Stedu *
57bfbda14Stedu * Permission to use, copy, modify, and distribute this software for any
67bfbda14Stedu * purpose with or without fee is hereby granted, provided that the above
77bfbda14Stedu * copyright notice and this permission notice appear in all copies.
87bfbda14Stedu *
97bfbda14Stedu * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
107bfbda14Stedu * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
117bfbda14Stedu * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
127bfbda14Stedu * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
137bfbda14Stedu * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
147bfbda14Stedu * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
157bfbda14Stedu * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
167bfbda14Stedu */
177bfbda14Stedu
187bfbda14Stedu %{
197bfbda14Stedu #include <sys/types.h>
207bfbda14Stedu #include <ctype.h>
21cbc92769Stobias #include <limits.h>
227bfbda14Stedu #include <unistd.h>
237bfbda14Stedu #include <stdint.h>
247bfbda14Stedu #include <stdarg.h>
257bfbda14Stedu #include <stdio.h>
267bfbda14Stedu #include <string.h>
277bfbda14Stedu #include <err.h>
287bfbda14Stedu
297bfbda14Stedu #include "doas.h"
307bfbda14Stedu
317bfbda14Stedu typedef struct {
327bfbda14Stedu union {
337bfbda14Stedu struct {
347bfbda14Stedu int action;
357bfbda14Stedu int options;
36cb7cef4cSzhuk const char *cmd;
37cb7cef4cSzhuk const char **cmdargs;
387bfbda14Stedu const char **envlist;
397bfbda14Stedu };
401a05de5dStedu const char **strlist;
417bfbda14Stedu const char *str;
427bfbda14Stedu };
43cbc92769Stobias unsigned long lineno;
44cbc92769Stobias unsigned long colno;
457bfbda14Stedu } yystype;
467bfbda14Stedu #define YYSTYPE yystype
477bfbda14Stedu
487bfbda14Stedu FILE *yyfp;
497bfbda14Stedu
507bfbda14Stedu struct rule **rules;
51618b6875Smillert size_t nrules;
52618b6875Smillert static size_t maxrules;
537bfbda14Stedu
54cbc92769Stobias int parse_error = 0;
552bab682cSderaadt
562bab682cSderaadt static void yyerror(const char *, ...);
572bab682cSderaadt static int yylex(void);
586ced1a25Snicm
5907b2eb22Stedu static size_t
arraylen(const char ** arr)6007b2eb22Stedu arraylen(const char **arr)
6107b2eb22Stedu {
6207b2eb22Stedu size_t cnt = 0;
6307b2eb22Stedu
6407b2eb22Stedu while (*arr) {
6507b2eb22Stedu cnt++;
6607b2eb22Stedu arr++;
6707b2eb22Stedu }
6807b2eb22Stedu return cnt;
6907b2eb22Stedu }
7007b2eb22Stedu
717bfbda14Stedu %}
727bfbda14Stedu
73cb7cef4cSzhuk %token TPERMIT TDENY TAS TCMD TARGS
74d4bf2b56Skn %token TNOPASS TNOLOG TPERSIST TKEEPENV TSETENV
757bfbda14Stedu %token TSTRING
767bfbda14Stedu
777bfbda14Stedu %%
787bfbda14Stedu
797bfbda14Stedu grammar: /* empty */
807bfbda14Stedu | grammar '\n'
817bfbda14Stedu | grammar rule '\n'
827c3b0d20Szhuk | error '\n'
837bfbda14Stedu ;
847bfbda14Stedu
857bfbda14Stedu rule: action ident target cmd {
867bfbda14Stedu struct rule *r;
87*ce8e0faaSderaadt
887bfbda14Stedu r = calloc(1, sizeof(*r));
899b960859Snicm if (!r)
909b960859Snicm errx(1, "can't allocate rule");
917bfbda14Stedu r->action = $1.action;
927bfbda14Stedu r->options = $1.options;
937bfbda14Stedu r->envlist = $1.envlist;
947bfbda14Stedu r->ident = $2.str;
957bfbda14Stedu r->target = $3.str;
96cb7cef4cSzhuk r->cmd = $4.cmd;
97cb7cef4cSzhuk r->cmdargs = $4.cmdargs;
987bfbda14Stedu if (nrules == maxrules) {
997bfbda14Stedu if (maxrules == 0)
100618b6875Smillert maxrules = 32;
101618b6875Smillert rules = reallocarray(rules, maxrules,
102618b6875Smillert 2 * sizeof(*rules));
103618b6875Smillert if (!rules)
1047bfbda14Stedu errx(1, "can't allocate rules");
105618b6875Smillert maxrules *= 2;
1067bfbda14Stedu }
1077bfbda14Stedu rules[nrules++] = r;
1087bfbda14Stedu } ;
1097bfbda14Stedu
1107bfbda14Stedu action: TPERMIT options {
1117bfbda14Stedu $$.action = PERMIT;
1127bfbda14Stedu $$.options = $2.options;
1137bfbda14Stedu $$.envlist = $2.envlist;
1147bfbda14Stedu } | TDENY {
1157bfbda14Stedu $$.action = DENY;
116ab7e7725Stedu $$.options = 0;
117ab7e7725Stedu $$.envlist = NULL;
1187bfbda14Stedu } ;
1197bfbda14Stedu
120ab7e7725Stedu options: /* none */ {
121ab7e7725Stedu $$.options = 0;
122ab7e7725Stedu $$.envlist = NULL;
123ab7e7725Stedu } | options option {
1247bfbda14Stedu $$.options = $1.options | $2.options;
1257bfbda14Stedu $$.envlist = $1.envlist;
1268dda54eeStedu if (($$.options & (NOPASS|PERSIST)) == (NOPASS|PERSIST)) {
1278dda54eeStedu yyerror("can't combine nopass and persist");
1288dda54eeStedu YYERROR;
1298dda54eeStedu }
1307bfbda14Stedu if ($2.envlist) {
1317c3b0d20Szhuk if ($$.envlist) {
132ab7e7725Stedu yyerror("can't have two setenv sections");
1337c3b0d20Szhuk YYERROR;
1347c3b0d20Szhuk } else
1357bfbda14Stedu $$.envlist = $2.envlist;
1367bfbda14Stedu }
1377bfbda14Stedu } ;
1387bfbda14Stedu option: TNOPASS {
1397bfbda14Stedu $$.options = NOPASS;
140ab7e7725Stedu $$.envlist = NULL;
141d4bf2b56Skn } | TNOLOG {
142d4bf2b56Skn $$.options = NOLOG;
143d4bf2b56Skn $$.envlist = NULL;
1440a39d05fStedu } | TPERSIST {
1450a39d05fStedu $$.options = PERSIST;
1460a39d05fStedu $$.envlist = NULL;
1477bfbda14Stedu } | TKEEPENV {
1487bfbda14Stedu $$.options = KEEPENV;
149ab7e7725Stedu $$.envlist = NULL;
1501a05de5dStedu } | TSETENV '{' strlist '}' {
151ab7e7725Stedu $$.options = 0;
1521a05de5dStedu $$.envlist = $3.strlist;
1537bfbda14Stedu } ;
1547bfbda14Stedu
1551a05de5dStedu strlist: /* empty */ {
1561a05de5dStedu if (!($$.strlist = calloc(1, sizeof(char *))))
1571a05de5dStedu errx(1, "can't allocate strlist");
1581a05de5dStedu } | strlist TSTRING {
1591a05de5dStedu int nstr = arraylen($1.strlist);
160*ce8e0faaSderaadt
1611a05de5dStedu if (!($$.strlist = reallocarray($1.strlist, nstr + 2,
162c79a7a63Sbenno sizeof(char *))))
1631a05de5dStedu errx(1, "can't allocate strlist");
1641a05de5dStedu $$.strlist[nstr] = $2.str;
1651a05de5dStedu $$.strlist[nstr + 1] = NULL;
166d023f658Stedu } ;
16706bf2893Sdjm
16806bf2893Sdjm
1697bfbda14Stedu ident: TSTRING {
1707bfbda14Stedu $$.str = $1.str;
1717bfbda14Stedu } ;
1727bfbda14Stedu
1737bfbda14Stedu target: /* optional */ {
1747bfbda14Stedu $$.str = NULL;
1757bfbda14Stedu } | TAS TSTRING {
1767bfbda14Stedu $$.str = $2.str;
1777bfbda14Stedu } ;
1787bfbda14Stedu
1797bfbda14Stedu cmd: /* optional */ {
180cb7cef4cSzhuk $$.cmd = NULL;
181cb7cef4cSzhuk $$.cmdargs = NULL;
182cb7cef4cSzhuk } | TCMD TSTRING args {
183cb7cef4cSzhuk $$.cmd = $2.str;
184cb7cef4cSzhuk $$.cmdargs = $3.cmdargs;
185cb7cef4cSzhuk } ;
186cb7cef4cSzhuk
187cb7cef4cSzhuk args: /* empty */ {
188cb7cef4cSzhuk $$.cmdargs = NULL;
1891a05de5dStedu } | TARGS strlist {
1901a05de5dStedu $$.cmdargs = $2.strlist;
1917bfbda14Stedu } ;
1927bfbda14Stedu
1937bfbda14Stedu %%
1947bfbda14Stedu
1957bfbda14Stedu void
1967bfbda14Stedu yyerror(const char *fmt, ...)
1977bfbda14Stedu {
1987bfbda14Stedu va_list va;
1997bfbda14Stedu
2000a57f0e3Sgsoares fprintf(stderr, "doas: ");
2017bfbda14Stedu va_start(va, fmt);
2027c3b0d20Szhuk vfprintf(stderr, fmt, va);
2037c3b0d20Szhuk va_end(va);
204cbc92769Stobias fprintf(stderr, " at line %lu\n", yylval.lineno + 1);
205cbc92769Stobias parse_error = 1;
2067bfbda14Stedu }
2077bfbda14Stedu
2082bab682cSderaadt static struct keyword {
2097bfbda14Stedu const char *word;
2107bfbda14Stedu int token;
2117bfbda14Stedu } keywords[] = {
2127bfbda14Stedu { "deny", TDENY },
2137bfbda14Stedu { "permit", TPERMIT },
2147bfbda14Stedu { "as", TAS },
2157bfbda14Stedu { "cmd", TCMD },
216cb7cef4cSzhuk { "args", TARGS },
2177bfbda14Stedu { "nopass", TNOPASS },
218d4bf2b56Skn { "nolog", TNOLOG },
2190a39d05fStedu { "persist", TPERSIST },
2207bfbda14Stedu { "keepenv", TKEEPENV },
221ab7e7725Stedu { "setenv", TSETENV },
2227bfbda14Stedu };
2237bfbda14Stedu
2247bfbda14Stedu int
yylex(void)2257bfbda14Stedu yylex(void)
2267bfbda14Stedu {
2277bfbda14Stedu char buf[1024], *ebuf, *p, *str;
228cbc92769Stobias int c, quoted = 0, quotes = 0, qerr = 0, escape = 0, nonkw = 0;
229cbc92769Stobias unsigned long qpos = 0;
230618b6875Smillert size_t i;
2317bfbda14Stedu
2327bfbda14Stedu p = buf;
2337bfbda14Stedu ebuf = buf + sizeof(buf);
234bc1c10e1Szhuk
235c3de7d61Sbenno repeat:
236bc1c10e1Szhuk /* skip whitespace first */
237bc1c10e1Szhuk for (c = getc(yyfp); c == ' ' || c == '\t'; c = getc(yyfp))
2387c3b0d20Szhuk yylval.colno++;
239bc1c10e1Szhuk
240bc1c10e1Szhuk /* check for special one-character constructions */
2417bfbda14Stedu switch (c) {
2427bfbda14Stedu case '\n':
2437c3b0d20Szhuk yylval.colno = 0;
2447c3b0d20Szhuk yylval.lineno++;
245bc1c10e1Szhuk /* FALLTHROUGH */
2467bfbda14Stedu case '{':
2477bfbda14Stedu case '}':
2487bfbda14Stedu return c;
2497bfbda14Stedu case '#':
250bc1c10e1Szhuk /* skip comments; NUL is allowed; no continuation */
251bc1c10e1Szhuk while ((c = getc(yyfp)) != '\n')
2527bfbda14Stedu if (c == EOF)
25380453096Stedu goto eof;
2547c3b0d20Szhuk yylval.colno = 0;
2557c3b0d20Szhuk yylval.lineno++;
2567bfbda14Stedu return c;
2577bfbda14Stedu case EOF:
25880453096Stedu goto eof;
2597bfbda14Stedu }
260bc1c10e1Szhuk
261bc1c10e1Szhuk /* parsing next word */
2627c3b0d20Szhuk for (;; c = getc(yyfp), yylval.colno++) {
263f83af921Szhuk switch (c) {
264bc1c10e1Szhuk case '\0':
265cbc92769Stobias yyerror("unallowed character NUL in column %lu",
26622ac959bSderaadt yylval.colno + 1);
267bc1c10e1Szhuk escape = 0;
268bc1c10e1Szhuk continue;
269bc1c10e1Szhuk case '\\':
270bc1c10e1Szhuk escape = !escape;
271bc1c10e1Szhuk if (escape)
272bc1c10e1Szhuk continue;
273bc1c10e1Szhuk break;
274f83af921Szhuk case '\n':
275cbc92769Stobias if (quotes && !qerr) {
276cbc92769Stobias yyerror("unterminated quotes in column %lu",
2777c3b0d20Szhuk qpos + 1);
278cbc92769Stobias qerr = 1;
279cbc92769Stobias }
280bc1c10e1Szhuk if (escape) {
281bc1c10e1Szhuk nonkw = 1;
282bc1c10e1Szhuk escape = 0;
283cbc92769Stobias yylval.colno = ULONG_MAX;
284a9033b92Smikeb yylval.lineno++;
285bc1c10e1Szhuk continue;
286bc1c10e1Szhuk }
287bc1c10e1Szhuk goto eow;
288bc1c10e1Szhuk case EOF:
289bc1c10e1Szhuk if (escape)
290cbc92769Stobias yyerror("unterminated escape in column %lu",
2917c3b0d20Szhuk yylval.colno);
292cbc92769Stobias if (quotes && !qerr)
293cbc92769Stobias yyerror("unterminated quotes in column %lu",
2947c3b0d20Szhuk qpos + 1);
2957c3b0d20Szhuk goto eow;
296f83af921Szhuk case '{':
297f83af921Szhuk case '}':
298f83af921Szhuk case '#':
299f83af921Szhuk case ' ':
300f83af921Szhuk case '\t':
301bc1c10e1Szhuk if (!escape && !quotes)
302f83af921Szhuk goto eow;
303bc1c10e1Szhuk break;
304bc1c10e1Szhuk case '"':
305bc1c10e1Szhuk if (!escape) {
306cbc92769Stobias quoted = 1;
307bc1c10e1Szhuk quotes = !quotes;
308bc1c10e1Szhuk if (quotes) {
309bc1c10e1Szhuk nonkw = 1;
310cbc92769Stobias qerr = 0;
3117c3b0d20Szhuk qpos = yylval.colno;
312bc1c10e1Szhuk }
313bc1c10e1Szhuk continue;
314bc1c10e1Szhuk }
315f83af921Szhuk }
3167bfbda14Stedu *p++ = c;
317402eeae3Stedu if (p == ebuf) {
3187c3b0d20Szhuk yyerror("too long line");
319402eeae3Stedu p = buf;
320402eeae3Stedu }
321bc1c10e1Szhuk escape = 0;
3227bfbda14Stedu }
323bc1c10e1Szhuk
324f83af921Szhuk eow:
3257bfbda14Stedu *p = 0;
3267bfbda14Stedu if (c != EOF)
3277bfbda14Stedu ungetc(c, yyfp);
328bc1c10e1Szhuk if (p == buf) {
329bc1c10e1Szhuk /*
33022ac959bSderaadt * There could be a number of reasons for empty buffer,
33122ac959bSderaadt * and we handle all of them here, to avoid cluttering
33222ac959bSderaadt * the main loop.
333bc1c10e1Szhuk */
334bc1c10e1Szhuk if (c == EOF)
33580453096Stedu goto eof;
336cbc92769Stobias else if (!quoted) /* accept, e.g., empty args: cmd foo args "" */
337bc1c10e1Szhuk goto repeat;
338bc1c10e1Szhuk }
339bc1c10e1Szhuk if (!nonkw) {
3407bfbda14Stedu for (i = 0; i < sizeof(keywords) / sizeof(keywords[0]); i++) {
3417bfbda14Stedu if (strcmp(buf, keywords[i].word) == 0)
3427bfbda14Stedu return keywords[i].token;
3437bfbda14Stedu }
344bc1c10e1Szhuk }
3457bfbda14Stedu if ((str = strdup(buf)) == NULL)
346a062aa9dSkrw err(1, "%s", __func__);
3477bfbda14Stedu yylval.str = str;
3487bfbda14Stedu return TSTRING;
34980453096Stedu
35080453096Stedu eof:
35180453096Stedu if (ferror(yyfp))
35280453096Stedu yyerror("input error reading config");
35380453096Stedu return 0;
3547bfbda14Stedu }
355