GCC Code Coverage Report | |||||||||||||||||||||
|
|||||||||||||||||||||
Line | Branch | Exec | Source |
1 |
/* $OpenBSD: parse.y,v 1.26 2017/01/02 01:40:20 tedu Exp $ */ |
||
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; |
||
35 |
const char *cmd; |
||
36 |
const char **cmdargs; |
||
37 |
const char **envlist; |
||
38 |
}; |
||
39 |
const char **strlist; |
||
40 |
const char *str; |
||
41 |
}; |
||
42 |
int lineno; |
||
43 |
int colno; |
||
44 |
} yystype; |
||
45 |
#define YYSTYPE yystype |
||
46 |
|||
47 |
FILE *yyfp; |
||
48 |
|||
49 |
struct rule **rules; |
||
50 |
int nrules; |
||
51 |
static int maxrules; |
||
52 |
|||
53 |
int parse_errors = 0; |
||
54 |
|||
55 |
static void yyerror(const char *, ...); |
||
56 |
static int yylex(void); |
||
57 |
|||
58 |
static size_t |
||
59 |
arraylen(const char **arr) |
||
60 |
{ |
||
61 |
size_t cnt = 0; |
||
62 |
|||
63 |
✓✓ | 3460 |
while (*arr) { |
64 |
1505 |
cnt++; |
|
65 |
1505 |
arr++; |
|
66 |
} |
||
67 |
150 |
return cnt; |
|
68 |
} |
||
69 |
|||
70 |
%} |
||
71 |
|||
72 |
%token TPERMIT TDENY TAS TCMD TARGS |
||
73 |
%token TNOPASS TPERSIST TKEEPENV TSETENV |
||
74 |
%token TSTRING |
||
75 |
|||
76 |
%% |
||
77 |
|||
78 |
grammar: /* empty */ |
||
79 |
| grammar '\n' |
||
80 |
| grammar rule '\n' |
||
81 |
| error '\n' |
||
82 |
; |
||
83 |
|||
84 |
rule: action ident target cmd { |
||
85 |
struct rule *r; |
||
86 |
r = calloc(1, sizeof(*r)); |
||
87 |
if (!r) |
||
88 |
errx(1, "can't allocate rule"); |
||
89 |
r->action = $1.action; |
||
90 |
r->options = $1.options; |
||
91 |
r->envlist = $1.envlist; |
||
92 |
r->ident = $2.str; |
||
93 |
r->target = $3.str; |
||
94 |
r->cmd = $4.cmd; |
||
95 |
r->cmdargs = $4.cmdargs; |
||
96 |
if (nrules == maxrules) { |
||
97 |
if (maxrules == 0) |
||
98 |
maxrules = 63; |
||
99 |
else |
||
100 |
maxrules *= 2; |
||
101 |
if (!(rules = reallocarray(rules, maxrules, |
||
102 |
sizeof(*rules)))) |
||
103 |
errx(1, "can't allocate rules"); |
||
104 |
} |
||
105 |
rules[nrules++] = r; |
||
106 |
} ; |
||
107 |
|||
108 |
action: TPERMIT options { |
||
109 |
$$.action = PERMIT; |
||
110 |
$$.options = $2.options; |
||
111 |
$$.envlist = $2.envlist; |
||
112 |
} | TDENY { |
||
113 |
$$.action = DENY; |
||
114 |
$$.options = 0; |
||
115 |
$$.envlist = NULL; |
||
116 |
} ; |
||
117 |
|||
118 |
options: /* none */ { |
||
119 |
$$.options = 0; |
||
120 |
$$.envlist = NULL; |
||
121 |
} | options option { |
||
122 |
$$.options = $1.options | $2.options; |
||
123 |
$$.envlist = $1.envlist; |
||
124 |
if (($$.options & (NOPASS|PERSIST)) == (NOPASS|PERSIST)) { |
||
125 |
yyerror("can't combine nopass and persist"); |
||
126 |
YYERROR; |
||
127 |
} |
||
128 |
if ($2.envlist) { |
||
129 |
if ($$.envlist) { |
||
130 |
yyerror("can't have two setenv sections"); |
||
131 |
YYERROR; |
||
132 |
} else |
||
133 |
$$.envlist = $2.envlist; |
||
134 |
} |
||
135 |
} ; |
||
136 |
option: TNOPASS { |
||
137 |
$$.options = NOPASS; |
||
138 |
$$.envlist = NULL; |
||
139 |
} | TPERSIST { |
||
140 |
$$.options = PERSIST; |
||
141 |
$$.envlist = NULL; |
||
142 |
} | TKEEPENV { |
||
143 |
$$.options = KEEPENV; |
||
144 |
$$.envlist = NULL; |
||
145 |
} | TSETENV '{' strlist '}' { |
||
146 |
$$.options = 0; |
||
147 |
$$.envlist = $3.strlist; |
||
148 |
} ; |
||
149 |
|||
150 |
strlist: /* empty */ { |
||
151 |
if (!($$.strlist = calloc(1, sizeof(char *)))) |
||
152 |
errx(1, "can't allocate strlist"); |
||
153 |
} | strlist TSTRING { |
||
154 |
int nstr = arraylen($1.strlist); |
||
155 |
if (!($$.strlist = reallocarray($1.strlist, nstr + 2, |
||
156 |
sizeof(char *)))) |
||
157 |
errx(1, "can't allocate strlist"); |
||
158 |
$$.strlist[nstr] = $2.str; |
||
159 |
$$.strlist[nstr + 1] = NULL; |
||
160 |
} ; |
||
161 |
|||
162 |
|||
163 |
ident: TSTRING { |
||
164 |
$$.str = $1.str; |
||
165 |
} ; |
||
166 |
|||
167 |
target: /* optional */ { |
||
168 |
$$.str = NULL; |
||
169 |
} | TAS TSTRING { |
||
170 |
$$.str = $2.str; |
||
171 |
} ; |
||
172 |
|||
173 |
cmd: /* optional */ { |
||
174 |
$$.cmd = NULL; |
||
175 |
$$.cmdargs = NULL; |
||
176 |
} | TCMD TSTRING args { |
||
177 |
$$.cmd = $2.str; |
||
178 |
$$.cmdargs = $3.cmdargs; |
||
179 |
} ; |
||
180 |
|||
181 |
args: /* empty */ { |
||
182 |
$$.cmdargs = NULL; |
||
183 |
} | TARGS strlist { |
||
184 |
$$.cmdargs = $2.strlist; |
||
185 |
} ; |
||
186 |
|||
187 |
%% |
||
188 |
|||
189 |
void |
||
190 |
yyerror(const char *fmt, ...) |
||
191 |
{ |
||
192 |
30 |
va_list va; |
|
193 |
|||
194 |
15 |
fprintf(stderr, "doas: "); |
|
195 |
15 |
va_start(va, fmt); |
|
196 |
15 |
vfprintf(stderr, fmt, va); |
|
197 |
15 |
va_end(va); |
|
198 |
15 |
fprintf(stderr, " at line %d\n", yylval.lineno + 1); |
|
199 |
15 |
parse_errors++; |
|
200 |
15 |
} |
|
201 |
|||
202 |
static struct keyword { |
||
203 |
const char *word; |
||
204 |
int token; |
||
205 |
} keywords[] = { |
||
206 |
{ "deny", TDENY }, |
||
207 |
{ "permit", TPERMIT }, |
||
208 |
{ "as", TAS }, |
||
209 |
{ "cmd", TCMD }, |
||
210 |
{ "args", TARGS }, |
||
211 |
{ "nopass", TNOPASS }, |
||
212 |
{ "persist", TPERSIST }, |
||
213 |
{ "keepenv", TKEEPENV }, |
||
214 |
{ "setenv", TSETENV }, |
||
215 |
}; |
||
216 |
|||
217 |
int |
||
218 |
yylex(void) |
||
219 |
{ |
||
220 |
1150 |
char buf[1024], *ebuf, *p, *str; |
|
221 |
int i, c, quotes = 0, escape = 0, qpos = -1, nonkw = 0; |
||
222 |
|||
223 |
575 |
p = buf; |
|
224 |
575 |
ebuf = buf + sizeof(buf); |
|
225 |
|||
226 |
repeat: |
||
227 |
/* skip whitespace first */ |
||
228 |
✓✗✓✓ ✓✓✓✗ ✗✓ |
5575 |
for (c = getc(yyfp); c == ' ' || c == '\t'; c = getc(yyfp)) |
229 |
510 |
yylval.colno++; |
|
230 |
|||
231 |
/* check for special one-character constructions */ |
||
232 |
✓✗✓✗ ✓✓ |
700 |
switch (c) { |
233 |
case '\n': |
||
234 |
95 |
yylval.colno = 0; |
|
235 |
95 |
yylval.lineno++; |
|
236 |
/* FALLTHROUGH */ |
||
237 |
case '{': |
||
238 |
case '}': |
||
239 |
105 |
return c; |
|
240 |
case '#': |
||
241 |
/* skip comments; NUL is allowed; no continuation */ |
||
242 |
while ((c = getc(yyfp)) != '\n') |
||
243 |
if (c == EOF) |
||
244 |
goto eof; |
||
245 |
yylval.colno = 0; |
||
246 |
yylval.lineno++; |
||
247 |
return c; |
||
248 |
case EOF: |
||
249 |
goto eof; |
||
250 |
} |
||
251 |
|||
252 |
/* parsing next word */ |
||
253 |
✓✗✓✓ |
12050 |
for (;; c = getc(yyfp), yylval.colno++) { |
254 |
✗✓✓✓ ✗✗✗✗ ✓✓✓ |
3395 |
switch (c) { |
255 |
case '\0': |
||
256 |
yyerror("unallowed character NUL in column %d", |
||
257 |
yylval.colno + 1); |
||
258 |
escape = 0; |
||
259 |
continue; |
||
260 |
case '\\': |
||
261 |
40 |
escape = !escape; |
|
262 |
✗✓ | 40 |
if (escape) |
263 |
continue; |
||
264 |
break; |
||
265 |
case '\n': |
||
266 |
✓✓ | 110 |
if (quotes) |
267 |
5 |
yyerror("unterminated quotes in column %d", |
|
268 |
5 |
qpos + 1); |
|
269 |
✓✓ | 110 |
if (escape) { |
270 |
nonkw = 1; |
||
271 |
escape = 0; |
||
272 |
35 |
yylval.colno = 0; |
|
273 |
35 |
yylval.lineno++; |
|
274 |
35 |
continue; |
|
275 |
} |
||
276 |
goto eow; |
||
277 |
case EOF: |
||
278 |
✗✓ | 5 |
if (escape) |
279 |
yyerror("unterminated escape in column %d", |
||
280 |
yylval.colno); |
||
281 |
✓✗ | 5 |
if (quotes) |
282 |
5 |
yyerror("unterminated quotes in column %d", |
|
283 |
5 |
qpos + 1); |
|
284 |
goto eow; |
||
285 |
/* FALLTHROUGH */ |
||
286 |
case '{': |
||
287 |
case '}': |
||
288 |
case '#': |
||
289 |
case ' ': |
||
290 |
case '\t': |
||
291 |
✓✓ | 425 |
if (!escape && !quotes) |
292 |
goto eow; |
||
293 |
break; |
||
294 |
case '"': |
||
295 |
✓✗ | 90 |
if (!escape) { |
296 |
90 |
quotes = !quotes; |
|
297 |
✓✓ | 90 |
if (quotes) { |
298 |
nonkw = 1; |
||
299 |
50 |
qpos = yylval.colno; |
|
300 |
50 |
} |
|
301 |
continue; |
||
302 |
} |
||
303 |
} |
||
304 |
2725 |
*p++ = c; |
|
305 |
✗✓ | 2725 |
if (p == ebuf) { |
306 |
yyerror("too long line"); |
||
307 |
p = buf; |
||
308 |
} |
||
309 |
escape = 0; |
||
310 |
2725 |
} |
|
311 |
|||
312 |
eow: |
||
313 |
490 |
*p = 0; |
|
314 |
✓✓ | 490 |
if (c != EOF) |
315 |
485 |
ungetc(c, yyfp); |
|
316 |
✓✓ | 490 |
if (p == buf) { |
317 |
/* |
||
318 |
* There could be a number of reasons for empty buffer, |
||
319 |
* and we handle all of them here, to avoid cluttering |
||
320 |
* the main loop. |
||
321 |
*/ |
||
322 |
✓✗ | 40 |
if (c == EOF) |
323 |
goto eof; |
||
324 |
✓✓ | 40 |
else if (qpos == -1) /* accept, e.g., empty args: cmd foo args "" */ |
325 |
30 |
goto repeat; |
|
326 |
} |
||
327 |
✓✓ | 460 |
if (!nonkw) { |
328 |
✓✓ | 5500 |
for (i = 0; i < sizeof(keywords) / sizeof(keywords[0]); i++) { |
329 |
✓✓ | 2535 |
if (strcmp(buf, keywords[i].word) == 0) |
330 |
175 |
return keywords[i].token; |
|
331 |
} |
||
332 |
} |
||
333 |
✗✓ | 285 |
if ((str = strdup(buf)) == NULL) |
334 |
err(1, "strdup"); |
||
335 |
285 |
yylval.str = str; |
|
336 |
285 |
return TSTRING; |
|
337 |
|||
338 |
eof: |
||
339 |
✓✗✗✓ ✗✗ |
20 |
if (ferror(yyfp)) |
340 |
yyerror("input error reading config"); |
||
341 |
10 |
return 0; |
|
342 |
575 |
} |
Generated by: GCOVR (Version 3.3) |