dst_api.c revision 254402
1/*
2 * Portions Copyright (C) 2004-2013  Internet Systems Consortium, Inc. ("ISC")
3 * Portions Copyright (C) 1999-2003  Internet Software Consortium.
4 *
5 * Permission to use, copy, modify, and/or distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC AND NETWORK ASSOCIATES DISCLAIMS
10 * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
11 * WARRANTIES OF MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE
12 * FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
15 * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 *
17 * Portions Copyright (C) 1995-2000 by Network Associates, Inc.
18 *
19 * Permission to use, copy, modify, and/or distribute this software for any
20 * purpose with or without fee is hereby granted, provided that the above
21 * copyright notice and this permission notice appear in all copies.
22 *
23 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC AND NETWORK ASSOCIATES DISCLAIMS
24 * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
25 * WARRANTIES OF MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE
26 * FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
27 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
28 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
29 * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
30 */
31
32/*
33 * Principal Author: Brian Wellington
34 * $Id$
35 */
36
37/*! \file */
38
39#include <config.h>
40
41#include <stdlib.h>
42#include <time.h>
43
44#include <isc/buffer.h>
45#include <isc/dir.h>
46#include <isc/entropy.h>
47#include <isc/fsaccess.h>
48#include <isc/hmacsha.h>
49#include <isc/lex.h>
50#include <isc/mem.h>
51#include <isc/once.h>
52#include <isc/platform.h>
53#include <isc/print.h>
54#include <isc/refcount.h>
55#include <isc/random.h>
56#include <isc/string.h>
57#include <isc/time.h>
58#include <isc/util.h>
59#include <isc/file.h>
60
61#include <dns/fixedname.h>
62#include <dns/keyvalues.h>
63#include <dns/name.h>
64#include <dns/rdata.h>
65#include <dns/rdataclass.h>
66#include <dns/ttl.h>
67#include <dns/types.h>
68
69#include <dst/result.h>
70
71#include "dst_internal.h"
72
73#define DST_AS_STR(t) ((t).value.as_textregion.base)
74
75static dst_func_t *dst_t_func[DST_MAX_ALGS];
76#ifdef BIND9
77static isc_entropy_t *dst_entropy_pool = NULL;
78#endif
79static unsigned int dst_entropy_flags = 0;
80static isc_boolean_t dst_initialized = ISC_FALSE;
81
82void gss_log(int level, const char *fmt, ...) ISC_FORMAT_PRINTF(2, 3);
83
84isc_mem_t *dst__memory_pool = NULL;
85
86/*
87 * Static functions.
88 */
89static dst_key_t *	get_key_struct(dns_name_t *name,
90				       unsigned int alg,
91				       unsigned int flags,
92				       unsigned int protocol,
93				       unsigned int bits,
94				       dns_rdataclass_t rdclass,
95				       isc_mem_t *mctx);
96static isc_result_t	write_public_key(const dst_key_t *key, int type,
97					 const char *directory);
98static isc_result_t	buildfilename(dns_name_t *name,
99				      dns_keytag_t id,
100				      unsigned int alg,
101				      unsigned int type,
102				      const char *directory,
103				      isc_buffer_t *out);
104static isc_result_t	computeid(dst_key_t *key);
105static isc_result_t	frombuffer(dns_name_t *name,
106				   unsigned int alg,
107				   unsigned int flags,
108				   unsigned int protocol,
109				   dns_rdataclass_t rdclass,
110				   isc_buffer_t *source,
111				   isc_mem_t *mctx,
112				   dst_key_t **keyp);
113
114static isc_result_t	algorithm_status(unsigned int alg);
115
116static isc_result_t	addsuffix(char *filename, int len,
117				  const char *dirname, const char *ofilename,
118				  const char *suffix);
119
120#define RETERR(x)				\
121	do {					\
122		result = (x);			\
123		if (result != ISC_R_SUCCESS)	\
124			goto out;		\
125	} while (0)
126
127#define CHECKALG(alg)				\
128	do {					\
129		isc_result_t _r;		\
130		_r = algorithm_status(alg);	\
131		if (_r != ISC_R_SUCCESS)	\
132			return (_r);		\
133	} while (0);				\
134
135#if defined(OPENSSL) && defined(BIND9)
136static void *
137default_memalloc(void *arg, size_t size) {
138	UNUSED(arg);
139	if (size == 0U)
140		size = 1;
141	return (malloc(size));
142}
143
144static void
145default_memfree(void *arg, void *ptr) {
146	UNUSED(arg);
147	free(ptr);
148}
149#endif
150
151isc_result_t
152dst_lib_init(isc_mem_t *mctx, isc_entropy_t *ectx, unsigned int eflags) {
153	return (dst_lib_init2(mctx, ectx, NULL, eflags));
154}
155
156isc_result_t
157dst_lib_init2(isc_mem_t *mctx, isc_entropy_t *ectx,
158	      const char *engine, unsigned int eflags) {
159	isc_result_t result;
160
161	REQUIRE(mctx != NULL);
162#ifdef BIND9
163	REQUIRE(ectx != NULL);
164#else
165	UNUSED(ectx);
166#endif
167	REQUIRE(dst_initialized == ISC_FALSE);
168
169#ifndef OPENSSL
170	UNUSED(engine);
171#endif
172
173	dst__memory_pool = NULL;
174
175#if defined(OPENSSL) && defined(BIND9)
176	UNUSED(mctx);
177	/*
178	 * When using --with-openssl, there seems to be no good way of not
179	 * leaking memory due to the openssl error handling mechanism.
180	 * Avoid assertions by using a local memory context and not checking
181	 * for leaks on exit.  Note: as there are leaks we cannot use
182	 * ISC_MEMFLAG_INTERNAL as it will free up memory still being used
183	 * by libcrypto.
184	 */
185	result = isc_mem_createx2(0, 0, default_memalloc, default_memfree,
186				  NULL, &dst__memory_pool, 0);
187	if (result != ISC_R_SUCCESS)
188		return (result);
189	isc_mem_setname(dst__memory_pool, "dst", NULL);
190#ifndef OPENSSL_LEAKS
191	isc_mem_setdestroycheck(dst__memory_pool, ISC_FALSE);
192#endif
193#else
194	isc_mem_attach(mctx, &dst__memory_pool);
195#endif
196#ifdef BIND9
197	isc_entropy_attach(ectx, &dst_entropy_pool);
198#endif
199	dst_entropy_flags = eflags;
200
201	dst_result_register();
202
203	memset(dst_t_func, 0, sizeof(dst_t_func));
204	RETERR(dst__hmacmd5_init(&dst_t_func[DST_ALG_HMACMD5]));
205	RETERR(dst__hmacsha1_init(&dst_t_func[DST_ALG_HMACSHA1]));
206	RETERR(dst__hmacsha224_init(&dst_t_func[DST_ALG_HMACSHA224]));
207	RETERR(dst__hmacsha256_init(&dst_t_func[DST_ALG_HMACSHA256]));
208	RETERR(dst__hmacsha384_init(&dst_t_func[DST_ALG_HMACSHA384]));
209	RETERR(dst__hmacsha512_init(&dst_t_func[DST_ALG_HMACSHA512]));
210#ifdef OPENSSL
211	RETERR(dst__openssl_init(engine));
212	RETERR(dst__opensslrsa_init(&dst_t_func[DST_ALG_RSAMD5],
213				    DST_ALG_RSAMD5));
214	RETERR(dst__opensslrsa_init(&dst_t_func[DST_ALG_RSASHA1],
215				    DST_ALG_RSASHA1));
216	RETERR(dst__opensslrsa_init(&dst_t_func[DST_ALG_NSEC3RSASHA1],
217				    DST_ALG_NSEC3RSASHA1));
218	RETERR(dst__opensslrsa_init(&dst_t_func[DST_ALG_RSASHA256],
219				    DST_ALG_RSASHA256));
220	RETERR(dst__opensslrsa_init(&dst_t_func[DST_ALG_RSASHA512],
221				    DST_ALG_RSASHA512));
222#ifdef HAVE_OPENSSL_DSA
223	RETERR(dst__openssldsa_init(&dst_t_func[DST_ALG_DSA]));
224	RETERR(dst__openssldsa_init(&dst_t_func[DST_ALG_NSEC3DSA]));
225#endif
226	RETERR(dst__openssldh_init(&dst_t_func[DST_ALG_DH]));
227#ifdef HAVE_OPENSSL_GOST
228	RETERR(dst__opensslgost_init(&dst_t_func[DST_ALG_ECCGOST]));
229#endif
230#ifdef HAVE_OPENSSL_ECDSA
231	RETERR(dst__opensslecdsa_init(&dst_t_func[DST_ALG_ECDSA256]));
232	RETERR(dst__opensslecdsa_init(&dst_t_func[DST_ALG_ECDSA384]));
233#endif
234#endif /* OPENSSL */
235#ifdef GSSAPI
236	RETERR(dst__gssapi_init(&dst_t_func[DST_ALG_GSSAPI]));
237#endif
238	dst_initialized = ISC_TRUE;
239	return (ISC_R_SUCCESS);
240
241 out:
242	/* avoid immediate crash! */
243	dst_initialized = ISC_TRUE;
244	dst_lib_destroy();
245	return (result);
246}
247
248void
249dst_lib_destroy(void) {
250	int i;
251	RUNTIME_CHECK(dst_initialized == ISC_TRUE);
252	dst_initialized = ISC_FALSE;
253
254	for (i = 0; i < DST_MAX_ALGS; i++)
255		if (dst_t_func[i] != NULL && dst_t_func[i]->cleanup != NULL)
256			dst_t_func[i]->cleanup();
257#ifdef OPENSSL
258	dst__openssl_destroy();
259#endif
260	if (dst__memory_pool != NULL)
261		isc_mem_detach(&dst__memory_pool);
262#ifdef BIND9
263	if (dst_entropy_pool != NULL)
264		isc_entropy_detach(&dst_entropy_pool);
265#endif
266}
267
268isc_boolean_t
269dst_algorithm_supported(unsigned int alg) {
270	REQUIRE(dst_initialized == ISC_TRUE);
271
272	if (alg >= DST_MAX_ALGS || dst_t_func[alg] == NULL)
273		return (ISC_FALSE);
274	return (ISC_TRUE);
275}
276
277isc_result_t
278dst_context_create(dst_key_t *key, isc_mem_t *mctx, dst_context_t **dctxp) {
279	return (dst_context_create2(key, mctx,
280				    DNS_LOGCATEGORY_GENERAL, dctxp));
281}
282
283isc_result_t
284dst_context_create2(dst_key_t *key, isc_mem_t *mctx,
285		    isc_logcategory_t *category, dst_context_t **dctxp) {
286	dst_context_t *dctx;
287	isc_result_t result;
288
289	REQUIRE(dst_initialized == ISC_TRUE);
290	REQUIRE(VALID_KEY(key));
291	REQUIRE(mctx != NULL);
292	REQUIRE(dctxp != NULL && *dctxp == NULL);
293
294	if (key->func->createctx == NULL)
295		return (DST_R_UNSUPPORTEDALG);
296	if (key->keydata.generic == NULL)
297		return (DST_R_NULLKEY);
298
299	dctx = isc_mem_get(mctx, sizeof(dst_context_t));
300	if (dctx == NULL)
301		return (ISC_R_NOMEMORY);
302	dctx->key = key;
303	dctx->mctx = mctx;
304	dctx->category = category;
305	result = key->func->createctx(key, dctx);
306	if (result != ISC_R_SUCCESS) {
307		isc_mem_put(mctx, dctx, sizeof(dst_context_t));
308		return (result);
309	}
310	dctx->magic = CTX_MAGIC;
311	*dctxp = dctx;
312	return (ISC_R_SUCCESS);
313}
314
315void
316dst_context_destroy(dst_context_t **dctxp) {
317	dst_context_t *dctx;
318
319	REQUIRE(dctxp != NULL && VALID_CTX(*dctxp));
320
321	dctx = *dctxp;
322	INSIST(dctx->key->func->destroyctx != NULL);
323	dctx->key->func->destroyctx(dctx);
324	dctx->magic = 0;
325	isc_mem_put(dctx->mctx, dctx, sizeof(dst_context_t));
326	*dctxp = NULL;
327}
328
329isc_result_t
330dst_context_adddata(dst_context_t *dctx, const isc_region_t *data) {
331	REQUIRE(VALID_CTX(dctx));
332	REQUIRE(data != NULL);
333	INSIST(dctx->key->func->adddata != NULL);
334
335	return (dctx->key->func->adddata(dctx, data));
336}
337
338isc_result_t
339dst_context_sign(dst_context_t *dctx, isc_buffer_t *sig) {
340	dst_key_t *key;
341
342	REQUIRE(VALID_CTX(dctx));
343	REQUIRE(sig != NULL);
344
345	key = dctx->key;
346	CHECKALG(key->key_alg);
347	if (key->keydata.generic == NULL)
348		return (DST_R_NULLKEY);
349
350	if (key->func->sign == NULL)
351		return (DST_R_NOTPRIVATEKEY);
352	if (key->func->isprivate == NULL ||
353	    key->func->isprivate(key) == ISC_FALSE)
354		return (DST_R_NOTPRIVATEKEY);
355
356	return (key->func->sign(dctx, sig));
357}
358
359isc_result_t
360dst_context_verify(dst_context_t *dctx, isc_region_t *sig) {
361	REQUIRE(VALID_CTX(dctx));
362	REQUIRE(sig != NULL);
363
364	CHECKALG(dctx->key->key_alg);
365	if (dctx->key->keydata.generic == NULL)
366		return (DST_R_NULLKEY);
367	if (dctx->key->func->verify == NULL)
368		return (DST_R_NOTPUBLICKEY);
369
370	return (dctx->key->func->verify(dctx, sig));
371}
372
373isc_result_t
374dst_key_computesecret(const dst_key_t *pub, const dst_key_t *priv,
375		      isc_buffer_t *secret)
376{
377	REQUIRE(dst_initialized == ISC_TRUE);
378	REQUIRE(VALID_KEY(pub) && VALID_KEY(priv));
379	REQUIRE(secret != NULL);
380
381	CHECKALG(pub->key_alg);
382	CHECKALG(priv->key_alg);
383
384	if (pub->keydata.generic == NULL || priv->keydata.generic == NULL)
385		return (DST_R_NULLKEY);
386
387	if (pub->key_alg != priv->key_alg ||
388	    pub->func->computesecret == NULL ||
389	    priv->func->computesecret == NULL)
390		return (DST_R_KEYCANNOTCOMPUTESECRET);
391
392	if (dst_key_isprivate(priv) == ISC_FALSE)
393		return (DST_R_NOTPRIVATEKEY);
394
395	return (pub->func->computesecret(pub, priv, secret));
396}
397
398isc_result_t
399dst_key_tofile(const dst_key_t *key, int type, const char *directory) {
400	isc_result_t ret = ISC_R_SUCCESS;
401
402	REQUIRE(dst_initialized == ISC_TRUE);
403	REQUIRE(VALID_KEY(key));
404	REQUIRE((type & (DST_TYPE_PRIVATE | DST_TYPE_PUBLIC)) != 0);
405
406	CHECKALG(key->key_alg);
407
408	if (key->func->tofile == NULL)
409		return (DST_R_UNSUPPORTEDALG);
410
411	if (type & DST_TYPE_PUBLIC) {
412		ret = write_public_key(key, type, directory);
413		if (ret != ISC_R_SUCCESS)
414			return (ret);
415	}
416
417	if ((type & DST_TYPE_PRIVATE) &&
418	    (key->key_flags & DNS_KEYFLAG_TYPEMASK) != DNS_KEYTYPE_NOKEY)
419		return (key->func->tofile(key, directory));
420	else
421		return (ISC_R_SUCCESS);
422}
423
424isc_result_t
425dst_key_fromfile(dns_name_t *name, dns_keytag_t id,
426		 unsigned int alg, int type, const char *directory,
427		 isc_mem_t *mctx, dst_key_t **keyp)
428{
429	char filename[ISC_DIR_NAMEMAX];
430	isc_buffer_t b;
431	dst_key_t *key;
432	isc_result_t result;
433
434	REQUIRE(dst_initialized == ISC_TRUE);
435	REQUIRE(dns_name_isabsolute(name));
436	REQUIRE((type & (DST_TYPE_PRIVATE | DST_TYPE_PUBLIC)) != 0);
437	REQUIRE(mctx != NULL);
438	REQUIRE(keyp != NULL && *keyp == NULL);
439
440	CHECKALG(alg);
441
442	isc_buffer_init(&b, filename, sizeof(filename));
443	result = buildfilename(name, id, alg, type, directory, &b);
444	if (result != ISC_R_SUCCESS)
445		return (result);
446
447	key = NULL;
448	result = dst_key_fromnamedfile(filename, NULL, type, mctx, &key);
449	if (result != ISC_R_SUCCESS)
450		return (result);
451
452	result = computeid(key);
453	if (result != ISC_R_SUCCESS) {
454		dst_key_free(&key);
455		return (result);
456	}
457
458	if (!dns_name_equal(name, key->key_name) || id != key->key_id ||
459	    alg != key->key_alg) {
460		dst_key_free(&key);
461		return (DST_R_INVALIDPRIVATEKEY);
462	}
463
464	*keyp = key;
465	return (ISC_R_SUCCESS);
466}
467
468isc_result_t
469dst_key_fromnamedfile(const char *filename, const char *dirname,
470		      int type, isc_mem_t *mctx, dst_key_t **keyp)
471{
472	isc_result_t result;
473	dst_key_t *pubkey = NULL, *key = NULL;
474	char *newfilename = NULL;
475	int newfilenamelen = 0;
476	isc_lex_t *lex = NULL;
477
478	REQUIRE(dst_initialized == ISC_TRUE);
479	REQUIRE(filename != NULL);
480	REQUIRE((type & (DST_TYPE_PRIVATE | DST_TYPE_PUBLIC)) != 0);
481	REQUIRE(mctx != NULL);
482	REQUIRE(keyp != NULL && *keyp == NULL);
483
484	/* If an absolute path is specified, don't use the key directory */
485#ifndef WIN32
486	if (filename[0] == '/')
487		dirname = NULL;
488#else /* WIN32 */
489	if (filename[0] == '/' || filename[0] == '\\')
490		dirname = NULL;
491#endif
492
493	newfilenamelen = strlen(filename) + 5;
494	if (dirname != NULL)
495		newfilenamelen += strlen(dirname) + 1;
496	newfilename = isc_mem_get(mctx, newfilenamelen);
497	if (newfilename == NULL)
498		return (ISC_R_NOMEMORY);
499	result = addsuffix(newfilename, newfilenamelen,
500			   dirname, filename, ".key");
501	INSIST(result == ISC_R_SUCCESS);
502
503	result = dst_key_read_public(newfilename, type, mctx, &pubkey);
504	isc_mem_put(mctx, newfilename, newfilenamelen);
505	newfilename = NULL;
506	RETERR(result);
507
508	if ((type & (DST_TYPE_PRIVATE | DST_TYPE_PUBLIC)) == DST_TYPE_PUBLIC ||
509	    (pubkey->key_flags & DNS_KEYFLAG_TYPEMASK) == DNS_KEYTYPE_NOKEY) {
510		result = computeid(pubkey);
511		if (result != ISC_R_SUCCESS) {
512			dst_key_free(&pubkey);
513			return (result);
514		}
515
516		*keyp = pubkey;
517		return (ISC_R_SUCCESS);
518	}
519
520	result = algorithm_status(pubkey->key_alg);
521	if (result != ISC_R_SUCCESS) {
522		dst_key_free(&pubkey);
523		return (result);
524	}
525
526	key = get_key_struct(pubkey->key_name, pubkey->key_alg,
527			     pubkey->key_flags, pubkey->key_proto, 0,
528			     pubkey->key_class, mctx);
529	if (key == NULL) {
530		dst_key_free(&pubkey);
531		return (ISC_R_NOMEMORY);
532	}
533
534	if (key->func->parse == NULL)
535		RETERR(DST_R_UNSUPPORTEDALG);
536
537	newfilenamelen = strlen(filename) + 9;
538	if (dirname != NULL)
539		newfilenamelen += strlen(dirname) + 1;
540	newfilename = isc_mem_get(mctx, newfilenamelen);
541	if (newfilename == NULL)
542		RETERR(ISC_R_NOMEMORY);
543	result = addsuffix(newfilename, newfilenamelen,
544			   dirname, filename, ".private");
545	INSIST(result == ISC_R_SUCCESS);
546
547	RETERR(isc_lex_create(mctx, 1500, &lex));
548	RETERR(isc_lex_openfile(lex, newfilename));
549	isc_mem_put(mctx, newfilename, newfilenamelen);
550
551	RETERR(key->func->parse(key, lex, pubkey));
552	isc_lex_destroy(&lex);
553
554	RETERR(computeid(key));
555
556	if (pubkey->key_id != key->key_id)
557		RETERR(DST_R_INVALIDPRIVATEKEY);
558	dst_key_free(&pubkey);
559
560	*keyp = key;
561	return (ISC_R_SUCCESS);
562
563 out:
564	if (pubkey != NULL)
565		dst_key_free(&pubkey);
566	if (newfilename != NULL)
567		isc_mem_put(mctx, newfilename, newfilenamelen);
568	if (lex != NULL)
569		isc_lex_destroy(&lex);
570	if (key != NULL)
571		dst_key_free(&key);
572	return (result);
573}
574
575isc_result_t
576dst_key_todns(const dst_key_t *key, isc_buffer_t *target) {
577	REQUIRE(dst_initialized == ISC_TRUE);
578	REQUIRE(VALID_KEY(key));
579	REQUIRE(target != NULL);
580
581	CHECKALG(key->key_alg);
582
583	if (key->func->todns == NULL)
584		return (DST_R_UNSUPPORTEDALG);
585
586	if (isc_buffer_availablelength(target) < 4)
587		return (ISC_R_NOSPACE);
588	isc_buffer_putuint16(target, (isc_uint16_t)(key->key_flags & 0xffff));
589	isc_buffer_putuint8(target, (isc_uint8_t)key->key_proto);
590	isc_buffer_putuint8(target, (isc_uint8_t)key->key_alg);
591
592	if (key->key_flags & DNS_KEYFLAG_EXTENDED) {
593		if (isc_buffer_availablelength(target) < 2)
594			return (ISC_R_NOSPACE);
595		isc_buffer_putuint16(target,
596				     (isc_uint16_t)((key->key_flags >> 16)
597						    & 0xffff));
598	}
599
600	if (key->keydata.generic == NULL) /*%< NULL KEY */
601		return (ISC_R_SUCCESS);
602
603	return (key->func->todns(key, target));
604}
605
606isc_result_t
607dst_key_fromdns(dns_name_t *name, dns_rdataclass_t rdclass,
608		isc_buffer_t *source, isc_mem_t *mctx, dst_key_t **keyp)
609{
610	isc_uint8_t alg, proto;
611	isc_uint32_t flags, extflags;
612	dst_key_t *key = NULL;
613	dns_keytag_t id, rid;
614	isc_region_t r;
615	isc_result_t result;
616
617	REQUIRE(dst_initialized);
618
619	isc_buffer_remainingregion(source, &r);
620
621	if (isc_buffer_remaininglength(source) < 4)
622		return (DST_R_INVALIDPUBLICKEY);
623	flags = isc_buffer_getuint16(source);
624	proto = isc_buffer_getuint8(source);
625	alg = isc_buffer_getuint8(source);
626
627	id = dst_region_computeid(&r, alg);
628	rid = dst_region_computerid(&r, alg);
629
630	if (flags & DNS_KEYFLAG_EXTENDED) {
631		if (isc_buffer_remaininglength(source) < 2)
632			return (DST_R_INVALIDPUBLICKEY);
633		extflags = isc_buffer_getuint16(source);
634		flags |= (extflags << 16);
635	}
636
637	result = frombuffer(name, alg, flags, proto, rdclass, source,
638			    mctx, &key);
639	if (result != ISC_R_SUCCESS)
640		return (result);
641	key->key_id = id;
642	key->key_rid = rid;
643
644	*keyp = key;
645	return (ISC_R_SUCCESS);
646}
647
648isc_result_t
649dst_key_frombuffer(dns_name_t *name, unsigned int alg,
650		   unsigned int flags, unsigned int protocol,
651		   dns_rdataclass_t rdclass,
652		   isc_buffer_t *source, isc_mem_t *mctx, dst_key_t **keyp)
653{
654	dst_key_t *key = NULL;
655	isc_result_t result;
656
657	REQUIRE(dst_initialized);
658
659	result = frombuffer(name, alg, flags, protocol, rdclass, source,
660			    mctx, &key);
661	if (result != ISC_R_SUCCESS)
662		return (result);
663
664	result = computeid(key);
665	if (result != ISC_R_SUCCESS) {
666		dst_key_free(&key);
667		return (result);
668	}
669
670	*keyp = key;
671	return (ISC_R_SUCCESS);
672}
673
674isc_result_t
675dst_key_tobuffer(const dst_key_t *key, isc_buffer_t *target) {
676	REQUIRE(dst_initialized == ISC_TRUE);
677	REQUIRE(VALID_KEY(key));
678	REQUIRE(target != NULL);
679
680	CHECKALG(key->key_alg);
681
682	if (key->func->todns == NULL)
683		return (DST_R_UNSUPPORTEDALG);
684
685	return (key->func->todns(key, target));
686}
687
688isc_result_t
689dst_key_privatefrombuffer(dst_key_t *key, isc_buffer_t *buffer) {
690	isc_lex_t *lex = NULL;
691	isc_result_t result = ISC_R_SUCCESS;
692
693	REQUIRE(dst_initialized == ISC_TRUE);
694	REQUIRE(VALID_KEY(key));
695	REQUIRE(!dst_key_isprivate(key));
696	REQUIRE(buffer != NULL);
697
698	if (key->func->parse == NULL)
699		RETERR(DST_R_UNSUPPORTEDALG);
700
701	RETERR(isc_lex_create(key->mctx, 1500, &lex));
702	RETERR(isc_lex_openbuffer(lex, buffer));
703	RETERR(key->func->parse(key, lex, NULL));
704 out:
705	if (lex != NULL)
706		isc_lex_destroy(&lex);
707	return (result);
708}
709
710gss_ctx_id_t
711dst_key_getgssctx(const dst_key_t *key)
712{
713	REQUIRE(key != NULL);
714
715	return (key->keydata.gssctx);
716}
717
718isc_result_t
719dst_key_fromgssapi(dns_name_t *name, gss_ctx_id_t gssctx, isc_mem_t *mctx,
720		   dst_key_t **keyp, isc_region_t *intoken)
721{
722	dst_key_t *key;
723	isc_result_t result;
724
725	REQUIRE(gssctx != NULL);
726	REQUIRE(keyp != NULL && *keyp == NULL);
727
728	key = get_key_struct(name, DST_ALG_GSSAPI, 0, DNS_KEYPROTO_DNSSEC,
729			     0, dns_rdataclass_in, mctx);
730	if (key == NULL)
731		return (ISC_R_NOMEMORY);
732
733	if (intoken != NULL) {
734		/*
735		 * Keep the token for use by external ssu rules. They may need
736		 * to examine the PAC in the kerberos ticket.
737		 */
738		RETERR(isc_buffer_allocate(key->mctx, &key->key_tkeytoken,
739		       intoken->length));
740		RETERR(isc_buffer_copyregion(key->key_tkeytoken, intoken));
741	}
742
743	key->keydata.gssctx = gssctx;
744	*keyp = key;
745	result = ISC_R_SUCCESS;
746out:
747	return result;
748}
749
750isc_result_t
751dst_key_fromlabel(dns_name_t *name, int alg, unsigned int flags,
752		  unsigned int protocol, dns_rdataclass_t rdclass,
753		  const char *engine, const char *label, const char *pin,
754		  isc_mem_t *mctx, dst_key_t **keyp)
755{
756	dst_key_t *key;
757	isc_result_t result;
758
759	REQUIRE(dst_initialized == ISC_TRUE);
760	REQUIRE(dns_name_isabsolute(name));
761	REQUIRE(mctx != NULL);
762	REQUIRE(keyp != NULL && *keyp == NULL);
763	REQUIRE(label != NULL);
764
765	CHECKALG(alg);
766
767	key = get_key_struct(name, alg, flags, protocol, 0, rdclass, mctx);
768	if (key == NULL)
769		return (ISC_R_NOMEMORY);
770
771	if (key->func->fromlabel == NULL) {
772		dst_key_free(&key);
773		return (DST_R_UNSUPPORTEDALG);
774	}
775
776	result = key->func->fromlabel(key, engine, label, pin);
777	if (result != ISC_R_SUCCESS) {
778		dst_key_free(&key);
779		return (result);
780	}
781
782	result = computeid(key);
783	if (result != ISC_R_SUCCESS) {
784		dst_key_free(&key);
785		return (result);
786	}
787
788	*keyp = key;
789	return (ISC_R_SUCCESS);
790}
791
792isc_result_t
793dst_key_generate(dns_name_t *name, unsigned int alg,
794		 unsigned int bits, unsigned int param,
795		 unsigned int flags, unsigned int protocol,
796		 dns_rdataclass_t rdclass,
797		 isc_mem_t *mctx, dst_key_t **keyp)
798{
799	return (dst_key_generate2(name, alg, bits, param, flags, protocol,
800				  rdclass, mctx, keyp, NULL));
801}
802
803isc_result_t
804dst_key_generate2(dns_name_t *name, unsigned int alg,
805		  unsigned int bits, unsigned int param,
806		  unsigned int flags, unsigned int protocol,
807		  dns_rdataclass_t rdclass,
808		  isc_mem_t *mctx, dst_key_t **keyp,
809		  void (*callback)(int))
810{
811	dst_key_t *key;
812	isc_result_t ret;
813
814	REQUIRE(dst_initialized == ISC_TRUE);
815	REQUIRE(dns_name_isabsolute(name));
816	REQUIRE(mctx != NULL);
817	REQUIRE(keyp != NULL && *keyp == NULL);
818
819	CHECKALG(alg);
820
821	key = get_key_struct(name, alg, flags, protocol, bits, rdclass, mctx);
822	if (key == NULL)
823		return (ISC_R_NOMEMORY);
824
825	if (bits == 0) { /*%< NULL KEY */
826		key->key_flags |= DNS_KEYTYPE_NOKEY;
827		*keyp = key;
828		return (ISC_R_SUCCESS);
829	}
830
831	if (key->func->generate == NULL) {
832		dst_key_free(&key);
833		return (DST_R_UNSUPPORTEDALG);
834	}
835
836	ret = key->func->generate(key, param, callback);
837	if (ret != ISC_R_SUCCESS) {
838		dst_key_free(&key);
839		return (ret);
840	}
841
842	ret = computeid(key);
843	if (ret != ISC_R_SUCCESS) {
844		dst_key_free(&key);
845		return (ret);
846	}
847
848	*keyp = key;
849	return (ISC_R_SUCCESS);
850}
851
852isc_result_t
853dst_key_getnum(const dst_key_t *key, int type, isc_uint32_t *valuep)
854{
855	REQUIRE(VALID_KEY(key));
856	REQUIRE(valuep != NULL);
857	REQUIRE(type <= DST_MAX_NUMERIC);
858	if (!key->numset[type])
859		return (ISC_R_NOTFOUND);
860	*valuep = key->nums[type];
861	return (ISC_R_SUCCESS);
862}
863
864void
865dst_key_setnum(dst_key_t *key, int type, isc_uint32_t value)
866{
867	REQUIRE(VALID_KEY(key));
868	REQUIRE(type <= DST_MAX_NUMERIC);
869	key->nums[type] = value;
870	key->numset[type] = ISC_TRUE;
871}
872
873void
874dst_key_unsetnum(dst_key_t *key, int type)
875{
876	REQUIRE(VALID_KEY(key));
877	REQUIRE(type <= DST_MAX_NUMERIC);
878	key->numset[type] = ISC_FALSE;
879}
880
881isc_result_t
882dst_key_gettime(const dst_key_t *key, int type, isc_stdtime_t *timep) {
883	REQUIRE(VALID_KEY(key));
884	REQUIRE(timep != NULL);
885	REQUIRE(type <= DST_MAX_TIMES);
886	if (!key->timeset[type])
887		return (ISC_R_NOTFOUND);
888	*timep = key->times[type];
889	return (ISC_R_SUCCESS);
890}
891
892void
893dst_key_settime(dst_key_t *key, int type, isc_stdtime_t when) {
894	REQUIRE(VALID_KEY(key));
895	REQUIRE(type <= DST_MAX_TIMES);
896	key->times[type] = when;
897	key->timeset[type] = ISC_TRUE;
898}
899
900void
901dst_key_unsettime(dst_key_t *key, int type) {
902	REQUIRE(VALID_KEY(key));
903	REQUIRE(type <= DST_MAX_TIMES);
904	key->timeset[type] = ISC_FALSE;
905}
906
907isc_result_t
908dst_key_getprivateformat(const dst_key_t *key, int *majorp, int *minorp) {
909	REQUIRE(VALID_KEY(key));
910	REQUIRE(majorp != NULL);
911	REQUIRE(minorp != NULL);
912	*majorp = key->fmt_major;
913	*minorp = key->fmt_minor;
914	return (ISC_R_SUCCESS);
915}
916
917void
918dst_key_setprivateformat(dst_key_t *key, int major, int minor) {
919	REQUIRE(VALID_KEY(key));
920	key->fmt_major = major;
921	key->fmt_minor = minor;
922}
923
924static isc_boolean_t
925comparekeys(const dst_key_t *key1, const dst_key_t *key2,
926	    isc_boolean_t match_revoked_key,
927	    isc_boolean_t (*compare)(const dst_key_t *key1,
928				     const dst_key_t *key2))
929{
930	REQUIRE(dst_initialized == ISC_TRUE);
931	REQUIRE(VALID_KEY(key1));
932	REQUIRE(VALID_KEY(key2));
933
934	if (key1 == key2)
935		return (ISC_TRUE);
936
937	if (key1 == NULL || key2 == NULL)
938		return (ISC_FALSE);
939
940	if (key1->key_alg != key2->key_alg)
941		return (ISC_FALSE);
942
943	if (key1->key_id != key2->key_id) {
944		if (!match_revoked_key)
945			return (ISC_FALSE);
946		if (key1->key_alg == DST_ALG_RSAMD5)
947			return (ISC_FALSE);
948		if ((key1->key_flags & DNS_KEYFLAG_REVOKE) ==
949		    (key2->key_flags & DNS_KEYFLAG_REVOKE))
950			return (ISC_FALSE);
951		if (key1->key_id != key2->key_rid &&
952		    key1->key_rid != key2->key_id)
953			return (ISC_FALSE);
954	}
955
956	if (compare != NULL)
957		return (compare(key1, key2));
958	else
959		return (ISC_FALSE);
960}
961
962
963/*
964 * Compares only the public portion of two keys, by converting them
965 * both to wire format and comparing the results.
966 */
967static isc_boolean_t
968pub_compare(const dst_key_t *key1, const dst_key_t *key2) {
969	isc_result_t result;
970	unsigned char buf1[DST_KEY_MAXSIZE], buf2[DST_KEY_MAXSIZE];
971	isc_buffer_t b1, b2;
972	isc_region_t r1, r2;
973
974	isc_buffer_init(&b1, buf1, sizeof(buf1));
975	result = dst_key_todns(key1, &b1);
976	if (result != ISC_R_SUCCESS)
977		return (ISC_FALSE);
978	/* Zero out flags. */
979	buf1[0] = buf1[1] = 0;
980	if ((key1->key_flags & DNS_KEYFLAG_EXTENDED) != 0)
981		isc_buffer_subtract(&b1, 2);
982
983	isc_buffer_init(&b2, buf2, sizeof(buf2));
984	result = dst_key_todns(key2, &b2);
985	if (result != ISC_R_SUCCESS)
986		return (ISC_FALSE);
987	/* Zero out flags. */
988	buf2[0] = buf2[1] = 0;
989	if ((key2->key_flags & DNS_KEYFLAG_EXTENDED) != 0)
990		isc_buffer_subtract(&b2, 2);
991
992	isc_buffer_usedregion(&b1, &r1);
993	/* Remove extended flags. */
994	if ((key1->key_flags & DNS_KEYFLAG_EXTENDED) != 0) {
995		memmove(&buf1[4], &buf1[6], r1.length - 6);
996		r1.length -= 2;
997	}
998
999	isc_buffer_usedregion(&b2, &r2);
1000	/* Remove extended flags. */
1001	if ((key2->key_flags & DNS_KEYFLAG_EXTENDED) != 0) {
1002		memmove(&buf2[4], &buf2[6], r2.length - 6);
1003		r2.length -= 2;
1004	}
1005	return (ISC_TF(isc_region_compare(&r1, &r2) == 0));
1006}
1007
1008isc_boolean_t
1009dst_key_compare(const dst_key_t *key1, const dst_key_t *key2) {
1010	return (comparekeys(key1, key2, ISC_FALSE, key1->func->compare));
1011}
1012
1013isc_boolean_t
1014dst_key_pubcompare(const dst_key_t *key1, const dst_key_t *key2,
1015		   isc_boolean_t match_revoked_key)
1016{
1017	return (comparekeys(key1, key2, match_revoked_key, pub_compare));
1018}
1019
1020
1021isc_boolean_t
1022dst_key_paramcompare(const dst_key_t *key1, const dst_key_t *key2) {
1023	REQUIRE(dst_initialized == ISC_TRUE);
1024	REQUIRE(VALID_KEY(key1));
1025	REQUIRE(VALID_KEY(key2));
1026
1027	if (key1 == key2)
1028		return (ISC_TRUE);
1029	if (key1 == NULL || key2 == NULL)
1030		return (ISC_FALSE);
1031	if (key1->key_alg == key2->key_alg &&
1032	    key1->func->paramcompare != NULL &&
1033	    key1->func->paramcompare(key1, key2) == ISC_TRUE)
1034		return (ISC_TRUE);
1035	else
1036		return (ISC_FALSE);
1037}
1038
1039void
1040dst_key_attach(dst_key_t *source, dst_key_t **target) {
1041
1042	REQUIRE(dst_initialized == ISC_TRUE);
1043	REQUIRE(target != NULL && *target == NULL);
1044	REQUIRE(VALID_KEY(source));
1045
1046	isc_refcount_increment(&source->refs, NULL);
1047	*target = source;
1048}
1049
1050void
1051dst_key_free(dst_key_t **keyp) {
1052	isc_mem_t *mctx;
1053	dst_key_t *key;
1054	unsigned int refs;
1055
1056	REQUIRE(dst_initialized == ISC_TRUE);
1057	REQUIRE(keyp != NULL && VALID_KEY(*keyp));
1058
1059	key = *keyp;
1060	mctx = key->mctx;
1061
1062	isc_refcount_decrement(&key->refs, &refs);
1063	if (refs != 0)
1064		return;
1065
1066	isc_refcount_destroy(&key->refs);
1067	if (key->keydata.generic != NULL) {
1068		INSIST(key->func->destroy != NULL);
1069		key->func->destroy(key);
1070	}
1071	if (key->engine != NULL)
1072		isc_mem_free(mctx, key->engine);
1073	if (key->label != NULL)
1074		isc_mem_free(mctx, key->label);
1075	dns_name_free(key->key_name, mctx);
1076	isc_mem_put(mctx, key->key_name, sizeof(dns_name_t));
1077	if (key->key_tkeytoken) {
1078		isc_buffer_free(&key->key_tkeytoken);
1079	}
1080	memset(key, 0, sizeof(dst_key_t));
1081	isc_mem_put(mctx, key, sizeof(dst_key_t));
1082	*keyp = NULL;
1083}
1084
1085isc_boolean_t
1086dst_key_isprivate(const dst_key_t *key) {
1087	REQUIRE(VALID_KEY(key));
1088	INSIST(key->func->isprivate != NULL);
1089	return (key->func->isprivate(key));
1090}
1091
1092isc_result_t
1093dst_key_buildfilename(const dst_key_t *key, int type,
1094		      const char *directory, isc_buffer_t *out) {
1095
1096	REQUIRE(VALID_KEY(key));
1097	REQUIRE(type == DST_TYPE_PRIVATE || type == DST_TYPE_PUBLIC ||
1098		type == 0);
1099
1100	return (buildfilename(key->key_name, key->key_id, key->key_alg,
1101			      type, directory, out));
1102}
1103
1104isc_result_t
1105dst_key_sigsize(const dst_key_t *key, unsigned int *n) {
1106	REQUIRE(dst_initialized == ISC_TRUE);
1107	REQUIRE(VALID_KEY(key));
1108	REQUIRE(n != NULL);
1109
1110	/* XXXVIX this switch statement is too sparse to gen a jump table. */
1111	switch (key->key_alg) {
1112	case DST_ALG_RSAMD5:
1113	case DST_ALG_RSASHA1:
1114	case DST_ALG_NSEC3RSASHA1:
1115	case DST_ALG_RSASHA256:
1116	case DST_ALG_RSASHA512:
1117		*n = (key->key_size + 7) / 8;
1118		break;
1119	case DST_ALG_DSA:
1120	case DST_ALG_NSEC3DSA:
1121		*n = DNS_SIG_DSASIGSIZE;
1122		break;
1123	case DST_ALG_ECCGOST:
1124		*n = DNS_SIG_GOSTSIGSIZE;
1125		break;
1126	case DST_ALG_ECDSA256:
1127		*n = DNS_SIG_ECDSA256SIZE;
1128		break;
1129	case DST_ALG_ECDSA384:
1130		*n = DNS_SIG_ECDSA384SIZE;
1131		break;
1132	case DST_ALG_HMACMD5:
1133		*n = 16;
1134		break;
1135	case DST_ALG_HMACSHA1:
1136		*n = ISC_SHA1_DIGESTLENGTH;
1137		break;
1138	case DST_ALG_HMACSHA224:
1139		*n = ISC_SHA224_DIGESTLENGTH;
1140		break;
1141	case DST_ALG_HMACSHA256:
1142		*n = ISC_SHA256_DIGESTLENGTH;
1143		break;
1144	case DST_ALG_HMACSHA384:
1145		*n = ISC_SHA384_DIGESTLENGTH;
1146		break;
1147	case DST_ALG_HMACSHA512:
1148		*n = ISC_SHA512_DIGESTLENGTH;
1149		break;
1150	case DST_ALG_GSSAPI:
1151		*n = 128; /*%< XXX */
1152		break;
1153	case DST_ALG_DH:
1154	default:
1155		return (DST_R_UNSUPPORTEDALG);
1156	}
1157	return (ISC_R_SUCCESS);
1158}
1159
1160isc_result_t
1161dst_key_secretsize(const dst_key_t *key, unsigned int *n) {
1162	REQUIRE(dst_initialized == ISC_TRUE);
1163	REQUIRE(VALID_KEY(key));
1164	REQUIRE(n != NULL);
1165
1166	if (key->key_alg == DST_ALG_DH)
1167		*n = (key->key_size + 7) / 8;
1168	else
1169		return (DST_R_UNSUPPORTEDALG);
1170	return (ISC_R_SUCCESS);
1171}
1172
1173/*%
1174 * Set the flags on a key, then recompute the key ID
1175 */
1176isc_result_t
1177dst_key_setflags(dst_key_t *key, isc_uint32_t flags) {
1178	REQUIRE(VALID_KEY(key));
1179	key->key_flags = flags;
1180	return (computeid(key));
1181}
1182
1183void
1184dst_key_format(const dst_key_t *key, char *cp, unsigned int size) {
1185	char namestr[DNS_NAME_FORMATSIZE];
1186	char algstr[DNS_NAME_FORMATSIZE];
1187
1188	dns_name_format(dst_key_name(key), namestr, sizeof(namestr));
1189	dns_secalg_format((dns_secalg_t) dst_key_alg(key), algstr,
1190			  sizeof(algstr));
1191	snprintf(cp, size, "%s/%s/%d", namestr, algstr, dst_key_id(key));
1192}
1193
1194isc_result_t
1195dst_key_dump(dst_key_t *key, isc_mem_t *mctx, char **buffer, int *length) {
1196
1197	REQUIRE(buffer != NULL && *buffer == NULL);
1198	REQUIRE(length != NULL && *length == 0);
1199	REQUIRE(VALID_KEY(key));
1200
1201	if (key->func->dump == NULL)
1202		return (ISC_R_NOTIMPLEMENTED);
1203	return (key->func->dump(key, mctx, buffer, length));
1204}
1205
1206isc_result_t
1207dst_key_restore(dns_name_t *name, unsigned int alg, unsigned int flags,
1208		unsigned int protocol, dns_rdataclass_t rdclass,
1209		isc_mem_t *mctx, const char *keystr, dst_key_t **keyp)
1210{
1211	isc_result_t result;
1212	dst_key_t *key;
1213
1214	REQUIRE(dst_initialized == ISC_TRUE);
1215	REQUIRE(keyp != NULL && *keyp == NULL);
1216
1217	if (alg >= DST_MAX_ALGS || dst_t_func[alg] == NULL)
1218		return (DST_R_UNSUPPORTEDALG);
1219
1220	if (dst_t_func[alg]->restore == NULL)
1221		return (ISC_R_NOTIMPLEMENTED);
1222
1223	key = get_key_struct(name, alg, flags, protocol, 0, rdclass, mctx);
1224	if (key == NULL)
1225		return (ISC_R_NOMEMORY);
1226
1227	result = (dst_t_func[alg]->restore)(key, keystr);
1228	if (result == ISC_R_SUCCESS)
1229		*keyp = key;
1230	else
1231		dst_key_free(&key);
1232
1233	return (result);
1234}
1235
1236/***
1237 *** Static methods
1238 ***/
1239
1240/*%
1241 * Allocates a key structure and fills in some of the fields.
1242 */
1243static dst_key_t *
1244get_key_struct(dns_name_t *name, unsigned int alg,
1245	       unsigned int flags, unsigned int protocol,
1246	       unsigned int bits, dns_rdataclass_t rdclass,
1247	       isc_mem_t *mctx)
1248{
1249	dst_key_t *key;
1250	isc_result_t result;
1251	int i;
1252
1253	key = (dst_key_t *) isc_mem_get(mctx, sizeof(dst_key_t));
1254	if (key == NULL)
1255		return (NULL);
1256
1257	memset(key, 0, sizeof(dst_key_t));
1258
1259	key->key_name = isc_mem_get(mctx, sizeof(dns_name_t));
1260	if (key->key_name == NULL) {
1261		isc_mem_put(mctx, key, sizeof(dst_key_t));
1262		return (NULL);
1263	}
1264
1265	dns_name_init(key->key_name, NULL);
1266	result = dns_name_dup(name, mctx, key->key_name);
1267	if (result != ISC_R_SUCCESS) {
1268		isc_mem_put(mctx, key->key_name, sizeof(dns_name_t));
1269		isc_mem_put(mctx, key, sizeof(dst_key_t));
1270		return (NULL);
1271	}
1272
1273	result = isc_refcount_init(&key->refs, 1);
1274	if (result != ISC_R_SUCCESS) {
1275		dns_name_free(key->key_name, mctx);
1276		isc_mem_put(mctx, key->key_name, sizeof(dns_name_t));
1277		isc_mem_put(mctx, key, sizeof(dst_key_t));
1278		return (NULL);
1279	}
1280	key->key_alg = alg;
1281	key->key_flags = flags;
1282	key->key_proto = protocol;
1283	key->mctx = mctx;
1284	key->keydata.generic = NULL;
1285	key->key_size = bits;
1286	key->key_class = rdclass;
1287	key->func = dst_t_func[alg];
1288	key->fmt_major = 0;
1289	key->fmt_minor = 0;
1290	for (i = 0; i < (DST_MAX_TIMES + 1); i++) {
1291		key->times[i] = 0;
1292		key->timeset[i] = ISC_FALSE;
1293	}
1294	key->magic = KEY_MAGIC;
1295	return (key);
1296}
1297
1298/*%
1299 * Reads a public key from disk
1300 */
1301isc_result_t
1302dst_key_read_public(const char *filename, int type,
1303		    isc_mem_t *mctx, dst_key_t **keyp)
1304{
1305	u_char rdatabuf[DST_KEY_MAXSIZE];
1306	isc_buffer_t b;
1307	dns_fixedname_t name;
1308	isc_lex_t *lex = NULL;
1309	isc_token_t token;
1310	isc_result_t ret;
1311	dns_rdata_t rdata = DNS_RDATA_INIT;
1312	unsigned int opt = ISC_LEXOPT_DNSMULTILINE;
1313	dns_rdataclass_t rdclass = dns_rdataclass_in;
1314	isc_lexspecials_t specials;
1315	isc_uint32_t ttl;
1316	isc_result_t result;
1317	dns_rdatatype_t keytype;
1318
1319	/*
1320	 * Open the file and read its formatted contents
1321	 * File format:
1322	 *    domain.name [ttl] [class] [KEY|DNSKEY] <flags> <protocol> <algorithm> <key>
1323	 */
1324
1325	/* 1500 should be large enough for any key */
1326	ret = isc_lex_create(mctx, 1500, &lex);
1327	if (ret != ISC_R_SUCCESS)
1328		goto cleanup;
1329
1330	memset(specials, 0, sizeof(specials));
1331	specials['('] = 1;
1332	specials[')'] = 1;
1333	specials['"'] = 1;
1334	isc_lex_setspecials(lex, specials);
1335	isc_lex_setcomments(lex, ISC_LEXCOMMENT_DNSMASTERFILE);
1336
1337	ret = isc_lex_openfile(lex, filename);
1338	if (ret != ISC_R_SUCCESS)
1339		goto cleanup;
1340
1341#define NEXTTOKEN(lex, opt, token) { \
1342	ret = isc_lex_gettoken(lex, opt, token); \
1343	if (ret != ISC_R_SUCCESS) \
1344		goto cleanup; \
1345	}
1346
1347#define BADTOKEN() { \
1348	ret = ISC_R_UNEXPECTEDTOKEN; \
1349	goto cleanup; \
1350	}
1351
1352	/* Read the domain name */
1353	NEXTTOKEN(lex, opt, &token);
1354	if (token.type != isc_tokentype_string)
1355		BADTOKEN();
1356
1357	/*
1358	 * We don't support "@" in .key files.
1359	 */
1360	if (!strcmp(DST_AS_STR(token), "@"))
1361		BADTOKEN();
1362
1363	dns_fixedname_init(&name);
1364	isc_buffer_init(&b, DST_AS_STR(token), strlen(DST_AS_STR(token)));
1365	isc_buffer_add(&b, strlen(DST_AS_STR(token)));
1366	ret = dns_name_fromtext(dns_fixedname_name(&name), &b, dns_rootname,
1367				0, NULL);
1368	if (ret != ISC_R_SUCCESS)
1369		goto cleanup;
1370
1371	/* Read the next word: either TTL, class, or 'KEY' */
1372	NEXTTOKEN(lex, opt, &token);
1373
1374	if (token.type != isc_tokentype_string)
1375		BADTOKEN();
1376
1377	/* If it's a TTL, read the next one */
1378	result = dns_ttl_fromtext(&token.value.as_textregion, &ttl);
1379	if (result == ISC_R_SUCCESS)
1380		NEXTTOKEN(lex, opt, &token);
1381
1382	if (token.type != isc_tokentype_string)
1383		BADTOKEN();
1384
1385	ret = dns_rdataclass_fromtext(&rdclass, &token.value.as_textregion);
1386	if (ret == ISC_R_SUCCESS)
1387		NEXTTOKEN(lex, opt, &token);
1388
1389	if (token.type != isc_tokentype_string)
1390		BADTOKEN();
1391
1392	if (strcasecmp(DST_AS_STR(token), "DNSKEY") == 0)
1393		keytype = dns_rdatatype_dnskey;
1394	else if (strcasecmp(DST_AS_STR(token), "KEY") == 0)
1395		keytype = dns_rdatatype_key; /*%< SIG(0), TKEY */
1396	else
1397		BADTOKEN();
1398
1399	if (((type & DST_TYPE_KEY) != 0 && keytype != dns_rdatatype_key) ||
1400	    ((type & DST_TYPE_KEY) == 0 && keytype != dns_rdatatype_dnskey)) {
1401		ret = DST_R_BADKEYTYPE;
1402		goto cleanup;
1403	}
1404
1405	isc_buffer_init(&b, rdatabuf, sizeof(rdatabuf));
1406	ret = dns_rdata_fromtext(&rdata, rdclass, keytype, lex, NULL,
1407				 ISC_FALSE, mctx, &b, NULL);
1408	if (ret != ISC_R_SUCCESS)
1409		goto cleanup;
1410
1411	ret = dst_key_fromdns(dns_fixedname_name(&name), rdclass, &b, mctx,
1412			      keyp);
1413	if (ret != ISC_R_SUCCESS)
1414		goto cleanup;
1415
1416 cleanup:
1417	if (lex != NULL)
1418		isc_lex_destroy(&lex);
1419	return (ret);
1420}
1421
1422static isc_boolean_t
1423issymmetric(const dst_key_t *key) {
1424	REQUIRE(dst_initialized == ISC_TRUE);
1425	REQUIRE(VALID_KEY(key));
1426
1427	/* XXXVIX this switch statement is too sparse to gen a jump table. */
1428	switch (key->key_alg) {
1429	case DST_ALG_RSAMD5:
1430	case DST_ALG_RSASHA1:
1431	case DST_ALG_NSEC3RSASHA1:
1432	case DST_ALG_RSASHA256:
1433	case DST_ALG_RSASHA512:
1434	case DST_ALG_DSA:
1435	case DST_ALG_NSEC3DSA:
1436	case DST_ALG_DH:
1437	case DST_ALG_ECCGOST:
1438	case DST_ALG_ECDSA256:
1439	case DST_ALG_ECDSA384:
1440		return (ISC_FALSE);
1441	case DST_ALG_HMACMD5:
1442	case DST_ALG_GSSAPI:
1443		return (ISC_TRUE);
1444	default:
1445		return (ISC_FALSE);
1446	}
1447}
1448
1449/*%
1450 * Write key timing metadata to a file pointer, preceded by 'tag'
1451 */
1452static void
1453printtime(const dst_key_t *key, int type, const char *tag, FILE *stream) {
1454	isc_result_t result;
1455#ifdef ISC_PLATFORM_USETHREADS
1456	char output[26]; /* Minimum buffer as per ctime_r() specification. */
1457#else
1458	const char *output;
1459#endif
1460	isc_stdtime_t when;
1461	time_t t;
1462	char utc[sizeof("YYYYMMDDHHSSMM")];
1463	isc_buffer_t b;
1464	isc_region_t r;
1465
1466	result = dst_key_gettime(key, type, &when);
1467	if (result == ISC_R_NOTFOUND)
1468		return;
1469
1470	/* time_t and isc_stdtime_t might be different sizes */
1471	t = when;
1472#ifdef ISC_PLATFORM_USETHREADS
1473#ifdef WIN32
1474	if (ctime_s(output, sizeof(output), &t) != 0)
1475		goto error;
1476#else
1477	if (ctime_r(&t, output) == NULL)
1478		goto error;
1479#endif
1480#else
1481	output = ctime(&t);
1482#endif
1483
1484	isc_buffer_init(&b, utc, sizeof(utc));
1485	result = dns_time32_totext(when, &b);
1486	if (result != ISC_R_SUCCESS)
1487		goto error;
1488
1489	isc_buffer_usedregion(&b, &r);
1490	fprintf(stream, "%s: %.*s (%.*s)\n", tag, (int)r.length, r.base,
1491		 (int)strlen(output) - 1, output);
1492	return;
1493
1494 error:
1495	fprintf(stream, "%s: (set, unable to display)\n", tag);
1496}
1497
1498/*%
1499 * Writes a public key to disk in DNS format.
1500 */
1501static isc_result_t
1502write_public_key(const dst_key_t *key, int type, const char *directory) {
1503	FILE *fp;
1504	isc_buffer_t keyb, textb, fileb, classb;
1505	isc_region_t r;
1506	char filename[ISC_DIR_NAMEMAX];
1507	unsigned char key_array[DST_KEY_MAXSIZE];
1508	char text_array[DST_KEY_MAXTEXTSIZE];
1509	char class_array[10];
1510	isc_result_t ret;
1511	dns_rdata_t rdata = DNS_RDATA_INIT;
1512	isc_fsaccess_t access;
1513
1514	REQUIRE(VALID_KEY(key));
1515
1516	isc_buffer_init(&keyb, key_array, sizeof(key_array));
1517	isc_buffer_init(&textb, text_array, sizeof(text_array));
1518	isc_buffer_init(&classb, class_array, sizeof(class_array));
1519
1520	ret = dst_key_todns(key, &keyb);
1521	if (ret != ISC_R_SUCCESS)
1522		return (ret);
1523
1524	isc_buffer_usedregion(&keyb, &r);
1525	dns_rdata_fromregion(&rdata, key->key_class, dns_rdatatype_dnskey, &r);
1526
1527	ret = dns_rdata_totext(&rdata, (dns_name_t *) NULL, &textb);
1528	if (ret != ISC_R_SUCCESS)
1529		return (DST_R_INVALIDPUBLICKEY);
1530
1531	ret = dns_rdataclass_totext(key->key_class, &classb);
1532	if (ret != ISC_R_SUCCESS)
1533		return (DST_R_INVALIDPUBLICKEY);
1534
1535	/*
1536	 * Make the filename.
1537	 */
1538	isc_buffer_init(&fileb, filename, sizeof(filename));
1539	ret = dst_key_buildfilename(key, DST_TYPE_PUBLIC, directory, &fileb);
1540	if (ret != ISC_R_SUCCESS)
1541		return (ret);
1542
1543	/*
1544	 * Create public key file.
1545	 */
1546	if ((fp = fopen(filename, "w")) == NULL)
1547		return (DST_R_WRITEERROR);
1548
1549	if (issymmetric(key)) {
1550		access = 0;
1551		isc_fsaccess_add(ISC_FSACCESS_OWNER,
1552				 ISC_FSACCESS_READ | ISC_FSACCESS_WRITE,
1553				 &access);
1554		(void)isc_fsaccess_set(filename, access);
1555	}
1556
1557	/* Write key information in comments */
1558	if ((type & DST_TYPE_KEY) == 0) {
1559		fprintf(fp, "; This is a %s%s-signing key, keyid %d, for ",
1560			(key->key_flags & DNS_KEYFLAG_REVOKE) != 0 ?
1561				"revoked " :
1562				"",
1563			(key->key_flags & DNS_KEYFLAG_KSK) != 0 ?
1564				"key" :
1565				"zone",
1566			key->key_id);
1567		ret = dns_name_print(key->key_name, fp);
1568		if (ret != ISC_R_SUCCESS) {
1569			fclose(fp);
1570			return (ret);
1571		}
1572		fputc('\n', fp);
1573
1574		printtime(key, DST_TIME_CREATED, "; Created", fp);
1575		printtime(key, DST_TIME_PUBLISH, "; Publish", fp);
1576		printtime(key, DST_TIME_ACTIVATE, "; Activate", fp);
1577		printtime(key, DST_TIME_REVOKE, "; Revoke", fp);
1578		printtime(key, DST_TIME_INACTIVE, "; Inactive", fp);
1579		printtime(key, DST_TIME_DELETE, "; Delete", fp);
1580	}
1581
1582	/* Now print the actual key */
1583	ret = dns_name_print(key->key_name, fp);
1584
1585	fprintf(fp, " ");
1586
1587	isc_buffer_usedregion(&classb, &r);
1588	if ((unsigned) fwrite(r.base, 1, r.length, fp) != r.length)
1589	       ret = DST_R_WRITEERROR;
1590
1591	if ((type & DST_TYPE_KEY) != 0)
1592		fprintf(fp, " KEY ");
1593	else
1594		fprintf(fp, " DNSKEY ");
1595
1596	isc_buffer_usedregion(&textb, &r);
1597	if ((unsigned) fwrite(r.base, 1, r.length, fp) != r.length)
1598	       ret = DST_R_WRITEERROR;
1599
1600	fputc('\n', fp);
1601	fflush(fp);
1602	if (ferror(fp))
1603		ret = DST_R_WRITEERROR;
1604	fclose(fp);
1605
1606	return (ret);
1607}
1608
1609static isc_result_t
1610buildfilename(dns_name_t *name, dns_keytag_t id,
1611	      unsigned int alg, unsigned int type,
1612	      const char *directory, isc_buffer_t *out)
1613{
1614	const char *suffix = "";
1615	unsigned int len;
1616	isc_result_t result;
1617
1618	REQUIRE(out != NULL);
1619	if ((type & DST_TYPE_PRIVATE) != 0)
1620		suffix = ".private";
1621	else if (type == DST_TYPE_PUBLIC)
1622		suffix = ".key";
1623	if (directory != NULL) {
1624		if (isc_buffer_availablelength(out) < strlen(directory))
1625			return (ISC_R_NOSPACE);
1626		isc_buffer_putstr(out, directory);
1627		if (strlen(directory) > 0U &&
1628		    directory[strlen(directory) - 1] != '/')
1629			isc_buffer_putstr(out, "/");
1630	}
1631	if (isc_buffer_availablelength(out) < 1)
1632		return (ISC_R_NOSPACE);
1633	isc_buffer_putstr(out, "K");
1634	result = dns_name_tofilenametext(name, ISC_FALSE, out);
1635	if (result != ISC_R_SUCCESS)
1636		return (result);
1637	len = 1 + 3 + 1 + 5 + strlen(suffix) + 1;
1638	if (isc_buffer_availablelength(out) < len)
1639		return (ISC_R_NOSPACE);
1640	sprintf((char *) isc_buffer_used(out), "+%03d+%05d%s", alg, id,
1641		suffix);
1642	isc_buffer_add(out, len);
1643
1644	return (ISC_R_SUCCESS);
1645}
1646
1647static isc_result_t
1648computeid(dst_key_t *key) {
1649	isc_buffer_t dnsbuf;
1650	unsigned char dns_array[DST_KEY_MAXSIZE];
1651	isc_region_t r;
1652	isc_result_t ret;
1653
1654	isc_buffer_init(&dnsbuf, dns_array, sizeof(dns_array));
1655	ret = dst_key_todns(key, &dnsbuf);
1656	if (ret != ISC_R_SUCCESS)
1657		return (ret);
1658
1659	isc_buffer_usedregion(&dnsbuf, &r);
1660	key->key_id = dst_region_computeid(&r, key->key_alg);
1661	key->key_rid = dst_region_computerid(&r, key->key_alg);
1662	return (ISC_R_SUCCESS);
1663}
1664
1665static isc_result_t
1666frombuffer(dns_name_t *name, unsigned int alg, unsigned int flags,
1667	   unsigned int protocol, dns_rdataclass_t rdclass,
1668	   isc_buffer_t *source, isc_mem_t *mctx, dst_key_t **keyp)
1669{
1670	dst_key_t *key;
1671	isc_result_t ret;
1672
1673	REQUIRE(dns_name_isabsolute(name));
1674	REQUIRE(source != NULL);
1675	REQUIRE(mctx != NULL);
1676	REQUIRE(keyp != NULL && *keyp == NULL);
1677
1678	key = get_key_struct(name, alg, flags, protocol, 0, rdclass, mctx);
1679	if (key == NULL)
1680		return (ISC_R_NOMEMORY);
1681
1682	if (isc_buffer_remaininglength(source) > 0) {
1683		ret = algorithm_status(alg);
1684		if (ret != ISC_R_SUCCESS) {
1685			dst_key_free(&key);
1686			return (ret);
1687		}
1688		if (key->func->fromdns == NULL) {
1689			dst_key_free(&key);
1690			return (DST_R_UNSUPPORTEDALG);
1691		}
1692
1693		ret = key->func->fromdns(key, source);
1694		if (ret != ISC_R_SUCCESS) {
1695			dst_key_free(&key);
1696			return (ret);
1697		}
1698	}
1699
1700	*keyp = key;
1701	return (ISC_R_SUCCESS);
1702}
1703
1704static isc_result_t
1705algorithm_status(unsigned int alg) {
1706	REQUIRE(dst_initialized == ISC_TRUE);
1707
1708	if (dst_algorithm_supported(alg))
1709		return (ISC_R_SUCCESS);
1710#ifndef OPENSSL
1711	if (alg == DST_ALG_RSAMD5 || alg == DST_ALG_RSASHA1 ||
1712	    alg == DST_ALG_DSA || alg == DST_ALG_DH ||
1713	    alg == DST_ALG_HMACMD5 || alg == DST_ALG_NSEC3DSA ||
1714	    alg == DST_ALG_NSEC3RSASHA1 ||
1715	    alg == DST_ALG_RSASHA256 || alg == DST_ALG_RSASHA512 ||
1716	    alg == DST_ALG_ECCGOST ||
1717	    alg == DST_ALG_ECDSA256 || alg == DST_ALG_ECDSA384)
1718		return (DST_R_NOCRYPTO);
1719#endif
1720	return (DST_R_UNSUPPORTEDALG);
1721}
1722
1723static isc_result_t
1724addsuffix(char *filename, int len, const char *odirname,
1725	  const char *ofilename, const char *suffix)
1726{
1727	int olen = strlen(ofilename);
1728	int n;
1729
1730	if (olen > 1 && ofilename[olen - 1] == '.')
1731		olen -= 1;
1732	else if (olen > 8 && strcmp(ofilename + olen - 8, ".private") == 0)
1733		olen -= 8;
1734	else if (olen > 4 && strcmp(ofilename + olen - 4, ".key") == 0)
1735		olen -= 4;
1736
1737	if (odirname == NULL)
1738		n = snprintf(filename, len, "%.*s%s", olen, ofilename, suffix);
1739	else
1740		n = snprintf(filename, len, "%s/%.*s%s",
1741			     odirname, olen, ofilename, suffix);
1742	if (n < 0)
1743		return (ISC_R_FAILURE);
1744	if (n >= len)
1745		return (ISC_R_NOSPACE);
1746	return (ISC_R_SUCCESS);
1747}
1748
1749isc_result_t
1750dst__entropy_getdata(void *buf, unsigned int len, isc_boolean_t pseudo) {
1751#ifdef BIND9
1752	unsigned int flags = dst_entropy_flags;
1753
1754	if (len == 0)
1755		return (ISC_R_SUCCESS);
1756	if (pseudo)
1757		flags &= ~ISC_ENTROPY_GOODONLY;
1758	else
1759		flags |= ISC_ENTROPY_BLOCKING;
1760	return (isc_entropy_getdata(dst_entropy_pool, buf, len, NULL, flags));
1761#else
1762	UNUSED(buf);
1763	UNUSED(len);
1764	UNUSED(pseudo);
1765
1766	return (ISC_R_NOTIMPLEMENTED);
1767#endif
1768}
1769
1770unsigned int
1771dst__entropy_status(void) {
1772#ifdef BIND9
1773#ifdef GSSAPI
1774	unsigned int flags = dst_entropy_flags;
1775	isc_result_t ret;
1776	unsigned char buf[32];
1777	static isc_boolean_t first = ISC_TRUE;
1778
1779	if (first) {
1780		/* Someone believes RAND_status() initializes the PRNG */
1781		flags &= ~ISC_ENTROPY_GOODONLY;
1782		ret = isc_entropy_getdata(dst_entropy_pool, buf,
1783					  sizeof(buf), NULL, flags);
1784		INSIST(ret == ISC_R_SUCCESS);
1785		isc_entropy_putdata(dst_entropy_pool, buf,
1786				    sizeof(buf), 2 * sizeof(buf));
1787		first = ISC_FALSE;
1788	}
1789#endif
1790	return (isc_entropy_status(dst_entropy_pool));
1791#else
1792	return (0);
1793#endif
1794}
1795
1796isc_buffer_t *
1797dst_key_tkeytoken(const dst_key_t *key) {
1798	REQUIRE(VALID_KEY(key));
1799	return (key->key_tkeytoken);
1800}
1801