chiark / gitweb /
policy.c: Implement IPv6 matching in match_addrpat.
[yaid] / policy.c
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       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     }
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 -------------------------------------------------*/