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 | ||
31 | /*----- Data structures ---------------------------------------------------*/ | |
32 | ||
33 | /*----- Static variables --------------------------------------------------*/ | |
34 | ||
35 | /*----- Main code ---------------------------------------------------------*/ | |
36 | ||
37 | /* syntax: addrpat portpat addrpar portpat policy | |
38 | * | |
39 | * local address/port first, then remote | |
40 | * addrpat ::= addr [/ len] | |
41 | * portpat ::= num | num - num | * | |
42 | * policy ::= user policy* | token | name | deny | hide | | |
43 | */ | |
44 | ||
45 | void init_policy(struct policy *p) { p->act.act = A_LIMIT; } | |
46 | ||
47 | static void free_action(struct action *a) | |
48 | { | |
49 | switch (a->act) { | |
50 | case A_LIE: | |
51 | xfree(a->u.lie); | |
52 | break; | |
53 | } | |
54 | a->act = A_LIMIT; | |
55 | } | |
56 | ||
57 | void free_policy(struct policy *p) | |
58 | { free_action(&p->act); } | |
59 | ||
60 | static void print_addrpat(int af, const struct addrpat *ap) | |
61 | { | |
62 | char buf[ADDRLEN]; | |
63 | ||
64 | if (ap->len == 0) putchar('*'); | |
65 | else printf("%s/%u", inet_ntop(af, &ap->addr, buf, sizeof(buf)), ap->len); | |
66 | } | |
67 | ||
68 | static void print_portpat(const struct portpat *pp) | |
69 | { | |
70 | if (pp->lo == 0 && pp->hi == 65535) putchar('*'); | |
71 | else if (pp->lo == pp->hi) printf("%u", pp->lo); | |
72 | else printf("%u-%u", pp->lo, pp->hi); | |
73 | } | |
74 | ||
75 | static void print_sockpat(int af, const struct sockpat *sp) | |
76 | { print_addrpat(af, &sp->addr); putchar(' '); print_portpat(&sp->port); } | |
77 | ||
78 | static const char *const acttab[] = { | |
79 | #define DEFACT(tag, name) name, | |
80 | ACTIONS(DEFACT) | |
81 | #undef DEFACT | |
82 | 0 | |
83 | }; | |
84 | ||
85 | static void print_action(const struct action *act) | |
86 | { | |
87 | assert(act->act < A_LIMIT); | |
88 | printf("%s", acttab[act->act]); | |
89 | switch (act->act) { | |
90 | case A_USER: { | |
91 | int i; | |
92 | unsigned m; | |
93 | for (i = 0, m = 1; i < A_LIMIT; i++, m <<= 1) | |
94 | if (act->u.user & m) printf(" %s", acttab[i]); | |
95 | } break; | |
96 | case A_LIE: | |
97 | printf(" %s", act->u.lie); | |
98 | break; | |
99 | } | |
100 | } | |
101 | ||
102 | void print_policy(const struct policy *p) | |
103 | { | |
104 | print_sockpat(p->af, &p->sp[L]); putchar(' '); | |
105 | print_sockpat(p->af, &p->sp[R]); putchar(' '); | |
106 | print_action(&p->act); putchar('\n'); | |
107 | } | |
108 | ||
109 | static int match_addrpat(int af, const struct addrpat *ap, | |
110 | const union addr *a) | |
111 | { | |
112 | if (!ap->len) | |
113 | return (1); | |
114 | switch (af) { | |
115 | case AF_INET: { | |
116 | unsigned mask = htonl((MASK32 << (32 - ap->len)) & MASK32); | |
117 | return (((ap->addr.ipv4.s_addr ^ a->ipv4.s_addr) & mask) == 0); | |
118 | } | |
119 | case AF_INET6: | |
120 | abort(); | |
121 | } | |
122 | return (0); | |
123 | } | |
124 | ||
125 | static int match_portpat(const struct portpat *pp, unsigned port) | |
126 | { return (pp->lo <= port && port <= pp->hi); } | |
127 | ||
128 | static int match_sockpat(int af, const struct sockpat *sp, | |
129 | const struct socket *s) | |
130 | { | |
131 | return (match_addrpat(af, &sp->addr, &s->addr) && | |
132 | match_portpat(&sp->port, s->port)); | |
133 | } | |
134 | ||
135 | int match_policy(const struct policy *p, const struct query *q) | |
136 | { | |
137 | return ((!p->af || p->af == q->af) && | |
138 | match_sockpat(p->af, &p->sp[L], &q->s[L]) && | |
139 | match_sockpat(p->af, &p->sp[R], &q->s[R])); | |
140 | } | |
141 | ||
142 | static void nextline(FILE *fp) | |
143 | { | |
144 | for (;;) { | |
145 | int ch = getc(fp); | |
146 | if (ch == '\n' || ch == EOF) break; | |
147 | } | |
148 | } | |
149 | ||
150 | static int scan(FILE *fp, char *buf, size_t sz) | |
151 | { | |
152 | int ch; | |
153 | ||
154 | skip_ws: | |
155 | ch = getc(fp); | |
156 | switch (ch) { | |
157 | case '\n': | |
158 | newline: | |
159 | ungetc(ch, fp); | |
160 | return (T_EOL); | |
161 | case EOF: | |
162 | eof: | |
163 | return (ferror(fp) ? T_ERROR : T_EOF); | |
164 | case '#': | |
165 | for (;;) { | |
166 | ch = getc(fp); | |
167 | if (ch == '\n') goto newline; | |
168 | else if (ch == EOF) goto eof; | |
169 | } | |
170 | default: | |
171 | if (isspace(ch)) goto skip_ws; | |
172 | break; | |
173 | } | |
174 | ||
175 | for (;;) { | |
176 | if (sz) { *buf++ = ch; sz--; } | |
177 | ch = getc(fp); | |
178 | switch (ch) { | |
179 | case '\n': | |
180 | ungetc(ch, fp); | |
181 | goto done; | |
182 | case EOF: | |
183 | goto done; | |
184 | default: | |
185 | if (isspace(ch)) goto done; | |
186 | break; | |
187 | } | |
188 | } | |
189 | ||
190 | done: | |
191 | if (!sz) | |
192 | return (T_ERROR); | |
193 | else { | |
194 | *buf++ = 0; sz--; | |
195 | return (T_OK); | |
196 | } | |
197 | } | |
198 | ||
199 | static int parse_actname(FILE *fp, unsigned *act) | |
200 | { | |
201 | char buf[32]; | |
202 | int t; | |
203 | const char *const *p; | |
204 | ||
205 | if ((t = scan(fp, buf, sizeof(buf))) != 0) return (t); | |
206 | for (p = acttab; *p; p++) | |
207 | if (strcmp(buf, *p) == 0) { *act = p - acttab; return (0); } | |
208 | return (T_ERROR); | |
209 | } | |
210 | ||
211 | static int parse_action(FILE *fp, struct action *act) | |
212 | { | |
213 | char buf[32]; | |
214 | int t; | |
215 | unsigned a; | |
216 | unsigned long m; | |
217 | ||
218 | if ((t = parse_actname(fp, &a)) != 0) return (t); | |
219 | switch (a) { | |
220 | case A_USER: | |
221 | m = 0; | |
222 | for (;;) { | |
223 | if ((t = parse_actname(fp, &a)) != 0) break; | |
224 | m |= (1 << a); | |
225 | } | |
226 | if (t != T_EOL && t != T_EOF) return (t); | |
227 | act->act = A_USER; | |
228 | act->u.user = m; | |
229 | break; | |
230 | case A_TOKEN: | |
231 | case A_NAME: | |
232 | case A_DENY: | |
233 | case A_HIDE: | |
234 | act->act = a; | |
235 | break; | |
236 | case A_LIE: | |
237 | if ((t = scan(fp, buf, sizeof(buf))) != 0) return (t); | |
238 | act->act = a; | |
239 | act->u.lie = xstrdup(buf); | |
240 | break; | |
241 | } | |
242 | t = scan(fp, buf, sizeof(buf)); | |
243 | if (t != T_EOF && t != T_EOL) { | |
244 | free_action(act); | |
245 | return (T_ERROR); | |
246 | } | |
247 | return (0); | |
248 | } | |
249 | ||
250 | static int parse_sockpat(FILE *fp, int *afp, struct sockpat *sp) | |
251 | { | |
252 | char buf[64]; | |
253 | int t; | |
254 | int af; | |
255 | int alen; | |
256 | long n; | |
257 | char *delim; | |
258 | ||
259 | if ((t = scan(fp, buf, sizeof(buf))) != 0) return (t); | |
260 | if (strcmp(buf, "*") == 0) | |
261 | sp->addr.len = 0; | |
262 | else { | |
263 | if (strchr(buf, ':')) { | |
264 | af = AF_INET6; | |
265 | alen = 128; | |
266 | } else { | |
267 | af = AF_INET; | |
268 | alen = 32; | |
269 | } | |
270 | if (!*afp) *afp = af; | |
271 | else if (*afp != af) return (T_ERROR); | |
272 | delim = strchr(buf, '/'); | |
273 | if (delim) *delim++ = 0; | |
274 | if (!inet_pton(af, buf, &sp->addr.addr)) return (T_ERROR); | |
275 | if (!delim) n = alen; | |
276 | else n = strtol(delim, 0, 10); | |
277 | if (n < 0 || n > alen) return (T_ERROR); | |
278 | sp->addr.len = n; | |
279 | } | |
280 | ||
281 | if ((t = scan(fp, buf, sizeof(buf))) != 0) return (T_ERROR); | |
282 | if (strcmp(buf, "*") == 0) { | |
283 | sp->port.lo = 0; | |
284 | sp->port.hi = 65535; | |
285 | } else { | |
286 | delim = strchr(buf, '-'); | |
287 | if (delim) *delim++ = 0; | |
288 | n = strtol(buf, 0, 0); | |
289 | if (n < 0 || n > 65535) return (T_ERROR); | |
290 | sp->port.lo = n; | |
291 | if (!delim) | |
292 | sp->port.hi = n; | |
293 | else { | |
294 | n = strtol(delim, 0, 0); | |
295 | if (n < 0 || n > 65535) return (T_ERROR); | |
296 | sp->port.hi = n; | |
297 | } | |
298 | } | |
299 | return (0); | |
300 | } | |
301 | ||
302 | int parse_policy(FILE *fp, struct policy *p) | |
303 | { | |
304 | int t; | |
305 | ||
306 | p->af = 0; | |
307 | free_policy(p); | |
308 | ||
309 | if ((t = parse_sockpat(fp, &p->af, &p->sp[L])) != 0) goto fail; | |
310 | if ((t = parse_sockpat(fp, &p->af, &p->sp[R])) != 0) goto err; | |
311 | if ((t = parse_action(fp, &p->act)) != 0) goto err; | |
312 | return (0); | |
313 | ||
314 | err: | |
315 | t = T_ERROR; | |
316 | fail: | |
317 | free_policy(p); | |
318 | return (t); | |
319 | } | |
320 | ||
321 | int open_policy_file(struct policy_file *pf, const char *name, | |
322 | const char *what, const struct query *q) | |
323 | { | |
324 | if ((pf->fp = fopen(name, "r")) == 0) { | |
325 | logmsg(q, LOG_ERR, "failed to open %s `%s': %s", | |
326 | what, name, strerror(errno)); | |
327 | return (-1); | |
328 | } | |
329 | ||
330 | pf->name = name; | |
331 | pf->what = what; | |
332 | pf->q = q; | |
333 | pf->err = 0; | |
334 | pf->lno = 0; | |
335 | init_policy(&pf->p); | |
336 | return (0); | |
337 | } | |
338 | ||
339 | int read_policy_file(struct policy_file *pf) | |
340 | { | |
341 | int t; | |
342 | ||
343 | for (;;) { | |
344 | pf->lno++; | |
345 | t = parse_policy(pf->fp, &pf->p); | |
346 | switch (t) { | |
347 | case T_OK: | |
348 | nextline(pf->fp); | |
349 | return (0); | |
350 | case T_ERROR: | |
351 | logmsg(pf->q, LOG_ERR, "%s:%d: parse error in %s", | |
352 | pf->name, pf->lno, pf->what); | |
353 | pf->err = 1; | |
354 | break; | |
355 | case T_EOF: | |
356 | if (ferror(pf->fp)) { | |
357 | logmsg(pf->q, LOG_ERR, "failed to read %s `%s': %s", | |
358 | pf->what, pf->name, strerror(errno)); | |
359 | } | |
360 | return (-1); | |
361 | case T_EOL: | |
362 | nextline(pf->fp); | |
363 | break; | |
364 | default: | |
365 | abort(); | |
366 | } | |
367 | } | |
368 | } | |
369 | ||
370 | void close_policy_file(struct policy_file *pf) | |
371 | { | |
372 | fclose(pf->fp); | |
373 | free_policy(&pf->p); | |
374 | } | |
375 | ||
376 | int load_policy_file(const char *file, policy_v *pv) | |
377 | { | |
378 | struct policy_file pf; | |
379 | policy_v v = DA_INIT; | |
380 | ||
381 | if (open_policy_file(&pf, file, "policy file", 0)) | |
382 | return (-1); | |
383 | while (!read_policy_file(&pf)) { | |
384 | DA_PUSH(&v, pf.p); | |
385 | init_policy(&pf.p); | |
386 | } | |
387 | close_policy_file(&pf); | |
388 | if (!pf.err) { | |
389 | DA_DESTROY(pv); | |
390 | *pv = v; | |
391 | return (0); | |
392 | } else { | |
393 | DA_DESTROY(&v); | |
394 | return (-1); | |
395 | } | |
396 | } | |
397 | ||
398 | /*----- That's all, folks -------------------------------------------------*/ |