source: lib/libvcl/vcc_acl.c @ c5c9e33

Revision c5c9e33, 11.4 KB checked in by Poul-Henning Kamp <phk@…>, 3 years ago (diff)

Eliminate a redundant include file

  • Property mode set to 100644
Line 
1/*-
2 * Copyright (c) 2006 Verdens Gang AS
3 * Copyright (c) 2006-2010 Varnish Software AS
4 * All rights reserved.
5 *
6 * Author: Poul-Henning Kamp <phk@phk.freebsd.dk>
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 */
29
30#include "config.h"
31
32#include <stdio.h>
33#include <stddef.h>
34#include <string.h>
35#include <sys/types.h>
36#include <sys/socket.h>
37#include <netdb.h>
38#include <netinet/in.h>
39
40
41#include "vcc_compile.h"
42#include "vrt.h"
43
44struct acl_e {
45        VTAILQ_ENTRY(acl_e)     list;
46        unsigned char           data[VRT_ACL_MAXADDR + 1];
47        unsigned                mask;
48        unsigned                not;
49        unsigned                para;
50        struct token            *t_addr;
51        struct token            *t_mask;
52};
53
54/* Compare two acl rules for ordering */
55
56#define CMP(a, b)                                                       \
57        do {                                                            \
58                if ((a) < (b))                                          \
59                        return (-1);                                    \
60                else if ((b) < (a))                                     \
61                        return (1);                                     \
62        } while (0)
63
64static int
65vcl_acl_cmp(struct acl_e *ae1, struct acl_e *ae2)
66{
67        unsigned char *p1, *p2;
68        unsigned m;
69
70        p1 = ae1->data;
71        p2 = ae2->data;
72        m = ae1->mask;
73        if (ae2->mask < m)
74                m = ae2->mask;
75        for (; m >= 8; m -= 8) {
76                CMP(*p1, *p2);
77                p1++;
78                p2++;
79        }
80        if (m) {
81                m = 0xff00 >> m;
82                m &= 0xff;
83                CMP(*p1 & m, *p2 & m);
84        }
85        /* Long mask is less than short mask */
86        CMP(ae2->mask, ae1->mask);
87
88        return (0);
89}
90
91
92static void
93vcc_acl_add_entry(struct vcc *tl, const struct acl_e *ae, int l,
94    const unsigned char *u, int fam)
95{
96        struct acl_e *ae2, *aen;
97        int i;
98
99        if (fam == PF_INET && ae->mask > 32) {
100                VSB_printf(tl->sb,
101                    "Too wide mask (%u) for IPv4 address", ae->mask);
102                vcc_ErrWhere(tl, ae->t_mask);
103                return;
104        }
105        if (fam == PF_INET6 && ae->mask > 128) {
106                VSB_printf(tl->sb,
107                    "Too wide mask (%u) for IPv6 address", ae->mask);
108                vcc_ErrWhere(tl, ae->t_mask);
109                return;
110        }
111
112        /* Make a copy from the template */
113        aen = TlAlloc(tl, sizeof *ae2);
114        AN(aen);
115        *aen = *ae;
116
117        /* We treat family as part of address, it saves code */
118        assert(fam <= 0xff);
119        aen->data[0] = fam & 0xff;
120        aen->mask += 8;
121
122        memcpy(aen->data + 1, u, l);
123
124        VTAILQ_FOREACH(ae2, &tl->acl, list) {
125                i = vcl_acl_cmp(aen, ae2);
126                if (i == 0) {
127                        /*
128                         * If the two rules agree, silently ignore it
129                         * XXX: is that counter intuitive ?
130                         */
131                        if (aen->not == ae2->not)
132                                return;
133                        VSB_printf(tl->sb, "Conflicting ACL entries:\n");
134                        vcc_ErrWhere(tl, ae2->t_addr);
135                        VSB_printf(tl->sb, "vs:\n");
136                        vcc_ErrWhere(tl, aen->t_addr);
137                        return;
138                }
139                /*
140                 * We could eliminate pointless rules here, for instance in:
141                 *      "10.1.0.1";
142                 *      "10.1";
143                 * The first rule is clearly pointless, as the second one
144                 * covers it.
145                 *
146                 * We do not do this however, because the shmlog may
147                 * be used to gather statistics.
148                 */
149                if (i < 0) {
150                        VTAILQ_INSERT_BEFORE(ae2, aen, list);
151                        return;
152                }
153        }
154        VTAILQ_INSERT_TAIL(&tl->acl, aen, list);
155}
156
157static void
158vcc_acl_try_getaddrinfo(struct vcc *tl, struct acl_e *ae)
159{
160        struct addrinfo *res0, *res, hint;
161        struct sockaddr_in *sin4;
162        struct sockaddr_in6 *sin6;
163        unsigned char *u, i4, i6;
164        int error;
165
166        memset(&hint, 0, sizeof hint);
167        hint.ai_family = PF_UNSPEC;
168        hint.ai_socktype = SOCK_STREAM;
169        error = getaddrinfo(ae->t_addr->dec, "0", &hint, &res0);
170        if (error) {
171                if (ae->para) {
172                        VSB_printf(tl->sb,
173                            "Warning: %s ignored\n  -- %s\n",
174                            ae->t_addr->dec, gai_strerror(error));
175                        Fh(tl, 1, "/* Ignored ACL entry: %s%s",
176                            ae->para ? "\"(\" " : "", ae->not ? "\"!\" " : "");
177                        EncToken(tl->fh, ae->t_addr);
178                        if (ae->t_mask)
179                                Fh(tl, 0, "/%u", ae->mask);
180                        Fh(tl, 0, "%s\n", ae->para ? " \")\"" : "");
181                        Fh(tl, 1, " * getaddrinfo:  %s */\n",
182                             gai_strerror(error));
183                } else {
184                        VSB_printf(tl->sb,
185                            "DNS lookup(%s): %s\n",
186                            ae->t_addr->dec, gai_strerror(error));
187                        vcc_ErrWhere(tl, ae->t_addr);
188                }
189                return;
190        }
191
192        i4 = i6 = 0;
193        for(res = res0; res != NULL; res = res->ai_next) {
194                switch(res->ai_family) {
195                case PF_INET:
196                        assert(PF_INET < 256);
197                        sin4 = (void*)res->ai_addr;
198                        assert(sizeof(sin4->sin_addr) == 4);
199                        u = (void*)&sin4->sin_addr;
200                        if (ae->t_mask == NULL)
201                                ae->mask = 32;
202                        i4++;
203                        vcc_acl_add_entry(tl, ae, 4, u, res->ai_family);
204                        break;
205                case PF_INET6:
206                        assert(PF_INET6 < 256);
207                        sin6 = (void*)res->ai_addr;
208                        assert(sizeof(sin6->sin6_addr) == 16);
209                        u = (void*)&sin6->sin6_addr;
210                        if (ae->t_mask == NULL)
211                                ae->mask = 128;
212                        i6++;
213                        vcc_acl_add_entry(tl, ae, 16, u, res->ai_family);
214                        break;
215                default:
216                        VSB_printf(tl->sb,
217                            "Ignoring unknown protocol family (%d) for %.*s\n",
218                                res->ai_family, PF(ae->t_addr));
219                        continue;
220                }
221                ERRCHK(tl);
222        }
223        freeaddrinfo(res0);
224
225        if (ae->t_mask != NULL && i4 > 0 && i6 > 0) {
226                VSB_printf(tl->sb,
227                    "Mask (%u) specified, but string resolves to"
228                    " both IPv4 and IPv6 addresses.\n", ae->mask);
229                vcc_ErrWhere(tl, ae->t_mask);
230                return;
231        }
232}
233
234/*--------------------------------------------------------------------
235 * Ancient stupidity on the part of X/Open and other standards orgs
236 * dictate that "192.168" be translated to 192.0.0.168.  Ever since
237 * CIDR happened, "192.168/16" notation has been used, but appearantly
238 * no API supports parsing this, so roll our own.
239 */
240
241static int
242vcc_acl_try_netnotation(struct vcc *tl, struct acl_e *ae)
243{
244        unsigned char b[4];
245        int i, j, k;
246        unsigned u;
247        const char *p;
248
249        memset(b, 0, sizeof b);
250        p = ae->t_addr->dec;
251        for (i = 0; i < 4; i++) {
252                j = sscanf(p, "%u%n", &u, &k);
253                if (j != 1)
254                        return (0);
255                if (u & ~0xff)
256                        return (0);
257                b[i] = (unsigned char)u;
258                if (p[k] == '\0')
259                        break;
260                if (p[k] != '.')
261                        return (0);
262                p += k + 1;
263        }
264        if (ae->t_mask == NULL)
265                ae->mask = 8 + 8 * i;
266        vcc_acl_add_entry(tl, ae, 4, b, AF_INET);
267        return (1);
268}
269
270static void
271vcc_acl_entry(struct vcc *tl)
272{
273        struct acl_e *ae;
274
275        ae = TlAlloc(tl, sizeof *ae);
276        AN(ae);
277
278        if (tl->t->tok == '!') {
279                ae->not = 1;
280                vcc_NextToken(tl);
281        }
282
283        if (tl->t->tok == '(') {
284                ae->para = 1;
285                vcc_NextToken(tl);
286        }
287
288        if (!ae->not && tl->t->tok == '!') {
289                ae->not = 1;
290                vcc_NextToken(tl);
291        }
292
293        ExpectErr(tl, CSTR);
294        ae->t_addr = tl->t;
295        vcc_NextToken(tl);
296
297        if (tl->t->tok == '/') {
298                vcc_NextToken(tl);
299                ae->t_mask = tl->t;
300                ExpectErr(tl, CNUM);
301                ae->mask = vcc_UintVal(tl);
302        }
303
304        if (ae->para)
305                SkipToken(tl, ')');
306
307        if (!vcc_acl_try_netnotation(tl, ae)) {
308                ERRCHK(tl);
309                vcc_acl_try_getaddrinfo(tl, ae);
310        }
311        ERRCHK(tl);
312}
313
314/*********************************************************************
315 * Emit a function to match the ACL we have collected
316 */
317
318/*
319 * XXX: this is semi-silly.  We try really hard to not depend in the
320 * XXX: systems include files while compiling VCL, but we need to know
321 * XXX: the size of the sa_familiy member.
322 * XXX: FlexeLint complains about these antics, so isolate it in a
323 * XXX: separate function.
324 */
325
326/*lint -save -e506 -e774 -e550 */
327static void
328c_is_a_silly_language(const struct vcc *tl)
329{
330        struct sockaddr sa;
331
332        assert(sizeof (unsigned char) == 1);
333        assert(sizeof (unsigned short) == 2);
334        assert(sizeof (unsigned int) == 4);
335        if (sizeof sa.sa_family == 1)
336                Fh(tl, 0, "\tunsigned char fam;\n");
337        else if (sizeof sa.sa_family == 2)
338                Fh(tl, 0, "\tunsigned short fam;\n");
339        else if (sizeof sa.sa_family == 4)
340                Fh(tl, 0, "\tunsigned int fam;\n");
341        else
342                assert(0 == __LINE__);
343}
344/*lint -restore */
345
346static void
347vcc_acl_emit(const struct vcc *tl, const char *acln, int anon)
348{
349        struct acl_e *ae;
350        int depth, l, m, i;
351        unsigned at[VRT_ACL_MAXADDR + 1];
352        const char *oc;
353
354        Fh(tl, 0, "\nstatic int\n");
355        Fh(tl, 0, "match_acl_%s_%s(const struct sess *sp, const void *p)\n",
356            anon ? "anon" : "named", acln);
357        Fh(tl, 0, "{\n");
358        Fh(tl, 0, "\tconst unsigned char *a;\n");
359        c_is_a_silly_language(tl);
360
361        Fh(tl, 0, "\n");
362        Fh(tl, 0, "\ta = p;\n");
363        Fh(tl, 0, "\tVRT_memmove(&fam, a + %d, sizeof fam);\n",
364            offsetof(struct sockaddr, sa_family));
365        Fh(tl, 0, "\tif (fam == %d)\n", PF_INET);
366        Fh(tl, 0, "\t\ta += %d;\n", offsetof(struct sockaddr_in, sin_addr));
367        Fh(tl, 0, "\telse if (fam == %d)\n", PF_INET6);
368        Fh(tl, 0, "\t\ta += %d;\n", offsetof(struct sockaddr_in6, sin6_addr));
369        Fh(tl, 0, "\telse {\n");
370        Fh(tl, 0, "\t\tVRT_acl_log(sp, \"NO_FAM %s\");\n", acln);
371        Fh(tl, 0, "\t\treturn(0);\n");
372        Fh(tl, 0, "\t}\n\n");
373        depth = -1;
374        oc = 0;
375        at[0] = 256;
376        VTAILQ_FOREACH(ae, &tl->acl, list) {
377
378                /* Find how much common prefix we have */
379                for (l = 0; l <= depth && l * 8 < ae->mask; l++) {
380                        assert(l >= 0);
381                        if (ae->data[l] != at[l])
382                                break;
383                }
384
385                /* Back down, if necessary */
386                oc = "";
387                while (l <= depth) {
388                        Fh(tl, 0, "\t%*s}\n", -depth, "");
389                        depth--;
390                        oc = "else ";
391                }
392
393                m = ae->mask;
394                m -= l * 8;
395
396                /* Do whole byte compares */
397                for (i = l; m >= 8; m -= 8, i++) {
398                        if (i == 0)
399                                Fh(tl, 0, "\t%*s%sif (fam == %d) {\n",
400                                    -i, "", oc, ae->data[i]);
401                        else
402                                Fh(tl, 0, "\t%*s%sif (a[%d] == %d) {\n",
403                                    -i, "", oc, i - 1, ae->data[i]);
404                        at[i] = ae->data[i];
405                        depth = i;
406                        oc = "";
407                }
408
409                if (m > 0) {
410                        /* Do fractional byte compares */
411                        Fh(tl, 0, "\t%*s%sif ((a[%d] & 0x%x) == %d) {\n",
412                            -i, "", oc, i - 1, (0xff00 >> m) & 0xff,
413                            ae->data[i] & ((0xff00 >> m) & 0xff));
414                        at[i] = 256;
415                        depth = i;
416                        oc = "";
417                }
418
419                i = (ae->mask + 7) / 8;
420
421                if (!anon) {
422                        Fh(tl, 0, "\t%*sVRT_acl_log(sp, \"%sMATCH %s \" ",
423                            -i, "", ae->not ? "NEG_" : "", acln,
424                            PF(ae->t_addr));
425                        EncToken(tl->fh, ae->t_addr);
426                        if (ae->t_mask != NULL)
427                                Fh(tl, 0, " \"/%.*s\" ", PF(ae->t_mask));
428                        Fh(tl, 0, ");\n");
429                }
430
431                Fh(tl, 0, "\t%*sreturn (%d);\n", -i, "", ae->not ? 0 : 1);
432        }
433
434        /* Unwind */
435        for (; 0 <= depth; depth--)
436                Fh(tl, 0, "\t%*.*s}\n", depth, depth, "");
437
438        /* Deny by default */
439        if (!anon)
440                Fh(tl, 0, "\tVRT_acl_log(sp, \"NO_MATCH %s\");\n", acln);
441        Fh(tl, 0, "\treturn (0);\n}\n");
442}
443
444void
445vcc_Acl_Hack(struct vcc *tl, char *b)
446{
447        char acln[32];
448        unsigned tcond;
449
450        VTAILQ_INIT(&tl->acl);
451        tcond = tl->t->tok;
452        vcc_NextToken(tl);
453        bprintf(acln, "%u", tl->unique++);
454        vcc_acl_entry(tl);
455        vcc_acl_emit(tl, acln, 1);
456        sprintf(b, "%smatch_acl_anon_%s(sp, \v1)",
457            (tcond == T_NEQ ? "!" : ""), acln);
458}
459
460void
461vcc_Acl(struct vcc *tl)
462{
463        struct token *an;
464        int i;
465        char acln[1024];
466
467        vcc_NextToken(tl);
468        VTAILQ_INIT(&tl->acl);
469
470        ExpectErr(tl, ID);
471        an = tl->t;
472        vcc_NextToken(tl);
473
474        i = vcc_AddDef(tl, an, SYM_ACL);
475        if (i > 1) {
476                VSB_printf(tl->sb, "ACL %.*s redefined\n", PF(an));
477                vcc_ErrWhere(tl, an);
478                return;
479        }
480        bprintf(acln, "%.*s", PF(an));
481
482        SkipToken(tl, '{');
483
484        while (tl->t->tok != '}') {
485                vcc_acl_entry(tl);
486                ERRCHK(tl);
487                SkipToken(tl, ';');
488        }
489        SkipToken(tl, '}');
490
491        vcc_acl_emit(tl, acln, 0);
492}
Note: See TracBrowser for help on using the repository browser.