bind - Upgraded vendor branch to 9.5.2-P1
[dragonfly.git] / contrib / bind-9.5.2 / lib / dns / keytable.c
1 /*
2  * Copyright (C) 2004, 2005, 2007  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.34 2007/06/19 23:47:16 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 struct dns_keytable {
35         /* Unlocked. */
36         unsigned int            magic;
37         isc_mem_t               *mctx;
38         isc_mutex_t             lock;
39         isc_rwlock_t            rwlock;
40         /* Locked by lock. */
41         isc_uint32_t            active_nodes;
42         /* Locked by rwlock. */
43         isc_uint32_t            references;
44         dns_rbt_t               *table;
45 };
46
47 #define KEYTABLE_MAGIC                  ISC_MAGIC('K', 'T', 'b', 'l')
48 #define VALID_KEYTABLE(kt)              ISC_MAGIC_VALID(kt, KEYTABLE_MAGIC)
49
50 struct dns_keynode {
51         unsigned int            magic;
52         dst_key_t *             key;
53         struct dns_keynode *    next;
54 };
55
56 #define KEYNODE_MAGIC                   ISC_MAGIC('K', 'N', 'o', 'd')
57 #define VALID_KEYNODE(kn)               ISC_MAGIC_VALID(kn, KEYNODE_MAGIC)
58
59 static void
60 free_keynode(void *node, void *arg) {
61         dns_keynode_t *keynode = node;
62         isc_mem_t *mctx = arg;
63
64         REQUIRE(VALID_KEYNODE(keynode));
65         dst_key_free(&keynode->key);
66         if (keynode->next != NULL)
67                 free_keynode(keynode->next, mctx);
68         isc_mem_put(mctx, keynode, sizeof(dns_keynode_t));
69 }
70
71 isc_result_t
72 dns_keytable_create(isc_mem_t *mctx, dns_keytable_t **keytablep) {
73         dns_keytable_t *keytable;
74         isc_result_t result;
75
76         /*
77          * Create a keytable.
78          */
79
80         REQUIRE(keytablep != NULL && *keytablep == NULL);
81
82         keytable = isc_mem_get(mctx, sizeof(*keytable));
83         if (keytable == NULL)
84                 return (ISC_R_NOMEMORY);
85
86         keytable->table = NULL;
87         result = dns_rbt_create(mctx, free_keynode, mctx, &keytable->table);
88         if (result != ISC_R_SUCCESS)
89                 goto cleanup_keytable;
90
91         result = isc_mutex_init(&keytable->lock);
92         if (result != ISC_R_SUCCESS)
93                 goto cleanup_rbt;
94
95         result = isc_rwlock_init(&keytable->rwlock, 0, 0);
96         if (result != ISC_R_SUCCESS)
97                 goto cleanup_lock;
98
99         keytable->mctx = mctx;
100         keytable->active_nodes = 0;
101         keytable->references = 1;
102         keytable->magic = KEYTABLE_MAGIC;
103         *keytablep = keytable;
104
105         return (ISC_R_SUCCESS);
106
107    cleanup_lock:
108         DESTROYLOCK(&keytable->lock);
109
110    cleanup_rbt:
111         dns_rbt_destroy(&keytable->table);
112
113    cleanup_keytable:
114         isc_mem_put(mctx, keytable, sizeof(*keytable));
115
116         return (result);
117 }
118
119
120 void
121 dns_keytable_attach(dns_keytable_t *source, dns_keytable_t **targetp) {
122
123         /*
124          * Attach *targetp to source.
125          */
126
127         REQUIRE(VALID_KEYTABLE(source));
128         REQUIRE(targetp != NULL && *targetp == NULL);
129
130         RWLOCK(&source->rwlock, isc_rwlocktype_write);
131
132         INSIST(source->references > 0);
133         source->references++;
134         INSIST(source->references != 0);
135
136         RWUNLOCK(&source->rwlock, isc_rwlocktype_write);
137
138         *targetp = source;
139 }
140
141 void
142 dns_keytable_detach(dns_keytable_t **keytablep) {
143         isc_boolean_t destroy = ISC_FALSE;
144         dns_keytable_t *keytable;
145
146         /*
147          * Detach *keytablep from its keytable.
148          */
149
150         REQUIRE(keytablep != NULL && VALID_KEYTABLE(*keytablep));
151
152         keytable = *keytablep;
153
154         RWLOCK(&keytable->rwlock, isc_rwlocktype_write);
155
156         INSIST(keytable->references > 0);
157         keytable->references--;
158         LOCK(&keytable->lock);
159         if (keytable->references == 0 && keytable->active_nodes == 0)
160                 destroy = ISC_TRUE;
161         UNLOCK(&keytable->lock);
162
163         RWUNLOCK(&keytable->rwlock, isc_rwlocktype_write);
164
165         if (destroy) {
166                 dns_rbt_destroy(&keytable->table);
167                 isc_rwlock_destroy(&keytable->rwlock);
168                 DESTROYLOCK(&keytable->lock);
169                 keytable->magic = 0;
170                 isc_mem_put(keytable->mctx, keytable, sizeof(*keytable));
171         }
172
173         *keytablep = NULL;
174 }
175
176 isc_result_t
177 dns_keytable_add(dns_keytable_t *keytable, dst_key_t **keyp) {
178         isc_result_t result;
179         dns_keynode_t *knode;
180         dns_rbtnode_t *node;
181         dns_name_t *keyname;
182
183         /*
184          * Add '*keyp' to 'keytable'.
185          */
186
187         REQUIRE(VALID_KEYTABLE(keytable));
188         REQUIRE(keyp != NULL);
189
190         keyname = dst_key_name(*keyp);
191
192         knode = isc_mem_get(keytable->mctx, sizeof(*knode));
193         if (knode == NULL)
194                 return (ISC_R_NOMEMORY);
195
196         RWLOCK(&keytable->rwlock, isc_rwlocktype_write);
197
198         node = NULL;
199         result = dns_rbt_addnode(keytable->table, keyname, &node);
200
201         if (result == ISC_R_SUCCESS || result == ISC_R_EXISTS) {
202                 knode->magic = KEYNODE_MAGIC;
203                 knode->key = *keyp;
204                 knode->next = node->data;
205                 node->data = knode;
206                 *keyp = NULL;
207                 knode = NULL;
208                 result = ISC_R_SUCCESS;
209         }
210
211         RWUNLOCK(&keytable->rwlock, isc_rwlocktype_write);
212
213         if (knode != NULL)
214                 isc_mem_put(keytable->mctx, knode, sizeof(*knode));
215
216         return (result);
217 }
218
219 isc_result_t
220 dns_keytable_findkeynode(dns_keytable_t *keytable, dns_name_t *name,
221                          dns_secalg_t algorithm, dns_keytag_t tag,
222                          dns_keynode_t **keynodep)
223 {
224         isc_result_t result;
225         dns_keynode_t *knode;
226         void *data;
227
228         /*
229          * Search for a key named 'name', matching 'algorithm' and 'tag' in
230          * 'keytable'.
231          */
232
233         REQUIRE(VALID_KEYTABLE(keytable));
234         REQUIRE(dns_name_isabsolute(name));
235         REQUIRE(keynodep != NULL && *keynodep == NULL);
236
237         RWLOCK(&keytable->rwlock, isc_rwlocktype_read);
238
239         /*
240          * Note we don't want the DNS_R_PARTIALMATCH from dns_rbt_findname()
241          * as that indicates that 'name' was not found.
242          *
243          * DNS_R_PARTIALMATCH indicates that the name was found but we
244          * didn't get a match on algorithm and key id arguments.
245          */
246         knode = NULL;
247         data = NULL;
248         result = dns_rbt_findname(keytable->table, name, 0, NULL, &data);
249
250         if (result == ISC_R_SUCCESS) {
251                 INSIST(data != NULL);
252                 for (knode = data; knode != NULL; knode = knode->next) {
253                         if (algorithm == dst_key_alg(knode->key)
254                             && tag == dst_key_id(knode->key))
255                                 break;
256                 }
257                 if (knode != NULL) {
258                         LOCK(&keytable->lock);
259                         keytable->active_nodes++;
260                         UNLOCK(&keytable->lock);
261                         *keynodep = knode;
262                 } else
263                         result = DNS_R_PARTIALMATCH;
264         } else if (result == DNS_R_PARTIALMATCH)
265                 result = ISC_R_NOTFOUND;
266
267         RWUNLOCK(&keytable->rwlock, isc_rwlocktype_read);
268
269         return (result);
270 }
271
272 isc_result_t
273 dns_keytable_findnextkeynode(dns_keytable_t *keytable, dns_keynode_t *keynode,
274                              dns_keynode_t **nextnodep)
275 {
276         isc_result_t result;
277         dns_keynode_t *knode;
278
279         /*
280          * Search for the next key with the same properties as 'keynode' in
281          * 'keytable'.
282          */
283
284         REQUIRE(VALID_KEYTABLE(keytable));
285         REQUIRE(VALID_KEYNODE(keynode));
286         REQUIRE(nextnodep != NULL && *nextnodep == NULL);
287
288         for (knode = keynode->next; knode != NULL; knode = knode->next) {
289                 if (dst_key_alg(keynode->key) == dst_key_alg(knode->key) &&
290                     dst_key_id(keynode->key) == dst_key_id(knode->key))
291                         break;
292         }
293         if (knode != NULL) {
294                 LOCK(&keytable->lock);
295                 keytable->active_nodes++;
296                 UNLOCK(&keytable->lock);
297                 result = ISC_R_SUCCESS;
298                 *nextnodep = knode;
299         } else
300                 result = ISC_R_NOTFOUND;
301
302         return (result);
303 }
304
305 isc_result_t
306 dns_keytable_finddeepestmatch(dns_keytable_t *keytable, dns_name_t *name,
307                               dns_name_t *foundname)
308 {
309         isc_result_t result;
310         void *data;
311
312         /*
313          * Search for the deepest match in 'keytable'.
314          */
315
316         REQUIRE(VALID_KEYTABLE(keytable));
317         REQUIRE(dns_name_isabsolute(name));
318         REQUIRE(foundname != NULL);
319
320         RWLOCK(&keytable->rwlock, isc_rwlocktype_read);
321
322         data = NULL;
323         result = dns_rbt_findname(keytable->table, name, 0, foundname, &data);
324
325         if (result == ISC_R_SUCCESS || result == DNS_R_PARTIALMATCH)
326                 result = ISC_R_SUCCESS;
327
328         RWUNLOCK(&keytable->rwlock, isc_rwlocktype_read);
329
330         return (result);
331 }
332
333 void
334 dns_keytable_detachkeynode(dns_keytable_t *keytable, dns_keynode_t **keynodep)
335 {
336         /*
337          * Give back a keynode found via dns_keytable_findkeynode().
338          */
339
340         REQUIRE(VALID_KEYTABLE(keytable));
341         REQUIRE(keynodep != NULL && VALID_KEYNODE(*keynodep));
342
343         LOCK(&keytable->lock);
344         INSIST(keytable->active_nodes > 0);
345         keytable->active_nodes--;
346         UNLOCK(&keytable->lock);
347
348         *keynodep = NULL;
349 }
350
351 isc_result_t
352 dns_keytable_issecuredomain(dns_keytable_t *keytable, dns_name_t *name,
353                             isc_boolean_t *wantdnssecp)
354 {
355         isc_result_t result;
356         void *data;
357
358         /*
359          * Is 'name' at or beneath a trusted key?
360          */
361
362         REQUIRE(VALID_KEYTABLE(keytable));
363         REQUIRE(dns_name_isabsolute(name));
364         REQUIRE(wantdnssecp != NULL);
365
366         RWLOCK(&keytable->rwlock, isc_rwlocktype_read);
367
368         data = NULL;
369         result = dns_rbt_findname(keytable->table, name, 0, NULL, &data);
370
371         if (result == ISC_R_SUCCESS || result == DNS_R_PARTIALMATCH) {
372                 INSIST(data != NULL);
373                 *wantdnssecp = ISC_TRUE;
374                 result = ISC_R_SUCCESS;
375         } else if (result == ISC_R_NOTFOUND) {
376                 *wantdnssecp = ISC_FALSE;
377                 result = ISC_R_SUCCESS;
378         }
379
380         RWUNLOCK(&keytable->rwlock, isc_rwlocktype_read);
381
382         return (result);
383 }
384
385 dst_key_t *
386 dns_keynode_key(dns_keynode_t *keynode) {
387
388         /*
389          * Get the DST key associated with keynode.
390          */
391
392         REQUIRE(VALID_KEYNODE(keynode));
393
394         return (keynode->key);
395 }