dst_api.c revision 224092
1143731Sdougb/*
2224092Sdougb * Portions Copyright (C) 2004-2011  Internet Systems Consortium, Inc. ("ISC")
3143731Sdougb * Portions Copyright (C) 1999-2003  Internet Software Consortium.
4193149Sdougb *
5193149Sdougb * Permission to use, copy, modify, and/or distribute this software for any
6193149Sdougb * purpose with or without fee is hereby granted, provided that the above
7193149Sdougb * copyright notice and this permission notice appear in all copies.
8193149Sdougb *
9193149Sdougb * THE SOFTWARE IS PROVIDED "AS IS" AND ISC AND NETWORK ASSOCIATES DISCLAIMS
10193149Sdougb * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
11193149Sdougb * WARRANTIES OF MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE
12193149Sdougb * FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13193149Sdougb * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14193149Sdougb * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
15193149Sdougb * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16193149Sdougb *
17143731Sdougb * Portions Copyright (C) 1995-2000 by Network Associates, Inc.
18143731Sdougb *
19193149Sdougb * Permission to use, copy, modify, and/or distribute this software for any
20143731Sdougb * purpose with or without fee is hereby granted, provided that the above
21143731Sdougb * copyright notice and this permission notice appear in all copies.
22143731Sdougb *
23143731Sdougb * THE SOFTWARE IS PROVIDED "AS IS" AND ISC AND NETWORK ASSOCIATES DISCLAIMS
24143731Sdougb * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
25143731Sdougb * WARRANTIES OF MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE
26143731Sdougb * FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
27143731Sdougb * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
28143731Sdougb * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
29143731Sdougb * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
30143731Sdougb */
31143731Sdougb
32143731Sdougb/*
33143731Sdougb * Principal Author: Brian Wellington
34224092Sdougb * $Id: dst_api.c,v 1.57 2011-01-11 23:47:13 tbox Exp $
35143731Sdougb */
36143731Sdougb
37170222Sdougb/*! \file */
38170222Sdougb
39143731Sdougb#include <config.h>
40143731Sdougb
41143731Sdougb#include <stdlib.h>
42224092Sdougb#include <time.h>
43143731Sdougb
44143731Sdougb#include <isc/buffer.h>
45143731Sdougb#include <isc/dir.h>
46143731Sdougb#include <isc/entropy.h>
47143731Sdougb#include <isc/fsaccess.h>
48170222Sdougb#include <isc/hmacsha.h>
49143731Sdougb#include <isc/lex.h>
50143731Sdougb#include <isc/mem.h>
51143731Sdougb#include <isc/once.h>
52224092Sdougb#include <isc/platform.h>
53143731Sdougb#include <isc/print.h>
54218384Sdougb#include <isc/refcount.h>
55143731Sdougb#include <isc/random.h>
56143731Sdougb#include <isc/string.h>
57143731Sdougb#include <isc/time.h>
58143731Sdougb#include <isc/util.h>
59143731Sdougb
60143731Sdougb#include <dns/fixedname.h>
61143731Sdougb#include <dns/keyvalues.h>
62143731Sdougb#include <dns/name.h>
63143731Sdougb#include <dns/rdata.h>
64143731Sdougb#include <dns/rdataclass.h>
65143731Sdougb#include <dns/ttl.h>
66143731Sdougb#include <dns/types.h>
67143731Sdougb
68143731Sdougb#include <dst/result.h>
69143731Sdougb
70143731Sdougb#include "dst_internal.h"
71143731Sdougb
72143731Sdougb#define DST_AS_STR(t) ((t).value.as_textregion.base)
73143731Sdougb
74143731Sdougbstatic dst_func_t *dst_t_func[DST_MAX_ALGS];
75224092Sdougb#ifdef BIND9
76143731Sdougbstatic isc_entropy_t *dst_entropy_pool = NULL;
77224092Sdougb#endif
78143731Sdougbstatic unsigned int dst_entropy_flags = 0;
79143731Sdougbstatic isc_boolean_t dst_initialized = ISC_FALSE;
80143731Sdougb
81193149Sdougbvoid gss_log(int level, const char *fmt, ...) ISC_FORMAT_PRINTF(2, 3);
82193149Sdougb
83143731Sdougbisc_mem_t *dst__memory_pool = NULL;
84143731Sdougb
85143731Sdougb/*
86143731Sdougb * Static functions.
87143731Sdougb */
88143731Sdougbstatic dst_key_t *	get_key_struct(dns_name_t *name,
89143731Sdougb				       unsigned int alg,
90143731Sdougb				       unsigned int flags,
91143731Sdougb				       unsigned int protocol,
92143731Sdougb				       unsigned int bits,
93143731Sdougb				       dns_rdataclass_t rdclass,
94143731Sdougb				       isc_mem_t *mctx);
95143731Sdougbstatic isc_result_t	write_public_key(const dst_key_t *key, int type,
96143731Sdougb					 const char *directory);
97143731Sdougbstatic isc_result_t	buildfilename(dns_name_t *name,
98143731Sdougb				      dns_keytag_t id,
99143731Sdougb				      unsigned int alg,
100143731Sdougb				      unsigned int type,
101143731Sdougb				      const char *directory,
102143731Sdougb				      isc_buffer_t *out);
103143731Sdougbstatic isc_result_t	computeid(dst_key_t *key);
104143731Sdougbstatic isc_result_t	frombuffer(dns_name_t *name,
105143731Sdougb				   unsigned int alg,
106143731Sdougb				   unsigned int flags,
107143731Sdougb				   unsigned int protocol,
108143731Sdougb				   dns_rdataclass_t rdclass,
109143731Sdougb				   isc_buffer_t *source,
110143731Sdougb				   isc_mem_t *mctx,
111143731Sdougb				   dst_key_t **keyp);
112143731Sdougb
113143731Sdougbstatic isc_result_t	algorithm_status(unsigned int alg);
114143731Sdougb
115224092Sdougbstatic isc_result_t	addsuffix(char *filename, int len,
116224092Sdougb				  const char *dirname, const char *ofilename,
117224092Sdougb				  const char *suffix);
118143731Sdougb
119224092Sdougb#define RETERR(x)				\
120143731Sdougb	do {					\
121143731Sdougb		result = (x);			\
122143731Sdougb		if (result != ISC_R_SUCCESS)	\
123143731Sdougb			goto out;		\
124143731Sdougb	} while (0)
125143731Sdougb
126143731Sdougb#define CHECKALG(alg)				\
127143731Sdougb	do {					\
128143731Sdougb		isc_result_t _r;		\
129143731Sdougb		_r = algorithm_status(alg);	\
130143731Sdougb		if (_r != ISC_R_SUCCESS)	\
131143731Sdougb			return (_r);		\
132143731Sdougb	} while (0);				\
133143731Sdougb
134224092Sdougb#if defined(OPENSSL) && defined(BIND9)
135170222Sdougbstatic void *
136170222Sdougbdefault_memalloc(void *arg, size_t size) {
137193149Sdougb	UNUSED(arg);
138193149Sdougb	if (size == 0U)
139193149Sdougb		size = 1;
140193149Sdougb	return (malloc(size));
141170222Sdougb}
142170222Sdougb
143170222Sdougbstatic void
144170222Sdougbdefault_memfree(void *arg, void *ptr) {
145193149Sdougb	UNUSED(arg);
146193149Sdougb	free(ptr);
147170222Sdougb}
148193149Sdougb#endif
149170222Sdougb
150143731Sdougbisc_result_t
151143731Sdougbdst_lib_init(isc_mem_t *mctx, isc_entropy_t *ectx, unsigned int eflags) {
152224092Sdougb	return (dst_lib_init2(mctx, ectx, NULL, eflags));
153224092Sdougb}
154224092Sdougb
155224092Sdougbisc_result_t
156224092Sdougbdst_lib_init2(isc_mem_t *mctx, isc_entropy_t *ectx,
157224092Sdougb	      const char *engine, unsigned int eflags) {
158143731Sdougb	isc_result_t result;
159143731Sdougb
160224092Sdougb	REQUIRE(mctx != NULL);
161224092Sdougb#ifdef BIND9
162224092Sdougb	REQUIRE(ectx != NULL);
163224092Sdougb#else
164224092Sdougb	UNUSED(ectx);
165224092Sdougb#endif
166143731Sdougb	REQUIRE(dst_initialized == ISC_FALSE);
167143731Sdougb
168224092Sdougb#ifndef OPENSSL
169224092Sdougb	UNUSED(engine);
170224092Sdougb#endif
171224092Sdougb
172143731Sdougb	dst__memory_pool = NULL;
173143731Sdougb
174224092Sdougb#if defined(OPENSSL) && defined(BIND9)
175143731Sdougb	UNUSED(mctx);
176143731Sdougb	/*
177143731Sdougb	 * When using --with-openssl, there seems to be no good way of not
178143731Sdougb	 * leaking memory due to the openssl error handling mechanism.
179143731Sdougb	 * Avoid assertions by using a local memory context and not checking
180170222Sdougb	 * for leaks on exit.  Note: as there are leaks we cannot use
181170222Sdougb	 * ISC_MEMFLAG_INTERNAL as it will free up memory still being used
182170222Sdougb	 * by libcrypto.
183143731Sdougb	 */
184170222Sdougb	result = isc_mem_createx2(0, 0, default_memalloc, default_memfree,
185170222Sdougb				  NULL, &dst__memory_pool, 0);
186143731Sdougb	if (result != ISC_R_SUCCESS)
187143731Sdougb		return (result);
188193149Sdougb	isc_mem_setname(dst__memory_pool, "dst", NULL);
189224092Sdougb#ifndef OPENSSL_LEAKS
190143731Sdougb	isc_mem_setdestroycheck(dst__memory_pool, ISC_FALSE);
191224092Sdougb#endif
192143731Sdougb#else
193143731Sdougb	isc_mem_attach(mctx, &dst__memory_pool);
194143731Sdougb#endif
195224092Sdougb#ifdef BIND9
196143731Sdougb	isc_entropy_attach(ectx, &dst_entropy_pool);
197224092Sdougb#endif
198143731Sdougb	dst_entropy_flags = eflags;
199143731Sdougb
200143731Sdougb	dst_result_register();
201143731Sdougb
202143731Sdougb	memset(dst_t_func, 0, sizeof(dst_t_func));
203143731Sdougb	RETERR(dst__hmacmd5_init(&dst_t_func[DST_ALG_HMACMD5]));
204170222Sdougb	RETERR(dst__hmacsha1_init(&dst_t_func[DST_ALG_HMACSHA1]));
205170222Sdougb	RETERR(dst__hmacsha224_init(&dst_t_func[DST_ALG_HMACSHA224]));
206170222Sdougb	RETERR(dst__hmacsha256_init(&dst_t_func[DST_ALG_HMACSHA256]));
207170222Sdougb	RETERR(dst__hmacsha384_init(&dst_t_func[DST_ALG_HMACSHA384]));
208170222Sdougb	RETERR(dst__hmacsha512_init(&dst_t_func[DST_ALG_HMACSHA512]));
209143731Sdougb#ifdef OPENSSL
210224092Sdougb	RETERR(dst__openssl_init(engine));
211204619Sdougb	RETERR(dst__opensslrsa_init(&dst_t_func[DST_ALG_RSAMD5],
212204619Sdougb				    DST_ALG_RSAMD5));
213204619Sdougb	RETERR(dst__opensslrsa_init(&dst_t_func[DST_ALG_RSASHA1],
214204619Sdougb				    DST_ALG_RSASHA1));
215204619Sdougb	RETERR(dst__opensslrsa_init(&dst_t_func[DST_ALG_NSEC3RSASHA1],
216204619Sdougb				    DST_ALG_NSEC3RSASHA1));
217204619Sdougb	RETERR(dst__opensslrsa_init(&dst_t_func[DST_ALG_RSASHA256],
218204619Sdougb				    DST_ALG_RSASHA256));
219204619Sdougb	RETERR(dst__opensslrsa_init(&dst_t_func[DST_ALG_RSASHA512],
220204619Sdougb				    DST_ALG_RSASHA512));
221143731Sdougb#ifdef HAVE_OPENSSL_DSA
222143731Sdougb	RETERR(dst__openssldsa_init(&dst_t_func[DST_ALG_DSA]));
223193149Sdougb	RETERR(dst__openssldsa_init(&dst_t_func[DST_ALG_NSEC3DSA]));
224143731Sdougb#endif
225143731Sdougb	RETERR(dst__openssldh_init(&dst_t_func[DST_ALG_DH]));
226224092Sdougb#ifdef HAVE_OPENSSL_GOST
227224092Sdougb	RETERR(dst__opensslgost_init(&dst_t_func[DST_ALG_ECCGOST]));
228224092Sdougb#endif
229143731Sdougb#endif /* OPENSSL */
230143731Sdougb#ifdef GSSAPI
231143731Sdougb	RETERR(dst__gssapi_init(&dst_t_func[DST_ALG_GSSAPI]));
232143731Sdougb#endif
233143731Sdougb	dst_initialized = ISC_TRUE;
234143731Sdougb	return (ISC_R_SUCCESS);
235143731Sdougb
236143731Sdougb out:
237224092Sdougb	/* avoid immediate crash! */
238224092Sdougb	dst_initialized = ISC_TRUE;
239143731Sdougb	dst_lib_destroy();
240143731Sdougb	return (result);
241143731Sdougb}
242143731Sdougb
243143731Sdougbvoid
244143731Sdougbdst_lib_destroy(void) {
245143731Sdougb	int i;
246143731Sdougb	RUNTIME_CHECK(dst_initialized == ISC_TRUE);
247143731Sdougb	dst_initialized = ISC_FALSE;
248143731Sdougb
249143731Sdougb	for (i = 0; i < DST_MAX_ALGS; i++)
250143731Sdougb		if (dst_t_func[i] != NULL && dst_t_func[i]->cleanup != NULL)
251143731Sdougb			dst_t_func[i]->cleanup();
252143731Sdougb#ifdef OPENSSL
253143731Sdougb	dst__openssl_destroy();
254143731Sdougb#endif
255143731Sdougb	if (dst__memory_pool != NULL)
256143731Sdougb		isc_mem_detach(&dst__memory_pool);
257224092Sdougb#ifdef BIND9
258143731Sdougb	if (dst_entropy_pool != NULL)
259143731Sdougb		isc_entropy_detach(&dst_entropy_pool);
260224092Sdougb#endif
261143731Sdougb}
262143731Sdougb
263143731Sdougbisc_boolean_t
264143731Sdougbdst_algorithm_supported(unsigned int alg) {
265143731Sdougb	REQUIRE(dst_initialized == ISC_TRUE);
266143731Sdougb
267143731Sdougb	if (alg >= DST_MAX_ALGS || dst_t_func[alg] == NULL)
268143731Sdougb		return (ISC_FALSE);
269143731Sdougb	return (ISC_TRUE);
270143731Sdougb}
271143731Sdougb
272143731Sdougbisc_result_t
273143731Sdougbdst_context_create(dst_key_t *key, isc_mem_t *mctx, dst_context_t **dctxp) {
274143731Sdougb	dst_context_t *dctx;
275143731Sdougb	isc_result_t result;
276143731Sdougb
277143731Sdougb	REQUIRE(dst_initialized == ISC_TRUE);
278143731Sdougb	REQUIRE(VALID_KEY(key));
279143731Sdougb	REQUIRE(mctx != NULL);
280143731Sdougb	REQUIRE(dctxp != NULL && *dctxp == NULL);
281143731Sdougb
282143731Sdougb	if (key->func->createctx == NULL)
283143731Sdougb		return (DST_R_UNSUPPORTEDALG);
284193149Sdougb	if (key->keydata.generic == NULL)
285143731Sdougb		return (DST_R_NULLKEY);
286143731Sdougb
287143731Sdougb	dctx = isc_mem_get(mctx, sizeof(dst_context_t));
288143731Sdougb	if (dctx == NULL)
289143731Sdougb		return (ISC_R_NOMEMORY);
290143731Sdougb	dctx->key = key;
291143731Sdougb	dctx->mctx = mctx;
292143731Sdougb	result = key->func->createctx(key, dctx);
293143731Sdougb	if (result != ISC_R_SUCCESS) {
294143731Sdougb		isc_mem_put(mctx, dctx, sizeof(dst_context_t));
295143731Sdougb		return (result);
296143731Sdougb	}
297143731Sdougb	dctx->magic = CTX_MAGIC;
298143731Sdougb	*dctxp = dctx;
299143731Sdougb	return (ISC_R_SUCCESS);
300143731Sdougb}
301143731Sdougb
302143731Sdougbvoid
303143731Sdougbdst_context_destroy(dst_context_t **dctxp) {
304143731Sdougb	dst_context_t *dctx;
305143731Sdougb
306143731Sdougb	REQUIRE(dctxp != NULL && VALID_CTX(*dctxp));
307143731Sdougb
308143731Sdougb	dctx = *dctxp;
309143731Sdougb	INSIST(dctx->key->func->destroyctx != NULL);
310143731Sdougb	dctx->key->func->destroyctx(dctx);
311143731Sdougb	dctx->magic = 0;
312143731Sdougb	isc_mem_put(dctx->mctx, dctx, sizeof(dst_context_t));
313143731Sdougb	*dctxp = NULL;
314143731Sdougb}
315143731Sdougb
316143731Sdougbisc_result_t
317143731Sdougbdst_context_adddata(dst_context_t *dctx, const isc_region_t *data) {
318143731Sdougb	REQUIRE(VALID_CTX(dctx));
319143731Sdougb	REQUIRE(data != NULL);
320143731Sdougb	INSIST(dctx->key->func->adddata != NULL);
321143731Sdougb
322143731Sdougb	return (dctx->key->func->adddata(dctx, data));
323143731Sdougb}
324143731Sdougb
325143731Sdougbisc_result_t
326143731Sdougbdst_context_sign(dst_context_t *dctx, isc_buffer_t *sig) {
327143731Sdougb	dst_key_t *key;
328143731Sdougb
329143731Sdougb	REQUIRE(VALID_CTX(dctx));
330143731Sdougb	REQUIRE(sig != NULL);
331143731Sdougb
332143731Sdougb	key = dctx->key;
333143731Sdougb	CHECKALG(key->key_alg);
334193149Sdougb	if (key->keydata.generic == NULL)
335143731Sdougb		return (DST_R_NULLKEY);
336193149Sdougb
337143731Sdougb	if (key->func->sign == NULL)
338143731Sdougb		return (DST_R_NOTPRIVATEKEY);
339143731Sdougb	if (key->func->isprivate == NULL ||
340143731Sdougb	    key->func->isprivate(key) == ISC_FALSE)
341143731Sdougb		return (DST_R_NOTPRIVATEKEY);
342143731Sdougb
343143731Sdougb	return (key->func->sign(dctx, sig));
344143731Sdougb}
345143731Sdougb
346143731Sdougbisc_result_t
347143731Sdougbdst_context_verify(dst_context_t *dctx, isc_region_t *sig) {
348143731Sdougb	REQUIRE(VALID_CTX(dctx));
349143731Sdougb	REQUIRE(sig != NULL);
350143731Sdougb
351143731Sdougb	CHECKALG(dctx->key->key_alg);
352193149Sdougb	if (dctx->key->keydata.generic == NULL)
353143731Sdougb		return (DST_R_NULLKEY);
354143731Sdougb	if (dctx->key->func->verify == NULL)
355143731Sdougb		return (DST_R_NOTPUBLICKEY);
356143731Sdougb
357143731Sdougb	return (dctx->key->func->verify(dctx, sig));
358143731Sdougb}
359143731Sdougb
360143731Sdougbisc_result_t
361143731Sdougbdst_key_computesecret(const dst_key_t *pub, const dst_key_t *priv,
362143731Sdougb		      isc_buffer_t *secret)
363143731Sdougb{
364143731Sdougb	REQUIRE(dst_initialized == ISC_TRUE);
365143731Sdougb	REQUIRE(VALID_KEY(pub) && VALID_KEY(priv));
366143731Sdougb	REQUIRE(secret != NULL);
367143731Sdougb
368143731Sdougb	CHECKALG(pub->key_alg);
369143731Sdougb	CHECKALG(priv->key_alg);
370143731Sdougb
371193149Sdougb	if (pub->keydata.generic == NULL || priv->keydata.generic == NULL)
372143731Sdougb		return (DST_R_NULLKEY);
373143731Sdougb
374143731Sdougb	if (pub->key_alg != priv->key_alg ||
375143731Sdougb	    pub->func->computesecret == NULL ||
376143731Sdougb	    priv->func->computesecret == NULL)
377143731Sdougb		return (DST_R_KEYCANNOTCOMPUTESECRET);
378143731Sdougb
379143731Sdougb	if (dst_key_isprivate(priv) == ISC_FALSE)
380143731Sdougb		return (DST_R_NOTPRIVATEKEY);
381143731Sdougb
382143731Sdougb	return (pub->func->computesecret(pub, priv, secret));
383143731Sdougb}
384143731Sdougb
385143731Sdougbisc_result_t
386143731Sdougbdst_key_tofile(const dst_key_t *key, int type, const char *directory) {
387143731Sdougb	isc_result_t ret = ISC_R_SUCCESS;
388143731Sdougb
389143731Sdougb	REQUIRE(dst_initialized == ISC_TRUE);
390143731Sdougb	REQUIRE(VALID_KEY(key));
391143731Sdougb	REQUIRE((type & (DST_TYPE_PRIVATE | DST_TYPE_PUBLIC)) != 0);
392143731Sdougb
393143731Sdougb	CHECKALG(key->key_alg);
394143731Sdougb
395143731Sdougb	if (key->func->tofile == NULL)
396143731Sdougb		return (DST_R_UNSUPPORTEDALG);
397143731Sdougb
398143731Sdougb	if (type & DST_TYPE_PUBLIC) {
399143731Sdougb		ret = write_public_key(key, type, directory);
400143731Sdougb		if (ret != ISC_R_SUCCESS)
401143731Sdougb			return (ret);
402143731Sdougb	}
403143731Sdougb
404143731Sdougb	if ((type & DST_TYPE_PRIVATE) &&
405143731Sdougb	    (key->key_flags & DNS_KEYFLAG_TYPEMASK) != DNS_KEYTYPE_NOKEY)
406143731Sdougb		return (key->func->tofile(key, directory));
407143731Sdougb	else
408143731Sdougb		return (ISC_R_SUCCESS);
409143731Sdougb}
410143731Sdougb
411143731Sdougbisc_result_t
412143731Sdougbdst_key_fromfile(dns_name_t *name, dns_keytag_t id,
413143731Sdougb		 unsigned int alg, int type, const char *directory,
414143731Sdougb		 isc_mem_t *mctx, dst_key_t **keyp)
415143731Sdougb{
416143731Sdougb	char filename[ISC_DIR_NAMEMAX];
417143731Sdougb	isc_buffer_t b;
418143731Sdougb	dst_key_t *key;
419143731Sdougb	isc_result_t result;
420143731Sdougb
421143731Sdougb	REQUIRE(dst_initialized == ISC_TRUE);
422143731Sdougb	REQUIRE(dns_name_isabsolute(name));
423143731Sdougb	REQUIRE((type & (DST_TYPE_PRIVATE | DST_TYPE_PUBLIC)) != 0);
424143731Sdougb	REQUIRE(mctx != NULL);
425143731Sdougb	REQUIRE(keyp != NULL && *keyp == NULL);
426143731Sdougb
427143731Sdougb	CHECKALG(alg);
428143731Sdougb
429143731Sdougb	isc_buffer_init(&b, filename, sizeof(filename));
430143731Sdougb	result = buildfilename(name, id, alg, type, directory, &b);
431143731Sdougb	if (result != ISC_R_SUCCESS)
432143731Sdougb		return (result);
433143731Sdougb
434143731Sdougb	key = NULL;
435224092Sdougb	result = dst_key_fromnamedfile(filename, NULL, type, mctx, &key);
436143731Sdougb	if (result != ISC_R_SUCCESS)
437143731Sdougb		return (result);
438143731Sdougb
439143731Sdougb	result = computeid(key);
440143731Sdougb	if (result != ISC_R_SUCCESS) {
441143731Sdougb		dst_key_free(&key);
442143731Sdougb		return (result);
443143731Sdougb	}
444143731Sdougb
445193149Sdougb	if (!dns_name_equal(name, key->key_name) || id != key->key_id ||
446193149Sdougb	    alg != key->key_alg) {
447143731Sdougb		dst_key_free(&key);
448143731Sdougb		return (DST_R_INVALIDPRIVATEKEY);
449143731Sdougb	}
450143731Sdougb	key->key_id = id;
451143731Sdougb
452143731Sdougb	*keyp = key;
453143731Sdougb	return (ISC_R_SUCCESS);
454143731Sdougb}
455143731Sdougb
456143731Sdougbisc_result_t
457224092Sdougbdst_key_fromnamedfile(const char *filename, const char *dirname,
458224092Sdougb		      int type, isc_mem_t *mctx, dst_key_t **keyp)
459143731Sdougb{
460143731Sdougb	isc_result_t result;
461143731Sdougb	dst_key_t *pubkey = NULL, *key = NULL;
462143731Sdougb	char *newfilename = NULL;
463143731Sdougb	int newfilenamelen = 0;
464143731Sdougb	isc_lex_t *lex = NULL;
465143731Sdougb
466143731Sdougb	REQUIRE(dst_initialized == ISC_TRUE);
467143731Sdougb	REQUIRE(filename != NULL);
468143731Sdougb	REQUIRE((type & (DST_TYPE_PRIVATE | DST_TYPE_PUBLIC)) != 0);
469143731Sdougb	REQUIRE(mctx != NULL);
470143731Sdougb	REQUIRE(keyp != NULL && *keyp == NULL);
471143731Sdougb
472224092Sdougb	/* If an absolute path is specified, don't use the key directory */
473224092Sdougb#ifndef WIN32
474224092Sdougb	if (filename[0] == '/')
475224092Sdougb		dirname = NULL;
476224092Sdougb#else /* WIN32 */
477224092Sdougb	if (filename[0] == '/' || filename[0] == '\\')
478224092Sdougb		dirname = NULL;
479224092Sdougb#endif
480224092Sdougb
481170222Sdougb	newfilenamelen = strlen(filename) + 5;
482224092Sdougb	if (dirname != NULL)
483224092Sdougb		newfilenamelen += strlen(dirname) + 1;
484170222Sdougb	newfilename = isc_mem_get(mctx, newfilenamelen);
485170222Sdougb	if (newfilename == NULL)
486170222Sdougb		return (ISC_R_NOMEMORY);
487224092Sdougb	result = addsuffix(newfilename, newfilenamelen,
488224092Sdougb			   dirname, filename, ".key");
489170222Sdougb	INSIST(result == ISC_R_SUCCESS);
490170222Sdougb
491170222Sdougb	result = dst_key_read_public(newfilename, type, mctx, &pubkey);
492170222Sdougb	isc_mem_put(mctx, newfilename, newfilenamelen);
493170222Sdougb	newfilename = NULL;
494143731Sdougb	if (result != ISC_R_SUCCESS)
495143731Sdougb		return (result);
496143731Sdougb
497143731Sdougb	if ((type & (DST_TYPE_PRIVATE | DST_TYPE_PUBLIC)) == DST_TYPE_PUBLIC ||
498193149Sdougb	    (pubkey->key_flags & DNS_KEYFLAG_TYPEMASK) == DNS_KEYTYPE_NOKEY) {
499143731Sdougb		result = computeid(pubkey);
500143731Sdougb		if (result != ISC_R_SUCCESS) {
501143731Sdougb			dst_key_free(&pubkey);
502143731Sdougb			return (result);
503143731Sdougb		}
504143731Sdougb
505143731Sdougb		*keyp = pubkey;
506143731Sdougb		return (ISC_R_SUCCESS);
507143731Sdougb	}
508143731Sdougb
509143731Sdougb	result = algorithm_status(pubkey->key_alg);
510143731Sdougb	if (result != ISC_R_SUCCESS) {
511143731Sdougb		dst_key_free(&pubkey);
512143731Sdougb		return (result);
513143731Sdougb	}
514143731Sdougb
515143731Sdougb	key = get_key_struct(pubkey->key_name, pubkey->key_alg,
516143731Sdougb			     pubkey->key_flags, pubkey->key_proto, 0,
517143731Sdougb			     pubkey->key_class, mctx);
518224092Sdougb	if (key == NULL) {
519224092Sdougb		dst_key_free(&pubkey);
520143731Sdougb		return (ISC_R_NOMEMORY);
521224092Sdougb	}
522143731Sdougb
523143731Sdougb	if (key->func->parse == NULL)
524143731Sdougb		RETERR(DST_R_UNSUPPORTEDALG);
525143731Sdougb
526143731Sdougb	newfilenamelen = strlen(filename) + 9;
527224092Sdougb	if (dirname != NULL)
528224092Sdougb		newfilenamelen += strlen(dirname) + 1;
529143731Sdougb	newfilename = isc_mem_get(mctx, newfilenamelen);
530143731Sdougb	if (newfilename == NULL)
531143731Sdougb		RETERR(ISC_R_NOMEMORY);
532224092Sdougb	result = addsuffix(newfilename, newfilenamelen,
533224092Sdougb			   dirname, filename, ".private");
534143731Sdougb	INSIST(result == ISC_R_SUCCESS);
535143731Sdougb
536143731Sdougb	RETERR(isc_lex_create(mctx, 1500, &lex));
537143731Sdougb	RETERR(isc_lex_openfile(lex, newfilename));
538143731Sdougb	isc_mem_put(mctx, newfilename, newfilenamelen);
539143731Sdougb
540224092Sdougb	RETERR(key->func->parse(key, lex, pubkey));
541143731Sdougb	isc_lex_destroy(&lex);
542143731Sdougb
543143731Sdougb	RETERR(computeid(key));
544143731Sdougb
545224092Sdougb	if (pubkey->key_id != key->key_id)
546143731Sdougb		RETERR(DST_R_INVALIDPRIVATEKEY);
547224092Sdougb	dst_key_free(&pubkey);
548143731Sdougb
549143731Sdougb	*keyp = key;
550143731Sdougb	return (ISC_R_SUCCESS);
551218384Sdougb
552143731Sdougb out:
553224092Sdougb	if (pubkey != NULL)
554224092Sdougb		dst_key_free(&pubkey);
555143731Sdougb	if (newfilename != NULL)
556143731Sdougb		isc_mem_put(mctx, newfilename, newfilenamelen);
557143731Sdougb	if (lex != NULL)
558143731Sdougb		isc_lex_destroy(&lex);
559143731Sdougb	dst_key_free(&key);
560143731Sdougb	return (result);
561143731Sdougb}
562143731Sdougb
563143731Sdougbisc_result_t
564143731Sdougbdst_key_todns(const dst_key_t *key, isc_buffer_t *target) {
565143731Sdougb	REQUIRE(dst_initialized == ISC_TRUE);
566143731Sdougb	REQUIRE(VALID_KEY(key));
567143731Sdougb	REQUIRE(target != NULL);
568143731Sdougb
569143731Sdougb	CHECKALG(key->key_alg);
570143731Sdougb
571143731Sdougb	if (key->func->todns == NULL)
572143731Sdougb		return (DST_R_UNSUPPORTEDALG);
573143731Sdougb
574143731Sdougb	if (isc_buffer_availablelength(target) < 4)
575143731Sdougb		return (ISC_R_NOSPACE);
576143731Sdougb	isc_buffer_putuint16(target, (isc_uint16_t)(key->key_flags & 0xffff));
577143731Sdougb	isc_buffer_putuint8(target, (isc_uint8_t)key->key_proto);
578143731Sdougb	isc_buffer_putuint8(target, (isc_uint8_t)key->key_alg);
579143731Sdougb
580143731Sdougb	if (key->key_flags & DNS_KEYFLAG_EXTENDED) {
581143731Sdougb		if (isc_buffer_availablelength(target) < 2)
582143731Sdougb			return (ISC_R_NOSPACE);
583143731Sdougb		isc_buffer_putuint16(target,
584143731Sdougb				     (isc_uint16_t)((key->key_flags >> 16)
585143731Sdougb						    & 0xffff));
586143731Sdougb	}
587143731Sdougb
588193149Sdougb	if (key->keydata.generic == NULL) /*%< NULL KEY */
589143731Sdougb		return (ISC_R_SUCCESS);
590143731Sdougb
591143731Sdougb	return (key->func->todns(key, target));
592143731Sdougb}
593143731Sdougb
594143731Sdougbisc_result_t
595143731Sdougbdst_key_fromdns(dns_name_t *name, dns_rdataclass_t rdclass,
596143731Sdougb		isc_buffer_t *source, isc_mem_t *mctx, dst_key_t **keyp)
597143731Sdougb{
598143731Sdougb	isc_uint8_t alg, proto;
599143731Sdougb	isc_uint32_t flags, extflags;
600143731Sdougb	dst_key_t *key = NULL;
601143731Sdougb	dns_keytag_t id;
602143731Sdougb	isc_region_t r;
603143731Sdougb	isc_result_t result;
604143731Sdougb
605143731Sdougb	REQUIRE(dst_initialized);
606143731Sdougb
607143731Sdougb	isc_buffer_remainingregion(source, &r);
608143731Sdougb
609143731Sdougb	if (isc_buffer_remaininglength(source) < 4)
610143731Sdougb		return (DST_R_INVALIDPUBLICKEY);
611143731Sdougb	flags = isc_buffer_getuint16(source);
612143731Sdougb	proto = isc_buffer_getuint8(source);
613143731Sdougb	alg = isc_buffer_getuint8(source);
614143731Sdougb
615143731Sdougb	id = dst_region_computeid(&r, alg);
616143731Sdougb
617143731Sdougb	if (flags & DNS_KEYFLAG_EXTENDED) {
618143731Sdougb		if (isc_buffer_remaininglength(source) < 2)
619143731Sdougb			return (DST_R_INVALIDPUBLICKEY);
620143731Sdougb		extflags = isc_buffer_getuint16(source);
621143731Sdougb		flags |= (extflags << 16);
622143731Sdougb	}
623143731Sdougb
624143731Sdougb	result = frombuffer(name, alg, flags, proto, rdclass, source,
625143731Sdougb			    mctx, &key);
626143731Sdougb	if (result != ISC_R_SUCCESS)
627143731Sdougb		return (result);
628143731Sdougb	key->key_id = id;
629143731Sdougb
630143731Sdougb	*keyp = key;
631143731Sdougb	return (ISC_R_SUCCESS);
632143731Sdougb}
633143731Sdougb
634143731Sdougbisc_result_t
635143731Sdougbdst_key_frombuffer(dns_name_t *name, unsigned int alg,
636143731Sdougb		   unsigned int flags, unsigned int protocol,
637143731Sdougb		   dns_rdataclass_t rdclass,
638143731Sdougb		   isc_buffer_t *source, isc_mem_t *mctx, dst_key_t **keyp)
639143731Sdougb{
640143731Sdougb	dst_key_t *key = NULL;
641143731Sdougb	isc_result_t result;
642143731Sdougb
643143731Sdougb	REQUIRE(dst_initialized);
644143731Sdougb
645143731Sdougb	result = frombuffer(name, alg, flags, protocol, rdclass, source,
646143731Sdougb			    mctx, &key);
647143731Sdougb	if (result != ISC_R_SUCCESS)
648143731Sdougb		return (result);
649143731Sdougb
650143731Sdougb	result = computeid(key);
651143731Sdougb	if (result != ISC_R_SUCCESS) {
652143731Sdougb		dst_key_free(&key);
653143731Sdougb		return (result);
654143731Sdougb	}
655143731Sdougb
656143731Sdougb	*keyp = key;
657143731Sdougb	return (ISC_R_SUCCESS);
658143731Sdougb}
659143731Sdougb
660143731Sdougbisc_result_t
661143731Sdougbdst_key_tobuffer(const dst_key_t *key, isc_buffer_t *target) {
662143731Sdougb	REQUIRE(dst_initialized == ISC_TRUE);
663143731Sdougb	REQUIRE(VALID_KEY(key));
664143731Sdougb	REQUIRE(target != NULL);
665143731Sdougb
666143731Sdougb	CHECKALG(key->key_alg);
667143731Sdougb
668143731Sdougb	if (key->func->todns == NULL)
669143731Sdougb		return (DST_R_UNSUPPORTEDALG);
670143731Sdougb
671143731Sdougb	return (key->func->todns(key, target));
672143731Sdougb}
673143731Sdougb
674143731Sdougbisc_result_t
675143731Sdougbdst_key_privatefrombuffer(dst_key_t *key, isc_buffer_t *buffer) {
676143731Sdougb	isc_lex_t *lex = NULL;
677143731Sdougb	isc_result_t result = ISC_R_SUCCESS;
678143731Sdougb
679143731Sdougb	REQUIRE(dst_initialized == ISC_TRUE);
680143731Sdougb	REQUIRE(VALID_KEY(key));
681143731Sdougb	REQUIRE(!dst_key_isprivate(key));
682143731Sdougb	REQUIRE(buffer != NULL);
683143731Sdougb
684143731Sdougb	if (key->func->parse == NULL)
685143731Sdougb		RETERR(DST_R_UNSUPPORTEDALG);
686143731Sdougb
687143731Sdougb	RETERR(isc_lex_create(key->mctx, 1500, &lex));
688143731Sdougb	RETERR(isc_lex_openbuffer(lex, buffer));
689224092Sdougb	RETERR(key->func->parse(key, lex, NULL));
690143731Sdougb out:
691143731Sdougb	if (lex != NULL)
692143731Sdougb		isc_lex_destroy(&lex);
693143731Sdougb	return (result);
694143731Sdougb}
695143731Sdougb
696193149Sdougbgss_ctx_id_t
697193149Sdougbdst_key_getgssctx(const dst_key_t *key)
698193149Sdougb{
699193149Sdougb	REQUIRE(key != NULL);
700193149Sdougb
701193149Sdougb	return (key->keydata.gssctx);
702193149Sdougb}
703193149Sdougb
704143731Sdougbisc_result_t
705193149Sdougbdst_key_fromgssapi(dns_name_t *name, gss_ctx_id_t gssctx, isc_mem_t *mctx,
706224092Sdougb		   dst_key_t **keyp, isc_region_t *intoken)
707143731Sdougb{
708143731Sdougb	dst_key_t *key;
709224092Sdougb	isc_result_t result;
710143731Sdougb
711193149Sdougb	REQUIRE(gssctx != NULL);
712143731Sdougb	REQUIRE(keyp != NULL && *keyp == NULL);
713143731Sdougb
714143731Sdougb	key = get_key_struct(name, DST_ALG_GSSAPI, 0, DNS_KEYPROTO_DNSSEC,
715143731Sdougb			     0, dns_rdataclass_in, mctx);
716143731Sdougb	if (key == NULL)
717143731Sdougb		return (ISC_R_NOMEMORY);
718193149Sdougb
719224092Sdougb	if (intoken != NULL) {
720224092Sdougb		/*
721224092Sdougb		 * Keep the token for use by external ssu rules. They may need
722224092Sdougb		 * to examine the PAC in the kerberos ticket.
723224092Sdougb		 */
724224092Sdougb		RETERR(isc_buffer_allocate(key->mctx, &key->key_tkeytoken,
725224092Sdougb		       intoken->length));
726224092Sdougb		RETERR(isc_buffer_copyregion(key->key_tkeytoken, intoken));
727224092Sdougb	}
728224092Sdougb
729193149Sdougb	key->keydata.gssctx = gssctx;
730143731Sdougb	*keyp = key;
731224092Sdougb	result = ISC_R_SUCCESS;
732224092Sdougbout:
733224092Sdougb	return result;
734143731Sdougb}
735143731Sdougb
736143731Sdougbisc_result_t
737193149Sdougbdst_key_fromlabel(dns_name_t *name, int alg, unsigned int flags,
738193149Sdougb		  unsigned int protocol, dns_rdataclass_t rdclass,
739193149Sdougb		  const char *engine, const char *label, const char *pin,
740193149Sdougb		  isc_mem_t *mctx, dst_key_t **keyp)
741193149Sdougb{
742193149Sdougb	dst_key_t *key;
743193149Sdougb	isc_result_t result;
744193149Sdougb
745193149Sdougb	REQUIRE(dst_initialized == ISC_TRUE);
746193149Sdougb	REQUIRE(dns_name_isabsolute(name));
747193149Sdougb	REQUIRE(mctx != NULL);
748193149Sdougb	REQUIRE(keyp != NULL && *keyp == NULL);
749193149Sdougb	REQUIRE(label != NULL);
750193149Sdougb
751193149Sdougb	CHECKALG(alg);
752193149Sdougb
753193149Sdougb	key = get_key_struct(name, alg, flags, protocol, 0, rdclass, mctx);
754193149Sdougb	if (key == NULL)
755193149Sdougb		return (ISC_R_NOMEMORY);
756193149Sdougb
757193149Sdougb	if (key->func->fromlabel == NULL) {
758193149Sdougb		dst_key_free(&key);
759193149Sdougb		return (DST_R_UNSUPPORTEDALG);
760193149Sdougb	}
761193149Sdougb
762193149Sdougb	result = key->func->fromlabel(key, engine, label, pin);
763193149Sdougb	if (result != ISC_R_SUCCESS) {
764193149Sdougb		dst_key_free(&key);
765193149Sdougb		return (result);
766193149Sdougb	}
767193149Sdougb
768193149Sdougb	result = computeid(key);
769193149Sdougb	if (result != ISC_R_SUCCESS) {
770193149Sdougb		dst_key_free(&key);
771193149Sdougb		return (result);
772193149Sdougb	}
773193149Sdougb
774193149Sdougb	*keyp = key;
775193149Sdougb	return (ISC_R_SUCCESS);
776193149Sdougb}
777193149Sdougb
778193149Sdougbisc_result_t
779143731Sdougbdst_key_generate(dns_name_t *name, unsigned int alg,
780143731Sdougb		 unsigned int bits, unsigned int param,
781143731Sdougb		 unsigned int flags, unsigned int protocol,
782143731Sdougb		 dns_rdataclass_t rdclass,
783143731Sdougb		 isc_mem_t *mctx, dst_key_t **keyp)
784143731Sdougb{
785224092Sdougb	return (dst_key_generate2(name, alg, bits, param, flags, protocol,
786224092Sdougb				  rdclass, mctx, keyp, NULL));
787224092Sdougb}
788224092Sdougb
789224092Sdougbisc_result_t
790224092Sdougbdst_key_generate2(dns_name_t *name, unsigned int alg,
791224092Sdougb		  unsigned int bits, unsigned int param,
792224092Sdougb		  unsigned int flags, unsigned int protocol,
793224092Sdougb		  dns_rdataclass_t rdclass,
794224092Sdougb		  isc_mem_t *mctx, dst_key_t **keyp,
795224092Sdougb		  void (*callback)(int))
796224092Sdougb{
797143731Sdougb	dst_key_t *key;
798143731Sdougb	isc_result_t ret;
799143731Sdougb
800143731Sdougb	REQUIRE(dst_initialized == ISC_TRUE);
801143731Sdougb	REQUIRE(dns_name_isabsolute(name));
802143731Sdougb	REQUIRE(mctx != NULL);
803143731Sdougb	REQUIRE(keyp != NULL && *keyp == NULL);
804143731Sdougb
805143731Sdougb	CHECKALG(alg);
806143731Sdougb
807143731Sdougb	key = get_key_struct(name, alg, flags, protocol, bits, rdclass, mctx);
808143731Sdougb	if (key == NULL)
809143731Sdougb		return (ISC_R_NOMEMORY);
810143731Sdougb
811170222Sdougb	if (bits == 0) { /*%< NULL KEY */
812143731Sdougb		key->key_flags |= DNS_KEYTYPE_NOKEY;
813143731Sdougb		*keyp = key;
814143731Sdougb		return (ISC_R_SUCCESS);
815143731Sdougb	}
816143731Sdougb
817143731Sdougb	if (key->func->generate == NULL) {
818143731Sdougb		dst_key_free(&key);
819143731Sdougb		return (DST_R_UNSUPPORTEDALG);
820143731Sdougb	}
821143731Sdougb
822224092Sdougb	ret = key->func->generate(key, param, callback);
823143731Sdougb	if (ret != ISC_R_SUCCESS) {
824143731Sdougb		dst_key_free(&key);
825143731Sdougb		return (ret);
826143731Sdougb	}
827143731Sdougb
828143731Sdougb	ret = computeid(key);
829143731Sdougb	if (ret != ISC_R_SUCCESS) {
830143731Sdougb		dst_key_free(&key);
831143731Sdougb		return (ret);
832143731Sdougb	}
833143731Sdougb
834143731Sdougb	*keyp = key;
835143731Sdougb	return (ISC_R_SUCCESS);
836143731Sdougb}
837143731Sdougb
838224092Sdougbisc_result_t
839224092Sdougbdst_key_getnum(const dst_key_t *key, int type, isc_uint32_t *valuep)
840224092Sdougb{
841224092Sdougb	REQUIRE(VALID_KEY(key));
842224092Sdougb	REQUIRE(valuep != NULL);
843224092Sdougb	REQUIRE(type <= DST_MAX_NUMERIC);
844224092Sdougb	if (!key->numset[type])
845224092Sdougb		return (ISC_R_NOTFOUND);
846224092Sdougb	*valuep = key->nums[type];
847224092Sdougb	return (ISC_R_SUCCESS);
848224092Sdougb}
849224092Sdougb
850224092Sdougbvoid
851224092Sdougbdst_key_setnum(dst_key_t *key, int type, isc_uint32_t value)
852224092Sdougb{
853224092Sdougb	REQUIRE(VALID_KEY(key));
854224092Sdougb	REQUIRE(type <= DST_MAX_NUMERIC);
855224092Sdougb	key->nums[type] = value;
856224092Sdougb	key->numset[type] = ISC_TRUE;
857224092Sdougb}
858224092Sdougb
859224092Sdougbvoid
860224092Sdougbdst_key_unsetnum(dst_key_t *key, int type)
861224092Sdougb{
862224092Sdougb	REQUIRE(VALID_KEY(key));
863224092Sdougb	REQUIRE(type <= DST_MAX_NUMERIC);
864224092Sdougb	key->numset[type] = ISC_FALSE;
865224092Sdougb}
866224092Sdougb
867224092Sdougbisc_result_t
868224092Sdougbdst_key_gettime(const dst_key_t *key, int type, isc_stdtime_t *timep) {
869224092Sdougb	REQUIRE(VALID_KEY(key));
870224092Sdougb	REQUIRE(timep != NULL);
871224092Sdougb	REQUIRE(type <= DST_MAX_TIMES);
872224092Sdougb	if (!key->timeset[type])
873224092Sdougb		return (ISC_R_NOTFOUND);
874224092Sdougb	*timep = key->times[type];
875224092Sdougb	return (ISC_R_SUCCESS);
876224092Sdougb}
877224092Sdougb
878224092Sdougbvoid
879224092Sdougbdst_key_settime(dst_key_t *key, int type, isc_stdtime_t when) {
880224092Sdougb	REQUIRE(VALID_KEY(key));
881224092Sdougb	REQUIRE(type <= DST_MAX_TIMES);
882224092Sdougb	key->times[type] = when;
883224092Sdougb	key->timeset[type] = ISC_TRUE;
884224092Sdougb}
885224092Sdougb
886224092Sdougbvoid
887224092Sdougbdst_key_unsettime(dst_key_t *key, int type) {
888224092Sdougb	REQUIRE(VALID_KEY(key));
889224092Sdougb	REQUIRE(type <= DST_MAX_TIMES);
890224092Sdougb	key->timeset[type] = ISC_FALSE;
891224092Sdougb}
892224092Sdougb
893224092Sdougbisc_result_t
894224092Sdougbdst_key_getprivateformat(const dst_key_t *key, int *majorp, int *minorp) {
895224092Sdougb	REQUIRE(VALID_KEY(key));
896224092Sdougb	REQUIRE(majorp != NULL);
897224092Sdougb	REQUIRE(minorp != NULL);
898224092Sdougb	*majorp = key->fmt_major;
899224092Sdougb	*minorp = key->fmt_minor;
900224092Sdougb	return (ISC_R_SUCCESS);
901224092Sdougb}
902224092Sdougb
903224092Sdougbvoid
904224092Sdougbdst_key_setprivateformat(dst_key_t *key, int major, int minor) {
905224092Sdougb	REQUIRE(VALID_KEY(key));
906224092Sdougb	key->fmt_major = major;
907224092Sdougb	key->fmt_minor = minor;
908224092Sdougb}
909224092Sdougb
910224092Sdougbstatic isc_boolean_t
911224092Sdougbcomparekeys(const dst_key_t *key1, const dst_key_t *key2,
912224092Sdougb	    isc_boolean_t match_revoked_key,
913224092Sdougb	    isc_boolean_t (*compare)(const dst_key_t *key1,
914224092Sdougb				     const dst_key_t *key2))
915224092Sdougb{
916143731Sdougb	REQUIRE(dst_initialized == ISC_TRUE);
917143731Sdougb	REQUIRE(VALID_KEY(key1));
918143731Sdougb	REQUIRE(VALID_KEY(key2));
919143731Sdougb
920143731Sdougb	if (key1 == key2)
921143731Sdougb		return (ISC_TRUE);
922224092Sdougb
923143731Sdougb	if (key1 == NULL || key2 == NULL)
924143731Sdougb		return (ISC_FALSE);
925224092Sdougb
926224092Sdougb	if (key1->key_alg != key2->key_alg)
927224092Sdougb		return (ISC_FALSE);
928224092Sdougb
929224092Sdougb	/*
930224092Sdougb	 * For all algorithms except RSAMD5, revoking the key
931224092Sdougb	 * changes the key ID, increasing it by 128.  If we want to
932224092Sdougb	 * be able to find matching keys even if one of them is the
933224092Sdougb	 * revoked version of the other one, then we need to check
934224092Sdougb	 * for that possibility.
935224092Sdougb	 */
936224092Sdougb	if (key1->key_id != key2->key_id) {
937224092Sdougb		if (!match_revoked_key)
938224092Sdougb			return (ISC_FALSE);
939224092Sdougb		if (key1->key_alg == DST_ALG_RSAMD5)
940224092Sdougb			return (ISC_FALSE);
941224092Sdougb		if ((key1->key_flags & DNS_KEYFLAG_REVOKE) ==
942224092Sdougb		    (key2->key_flags & DNS_KEYFLAG_REVOKE))
943224092Sdougb			return (ISC_FALSE);
944224092Sdougb		if ((key1->key_flags & DNS_KEYFLAG_REVOKE) != 0 &&
945224092Sdougb		    key1->key_id != ((key2->key_id + 128) & 0xffff))
946224092Sdougb			return (ISC_FALSE);
947224092Sdougb		if ((key2->key_flags & DNS_KEYFLAG_REVOKE) != 0 &&
948224092Sdougb		    key2->key_id != ((key1->key_id + 128) & 0xffff))
949224092Sdougb			return (ISC_FALSE);
950224092Sdougb	}
951224092Sdougb
952224092Sdougb	if (compare != NULL)
953224092Sdougb		return (compare(key1, key2));
954143731Sdougb	else
955143731Sdougb		return (ISC_FALSE);
956143731Sdougb}
957143731Sdougb
958224092Sdougb
959224092Sdougb/*
960224092Sdougb * Compares only the public portion of two keys, by converting them
961224092Sdougb * both to wire format and comparing the results.
962224092Sdougb */
963224092Sdougbstatic isc_boolean_t
964224092Sdougbpub_compare(const dst_key_t *key1, const dst_key_t *key2) {
965224092Sdougb	isc_result_t result;
966224092Sdougb	unsigned char buf1[DST_KEY_MAXSIZE], buf2[DST_KEY_MAXSIZE];
967224092Sdougb	isc_buffer_t b1, b2;
968224092Sdougb	isc_region_t r1, r2;
969224092Sdougb
970224092Sdougb	isc_buffer_init(&b1, buf1, sizeof(buf1));
971224092Sdougb	result = dst_key_todns(key1, &b1);
972224092Sdougb	if (result != ISC_R_SUCCESS)
973224092Sdougb		return (ISC_FALSE);
974224092Sdougb	/* Zero out flags. */
975224092Sdougb	buf1[0] = buf1[1] = 0;
976224092Sdougb	if ((key1->key_flags & DNS_KEYFLAG_EXTENDED) != 0)
977224092Sdougb		isc_buffer_subtract(&b1, 2);
978224092Sdougb
979224092Sdougb	isc_buffer_init(&b2, buf2, sizeof(buf2));
980224092Sdougb	result = dst_key_todns(key2, &b2);
981224092Sdougb	if (result != ISC_R_SUCCESS)
982224092Sdougb		return (ISC_FALSE);
983224092Sdougb	/* Zero out flags. */
984224092Sdougb	buf2[0] = buf2[1] = 0;
985224092Sdougb	if ((key2->key_flags & DNS_KEYFLAG_EXTENDED) != 0)
986224092Sdougb		isc_buffer_subtract(&b2, 2);
987224092Sdougb
988224092Sdougb	isc_buffer_usedregion(&b1, &r1);
989224092Sdougb	/* Remove extended flags. */
990224092Sdougb	if ((key1->key_flags & DNS_KEYFLAG_EXTENDED) != 0) {
991224092Sdougb		memmove(&buf1[4], &buf1[6], r1.length - 6);
992224092Sdougb		r1.length -= 2;
993224092Sdougb	}
994224092Sdougb
995224092Sdougb	isc_buffer_usedregion(&b2, &r2);
996224092Sdougb	/* Remove extended flags. */
997224092Sdougb	if ((key2->key_flags & DNS_KEYFLAG_EXTENDED) != 0) {
998224092Sdougb		memmove(&buf2[4], &buf2[6], r2.length - 6);
999224092Sdougb		r2.length -= 2;
1000224092Sdougb	}
1001224092Sdougb	return (ISC_TF(isc_region_compare(&r1, &r2) == 0));
1002224092Sdougb}
1003224092Sdougb
1004143731Sdougbisc_boolean_t
1005224092Sdougbdst_key_compare(const dst_key_t *key1, const dst_key_t *key2) {
1006224092Sdougb	return (comparekeys(key1, key2, ISC_FALSE, key1->func->compare));
1007224092Sdougb}
1008224092Sdougb
1009224092Sdougbisc_boolean_t
1010224092Sdougbdst_key_pubcompare(const dst_key_t *key1, const dst_key_t *key2,
1011224092Sdougb		   isc_boolean_t match_revoked_key)
1012224092Sdougb{
1013224092Sdougb	return (comparekeys(key1, key2, match_revoked_key, pub_compare));
1014224092Sdougb}
1015224092Sdougb
1016224092Sdougb
1017224092Sdougbisc_boolean_t
1018143731Sdougbdst_key_paramcompare(const dst_key_t *key1, const dst_key_t *key2) {
1019143731Sdougb	REQUIRE(dst_initialized == ISC_TRUE);
1020143731Sdougb	REQUIRE(VALID_KEY(key1));
1021143731Sdougb	REQUIRE(VALID_KEY(key2));
1022143731Sdougb
1023143731Sdougb	if (key1 == key2)
1024143731Sdougb		return (ISC_TRUE);
1025143731Sdougb	if (key1 == NULL || key2 == NULL)
1026143731Sdougb		return (ISC_FALSE);
1027143731Sdougb	if (key1->key_alg == key2->key_alg &&
1028143731Sdougb	    key1->func->paramcompare != NULL &&
1029143731Sdougb	    key1->func->paramcompare(key1, key2) == ISC_TRUE)
1030143731Sdougb		return (ISC_TRUE);
1031143731Sdougb	else
1032143731Sdougb		return (ISC_FALSE);
1033143731Sdougb}
1034143731Sdougb
1035143731Sdougbvoid
1036218384Sdougbdst_key_attach(dst_key_t *source, dst_key_t **target) {
1037218384Sdougb
1038218384Sdougb	REQUIRE(dst_initialized == ISC_TRUE);
1039218384Sdougb	REQUIRE(target != NULL && *target == NULL);
1040218384Sdougb	REQUIRE(VALID_KEY(source));
1041218384Sdougb
1042218384Sdougb	isc_refcount_increment(&source->refs, NULL);
1043218384Sdougb	*target = source;
1044218384Sdougb}
1045218384Sdougb
1046218384Sdougbvoid
1047143731Sdougbdst_key_free(dst_key_t **keyp) {
1048143731Sdougb	isc_mem_t *mctx;
1049143731Sdougb	dst_key_t *key;
1050218384Sdougb	unsigned int refs;
1051143731Sdougb
1052143731Sdougb	REQUIRE(dst_initialized == ISC_TRUE);
1053143731Sdougb	REQUIRE(keyp != NULL && VALID_KEY(*keyp));
1054143731Sdougb
1055143731Sdougb	key = *keyp;
1056143731Sdougb	mctx = key->mctx;
1057143731Sdougb
1058218384Sdougb	isc_refcount_decrement(&key->refs, &refs);
1059218384Sdougb	if (refs != 0)
1060218384Sdougb		return;
1061218384Sdougb
1062218384Sdougb	isc_refcount_destroy(&key->refs);
1063193149Sdougb	if (key->keydata.generic != NULL) {
1064143731Sdougb		INSIST(key->func->destroy != NULL);
1065143731Sdougb		key->func->destroy(key);
1066143731Sdougb	}
1067193149Sdougb	if (key->engine != NULL)
1068193149Sdougb		isc_mem_free(mctx, key->engine);
1069193149Sdougb	if (key->label != NULL)
1070193149Sdougb		isc_mem_free(mctx, key->label);
1071143731Sdougb	dns_name_free(key->key_name, mctx);
1072143731Sdougb	isc_mem_put(mctx, key->key_name, sizeof(dns_name_t));
1073224092Sdougb	if (key->key_tkeytoken) {
1074224092Sdougb		isc_buffer_free(&key->key_tkeytoken);
1075224092Sdougb	}
1076143731Sdougb	memset(key, 0, sizeof(dst_key_t));
1077143731Sdougb	isc_mem_put(mctx, key, sizeof(dst_key_t));
1078143731Sdougb	*keyp = NULL;
1079143731Sdougb}
1080143731Sdougb
1081143731Sdougbisc_boolean_t
1082143731Sdougbdst_key_isprivate(const dst_key_t *key) {
1083143731Sdougb	REQUIRE(VALID_KEY(key));
1084143731Sdougb	INSIST(key->func->isprivate != NULL);
1085143731Sdougb	return (key->func->isprivate(key));
1086143731Sdougb}
1087143731Sdougb
1088143731Sdougbisc_result_t
1089143731Sdougbdst_key_buildfilename(const dst_key_t *key, int type,
1090143731Sdougb		      const char *directory, isc_buffer_t *out) {
1091143731Sdougb
1092143731Sdougb	REQUIRE(VALID_KEY(key));
1093143731Sdougb	REQUIRE(type == DST_TYPE_PRIVATE || type == DST_TYPE_PUBLIC ||
1094143731Sdougb		type == 0);
1095143731Sdougb
1096143731Sdougb	return (buildfilename(key->key_name, key->key_id, key->key_alg,
1097143731Sdougb			      type, directory, out));
1098143731Sdougb}
1099143731Sdougb
1100143731Sdougbisc_result_t
1101143731Sdougbdst_key_sigsize(const dst_key_t *key, unsigned int *n) {
1102143731Sdougb	REQUIRE(dst_initialized == ISC_TRUE);
1103143731Sdougb	REQUIRE(VALID_KEY(key));
1104143731Sdougb	REQUIRE(n != NULL);
1105143731Sdougb
1106143731Sdougb	/* XXXVIX this switch statement is too sparse to gen a jump table. */
1107143731Sdougb	switch (key->key_alg) {
1108143731Sdougb	case DST_ALG_RSAMD5:
1109143731Sdougb	case DST_ALG_RSASHA1:
1110193149Sdougb	case DST_ALG_NSEC3RSASHA1:
1111204619Sdougb	case DST_ALG_RSASHA256:
1112204619Sdougb	case DST_ALG_RSASHA512:
1113143731Sdougb		*n = (key->key_size + 7) / 8;
1114143731Sdougb		break;
1115143731Sdougb	case DST_ALG_DSA:
1116193149Sdougb	case DST_ALG_NSEC3DSA:
1117143731Sdougb		*n = DNS_SIG_DSASIGSIZE;
1118143731Sdougb		break;
1119224092Sdougb	case DST_ALG_ECCGOST:
1120224092Sdougb		*n = DNS_SIG_GOSTSIGSIZE;
1121224092Sdougb		break;
1122143731Sdougb	case DST_ALG_HMACMD5:
1123143731Sdougb		*n = 16;
1124143731Sdougb		break;
1125170222Sdougb	case DST_ALG_HMACSHA1:
1126170222Sdougb		*n = ISC_SHA1_DIGESTLENGTH;
1127170222Sdougb		break;
1128170222Sdougb	case DST_ALG_HMACSHA224:
1129170222Sdougb		*n = ISC_SHA224_DIGESTLENGTH;
1130170222Sdougb		break;
1131170222Sdougb	case DST_ALG_HMACSHA256:
1132170222Sdougb		*n = ISC_SHA256_DIGESTLENGTH;
1133170222Sdougb		break;
1134170222Sdougb	case DST_ALG_HMACSHA384:
1135170222Sdougb		*n = ISC_SHA384_DIGESTLENGTH;
1136170222Sdougb		break;
1137170222Sdougb	case DST_ALG_HMACSHA512:
1138170222Sdougb		*n = ISC_SHA512_DIGESTLENGTH;
1139170222Sdougb		break;
1140143731Sdougb	case DST_ALG_GSSAPI:
1141170222Sdougb		*n = 128; /*%< XXX */
1142143731Sdougb		break;
1143143731Sdougb	case DST_ALG_DH:
1144143731Sdougb	default:
1145143731Sdougb		return (DST_R_UNSUPPORTEDALG);
1146143731Sdougb	}
1147143731Sdougb	return (ISC_R_SUCCESS);
1148143731Sdougb}
1149143731Sdougb
1150143731Sdougbisc_result_t
1151143731Sdougbdst_key_secretsize(const dst_key_t *key, unsigned int *n) {
1152143731Sdougb	REQUIRE(dst_initialized == ISC_TRUE);
1153143731Sdougb	REQUIRE(VALID_KEY(key));
1154143731Sdougb	REQUIRE(n != NULL);
1155143731Sdougb
1156143731Sdougb	if (key->key_alg == DST_ALG_DH)
1157143731Sdougb		*n = (key->key_size + 7) / 8;
1158143731Sdougb	else
1159143731Sdougb		return (DST_R_UNSUPPORTEDALG);
1160143731Sdougb	return (ISC_R_SUCCESS);
1161143731Sdougb}
1162143731Sdougb
1163224092Sdougb/*%
1164224092Sdougb * Set the flags on a key, then recompute the key ID
1165224092Sdougb */
1166224092Sdougbisc_result_t
1167224092Sdougbdst_key_setflags(dst_key_t *key, isc_uint32_t flags) {
1168224092Sdougb	REQUIRE(VALID_KEY(key));
1169224092Sdougb	key->key_flags = flags;
1170224092Sdougb	return (computeid(key));
1171224092Sdougb}
1172224092Sdougb
1173224092Sdougbvoid
1174224092Sdougbdst_key_format(const dst_key_t *key, char *cp, unsigned int size) {
1175224092Sdougb	char namestr[DNS_NAME_FORMATSIZE];
1176224092Sdougb	char algstr[DNS_NAME_FORMATSIZE];
1177224092Sdougb
1178224092Sdougb	dns_name_format(dst_key_name(key), namestr, sizeof(namestr));
1179224092Sdougb	dns_secalg_format((dns_secalg_t) dst_key_alg(key), algstr,
1180224092Sdougb			  sizeof(algstr));
1181224092Sdougb	snprintf(cp, size, "%s/%s/%d", namestr, algstr, dst_key_id(key));
1182224092Sdougb}
1183224092Sdougb
1184224092Sdougbisc_result_t
1185224092Sdougbdst_key_dump(dst_key_t *key, isc_mem_t *mctx, char **buffer, int *length) {
1186224092Sdougb
1187224092Sdougb	REQUIRE(buffer != NULL && *buffer == NULL);
1188224092Sdougb	REQUIRE(length != NULL && *length == 0);
1189224092Sdougb	REQUIRE(VALID_KEY(key));
1190224092Sdougb
1191224092Sdougb	if (key->func->isprivate == NULL)
1192224092Sdougb		return (ISC_R_NOTIMPLEMENTED);
1193224092Sdougb	return (key->func->dump(key, mctx, buffer, length));
1194224092Sdougb}
1195224092Sdougb
1196224092Sdougbisc_result_t
1197224092Sdougbdst_key_restore(dns_name_t *name, unsigned int alg, unsigned int flags,
1198224092Sdougb		unsigned int protocol, dns_rdataclass_t rdclass,
1199224092Sdougb		isc_mem_t *mctx, const char *keystr, dst_key_t **keyp)
1200224092Sdougb{
1201224092Sdougb	isc_result_t result;
1202224092Sdougb	dst_key_t *key;
1203224092Sdougb
1204224092Sdougb	REQUIRE(dst_initialized == ISC_TRUE);
1205224092Sdougb	REQUIRE(keyp != NULL && *keyp == NULL);
1206224092Sdougb
1207224092Sdougb	if (alg >= DST_MAX_ALGS || dst_t_func[alg] == NULL)
1208224092Sdougb		return (DST_R_UNSUPPORTEDALG);
1209224092Sdougb
1210224092Sdougb	if (dst_t_func[alg]->restore == NULL)
1211224092Sdougb		return (ISC_R_NOTIMPLEMENTED);
1212224092Sdougb
1213224092Sdougb	key = get_key_struct(name, alg, flags, protocol, 0, rdclass, mctx);
1214224092Sdougb	if (key == NULL)
1215224092Sdougb		return (ISC_R_NOMEMORY);
1216224092Sdougb
1217224092Sdougb	result = (dst_t_func[alg]->restore)(key, keystr);
1218224092Sdougb	if (result == ISC_R_SUCCESS)
1219224092Sdougb		*keyp = key;
1220224092Sdougb	else
1221224092Sdougb		dst_key_free(&key);
1222224092Sdougb
1223224092Sdougb	return (result);
1224224092Sdougb}
1225224092Sdougb
1226143731Sdougb/***
1227143731Sdougb *** Static methods
1228143731Sdougb ***/
1229143731Sdougb
1230170222Sdougb/*%
1231143731Sdougb * Allocates a key structure and fills in some of the fields.
1232143731Sdougb */
1233143731Sdougbstatic dst_key_t *
1234143731Sdougbget_key_struct(dns_name_t *name, unsigned int alg,
1235143731Sdougb	       unsigned int flags, unsigned int protocol,
1236143731Sdougb	       unsigned int bits, dns_rdataclass_t rdclass,
1237143731Sdougb	       isc_mem_t *mctx)
1238143731Sdougb{
1239143731Sdougb	dst_key_t *key;
1240143731Sdougb	isc_result_t result;
1241224092Sdougb	int i;
1242143731Sdougb
1243143731Sdougb	key = (dst_key_t *) isc_mem_get(mctx, sizeof(dst_key_t));
1244143731Sdougb	if (key == NULL)
1245143731Sdougb		return (NULL);
1246143731Sdougb
1247143731Sdougb	memset(key, 0, sizeof(dst_key_t));
1248143731Sdougb	key->magic = KEY_MAGIC;
1249143731Sdougb
1250218384Sdougb	result = isc_refcount_init(&key->refs, 1);
1251218384Sdougb	if (result != ISC_R_SUCCESS) {
1252218384Sdougb		isc_mem_put(mctx, key, sizeof(dst_key_t));
1253218384Sdougb		return (NULL);
1254218384Sdougb	}
1255218384Sdougb
1256143731Sdougb	key->key_name = isc_mem_get(mctx, sizeof(dns_name_t));
1257143731Sdougb	if (key->key_name == NULL) {
1258218384Sdougb		isc_refcount_destroy(&key->refs);
1259143731Sdougb		isc_mem_put(mctx, key, sizeof(dst_key_t));
1260143731Sdougb		return (NULL);
1261143731Sdougb	}
1262143731Sdougb	dns_name_init(key->key_name, NULL);
1263143731Sdougb	result = dns_name_dup(name, mctx, key->key_name);
1264143731Sdougb	if (result != ISC_R_SUCCESS) {
1265218384Sdougb		isc_refcount_destroy(&key->refs);
1266143731Sdougb		isc_mem_put(mctx, key->key_name, sizeof(dns_name_t));
1267143731Sdougb		isc_mem_put(mctx, key, sizeof(dst_key_t));
1268143731Sdougb		return (NULL);
1269143731Sdougb	}
1270143731Sdougb	key->key_alg = alg;
1271143731Sdougb	key->key_flags = flags;
1272143731Sdougb	key->key_proto = protocol;
1273143731Sdougb	key->mctx = mctx;
1274193149Sdougb	key->keydata.generic = NULL;
1275143731Sdougb	key->key_size = bits;
1276143731Sdougb	key->key_class = rdclass;
1277143731Sdougb	key->func = dst_t_func[alg];
1278224092Sdougb	key->fmt_major = 0;
1279224092Sdougb	key->fmt_minor = 0;
1280224092Sdougb	for (i = 0; i < (DST_MAX_TIMES + 1); i++) {
1281224092Sdougb		key->times[i] = 0;
1282224092Sdougb		key->timeset[i] = ISC_FALSE;
1283224092Sdougb	}
1284143731Sdougb	return (key);
1285143731Sdougb}
1286143731Sdougb
1287170222Sdougb/*%
1288143731Sdougb * Reads a public key from disk
1289143731Sdougb */
1290170222Sdougbisc_result_t
1291170222Sdougbdst_key_read_public(const char *filename, int type,
1292170222Sdougb		    isc_mem_t *mctx, dst_key_t **keyp)
1293143731Sdougb{
1294143731Sdougb	u_char rdatabuf[DST_KEY_MAXSIZE];
1295143731Sdougb	isc_buffer_t b;
1296143731Sdougb	dns_fixedname_t name;
1297143731Sdougb	isc_lex_t *lex = NULL;
1298143731Sdougb	isc_token_t token;
1299143731Sdougb	isc_result_t ret;
1300143731Sdougb	dns_rdata_t rdata = DNS_RDATA_INIT;
1301143731Sdougb	unsigned int opt = ISC_LEXOPT_DNSMULTILINE;
1302143731Sdougb	dns_rdataclass_t rdclass = dns_rdataclass_in;
1303143731Sdougb	isc_lexspecials_t specials;
1304143731Sdougb	isc_uint32_t ttl;
1305143731Sdougb	isc_result_t result;
1306143731Sdougb	dns_rdatatype_t keytype;
1307143731Sdougb
1308143731Sdougb	/*
1309143731Sdougb	 * Open the file and read its formatted contents
1310143731Sdougb	 * File format:
1311170222Sdougb	 *    domain.name [ttl] [class] [KEY|DNSKEY] <flags> <protocol> <algorithm> <key>
1312143731Sdougb	 */
1313143731Sdougb
1314143731Sdougb	/* 1500 should be large enough for any key */
1315143731Sdougb	ret = isc_lex_create(mctx, 1500, &lex);
1316143731Sdougb	if (ret != ISC_R_SUCCESS)
1317143731Sdougb		goto cleanup;
1318143731Sdougb
1319143731Sdougb	memset(specials, 0, sizeof(specials));
1320143731Sdougb	specials['('] = 1;
1321143731Sdougb	specials[')'] = 1;
1322143731Sdougb	specials['"'] = 1;
1323143731Sdougb	isc_lex_setspecials(lex, specials);
1324143731Sdougb	isc_lex_setcomments(lex, ISC_LEXCOMMENT_DNSMASTERFILE);
1325143731Sdougb
1326170222Sdougb	ret = isc_lex_openfile(lex, filename);
1327143731Sdougb	if (ret != ISC_R_SUCCESS)
1328143731Sdougb		goto cleanup;
1329143731Sdougb
1330143731Sdougb#define NEXTTOKEN(lex, opt, token) { \
1331143731Sdougb	ret = isc_lex_gettoken(lex, opt, token); \
1332143731Sdougb	if (ret != ISC_R_SUCCESS) \
1333143731Sdougb		goto cleanup; \
1334143731Sdougb	}
1335143731Sdougb
1336143731Sdougb#define BADTOKEN() { \
1337143731Sdougb	ret = ISC_R_UNEXPECTEDTOKEN; \
1338143731Sdougb	goto cleanup; \
1339143731Sdougb	}
1340143731Sdougb
1341143731Sdougb	/* Read the domain name */
1342143731Sdougb	NEXTTOKEN(lex, opt, &token);
1343143731Sdougb	if (token.type != isc_tokentype_string)
1344143731Sdougb		BADTOKEN();
1345193149Sdougb
1346193149Sdougb	/*
1347193149Sdougb	 * We don't support "@" in .key files.
1348193149Sdougb	 */
1349193149Sdougb	if (!strcmp(DST_AS_STR(token), "@"))
1350193149Sdougb		BADTOKEN();
1351193149Sdougb
1352143731Sdougb	dns_fixedname_init(&name);
1353143731Sdougb	isc_buffer_init(&b, DST_AS_STR(token), strlen(DST_AS_STR(token)));
1354143731Sdougb	isc_buffer_add(&b, strlen(DST_AS_STR(token)));
1355143731Sdougb	ret = dns_name_fromtext(dns_fixedname_name(&name), &b, dns_rootname,
1356224092Sdougb				0, NULL);
1357143731Sdougb	if (ret != ISC_R_SUCCESS)
1358143731Sdougb		goto cleanup;
1359143731Sdougb
1360143731Sdougb	/* Read the next word: either TTL, class, or 'KEY' */
1361143731Sdougb	NEXTTOKEN(lex, opt, &token);
1362143731Sdougb
1363204619Sdougb	if (token.type != isc_tokentype_string)
1364204619Sdougb		BADTOKEN();
1365204619Sdougb
1366143731Sdougb	/* If it's a TTL, read the next one */
1367143731Sdougb	result = dns_ttl_fromtext(&token.value.as_textregion, &ttl);
1368143731Sdougb	if (result == ISC_R_SUCCESS)
1369143731Sdougb		NEXTTOKEN(lex, opt, &token);
1370143731Sdougb
1371143731Sdougb	if (token.type != isc_tokentype_string)
1372143731Sdougb		BADTOKEN();
1373143731Sdougb
1374143731Sdougb	ret = dns_rdataclass_fromtext(&rdclass, &token.value.as_textregion);
1375143731Sdougb	if (ret == ISC_R_SUCCESS)
1376143731Sdougb		NEXTTOKEN(lex, opt, &token);
1377143731Sdougb
1378143731Sdougb	if (token.type != isc_tokentype_string)
1379143731Sdougb		BADTOKEN();
1380143731Sdougb
1381143731Sdougb	if (strcasecmp(DST_AS_STR(token), "DNSKEY") == 0)
1382143731Sdougb		keytype = dns_rdatatype_dnskey;
1383143731Sdougb	else if (strcasecmp(DST_AS_STR(token), "KEY") == 0)
1384170222Sdougb		keytype = dns_rdatatype_key; /*%< SIG(0), TKEY */
1385143731Sdougb	else
1386143731Sdougb		BADTOKEN();
1387143731Sdougb
1388143731Sdougb	if (((type & DST_TYPE_KEY) != 0 && keytype != dns_rdatatype_key) ||
1389143731Sdougb	    ((type & DST_TYPE_KEY) == 0 && keytype != dns_rdatatype_dnskey)) {
1390143731Sdougb		ret = DST_R_BADKEYTYPE;
1391143731Sdougb		goto cleanup;
1392143731Sdougb	}
1393143731Sdougb
1394143731Sdougb	isc_buffer_init(&b, rdatabuf, sizeof(rdatabuf));
1395143731Sdougb	ret = dns_rdata_fromtext(&rdata, rdclass, keytype, lex, NULL,
1396143731Sdougb				 ISC_FALSE, mctx, &b, NULL);
1397143731Sdougb	if (ret != ISC_R_SUCCESS)
1398143731Sdougb		goto cleanup;
1399143731Sdougb
1400143731Sdougb	ret = dst_key_fromdns(dns_fixedname_name(&name), rdclass, &b, mctx,
1401143731Sdougb			      keyp);
1402143731Sdougb	if (ret != ISC_R_SUCCESS)
1403143731Sdougb		goto cleanup;
1404143731Sdougb
1405143731Sdougb cleanup:
1406143731Sdougb	if (lex != NULL)
1407143731Sdougb		isc_lex_destroy(&lex);
1408143731Sdougb	return (ret);
1409143731Sdougb}
1410143731Sdougb
1411143731Sdougbstatic isc_boolean_t
1412143731Sdougbissymmetric(const dst_key_t *key) {
1413143731Sdougb	REQUIRE(dst_initialized == ISC_TRUE);
1414143731Sdougb	REQUIRE(VALID_KEY(key));
1415143731Sdougb
1416143731Sdougb	/* XXXVIX this switch statement is too sparse to gen a jump table. */
1417143731Sdougb	switch (key->key_alg) {
1418143731Sdougb	case DST_ALG_RSAMD5:
1419143731Sdougb	case DST_ALG_RSASHA1:
1420193149Sdougb	case DST_ALG_NSEC3RSASHA1:
1421204619Sdougb	case DST_ALG_RSASHA256:
1422204619Sdougb	case DST_ALG_RSASHA512:
1423143731Sdougb	case DST_ALG_DSA:
1424193149Sdougb	case DST_ALG_NSEC3DSA:
1425143731Sdougb	case DST_ALG_DH:
1426224092Sdougb	case DST_ALG_ECCGOST:
1427143731Sdougb		return (ISC_FALSE);
1428143731Sdougb	case DST_ALG_HMACMD5:
1429143731Sdougb	case DST_ALG_GSSAPI:
1430143731Sdougb		return (ISC_TRUE);
1431143731Sdougb	default:
1432143731Sdougb		return (ISC_FALSE);
1433143731Sdougb	}
1434143731Sdougb}
1435143731Sdougb
1436170222Sdougb/*%
1437224092Sdougb * Write key timing metadata to a file pointer, preceded by 'tag'
1438224092Sdougb */
1439224092Sdougbstatic void
1440224092Sdougbprinttime(const dst_key_t *key, int type, const char *tag, FILE *stream) {
1441224092Sdougb	isc_result_t result;
1442224092Sdougb#ifdef ISC_PLATFORM_USETHREADS
1443224092Sdougb	char output[26]; /* Minimum buffer as per ctime_r() specification. */
1444224092Sdougb#else
1445224092Sdougb	const char *output;
1446224092Sdougb#endif
1447224092Sdougb	isc_stdtime_t when;
1448224092Sdougb	time_t t;
1449224092Sdougb	char utc[sizeof("YYYYMMDDHHSSMM")];
1450224092Sdougb	isc_buffer_t b;
1451224092Sdougb	isc_region_t r;
1452224092Sdougb
1453224092Sdougb	result = dst_key_gettime(key, type, &when);
1454224092Sdougb	if (result == ISC_R_NOTFOUND)
1455224092Sdougb		return;
1456224092Sdougb
1457224092Sdougb	/* time_t and isc_stdtime_t might be different sizes */
1458224092Sdougb	t = when;
1459224092Sdougb#ifdef ISC_PLATFORM_USETHREADS
1460224092Sdougb#ifdef WIN32
1461224092Sdougb	if (ctime_s(output, sizeof(output), &t) != 0)
1462224092Sdougb		goto error;
1463224092Sdougb#else
1464224092Sdougb	if (ctime_r(&t, output) == NULL)
1465224092Sdougb		goto error;
1466224092Sdougb#endif
1467224092Sdougb#else
1468224092Sdougb	output = ctime(&t);
1469224092Sdougb#endif
1470224092Sdougb
1471224092Sdougb	isc_buffer_init(&b, utc, sizeof(utc));
1472224092Sdougb	result = dns_time32_totext(when, &b);
1473224092Sdougb	if (result != ISC_R_SUCCESS)
1474224092Sdougb		goto error;
1475224092Sdougb
1476224092Sdougb	isc_buffer_usedregion(&b, &r);
1477224092Sdougb	fprintf(stream, "%s: %.*s (%.*s)\n", tag, (int)r.length, r.base,
1478224092Sdougb		 (int)strlen(output) - 1, output);
1479224092Sdougb	return;
1480224092Sdougb
1481224092Sdougb error:
1482224092Sdougb	fprintf(stream, "%s: (set, unable to display)\n", tag);
1483224092Sdougb}
1484224092Sdougb
1485224092Sdougb/*%
1486143731Sdougb * Writes a public key to disk in DNS format.
1487143731Sdougb */
1488143731Sdougbstatic isc_result_t
1489143731Sdougbwrite_public_key(const dst_key_t *key, int type, const char *directory) {
1490143731Sdougb	FILE *fp;
1491143731Sdougb	isc_buffer_t keyb, textb, fileb, classb;
1492143731Sdougb	isc_region_t r;
1493143731Sdougb	char filename[ISC_DIR_NAMEMAX];
1494143731Sdougb	unsigned char key_array[DST_KEY_MAXSIZE];
1495143731Sdougb	char text_array[DST_KEY_MAXTEXTSIZE];
1496143731Sdougb	char class_array[10];
1497143731Sdougb	isc_result_t ret;
1498143731Sdougb	dns_rdata_t rdata = DNS_RDATA_INIT;
1499143731Sdougb	isc_fsaccess_t access;
1500143731Sdougb
1501143731Sdougb	REQUIRE(VALID_KEY(key));
1502143731Sdougb
1503143731Sdougb	isc_buffer_init(&keyb, key_array, sizeof(key_array));
1504143731Sdougb	isc_buffer_init(&textb, text_array, sizeof(text_array));
1505143731Sdougb	isc_buffer_init(&classb, class_array, sizeof(class_array));
1506143731Sdougb
1507143731Sdougb	ret = dst_key_todns(key, &keyb);
1508143731Sdougb	if (ret != ISC_R_SUCCESS)
1509143731Sdougb		return (ret);
1510143731Sdougb
1511143731Sdougb	isc_buffer_usedregion(&keyb, &r);
1512143731Sdougb	dns_rdata_fromregion(&rdata, key->key_class, dns_rdatatype_dnskey, &r);
1513143731Sdougb
1514143731Sdougb	ret = dns_rdata_totext(&rdata, (dns_name_t *) NULL, &textb);
1515143731Sdougb	if (ret != ISC_R_SUCCESS)
1516143731Sdougb		return (DST_R_INVALIDPUBLICKEY);
1517143731Sdougb
1518143731Sdougb	ret = dns_rdataclass_totext(key->key_class, &classb);
1519143731Sdougb	if (ret != ISC_R_SUCCESS)
1520143731Sdougb		return (DST_R_INVALIDPUBLICKEY);
1521143731Sdougb
1522143731Sdougb	/*
1523143731Sdougb	 * Make the filename.
1524143731Sdougb	 */
1525143731Sdougb	isc_buffer_init(&fileb, filename, sizeof(filename));
1526143731Sdougb	ret = dst_key_buildfilename(key, DST_TYPE_PUBLIC, directory, &fileb);
1527143731Sdougb	if (ret != ISC_R_SUCCESS)
1528143731Sdougb		return (ret);
1529143731Sdougb
1530143731Sdougb	/*
1531143731Sdougb	 * Create public key file.
1532143731Sdougb	 */
1533143731Sdougb	if ((fp = fopen(filename, "w")) == NULL)
1534143731Sdougb		return (DST_R_WRITEERROR);
1535143731Sdougb
1536143731Sdougb	if (issymmetric(key)) {
1537143731Sdougb		access = 0;
1538143731Sdougb		isc_fsaccess_add(ISC_FSACCESS_OWNER,
1539143731Sdougb				 ISC_FSACCESS_READ | ISC_FSACCESS_WRITE,
1540143731Sdougb				 &access);
1541143731Sdougb		(void)isc_fsaccess_set(filename, access);
1542143731Sdougb	}
1543143731Sdougb
1544224092Sdougb	/* Write key information in comments */
1545224092Sdougb	if ((type & DST_TYPE_KEY) == 0) {
1546224092Sdougb		fprintf(fp, "; This is a %s%s-signing key, keyid %d, for ",
1547224092Sdougb			(key->key_flags & DNS_KEYFLAG_REVOKE) != 0 ?
1548224092Sdougb				"revoked " :
1549224092Sdougb				"",
1550224092Sdougb			(key->key_flags & DNS_KEYFLAG_KSK) != 0 ?
1551224092Sdougb				"key" :
1552224092Sdougb				"zone",
1553224092Sdougb			key->key_id);
1554224092Sdougb		ret = dns_name_print(key->key_name, fp);
1555224092Sdougb		if (ret != ISC_R_SUCCESS) {
1556224092Sdougb			fclose(fp);
1557224092Sdougb			return (ret);
1558224092Sdougb		}
1559224092Sdougb		fputc('\n', fp);
1560224092Sdougb
1561224092Sdougb		printtime(key, DST_TIME_CREATED, "; Created", fp);
1562224092Sdougb		printtime(key, DST_TIME_PUBLISH, "; Publish", fp);
1563224092Sdougb		printtime(key, DST_TIME_ACTIVATE, "; Activate", fp);
1564224092Sdougb		printtime(key, DST_TIME_REVOKE, "; Revoke", fp);
1565224092Sdougb		printtime(key, DST_TIME_INACTIVE, "; Inactive", fp);
1566224092Sdougb		printtime(key, DST_TIME_DELETE, "; Delete", fp);
1567165071Sdougb	}
1568143731Sdougb
1569224092Sdougb	/* Now print the actual key */
1570224092Sdougb	ret = dns_name_print(key->key_name, fp);
1571224092Sdougb
1572143731Sdougb	fprintf(fp, " ");
1573143731Sdougb
1574143731Sdougb	isc_buffer_usedregion(&classb, &r);
1575204619Sdougb	isc_util_fwrite(r.base, 1, r.length, fp);
1576143731Sdougb
1577143731Sdougb	if ((type & DST_TYPE_KEY) != 0)
1578143731Sdougb		fprintf(fp, " KEY ");
1579143731Sdougb	else
1580143731Sdougb		fprintf(fp, " DNSKEY ");
1581143731Sdougb
1582143731Sdougb	isc_buffer_usedregion(&textb, &r);
1583204619Sdougb	isc_util_fwrite(r.base, 1, r.length, fp);
1584143731Sdougb
1585143731Sdougb	fputc('\n', fp);
1586193149Sdougb	fflush(fp);
1587193149Sdougb	if (ferror(fp))
1588193149Sdougb		ret = DST_R_WRITEERROR;
1589143731Sdougb	fclose(fp);
1590143731Sdougb
1591193149Sdougb	return (ret);
1592143731Sdougb}
1593143731Sdougb
1594143731Sdougbstatic isc_result_t
1595143731Sdougbbuildfilename(dns_name_t *name, dns_keytag_t id,
1596143731Sdougb	      unsigned int alg, unsigned int type,
1597143731Sdougb	      const char *directory, isc_buffer_t *out)
1598143731Sdougb{
1599143731Sdougb	const char *suffix = "";
1600143731Sdougb	unsigned int len;
1601143731Sdougb	isc_result_t result;
1602143731Sdougb
1603143731Sdougb	REQUIRE(out != NULL);
1604143731Sdougb	if ((type & DST_TYPE_PRIVATE) != 0)
1605143731Sdougb		suffix = ".private";
1606143731Sdougb	else if (type == DST_TYPE_PUBLIC)
1607143731Sdougb		suffix = ".key";
1608143731Sdougb	if (directory != NULL) {
1609143731Sdougb		if (isc_buffer_availablelength(out) < strlen(directory))
1610143731Sdougb			return (ISC_R_NOSPACE);
1611143731Sdougb		isc_buffer_putstr(out, directory);
1612143731Sdougb		if (strlen(directory) > 0U &&
1613143731Sdougb		    directory[strlen(directory) - 1] != '/')
1614143731Sdougb			isc_buffer_putstr(out, "/");
1615143731Sdougb	}
1616143731Sdougb	if (isc_buffer_availablelength(out) < 1)
1617143731Sdougb		return (ISC_R_NOSPACE);
1618143731Sdougb	isc_buffer_putstr(out, "K");
1619143731Sdougb	result = dns_name_tofilenametext(name, ISC_FALSE, out);
1620143731Sdougb	if (result != ISC_R_SUCCESS)
1621143731Sdougb		return (result);
1622143731Sdougb	len = 1 + 3 + 1 + 5 + strlen(suffix) + 1;
1623143731Sdougb	if (isc_buffer_availablelength(out) < len)
1624143731Sdougb		return (ISC_R_NOSPACE);
1625193149Sdougb	sprintf((char *) isc_buffer_used(out), "+%03d+%05d%s", alg, id,
1626193149Sdougb		suffix);
1627143731Sdougb	isc_buffer_add(out, len);
1628193149Sdougb
1629143731Sdougb	return (ISC_R_SUCCESS);
1630143731Sdougb}
1631143731Sdougb
1632143731Sdougbstatic isc_result_t
1633143731Sdougbcomputeid(dst_key_t *key) {
1634143731Sdougb	isc_buffer_t dnsbuf;
1635143731Sdougb	unsigned char dns_array[DST_KEY_MAXSIZE];
1636143731Sdougb	isc_region_t r;
1637143731Sdougb	isc_result_t ret;
1638143731Sdougb
1639143731Sdougb	isc_buffer_init(&dnsbuf, dns_array, sizeof(dns_array));
1640143731Sdougb	ret = dst_key_todns(key, &dnsbuf);
1641143731Sdougb	if (ret != ISC_R_SUCCESS)
1642143731Sdougb		return (ret);
1643143731Sdougb
1644143731Sdougb	isc_buffer_usedregion(&dnsbuf, &r);
1645143731Sdougb	key->key_id = dst_region_computeid(&r, key->key_alg);
1646143731Sdougb	return (ISC_R_SUCCESS);
1647143731Sdougb}
1648143731Sdougb
1649143731Sdougbstatic isc_result_t
1650143731Sdougbfrombuffer(dns_name_t *name, unsigned int alg, unsigned int flags,
1651143731Sdougb	   unsigned int protocol, dns_rdataclass_t rdclass,
1652143731Sdougb	   isc_buffer_t *source, isc_mem_t *mctx, dst_key_t **keyp)
1653143731Sdougb{
1654143731Sdougb	dst_key_t *key;
1655143731Sdougb	isc_result_t ret;
1656143731Sdougb
1657143731Sdougb	REQUIRE(dns_name_isabsolute(name));
1658143731Sdougb	REQUIRE(source != NULL);
1659143731Sdougb	REQUIRE(mctx != NULL);
1660143731Sdougb	REQUIRE(keyp != NULL && *keyp == NULL);
1661143731Sdougb
1662143731Sdougb	key = get_key_struct(name, alg, flags, protocol, 0, rdclass, mctx);
1663143731Sdougb	if (key == NULL)
1664143731Sdougb		return (ISC_R_NOMEMORY);
1665143731Sdougb
1666143731Sdougb	if (isc_buffer_remaininglength(source) > 0) {
1667143731Sdougb		ret = algorithm_status(alg);
1668143731Sdougb		if (ret != ISC_R_SUCCESS) {
1669143731Sdougb			dst_key_free(&key);
1670143731Sdougb			return (ret);
1671143731Sdougb		}
1672143731Sdougb		if (key->func->fromdns == NULL) {
1673143731Sdougb			dst_key_free(&key);
1674143731Sdougb			return (DST_R_UNSUPPORTEDALG);
1675143731Sdougb		}
1676143731Sdougb
1677143731Sdougb		ret = key->func->fromdns(key, source);
1678143731Sdougb		if (ret != ISC_R_SUCCESS) {
1679143731Sdougb			dst_key_free(&key);
1680143731Sdougb			return (ret);
1681143731Sdougb		}
1682143731Sdougb	}
1683143731Sdougb
1684143731Sdougb	*keyp = key;
1685143731Sdougb	return (ISC_R_SUCCESS);
1686143731Sdougb}
1687143731Sdougb
1688143731Sdougbstatic isc_result_t
1689143731Sdougbalgorithm_status(unsigned int alg) {
1690143731Sdougb	REQUIRE(dst_initialized == ISC_TRUE);
1691143731Sdougb
1692143731Sdougb	if (dst_algorithm_supported(alg))
1693143731Sdougb		return (ISC_R_SUCCESS);
1694143731Sdougb#ifndef OPENSSL
1695143731Sdougb	if (alg == DST_ALG_RSAMD5 || alg == DST_ALG_RSASHA1 ||
1696143731Sdougb	    alg == DST_ALG_DSA || alg == DST_ALG_DH ||
1697193149Sdougb	    alg == DST_ALG_HMACMD5 || alg == DST_ALG_NSEC3DSA ||
1698204619Sdougb	    alg == DST_ALG_NSEC3RSASHA1 ||
1699224092Sdougb	    alg == DST_ALG_RSASHA256 || alg == DST_ALG_RSASHA512 ||
1700224092Sdougb	    alg == DST_ALG_ECCGOST)
1701143731Sdougb		return (DST_R_NOCRYPTO);
1702143731Sdougb#endif
1703143731Sdougb	return (DST_R_UNSUPPORTEDALG);
1704143731Sdougb}
1705143731Sdougb
1706143731Sdougbstatic isc_result_t
1707224092Sdougbaddsuffix(char *filename, int len, const char *odirname,
1708224092Sdougb	  const char *ofilename, const char *suffix)
1709143731Sdougb{
1710143731Sdougb	int olen = strlen(ofilename);
1711143731Sdougb	int n;
1712143731Sdougb
1713143731Sdougb	if (olen > 1 && ofilename[olen - 1] == '.')
1714143731Sdougb		olen -= 1;
1715143731Sdougb	else if (olen > 8 && strcmp(ofilename + olen - 8, ".private") == 0)
1716143731Sdougb		olen -= 8;
1717143731Sdougb	else if (olen > 4 && strcmp(ofilename + olen - 4, ".key") == 0)
1718143731Sdougb		olen -= 4;
1719143731Sdougb
1720224092Sdougb	if (odirname == NULL)
1721224092Sdougb		n = snprintf(filename, len, "%.*s%s", olen, ofilename, suffix);
1722224092Sdougb	else
1723224092Sdougb		n = snprintf(filename, len, "%s/%.*s%s",
1724224092Sdougb			     odirname, olen, ofilename, suffix);
1725143731Sdougb	if (n < 0)
1726204619Sdougb		return (ISC_R_FAILURE);
1727224092Sdougb	if (n >= len)
1728143731Sdougb		return (ISC_R_NOSPACE);
1729143731Sdougb	return (ISC_R_SUCCESS);
1730143731Sdougb}
1731143731Sdougb
1732143731Sdougbisc_result_t
1733143731Sdougbdst__entropy_getdata(void *buf, unsigned int len, isc_boolean_t pseudo) {
1734224092Sdougb#ifdef BIND9
1735143731Sdougb	unsigned int flags = dst_entropy_flags;
1736204619Sdougb
1737204619Sdougb	if (len == 0)
1738204619Sdougb		return (ISC_R_SUCCESS);
1739143731Sdougb	if (pseudo)
1740143731Sdougb		flags &= ~ISC_ENTROPY_GOODONLY;
1741224092Sdougb	else
1742224092Sdougb		flags |= ISC_ENTROPY_BLOCKING;
1743143731Sdougb	return (isc_entropy_getdata(dst_entropy_pool, buf, len, NULL, flags));
1744224092Sdougb#else
1745224092Sdougb	UNUSED(buf);
1746224092Sdougb	UNUSED(len);
1747224092Sdougb	UNUSED(pseudo);
1748224092Sdougb
1749224092Sdougb	return (ISC_R_NOTIMPLEMENTED);
1750224092Sdougb#endif
1751143731Sdougb}
1752193149Sdougb
1753193149Sdougbunsigned int
1754193149Sdougbdst__entropy_status(void) {
1755224092Sdougb#ifdef BIND9
1756204619Sdougb#ifdef GSSAPI
1757204619Sdougb	unsigned int flags = dst_entropy_flags;
1758204619Sdougb	isc_result_t ret;
1759204619Sdougb	unsigned char buf[32];
1760204619Sdougb	static isc_boolean_t first = ISC_TRUE;
1761204619Sdougb
1762204619Sdougb	if (first) {
1763204619Sdougb		/* Someone believes RAND_status() initializes the PRNG */
1764204619Sdougb		flags &= ~ISC_ENTROPY_GOODONLY;
1765204619Sdougb		ret = isc_entropy_getdata(dst_entropy_pool, buf,
1766204619Sdougb					  sizeof(buf), NULL, flags);
1767204619Sdougb		INSIST(ret == ISC_R_SUCCESS);
1768204619Sdougb		isc_entropy_putdata(dst_entropy_pool, buf,
1769204619Sdougb				    sizeof(buf), 2 * sizeof(buf));
1770204619Sdougb		first = ISC_FALSE;
1771204619Sdougb	}
1772204619Sdougb#endif
1773193149Sdougb	return (isc_entropy_status(dst_entropy_pool));
1774224092Sdougb#else
1775224092Sdougb	return (0);
1776224092Sdougb#endif
1777193149Sdougb}
1778224092Sdougb
1779224092Sdougbisc_buffer_t *
1780224092Sdougbdst_key_tkeytoken(const dst_key_t *key) {
1781224092Sdougb	return (key->key_tkeytoken);
1782224092Sdougb}
1783