2 7df505fb 2025-01-27 martijn * Copyright (c) 2016 Martijn van Duren <martijn@openbsd.org>
3 09a12e37 2016-09-08 martijn * Copyright (c) 2015 Ted Unangst <tedu@openbsd.org>
5 09a12e37 2016-09-08 martijn * Permission to use, copy, modify, and distribute this software for any
6 09a12e37 2016-09-08 martijn * purpose with or without fee is hereby granted, provided that the above
7 09a12e37 2016-09-08 martijn * copyright notice and this permission notice appear in all copies.
9 09a12e37 2016-09-08 martijn * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 09a12e37 2016-09-08 martijn * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 09a12e37 2016-09-08 martijn * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 09a12e37 2016-09-08 martijn * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 09a12e37 2016-09-08 martijn * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 09a12e37 2016-09-08 martijn * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 09a12e37 2016-09-08 martijn * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19 09a12e37 2016-09-08 martijn #include <sys/stat.h>
20 09a12e37 2016-09-08 martijn #include <sys/types.h>
22 09a12e37 2016-09-08 martijn #include <ctype.h>
23 09a12e37 2016-09-08 martijn #include <errno.h>
24 09a12e37 2016-09-08 martijn #include <libgen.h>
25 09a12e37 2016-09-08 martijn #include <unistd.h>
26 09a12e37 2016-09-08 martijn #include <stdint.h>
27 09a12e37 2016-09-08 martijn #include <stdarg.h>
28 09a12e37 2016-09-08 martijn #include <stdio.h>
29 09a12e37 2016-09-08 martijn #include <string.h>
30 09a12e37 2016-09-08 martijn #include <err.h>
32 09a12e37 2016-09-08 martijn #include "vias.h"
34 09a12e37 2016-09-08 martijn typedef struct {
37 09a12e37 2016-09-08 martijn int action;
38 09a12e37 2016-09-08 martijn int options;
39 09a12e37 2016-09-08 martijn const char **files;
41 09a12e37 2016-09-08 martijn const char *str;
43 09a12e37 2016-09-08 martijn int lineno;
44 09a12e37 2016-09-08 martijn int colno;
45 09a12e37 2016-09-08 martijn } yystype;
46 09a12e37 2016-09-08 martijn #define YYSTYPE yystype
48 09a12e37 2016-09-08 martijn FILE *yyfp;
50 09a12e37 2016-09-08 martijn struct rule **rules;
51 09a12e37 2016-09-08 martijn int nrules, maxrules;
52 09a12e37 2016-09-08 martijn int parse_errors = 0;
53 09a12e37 2016-09-08 martijn int obsolete_warned = 0;
55 09a12e37 2016-09-08 martijn void yyerror(const char *, ...);
56 09a12e37 2016-09-08 martijn int yylex(void);
57 09a12e37 2016-09-08 martijn int yyparse(void);
61 a689c182 2016-09-22 martijn %token TPERMIT TDENY TAS TEDIT
62 09a12e37 2016-09-08 martijn %token TNOPASS TPERSIST
63 09a12e37 2016-09-08 martijn %token TSTRING
67 09a12e37 2016-09-08 martijn grammar: /* empty */
68 09a12e37 2016-09-08 martijn | grammar '\n'
69 09a12e37 2016-09-08 martijn | grammar rule '\n'
70 09a12e37 2016-09-08 martijn | error '\n'
73 09a12e37 2016-09-08 martijn rule: action ident target edit {
74 09a12e37 2016-09-08 martijn struct rule *r;
75 09a12e37 2016-09-08 martijn if ((r = calloc(1, sizeof(*r))) == NULL)
76 09a12e37 2016-09-08 martijn err(1, NULL);
77 09a12e37 2016-09-08 martijn r->action = $1.action;
78 09a12e37 2016-09-08 martijn r->options = $1.options;
79 09a12e37 2016-09-08 martijn r->ident = $2.str;
80 09a12e37 2016-09-08 martijn r->target = $3.str;
81 09a12e37 2016-09-08 martijn r->files = $4.files;
82 09a12e37 2016-09-08 martijn if (nrules == maxrules) {
83 09a12e37 2016-09-08 martijn if (maxrules == 0)
84 09a12e37 2016-09-08 martijn maxrules = 63;
86 09a12e37 2016-09-08 martijn maxrules *= 2;
87 09a12e37 2016-09-08 martijn if ((rules = reallocarray(rules, maxrules,
88 09a12e37 2016-09-08 martijn sizeof(*rules))) == NULL)
89 09a12e37 2016-09-08 martijn err(1, NULL);
91 09a12e37 2016-09-08 martijn rules[nrules++] = r;
94 09a12e37 2016-09-08 martijn action: TPERMIT options {
95 09a12e37 2016-09-08 martijn $$.action = PERMIT;
96 09a12e37 2016-09-08 martijn $$.options = $2.options;
97 09a12e37 2016-09-08 martijn } | TDENY {
98 09a12e37 2016-09-08 martijn $$.action = DENY;
99 09a12e37 2016-09-08 martijn $$.options = 0;
102 09a12e37 2016-09-08 martijn options: /* none */ {
103 09a12e37 2016-09-08 martijn $$.options = 0;
104 09a12e37 2016-09-08 martijn } | options option {
105 09a12e37 2016-09-08 martijn $$.options = $1.options | $2.options;
106 09a12e37 2016-09-08 martijn if (($$.options & (NOPASS|PERSIST)) == (NOPASS|PERSIST)) {
107 09a12e37 2016-09-08 martijn yyerror("can't combine nopass and persist");
108 09a12e37 2016-09-08 martijn YYERROR;
111 09a12e37 2016-09-08 martijn option: TNOPASS {
112 09a12e37 2016-09-08 martijn $$.options = NOPASS;
113 09a12e37 2016-09-08 martijn } | TPERSIST {
114 09a12e37 2016-09-08 martijn $$.options = PERSIST;
117 09a12e37 2016-09-08 martijn ident: TSTRING {
118 09a12e37 2016-09-08 martijn $$.str = $1.str;
121 09a12e37 2016-09-08 martijn target: /* optional */ {
122 09a12e37 2016-09-08 martijn $$.str = NULL;
123 09a12e37 2016-09-08 martijn } | TAS TSTRING {
124 09a12e37 2016-09-08 martijn $$.str = $2.str;
127 09a12e37 2016-09-08 martijn edit: /* none */ {
128 09a12e37 2016-09-08 martijn $$.files = NULL;
129 09a12e37 2016-09-08 martijn } | TEDIT files {
130 09a12e37 2016-09-08 martijn if (arraylen($2.files))
131 09a12e37 2016-09-08 martijn $$.files = $2.files;
133 09a12e37 2016-09-08 martijn free($2.files);
134 09a12e37 2016-09-08 martijn $$.files = NULL;
138 09a12e37 2016-09-08 martijn files: /* empty */ {
139 09a12e37 2016-09-08 martijn if (($$.files = calloc(1, sizeof(char *))) == NULL)
140 09a12e37 2016-09-08 martijn err(1, NULL);
141 09a12e37 2016-09-08 martijn } | files TSTRING {
142 09a12e37 2016-09-08 martijn int nargs = arraylen($1.files);
143 09a12e37 2016-09-08 martijn char *str;
144 09a12e37 2016-09-08 martijn int strl, skip = 0;
145 09a12e37 2016-09-08 martijn if ($2.str[0] != '/') {
146 09a12e37 2016-09-08 martijn warnx("File %s can't be relative", $2.str);
147 09a12e37 2016-09-08 martijn skip = 1;
149 09a12e37 2016-09-08 martijn if ((str = realpath($2.str, NULL)) == NULL &&
150 09a12e37 2016-09-08 martijn errno != ENOENT) {
151 09a12e37 2016-09-08 martijn warn("Problem verifying %s", $2.str);
152 09a12e37 2016-09-08 martijn skip = 1;
154 09a12e37 2016-09-08 martijn if (str != NULL && strcmp($2.str, str)) {
155 09a12e37 2016-09-08 martijn strl = strlen(str);
156 09a12e37 2016-09-08 martijn if ((str = reallocarray(str, strl + 2,
157 09a12e37 2016-09-08 martijn sizeof(*str))) == NULL)
158 09a12e37 2016-09-08 martijn err(1, NULL);
159 09a12e37 2016-09-08 martijn str[strl] = '/';
160 09a12e37 2016-09-08 martijn str[strl+1] = '\0';
161 09a12e37 2016-09-08 martijn if (strcmp($2.str, str)) {
162 09a12e37 2016-09-08 martijn warnx("file %s needs to be a resolved "
163 09a12e37 2016-09-08 martijn "path", $2.str);
164 09a12e37 2016-09-08 martijn skip = 1;
167 09a12e37 2016-09-08 martijn free(str);
168 09a12e37 2016-09-08 martijn if (!skip) {
169 09a12e37 2016-09-08 martijn if (($$.files = reallocarray($1.files, nargs + 2,
170 09a12e37 2016-09-08 martijn sizeof(char *))) == NULL)
171 09a12e37 2016-09-08 martijn err(1, NULL);
172 09a12e37 2016-09-08 martijn $$.files[nargs] = $2.str;
173 09a12e37 2016-09-08 martijn $$.files[nargs + 1] = NULL;
180 09a12e37 2016-09-08 martijn yyerror(const char *fmt, ...)
182 09a12e37 2016-09-08 martijn va_list va;
184 09a12e37 2016-09-08 martijn fprintf(stderr, "vias: ");
185 09a12e37 2016-09-08 martijn va_start(va, fmt);
186 09a12e37 2016-09-08 martijn vfprintf(stderr, fmt, va);
187 09a12e37 2016-09-08 martijn va_end(va);
188 09a12e37 2016-09-08 martijn fprintf(stderr, " at line %d\n", yylval.lineno + 1);
189 09a12e37 2016-09-08 martijn parse_errors++;
192 09a12e37 2016-09-08 martijn struct keyword {
193 09a12e37 2016-09-08 martijn const char *word;
194 09a12e37 2016-09-08 martijn int token;
195 09a12e37 2016-09-08 martijn } keywords[] = {
196 09a12e37 2016-09-08 martijn { "deny", TDENY },
197 09a12e37 2016-09-08 martijn { "permit", TPERMIT },
198 09a12e37 2016-09-08 martijn { "as", TAS },
199 09a12e37 2016-09-08 martijn { "nopass", TNOPASS },
200 09a12e37 2016-09-08 martijn { "persist", TPERSIST },
201 09a12e37 2016-09-08 martijn { "edit", TEDIT },
205 09a12e37 2016-09-08 martijn yylex(void)
207 09a12e37 2016-09-08 martijn char buf[1024], *ebuf, *p, *str;
208 b6e88a9a 2020-12-18 martijn int c, quotes = 0, escape = 0, qpos = -1, nonkw = 0;
209 b6e88a9a 2020-12-18 martijn size_t i;
211 09a12e37 2016-09-08 martijn p = buf;
212 09a12e37 2016-09-08 martijn ebuf = buf + sizeof(buf);
215 09a12e37 2016-09-08 martijn /* skip whitespace first */
216 09a12e37 2016-09-08 martijn for (c = getc(yyfp); c == ' ' || c == '\t'; c = getc(yyfp))
217 09a12e37 2016-09-08 martijn yylval.colno++;
219 09a12e37 2016-09-08 martijn /* check for special one-character constructions */
220 09a12e37 2016-09-08 martijn switch (c) {
221 09a12e37 2016-09-08 martijn case '\n':
222 09a12e37 2016-09-08 martijn yylval.colno = 0;
223 09a12e37 2016-09-08 martijn yylval.lineno++;
224 09a12e37 2016-09-08 martijn /* FALLTHROUGH */
225 09a12e37 2016-09-08 martijn case '{':
226 09a12e37 2016-09-08 martijn case '}':
227 09a12e37 2016-09-08 martijn return c;
228 09a12e37 2016-09-08 martijn case '#':
229 09a12e37 2016-09-08 martijn /* skip comments; NUL is allowed; no continuation */
230 09a12e37 2016-09-08 martijn while ((c = getc(yyfp)) != '\n')
231 09a12e37 2016-09-08 martijn if (c == EOF)
232 09a12e37 2016-09-08 martijn goto eof;
233 09a12e37 2016-09-08 martijn yylval.colno = 0;
234 09a12e37 2016-09-08 martijn yylval.lineno++;
235 09a12e37 2016-09-08 martijn return c;
236 09a12e37 2016-09-08 martijn case EOF:
237 09a12e37 2016-09-08 martijn goto eof;
240 09a12e37 2016-09-08 martijn /* parsing next word */
241 09a12e37 2016-09-08 martijn for (;; c = getc(yyfp), yylval.colno++) {
242 09a12e37 2016-09-08 martijn switch (c) {
243 09a12e37 2016-09-08 martijn case '\0':
244 09a12e37 2016-09-08 martijn yyerror("unallowed character NUL in column %d",
245 09a12e37 2016-09-08 martijn yylval.colno + 1);
246 09a12e37 2016-09-08 martijn escape = 0;
247 09a12e37 2016-09-08 martijn continue;
248 09a12e37 2016-09-08 martijn case '\\':
249 09a12e37 2016-09-08 martijn escape = !escape;
250 09a12e37 2016-09-08 martijn if (escape)
251 09a12e37 2016-09-08 martijn continue;
253 09a12e37 2016-09-08 martijn case '\n':
254 09a12e37 2016-09-08 martijn if (quotes)
255 09a12e37 2016-09-08 martijn yyerror("unterminated quotes in column %d",
256 09a12e37 2016-09-08 martijn qpos + 1);
257 09a12e37 2016-09-08 martijn if (escape) {
258 09a12e37 2016-09-08 martijn nonkw = 1;
259 09a12e37 2016-09-08 martijn escape = 0;
260 09a12e37 2016-09-08 martijn yylval.colno = 0;
261 09a12e37 2016-09-08 martijn yylval.lineno++;
262 09a12e37 2016-09-08 martijn continue;
264 09a12e37 2016-09-08 martijn goto eow;
265 09a12e37 2016-09-08 martijn case EOF:
266 09a12e37 2016-09-08 martijn if (escape)
267 09a12e37 2016-09-08 martijn yyerror("unterminated escape in column %d",
268 09a12e37 2016-09-08 martijn yylval.colno);
269 09a12e37 2016-09-08 martijn if (quotes)
270 09a12e37 2016-09-08 martijn yyerror("unterminated quotes in column %d",
271 09a12e37 2016-09-08 martijn qpos + 1);
272 09a12e37 2016-09-08 martijn goto eow;
273 09a12e37 2016-09-08 martijn /* FALLTHROUGH */
274 09a12e37 2016-09-08 martijn case '{':
275 09a12e37 2016-09-08 martijn case '}':
276 09a12e37 2016-09-08 martijn case '#':
277 09a12e37 2016-09-08 martijn case ' ':
278 09a12e37 2016-09-08 martijn case '\t':
279 09a12e37 2016-09-08 martijn if (!escape && !quotes)
280 09a12e37 2016-09-08 martijn goto eow;
282 09a12e37 2016-09-08 martijn case '"':
283 09a12e37 2016-09-08 martijn if (!escape) {
284 09a12e37 2016-09-08 martijn quotes = !quotes;
285 09a12e37 2016-09-08 martijn if (quotes) {
286 09a12e37 2016-09-08 martijn nonkw = 1;
287 09a12e37 2016-09-08 martijn qpos = yylval.colno;
289 09a12e37 2016-09-08 martijn continue;
292 09a12e37 2016-09-08 martijn *p++ = c;
293 09a12e37 2016-09-08 martijn if (p == ebuf) {
294 09a12e37 2016-09-08 martijn yyerror("too long line");
295 09a12e37 2016-09-08 martijn p = buf;
297 09a12e37 2016-09-08 martijn escape = 0;
302 09a12e37 2016-09-08 martijn if (c != EOF)
303 09a12e37 2016-09-08 martijn ungetc(c, yyfp);
304 09a12e37 2016-09-08 martijn if (p == buf) {
306 09a12e37 2016-09-08 martijn * There could be a number of reasons for empty buffer,
307 09a12e37 2016-09-08 martijn * and we handle all of them here, to avoid cluttering
308 09a12e37 2016-09-08 martijn * the main loop.
310 09a12e37 2016-09-08 martijn if (c == EOF)
311 09a12e37 2016-09-08 martijn goto eof;
312 09a12e37 2016-09-08 martijn else if (qpos == -1) /* accept, e.g., empty args: cmd foo args "" */
313 09a12e37 2016-09-08 martijn goto repeat;
315 09a12e37 2016-09-08 martijn if (!nonkw) {
316 09a12e37 2016-09-08 martijn for (i = 0; i < sizeof(keywords) / sizeof(keywords[0]); i++) {
317 09a12e37 2016-09-08 martijn if (strcmp(buf, keywords[i].word) == 0)
318 09a12e37 2016-09-08 martijn return keywords[i].token;
321 09a12e37 2016-09-08 martijn if ((str = strdup(buf)) == NULL)
322 09a12e37 2016-09-08 martijn err(1, "strdup");
323 09a12e37 2016-09-08 martijn yylval.str = str;
324 09a12e37 2016-09-08 martijn return TSTRING;
327 09a12e37 2016-09-08 martijn if (ferror(yyfp))
328 09a12e37 2016-09-08 martijn yyerror("input error reading config");
329 09a12e37 2016-09-08 martijn return 0;