]> CyberLeo.Net >> Repos - FreeBSD/releng/9.3.git/blob - contrib/bind9/lib/dns/acl.c
Copy stable/9 to releng/9.3 as part of the 9.3-RELEASE cycle.
[FreeBSD/releng/9.3.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                 /* Copy in the original elements */
294                 memmove(newmem, dest->elements,
295                         dest->length * sizeof(dns_aclelement_t));
296
297                 /* Release the memory for the old elements array */
298                 isc_mem_put(dest->mctx, dest->elements,
299                             dest->alloc * sizeof(dns_aclelement_t));
300                 dest->elements = newmem;
301                 dest->alloc = newalloc;
302         }
303
304         /*
305          * Now copy in the new elements, increasing their node_num
306          * values so as to keep the new ACL consistent.  If we're
307          * negating, then negate positive elements, but keep negative
308          * elements the same for security reasons.
309          */
310         nelem = dest->length;
311         dest->length += source->length;
312         for (i = 0; i < source->length; i++) {
313                 if (source->elements[i].node_num > max_node)
314                         max_node = source->elements[i].node_num;
315
316                 /* Copy type. */
317                 dest->elements[nelem + i].type = source->elements[i].type;
318
319                 /* Adjust node numbering. */
320                 dest->elements[nelem + i].node_num =
321                         source->elements[i].node_num + dest->node_count;
322
323                 /* Duplicate nested acl. */
324                 if (source->elements[i].type == dns_aclelementtype_nestedacl &&
325                    source->elements[i].nestedacl != NULL)
326                         dns_acl_attach(source->elements[i].nestedacl,
327                                        &dest->elements[nelem + i].nestedacl);
328
329                 /* Duplicate key name. */
330                 if (source->elements[i].type == dns_aclelementtype_keyname) {
331                         dns_name_init(&dest->elements[nelem+i].keyname, NULL);
332                         result = dns_name_dup(&source->elements[i].keyname,
333                                               dest->mctx,
334                                               &dest->elements[nelem+i].keyname);
335                         if (result != ISC_R_SUCCESS)
336                                 return result;
337                 }
338
339                 /* reverse sense of positives if this is a negative acl */
340                 if (!pos && source->elements[i].negative == ISC_FALSE) {
341                         dest->elements[nelem + i].negative = ISC_TRUE;
342                 } else {
343                         dest->elements[nelem + i].negative =
344                                 source->elements[i].negative;
345                 }
346         }
347
348         /*
349          * Merge the iptables.  Make sure the destination ACL's
350          * node_count value is set correctly afterward.
351          */
352         nodes = max_node + dest->node_count;
353         result = dns_iptable_merge(dest->iptable, source->iptable, pos);
354         if (result != ISC_R_SUCCESS)
355                 return (result);
356         if (nodes > dest->node_count)
357                 dest->node_count = nodes;
358
359         return (ISC_R_SUCCESS);
360 }
361
362 /*
363  * Like dns_acl_match, but matches against the single ACL element 'e'
364  * rather than a complete ACL, and returns ISC_TRUE iff it matched.
365  *
366  * To determine whether the match was positive or negative, the
367  * caller should examine e->negative.  Since the element 'e' may be
368  * a reference to a named ACL or a nested ACL, a matching element
369  * returned through 'matchelt' is not necessarily 'e' itself.
370  */
371 isc_boolean_t
372 dns_aclelement_match(const isc_netaddr_t *reqaddr,
373                      const dns_name_t *reqsigner,
374                      const dns_aclelement_t *e,
375                      const dns_aclenv_t *env,
376                      const dns_aclelement_t **matchelt)
377 {
378         dns_acl_t *inner = NULL;
379         int indirectmatch;
380         isc_result_t result;
381
382         switch (e->type) {
383         case dns_aclelementtype_keyname:
384                 if (reqsigner != NULL &&
385                     dns_name_equal(reqsigner, &e->keyname)) {
386                         if (matchelt != NULL)
387                                 *matchelt = e;
388                         return (ISC_TRUE);
389                 } else {
390                         return (ISC_FALSE);
391                 }
392
393         case dns_aclelementtype_nestedacl:
394                 inner = e->nestedacl;
395                 break;
396
397         case dns_aclelementtype_localhost:
398                 if (env == NULL || env->localhost == NULL)
399                         return (ISC_FALSE);
400                 inner = env->localhost;
401                 break;
402
403         case dns_aclelementtype_localnets:
404                 if (env == NULL || env->localnets == NULL)
405                         return (ISC_FALSE);
406                 inner = env->localnets;
407                 break;
408
409         default:
410                 /* Should be impossible. */
411                 INSIST(0);
412         }
413
414         result = dns_acl_match(reqaddr, reqsigner, inner, env,
415                                &indirectmatch, matchelt);
416         INSIST(result == ISC_R_SUCCESS);
417
418         /*
419          * Treat negative matches in indirect ACLs as "no match".
420          * That way, a negated indirect ACL will never become a
421          * surprise positive match through double negation.
422          * XXXDCL this should be documented.
423          */
424
425         if (indirectmatch > 0) {
426                 if (matchelt != NULL)
427                         *matchelt = e;
428                 return (ISC_TRUE);
429         }
430
431         /*
432          * A negative indirect match may have set *matchelt, but we don't
433          * want it set when we return.
434          */
435
436         if (matchelt != NULL)
437                 *matchelt = NULL;
438
439         return (ISC_FALSE);
440 }
441
442 void
443 dns_acl_attach(dns_acl_t *source, dns_acl_t **target) {
444         REQUIRE(DNS_ACL_VALID(source));
445
446         isc_refcount_increment(&source->refcount, NULL);
447         *target = source;
448 }
449
450 static void
451 destroy(dns_acl_t *dacl) {
452         unsigned int i;
453
454         INSIST(!ISC_LINK_LINKED(dacl, nextincache));
455
456         for (i = 0; i < dacl->length; i++) {
457                 dns_aclelement_t *de = &dacl->elements[i];
458                 if (de->type == dns_aclelementtype_keyname) {
459                         dns_name_free(&de->keyname, dacl->mctx);
460                 } else if (de->type == dns_aclelementtype_nestedacl) {
461                         dns_acl_detach(&de->nestedacl);
462                 }
463         }
464         if (dacl->elements != NULL)
465                 isc_mem_put(dacl->mctx, dacl->elements,
466                             dacl->alloc * sizeof(dns_aclelement_t));
467         if (dacl->name != NULL)
468                 isc_mem_free(dacl->mctx, dacl->name);
469         if (dacl->iptable != NULL)
470                 dns_iptable_detach(&dacl->iptable);
471         isc_refcount_destroy(&dacl->refcount);
472         dacl->magic = 0;
473         isc_mem_putanddetach(&dacl->mctx, dacl, sizeof(*dacl));
474 }
475
476 void
477 dns_acl_detach(dns_acl_t **aclp) {
478         dns_acl_t *acl = *aclp;
479         unsigned int refs;
480
481         REQUIRE(DNS_ACL_VALID(acl));
482
483         isc_refcount_decrement(&acl->refcount, &refs);
484         if (refs == 0)
485                 destroy(acl);
486         *aclp = NULL;
487 }
488
489
490 static isc_once_t       insecure_prefix_once = ISC_ONCE_INIT;
491 static isc_mutex_t      insecure_prefix_lock;
492 static isc_boolean_t    insecure_prefix_found;
493
494 static void
495 initialize_action(void) {
496         RUNTIME_CHECK(isc_mutex_init(&insecure_prefix_lock) == ISC_R_SUCCESS);
497 }
498
499 /*
500  * Called via isc_radix_walk() to find IP table nodes that are
501  * insecure.
502  */
503 static void
504 is_insecure(isc_prefix_t *prefix, void **data) {
505         isc_boolean_t secure;
506         int bitlen, family;
507
508         bitlen = prefix->bitlen;
509         family = prefix->family;
510
511         /* Negated entries are always secure. */
512         secure = * (isc_boolean_t *)data[ISC_IS6(family)];
513         if (!secure) {
514                 return;
515         }
516
517         /* If loopback prefix found, return */
518         switch (family) {
519         case AF_INET:
520                 if (bitlen == 32 &&
521                     htonl(prefix->add.sin.s_addr) == INADDR_LOOPBACK)
522                         return;
523                 break;
524         case AF_INET6:
525                 if (bitlen == 128 && IN6_IS_ADDR_LOOPBACK(&prefix->add.sin6))
526                         return;
527                 break;
528         default:
529                 break;
530         }
531
532         /* Non-negated, non-loopback */
533         insecure_prefix_found = ISC_TRUE;       /* LOCKED */
534         return;
535 }
536
537 /*
538  * Return ISC_TRUE iff the acl 'a' is considered insecure, that is,
539  * if it contains IP addresses other than those of the local host.
540  * This is intended for applications such as printing warning
541  * messages for suspect ACLs; it is not intended for making access
542  * control decisions.  We make no guarantee that an ACL for which
543  * this function returns ISC_FALSE is safe.
544  */
545 isc_boolean_t
546 dns_acl_isinsecure(const dns_acl_t *a) {
547         unsigned int i;
548         isc_boolean_t insecure;
549
550         RUNTIME_CHECK(isc_once_do(&insecure_prefix_once,
551                                   initialize_action) == ISC_R_SUCCESS);
552
553         /*
554          * Walk radix tree to find out if there are any non-negated,
555          * non-loopback prefixes.
556          */
557         LOCK(&insecure_prefix_lock);
558         insecure_prefix_found = ISC_FALSE;
559         isc_radix_process(a->iptable->radix, is_insecure);
560         insecure = insecure_prefix_found;
561         UNLOCK(&insecure_prefix_lock);
562         if (insecure)
563                 return(ISC_TRUE);
564
565         /* Now check non-radix elements */
566         for (i = 0; i < a->length; i++) {
567                 dns_aclelement_t *e = &a->elements[i];
568
569                 /* A negated match can never be insecure. */
570                 if (e->negative)
571                         continue;
572
573                 switch (e->type) {
574                 case dns_aclelementtype_keyname:
575                 case dns_aclelementtype_localhost:
576                         continue;
577
578                 case dns_aclelementtype_nestedacl:
579                         if (dns_acl_isinsecure(e->nestedacl))
580                                 return (ISC_TRUE);
581                         continue;
582
583                 case dns_aclelementtype_localnets:
584                         return (ISC_TRUE);
585
586                 default:
587                         INSIST(0);
588                         return (ISC_TRUE);
589                 }
590         }
591
592         /* No insecure elements were found. */
593         return (ISC_FALSE);
594 }
595
596 /*
597  * Initialize ACL environment, setting up localhost and localnets ACLs
598  */
599 isc_result_t
600 dns_aclenv_init(isc_mem_t *mctx, dns_aclenv_t *env) {
601         isc_result_t result;
602
603         env->localhost = NULL;
604         env->localnets = NULL;
605         result = dns_acl_create(mctx, 0, &env->localhost);
606         if (result != ISC_R_SUCCESS)
607                 goto cleanup_nothing;
608         result = dns_acl_create(mctx, 0, &env->localnets);
609         if (result != ISC_R_SUCCESS)
610                 goto cleanup_localhost;
611         env->match_mapped = ISC_FALSE;
612         return (ISC_R_SUCCESS);
613
614  cleanup_localhost:
615         dns_acl_detach(&env->localhost);
616  cleanup_nothing:
617         return (result);
618 }
619
620 void
621 dns_aclenv_copy(dns_aclenv_t *t, dns_aclenv_t *s) {
622         dns_acl_detach(&t->localhost);
623         dns_acl_attach(s->localhost, &t->localhost);
624         dns_acl_detach(&t->localnets);
625         dns_acl_attach(s->localnets, &t->localnets);
626         t->match_mapped = s->match_mapped;
627 }
628
629 void
630 dns_aclenv_destroy(dns_aclenv_t *env) {
631         dns_acl_detach(&env->localhost);
632         dns_acl_detach(&env->localnets);
633 }