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