]> CyberLeo.Net >> Repos - FreeBSD/stable/9.git/blob - contrib/bind9/lib/dns/acl.c
MFC r363988:
[FreeBSD/stable/9.git] / contrib / bind9 / lib / dns / acl.c
1 /*
2  * Copyright (C) 2004-2009, 2011, 2013, 2014  Internet Systems Consortium, Inc. ("ISC")
3  * Copyright (C) 1999-2002  Internet Software Consortium.
4  *
5  * Permission to use, copy, modify, and/or distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
10  * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
11  * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
12  * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
13  * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
14  * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
15  * PERFORMANCE OF THIS SOFTWARE.
16  */
17
18 /* $Id: acl.c,v 1.55 2011/06/17 23:47:49 tbox Exp $ */
19
20 /*! \file */
21
22 #include <config.h>
23
24 #include <isc/mem.h>
25 #include <isc/once.h>
26 #include <isc/string.h>
27 #include <isc/util.h>
28
29 #include <dns/acl.h>
30 #include <dns/iptable.h>
31
32 /*
33  * Create a new ACL, including an IP table and an array with room
34  * for 'n' ACL elements.  The elements are uninitialized and the
35  * length is 0.
36  */
37 isc_result_t
38 dns_acl_create(isc_mem_t *mctx, int n, dns_acl_t **target) {
39         isc_result_t result;
40         dns_acl_t *acl;
41
42         /*
43          * Work around silly limitation of isc_mem_get().
44          */
45         if (n == 0)
46                 n = 1;
47
48         acl = isc_mem_get(mctx, sizeof(*acl));
49         if (acl == NULL)
50                 return (ISC_R_NOMEMORY);
51
52         acl->mctx = NULL;
53         isc_mem_attach(mctx, &acl->mctx);
54
55         acl->name = NULL;
56
57         result = isc_refcount_init(&acl->refcount, 1);
58         if (result != ISC_R_SUCCESS) {
59                 isc_mem_put(mctx, acl, sizeof(*acl));
60                 return (result);
61         }
62
63         result = dns_iptable_create(mctx, &acl->iptable);
64         if (result != ISC_R_SUCCESS) {
65                 isc_mem_put(mctx, acl, sizeof(*acl));
66                 return (result);
67         }
68
69         acl->elements = NULL;
70         acl->alloc = 0;
71         acl->length = 0;
72         acl->has_negatives = ISC_FALSE;
73
74         ISC_LINK_INIT(acl, nextincache);
75         /*
76          * Must set magic early because we use dns_acl_detach() to clean up.
77          */
78         acl->magic = DNS_ACL_MAGIC;
79
80         acl->elements = isc_mem_get(mctx, n * sizeof(dns_aclelement_t));
81         if (acl->elements == NULL) {
82                 result = ISC_R_NOMEMORY;
83                 goto cleanup;
84         }
85         acl->alloc = n;
86         memset(acl->elements, 0, n * sizeof(dns_aclelement_t));
87         *target = acl;
88         return (ISC_R_SUCCESS);
89
90  cleanup:
91         dns_acl_detach(&acl);
92         return (result);
93 }
94
95 /*
96  * Create a new ACL and initialize it with the value "any" or "none",
97  * depending on the value of the "neg" parameter.
98  * "any" is a positive iptable entry with bit length 0.
99  * "none" is the same as "!any".
100  */
101 static isc_result_t
102 dns_acl_anyornone(isc_mem_t *mctx, isc_boolean_t neg, dns_acl_t **target) {
103         isc_result_t result;
104         dns_acl_t *acl = NULL;
105
106         result = dns_acl_create(mctx, 0, &acl);
107         if (result != ISC_R_SUCCESS)
108                 return (result);
109
110         result = dns_iptable_addprefix(acl->iptable, NULL, 0, ISC_TF(!neg));
111         if (result != ISC_R_SUCCESS) {
112                 dns_acl_detach(&acl);
113                 return (result);
114         }
115
116         *target = acl;
117         return (result);
118 }
119
120 /*
121  * Create a new ACL that matches everything.
122  */
123 isc_result_t
124 dns_acl_any(isc_mem_t *mctx, dns_acl_t **target) {
125         return (dns_acl_anyornone(mctx, ISC_FALSE, target));
126 }
127
128 /*
129  * Create a new ACL that matches nothing.
130  */
131 isc_result_t
132 dns_acl_none(isc_mem_t *mctx, dns_acl_t **target) {
133         return (dns_acl_anyornone(mctx, ISC_TRUE, target));
134 }
135
136 /*
137  * If pos is ISC_TRUE, test whether acl is set to "{ any; }"
138  * If pos is ISC_FALSE, test whether acl is set to "{ none; }"
139  */
140 static isc_boolean_t
141 dns_acl_isanyornone(dns_acl_t *acl, isc_boolean_t pos)
142 {
143         /* Should never happen but let's be safe */
144         if (acl == NULL ||
145             acl->iptable == NULL ||
146             acl->iptable->radix == NULL ||
147             acl->iptable->radix->head == NULL ||
148             acl->iptable->radix->head->prefix == NULL)
149                 return (ISC_FALSE);
150
151         if (acl->length != 0 || acl->node_count != 1)
152                 return (ISC_FALSE);
153
154         if (acl->iptable->radix->head->prefix->bitlen == 0 &&
155             acl->iptable->radix->head->data[0] != NULL &&
156             acl->iptable->radix->head->data[0] ==
157                     acl->iptable->radix->head->data[1] &&
158             *(isc_boolean_t *) (acl->iptable->radix->head->data[0]) == pos)
159                 return (ISC_TRUE);
160
161         return (ISC_FALSE); /* All others */
162 }
163
164 /*
165  * Test whether acl is set to "{ any; }"
166  */
167 isc_boolean_t
168 dns_acl_isany(dns_acl_t *acl)
169 {
170         return (dns_acl_isanyornone(acl, ISC_TRUE));
171 }
172
173 /*
174  * Test whether acl is set to "{ none; }"
175  */
176 isc_boolean_t
177 dns_acl_isnone(dns_acl_t *acl)
178 {
179         return (dns_acl_isanyornone(acl, ISC_FALSE));
180 }
181
182 /*
183  * Determine whether a given address or signer matches a given ACL.
184  * For a match with a positive ACL element or iptable radix entry,
185  * return with a positive value in match; for a match with a negated ACL
186  * element or radix entry, return with a negative value in match.
187  */
188 isc_result_t
189 dns_acl_match(const isc_netaddr_t *reqaddr,
190               const dns_name_t *reqsigner,
191               const dns_acl_t *acl,
192               const dns_aclenv_t *env,
193               int *match,
194               const dns_aclelement_t **matchelt)
195 {
196         isc_uint16_t bitlen, family;
197         isc_prefix_t pfx;
198         isc_radix_node_t *node = NULL;
199         const isc_netaddr_t *addr;
200         isc_netaddr_t v4addr;
201         isc_result_t result;
202         int match_num = -1;
203         unsigned int i;
204
205         REQUIRE(reqaddr != NULL);
206         REQUIRE(matchelt == NULL || *matchelt == NULL);
207
208         if (env == NULL || env->match_mapped == ISC_FALSE ||
209             reqaddr->family != AF_INET6 ||
210             !IN6_IS_ADDR_V4MAPPED(&reqaddr->type.in6))
211                 addr = reqaddr;
212         else {
213                 isc_netaddr_fromv4mapped(&v4addr, reqaddr);
214                 addr = &v4addr;
215         }
216
217         /* Always match with host addresses. */
218         family = addr->family;
219         bitlen = family == AF_INET6 ? 128 : 32;
220         NETADDR_TO_PREFIX_T(addr, pfx, bitlen);
221
222         /* Assume no match. */
223         *match = 0;
224
225         /* Search radix. */
226         result = isc_radix_search(acl->iptable->radix, &node, &pfx);
227
228         /* Found a match. */
229         if (result == ISC_R_SUCCESS && node != NULL) {
230                 match_num = node->node_num[ISC_IS6(family)];
231                 if (*(isc_boolean_t *) node->data[ISC_IS6(family)] == ISC_TRUE)
232                         *match = match_num;
233                 else
234                         *match = -match_num;
235         }
236
237         /* Now search non-radix elements for a match with a lower node_num. */
238         for (i = 0; i < acl->length; i++) {
239                 dns_aclelement_t *e = &acl->elements[i];
240
241                 /* Already found a better match? */
242                 if (match_num != -1 && match_num < e->node_num) {
243                         isc_refcount_destroy(&pfx.refcount);
244                         return (ISC_R_SUCCESS);
245                 }
246
247                 if (dns_aclelement_match(reqaddr, reqsigner,
248                                          e, env, matchelt)) {
249                         if (match_num == -1 || e->node_num < match_num) {
250                                 if (e->negative == ISC_TRUE)
251                                         *match = -e->node_num;
252                                 else
253                                         *match = e->node_num;
254                         }
255                         isc_refcount_destroy(&pfx.refcount);
256                         return (ISC_R_SUCCESS);
257                 }
258         }
259
260         isc_refcount_destroy(&pfx.refcount);
261         return (ISC_R_SUCCESS);
262 }
263
264 /*
265  * Merge the contents of one ACL into another.  Call dns_iptable_merge()
266  * for the IP tables, then concatenate the element arrays.
267  *
268  * If pos is set to false, then the nested ACL is to be negated.  This
269  * means reverse the sense of each *positive* element or IP table node,
270  * but leave negatives alone, so as to prevent a double-negative causing
271  * an unexpected positive match in the parent ACL.
272  */
273 isc_result_t
274 dns_acl_merge(dns_acl_t *dest, dns_acl_t *source, isc_boolean_t pos)
275 {
276         isc_result_t result;
277         unsigned int newalloc, nelem, i;
278         int max_node = 0, nodes;
279
280         /* Resize the element array if needed. */
281         if (dest->length + source->length > dest->alloc) {
282                 void *newmem;
283
284                 newalloc = dest->alloc + source->alloc;
285                 if (newalloc < 4)
286                         newalloc = 4;
287
288                 newmem = isc_mem_get(dest->mctx,
289                                      newalloc * sizeof(dns_aclelement_t));
290                 if (newmem == NULL)
291                         return (ISC_R_NOMEMORY);
292
293                 /* Zero. */
294                 memset(newmem, 0, newalloc * sizeof(dns_aclelement_t));
295
296                 /* Copy in the original elements */
297                 memmove(newmem, dest->elements,
298                         dest->length * sizeof(dns_aclelement_t));
299
300                 /* Release the memory for the old elements array */
301                 isc_mem_put(dest->mctx, dest->elements,
302                             dest->alloc * sizeof(dns_aclelement_t));
303                 dest->elements = newmem;
304                 dest->alloc = newalloc;
305         }
306
307         /*
308          * Now copy in the new elements, increasing their node_num
309          * values so as to keep the new ACL consistent.  If we're
310          * negating, then negate positive elements, but keep negative
311          * elements the same for security reasons.
312          */
313         nelem = dest->length;
314         dest->length += source->length;
315         for (i = 0; i < source->length; i++) {
316                 if (source->elements[i].node_num > max_node)
317                         max_node = source->elements[i].node_num;
318
319                 /* Copy type. */
320                 dest->elements[nelem + i].type = source->elements[i].type;
321
322                 /* Adjust node numbering. */
323                 dest->elements[nelem + i].node_num =
324                         source->elements[i].node_num + dest->node_count;
325
326                 /* Duplicate nested acl. */
327                 if (source->elements[i].type == dns_aclelementtype_nestedacl &&
328                    source->elements[i].nestedacl != NULL)
329                         dns_acl_attach(source->elements[i].nestedacl,
330                                        &dest->elements[nelem + i].nestedacl);
331
332                 /* Duplicate key name. */
333                 if (source->elements[i].type == dns_aclelementtype_keyname) {
334                         dns_name_init(&dest->elements[nelem+i].keyname, NULL);
335                         result = dns_name_dup(&source->elements[i].keyname,
336                                               dest->mctx,
337                                               &dest->elements[nelem+i].keyname);
338                         if (result != ISC_R_SUCCESS)
339                                 return result;
340                 }
341
342                 /* reverse sense of positives if this is a negative acl */
343                 if (!pos && source->elements[i].negative == ISC_FALSE) {
344                         dest->elements[nelem + i].negative = ISC_TRUE;
345                 } else {
346                         dest->elements[nelem + i].negative =
347                                 source->elements[i].negative;
348                 }
349         }
350
351         /*
352          * Merge the iptables.  Make sure the destination ACL's
353          * node_count value is set correctly afterward.
354          */
355         nodes = max_node + dest->node_count;
356         result = dns_iptable_merge(dest->iptable, source->iptable, pos);
357         if (result != ISC_R_SUCCESS)
358                 return (result);
359         if (nodes > dest->node_count)
360                 dest->node_count = nodes;
361
362         return (ISC_R_SUCCESS);
363 }
364
365 /*
366  * Like dns_acl_match, but matches against the single ACL element 'e'
367  * rather than a complete ACL, and returns ISC_TRUE iff it matched.
368  *
369  * To determine whether the match was positive or negative, the
370  * caller should examine e->negative.  Since the element 'e' may be
371  * a reference to a named ACL or a nested ACL, a matching element
372  * returned through 'matchelt' is not necessarily 'e' itself.
373  */
374 isc_boolean_t
375 dns_aclelement_match(const isc_netaddr_t *reqaddr,
376                      const dns_name_t *reqsigner,
377                      const dns_aclelement_t *e,
378                      const dns_aclenv_t *env,
379                      const dns_aclelement_t **matchelt)
380 {
381         dns_acl_t *inner = NULL;
382         int indirectmatch;
383         isc_result_t result;
384
385         switch (e->type) {
386         case dns_aclelementtype_keyname:
387                 if (reqsigner != NULL &&
388                     dns_name_equal(reqsigner, &e->keyname)) {
389                         if (matchelt != NULL)
390                                 *matchelt = e;
391                         return (ISC_TRUE);
392                 } else {
393                         return (ISC_FALSE);
394                 }
395
396         case dns_aclelementtype_nestedacl:
397                 inner = e->nestedacl;
398                 break;
399
400         case dns_aclelementtype_localhost:
401                 if (env == NULL || env->localhost == NULL)
402                         return (ISC_FALSE);
403                 inner = env->localhost;
404                 break;
405
406         case dns_aclelementtype_localnets:
407                 if (env == NULL || env->localnets == NULL)
408                         return (ISC_FALSE);
409                 inner = env->localnets;
410                 break;
411
412         default:
413                 /* Should be impossible. */
414                 INSIST(0);
415         }
416
417         result = dns_acl_match(reqaddr, reqsigner, inner, env,
418                                &indirectmatch, matchelt);
419         INSIST(result == ISC_R_SUCCESS);
420
421         /*
422          * Treat negative matches in indirect ACLs as "no match".
423          * That way, a negated indirect ACL will never become a
424          * surprise positive match through double negation.
425          * XXXDCL this should be documented.
426          */
427
428         if (indirectmatch > 0) {
429                 if (matchelt != NULL)
430                         *matchelt = e;
431                 return (ISC_TRUE);
432         }
433
434         /*
435          * A negative indirect match may have set *matchelt, but we don't
436          * want it set when we return.
437          */
438
439         if (matchelt != NULL)
440                 *matchelt = NULL;
441
442         return (ISC_FALSE);
443 }
444
445 void
446 dns_acl_attach(dns_acl_t *source, dns_acl_t **target) {
447         REQUIRE(DNS_ACL_VALID(source));
448
449         isc_refcount_increment(&source->refcount, NULL);
450         *target = source;
451 }
452
453 static void
454 destroy(dns_acl_t *dacl) {
455         unsigned int i;
456
457         INSIST(!ISC_LINK_LINKED(dacl, nextincache));
458
459         for (i = 0; i < dacl->length; i++) {
460                 dns_aclelement_t *de = &dacl->elements[i];
461                 if (de->type == dns_aclelementtype_keyname) {
462                         dns_name_free(&de->keyname, dacl->mctx);
463                 } else if (de->type == dns_aclelementtype_nestedacl) {
464                         dns_acl_detach(&de->nestedacl);
465                 }
466         }
467         if (dacl->elements != NULL)
468                 isc_mem_put(dacl->mctx, dacl->elements,
469                             dacl->alloc * sizeof(dns_aclelement_t));
470         if (dacl->name != NULL)
471                 isc_mem_free(dacl->mctx, dacl->name);
472         if (dacl->iptable != NULL)
473                 dns_iptable_detach(&dacl->iptable);
474         isc_refcount_destroy(&dacl->refcount);
475         dacl->magic = 0;
476         isc_mem_putanddetach(&dacl->mctx, dacl, sizeof(*dacl));
477 }
478
479 void
480 dns_acl_detach(dns_acl_t **aclp) {
481         dns_acl_t *acl = *aclp;
482         unsigned int refs;
483
484         REQUIRE(DNS_ACL_VALID(acl));
485
486         isc_refcount_decrement(&acl->refcount, &refs);
487         if (refs == 0)
488                 destroy(acl);
489         *aclp = NULL;
490 }
491
492
493 static isc_once_t       insecure_prefix_once = ISC_ONCE_INIT;
494 static isc_mutex_t      insecure_prefix_lock;
495 static isc_boolean_t    insecure_prefix_found;
496
497 static void
498 initialize_action(void) {
499         RUNTIME_CHECK(isc_mutex_init(&insecure_prefix_lock) == ISC_R_SUCCESS);
500 }
501
502 /*
503  * Called via isc_radix_walk() to find IP table nodes that are
504  * insecure.
505  */
506 static void
507 is_insecure(isc_prefix_t *prefix, void **data) {
508         isc_boolean_t secure;
509         int bitlen, family;
510
511         bitlen = prefix->bitlen;
512         family = prefix->family;
513
514         /* Negated entries are always secure. */
515         secure = * (isc_boolean_t *)data[ISC_IS6(family)];
516         if (!secure) {
517                 return;
518         }
519
520         /* If loopback prefix found, return */
521         switch (family) {
522         case AF_INET:
523                 if (bitlen == 32 &&
524                     htonl(prefix->add.sin.s_addr) == INADDR_LOOPBACK)
525                         return;
526                 break;
527         case AF_INET6:
528                 if (bitlen == 128 && IN6_IS_ADDR_LOOPBACK(&prefix->add.sin6))
529                         return;
530                 break;
531         default:
532                 break;
533         }
534
535         /* Non-negated, non-loopback */
536         insecure_prefix_found = ISC_TRUE;       /* LOCKED */
537         return;
538 }
539
540 /*
541  * Return ISC_TRUE iff the acl 'a' is considered insecure, that is,
542  * if it contains IP addresses other than those of the local host.
543  * This is intended for applications such as printing warning
544  * messages for suspect ACLs; it is not intended for making access
545  * control decisions.  We make no guarantee that an ACL for which
546  * this function returns ISC_FALSE is safe.
547  */
548 isc_boolean_t
549 dns_acl_isinsecure(const dns_acl_t *a) {
550         unsigned int i;
551         isc_boolean_t insecure;
552
553         RUNTIME_CHECK(isc_once_do(&insecure_prefix_once,
554                                   initialize_action) == ISC_R_SUCCESS);
555
556         /*
557          * Walk radix tree to find out if there are any non-negated,
558          * non-loopback prefixes.
559          */
560         LOCK(&insecure_prefix_lock);
561         insecure_prefix_found = ISC_FALSE;
562         isc_radix_process(a->iptable->radix, is_insecure);
563         insecure = insecure_prefix_found;
564         UNLOCK(&insecure_prefix_lock);
565         if (insecure)
566                 return(ISC_TRUE);
567
568         /* Now check non-radix elements */
569         for (i = 0; i < a->length; i++) {
570                 dns_aclelement_t *e = &a->elements[i];
571
572                 /* A negated match can never be insecure. */
573                 if (e->negative)
574                         continue;
575
576                 switch (e->type) {
577                 case dns_aclelementtype_keyname:
578                 case dns_aclelementtype_localhost:
579                         continue;
580
581                 case dns_aclelementtype_nestedacl:
582                         if (dns_acl_isinsecure(e->nestedacl))
583                                 return (ISC_TRUE);
584                         continue;
585
586                 case dns_aclelementtype_localnets:
587                         return (ISC_TRUE);
588
589                 default:
590                         INSIST(0);
591                         return (ISC_TRUE);
592                 }
593         }
594
595         /* No insecure elements were found. */
596         return (ISC_FALSE);
597 }
598
599 /*
600  * Initialize ACL environment, setting up localhost and localnets ACLs
601  */
602 isc_result_t
603 dns_aclenv_init(isc_mem_t *mctx, dns_aclenv_t *env) {
604         isc_result_t result;
605
606         env->localhost = NULL;
607         env->localnets = NULL;
608         result = dns_acl_create(mctx, 0, &env->localhost);
609         if (result != ISC_R_SUCCESS)
610                 goto cleanup_nothing;
611         result = dns_acl_create(mctx, 0, &env->localnets);
612         if (result != ISC_R_SUCCESS)
613                 goto cleanup_localhost;
614         env->match_mapped = ISC_FALSE;
615         return (ISC_R_SUCCESS);
616
617  cleanup_localhost:
618         dns_acl_detach(&env->localhost);
619  cleanup_nothing:
620         return (result);
621 }
622
623 void
624 dns_aclenv_copy(dns_aclenv_t *t, dns_aclenv_t *s) {
625         dns_acl_detach(&t->localhost);
626         dns_acl_attach(s->localhost, &t->localhost);
627         dns_acl_detach(&t->localnets);
628         dns_acl_attach(s->localnets, &t->localnets);
629         t->match_mapped = s->match_mapped;
630 }
631
632 void
633 dns_aclenv_destroy(dns_aclenv_t *env) {
634         dns_acl_detach(&env->localhost);
635         dns_acl_detach(&env->localnets);
636 }