]> CyberLeo.Net >> Repos - FreeBSD/releng/9.2.git/blob - contrib/bind9/lib/dns/keytable.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 / keytable.c
1 /*
2  * Copyright (C) 2004, 2005, 2007, 2009, 2010  Internet Systems Consortium, Inc. ("ISC")
3  * Copyright (C) 2000, 2001  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: keytable.c,v 1.41 2010/06/25 23:46:51 tbox Exp $ */
19
20 /*! \file */
21
22 #include <config.h>
23
24 #include <isc/mem.h>
25 #include <isc/rwlock.h>
26 #include <isc/string.h>         /* Required for HP/UX (and others?) */
27 #include <isc/util.h>
28
29 #include <dns/keytable.h>
30 #include <dns/fixedname.h>
31 #include <dns/rbt.h>
32 #include <dns/result.h>
33
34 static void
35 free_keynode(void *node, void *arg) {
36         dns_keynode_t *keynode = node;
37         isc_mem_t *mctx = arg;
38
39         dns_keynode_detachall(mctx, &keynode);
40 }
41
42 isc_result_t
43 dns_keytable_create(isc_mem_t *mctx, dns_keytable_t **keytablep) {
44         dns_keytable_t *keytable;
45         isc_result_t result;
46
47         /*
48          * Create a keytable.
49          */
50
51         REQUIRE(keytablep != NULL && *keytablep == NULL);
52
53         keytable = isc_mem_get(mctx, sizeof(*keytable));
54         if (keytable == NULL)
55                 return (ISC_R_NOMEMORY);
56
57         keytable->table = NULL;
58         result = dns_rbt_create(mctx, free_keynode, mctx, &keytable->table);
59         if (result != ISC_R_SUCCESS)
60                 goto cleanup_keytable;
61
62         result = isc_mutex_init(&keytable->lock);
63         if (result != ISC_R_SUCCESS)
64                 goto cleanup_rbt;
65
66         result = isc_rwlock_init(&keytable->rwlock, 0, 0);
67         if (result != ISC_R_SUCCESS)
68                 goto cleanup_lock;
69
70         keytable->mctx = mctx;
71         keytable->active_nodes = 0;
72         keytable->references = 1;
73         keytable->magic = KEYTABLE_MAGIC;
74         *keytablep = keytable;
75
76         return (ISC_R_SUCCESS);
77
78    cleanup_lock:
79         DESTROYLOCK(&keytable->lock);
80
81    cleanup_rbt:
82         dns_rbt_destroy(&keytable->table);
83
84    cleanup_keytable:
85         isc_mem_put(mctx, keytable, sizeof(*keytable));
86
87         return (result);
88 }
89
90 void
91 dns_keytable_attach(dns_keytable_t *source, dns_keytable_t **targetp) {
92
93         /*
94          * Attach *targetp to source.
95          */
96
97         REQUIRE(VALID_KEYTABLE(source));
98         REQUIRE(targetp != NULL && *targetp == NULL);
99
100         RWLOCK(&source->rwlock, isc_rwlocktype_write);
101
102         INSIST(source->references > 0);
103         source->references++;
104         INSIST(source->references != 0);
105
106         RWUNLOCK(&source->rwlock, isc_rwlocktype_write);
107
108         *targetp = source;
109 }
110
111 void
112 dns_keytable_detach(dns_keytable_t **keytablep) {
113         isc_boolean_t destroy = ISC_FALSE;
114         dns_keytable_t *keytable;
115
116         /*
117          * Detach *keytablep from its keytable.
118          */
119
120         REQUIRE(keytablep != NULL && VALID_KEYTABLE(*keytablep));
121
122         keytable = *keytablep;
123
124         RWLOCK(&keytable->rwlock, isc_rwlocktype_write);
125
126         INSIST(keytable->references > 0);
127         keytable->references--;
128         LOCK(&keytable->lock);
129         if (keytable->references == 0 && keytable->active_nodes == 0)
130                 destroy = ISC_TRUE;
131         UNLOCK(&keytable->lock);
132
133         RWUNLOCK(&keytable->rwlock, isc_rwlocktype_write);
134
135         if (destroy) {
136                 dns_rbt_destroy(&keytable->table);
137                 isc_rwlock_destroy(&keytable->rwlock);
138                 DESTROYLOCK(&keytable->lock);
139                 keytable->magic = 0;
140                 isc_mem_put(keytable->mctx, keytable, sizeof(*keytable));
141         }
142
143         *keytablep = NULL;
144 }
145
146 static isc_result_t
147 insert(dns_keytable_t *keytable, isc_boolean_t managed,
148        dns_name_t *keyname, dst_key_t **keyp)
149 {
150         isc_result_t result;
151         dns_keynode_t *knode = NULL;
152         dns_rbtnode_t *node;
153
154         REQUIRE(keyp == NULL || *keyp != NULL);
155         REQUIRE(VALID_KEYTABLE(keytable));
156
157         result = dns_keynode_create(keytable->mctx, &knode);
158         if (result != ISC_R_SUCCESS)
159                 return (result);
160
161         knode->managed = managed;
162
163         RWLOCK(&keytable->rwlock, isc_rwlocktype_write);
164
165         node = NULL;
166         result = dns_rbt_addnode(keytable->table, keyname, &node);
167
168         if (keyp != NULL) {
169                 if (result == ISC_R_EXISTS) {
170                         /* Key already in table? */
171                         dns_keynode_t *k;
172                         for (k = node->data; k != NULL; k = k->next) {
173                                 if (k->key == NULL) {
174                                         k->key = *keyp;
175                                         break;
176                                 }
177                                 if (dst_key_compare(k->key, *keyp) == ISC_TRUE)
178                                         break;
179                         }
180
181                         if (k == NULL)
182                                 result = ISC_R_SUCCESS;
183                         else
184                                 dst_key_free(keyp);
185                 }
186
187                 if (result == ISC_R_SUCCESS) {
188                         knode->key = *keyp;
189                         knode->next = node->data;
190                         *keyp = NULL;
191                 }
192         }
193
194         if (result == ISC_R_SUCCESS) {
195                 node->data = knode;
196                 knode = NULL;
197         }
198
199         /* Key was already there?  That's the same as a success */
200         if (result == ISC_R_EXISTS)
201                 result = ISC_R_SUCCESS;
202
203         RWUNLOCK(&keytable->rwlock, isc_rwlocktype_write);
204
205         if (knode != NULL)
206                 dns_keynode_detach(keytable->mctx, &knode);
207
208         return (result);
209 }
210
211 isc_result_t
212 dns_keytable_add(dns_keytable_t *keytable, isc_boolean_t managed,
213                  dst_key_t **keyp)
214 {
215         REQUIRE(keyp != NULL && *keyp != NULL);
216         return (insert(keytable, managed, dst_key_name(*keyp), keyp));
217 }
218
219 isc_result_t
220 dns_keytable_marksecure(dns_keytable_t *keytable, dns_name_t *name) {
221         return (insert(keytable, ISC_TRUE, name, NULL));
222 }
223
224 isc_result_t
225 dns_keytable_delete(dns_keytable_t *keytable, dns_name_t *keyname) {
226         isc_result_t result;
227         dns_rbtnode_t *node = NULL;
228
229         REQUIRE(VALID_KEYTABLE(keytable));
230         REQUIRE(keyname != NULL);
231
232         RWLOCK(&keytable->rwlock, isc_rwlocktype_write);
233         result = dns_rbt_findnode(keytable->table, keyname, NULL, &node, NULL,
234                                   DNS_RBTFIND_NOOPTIONS, NULL, NULL);
235         if (result == ISC_R_SUCCESS) {
236                 if (node->data != NULL)
237                         result = dns_rbt_deletenode(keytable->table,
238                                                     node, ISC_FALSE);
239                 else
240                         result = ISC_R_NOTFOUND;
241         } else if (result == DNS_R_PARTIALMATCH)
242                 result = ISC_R_NOTFOUND;
243         RWUNLOCK(&keytable->rwlock, isc_rwlocktype_write);
244
245         return (result);
246 }
247
248 isc_result_t
249 dns_keytable_deletekeynode(dns_keytable_t *keytable, dst_key_t *dstkey) {
250         isc_result_t result;
251         dns_name_t *keyname;
252         dns_rbtnode_t *node = NULL;
253         dns_keynode_t *knode = NULL, **kprev = NULL;
254
255         REQUIRE(VALID_KEYTABLE(keytable));
256         REQUIRE(dstkey != NULL);
257
258         keyname = dst_key_name(dstkey);
259
260         RWLOCK(&keytable->rwlock, isc_rwlocktype_write);
261         result = dns_rbt_findnode(keytable->table, keyname, NULL, &node, NULL,
262                                   DNS_RBTFIND_NOOPTIONS, NULL, NULL);
263
264         if (result == DNS_R_PARTIALMATCH)
265                 result = ISC_R_NOTFOUND;
266         if (result != ISC_R_SUCCESS)
267                 goto finish;
268
269         if (node->data == NULL) {
270                 result = ISC_R_NOTFOUND;
271                 goto finish;
272         }
273
274         knode = node->data;
275         if (knode->next == NULL &&
276             (knode->key == NULL ||
277              dst_key_compare(knode->key, dstkey) == ISC_TRUE)) {
278                 result = dns_rbt_deletenode(keytable->table, node, ISC_FALSE);
279                 goto finish;
280         }
281
282         kprev = (dns_keynode_t **) &node->data;
283         while (knode != NULL) {
284                 if (dst_key_compare(knode->key, dstkey) == ISC_TRUE)
285                         break;
286                 kprev = &knode->next;
287                 knode = knode->next;
288         }
289
290         if (knode != NULL) {
291                 if (knode->key != NULL)
292                         dst_key_free(&knode->key);
293                 /*
294                  * This is equivalent to:
295                  * dns_keynode_attach(knode->next, &tmp);
296                  * dns_keynode_detach(kprev);
297                  * dns_keynode_attach(tmp, &kprev);
298                  * dns_keynode_detach(&tmp);
299                  */
300                 *kprev = knode->next;
301                 knode->next = NULL;
302                 dns_keynode_detach(keytable->mctx, &knode);
303         } else
304                 result = DNS_R_PARTIALMATCH;
305   finish:
306         RWUNLOCK(&keytable->rwlock, isc_rwlocktype_write);
307         return (result);
308 }
309
310 isc_result_t
311 dns_keytable_find(dns_keytable_t *keytable, dns_name_t *keyname,
312                   dns_keynode_t **keynodep)
313 {
314         isc_result_t result;
315         dns_rbtnode_t *node = NULL;
316
317         REQUIRE(VALID_KEYTABLE(keytable));
318         REQUIRE(keyname != NULL);
319         REQUIRE(keynodep != NULL && *keynodep == NULL);
320
321         RWLOCK(&keytable->rwlock, isc_rwlocktype_read);
322         result = dns_rbt_findnode(keytable->table, keyname, NULL, &node, NULL,
323                                   DNS_RBTFIND_NOOPTIONS, NULL, NULL);
324         if (result == ISC_R_SUCCESS) {
325                 if (node->data != NULL) {
326                         LOCK(&keytable->lock);
327                         keytable->active_nodes++;
328                         UNLOCK(&keytable->lock);
329                         dns_keynode_attach(node->data, keynodep);
330                 } else
331                         result = ISC_R_NOTFOUND;
332         } else if (result == DNS_R_PARTIALMATCH)
333                 result = ISC_R_NOTFOUND;
334         RWUNLOCK(&keytable->rwlock, isc_rwlocktype_read);
335
336         return (result);
337 }
338
339 isc_result_t
340 dns_keytable_nextkeynode(dns_keytable_t *keytable, dns_keynode_t *keynode,
341                          dns_keynode_t **nextnodep)
342 {
343         /*
344          * Return the next key after 'keynode', regardless of
345          * properties.
346          */
347
348         REQUIRE(VALID_KEYTABLE(keytable));
349         REQUIRE(VALID_KEYNODE(keynode));
350         REQUIRE(nextnodep != NULL && *nextnodep == NULL);
351
352         if (keynode->next == NULL)
353                 return (ISC_R_NOTFOUND);
354
355         dns_keynode_attach(keynode->next, nextnodep);
356         LOCK(&keytable->lock);
357         keytable->active_nodes++;
358         UNLOCK(&keytable->lock);
359
360         return (ISC_R_SUCCESS);
361 }
362
363 isc_result_t
364 dns_keytable_findkeynode(dns_keytable_t *keytable, dns_name_t *name,
365                          dns_secalg_t algorithm, dns_keytag_t tag,
366                          dns_keynode_t **keynodep)
367 {
368         isc_result_t result;
369         dns_keynode_t *knode;
370         void *data;
371
372         /*
373          * Search for a key named 'name', matching 'algorithm' and 'tag' in
374          * 'keytable'.
375          */
376
377         REQUIRE(VALID_KEYTABLE(keytable));
378         REQUIRE(dns_name_isabsolute(name));
379         REQUIRE(keynodep != NULL && *keynodep == NULL);
380
381         RWLOCK(&keytable->rwlock, isc_rwlocktype_read);
382
383         /*
384          * Note we don't want the DNS_R_PARTIALMATCH from dns_rbt_findname()
385          * as that indicates that 'name' was not found.
386          *
387          * DNS_R_PARTIALMATCH indicates that the name was found but we
388          * didn't get a match on algorithm and key id arguments.
389          */
390         knode = NULL;
391         data = NULL;
392         result = dns_rbt_findname(keytable->table, name, 0, NULL, &data);
393
394         if (result == ISC_R_SUCCESS) {
395                 INSIST(data != NULL);
396                 for (knode = data; knode != NULL; knode = knode->next) {
397                         if (knode->key == NULL) {
398                                 knode = NULL;
399                                 break;
400                         }
401                         if (algorithm == dst_key_alg(knode->key)
402                             && tag == dst_key_id(knode->key))
403                                 break;
404                 }
405                 if (knode != NULL) {
406                         LOCK(&keytable->lock);
407                         keytable->active_nodes++;
408                         UNLOCK(&keytable->lock);
409                         dns_keynode_attach(knode, keynodep);
410                 } else
411                         result = DNS_R_PARTIALMATCH;
412         } else if (result == DNS_R_PARTIALMATCH)
413                 result = ISC_R_NOTFOUND;
414
415         RWUNLOCK(&keytable->rwlock, isc_rwlocktype_read);
416
417         return (result);
418 }
419
420 isc_result_t
421 dns_keytable_findnextkeynode(dns_keytable_t *keytable, dns_keynode_t *keynode,
422                              dns_keynode_t **nextnodep)
423 {
424         isc_result_t result;
425         dns_keynode_t *knode;
426
427         /*
428          * Search for the next key with the same properties as 'keynode' in
429          * 'keytable'.
430          */
431
432         REQUIRE(VALID_KEYTABLE(keytable));
433         REQUIRE(VALID_KEYNODE(keynode));
434         REQUIRE(nextnodep != NULL && *nextnodep == NULL);
435
436         for (knode = keynode->next; knode != NULL; knode = knode->next) {
437                 if (knode->key == NULL) {
438                         knode = NULL;
439                         break;
440                 }
441                 if (dst_key_alg(keynode->key) == dst_key_alg(knode->key) &&
442                     dst_key_id(keynode->key) == dst_key_id(knode->key))
443                         break;
444         }
445         if (knode != NULL) {
446                 LOCK(&keytable->lock);
447                 keytable->active_nodes++;
448                 UNLOCK(&keytable->lock);
449                 result = ISC_R_SUCCESS;
450                 dns_keynode_attach(knode, nextnodep);
451         } else
452                 result = ISC_R_NOTFOUND;
453
454         return (result);
455 }
456
457 isc_result_t
458 dns_keytable_finddeepestmatch(dns_keytable_t *keytable, dns_name_t *name,
459                               dns_name_t *foundname)
460 {
461         isc_result_t result;
462         void *data;
463
464         /*
465          * Search for the deepest match in 'keytable'.
466          */
467
468         REQUIRE(VALID_KEYTABLE(keytable));
469         REQUIRE(dns_name_isabsolute(name));
470         REQUIRE(foundname != NULL);
471
472         RWLOCK(&keytable->rwlock, isc_rwlocktype_read);
473
474         data = NULL;
475         result = dns_rbt_findname(keytable->table, name, 0, foundname, &data);
476
477         if (result == ISC_R_SUCCESS || result == DNS_R_PARTIALMATCH)
478                 result = ISC_R_SUCCESS;
479
480         RWUNLOCK(&keytable->rwlock, isc_rwlocktype_read);
481
482         return (result);
483 }
484
485 void
486 dns_keytable_attachkeynode(dns_keytable_t *keytable, dns_keynode_t *source,
487                            dns_keynode_t **target)
488 {
489         /*
490          * Give back a keynode found via dns_keytable_findkeynode().
491          */
492
493         REQUIRE(VALID_KEYTABLE(keytable));
494         REQUIRE(VALID_KEYNODE(source));
495         REQUIRE(target != NULL && *target == NULL);
496
497         LOCK(&keytable->lock);
498         keytable->active_nodes++;
499         UNLOCK(&keytable->lock);
500
501         dns_keynode_attach(source, target);
502 }
503
504 void
505 dns_keytable_detachkeynode(dns_keytable_t *keytable, dns_keynode_t **keynodep)
506 {
507         /*
508          * Give back a keynode found via dns_keytable_findkeynode().
509          */
510
511         REQUIRE(VALID_KEYTABLE(keytable));
512         REQUIRE(keynodep != NULL && VALID_KEYNODE(*keynodep));
513
514         LOCK(&keytable->lock);
515         INSIST(keytable->active_nodes > 0);
516         keytable->active_nodes--;
517         UNLOCK(&keytable->lock);
518
519         dns_keynode_detach(keytable->mctx, keynodep);
520 }
521
522 isc_result_t
523 dns_keytable_issecuredomain(dns_keytable_t *keytable, dns_name_t *name,
524                             isc_boolean_t *wantdnssecp)
525 {
526         isc_result_t result;
527         void *data;
528
529         /*
530          * Is 'name' at or beneath a trusted key?
531          */
532
533         REQUIRE(VALID_KEYTABLE(keytable));
534         REQUIRE(dns_name_isabsolute(name));
535         REQUIRE(wantdnssecp != NULL);
536
537         RWLOCK(&keytable->rwlock, isc_rwlocktype_read);
538
539         data = NULL;
540         result = dns_rbt_findname(keytable->table, name, 0, NULL, &data);
541
542         if (result == ISC_R_SUCCESS || result == DNS_R_PARTIALMATCH) {
543                 INSIST(data != NULL);
544                 *wantdnssecp = ISC_TRUE;
545                 result = ISC_R_SUCCESS;
546         } else if (result == ISC_R_NOTFOUND) {
547                 *wantdnssecp = ISC_FALSE;
548                 result = ISC_R_SUCCESS;
549         }
550
551         RWUNLOCK(&keytable->rwlock, isc_rwlocktype_read);
552
553         return (result);
554 }
555
556 isc_result_t
557 dns_keytable_dump(dns_keytable_t *keytable, FILE *fp)
558 {
559         isc_result_t result;
560         dns_keynode_t *knode;
561         dns_rbtnode_t *node;
562         dns_rbtnodechain_t chain;
563
564         REQUIRE(VALID_KEYTABLE(keytable));
565
566         RWLOCK(&keytable->rwlock, isc_rwlocktype_read);
567         dns_rbtnodechain_init(&chain, keytable->mctx);
568         result = dns_rbtnodechain_first(&chain, keytable->table, NULL, NULL);
569         if (result != ISC_R_SUCCESS && result != DNS_R_NEWORIGIN)
570                 goto cleanup;
571         for (;;) {
572                 char pbuf[DST_KEY_FORMATSIZE];
573
574                 dns_rbtnodechain_current(&chain, NULL, NULL, &node);
575                 for (knode = node->data; knode != NULL; knode = knode->next) {
576                         dst_key_format(knode->key, pbuf, sizeof(pbuf));
577                         fprintf(fp, "%s ; %s\n", pbuf,
578                                 knode->managed ? "managed" : "trusted");
579                 }
580                 result = dns_rbtnodechain_next(&chain, NULL, NULL);
581                 if (result != ISC_R_SUCCESS && result != DNS_R_NEWORIGIN) {
582                         if (result == ISC_R_NOMORE)
583                                 result = ISC_R_SUCCESS;
584                         break;
585                 }
586         }
587
588    cleanup:
589         dns_rbtnodechain_invalidate(&chain);
590         RWUNLOCK(&keytable->rwlock, isc_rwlocktype_read);
591         return (result);
592 }
593
594 dst_key_t *
595 dns_keynode_key(dns_keynode_t *keynode) {
596
597         /*
598          * Get the DST key associated with keynode.
599          */
600
601         REQUIRE(VALID_KEYNODE(keynode));
602
603         return (keynode->key);
604 }
605
606 isc_boolean_t
607 dns_keynode_managed(dns_keynode_t *keynode) {
608         /*
609          * Is this a managed key?
610          */
611         REQUIRE(VALID_KEYNODE(keynode));
612
613         return (keynode->managed);
614 }
615
616 isc_result_t
617 dns_keynode_create(isc_mem_t *mctx, dns_keynode_t **target) {
618         isc_result_t result;
619         dns_keynode_t *knode = NULL;
620
621         REQUIRE(target != NULL && *target == NULL);
622
623         knode = isc_mem_get(mctx, sizeof(dns_keynode_t));
624         if (knode == NULL)
625                 return (ISC_R_NOMEMORY);
626
627         knode->magic = KEYNODE_MAGIC;
628         knode->managed = ISC_FALSE;
629         knode->key = NULL;
630         knode->next = NULL;
631
632         result = isc_refcount_init(&knode->refcount, 1);
633         if (result != ISC_R_SUCCESS)
634                 return (result);
635
636         *target = knode;
637         return (ISC_R_SUCCESS);
638 }
639
640 void
641 dns_keynode_attach(dns_keynode_t *source, dns_keynode_t **target) {
642         REQUIRE(VALID_KEYNODE(source));
643         isc_refcount_increment(&source->refcount, NULL);
644         *target = source;
645 }
646
647 void
648 dns_keynode_detach(isc_mem_t *mctx, dns_keynode_t **keynode) {
649         unsigned int refs;
650         dns_keynode_t *node = *keynode;
651         REQUIRE(VALID_KEYNODE(node));
652         isc_refcount_decrement(&node->refcount, &refs);
653         if (refs == 0) {
654                 if (node->key != NULL)
655                         dst_key_free(&node->key);
656                 isc_refcount_destroy(&node->refcount);
657                 isc_mem_put(mctx, node, sizeof(dns_keynode_t));
658         }
659         *keynode = NULL;
660 }
661
662 void
663 dns_keynode_detachall(isc_mem_t *mctx, dns_keynode_t **keynode) {
664         dns_keynode_t *next = NULL, *node = *keynode;
665         REQUIRE(VALID_KEYNODE(node));
666         while (node != NULL) {
667                 next = node->next;
668                 dns_keynode_detach(mctx, &node);
669                 node = next;
670         }
671         *keynode = NULL;
672 }