Change the kernel dev_t, representing a pointer to a specinfo structure,
[dragonfly.git] / contrib / bind-9.2.4rc7 / lib / dns / acl.c
1 /*
2  * Copyright (C) 2004  Internet Systems Consortium, Inc. ("ISC")
3  * Copyright (C) 1999-2001  Internet Software Consortium.
4  *
5  * Permission to use, copy, modify, and 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.23.2.1 2004/03/09 06:10:59 marka Exp $ */
19
20 #include <config.h>
21
22 #include <isc/mem.h>
23 #include <isc/string.h>
24 #include <isc/util.h>
25
26 #include <dns/acl.h>
27
28 isc_result_t
29 dns_acl_create(isc_mem_t *mctx, int n, dns_acl_t **target) {
30         isc_result_t result;
31         dns_acl_t *acl;
32
33         /*
34          * Work around silly limitation of isc_mem_get().
35          */
36         if (n == 0)
37                 n = 1;
38
39         acl = isc_mem_get(mctx, sizeof(*acl));
40         if (acl == NULL)
41                 return (ISC_R_NOMEMORY);
42         acl->mctx = mctx;
43         acl->name = NULL;
44         isc_refcount_init(&acl->refcount, 1);
45         acl->elements = NULL;
46         acl->alloc = 0;
47         acl->length = 0;
48
49         ISC_LINK_INIT(acl, nextincache);
50         /*
51          * Must set magic early because we use dns_acl_detach() to clean up.
52          */
53         acl->magic = DNS_ACL_MAGIC;
54
55         acl->elements = isc_mem_get(mctx, n * sizeof(dns_aclelement_t));
56         if (acl->elements == NULL) {
57                 result = ISC_R_NOMEMORY;
58                 goto cleanup;
59         }
60         acl->alloc = n;
61         memset(acl->elements, 0, n * sizeof(dns_aclelement_t));
62         *target = acl;
63         return (ISC_R_SUCCESS);
64
65  cleanup:
66         dns_acl_detach(&acl);
67         return (result);
68 }
69
70 isc_result_t
71 dns_acl_appendelement(dns_acl_t *acl, dns_aclelement_t *elt) {
72         if (acl->length + 1 > acl->alloc) {
73                 /*
74                  * Resize the ACL.
75                  */
76                 unsigned int newalloc;
77                 void *newmem;
78
79                 newalloc = acl->alloc * 2;
80                 if (newalloc < 4)
81                         newalloc = 4;
82                 newmem = isc_mem_get(acl->mctx,
83                                      newalloc * sizeof(dns_aclelement_t));
84                 if (newmem == NULL)
85                         return (ISC_R_NOMEMORY);
86                 memcpy(newmem, acl->elements,
87                        acl->length * sizeof(dns_aclelement_t));
88                 isc_mem_put(acl->mctx, acl->elements,
89                             acl->alloc * sizeof(dns_aclelement_t));
90                 acl->elements = newmem;
91                 acl->alloc = newalloc;
92         }
93         /*
94          * Append the new element.
95          */
96         acl->elements[acl->length++] = *elt;
97
98         return (ISC_R_SUCCESS);
99 }
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         result = dns_acl_create(mctx, 1, &acl);
106         if (result != ISC_R_SUCCESS)
107                 return (result);
108         acl->elements[0].negative = neg;
109         acl->elements[0].type = dns_aclelementtype_any;
110         acl->length = 1;
111         *target = acl;
112         return (result);
113 }
114
115 isc_result_t
116 dns_acl_any(isc_mem_t *mctx, dns_acl_t **target) {
117         return (dns_acl_anyornone(mctx, ISC_FALSE, target));
118 }
119
120 isc_result_t
121 dns_acl_none(isc_mem_t *mctx, dns_acl_t **target) {
122         return (dns_acl_anyornone(mctx, ISC_TRUE, target));
123 }
124
125 isc_result_t
126 dns_acl_match(isc_netaddr_t *reqaddr,
127               dns_name_t *reqsigner,
128               dns_acl_t *acl,
129               dns_aclenv_t *env,
130               int *match,
131               dns_aclelement_t **matchelt)
132 {
133         unsigned int i;
134
135         REQUIRE(reqaddr != NULL);
136         REQUIRE(matchelt == NULL || *matchelt == NULL);
137         
138         for (i = 0; i < acl->length; i++) {
139                 dns_aclelement_t *e = &acl->elements[i];
140
141                 if (dns_aclelement_match(reqaddr, reqsigner,
142                                          e, env, matchelt)) {
143                         *match = e->negative ? -((int)i+1) : ((int)i+1);
144                         return (ISC_R_SUCCESS);
145                 }
146         }
147         /* No match. */
148         *match = 0;
149         return (ISC_R_SUCCESS);
150 }
151
152 isc_boolean_t
153 dns_aclelement_match(isc_netaddr_t *reqaddr,
154                      dns_name_t *reqsigner,
155                      dns_aclelement_t *e,
156                      dns_aclenv_t *env,
157                      dns_aclelement_t **matchelt)
158 {
159         dns_acl_t *inner = NULL;
160         isc_netaddr_t *addr;
161         isc_netaddr_t v4addr;
162         int indirectmatch;
163         isc_result_t result;
164
165         switch (e->type) {
166         case dns_aclelementtype_ipprefix:
167                 if (env == NULL ||
168                     env->match_mapped == ISC_FALSE ||
169                     reqaddr->family != AF_INET6 ||
170                     !IN6_IS_ADDR_V4MAPPED(&reqaddr->type.in6))
171                         addr = reqaddr;
172                 else {
173                         isc_netaddr_fromv4mapped(&v4addr, reqaddr);
174                         addr = &v4addr;
175                 }
176
177                 if (isc_netaddr_eqprefix(addr,
178                                          &e->u.ip_prefix.address, 
179                                          e->u.ip_prefix.prefixlen))
180                         goto matched;
181                 break;
182                 
183         case dns_aclelementtype_keyname:
184                 if (reqsigner != NULL &&
185                     dns_name_equal(reqsigner, &e->u.keyname))
186                         goto matched;
187                 break;
188                 
189         case dns_aclelementtype_nestedacl:
190                 inner = e->u.nestedacl;
191         nested:
192                 result = dns_acl_match(reqaddr, reqsigner,
193                                        inner,
194                                        env,
195                                        &indirectmatch, matchelt);
196                 INSIST(result == ISC_R_SUCCESS);
197
198                 /*
199                  * Treat negative matches in indirect ACLs as
200                  * "no match".
201                  * That way, a negated indirect ACL will never become 
202                  * a surprise positive match through double negation.
203                  * XXXDCL this should be documented.
204                  */
205                 if (indirectmatch > 0)
206                         goto matchelt_set;
207                 
208                 /*
209                  * A negative indirect match may have set *matchelt,
210                  * but we don't want it set when we return.
211                  */
212                 if (matchelt != NULL)
213                         *matchelt = NULL;
214                 break;
215                 
216         case dns_aclelementtype_any:
217         matched:
218                 if (matchelt != NULL)
219                         *matchelt = e;
220         matchelt_set:
221                 return (ISC_TRUE);
222                         
223         case dns_aclelementtype_localhost:
224                 if (env != NULL && env->localhost != NULL) {
225                         inner = env->localhost;
226                         goto nested;
227                 } else {
228                         break;
229                 }
230                 
231         case dns_aclelementtype_localnets:
232                 if (env != NULL && env->localnets != NULL) {
233                         inner = env->localnets;
234                         goto nested;
235                 } else {
236                         break;
237                 }
238                 
239         default:
240                 INSIST(0);
241                 break;
242         }
243
244         return (ISC_FALSE);
245 }       
246
247 void
248 dns_acl_attach(dns_acl_t *source, dns_acl_t **target) {
249         REQUIRE(DNS_ACL_VALID(source));
250         isc_refcount_increment(&source->refcount, NULL);
251         *target = source;
252 }
253
254 static void
255 destroy(dns_acl_t *dacl) {
256         unsigned int i;
257         for (i = 0; i < dacl->length; i++) {
258                 dns_aclelement_t *de = &dacl->elements[i];
259                 switch (de->type) {
260                 case dns_aclelementtype_keyname:
261                         dns_name_free(&de->u.keyname, dacl->mctx);
262                         break;
263                 case dns_aclelementtype_nestedacl:
264                         dns_acl_detach(&de->u.nestedacl);
265                         break;
266                 default:
267                         break;
268                 }
269         }
270         if (dacl->elements != NULL)
271                 isc_mem_put(dacl->mctx, dacl->elements,
272                             dacl->alloc * sizeof(dns_aclelement_t));
273         if (dacl->name != NULL)
274                 isc_mem_free(dacl->mctx, dacl->name);
275         isc_refcount_destroy(&dacl->refcount);
276         dacl->magic = 0;
277         isc_mem_put(dacl->mctx, dacl, sizeof(*dacl));
278 }
279
280 void
281 dns_acl_detach(dns_acl_t **aclp) {
282         dns_acl_t *acl = *aclp;
283         unsigned int refs;
284         REQUIRE(DNS_ACL_VALID(acl));
285         isc_refcount_decrement(&acl->refcount, &refs);
286         if (refs == 0)
287                 destroy(acl);
288         *aclp = NULL;
289 }
290
291 isc_boolean_t
292 dns_aclelement_equal(dns_aclelement_t *ea, dns_aclelement_t *eb) {
293         if (ea->type != eb->type)
294                 return (ISC_FALSE);
295         switch (ea->type) {
296         case dns_aclelementtype_ipprefix:
297                 if (ea->u.ip_prefix.prefixlen !=
298                     eb->u.ip_prefix.prefixlen)
299                         return (ISC_FALSE);
300                 return (isc_netaddr_equal(&ea->u.ip_prefix.address,
301                                           &eb->u.ip_prefix.address));
302         case dns_aclelementtype_keyname:
303                 return (dns_name_equal(&ea->u.keyname, &eb->u.keyname));
304         case dns_aclelementtype_nestedacl:
305                 return (dns_acl_equal(ea->u.nestedacl, eb->u.nestedacl));
306         case dns_aclelementtype_localhost:
307         case dns_aclelementtype_localnets:
308         case dns_aclelementtype_any:
309                 return (ISC_TRUE);
310         default:
311                 INSIST(0);
312                 return (ISC_FALSE);
313         }
314 }
315
316 isc_boolean_t
317 dns_acl_equal(dns_acl_t *a, dns_acl_t *b) {
318         unsigned int i;
319         if (a == b)
320                 return (ISC_TRUE);
321         if (a->length != b->length)
322                 return (ISC_FALSE);
323         for (i = 0; i < a->length; i++) {
324                 if (! dns_aclelement_equal(&a->elements[i],
325                                            &b->elements[i]))
326                         return (ISC_FALSE);
327         }
328         return (ISC_TRUE);
329 }
330
331 static isc_boolean_t
332 is_loopback(dns_aclipprefix_t *p) {
333         switch (p->address.family) {
334         case AF_INET:
335                 if (p->prefixlen == 32 &&
336                     htonl(p->address.type.in.s_addr) == INADDR_LOOPBACK)
337                         return (ISC_TRUE);
338                 break;
339         case AF_INET6:
340                 if (p->prefixlen == 128 &&
341                     IN6_IS_ADDR_LOOPBACK(&p->address.type.in6))
342                         return (ISC_TRUE);
343                 break;
344         default:
345                 break;
346         }
347         return (ISC_FALSE);
348 }
349
350 isc_boolean_t
351 dns_acl_isinsecure(dns_acl_t *a) {
352         unsigned int i;
353         for (i = 0; i < a->length; i++) {
354                 dns_aclelement_t *e = &a->elements[i];
355
356                 /* A negated match can never be insecure. */
357                 if (e->negative)
358                         continue;
359
360                 switch (e->type) {
361                 case dns_aclelementtype_ipprefix:
362                         /* The loopback address is considered secure. */
363                         if (! is_loopback(&e->u.ip_prefix))
364                                 return (ISC_TRUE);
365                         continue;
366                         
367                 case dns_aclelementtype_keyname:
368                 case dns_aclelementtype_localhost:
369                         continue;
370
371                 case dns_aclelementtype_nestedacl:
372                         if (dns_acl_isinsecure(e->u.nestedacl))
373                                 return (ISC_TRUE);
374                         continue;
375                         
376                 case dns_aclelementtype_localnets:
377                 case dns_aclelementtype_any:
378                         return (ISC_TRUE);
379
380                 default:
381                         INSIST(0);
382                         return (ISC_TRUE);
383                 }
384         }
385         /* No insecure elements were found. */
386         return (ISC_FALSE);
387 }
388
389 isc_result_t
390 dns_aclenv_init(isc_mem_t *mctx, dns_aclenv_t *env) {
391         isc_result_t result;
392         env->localhost = NULL;
393         env->localnets = NULL;
394         result = dns_acl_create(mctx, 0, &env->localhost);
395         if (result != ISC_R_SUCCESS)
396                 goto cleanup_nothing;
397         result = dns_acl_create(mctx, 0, &env->localnets);
398         if (result != ISC_R_SUCCESS)
399                 goto cleanup_localhost;
400         env->match_mapped = ISC_FALSE;
401         return (ISC_R_SUCCESS);
402
403  cleanup_localhost:
404         dns_acl_detach(&env->localhost);
405  cleanup_nothing:
406         return (result);
407 }
408
409 void
410 dns_aclenv_copy(dns_aclenv_t *t, dns_aclenv_t *s) {
411         dns_acl_detach(&t->localhost);
412         dns_acl_attach(s->localhost, &t->localhost);
413         dns_acl_detach(&t->localnets);
414         dns_acl_attach(s->localnets, &t->localnets);
415         t->match_mapped = s->match_mapped;
416 }
417
418 void
419 dns_aclenv_destroy(dns_aclenv_t *env) {
420         dns_acl_detach(&env->localhost);
421         dns_acl_detach(&env->localnets);
422 }