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