Merge branch 'vendor/LIBPCAP' (early part)
[dragonfly.git] / contrib / bind-9.3 / lib / dns / dst_api.c
1 /*
2  * Portions Copyright (C) 2004, 2006  Internet Systems Consortium, Inc. ("ISC")
3  * Portions Copyright (C) 1999-2003  Internet Software Consortium.
4  * Portions Copyright (C) 1995-2000 by Network Associates, Inc.
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND ISC AND NETWORK ASSOCIATES DISCLAIMS
11  * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
12  * WARRANTIES OF MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE
13  * FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
16  * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18
19 /*
20  * Principal Author: Brian Wellington
21  * $Id: dst_api.c,v 1.1.4.3 2006/01/04 23:50:20 marka Exp $
22  */
23
24 #include <config.h>
25
26 #include <stdlib.h>
27
28 #include <isc/buffer.h>
29 #include <isc/dir.h>
30 #include <isc/entropy.h>
31 #include <isc/fsaccess.h>
32 #include <isc/lex.h>
33 #include <isc/mem.h>
34 #include <isc/once.h>
35 #include <isc/print.h>
36 #include <isc/random.h>
37 #include <isc/string.h>
38 #include <isc/time.h>
39 #include <isc/util.h>
40
41 #include <dns/fixedname.h>
42 #include <dns/keyvalues.h>
43 #include <dns/name.h>
44 #include <dns/rdata.h>
45 #include <dns/rdataclass.h>
46 #include <dns/ttl.h>
47 #include <dns/types.h>
48
49 #include <dst/result.h>
50
51 #include "dst_internal.h"
52
53 #define DST_AS_STR(t) ((t).value.as_textregion.base)
54
55 static dst_func_t *dst_t_func[DST_MAX_ALGS];
56 static isc_entropy_t *dst_entropy_pool = NULL;
57 static unsigned int dst_entropy_flags = 0;
58 static isc_boolean_t dst_initialized = ISC_FALSE;
59
60 isc_mem_t *dst__memory_pool = NULL;
61
62 /*
63  * Static functions.
64  */
65 static dst_key_t *      get_key_struct(dns_name_t *name,
66                                        unsigned int alg,
67                                        unsigned int flags,
68                                        unsigned int protocol,
69                                        unsigned int bits,
70                                        dns_rdataclass_t rdclass,
71                                        isc_mem_t *mctx);
72 static isc_result_t     read_public_key(const char *filename,
73                                         int type,
74                                         isc_mem_t *mctx,
75                                         dst_key_t **keyp);
76 static isc_result_t     write_public_key(const dst_key_t *key, int type,
77                                          const char *directory);
78 static isc_result_t     buildfilename(dns_name_t *name,
79                                       dns_keytag_t id,
80                                       unsigned int alg,
81                                       unsigned int type,
82                                       const char *directory,
83                                       isc_buffer_t *out);
84 static isc_result_t     computeid(dst_key_t *key);
85 static isc_result_t     frombuffer(dns_name_t *name,
86                                    unsigned int alg,
87                                    unsigned int flags,
88                                    unsigned int protocol,
89                                    dns_rdataclass_t rdclass,
90                                    isc_buffer_t *source,
91                                    isc_mem_t *mctx,
92                                    dst_key_t **keyp);
93
94 static isc_result_t     algorithm_status(unsigned int alg);
95
96 static isc_result_t     addsuffix(char *filename, unsigned int len,
97                                   const char *ofilename, const char *suffix);
98
99 #define RETERR(x)                               \
100         do {                                    \
101                 result = (x);                   \
102                 if (result != ISC_R_SUCCESS)    \
103                         goto out;               \
104         } while (0)
105
106 #define CHECKALG(alg)                           \
107         do {                                    \
108                 isc_result_t _r;                \
109                 _r = algorithm_status(alg);     \
110                 if (_r != ISC_R_SUCCESS)        \
111                         return (_r);            \
112         } while (0);                            \
113
114 isc_result_t
115 dst_lib_init(isc_mem_t *mctx, isc_entropy_t *ectx, unsigned int eflags) {
116         isc_result_t result;
117
118         REQUIRE(mctx != NULL && ectx != NULL);
119         REQUIRE(dst_initialized == ISC_FALSE);
120
121         dst__memory_pool = NULL;
122
123 #ifdef OPENSSL
124         UNUSED(mctx);
125         /*
126          * When using --with-openssl, there seems to be no good way of not
127          * leaking memory due to the openssl error handling mechanism.
128          * Avoid assertions by using a local memory context and not checking
129          * for leaks on exit.
130          */
131         result = isc_mem_create(0, 0, &dst__memory_pool);
132         if (result != ISC_R_SUCCESS)
133                 return (result);
134         isc_mem_setdestroycheck(dst__memory_pool, ISC_FALSE);
135 #else
136         isc_mem_attach(mctx, &dst__memory_pool);
137 #endif
138         isc_entropy_attach(ectx, &dst_entropy_pool);
139         dst_entropy_flags = eflags;
140
141         dst_result_register();
142
143         memset(dst_t_func, 0, sizeof(dst_t_func));
144         RETERR(dst__hmacmd5_init(&dst_t_func[DST_ALG_HMACMD5]));
145 #ifdef OPENSSL
146         RETERR(dst__openssl_init());
147         RETERR(dst__opensslrsa_init(&dst_t_func[DST_ALG_RSAMD5]));
148         RETERR(dst__opensslrsa_init(&dst_t_func[DST_ALG_RSASHA1]));
149 #ifdef HAVE_OPENSSL_DSA
150         RETERR(dst__openssldsa_init(&dst_t_func[DST_ALG_DSA]));
151 #endif
152         RETERR(dst__openssldh_init(&dst_t_func[DST_ALG_DH]));
153 #endif /* OPENSSL */
154 #ifdef GSSAPI
155         RETERR(dst__gssapi_init(&dst_t_func[DST_ALG_GSSAPI]));
156 #endif
157         dst_initialized = ISC_TRUE;
158         return (ISC_R_SUCCESS);
159
160  out:
161         dst_lib_destroy();
162         return (result);
163 }
164
165 void
166 dst_lib_destroy(void) {
167         int i;
168         RUNTIME_CHECK(dst_initialized == ISC_TRUE);
169         dst_initialized = ISC_FALSE;
170
171         for (i = 0; i < DST_MAX_ALGS; i++)
172                 if (dst_t_func[i] != NULL && dst_t_func[i]->cleanup != NULL)
173                         dst_t_func[i]->cleanup();
174 #ifdef OPENSSL
175         dst__openssl_destroy();
176 #endif
177         if (dst__memory_pool != NULL)
178                 isc_mem_detach(&dst__memory_pool);
179         if (dst_entropy_pool != NULL)
180                 isc_entropy_detach(&dst_entropy_pool);
181
182 }
183
184 isc_boolean_t
185 dst_algorithm_supported(unsigned int alg) {
186         REQUIRE(dst_initialized == ISC_TRUE);
187
188         if (alg >= DST_MAX_ALGS || dst_t_func[alg] == NULL)
189                 return (ISC_FALSE);
190         return (ISC_TRUE);
191 }
192
193 isc_result_t
194 dst_context_create(dst_key_t *key, isc_mem_t *mctx, dst_context_t **dctxp) {
195         dst_context_t *dctx;
196         isc_result_t result;
197
198         REQUIRE(dst_initialized == ISC_TRUE);
199         REQUIRE(VALID_KEY(key));
200         REQUIRE(mctx != NULL);
201         REQUIRE(dctxp != NULL && *dctxp == NULL);
202
203         if (key->func->createctx == NULL)
204                 return (DST_R_UNSUPPORTEDALG);
205         if (key->opaque == NULL)
206                 return (DST_R_NULLKEY);
207
208         dctx = isc_mem_get(mctx, sizeof(dst_context_t));
209         if (dctx == NULL)
210                 return (ISC_R_NOMEMORY);
211         dctx->key = key;
212         dctx->mctx = mctx;
213         result = key->func->createctx(key, dctx);
214         if (result != ISC_R_SUCCESS) {
215                 isc_mem_put(mctx, dctx, sizeof(dst_context_t));
216                 return (result);
217         }
218         dctx->magic = CTX_MAGIC;
219         *dctxp = dctx;
220         return (ISC_R_SUCCESS);
221 }
222
223 void
224 dst_context_destroy(dst_context_t **dctxp) {
225         dst_context_t *dctx;
226
227         REQUIRE(dctxp != NULL && VALID_CTX(*dctxp));
228
229         dctx = *dctxp;
230         INSIST(dctx->key->func->destroyctx != NULL);
231         dctx->key->func->destroyctx(dctx);
232         dctx->magic = 0;
233         isc_mem_put(dctx->mctx, dctx, sizeof(dst_context_t));
234         *dctxp = NULL;
235 }
236
237 isc_result_t
238 dst_context_adddata(dst_context_t *dctx, const isc_region_t *data) {
239         REQUIRE(VALID_CTX(dctx));
240         REQUIRE(data != NULL);
241         INSIST(dctx->key->func->adddata != NULL);
242
243         return (dctx->key->func->adddata(dctx, data));
244 }
245
246 isc_result_t
247 dst_context_sign(dst_context_t *dctx, isc_buffer_t *sig) {
248         dst_key_t *key;
249
250         REQUIRE(VALID_CTX(dctx));
251         REQUIRE(sig != NULL);
252
253         key = dctx->key;
254         CHECKALG(key->key_alg);
255         if (key->opaque == NULL)
256                 return (DST_R_NULLKEY);
257         if (key->func->sign == NULL)
258                 return (DST_R_NOTPRIVATEKEY);
259         if (key->func->isprivate == NULL ||
260             key->func->isprivate(key) == ISC_FALSE)
261                 return (DST_R_NOTPRIVATEKEY);
262
263         return (key->func->sign(dctx, sig));
264 }
265
266 isc_result_t
267 dst_context_verify(dst_context_t *dctx, isc_region_t *sig) {
268         REQUIRE(VALID_CTX(dctx));
269         REQUIRE(sig != NULL);
270
271         CHECKALG(dctx->key->key_alg);
272         if (dctx->key->opaque == NULL)
273                 return (DST_R_NULLKEY);
274         if (dctx->key->func->verify == NULL)
275                 return (DST_R_NOTPUBLICKEY);
276
277         return (dctx->key->func->verify(dctx, sig));
278 }
279
280 isc_result_t
281 dst_key_computesecret(const dst_key_t *pub, const dst_key_t *priv,
282                       isc_buffer_t *secret)
283 {
284         REQUIRE(dst_initialized == ISC_TRUE);
285         REQUIRE(VALID_KEY(pub) && VALID_KEY(priv));
286         REQUIRE(secret != NULL);
287
288         CHECKALG(pub->key_alg);
289         CHECKALG(priv->key_alg);
290
291         if (pub->opaque == NULL || priv->opaque == NULL)
292                 return (DST_R_NULLKEY);
293
294         if (pub->key_alg != priv->key_alg ||
295             pub->func->computesecret == NULL ||
296             priv->func->computesecret == NULL)
297                 return (DST_R_KEYCANNOTCOMPUTESECRET);
298
299         if (dst_key_isprivate(priv) == ISC_FALSE)
300                 return (DST_R_NOTPRIVATEKEY);
301
302         return (pub->func->computesecret(pub, priv, secret));
303 }
304
305 isc_result_t
306 dst_key_tofile(const dst_key_t *key, int type, const char *directory) {
307         isc_result_t ret = ISC_R_SUCCESS;
308
309         REQUIRE(dst_initialized == ISC_TRUE);
310         REQUIRE(VALID_KEY(key));
311         REQUIRE((type & (DST_TYPE_PRIVATE | DST_TYPE_PUBLIC)) != 0);
312
313         CHECKALG(key->key_alg);
314
315         if (key->func->tofile == NULL)
316                 return (DST_R_UNSUPPORTEDALG);
317
318         if (type & DST_TYPE_PUBLIC) {
319                 ret = write_public_key(key, type, directory);
320                 if (ret != ISC_R_SUCCESS)
321                         return (ret);
322         }
323
324         if ((type & DST_TYPE_PRIVATE) &&
325             (key->key_flags & DNS_KEYFLAG_TYPEMASK) != DNS_KEYTYPE_NOKEY)
326                 return (key->func->tofile(key, directory));
327         else
328                 return (ISC_R_SUCCESS);
329 }
330
331 isc_result_t
332 dst_key_fromfile(dns_name_t *name, dns_keytag_t id,
333                  unsigned int alg, int type, const char *directory,
334                  isc_mem_t *mctx, dst_key_t **keyp)
335 {
336         char filename[ISC_DIR_NAMEMAX];
337         isc_buffer_t b;
338         dst_key_t *key;
339         isc_result_t result;
340
341         REQUIRE(dst_initialized == ISC_TRUE);
342         REQUIRE(dns_name_isabsolute(name));
343         REQUIRE((type & (DST_TYPE_PRIVATE | DST_TYPE_PUBLIC)) != 0);
344         REQUIRE(mctx != NULL);
345         REQUIRE(keyp != NULL && *keyp == NULL);
346
347         CHECKALG(alg);
348
349         isc_buffer_init(&b, filename, sizeof(filename));
350         result = buildfilename(name, id, alg, type, directory, &b);
351         if (result != ISC_R_SUCCESS)
352                 return (result);
353
354         key = NULL;
355         result = dst_key_fromnamedfile(filename, type, mctx, &key);
356         if (result != ISC_R_SUCCESS)
357                 return (result);
358
359         result = computeid(key);
360         if (result != ISC_R_SUCCESS) {
361                 dst_key_free(&key);
362                 return (result);
363         }
364
365         if (!dns_name_equal(name, key->key_name) ||
366             id != key->key_id ||
367             alg != key->key_alg)
368         {
369                 dst_key_free(&key);
370                 return (DST_R_INVALIDPRIVATEKEY);
371         }
372         key->key_id = id;
373
374         *keyp = key;
375         return (ISC_R_SUCCESS);
376 }
377
378 isc_result_t
379 dst_key_fromnamedfile(const char *filename, int type, isc_mem_t *mctx,
380                       dst_key_t **keyp)
381 {
382         isc_result_t result;
383         dst_key_t *pubkey = NULL, *key = NULL;
384         dns_keytag_t id;
385         char *newfilename = NULL;
386         int newfilenamelen = 0;
387         isc_lex_t *lex = NULL;
388
389         REQUIRE(dst_initialized == ISC_TRUE);
390         REQUIRE(filename != NULL);
391         REQUIRE((type & (DST_TYPE_PRIVATE | DST_TYPE_PUBLIC)) != 0);
392         REQUIRE(mctx != NULL);
393         REQUIRE(keyp != NULL && *keyp == NULL);
394
395         result = read_public_key(filename, type, mctx, &pubkey);
396         if (result != ISC_R_SUCCESS)
397                 return (result);
398
399         if ((type & (DST_TYPE_PRIVATE | DST_TYPE_PUBLIC)) == DST_TYPE_PUBLIC ||
400             (pubkey->key_flags & DNS_KEYFLAG_TYPEMASK) == DNS_KEYTYPE_NOKEY)
401         {
402                 result = computeid(pubkey);
403                 if (result != ISC_R_SUCCESS) {
404                         dst_key_free(&pubkey);
405                         return (result);
406                 }
407
408                 *keyp = pubkey;
409                 return (ISC_R_SUCCESS);
410         }
411
412         result = algorithm_status(pubkey->key_alg);
413         if (result != ISC_R_SUCCESS) {
414                 dst_key_free(&pubkey);
415                 return (result);
416         }
417
418         key = get_key_struct(pubkey->key_name, pubkey->key_alg,
419                              pubkey->key_flags, pubkey->key_proto, 0,
420                              pubkey->key_class, mctx);
421         id = pubkey->key_id;
422         dst_key_free(&pubkey);
423
424         if (key == NULL)
425                 return (ISC_R_NOMEMORY);
426
427         if (key->func->parse == NULL)
428                 RETERR(DST_R_UNSUPPORTEDALG);
429
430         newfilenamelen = strlen(filename) + 9;
431         newfilename = isc_mem_get(mctx, newfilenamelen);
432         if (newfilename == NULL)
433                 RETERR(ISC_R_NOMEMORY);
434         result = addsuffix(newfilename, newfilenamelen, filename, ".private");
435         INSIST(result == ISC_R_SUCCESS);
436
437         RETERR(isc_lex_create(mctx, 1500, &lex));
438         RETERR(isc_lex_openfile(lex, newfilename));
439         isc_mem_put(mctx, newfilename, newfilenamelen);
440
441         RETERR(key->func->parse(key, lex));
442         isc_lex_destroy(&lex);
443
444         RETERR(computeid(key));
445
446         if (id != key->key_id)
447                 RETERR(DST_R_INVALIDPRIVATEKEY);
448
449         *keyp = key;
450         return (ISC_R_SUCCESS);
451  out:
452         if (newfilename != NULL)
453                 isc_mem_put(mctx, newfilename, newfilenamelen);
454         if (lex != NULL)
455                 isc_lex_destroy(&lex);
456         dst_key_free(&key);
457         return (result);
458 }
459
460 isc_result_t
461 dst_key_todns(const dst_key_t *key, isc_buffer_t *target) {
462         REQUIRE(dst_initialized == ISC_TRUE);
463         REQUIRE(VALID_KEY(key));
464         REQUIRE(target != NULL);
465
466         CHECKALG(key->key_alg);
467
468         if (key->func->todns == NULL)
469                 return (DST_R_UNSUPPORTEDALG);
470
471         if (isc_buffer_availablelength(target) < 4)
472                 return (ISC_R_NOSPACE);
473         isc_buffer_putuint16(target, (isc_uint16_t)(key->key_flags & 0xffff));
474         isc_buffer_putuint8(target, (isc_uint8_t)key->key_proto);
475         isc_buffer_putuint8(target, (isc_uint8_t)key->key_alg);
476
477         if (key->key_flags & DNS_KEYFLAG_EXTENDED) {
478                 if (isc_buffer_availablelength(target) < 2)
479                         return (ISC_R_NOSPACE);
480                 isc_buffer_putuint16(target,
481                                      (isc_uint16_t)((key->key_flags >> 16)
482                                                     & 0xffff));
483         }
484
485         if (key->opaque == NULL) /* NULL KEY */
486                 return (ISC_R_SUCCESS);
487
488         return (key->func->todns(key, target));
489 }
490
491 isc_result_t
492 dst_key_fromdns(dns_name_t *name, dns_rdataclass_t rdclass,
493                 isc_buffer_t *source, isc_mem_t *mctx, dst_key_t **keyp)
494 {
495         isc_uint8_t alg, proto;
496         isc_uint32_t flags, extflags;
497         dst_key_t *key = NULL;
498         dns_keytag_t id;
499         isc_region_t r;
500         isc_result_t result;
501
502         REQUIRE(dst_initialized);
503
504         isc_buffer_remainingregion(source, &r);
505
506         if (isc_buffer_remaininglength(source) < 4)
507                 return (DST_R_INVALIDPUBLICKEY);
508         flags = isc_buffer_getuint16(source);
509         proto = isc_buffer_getuint8(source);
510         alg = isc_buffer_getuint8(source);
511
512         id = dst_region_computeid(&r, alg);
513
514         if (flags & DNS_KEYFLAG_EXTENDED) {
515                 if (isc_buffer_remaininglength(source) < 2)
516                         return (DST_R_INVALIDPUBLICKEY);
517                 extflags = isc_buffer_getuint16(source);
518                 flags |= (extflags << 16);
519         }
520
521         result = frombuffer(name, alg, flags, proto, rdclass, source,
522                             mctx, &key);
523         if (result != ISC_R_SUCCESS)
524                 return (result);
525         key->key_id = id;
526
527         *keyp = key;
528         return (ISC_R_SUCCESS);
529 }
530
531 isc_result_t
532 dst_key_frombuffer(dns_name_t *name, unsigned int alg,
533                    unsigned int flags, unsigned int protocol,
534                    dns_rdataclass_t rdclass,
535                    isc_buffer_t *source, isc_mem_t *mctx, dst_key_t **keyp)
536 {
537         dst_key_t *key = NULL;
538         isc_result_t result;
539
540         REQUIRE(dst_initialized);
541
542         result = frombuffer(name, alg, flags, protocol, rdclass, source,
543                             mctx, &key);
544         if (result != ISC_R_SUCCESS)
545                 return (result);
546
547         result = computeid(key);
548         if (result != ISC_R_SUCCESS) {
549                 dst_key_free(&key);
550                 return (result);
551         }
552
553         *keyp = key;
554         return (ISC_R_SUCCESS);
555 }
556
557 isc_result_t
558 dst_key_tobuffer(const dst_key_t *key, isc_buffer_t *target) {
559         REQUIRE(dst_initialized == ISC_TRUE);
560         REQUIRE(VALID_KEY(key));
561         REQUIRE(target != NULL);
562
563         CHECKALG(key->key_alg);
564
565         if (key->func->todns == NULL)
566                 return (DST_R_UNSUPPORTEDALG);
567
568         return (key->func->todns(key, target));
569 }
570
571 isc_result_t
572 dst_key_privatefrombuffer(dst_key_t *key, isc_buffer_t *buffer) {
573         isc_lex_t *lex = NULL;
574         isc_result_t result = ISC_R_SUCCESS;
575
576         REQUIRE(dst_initialized == ISC_TRUE);
577         REQUIRE(VALID_KEY(key));
578         REQUIRE(!dst_key_isprivate(key));
579         REQUIRE(buffer != NULL);
580
581         if (key->func->parse == NULL)
582                 RETERR(DST_R_UNSUPPORTEDALG);
583
584         RETERR(isc_lex_create(key->mctx, 1500, &lex));
585         RETERR(isc_lex_openbuffer(lex, buffer));
586         RETERR(key->func->parse(key, lex));
587  out:
588         if (lex != NULL)
589                 isc_lex_destroy(&lex);
590         return (result);
591 }
592
593 isc_result_t
594 dst_key_fromgssapi(dns_name_t *name, void *opaque, isc_mem_t *mctx,
595                    dst_key_t **keyp)
596 {
597         dst_key_t *key;
598
599         REQUIRE(opaque != NULL);
600         REQUIRE(keyp != NULL && *keyp == NULL);
601
602         key = get_key_struct(name, DST_ALG_GSSAPI, 0, DNS_KEYPROTO_DNSSEC,
603                              0, dns_rdataclass_in, mctx);
604         if (key == NULL)
605                 return (ISC_R_NOMEMORY);
606         key->opaque = opaque;
607         *keyp = key;
608         return (ISC_R_SUCCESS);
609 }
610
611 isc_result_t
612 dst_key_generate(dns_name_t *name, unsigned int alg,
613                  unsigned int bits, unsigned int param,
614                  unsigned int flags, unsigned int protocol,
615                  dns_rdataclass_t rdclass,
616                  isc_mem_t *mctx, dst_key_t **keyp)
617 {
618         dst_key_t *key;
619         isc_result_t ret;
620
621         REQUIRE(dst_initialized == ISC_TRUE);
622         REQUIRE(dns_name_isabsolute(name));
623         REQUIRE(mctx != NULL);
624         REQUIRE(keyp != NULL && *keyp == NULL);
625
626         CHECKALG(alg);
627
628         key = get_key_struct(name, alg, flags, protocol, bits, rdclass, mctx);
629         if (key == NULL)
630                 return (ISC_R_NOMEMORY);
631
632         if (bits == 0) { /* NULL KEY */
633                 key->key_flags |= DNS_KEYTYPE_NOKEY;
634                 *keyp = key;
635                 return (ISC_R_SUCCESS);
636         }
637
638         if (key->func->generate == NULL) {
639                 dst_key_free(&key);
640                 return (DST_R_UNSUPPORTEDALG);
641         }
642
643         ret = key->func->generate(key, param);
644         if (ret != ISC_R_SUCCESS) {
645                 dst_key_free(&key);
646                 return (ret);
647         }
648
649         ret = computeid(key);
650         if (ret != ISC_R_SUCCESS) {
651                 dst_key_free(&key);
652                 return (ret);
653         }
654
655         *keyp = key;
656         return (ISC_R_SUCCESS);
657 }
658
659 isc_boolean_t
660 dst_key_compare(const dst_key_t *key1, const dst_key_t *key2) {
661         REQUIRE(dst_initialized == ISC_TRUE);
662         REQUIRE(VALID_KEY(key1));
663         REQUIRE(VALID_KEY(key2));
664
665         if (key1 == key2)
666                 return (ISC_TRUE);
667         if (key1 == NULL || key2 == NULL)
668                 return (ISC_FALSE);
669         if (key1->key_alg == key2->key_alg &&
670             key1->key_id == key2->key_id &&
671             key1->func->compare != NULL &&
672             key1->func->compare(key1, key2) == ISC_TRUE)
673                 return (ISC_TRUE);
674         else
675                 return (ISC_FALSE);
676 }
677
678 isc_boolean_t
679 dst_key_paramcompare(const dst_key_t *key1, const dst_key_t *key2) {
680         REQUIRE(dst_initialized == ISC_TRUE);
681         REQUIRE(VALID_KEY(key1));
682         REQUIRE(VALID_KEY(key2));
683
684         if (key1 == key2)
685                 return (ISC_TRUE);
686         if (key1 == NULL || key2 == NULL)
687                 return (ISC_FALSE);
688         if (key1->key_alg == key2->key_alg &&
689             key1->func->paramcompare != NULL &&
690             key1->func->paramcompare(key1, key2) == ISC_TRUE)
691                 return (ISC_TRUE);
692         else
693                 return (ISC_FALSE);
694 }
695
696 void
697 dst_key_free(dst_key_t **keyp) {
698         isc_mem_t *mctx;
699         dst_key_t *key;
700
701         REQUIRE(dst_initialized == ISC_TRUE);
702         REQUIRE(keyp != NULL && VALID_KEY(*keyp));
703
704         key = *keyp;
705         mctx = key->mctx;
706
707         if (key->opaque != NULL) {
708                 INSIST(key->func->destroy != NULL);
709                 key->func->destroy(key);
710         }
711
712         dns_name_free(key->key_name, mctx);
713         isc_mem_put(mctx, key->key_name, sizeof(dns_name_t));
714         memset(key, 0, sizeof(dst_key_t));
715         isc_mem_put(mctx, key, sizeof(dst_key_t));
716         *keyp = NULL;
717 }
718
719 isc_boolean_t
720 dst_key_isprivate(const dst_key_t *key) {
721         REQUIRE(VALID_KEY(key));
722         INSIST(key->func->isprivate != NULL);
723         return (key->func->isprivate(key));
724 }
725
726 isc_result_t
727 dst_key_buildfilename(const dst_key_t *key, int type,
728                       const char *directory, isc_buffer_t *out) {
729
730         REQUIRE(VALID_KEY(key));
731         REQUIRE(type == DST_TYPE_PRIVATE || type == DST_TYPE_PUBLIC ||
732                 type == 0);
733
734         return (buildfilename(key->key_name, key->key_id, key->key_alg,
735                               type, directory, out));
736 }
737
738 isc_result_t
739 dst_key_sigsize(const dst_key_t *key, unsigned int *n) {
740         REQUIRE(dst_initialized == ISC_TRUE);
741         REQUIRE(VALID_KEY(key));
742         REQUIRE(n != NULL);
743
744         /* XXXVIX this switch statement is too sparse to gen a jump table. */
745         switch (key->key_alg) {
746         case DST_ALG_RSAMD5:
747         case DST_ALG_RSASHA1:
748                 *n = (key->key_size + 7) / 8;
749                 break;
750         case DST_ALG_DSA:
751                 *n = DNS_SIG_DSASIGSIZE;
752                 break;
753         case DST_ALG_HMACMD5:
754                 *n = 16;
755                 break;
756         case DST_ALG_GSSAPI:
757                 *n = 128; /* XXX */
758                 break;
759         case DST_ALG_DH:
760         default:
761                 return (DST_R_UNSUPPORTEDALG);
762         }
763         return (ISC_R_SUCCESS);
764 }
765
766 isc_result_t
767 dst_key_secretsize(const dst_key_t *key, unsigned int *n) {
768         REQUIRE(dst_initialized == ISC_TRUE);
769         REQUIRE(VALID_KEY(key));
770         REQUIRE(n != NULL);
771
772         if (key->key_alg == DST_ALG_DH)
773                 *n = (key->key_size + 7) / 8;
774         else
775                 return (DST_R_UNSUPPORTEDALG);
776         return (ISC_R_SUCCESS);
777 }
778
779 /***
780  *** Static methods
781  ***/
782
783 /*
784  * Allocates a key structure and fills in some of the fields.
785  */
786 static dst_key_t *
787 get_key_struct(dns_name_t *name, unsigned int alg,
788                unsigned int flags, unsigned int protocol,
789                unsigned int bits, dns_rdataclass_t rdclass,
790                isc_mem_t *mctx)
791 {
792         dst_key_t *key;
793         isc_result_t result;
794
795         key = (dst_key_t *) isc_mem_get(mctx, sizeof(dst_key_t));
796         if (key == NULL)
797                 return (NULL);
798
799         memset(key, 0, sizeof(dst_key_t));
800         key->magic = KEY_MAGIC;
801
802         key->key_name = isc_mem_get(mctx, sizeof(dns_name_t));
803         if (key->key_name == NULL) {
804                 isc_mem_put(mctx, key, sizeof(dst_key_t));
805                 return (NULL);
806         }
807         dns_name_init(key->key_name, NULL);
808         result = dns_name_dup(name, mctx, key->key_name);
809         if (result != ISC_R_SUCCESS) {
810                 isc_mem_put(mctx, key->key_name, sizeof(dns_name_t));
811                 isc_mem_put(mctx, key, sizeof(dst_key_t));
812                 return (NULL);
813         }
814         key->key_alg = alg;
815         key->key_flags = flags;
816         key->key_proto = protocol;
817         key->mctx = mctx;
818         key->opaque = NULL;
819         key->key_size = bits;
820         key->key_class = rdclass;
821         key->func = dst_t_func[alg];
822         return (key);
823 }
824
825 /*
826  * Reads a public key from disk
827  */
828 static isc_result_t
829 read_public_key(const char *filename, int type,
830                 isc_mem_t *mctx, dst_key_t **keyp)
831 {
832         u_char rdatabuf[DST_KEY_MAXSIZE];
833         isc_buffer_t b;
834         dns_fixedname_t name;
835         isc_lex_t *lex = NULL;
836         isc_token_t token;
837         isc_result_t ret;
838         dns_rdata_t rdata = DNS_RDATA_INIT;
839         unsigned int opt = ISC_LEXOPT_DNSMULTILINE;
840         char *newfilename;
841         unsigned int newfilenamelen;
842         dns_rdataclass_t rdclass = dns_rdataclass_in;
843         isc_lexspecials_t specials;
844         isc_uint32_t ttl;
845         isc_result_t result;
846         dns_rdatatype_t keytype;
847
848         newfilenamelen = strlen(filename) + 5;
849         newfilename = isc_mem_get(mctx, newfilenamelen);
850         if (newfilename == NULL)
851                 return (ISC_R_NOMEMORY);
852         ret = addsuffix(newfilename, newfilenamelen, filename, ".key");
853         INSIST(ret == ISC_R_SUCCESS);
854
855         /*
856          * Open the file and read its formatted contents
857          * File format:
858          *    domain.name [ttl] [class] KEY <flags> <protocol> <algorithm> <key>
859          */
860
861         /* 1500 should be large enough for any key */
862         ret = isc_lex_create(mctx, 1500, &lex);
863         if (ret != ISC_R_SUCCESS)
864                 goto cleanup;
865
866         memset(specials, 0, sizeof(specials));
867         specials['('] = 1;
868         specials[')'] = 1;
869         specials['"'] = 1;
870         isc_lex_setspecials(lex, specials);
871         isc_lex_setcomments(lex, ISC_LEXCOMMENT_DNSMASTERFILE);
872
873         ret = isc_lex_openfile(lex, newfilename);
874         if (ret != ISC_R_SUCCESS)
875                 goto cleanup;
876
877 #define NEXTTOKEN(lex, opt, token) { \
878         ret = isc_lex_gettoken(lex, opt, token); \
879         if (ret != ISC_R_SUCCESS) \
880                 goto cleanup; \
881         }
882
883 #define BADTOKEN() { \
884         ret = ISC_R_UNEXPECTEDTOKEN; \
885         goto cleanup; \
886         }
887
888         /* Read the domain name */
889         NEXTTOKEN(lex, opt, &token);
890         if (token.type != isc_tokentype_string)
891                 BADTOKEN();
892         dns_fixedname_init(&name);
893         isc_buffer_init(&b, DST_AS_STR(token), strlen(DST_AS_STR(token)));
894         isc_buffer_add(&b, strlen(DST_AS_STR(token)));
895         ret = dns_name_fromtext(dns_fixedname_name(&name), &b, dns_rootname,
896                                 ISC_FALSE, NULL);
897         if (ret != ISC_R_SUCCESS)
898                 goto cleanup;
899
900         /* Read the next word: either TTL, class, or 'KEY' */
901         NEXTTOKEN(lex, opt, &token);
902
903         /* If it's a TTL, read the next one */
904         result = dns_ttl_fromtext(&token.value.as_textregion, &ttl);
905         if (result == ISC_R_SUCCESS)
906                 NEXTTOKEN(lex, opt, &token);
907
908         if (token.type != isc_tokentype_string)
909                 BADTOKEN();
910
911         ret = dns_rdataclass_fromtext(&rdclass, &token.value.as_textregion);
912         if (ret == ISC_R_SUCCESS)
913                 NEXTTOKEN(lex, opt, &token);
914
915         if (token.type != isc_tokentype_string)
916                 BADTOKEN();
917
918         if (strcasecmp(DST_AS_STR(token), "DNSKEY") == 0)
919                 keytype = dns_rdatatype_dnskey;
920         else if (strcasecmp(DST_AS_STR(token), "KEY") == 0)
921                 keytype = dns_rdatatype_key; /* SIG(0), TKEY */
922         else
923                 BADTOKEN();
924
925         if (((type & DST_TYPE_KEY) != 0 && keytype != dns_rdatatype_key) ||
926             ((type & DST_TYPE_KEY) == 0 && keytype != dns_rdatatype_dnskey)) {
927                 ret = DST_R_BADKEYTYPE;
928                 goto cleanup;
929         }
930
931         isc_buffer_init(&b, rdatabuf, sizeof(rdatabuf));
932         ret = dns_rdata_fromtext(&rdata, rdclass, keytype, lex, NULL,
933                                  ISC_FALSE, mctx, &b, NULL);
934         if (ret != ISC_R_SUCCESS)
935                 goto cleanup;
936
937         ret = dst_key_fromdns(dns_fixedname_name(&name), rdclass, &b, mctx,
938                               keyp);
939         if (ret != ISC_R_SUCCESS)
940                 goto cleanup;
941
942  cleanup:
943         if (lex != NULL)
944                 isc_lex_destroy(&lex);
945         isc_mem_put(mctx, newfilename, newfilenamelen);
946
947         return (ret);
948 }
949
950 static isc_boolean_t
951 issymmetric(const dst_key_t *key) {
952         REQUIRE(dst_initialized == ISC_TRUE);
953         REQUIRE(VALID_KEY(key));
954
955         /* XXXVIX this switch statement is too sparse to gen a jump table. */
956         switch (key->key_alg) {
957         case DST_ALG_RSAMD5:
958         case DST_ALG_RSASHA1:
959         case DST_ALG_DSA:
960         case DST_ALG_DH:
961                 return (ISC_FALSE);
962         case DST_ALG_HMACMD5:
963         case DST_ALG_GSSAPI:
964                 return (ISC_TRUE);
965         default:
966                 return (ISC_FALSE);
967         }
968 }
969
970 /*
971  * Writes a public key to disk in DNS format.
972  */
973 static isc_result_t
974 write_public_key(const dst_key_t *key, int type, const char *directory) {
975         FILE *fp;
976         isc_buffer_t keyb, textb, fileb, classb;
977         isc_region_t r;
978         char filename[ISC_DIR_NAMEMAX];
979         unsigned char key_array[DST_KEY_MAXSIZE];
980         char text_array[DST_KEY_MAXTEXTSIZE];
981         char class_array[10];
982         isc_result_t ret;
983         dns_rdata_t rdata = DNS_RDATA_INIT;
984         isc_fsaccess_t access;
985
986         REQUIRE(VALID_KEY(key));
987
988         isc_buffer_init(&keyb, key_array, sizeof(key_array));
989         isc_buffer_init(&textb, text_array, sizeof(text_array));
990         isc_buffer_init(&classb, class_array, sizeof(class_array));
991
992         ret = dst_key_todns(key, &keyb);
993         if (ret != ISC_R_SUCCESS)
994                 return (ret);
995
996         isc_buffer_usedregion(&keyb, &r);
997         dns_rdata_fromregion(&rdata, key->key_class, dns_rdatatype_dnskey, &r);
998
999         ret = dns_rdata_totext(&rdata, (dns_name_t *) NULL, &textb);
1000         if (ret != ISC_R_SUCCESS)
1001                 return (DST_R_INVALIDPUBLICKEY);
1002
1003         ret = dns_rdataclass_totext(key->key_class, &classb);
1004         if (ret != ISC_R_SUCCESS)
1005                 return (DST_R_INVALIDPUBLICKEY);
1006
1007         /*
1008          * Make the filename.
1009          */
1010         isc_buffer_init(&fileb, filename, sizeof(filename));
1011         ret = dst_key_buildfilename(key, DST_TYPE_PUBLIC, directory, &fileb);
1012         if (ret != ISC_R_SUCCESS)
1013                 return (ret);
1014
1015         /*
1016          * Create public key file.
1017          */
1018         if ((fp = fopen(filename, "w")) == NULL)
1019                 return (DST_R_WRITEERROR);
1020
1021         if (issymmetric(key)) {
1022                 access = 0;
1023                 isc_fsaccess_add(ISC_FSACCESS_OWNER,
1024                                  ISC_FSACCESS_READ | ISC_FSACCESS_WRITE,
1025                                  &access);
1026                 (void)isc_fsaccess_set(filename, access);
1027         }
1028
1029         ret = dns_name_print(key->key_name, fp);
1030         if (ret != ISC_R_SUCCESS) {
1031                 fclose(fp);
1032                 return (ret);
1033         }
1034
1035         fprintf(fp, " ");
1036
1037         isc_buffer_usedregion(&classb, &r);
1038         fwrite(r.base, 1, r.length, fp);
1039
1040         if ((type & DST_TYPE_KEY) != 0)
1041                 fprintf(fp, " KEY ");
1042         else
1043                 fprintf(fp, " DNSKEY ");
1044
1045         isc_buffer_usedregion(&textb, &r);
1046         fwrite(r.base, 1, r.length, fp);
1047
1048         fputc('\n', fp);
1049         fclose(fp);
1050
1051         return (ISC_R_SUCCESS);
1052 }
1053
1054 static isc_result_t
1055 buildfilename(dns_name_t *name, dns_keytag_t id,
1056               unsigned int alg, unsigned int type,
1057               const char *directory, isc_buffer_t *out)
1058 {
1059         const char *suffix = "";
1060         unsigned int len;
1061         isc_result_t result;
1062
1063         REQUIRE(out != NULL);
1064         if ((type & DST_TYPE_PRIVATE) != 0)
1065                 suffix = ".private";
1066         else if (type == DST_TYPE_PUBLIC)
1067                 suffix = ".key";
1068         if (directory != NULL) {
1069                 if (isc_buffer_availablelength(out) < strlen(directory))
1070                         return (ISC_R_NOSPACE);
1071                 isc_buffer_putstr(out, directory);
1072                 if (strlen(directory) > 0U &&
1073                     directory[strlen(directory) - 1] != '/')
1074                         isc_buffer_putstr(out, "/");
1075         }
1076         if (isc_buffer_availablelength(out) < 1)
1077                 return (ISC_R_NOSPACE);
1078         isc_buffer_putstr(out, "K");
1079         result = dns_name_tofilenametext(name, ISC_FALSE, out);
1080         if (result != ISC_R_SUCCESS)
1081                 return (result);
1082         len = 1 + 3 + 1 + 5 + strlen(suffix) + 1;
1083         if (isc_buffer_availablelength(out) < len)
1084                 return (ISC_R_NOSPACE);
1085         sprintf((char *) isc_buffer_used(out), "+%03d+%05d%s", alg, id, suffix);
1086         isc_buffer_add(out, len);
1087         return (ISC_R_SUCCESS);
1088 }
1089
1090 static isc_result_t
1091 computeid(dst_key_t *key) {
1092         isc_buffer_t dnsbuf;
1093         unsigned char dns_array[DST_KEY_MAXSIZE];
1094         isc_region_t r;
1095         isc_result_t ret;
1096
1097         isc_buffer_init(&dnsbuf, dns_array, sizeof(dns_array));
1098         ret = dst_key_todns(key, &dnsbuf);
1099         if (ret != ISC_R_SUCCESS)
1100                 return (ret);
1101
1102         isc_buffer_usedregion(&dnsbuf, &r);
1103         key->key_id = dst_region_computeid(&r, key->key_alg);
1104         return (ISC_R_SUCCESS);
1105 }
1106
1107 static isc_result_t
1108 frombuffer(dns_name_t *name, unsigned int alg, unsigned int flags,
1109            unsigned int protocol, dns_rdataclass_t rdclass,
1110            isc_buffer_t *source, isc_mem_t *mctx, dst_key_t **keyp)
1111 {
1112         dst_key_t *key;
1113         isc_result_t ret;
1114
1115         REQUIRE(dns_name_isabsolute(name));
1116         REQUIRE(source != NULL);
1117         REQUIRE(mctx != NULL);
1118         REQUIRE(keyp != NULL && *keyp == NULL);
1119
1120         key = get_key_struct(name, alg, flags, protocol, 0, rdclass, mctx);
1121         if (key == NULL)
1122                 return (ISC_R_NOMEMORY);
1123
1124         if (isc_buffer_remaininglength(source) > 0) {
1125                 ret = algorithm_status(alg);
1126                 if (ret != ISC_R_SUCCESS) {
1127                         dst_key_free(&key);
1128                         return (ret);
1129                 }
1130                 if (key->func->fromdns == NULL) {
1131                         dst_key_free(&key);
1132                         return (DST_R_UNSUPPORTEDALG);
1133                 }
1134
1135                 ret = key->func->fromdns(key, source);
1136                 if (ret != ISC_R_SUCCESS) {
1137                         dst_key_free(&key);
1138                         return (ret);
1139                 }
1140         }
1141
1142         *keyp = key;
1143         return (ISC_R_SUCCESS);
1144 }
1145
1146 static isc_result_t
1147 algorithm_status(unsigned int alg) {
1148         REQUIRE(dst_initialized == ISC_TRUE);
1149
1150         if (dst_algorithm_supported(alg))
1151                 return (ISC_R_SUCCESS);
1152 #ifndef OPENSSL
1153         if (alg == DST_ALG_RSAMD5 || alg == DST_ALG_RSASHA1 ||
1154             alg == DST_ALG_DSA || alg == DST_ALG_DH ||
1155             alg == DST_ALG_HMACMD5)
1156                 return (DST_R_NOCRYPTO);
1157 #endif
1158         return (DST_R_UNSUPPORTEDALG);
1159 }
1160
1161 static isc_result_t
1162 addsuffix(char *filename, unsigned int len, const char *ofilename,
1163           const char *suffix)
1164 {
1165         int olen = strlen(ofilename);
1166         int n;
1167
1168         if (olen > 1 && ofilename[olen - 1] == '.')
1169                 olen -= 1;
1170         else if (olen > 8 && strcmp(ofilename + olen - 8, ".private") == 0)
1171                 olen -= 8;
1172         else if (olen > 4 && strcmp(ofilename + olen - 4, ".key") == 0)
1173                 olen -= 4;
1174
1175         n = snprintf(filename, len, "%.*s%s", olen, ofilename, suffix);
1176         if (n < 0)
1177                 return (ISC_R_NOSPACE);
1178         return (ISC_R_SUCCESS);
1179 }
1180
1181 isc_result_t
1182 dst__entropy_getdata(void *buf, unsigned int len, isc_boolean_t pseudo) {
1183         unsigned int flags = dst_entropy_flags;
1184         if (pseudo)
1185                 flags &= ~ISC_ENTROPY_GOODONLY;
1186         return (isc_entropy_getdata(dst_entropy_pool, buf, len, NULL, flags));
1187 }