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