Merge from vendor branch TCSH:
[dragonfly.git] / contrib / bind-9.3 / 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.23.52.6 2006/03/02 00:37:20 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, const 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(const isc_netaddr_t *reqaddr,
127               const dns_name_t *reqsigner,
128               const dns_acl_t *acl,
129               const dns_aclenv_t *env,
130               int *match,
131               dns_aclelement_t const**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_result_t
153 dns_acl_elementmatch(const dns_acl_t *acl,
154                      const dns_aclelement_t *elt,
155                      const dns_aclelement_t **matchelt)
156 {
157         unsigned int i;
158
159         REQUIRE(elt != NULL);
160         REQUIRE(matchelt == NULL || *matchelt == NULL);
161         
162         for (i = 0; i < acl->length; i++) {
163                 dns_aclelement_t *e = &acl->elements[i];
164
165                 if (dns_aclelement_equal(e, elt) == ISC_TRUE) {
166                         if (matchelt != NULL)
167                                 *matchelt = e;
168                         return (ISC_R_SUCCESS);
169                 }
170         }
171
172         return (ISC_R_NOTFOUND);
173 }
174
175 isc_boolean_t
176 dns_aclelement_match(const isc_netaddr_t *reqaddr,
177                      const dns_name_t *reqsigner,
178                      const dns_aclelement_t *e,
179                      const dns_aclenv_t *env,
180                      const dns_aclelement_t **matchelt)
181 {
182         dns_acl_t *inner = NULL;
183         const isc_netaddr_t *addr;
184         isc_netaddr_t v4addr;
185         int indirectmatch;
186         isc_result_t result;
187
188         switch (e->type) {
189         case dns_aclelementtype_ipprefix:
190                 if (env == NULL ||
191                     env->match_mapped == ISC_FALSE ||
192                     reqaddr->family != AF_INET6 ||
193                     !IN6_IS_ADDR_V4MAPPED(&reqaddr->type.in6))
194                         addr = reqaddr;
195                 else {
196                         isc_netaddr_fromv4mapped(&v4addr, reqaddr);
197                         addr = &v4addr;
198                 }
199
200                 if (isc_netaddr_eqprefix(addr,
201                                          &e->u.ip_prefix.address, 
202                                          e->u.ip_prefix.prefixlen))
203                         goto matched;
204                 break;
205                 
206         case dns_aclelementtype_keyname:
207                 if (reqsigner != NULL &&
208                     dns_name_equal(reqsigner, &e->u.keyname))
209                         goto matched;
210                 break;
211                 
212         case dns_aclelementtype_nestedacl:
213                 inner = e->u.nestedacl;
214         nested:
215                 result = dns_acl_match(reqaddr, reqsigner,
216                                        inner,
217                                        env,
218                                        &indirectmatch, matchelt);
219                 INSIST(result == ISC_R_SUCCESS);
220
221                 /*
222                  * Treat negative matches in indirect ACLs as
223                  * "no match".
224                  * That way, a negated indirect ACL will never become 
225                  * a surprise positive match through double negation.
226                  * XXXDCL this should be documented.
227                  */
228                 if (indirectmatch > 0)
229                         goto matchelt_set;
230                 
231                 /*
232                  * A negative indirect match may have set *matchelt,
233                  * but we don't want it set when we return.
234                  */
235                 if (matchelt != NULL)
236                         *matchelt = NULL;
237                 break;
238                 
239         case dns_aclelementtype_any:
240         matched:
241                 if (matchelt != NULL)
242                         *matchelt = e;
243         matchelt_set:
244                 return (ISC_TRUE);
245                         
246         case dns_aclelementtype_localhost:
247                 if (env != NULL && env->localhost != NULL) {
248                         inner = env->localhost;
249                         goto nested;
250                 } else {
251                         break;
252                 }
253                 
254         case dns_aclelementtype_localnets:
255                 if (env != NULL && env->localnets != NULL) {
256                         inner = env->localnets;
257                         goto nested;
258                 } else {
259                         break;
260                 }
261                 
262         default:
263                 INSIST(0);
264                 break;
265         }
266
267         return (ISC_FALSE);
268 }       
269
270 void
271 dns_acl_attach(dns_acl_t *source, dns_acl_t **target) {
272         REQUIRE(DNS_ACL_VALID(source));
273         isc_refcount_increment(&source->refcount, NULL);
274         *target = source;
275 }
276
277 static void
278 destroy(dns_acl_t *dacl) {
279         unsigned int i;
280         for (i = 0; i < dacl->length; i++) {
281                 dns_aclelement_t *de = &dacl->elements[i];
282                 switch (de->type) {
283                 case dns_aclelementtype_keyname:
284                         dns_name_free(&de->u.keyname, dacl->mctx);
285                         break;
286                 case dns_aclelementtype_nestedacl:
287                         dns_acl_detach(&de->u.nestedacl);
288                         break;
289                 default:
290                         break;
291                 }
292         }
293         if (dacl->elements != NULL)
294                 isc_mem_put(dacl->mctx, dacl->elements,
295                             dacl->alloc * sizeof(dns_aclelement_t));
296         if (dacl->name != NULL)
297                 isc_mem_free(dacl->mctx, dacl->name);
298         isc_refcount_destroy(&dacl->refcount);
299         dacl->magic = 0;
300         isc_mem_put(dacl->mctx, dacl, sizeof(*dacl));
301 }
302
303 void
304 dns_acl_detach(dns_acl_t **aclp) {
305         dns_acl_t *acl = *aclp;
306         unsigned int refs;
307         REQUIRE(DNS_ACL_VALID(acl));
308         isc_refcount_decrement(&acl->refcount, &refs);
309         if (refs == 0)
310                 destroy(acl);
311         *aclp = NULL;
312 }
313
314 isc_boolean_t
315 dns_aclelement_equal(const dns_aclelement_t *ea, const dns_aclelement_t *eb) {
316         if (ea->type != eb->type)
317                 return (ISC_FALSE);
318         switch (ea->type) {
319         case dns_aclelementtype_ipprefix:
320                 if (ea->u.ip_prefix.prefixlen !=
321                     eb->u.ip_prefix.prefixlen)
322                         return (ISC_FALSE);
323                 return (isc_netaddr_eqprefix(&ea->u.ip_prefix.address,
324                                              &eb->u.ip_prefix.address,
325                                              ea->u.ip_prefix.prefixlen));
326         case dns_aclelementtype_keyname:
327                 return (dns_name_equal(&ea->u.keyname, &eb->u.keyname));
328         case dns_aclelementtype_nestedacl:
329                 return (dns_acl_equal(ea->u.nestedacl, eb->u.nestedacl));
330         case dns_aclelementtype_localhost:
331         case dns_aclelementtype_localnets:
332         case dns_aclelementtype_any:
333                 return (ISC_TRUE);
334         default:
335                 INSIST(0);
336                 return (ISC_FALSE);
337         }
338 }
339
340 isc_boolean_t
341 dns_acl_equal(const dns_acl_t *a, const dns_acl_t *b) {
342         unsigned int i;
343         if (a == b)
344                 return (ISC_TRUE);
345         if (a->length != b->length)
346                 return (ISC_FALSE);
347         for (i = 0; i < a->length; i++) {
348                 if (! dns_aclelement_equal(&a->elements[i],
349                                            &b->elements[i]))
350                         return (ISC_FALSE);
351         }
352         return (ISC_TRUE);
353 }
354
355 static isc_boolean_t
356 is_loopback(const dns_aclipprefix_t *p) {
357         switch (p->address.family) {
358         case AF_INET:
359                 if (p->prefixlen == 32 &&
360                     htonl(p->address.type.in.s_addr) == INADDR_LOOPBACK)
361                         return (ISC_TRUE);
362                 break;
363         case AF_INET6:
364                 if (p->prefixlen == 128 &&
365                     IN6_IS_ADDR_LOOPBACK(&p->address.type.in6))
366                         return (ISC_TRUE);
367                 break;
368         default:
369                 break;
370         }
371         return (ISC_FALSE);
372 }
373
374 isc_boolean_t
375 dns_acl_isinsecure(const dns_acl_t *a) {
376         unsigned int i;
377         for (i = 0; i < a->length; i++) {
378                 dns_aclelement_t *e = &a->elements[i];
379
380                 /* A negated match can never be insecure. */
381                 if (e->negative)
382                         continue;
383
384                 switch (e->type) {
385                 case dns_aclelementtype_ipprefix:
386                         /* The loopback address is considered secure. */
387                         if (! is_loopback(&e->u.ip_prefix))
388                                 return (ISC_TRUE);
389                         continue;
390                         
391                 case dns_aclelementtype_keyname:
392                 case dns_aclelementtype_localhost:
393                         continue;
394
395                 case dns_aclelementtype_nestedacl:
396                         if (dns_acl_isinsecure(e->u.nestedacl))
397                                 return (ISC_TRUE);
398                         continue;
399                         
400                 case dns_aclelementtype_localnets:
401                 case dns_aclelementtype_any:
402                         return (ISC_TRUE);
403
404                 default:
405                         INSIST(0);
406                         return (ISC_TRUE);
407                 }
408         }
409         /* No insecure elements were found. */
410         return (ISC_FALSE);
411 }
412
413 isc_result_t
414 dns_aclenv_init(isc_mem_t *mctx, dns_aclenv_t *env) {
415         isc_result_t result;
416         env->localhost = NULL;
417         env->localnets = NULL;
418         result = dns_acl_create(mctx, 0, &env->localhost);
419         if (result != ISC_R_SUCCESS)
420                 goto cleanup_nothing;
421         result = dns_acl_create(mctx, 0, &env->localnets);
422         if (result != ISC_R_SUCCESS)
423                 goto cleanup_localhost;
424         env->match_mapped = ISC_FALSE;
425         return (ISC_R_SUCCESS);
426
427  cleanup_localhost:
428         dns_acl_detach(&env->localhost);
429  cleanup_nothing:
430         return (result);
431 }
432
433 void
434 dns_aclenv_copy(dns_aclenv_t *t, dns_aclenv_t *s) {
435         dns_acl_detach(&t->localhost);
436         dns_acl_attach(s->localhost, &t->localhost);
437         dns_acl_detach(&t->localnets);
438         dns_acl_attach(s->localnets, &t->localnets);
439         t->match_mapped = s->match_mapped;
440 }
441
442 void
443 dns_aclenv_destroy(dns_aclenv_t *env) {
444         dns_acl_detach(&env->localhost);
445         dns_acl_detach(&env->localnets);
446 }