Detect FPU by checking CPUID features.
[dragonfly.git] / contrib / bind-9.5.2 / lib / dns / acl.c
1 /*
2  * Copyright (C) 2004-2009  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.37.2.14 2009/01/19 23:47:02 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         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         result = dns_acl_create(mctx, 0, &acl);
103         if (result != ISC_R_SUCCESS)
104                 return (result);
105
106         result = dns_iptable_addprefix(acl->iptable, NULL, 0, ISC_TF(!neg));
107         if (result != ISC_R_SUCCESS) {
108                 dns_acl_detach(&acl);
109                 return (result);
110         }
111
112         *target = acl;
113         return (result);
114 }
115
116 /*
117  * Create a new ACL that matches everything.
118  */
119 isc_result_t
120 dns_acl_any(isc_mem_t *mctx, dns_acl_t **target) {
121         return (dns_acl_anyornone(mctx, ISC_FALSE, target));
122 }
123
124 /*
125  * Create a new ACL that matches nothing.
126  */
127 isc_result_t
128 dns_acl_none(isc_mem_t *mctx, dns_acl_t **target) {
129         return (dns_acl_anyornone(mctx, ISC_TRUE, target));
130 }
131
132 /*
133  * If pos is ISC_TRUE, test whether acl is set to "{ any; }"
134  * If pos is ISC_FALSE, test whether acl is set to "{ none; }"
135  */
136 static isc_boolean_t
137 dns_acl_isanyornone(dns_acl_t *acl, isc_boolean_t pos)
138 {
139         /* Should never happen but let's be safe */
140         if (acl == NULL ||
141             acl->iptable == NULL ||
142             acl->iptable->radix == NULL ||
143             acl->iptable->radix->head == NULL ||
144             acl->iptable->radix->head->prefix == NULL)
145                 return (ISC_FALSE);
146
147         if (acl->length != 0 || acl->node_count != 1)
148                 return (ISC_FALSE);
149
150         if (acl->iptable->radix->head->prefix->bitlen == 0 &&
151             acl->iptable->radix->head->data[0] != NULL &&
152             acl->iptable->radix->head->data[0] ==
153                     acl->iptable->radix->head->data[1] &&
154             *(isc_boolean_t *) (acl->iptable->radix->head->data[0]) == pos)
155                 return (ISC_TRUE);
156
157         return (ISC_FALSE); /* All others */
158 }
159
160 /*
161  * Test whether acl is set to "{ any; }"
162  */
163 isc_boolean_t
164 dns_acl_isany(dns_acl_t *acl)
165 {
166         return (dns_acl_isanyornone(acl, ISC_TRUE));
167 }
168
169 /*
170  * Test whether acl is set to "{ none; }"
171  */
172 isc_boolean_t
173 dns_acl_isnone(dns_acl_t *acl)
174 {
175         return (dns_acl_isanyornone(acl, ISC_FALSE));
176 }
177
178 /*
179  * Determine whether a given address or signer matches a given ACL.
180  * For a match with a positive ACL element or iptable radix entry,
181  * return with a positive value in match; for a match with a negated ACL
182  * element or radix entry, return with a negative value in match.
183  */
184 isc_result_t
185 dns_acl_match(const isc_netaddr_t *reqaddr,
186               const dns_name_t *reqsigner,
187               const dns_acl_t *acl,
188               const dns_aclenv_t *env,
189               int *match,
190               const dns_aclelement_t **matchelt)
191 {
192         isc_uint16_t bitlen, family;
193         isc_prefix_t pfx;
194         isc_radix_node_t *node = NULL;
195         const isc_netaddr_t *addr;
196         isc_netaddr_t v4addr;
197         isc_result_t result;
198         int match_num = -1;
199         unsigned int i;
200
201         REQUIRE(reqaddr != NULL);
202         REQUIRE(matchelt == NULL || *matchelt == NULL);
203
204         if (env == NULL || env->match_mapped == ISC_FALSE ||
205             reqaddr->family != AF_INET6 ||
206             !IN6_IS_ADDR_V4MAPPED(&reqaddr->type.in6))
207                 addr = reqaddr;
208         else {
209                 isc_netaddr_fromv4mapped(&v4addr, reqaddr);
210                 addr = &v4addr;
211         }
212
213         /* Always match with host addresses. */
214         family = addr->family;
215         bitlen = family == AF_INET6 ? 128 : 32;
216         NETADDR_TO_PREFIX_T(addr, pfx, bitlen);
217
218         /* Assume no match. */
219         *match = 0;
220
221         /* Search radix. */
222         result = isc_radix_search(acl->iptable->radix, &node, &pfx);
223
224         /* Found a match. */
225         if (result == ISC_R_SUCCESS && node != NULL) {
226                 match_num = node->node_num[ISC_IS6(family)];
227                 if (*(isc_boolean_t *) node->data[ISC_IS6(family)] == ISC_TRUE)
228                         *match = match_num;
229                 else
230                         *match = -match_num;
231         }
232
233         /* Now search non-radix elements for a match with a lower node_num. */
234         for (i = 0; i < acl->length; i++) {
235                 dns_aclelement_t *e = &acl->elements[i];
236
237                 /* Already found a better match? */
238                 if (match_num != -1 && match_num < e->node_num) {
239                         isc_refcount_destroy(&pfx.refcount);
240                         return (ISC_R_SUCCESS);
241                 }
242
243                 if (dns_aclelement_match(reqaddr, reqsigner,
244                                          e, env, matchelt)) {
245                         if (match_num == -1 || e->node_num < match_num) {
246                                 if (e->negative == ISC_TRUE)
247                                         *match = -e->node_num;
248                                 else
249                                         *match = e->node_num;
250                         }
251                         isc_refcount_destroy(&pfx.refcount);
252                         return (ISC_R_SUCCESS);
253                 }
254         }
255
256         isc_refcount_destroy(&pfx.refcount);
257         return (ISC_R_SUCCESS);
258 }
259
260 /*
261  * Merge the contents of one ACL into another.  Call dns_iptable_merge()
262  * for the IP tables, then concatenate the element arrays.
263  *
264  * If pos is set to false, then the nested ACL is to be negated.  This
265  * means reverse the sense of each *positive* element or IP table node,
266  * but leave negatives alone, so as to prevent a double-negative causing
267  * an unexpected positive match in the parent ACL.
268  */
269 isc_result_t
270 dns_acl_merge(dns_acl_t *dest, dns_acl_t *source, isc_boolean_t pos)
271 {
272         isc_result_t result;
273         unsigned int newalloc, nelem, i;
274         int max_node = 0, nodes;
275
276         /* Resize the element array if needed. */
277         if (dest->length + source->length > dest->alloc) {
278                 void *newmem;
279
280                 newalloc = dest->alloc + source->alloc;
281                 if (newalloc < 4)
282                         newalloc = 4;
283
284                 newmem = isc_mem_get(dest->mctx,
285                                      newalloc * sizeof(dns_aclelement_t));
286                 if (newmem == NULL)
287                         return (ISC_R_NOMEMORY);
288
289                 /* Copy in the original elements */
290                 memcpy(newmem, dest->elements,
291                        dest->length * sizeof(dns_aclelement_t));
292
293                 /* Release the memory for the old elements array */
294                 isc_mem_put(dest->mctx, dest->elements,
295                             dest->alloc * sizeof(dns_aclelement_t));
296                 dest->elements = newmem;
297                 dest->alloc = newalloc;
298         }
299
300         /*
301          * Now copy in the new elements, increasing their node_num
302          * values so as to keep the new ACL consistent.  If we're
303          * negating, then negate positive elements, but keep negative
304          * elements the same for security reasons.
305          */
306         nelem = dest->length;
307         dest->length += source->length;
308         for (i = 0; i < source->length; i++) {
309                 if (source->elements[i].node_num > max_node)
310                         max_node = source->elements[i].node_num;
311
312                 /* Copy type. */
313                 dest->elements[nelem + i].type = source->elements[i].type;
314
315                 /* Adjust node numbering. */
316                 dest->elements[nelem + i].node_num =
317                         source->elements[i].node_num + dest->node_count;
318
319                 /* Duplicate nested acl. */
320                 if (source->elements[i].type == dns_aclelementtype_nestedacl &&
321                    source->elements[i].nestedacl != NULL)
322                         dns_acl_attach(source->elements[i].nestedacl,
323                                        &dest->elements[nelem + i].nestedacl);
324
325                 /* Duplicate key name. */
326                 if (source->elements[i].type == dns_aclelementtype_keyname) {
327                         dns_name_init(&dest->elements[nelem+i].keyname, NULL);
328                         result = dns_name_dup(&source->elements[i].keyname,
329                                               dest->mctx,
330                                               &dest->elements[nelem+i].keyname);
331                         if (result != ISC_R_SUCCESS)
332                                 return result;
333                 }
334
335                 /* reverse sense of positives if this is a negative acl */
336                 if (!pos && source->elements[i].negative == ISC_FALSE) {
337                         dest->elements[nelem + i].negative = ISC_TRUE;
338                 } else {
339                         dest->elements[nelem + i].negative =
340                                 source->elements[i].negative;
341                 }
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         isc_refcount_increment(&source->refcount, NULL);
443         *target = source;
444 }
445
446 static void
447 destroy(dns_acl_t *dacl) {
448         unsigned int i;
449         for (i = 0; i < dacl->length; i++) {
450                 dns_aclelement_t *de = &dacl->elements[i];
451                 if (de->type == dns_aclelementtype_keyname) {
452                         dns_name_free(&de->keyname, dacl->mctx);
453                 } else if (de->type == dns_aclelementtype_nestedacl) {
454                         dns_acl_detach(&de->nestedacl);
455                 }
456         }
457         if (dacl->elements != NULL)
458                 isc_mem_put(dacl->mctx, dacl->elements,
459                             dacl->alloc * sizeof(dns_aclelement_t));
460         if (dacl->name != NULL)
461                 isc_mem_free(dacl->mctx, dacl->name);
462         if (dacl->iptable != NULL)
463                 dns_iptable_detach(&dacl->iptable);
464         isc_refcount_destroy(&dacl->refcount);
465         dacl->magic = 0;
466         isc_mem_put(dacl->mctx, dacl, sizeof(*dacl));
467 }
468
469 void
470 dns_acl_detach(dns_acl_t **aclp) {
471         dns_acl_t *acl = *aclp;
472         unsigned int refs;
473         REQUIRE(DNS_ACL_VALID(acl));
474         isc_refcount_decrement(&acl->refcount, &refs);
475         if (refs == 0)
476                 destroy(acl);
477         *aclp = NULL;
478 }
479
480
481 static isc_once_t       insecure_prefix_once = ISC_ONCE_INIT;
482 static isc_mutex_t      insecure_prefix_lock;
483 static isc_boolean_t    insecure_prefix_found;
484
485 static void
486 initialize_action(void) {
487         RUNTIME_CHECK(isc_mutex_init(&insecure_prefix_lock) == ISC_R_SUCCESS);
488 }
489
490 /*
491  * Called via isc_radix_walk() to find IP table nodes that are
492  * insecure.
493  */
494 static void
495 is_insecure(isc_prefix_t *prefix, void **data) {
496         isc_boolean_t secure;
497         int bitlen, family;
498
499         bitlen = prefix->bitlen;
500         family = prefix->family;
501
502         /* Negated entries are always secure. */
503         secure = * (isc_boolean_t *)data[ISC_IS6(family)];
504         if (!secure) {
505                 return;
506         }
507
508         /* If loopback prefix found, return */
509         switch (family) {
510         case AF_INET:
511                 if (bitlen == 32 &&
512                     htonl(prefix->add.sin.s_addr) == INADDR_LOOPBACK)
513                         return;
514                 break;
515         case AF_INET6:
516                 if (bitlen == 128 && IN6_IS_ADDR_LOOPBACK(&prefix->add.sin6))
517                         return;
518                 break;
519         default:
520                 break;
521         }
522
523         /* Non-negated, non-loopback */
524         insecure_prefix_found = ISC_TRUE;       /* LOCKED */
525         return;
526 }
527
528 /*
529  * Return ISC_TRUE iff the acl 'a' is considered insecure, that is,
530  * if it contains IP addresses other than those of the local host.
531  * This is intended for applications such as printing warning
532  * messages for suspect ACLs; it is not intended for making access
533  * control decisions.  We make no guarantee that an ACL for which
534  * this function returns ISC_FALSE is safe.
535  */
536 isc_boolean_t
537 dns_acl_isinsecure(const dns_acl_t *a) {
538         unsigned int i;
539         isc_boolean_t insecure;
540
541         RUNTIME_CHECK(isc_once_do(&insecure_prefix_once,
542                                   initialize_action) == ISC_R_SUCCESS);
543
544         /*
545          * Walk radix tree to find out if there are any non-negated,
546          * non-loopback prefixes.
547          */
548         LOCK(&insecure_prefix_lock);
549         insecure_prefix_found = ISC_FALSE;
550         isc_radix_process(a->iptable->radix, is_insecure);
551         insecure = insecure_prefix_found;
552         UNLOCK(&insecure_prefix_lock);
553         if (insecure)
554                 return(ISC_TRUE);
555
556         /* Now check non-radix elements */
557         for (i = 0; i < a->length; i++) {
558                 dns_aclelement_t *e = &a->elements[i];
559
560                 /* A negated match can never be insecure. */
561                 if (e->negative)
562                         continue;
563
564                 switch (e->type) {
565                 case dns_aclelementtype_keyname:
566                 case dns_aclelementtype_localhost:
567                         continue;
568
569                 case dns_aclelementtype_nestedacl:
570                         if (dns_acl_isinsecure(e->nestedacl))
571                                 return (ISC_TRUE);
572                         continue;
573
574                 case dns_aclelementtype_localnets:
575                         return (ISC_TRUE);
576
577                 default:
578                         INSIST(0);
579                         return (ISC_TRUE);
580                 }
581         }
582
583         /* No insecure elements were found. */
584         return (ISC_FALSE);
585 }
586
587 /*
588  * Initialize ACL environment, setting up localhost and localnets ACLs
589  */
590 isc_result_t
591 dns_aclenv_init(isc_mem_t *mctx, dns_aclenv_t *env) {
592         isc_result_t result;
593         env->localhost = NULL;
594         env->localnets = NULL;
595         result = dns_acl_create(mctx, 0, &env->localhost);
596         if (result != ISC_R_SUCCESS)
597                 goto cleanup_nothing;
598         result = dns_acl_create(mctx, 0, &env->localnets);
599         if (result != ISC_R_SUCCESS)
600                 goto cleanup_localhost;
601         env->match_mapped = ISC_FALSE;
602         return (ISC_R_SUCCESS);
603
604  cleanup_localhost:
605         dns_acl_detach(&env->localhost);
606  cleanup_nothing:
607         return (result);
608 }
609
610 void
611 dns_aclenv_copy(dns_aclenv_t *t, dns_aclenv_t *s) {
612         dns_acl_detach(&t->localhost);
613         dns_acl_attach(s->localhost, &t->localhost);
614         dns_acl_detach(&t->localnets);
615         dns_acl_attach(s->localnets, &t->localnets);
616         t->match_mapped = s->match_mapped;
617 }
618
619 void
620 dns_aclenv_destroy(dns_aclenv_t *env) {
621         dns_acl_detach(&env->localhost);
622         dns_acl_detach(&env->localnets);
623 }