yaid.c: Don't fail if either IPv4 or IPv6 is unavailable.
[yaid] / policy.c
CommitLineData
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
45void init_policy(struct policy *p) { p->act.act = A_LIMIT; }
46
47static 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
57void free_policy(struct policy *p)
58 { free_action(&p->act); }
59
60static 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
68static 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
75static void print_sockpat(int af, const struct sockpat *sp)
76 { print_addrpat(af, &sp->addr); putchar(' '); print_portpat(&sp->port); }
77
78static const char *const acttab[] = {
79#define DEFACT(tag, name) name,
80 ACTIONS(DEFACT)
81#undef DEFACT
82 0
83};
84
85static 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
102void 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
109static 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
133static int match_portpat(const struct portpat *pp, unsigned port)
134 { return (pp->lo <= port && port <= pp->hi); }
135
136static 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
143int 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
150static void nextline(FILE *fp)
151{
152 for (;;) {
153 int ch = getc(fp);
154 if (ch == '\n' || ch == EOF) break;
155 }
156}
157
158static int scan(FILE *fp, char *buf, size_t sz)
159{
160 int ch;
161
162skip_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
198done:
199 if (!sz)
200 return (T_ERROR);
201 else {
202 *buf++ = 0; sz--;
203 return (T_OK);
204 }
205}
206
207static 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
219static 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
258static 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
310int 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
322err:
323 t = T_ERROR;
324fail:
325 free_policy(p);
326 return (t);
327}
328
329int 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
347int 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
378void close_policy_file(struct policy_file *pf)
379{
380 fclose(pf->fp);
381 free_policy(&pf->p);
382}
383
384int 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 -------------------------------------------------*/