source: lib/libvcl/vcc_acl.c @ 710f8c

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

On platforms with multibyte sa_family and big endian, the compiled
ACLs would not work, as evidenced by #311.

Add a log message for unknown sa_families, and use memmove to extract
the sa_family member of the sockaddr into a suitably sized variable.

Diagnosed by: Mithrandir
Fixes: #311

git-svn-id:  http://www.varnish-cache.org/svn/trunk@3171 d4fa192b-c00b-0410-8231-f00ffab90ce4

  • Property mode set to 100644
Line 
1/*-
2 * Copyright (c) 2006 Verdens Gang AS
3 * Copyright (c) 2006-2008 Linpro 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 * $Id$
30 */
31
32#include "config.h"
33
34#include <stdio.h>
35#include <stdlib.h>
36#include <stddef.h>
37#include <string.h>
38#include <sys/types.h>
39#include <sys/socket.h>
40#include <netdb.h>
41#include <netinet/in.h>
42#include <arpa/inet.h>
43
44#include "config.h"
45#include "vsb.h"
46#include "vrt.h"
47
48#include "vcc_priv.h"
49#include "vcc_compile.h"
50#include "libvarnish.h"
51
52struct acl_e {
53        VTAILQ_ENTRY(acl_e)     list;
54        unsigned char           data[VRT_ACL_MAXADDR + 1];
55        unsigned                mask;
56        unsigned                not;
57        unsigned                para;
58        struct token            *t_addr;
59        struct token            *t_mask;
60};
61
62/* Compare two acl rules for ordering */
63
64static int
65vcl_acl_cmp(struct tokenlist *tl, struct acl_e *ae1, struct acl_e *ae2)
66{
67        unsigned char *p1, *p2;
68        unsigned m;
69
70        (void)tl;
71        p1 = ae1->data;
72        p2 = ae2->data;
73        m = ae1->mask;
74        if (ae2->mask < m)
75                m = ae2->mask;
76        for (; m >= 8; m -= 8) {
77                if (*p1 < *p2) 
78                        return (-1);
79                if (*p1 > *p2) 
80                        return (1);
81                p1++;
82                p2++;
83        }
84        if (m) {
85                m = 0xff00 >> m;
86                m &= 0xff;
87                if ((*p1 & m) < (*p2 & m))
88                        return (-1);
89                if ((*p1 & m) > (*p2 & m))
90                        return (1);
91        }
92        if (ae1->mask > ae2->mask)
93                return (-1);
94        if (ae1->mask < ae2->mask)
95                return (1);
96
97        return (0);
98}
99
100
101static void
102vcl_acl_add_entry(struct tokenlist *tl, struct acl_e *ae)
103{
104        struct acl_e *ae2;
105        int i;
106
107        VTAILQ_FOREACH(ae2, &tl->acl, list) {
108                i = vcl_acl_cmp(tl, ae, ae2);
109                if (i == 0) {
110                        /* If the two rules agree, silently ignore it */
111                        if (ae->not == ae2->not)
112                                return;
113                        vsb_printf(tl->sb, "Conflicting ACL entries:\n");
114                        vcc_ErrWhere(tl, ae2->t_addr);
115                        vsb_printf(tl->sb, "vs:\n");
116                        vcc_ErrWhere(tl, ae->t_addr);
117                        return;
118                }
119                /*
120                 * We could eliminate pointless rules here, for instance in:
121                 *      "10.1.0.1";
122                 *      "10.1";
123                 * The first rule is clearly pointless, as the second one
124                 * covers it.
125                 *
126                 * We do not do this however, because the shmlog may
127                 * be used to gather statistics.
128                 */
129                if (i < 0) {
130                        VTAILQ_INSERT_BEFORE(ae2, ae, list);
131                        return;
132                }
133        }
134        VTAILQ_INSERT_TAIL(&tl->acl, ae, list);
135}
136
137static void
138vcc_acl_emit_entry(struct tokenlist *tl, const struct acl_e *ae, int l, const unsigned char *u, int fam)
139{
140        struct acl_e *ae2;
141
142        if (fam == PF_INET && ae->mask > 32) {
143                vsb_printf(tl->sb,
144                    "Too wide mask (%u) for IPv4 address", ae->mask);
145                vcc_ErrWhere(tl, ae->t_mask);
146                return;
147        }
148        if (fam == PF_INET6 && ae->mask > 128) {
149                vsb_printf(tl->sb,
150                    "Too wide mask (%u) for IPv6 address", ae->mask);
151                vcc_ErrWhere(tl, ae->t_mask);
152                return;
153        }
154
155        ae2 = TlAlloc(tl, sizeof *ae2);
156        AN(ae2);
157        *ae2 = *ae;
158
159        ae2->data[0] = fam & 0xff;
160        ae2->mask += 8; /* family matching */
161
162        memcpy(ae2->data + 1, u, l);
163
164        vcl_acl_add_entry(tl, ae2);
165
166}
167
168static void
169vcc_acl_try_getaddrinfo(struct tokenlist *tl, struct acl_e *ae)
170{
171        struct addrinfo *res0, *res, hint;
172        struct sockaddr_in *sin4;
173        struct sockaddr_in6 *sin6;
174        unsigned char *u, i4, i6;
175        int error;
176
177        memset(&hint, 0, sizeof hint);
178        hint.ai_family = PF_UNSPEC;
179        hint.ai_socktype = SOCK_STREAM;
180        error = getaddrinfo(ae->t_addr->dec, "0", &hint, &res0);
181        if (error) {
182                if (ae->para) {
183                        vsb_printf(tl->sb,
184                            "Warning: %s ignored\n  -- %s\n",
185                            ae->t_addr->dec, gai_strerror(error));
186                        Fh(tl, 1, "/* Ignored ACL entry: %s%s",
187                            ae->para ? "\"(\" " : "", ae->not ? "\"!\" " : "");
188                        EncToken(tl->fh, ae->t_addr);
189                        if (ae->t_mask) 
190                                Fh(tl, 0, "/%u", ae->mask);
191                        Fh(tl, 0, "%s\n", ae->para ? " \")\"" : "");
192                        Fh(tl, 1, " * getaddrinfo:  %s */\n",
193                             gai_strerror(error));
194                } else {
195                        vsb_printf(tl->sb,
196                            "DNS lookup(%s): %s\n",
197                            ae->t_addr->dec, gai_strerror(error));
198                        vcc_ErrWhere(tl, ae->t_addr);
199                }
200                return;
201        }
202
203        i4 = i6 = 0;
204        for(res = res0; res != NULL; res = res->ai_next) {
205                switch(res->ai_family) {
206                case PF_INET:
207                        assert(PF_INET < 256);
208                        sin4 = (void*)res->ai_addr;
209                        assert(sizeof(sin4->sin_addr) == 4);
210                        u = (void*)&sin4->sin_addr;
211                        if (ae->t_mask == NULL)
212                                ae->mask = 32;
213                        i4++;
214                        vcc_acl_emit_entry(tl, ae, 4, u, res->ai_family);
215                        break;
216                case PF_INET6:
217                        assert(PF_INET6 < 256);
218                        sin6 = (void*)res->ai_addr;
219                        assert(sizeof(sin6->sin6_addr) == 16);
220                        u = (void*)&sin6->sin6_addr;
221                        if (ae->t_mask == NULL)
222                                ae->mask = 128;
223                        i6++;
224                        vcc_acl_emit_entry(tl, ae, 16, u, res->ai_family);
225                        break;
226                default:
227                        vsb_printf(tl->sb,
228                            "Ignoring unknown protocol family (%d) for %.*s\n",
229                                res->ai_family, PF(ae->t_addr));
230                        continue;
231                }
232                ERRCHK(tl);
233        }
234        freeaddrinfo(res0);
235
236        if (ae->t_mask != NULL && i4 > 0 && i6 > 0) {
237                vsb_printf(tl->sb,
238                    "Mask (%u) specified, but string resolves to"
239                    " both IPv4 and IPv6 addresses.\n", ae->mask);
240                vcc_ErrWhere(tl, ae->t_mask);
241                return;
242        }
243}
244
245/*--------------------------------------------------------------------
246 * Ancient stupidity on the part of X/Open and other standards orgs
247 * dictate that "192.168" be translated to 192.0.0.168.  Ever since
248 * CIDR happened, "192.168/16" notation has been used, but appearantly
249 * no API supports parsing this, so roll our own.
250 */
251
252static int
253vcc_acl_try_netnotation(struct tokenlist *tl, struct acl_e *ae)
254{
255        unsigned char b[4];
256        int i, j, k;
257        const char *p;
258
259        memset(b, 0, sizeof b);
260        p = ae->t_addr->dec;
261        for (i = 0; i < 4; i++) {
262                j = sscanf(p, "%hhu%n", &b[i], &k);
263                if (j != 1)
264                        return (0);
265                if (p[k] == '\0')
266                        break;
267                if (p[k] != '.')
268                        return (0);
269                p += k + 1;
270        }
271        if (ae->t_mask == NULL)
272                ae->mask = 8 + 8 * i;
273        vcc_acl_emit_entry(tl, ae, 4, b, AF_INET);
274        return (1);
275}
276
277static void
278vcc_acl_entry(struct tokenlist *tl)
279{
280        struct acl_e *ae;
281
282        ae = TlAlloc(tl, sizeof *ae);
283        AN(ae);
284
285        if (tl->t->tok == '!') {
286                ae->not = 1;
287                vcc_NextToken(tl);
288        }
289
290        if (tl->t->tok == '(') {
291                ae->para = 1;
292                vcc_NextToken(tl);
293        }
294
295        if (!ae->not && tl->t->tok == '!') {
296                ae->not = 1;
297                vcc_NextToken(tl);
298        }
299
300        ExpectErr(tl, CSTR);
301        ae->t_addr = tl->t;
302        vcc_NextToken(tl);
303
304        if (tl->t->tok == '/') {
305                vcc_NextToken(tl);
306                ae->t_mask = tl->t;
307                ExpectErr(tl, CNUM);
308                ae->mask = vcc_UintVal(tl);
309                vcc_NextToken(tl);
310        }
311
312        if (ae->para) {
313                ExpectErr(tl, ')');
314                vcc_NextToken(tl);
315        }
316
317        if (!vcc_acl_try_netnotation(tl, ae)) {
318                ERRCHK(tl);
319                vcc_acl_try_getaddrinfo(tl, ae);
320        }
321        ERRCHK(tl);
322}
323
324static void
325vcc_acl_bot(const struct tokenlist *tl, const char *acln, int silent, const char *pfx)
326{
327        struct acl_e *ae;
328        int depth, l, m, i;
329        unsigned at[VRT_ACL_MAXADDR + 1];
330        const char *oc;
331        struct sockaddr sa;
332
333        Fh(tl, 0, "\nstatic int\n");
334        Fh(tl, 0, "match_acl_%s_%s(const struct sess *sp, const void *p)\n",
335            pfx, acln);
336        Fh(tl, 0, "{\n");
337        Fh(tl, 0, "\tconst unsigned char *a;\n");
338        assert(sizeof (unsigned char) == 1);
339        assert(sizeof (unsigned short) == 2);
340        assert(sizeof (unsigned int) == 4);
341        if (sizeof sa.sa_family == 1)
342                Fh(tl, 0, "\tunsigned char fam;\n");
343        else if (sizeof sa.sa_family == 2)
344                Fh(tl, 0, "\tunsigned short fam;\n");
345        else if (sizeof sa.sa_family == 4)
346                Fh(tl, 0, "\tunsigned int fam;\n");
347        else
348                assert(0 == __LINE__);
349               
350        Fh(tl, 0, "\n");
351        Fh(tl, 0, "\ta = p;\n");
352        Fh(tl, 0, "\tVRT_memmove(&fam, a + %d, sizeof fam);\n",
353            offsetof(struct sockaddr, sa_family));
354        Fh(tl, 0, "\tif (fam == %d)\n", PF_INET);
355        Fh(tl, 0, "\t\ta += %d;\n", offsetof(struct sockaddr_in, sin_addr));
356        Fh(tl, 0, "\telse if (fam == %d)\n", PF_INET6);
357        Fh(tl, 0, "\t\ta += %d;\n", offsetof(struct sockaddr_in6, sin6_addr));
358        Fh(tl, 0, "\telse {\n");
359        Fh(tl, 0, "\t\tVRT_acl_log(sp, \"NO_FAM %s\");\n", acln);
360        Fh(tl, 0, "\t\treturn(0);\n");
361        Fh(tl, 0, "\t}\n\n");
362        depth = -1;
363        oc = 0;
364        at[0] = 256;
365        VTAILQ_FOREACH(ae, &tl->acl, list) {
366
367                /* Find how much common prefix we have */
368                for (l = 0; l <= depth && l * 8 < ae->mask; l++) {
369                        assert(l >= 0);
370                        if (ae->data[l] != at[l])
371                                break;
372                }
373
374                /* Back down, if necessary */
375                oc = "";
376                while (l <= depth) {
377                        Fh(tl, 0, "\t%*s}\n",
378                            -depth, "");
379                        depth--;
380                        oc = "else ";
381                }
382                m = ae->mask;
383                m -= l * 8;
384                for (i = l; m >= 8; m -= 8, i++) {
385                        if (i == 0) {
386                                Fh(tl, 0, "\t%*s%sif (fam == %d) {\n",
387                                    -i, "", oc, ae->data[i]);
388                        } else {
389                                Fh(tl, 0, "\t%*s%sif (a[%d] == %d) {\n",
390                                    -i, "", oc, i - 1, ae->data[i]);
391                        }
392                        at[i] = ae->data[i];
393                        depth = i;
394                        oc = "";
395                }
396                if (m > 0) {
397                        Fh(tl, 0, "\t%*s%sif ((a[%d] & 0x%x) == %d) {\n",
398                            -i, "",
399                            oc,
400                            i, (0xff00 >> m) & 0xff,
401                            ae->data[i] & ((0xff00 >> m) & 0xff));
402                        at[i] = 256;
403                        depth = i;
404                        oc = "";
405                }
406
407                i = (ae->mask + 7) / 8;
408
409                if (!silent) {
410                        Fh(tl, 0, "\t%*sVRT_acl_log(sp, \"%sMATCH %s \" ",
411                            -i, "",
412                            ae->not ? "NEG_" : "",
413                            acln,
414                            PF(ae->t_addr));
415                        EncToken(tl->fh, ae->t_addr);
416                        if (ae->t_mask != NULL)
417                                Fh(tl, 0, " \"/%.*s\" ", PF(ae->t_mask));
418                        Fh(tl, 0, ");\n");
419                }
420
421                Fh(tl, 0, "\t%*sreturn (%d);\n", -i, "", ae->not ? 0 : 1);
422        }
423
424        for (; 0 <= depth; depth--) 
425                Fh(tl, 0, "\t%*.*s}\n", depth, depth, "");
426        if (!silent)
427                Fh(tl, 0, "\tVRT_acl_log(sp, \"NO_MATCH %s\");\n", acln);
428        Fh(tl, 0, "\treturn (0);\n}\n");
429}
430
431void
432vcc_Cond_Ip(const struct var *vp, struct tokenlist *tl)
433{
434        unsigned tcond;
435        char *acln;
436
437        switch (tl->t->tok) {
438        case '~':
439                vcc_NextToken(tl);
440                ExpectErr(tl, ID);
441                vcc_AddRef(tl, tl->t, R_ACL);
442                Fb(tl, 1, "match_acl_named_%.*s(sp, %s)\n", PF(tl->t), vp->rname);
443                vcc_NextToken(tl);
444                break;
445        case T_EQ:
446        case T_NEQ:
447
448                VTAILQ_INIT(&tl->acl);
449                tcond = tl->t->tok;
450                vcc_NextToken(tl);
451                asprintf(&acln, "%u", tl->cnt);
452                assert(acln != NULL);
453                vcc_acl_entry(tl);
454                vcc_acl_bot(tl, acln, 1, "anon");
455                Fb(tl, 1, "%smatch_acl_anon_%s(sp, %s)\n",
456                    (tcond == T_NEQ ? "!" : ""), acln, vp->rname);
457                free(acln);
458                break;
459        default:
460                vsb_printf(tl->sb, "Invalid condition ");
461                vcc_ErrToken(tl, tl->t);
462                vsb_printf(tl->sb, " on IP number variable\n");
463                vsb_printf(tl->sb, "  only '==', '!=' and '~' are legal\n");
464                vcc_ErrWhere(tl, tl->t);
465                break;
466        }
467}
468
469void
470vcc_Acl(struct tokenlist *tl)
471{
472        struct token *an;
473        char *acln;
474
475        vcc_NextToken(tl);
476        VTAILQ_INIT(&tl->acl);
477
478        ExpectErr(tl, ID);
479        an = tl->t;
480        vcc_NextToken(tl);
481
482        vcc_AddDef(tl, an, R_ACL);
483        asprintf(&acln, "%.*s", PF(an));
484        assert(acln != NULL);
485
486        ExpectErr(tl, '{');
487        vcc_NextToken(tl);
488
489        while (tl->t->tok != '}') {
490                vcc_acl_entry(tl);
491                ERRCHK(tl);
492                ExpectErr(tl, ';');
493                vcc_NextToken(tl);
494        }
495        ExpectErr(tl, '}');
496        vcc_NextToken(tl);
497
498        vcc_acl_bot(tl, acln, 0, "named");
499
500        free(acln);
501}
Note: See TracBrowser for help on using the repository browser.