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 | } | |
f7a8c7eb MW |
119 | case AF_INET6: { |
120 | unsigned i, m, n = ap->len; | |
121 | for (i = 0; n >= 8; i++, n -= 8) { | |
122 | if (ap->addr.ipv6.s6_addr[i] != a->ipv6.s6_addr[i]) | |
123 | return (0); | |
124 | } | |
125 | if (!n) return (1); | |
126 | m = (MASK8 << (8 - n)) & MASK8; | |
127 | return (((ap->addr.ipv6.s6_addr[i] ^ a->ipv6.s6_addr[i]) & m) == 0); | |
128 | } | |
9da480be MW |
129 | } |
130 | return (0); | |
131 | } | |
132 | ||
133 | static int match_portpat(const struct portpat *pp, unsigned port) | |
134 | { return (pp->lo <= port && port <= pp->hi); } | |
135 | ||
136 | static int match_sockpat(int af, const struct sockpat *sp, | |
137 | const struct socket *s) | |
138 | { | |
139 | return (match_addrpat(af, &sp->addr, &s->addr) && | |
140 | match_portpat(&sp->port, s->port)); | |
141 | } | |
142 | ||
143 | int match_policy(const struct policy *p, const struct query *q) | |
144 | { | |
145 | return ((!p->af || p->af == q->af) && | |
146 | match_sockpat(p->af, &p->sp[L], &q->s[L]) && | |
147 | match_sockpat(p->af, &p->sp[R], &q->s[R])); | |
148 | } | |
149 | ||
150 | static void nextline(FILE *fp) | |
151 | { | |
152 | for (;;) { | |
153 | int ch = getc(fp); | |
154 | if (ch == '\n' || ch == EOF) break; | |
155 | } | |
156 | } | |
157 | ||
158 | static int scan(FILE *fp, char *buf, size_t sz) | |
159 | { | |
160 | int ch; | |
161 | ||
162 | skip_ws: | |
163 | ch = getc(fp); | |
164 | switch (ch) { | |
165 | case '\n': | |
166 | newline: | |
167 | ungetc(ch, fp); | |
168 | return (T_EOL); | |
169 | case EOF: | |
170 | eof: | |
171 | return (ferror(fp) ? T_ERROR : T_EOF); | |
172 | case '#': | |
173 | for (;;) { | |
174 | ch = getc(fp); | |
175 | if (ch == '\n') goto newline; | |
176 | else if (ch == EOF) goto eof; | |
177 | } | |
178 | default: | |
179 | if (isspace(ch)) goto skip_ws; | |
180 | break; | |
181 | } | |
182 | ||
183 | for (;;) { | |
184 | if (sz) { *buf++ = ch; sz--; } | |
185 | ch = getc(fp); | |
186 | switch (ch) { | |
187 | case '\n': | |
188 | ungetc(ch, fp); | |
189 | goto done; | |
190 | case EOF: | |
191 | goto done; | |
192 | default: | |
193 | if (isspace(ch)) goto done; | |
194 | break; | |
195 | } | |
196 | } | |
197 | ||
198 | done: | |
199 | if (!sz) | |
200 | return (T_ERROR); | |
201 | else { | |
202 | *buf++ = 0; sz--; | |
203 | return (T_OK); | |
204 | } | |
205 | } | |
206 | ||
207 | static int parse_actname(FILE *fp, unsigned *act) | |
208 | { | |
209 | char buf[32]; | |
210 | int t; | |
211 | const char *const *p; | |
212 | ||
213 | if ((t = scan(fp, buf, sizeof(buf))) != 0) return (t); | |
214 | for (p = acttab; *p; p++) | |
215 | if (strcmp(buf, *p) == 0) { *act = p - acttab; return (0); } | |
216 | return (T_ERROR); | |
217 | } | |
218 | ||
219 | static int parse_action(FILE *fp, struct action *act) | |
220 | { | |
221 | char buf[32]; | |
222 | int t; | |
223 | unsigned a; | |
224 | unsigned long m; | |
225 | ||
226 | if ((t = parse_actname(fp, &a)) != 0) return (t); | |
227 | switch (a) { | |
228 | case A_USER: | |
229 | m = 0; | |
230 | for (;;) { | |
231 | if ((t = parse_actname(fp, &a)) != 0) break; | |
232 | m |= (1 << a); | |
233 | } | |
234 | if (t != T_EOL && t != T_EOF) return (t); | |
235 | act->act = A_USER; | |
236 | act->u.user = m; | |
237 | break; | |
238 | case A_TOKEN: | |
239 | case A_NAME: | |
240 | case A_DENY: | |
241 | case A_HIDE: | |
242 | act->act = a; | |
243 | break; | |
244 | case A_LIE: | |
245 | if ((t = scan(fp, buf, sizeof(buf))) != 0) return (t); | |
246 | act->act = a; | |
247 | act->u.lie = xstrdup(buf); | |
248 | break; | |
249 | } | |
250 | t = scan(fp, buf, sizeof(buf)); | |
251 | if (t != T_EOF && t != T_EOL) { | |
252 | free_action(act); | |
253 | return (T_ERROR); | |
254 | } | |
255 | return (0); | |
256 | } | |
257 | ||
258 | static int parse_sockpat(FILE *fp, int *afp, struct sockpat *sp) | |
259 | { | |
260 | char buf[64]; | |
261 | int t; | |
262 | int af; | |
263 | int alen; | |
264 | long n; | |
265 | char *delim; | |
266 | ||
267 | if ((t = scan(fp, buf, sizeof(buf))) != 0) return (t); | |
268 | if (strcmp(buf, "*") == 0) | |
269 | sp->addr.len = 0; | |
270 | else { | |
271 | if (strchr(buf, ':')) { | |
272 | af = AF_INET6; | |
273 | alen = 128; | |
274 | } else { | |
275 | af = AF_INET; | |
276 | alen = 32; | |
277 | } | |
278 | if (!*afp) *afp = af; | |
279 | else if (*afp != af) return (T_ERROR); | |
280 | delim = strchr(buf, '/'); | |
281 | if (delim) *delim++ = 0; | |
282 | if (!inet_pton(af, buf, &sp->addr.addr)) return (T_ERROR); | |
283 | if (!delim) n = alen; | |
284 | else n = strtol(delim, 0, 10); | |
285 | if (n < 0 || n > alen) return (T_ERROR); | |
286 | sp->addr.len = n; | |
287 | } | |
288 | ||
289 | if ((t = scan(fp, buf, sizeof(buf))) != 0) return (T_ERROR); | |
290 | if (strcmp(buf, "*") == 0) { | |
291 | sp->port.lo = 0; | |
292 | sp->port.hi = 65535; | |
293 | } else { | |
294 | delim = strchr(buf, '-'); | |
295 | if (delim) *delim++ = 0; | |
296 | n = strtol(buf, 0, 0); | |
297 | if (n < 0 || n > 65535) return (T_ERROR); | |
298 | sp->port.lo = n; | |
299 | if (!delim) | |
300 | sp->port.hi = n; | |
301 | else { | |
302 | n = strtol(delim, 0, 0); | |
303 | if (n < 0 || n > 65535) return (T_ERROR); | |
304 | sp->port.hi = n; | |
305 | } | |
306 | } | |
307 | return (0); | |
308 | } | |
309 | ||
310 | int parse_policy(FILE *fp, struct policy *p) | |
311 | { | |
312 | int t; | |
313 | ||
314 | p->af = 0; | |
315 | free_policy(p); | |
316 | ||
317 | if ((t = parse_sockpat(fp, &p->af, &p->sp[L])) != 0) goto fail; | |
318 | if ((t = parse_sockpat(fp, &p->af, &p->sp[R])) != 0) goto err; | |
319 | if ((t = parse_action(fp, &p->act)) != 0) goto err; | |
320 | return (0); | |
321 | ||
322 | err: | |
323 | t = T_ERROR; | |
324 | fail: | |
325 | free_policy(p); | |
326 | return (t); | |
327 | } | |
328 | ||
329 | int open_policy_file(struct policy_file *pf, const char *name, | |
330 | const char *what, const struct query *q) | |
331 | { | |
332 | if ((pf->fp = fopen(name, "r")) == 0) { | |
333 | logmsg(q, LOG_ERR, "failed to open %s `%s': %s", | |
334 | what, name, strerror(errno)); | |
335 | return (-1); | |
336 | } | |
337 | ||
338 | pf->name = name; | |
339 | pf->what = what; | |
340 | pf->q = q; | |
341 | pf->err = 0; | |
342 | pf->lno = 0; | |
343 | init_policy(&pf->p); | |
344 | return (0); | |
345 | } | |
346 | ||
347 | int read_policy_file(struct policy_file *pf) | |
348 | { | |
349 | int t; | |
350 | ||
351 | for (;;) { | |
352 | pf->lno++; | |
353 | t = parse_policy(pf->fp, &pf->p); | |
354 | switch (t) { | |
355 | case T_OK: | |
356 | nextline(pf->fp); | |
357 | return (0); | |
358 | case T_ERROR: | |
359 | logmsg(pf->q, LOG_ERR, "%s:%d: parse error in %s", | |
360 | pf->name, pf->lno, pf->what); | |
361 | pf->err = 1; | |
362 | break; | |
363 | case T_EOF: | |
364 | if (ferror(pf->fp)) { | |
365 | logmsg(pf->q, LOG_ERR, "failed to read %s `%s': %s", | |
366 | pf->what, pf->name, strerror(errno)); | |
367 | } | |
368 | return (-1); | |
369 | case T_EOL: | |
370 | nextline(pf->fp); | |
371 | break; | |
372 | default: | |
373 | abort(); | |
374 | } | |
375 | } | |
376 | } | |
377 | ||
378 | void close_policy_file(struct policy_file *pf) | |
379 | { | |
380 | fclose(pf->fp); | |
381 | free_policy(&pf->p); | |
382 | } | |
383 | ||
384 | int load_policy_file(const char *file, policy_v *pv) | |
385 | { | |
386 | struct policy_file pf; | |
387 | policy_v v = DA_INIT; | |
388 | ||
389 | if (open_policy_file(&pf, file, "policy file", 0)) | |
390 | return (-1); | |
391 | while (!read_policy_file(&pf)) { | |
392 | DA_PUSH(&v, pf.p); | |
393 | init_policy(&pf.p); | |
394 | } | |
395 | close_policy_file(&pf); | |
396 | if (!pf.err) { | |
397 | DA_DESTROY(pv); | |
398 | *pv = v; | |
399 | return (0); | |
400 | } else { | |
401 | DA_DESTROY(&v); | |
402 | return (-1); | |
403 | } | |
404 | } | |
405 | ||
406 | /*----- That's all, folks -------------------------------------------------*/ |