Annotation of src/usr.bin/doas/parse.y, Revision 1.18
1.18 ! tedu 1: /* $OpenBSD: parse.y,v 1.15 2016/04/27 02:35:55 gsoares Exp $ */
1.1 tedu 2: /*
3: * Copyright (c) 2015 Ted Unangst <tedu@openbsd.org>
4: *
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.
8: *
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.
16: */
17:
18: %{
19: #include <sys/types.h>
20: #include <ctype.h>
21: #include <unistd.h>
22: #include <stdint.h>
23: #include <stdarg.h>
24: #include <stdio.h>
25: #include <string.h>
26: #include <err.h>
27:
28: #include "doas.h"
29:
30: typedef struct {
31: union {
32: struct {
33: int action;
34: int options;
1.7 zhuk 35: const char *cmd;
36: const char **cmdargs;
1.1 tedu 37: const char **envlist;
38: };
39: const char *str;
40: };
1.10 zhuk 41: int lineno;
42: int colno;
1.1 tedu 43: } yystype;
44: #define YYSTYPE yystype
45:
46: FILE *yyfp;
47:
48: struct rule **rules;
49: int nrules, maxrules;
1.10 zhuk 50: int parse_errors = 0;
1.1 tedu 51:
1.4 nicm 52: void yyerror(const char *, ...);
53: int yylex(void);
54: int yyparse(void);
55:
1.1 tedu 56: %}
57:
1.7 zhuk 58: %token TPERMIT TDENY TAS TCMD TARGS
1.17 tedu 59: %token TNOPASS TKEEPENV
1.1 tedu 60: %token TSTRING
61:
62: %%
63:
64: grammar: /* empty */
65: | grammar '\n'
66: | grammar rule '\n'
1.10 zhuk 67: | error '\n'
1.1 tedu 68: ;
69:
70: rule: action ident target cmd {
71: struct rule *r;
72: r = calloc(1, sizeof(*r));
1.2 nicm 73: if (!r)
74: errx(1, "can't allocate rule");
1.1 tedu 75: r->action = $1.action;
76: r->options = $1.options;
77: r->envlist = $1.envlist;
78: r->ident = $2.str;
79: r->target = $3.str;
1.7 zhuk 80: r->cmd = $4.cmd;
81: r->cmdargs = $4.cmdargs;
1.1 tedu 82: if (nrules == maxrules) {
83: if (maxrules == 0)
84: maxrules = 63;
85: else
86: maxrules *= 2;
1.6 benno 87: if (!(rules = reallocarray(rules, maxrules,
88: sizeof(*rules))))
1.1 tedu 89: errx(1, "can't allocate rules");
90: }
91: rules[nrules++] = r;
92: } ;
93:
94: action: TPERMIT options {
95: $$.action = PERMIT;
96: $$.options = $2.options;
97: $$.envlist = $2.envlist;
98: } | TDENY {
99: $$.action = DENY;
100: } ;
101:
102: options: /* none */
103: | options option {
104: $$.options = $1.options | $2.options;
105: $$.envlist = $1.envlist;
106: if ($2.envlist) {
1.10 zhuk 107: if ($$.envlist) {
108: yyerror("can't have two keepenv sections");
109: YYERROR;
110: } else
1.1 tedu 111: $$.envlist = $2.envlist;
112: }
113: } ;
114: option: TNOPASS {
115: $$.options = NOPASS;
116: } | TKEEPENV {
117: $$.options = KEEPENV;
118: } | TKEEPENV '{' envlist '}' {
119: $$.options = KEEPENV;
120: $$.envlist = $3.envlist;
121: } ;
122:
123: envlist: /* empty */ {
124: if (!($$.envlist = calloc(1, sizeof(char *))))
125: errx(1, "can't allocate envlist");
126: } | envlist TSTRING {
127: int nenv = arraylen($1.envlist);
1.6 benno 128: if (!($$.envlist = reallocarray($1.envlist, nenv + 2,
129: sizeof(char *))))
1.1 tedu 130: errx(1, "can't allocate envlist");
131: $$.envlist[nenv] = $2.str;
132: $$.envlist[nenv + 1] = NULL;
1.18 ! tedu 133: }
1.16 djm 134:
135:
1.1 tedu 136: ident: TSTRING {
137: $$.str = $1.str;
138: } ;
139:
140: target: /* optional */ {
141: $$.str = NULL;
142: } | TAS TSTRING {
143: $$.str = $2.str;
144: } ;
145:
146: cmd: /* optional */ {
1.7 zhuk 147: $$.cmd = NULL;
148: $$.cmdargs = NULL;
149: } | TCMD TSTRING args {
150: $$.cmd = $2.str;
151: $$.cmdargs = $3.cmdargs;
152: } ;
153:
154: args: /* empty */ {
155: $$.cmdargs = NULL;
156: } | TARGS argslist {
157: $$.cmdargs = $2.cmdargs;
158: } ;
159:
160: argslist: /* empty */ {
161: if (!($$.cmdargs = calloc(1, sizeof(char *))))
162: errx(1, "can't allocate args");
163: } | argslist TSTRING {
164: int nargs = arraylen($1.cmdargs);
1.11 deraadt 165: if (!($$.cmdargs = reallocarray($1.cmdargs, nargs + 2,
166: sizeof(char *))))
1.7 zhuk 167: errx(1, "can't allocate args");
168: $$.cmdargs[nargs] = $2.str;
169: $$.cmdargs[nargs + 1] = NULL;
1.1 tedu 170: } ;
171:
172: %%
173:
174: void
175: yyerror(const char *fmt, ...)
176: {
177: va_list va;
178:
1.15 gsoares 179: fprintf(stderr, "doas: ");
1.1 tedu 180: va_start(va, fmt);
1.10 zhuk 181: vfprintf(stderr, fmt, va);
182: va_end(va);
183: fprintf(stderr, " at line %d\n", yylval.lineno + 1);
184: parse_errors++;
1.1 tedu 185: }
186:
187: struct keyword {
188: const char *word;
189: int token;
190: } keywords[] = {
191: { "deny", TDENY },
192: { "permit", TPERMIT },
193: { "as", TAS },
194: { "cmd", TCMD },
1.7 zhuk 195: { "args", TARGS },
1.1 tedu 196: { "nopass", TNOPASS },
197: { "keepenv", TKEEPENV },
198: };
199:
200: int
201: yylex(void)
202: {
203: char buf[1024], *ebuf, *p, *str;
1.10 zhuk 204: int i, c, quotes = 0, escape = 0, qpos = -1, nonkw = 0;
1.1 tedu 205:
206: p = buf;
207: ebuf = buf + sizeof(buf);
1.9 zhuk 208:
1.5 benno 209: repeat:
1.9 zhuk 210: /* skip whitespace first */
211: for (c = getc(yyfp); c == ' ' || c == '\t'; c = getc(yyfp))
1.10 zhuk 212: yylval.colno++;
1.9 zhuk 213:
214: /* check for special one-character constructions */
1.1 tedu 215: switch (c) {
1.9 zhuk 216: case '\n':
1.10 zhuk 217: yylval.colno = 0;
218: yylval.lineno++;
1.9 zhuk 219: /* FALLTHROUGH */
220: case '{':
221: case '}':
222: return c;
223: case '#':
224: /* skip comments; NUL is allowed; no continuation */
225: while ((c = getc(yyfp)) != '\n')
226: if (c == EOF)
1.14 tedu 227: goto eof;
1.10 zhuk 228: yylval.colno = 0;
229: yylval.lineno++;
1.9 zhuk 230: return c;
231: case EOF:
1.14 tedu 232: goto eof;
1.1 tedu 233: }
1.9 zhuk 234:
235: /* parsing next word */
1.10 zhuk 236: for (;; c = getc(yyfp), yylval.colno++) {
1.3 zhuk 237: switch (c) {
1.9 zhuk 238: case '\0':
1.11 deraadt 239: yyerror("unallowed character NUL in column %d",
240: yylval.colno + 1);
1.9 zhuk 241: escape = 0;
242: continue;
243: case '\\':
244: escape = !escape;
245: if (escape)
246: continue;
247: break;
1.3 zhuk 248: case '\n':
1.9 zhuk 249: if (quotes)
1.10 zhuk 250: yyerror("unterminated quotes in column %d",
251: qpos + 1);
1.9 zhuk 252: if (escape) {
253: nonkw = 1;
254: escape = 0;
1.12 mikeb 255: yylval.colno = 0;
256: yylval.lineno++;
1.9 zhuk 257: continue;
258: }
259: goto eow;
260: case EOF:
261: if (escape)
1.10 zhuk 262: yyerror("unterminated escape in column %d",
263: yylval.colno);
1.9 zhuk 264: if (quotes)
1.10 zhuk 265: yyerror("unterminated quotes in column %d",
266: qpos + 1);
267: goto eow;
1.9 zhuk 268: /* FALLTHROUGH */
1.3 zhuk 269: case '{':
270: case '}':
271: case '#':
272: case ' ':
273: case '\t':
1.9 zhuk 274: if (!escape && !quotes)
275: goto eow;
276: break;
277: case '"':
278: if (!escape) {
279: quotes = !quotes;
280: if (quotes) {
281: nonkw = 1;
1.10 zhuk 282: qpos = yylval.colno;
1.9 zhuk 283: }
284: continue;
285: }
1.3 zhuk 286: }
1.1 tedu 287: *p++ = c;
1.13 tedu 288: if (p == ebuf) {
1.10 zhuk 289: yyerror("too long line");
1.13 tedu 290: p = buf;
291: }
1.9 zhuk 292: escape = 0;
1.1 tedu 293: }
1.9 zhuk 294:
1.3 zhuk 295: eow:
1.1 tedu 296: *p = 0;
297: if (c != EOF)
298: ungetc(c, yyfp);
1.9 zhuk 299: if (p == buf) {
300: /*
1.11 deraadt 301: * There could be a number of reasons for empty buffer,
302: * and we handle all of them here, to avoid cluttering
303: * the main loop.
1.9 zhuk 304: */
305: if (c == EOF)
1.14 tedu 306: goto eof;
1.10 zhuk 307: else if (qpos == -1) /* accept, e.g., empty args: cmd foo args "" */
1.9 zhuk 308: goto repeat;
309: }
310: if (!nonkw) {
311: for (i = 0; i < sizeof(keywords) / sizeof(keywords[0]); i++) {
312: if (strcmp(buf, keywords[i].word) == 0)
313: return keywords[i].token;
314: }
1.1 tedu 315: }
316: if ((str = strdup(buf)) == NULL)
317: err(1, "strdup");
318: yylval.str = str;
319: return TSTRING;
1.14 tedu 320:
321: eof:
322: if (ferror(yyfp))
323: yyerror("input error reading config");
324: return 0;
1.1 tedu 325: }