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