]> git.armaanb.net Git - opendoas.git/blob - parse.y
In the config file allow line continuations with backslashes.
[opendoas.git] / parse.y
1 /* $OpenBSD: parse.y,v 1.4 2015/07/16 23:02:56 nicm 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 **envlist;
36                 };
37                 const char *str;
38         };
39 } yystype;
40 #define YYSTYPE yystype
41
42 FILE *yyfp;
43
44 struct rule **rules;
45 int nrules, maxrules;
46
47 void yyerror(const char *, ...);
48 int yylex(void);
49 int yyparse(void);
50
51 %}
52
53 %token TPERMIT TDENY TAS TCMD
54 %token TNOPASS TKEEPENV
55 %token TSTRING
56
57 %%
58
59 grammar:        /* empty */
60                 | grammar '\n'
61                 | grammar rule '\n'
62                 ;
63
64 rule:           action ident target cmd {
65                         struct rule *r;
66                         r = calloc(1, sizeof(*r));
67                         if (!r)
68                                 errx(1, "can't allocate rule");
69                         r->action = $1.action;
70                         r->options = $1.options;
71                         r->envlist = $1.envlist;
72                         r->ident = $2.str;
73                         r->target = $3.str;
74                         r->cmd = $4.str;
75                         if (nrules == maxrules) {
76                                 if (maxrules == 0)
77                                         maxrules = 63;
78                                 else
79                                         maxrules *= 2;
80                                 if (!(rules = reallocarray(rules, maxrules, sizeof(*rules))))
81                                         errx(1, "can't allocate rules");
82                         }
83                         rules[nrules++] = r;
84                 } ;
85
86 action:         TPERMIT options {
87                         $$.action = PERMIT;
88                         $$.options = $2.options;
89                         $$.envlist = $2.envlist;
90                 } | TDENY {
91                         $$.action = DENY;
92                 } ;
93
94 options:        /* none */
95                 | options option {
96                         $$.options = $1.options | $2.options;
97                         $$.envlist = $1.envlist;
98                         if ($2.envlist) {
99                                 if ($$.envlist)
100                                         errx(1, "can't have two keepenv sections");
101                                 else
102                                         $$.envlist = $2.envlist;
103                         }
104                 } ;
105 option:         TNOPASS {
106                         $$.options = NOPASS;
107                 } | TKEEPENV {
108                         $$.options = KEEPENV;
109                 } | TKEEPENV '{' envlist '}' {
110                         $$.options = KEEPENV;
111                         $$.envlist = $3.envlist;
112                 } ;
113
114 envlist:        /* empty */ {
115                         if (!($$.envlist = calloc(1, sizeof(char *))))
116                                 errx(1, "can't allocate envlist");
117                 } | envlist TSTRING {
118                         int nenv = arraylen($1.envlist);
119                         if (!($$.envlist = reallocarray($1.envlist, nenv + 2, sizeof(char *))))
120                                 errx(1, "can't allocate envlist");
121                         $$.envlist[nenv] = $2.str;
122                         $$.envlist[nenv + 1] = NULL;
123                 }
124
125
126 ident:          TSTRING {
127                         $$.str = $1.str;
128                 } ;
129
130 target:         /* optional */ {
131                         $$.str = NULL;
132                 } | TAS TSTRING {
133                         $$.str = $2.str;
134                 } ;
135
136 cmd:            /* optional */ {
137                         $$.str = NULL;
138                 } | TCMD TSTRING {
139                         $$.str = $2.str;
140                 } ;
141
142 %%
143
144 void
145 yyerror(const char *fmt, ...)
146 {
147         va_list va;
148
149         va_start(va, fmt);
150         verrx(1, fmt, va);
151 }
152
153 struct keyword {
154         const char *word;
155         int token;
156 } keywords[] = {
157         { "deny", TDENY },
158         { "permit", TPERMIT },
159         { "as", TAS },
160         { "cmd", TCMD },
161         { "nopass", TNOPASS },
162         { "keepenv", TKEEPENV },
163 };
164
165 int
166 yylex(void)
167 {
168         char buf[1024], *ebuf, *p, *str;
169         int i, c, next;
170
171         p = buf;
172         ebuf = buf + sizeof(buf);
173 repeat:
174         c = getc(yyfp);
175         switch (c) {
176                 case ' ':
177                 case '\t':
178                         goto repeat; /* skip spaces */
179                 case '\\':
180                         next = getc(yyfp);
181                         if (next == '\n')
182                                 goto repeat;
183                         else 
184                                 c = next;
185                 case '\n':
186                 case '{':
187                 case '}':
188                         return c;
189                 case '#':
190                         while ((c = getc(yyfp)) != '\n' && c != EOF)
191                                 ; /* skip comments */
192                         if (c == EOF)
193                                 return 0;
194                         return c;
195                 case EOF:
196                         return 0;
197         }
198         while (1) {
199                 switch (c) {
200                 case '\n':
201                 case '{':
202                 case '}':
203                 case '#':
204                 case ' ':
205                 case '\t':
206                 case EOF:
207                         goto eow;
208                 }
209                 *p++ = c;
210                 if (p == ebuf)
211                         yyerror("too much stuff");
212                 c = getc(yyfp);
213         }
214 eow:
215         *p = 0;
216         if (c != EOF)
217                 ungetc(c, yyfp);
218         for (i = 0; i < sizeof(keywords) / sizeof(keywords[0]); i++) {
219                 if (strcmp(buf, keywords[i].word) == 0)
220                         return keywords[i].token;
221         }
222         if ((str = strdup(buf)) == NULL)
223                 err(1, "strdup");
224         yylval.str = str;
225         return TSTRING;
226 }