Commit | Line | Data |
---|---|---|
9da480be MW |
1 | /* -*-c-*- |
2 | * | |
3 | * Policy parsing and implementation | |
4 | * | |
5 | * (c) 2012 Straylight/Edgeware | |
6 | */ | |
7 | ||
8 | /*----- Licensing notice --------------------------------------------------* | |
9 | * | |
10 | * This file is part of Yet Another Ident Daemon (YAID). | |
11 | * | |
12 | * YAID is free software; you can redistribute it and/or modify | |
13 | * it under the terms of the GNU General Public License as published by | |
14 | * the Free Software Foundation; either version 2 of the License, or | |
15 | * (at your option) any later version. | |
16 | * | |
17 | * YAID is distributed in the hope that it will be useful, | |
18 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
20 | * GNU General Public License for more details. | |
21 | * | |
22 | * You should have received a copy of the GNU General Public License | |
23 | * along with YAID; if not, write to the Free Software Foundation, | |
24 | * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | |
25 | */ | |
26 | ||
27 | /*----- Header files ------------------------------------------------------*/ | |
28 | ||
29 | #include "yaid.h" | |
30 | ||
9da480be MW |
31 | /*----- Main code ---------------------------------------------------------*/ |
32 | ||
33 | /* syntax: addrpat portpat addrpar portpat policy | |
34 | * | |
35 | * local address/port first, then remote | |
36 | * addrpat ::= addr [/ len] | |
37 | * portpat ::= num | num - num | * | |
38 | * policy ::= user policy* | token | name | deny | hide | | |
39 | */ | |
40 | ||
41 | void init_policy(struct policy *p) { p->act.act = A_LIMIT; } | |
42 | ||
43 | static void free_action(struct action *a) | |
44 | { | |
45 | switch (a->act) { | |
46 | case A_LIE: | |
47 | xfree(a->u.lie); | |
48 | break; | |
49 | } | |
50 | a->act = A_LIMIT; | |
51 | } | |
52 | ||
53 | void free_policy(struct policy *p) | |
54 | { free_action(&p->act); } | |
55 | ||
bf4d9761 | 56 | static void print_addrpat(const struct addrops *ao, const struct addrpat *ap) |
9da480be MW |
57 | { |
58 | char buf[ADDRLEN]; | |
59 | ||
bf4d9761 MW |
60 | if (ap->len == 0) |
61 | putchar('*'); | |
62 | else { | |
63 | printf("%s/%u", | |
64 | inet_ntop(ao->af, &ap->addr, buf, sizeof(buf)), | |
65 | ap->len); | |
66 | } | |
9da480be MW |
67 | } |
68 | ||
69 | static void print_portpat(const struct portpat *pp) | |
70 | { | |
71 | if (pp->lo == 0 && pp->hi == 65535) putchar('*'); | |
72 | else if (pp->lo == pp->hi) printf("%u", pp->lo); | |
73 | else printf("%u-%u", pp->lo, pp->hi); | |
74 | } | |
75 | ||
bf4d9761 MW |
76 | static void print_sockpat(const struct addrops *ao, const struct sockpat *sp) |
77 | { print_addrpat(ao, &sp->addr); putchar(' '); print_portpat(&sp->port); } | |
9da480be MW |
78 | |
79 | static const char *const acttab[] = { | |
80 | #define DEFACT(tag, name) name, | |
81 | ACTIONS(DEFACT) | |
82 | #undef DEFACT | |
83 | 0 | |
84 | }; | |
85 | ||
86 | static void print_action(const struct action *act) | |
87 | { | |
88 | assert(act->act < A_LIMIT); | |
89 | printf("%s", acttab[act->act]); | |
90 | switch (act->act) { | |
91 | case A_USER: { | |
92 | int i; | |
93 | unsigned m; | |
94 | for (i = 0, m = 1; i < A_LIMIT; i++, m <<= 1) | |
95 | if (act->u.user & m) printf(" %s", acttab[i]); | |
96 | } break; | |
97 | case A_LIE: | |
98 | printf(" %s", act->u.lie); | |
99 | break; | |
100 | } | |
101 | } | |
102 | ||
103 | void print_policy(const struct policy *p) | |
104 | { | |
bf4d9761 MW |
105 | print_sockpat(p->ao, &p->sp[L]); putchar(' '); |
106 | print_sockpat(p->ao, &p->sp[R]); putchar(' '); | |
9da480be MW |
107 | print_action(&p->act); putchar('\n'); |
108 | } | |
109 | ||
9da480be MW |
110 | static int match_portpat(const struct portpat *pp, unsigned port) |
111 | { return (pp->lo <= port && port <= pp->hi); } | |
112 | ||
bf4d9761 MW |
113 | static int match_sockpat(const struct addrops *ao, |
114 | const struct sockpat *sp, const struct socket *s) | |
9da480be | 115 | { |
bf4d9761 | 116 | return (ao->match_addrpat(&sp->addr, &s->addr) && |
9da480be MW |
117 | match_portpat(&sp->port, s->port)); |
118 | } | |
119 | ||
120 | int match_policy(const struct policy *p, const struct query *q) | |
121 | { | |
bf4d9761 MW |
122 | return ((!p->ao || p->ao == q->ao) && |
123 | match_sockpat(q->ao, &p->sp[L], &q->s[L]) && | |
124 | match_sockpat(q->ao, &p->sp[R], &q->s[R])); | |
9da480be MW |
125 | } |
126 | ||
127 | static void nextline(FILE *fp) | |
128 | { | |
129 | for (;;) { | |
130 | int ch = getc(fp); | |
131 | if (ch == '\n' || ch == EOF) break; | |
132 | } | |
133 | } | |
134 | ||
135 | static int scan(FILE *fp, char *buf, size_t sz) | |
136 | { | |
137 | int ch; | |
138 | ||
139 | skip_ws: | |
140 | ch = getc(fp); | |
141 | switch (ch) { | |
142 | case '\n': | |
143 | newline: | |
144 | ungetc(ch, fp); | |
145 | return (T_EOL); | |
146 | case EOF: | |
147 | eof: | |
148 | return (ferror(fp) ? T_ERROR : T_EOF); | |
149 | case '#': | |
150 | for (;;) { | |
151 | ch = getc(fp); | |
152 | if (ch == '\n') goto newline; | |
153 | else if (ch == EOF) goto eof; | |
154 | } | |
155 | default: | |
156 | if (isspace(ch)) goto skip_ws; | |
157 | break; | |
158 | } | |
159 | ||
160 | for (;;) { | |
161 | if (sz) { *buf++ = ch; sz--; } | |
162 | ch = getc(fp); | |
163 | switch (ch) { | |
164 | case '\n': | |
165 | ungetc(ch, fp); | |
166 | goto done; | |
167 | case EOF: | |
168 | goto done; | |
169 | default: | |
170 | if (isspace(ch)) goto done; | |
171 | break; | |
172 | } | |
173 | } | |
174 | ||
175 | done: | |
176 | if (!sz) | |
177 | return (T_ERROR); | |
178 | else { | |
179 | *buf++ = 0; sz--; | |
180 | return (T_OK); | |
181 | } | |
182 | } | |
183 | ||
184 | static int parse_actname(FILE *fp, unsigned *act) | |
185 | { | |
186 | char buf[32]; | |
187 | int t; | |
188 | const char *const *p; | |
189 | ||
190 | if ((t = scan(fp, buf, sizeof(buf))) != 0) return (t); | |
191 | for (p = acttab; *p; p++) | |
192 | if (strcmp(buf, *p) == 0) { *act = p - acttab; return (0); } | |
193 | return (T_ERROR); | |
194 | } | |
195 | ||
196 | static int parse_action(FILE *fp, struct action *act) | |
197 | { | |
198 | char buf[32]; | |
199 | int t; | |
200 | unsigned a; | |
201 | unsigned long m; | |
202 | ||
203 | if ((t = parse_actname(fp, &a)) != 0) return (t); | |
204 | switch (a) { | |
205 | case A_USER: | |
206 | m = 0; | |
207 | for (;;) { | |
208 | if ((t = parse_actname(fp, &a)) != 0) break; | |
209 | m |= (1 << a); | |
210 | } | |
211 | if (t != T_EOL && t != T_EOF) return (t); | |
212 | act->act = A_USER; | |
213 | act->u.user = m; | |
214 | break; | |
215 | case A_TOKEN: | |
216 | case A_NAME: | |
217 | case A_DENY: | |
218 | case A_HIDE: | |
219 | act->act = a; | |
220 | break; | |
221 | case A_LIE: | |
222 | if ((t = scan(fp, buf, sizeof(buf))) != 0) return (t); | |
223 | act->act = a; | |
224 | act->u.lie = xstrdup(buf); | |
225 | break; | |
226 | } | |
227 | t = scan(fp, buf, sizeof(buf)); | |
228 | if (t != T_EOF && t != T_EOL) { | |
229 | free_action(act); | |
230 | return (T_ERROR); | |
231 | } | |
232 | return (0); | |
233 | } | |
234 | ||
bf4d9761 MW |
235 | static int parse_sockpat(FILE *fp, const struct addrops **aop, |
236 | struct sockpat *sp) | |
9da480be MW |
237 | { |
238 | char buf[64]; | |
239 | int t; | |
bf4d9761 | 240 | const struct addrops *ao; |
9da480be MW |
241 | long n; |
242 | char *delim; | |
243 | ||
244 | if ((t = scan(fp, buf, sizeof(buf))) != 0) return (t); | |
245 | if (strcmp(buf, "*") == 0) | |
246 | sp->addr.len = 0; | |
247 | else { | |
bf4d9761 MW |
248 | if (strchr(buf, ':')) |
249 | ao = &addroptab[ADDR_IPV6]; | |
250 | else | |
251 | ao = &addroptab[ADDR_IPV4]; | |
252 | if (!*aop) *aop = ao; | |
253 | else if (*aop != ao) return (T_ERROR); | |
9da480be MW |
254 | delim = strchr(buf, '/'); |
255 | if (delim) *delim++ = 0; | |
bf4d9761 MW |
256 | if (!inet_pton(ao->af, buf, &sp->addr.addr)) return (T_ERROR); |
257 | if (!delim) n = ao->len; | |
9da480be | 258 | else n = strtol(delim, 0, 10); |
bf4d9761 | 259 | if (n < 0 || n > ao->len) return (T_ERROR); |
9da480be MW |
260 | sp->addr.len = n; |
261 | } | |
262 | ||
263 | if ((t = scan(fp, buf, sizeof(buf))) != 0) return (T_ERROR); | |
264 | if (strcmp(buf, "*") == 0) { | |
265 | sp->port.lo = 0; | |
266 | sp->port.hi = 65535; | |
267 | } else { | |
268 | delim = strchr(buf, '-'); | |
269 | if (delim) *delim++ = 0; | |
270 | n = strtol(buf, 0, 0); | |
271 | if (n < 0 || n > 65535) return (T_ERROR); | |
272 | sp->port.lo = n; | |
273 | if (!delim) | |
274 | sp->port.hi = n; | |
275 | else { | |
276 | n = strtol(delim, 0, 0); | |
277 | if (n < 0 || n > 65535) return (T_ERROR); | |
278 | sp->port.hi = n; | |
279 | } | |
280 | } | |
281 | return (0); | |
282 | } | |
283 | ||
284 | int parse_policy(FILE *fp, struct policy *p) | |
285 | { | |
286 | int t; | |
287 | ||
bf4d9761 | 288 | p->ao = 0; |
9da480be MW |
289 | free_policy(p); |
290 | ||
bf4d9761 MW |
291 | if ((t = parse_sockpat(fp, &p->ao, &p->sp[L])) != 0) goto fail; |
292 | if ((t = parse_sockpat(fp, &p->ao, &p->sp[R])) != 0) goto err; | |
9da480be MW |
293 | if ((t = parse_action(fp, &p->act)) != 0) goto err; |
294 | return (0); | |
295 | ||
296 | err: | |
297 | t = T_ERROR; | |
298 | fail: | |
299 | free_policy(p); | |
300 | return (t); | |
301 | } | |
302 | ||
303 | int open_policy_file(struct policy_file *pf, const char *name, | |
304 | const char *what, const struct query *q) | |
305 | { | |
306 | if ((pf->fp = fopen(name, "r")) == 0) { | |
307 | logmsg(q, LOG_ERR, "failed to open %s `%s': %s", | |
308 | what, name, strerror(errno)); | |
309 | return (-1); | |
310 | } | |
311 | ||
312 | pf->name = name; | |
313 | pf->what = what; | |
314 | pf->q = q; | |
315 | pf->err = 0; | |
316 | pf->lno = 0; | |
317 | init_policy(&pf->p); | |
318 | return (0); | |
319 | } | |
320 | ||
321 | int read_policy_file(struct policy_file *pf) | |
322 | { | |
323 | int t; | |
324 | ||
325 | for (;;) { | |
326 | pf->lno++; | |
327 | t = parse_policy(pf->fp, &pf->p); | |
328 | switch (t) { | |
329 | case T_OK: | |
330 | nextline(pf->fp); | |
331 | return (0); | |
332 | case T_ERROR: | |
333 | logmsg(pf->q, LOG_ERR, "%s:%d: parse error in %s", | |
334 | pf->name, pf->lno, pf->what); | |
335 | pf->err = 1; | |
336 | break; | |
337 | case T_EOF: | |
338 | if (ferror(pf->fp)) { | |
339 | logmsg(pf->q, LOG_ERR, "failed to read %s `%s': %s", | |
340 | pf->what, pf->name, strerror(errno)); | |
341 | } | |
342 | return (-1); | |
343 | case T_EOL: | |
344 | nextline(pf->fp); | |
345 | break; | |
346 | default: | |
347 | abort(); | |
348 | } | |
349 | } | |
350 | } | |
351 | ||
352 | void close_policy_file(struct policy_file *pf) | |
353 | { | |
354 | fclose(pf->fp); | |
355 | free_policy(&pf->p); | |
356 | } | |
357 | ||
358 | int load_policy_file(const char *file, policy_v *pv) | |
359 | { | |
360 | struct policy_file pf; | |
361 | policy_v v = DA_INIT; | |
362 | ||
363 | if (open_policy_file(&pf, file, "policy file", 0)) | |
364 | return (-1); | |
365 | while (!read_policy_file(&pf)) { | |
366 | DA_PUSH(&v, pf.p); | |
367 | init_policy(&pf.p); | |
368 | } | |
369 | close_policy_file(&pf); | |
370 | if (!pf.err) { | |
371 | DA_DESTROY(pv); | |
372 | *pv = v; | |
373 | return (0); | |
374 | } else { | |
375 | DA_DESTROY(&v); | |
376 | return (-1); | |
377 | } | |
378 | } | |
379 | ||
380 | /*----- That's all, folks -------------------------------------------------*/ |