dst_api.c revision 234010
1143731Sdougb/*
2234010Sdougb * Portions Copyright (C) 2004-2012  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
34234010Sdougb * $Id$
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
451143731Sdougb	*keyp = key;
452143731Sdougb	return (ISC_R_SUCCESS);
453143731Sdougb}
454143731Sdougb
455143731Sdougbisc_result_t
456224092Sdougbdst_key_fromnamedfile(const char *filename, const char *dirname,
457224092Sdougb		      int type, isc_mem_t *mctx, dst_key_t **keyp)
458143731Sdougb{
459143731Sdougb	isc_result_t result;
460143731Sdougb	dst_key_t *pubkey = NULL, *key = NULL;
461143731Sdougb	char *newfilename = NULL;
462143731Sdougb	int newfilenamelen = 0;
463143731Sdougb	isc_lex_t *lex = NULL;
464143731Sdougb
465143731Sdougb	REQUIRE(dst_initialized == ISC_TRUE);
466143731Sdougb	REQUIRE(filename != NULL);
467143731Sdougb	REQUIRE((type & (DST_TYPE_PRIVATE | DST_TYPE_PUBLIC)) != 0);
468143731Sdougb	REQUIRE(mctx != NULL);
469143731Sdougb	REQUIRE(keyp != NULL && *keyp == NULL);
470143731Sdougb
471224092Sdougb	/* If an absolute path is specified, don't use the key directory */
472224092Sdougb#ifndef WIN32
473224092Sdougb	if (filename[0] == '/')
474224092Sdougb		dirname = NULL;
475224092Sdougb#else /* WIN32 */
476224092Sdougb	if (filename[0] == '/' || filename[0] == '\\')
477224092Sdougb		dirname = NULL;
478224092Sdougb#endif
479224092Sdougb
480170222Sdougb	newfilenamelen = strlen(filename) + 5;
481224092Sdougb	if (dirname != NULL)
482224092Sdougb		newfilenamelen += strlen(dirname) + 1;
483170222Sdougb	newfilename = isc_mem_get(mctx, newfilenamelen);
484170222Sdougb	if (newfilename == NULL)
485170222Sdougb		return (ISC_R_NOMEMORY);
486224092Sdougb	result = addsuffix(newfilename, newfilenamelen,
487224092Sdougb			   dirname, filename, ".key");
488170222Sdougb	INSIST(result == ISC_R_SUCCESS);
489170222Sdougb
490170222Sdougb	result = dst_key_read_public(newfilename, type, mctx, &pubkey);
491170222Sdougb	isc_mem_put(mctx, newfilename, newfilenamelen);
492170222Sdougb	newfilename = NULL;
493143731Sdougb	if (result != ISC_R_SUCCESS)
494143731Sdougb		return (result);
495143731Sdougb
496143731Sdougb	if ((type & (DST_TYPE_PRIVATE | DST_TYPE_PUBLIC)) == DST_TYPE_PUBLIC ||
497193149Sdougb	    (pubkey->key_flags & DNS_KEYFLAG_TYPEMASK) == DNS_KEYTYPE_NOKEY) {
498143731Sdougb		result = computeid(pubkey);
499143731Sdougb		if (result != ISC_R_SUCCESS) {
500143731Sdougb			dst_key_free(&pubkey);
501143731Sdougb			return (result);
502143731Sdougb		}
503143731Sdougb
504143731Sdougb		*keyp = pubkey;
505143731Sdougb		return (ISC_R_SUCCESS);
506143731Sdougb	}
507143731Sdougb
508143731Sdougb	result = algorithm_status(pubkey->key_alg);
509143731Sdougb	if (result != ISC_R_SUCCESS) {
510143731Sdougb		dst_key_free(&pubkey);
511143731Sdougb		return (result);
512143731Sdougb	}
513143731Sdougb
514143731Sdougb	key = get_key_struct(pubkey->key_name, pubkey->key_alg,
515143731Sdougb			     pubkey->key_flags, pubkey->key_proto, 0,
516143731Sdougb			     pubkey->key_class, mctx);
517224092Sdougb	if (key == NULL) {
518224092Sdougb		dst_key_free(&pubkey);
519143731Sdougb		return (ISC_R_NOMEMORY);
520224092Sdougb	}
521143731Sdougb
522143731Sdougb	if (key->func->parse == NULL)
523143731Sdougb		RETERR(DST_R_UNSUPPORTEDALG);
524143731Sdougb
525143731Sdougb	newfilenamelen = strlen(filename) + 9;
526224092Sdougb	if (dirname != NULL)
527224092Sdougb		newfilenamelen += strlen(dirname) + 1;
528143731Sdougb	newfilename = isc_mem_get(mctx, newfilenamelen);
529143731Sdougb	if (newfilename == NULL)
530143731Sdougb		RETERR(ISC_R_NOMEMORY);
531224092Sdougb	result = addsuffix(newfilename, newfilenamelen,
532224092Sdougb			   dirname, filename, ".private");
533143731Sdougb	INSIST(result == ISC_R_SUCCESS);
534143731Sdougb
535143731Sdougb	RETERR(isc_lex_create(mctx, 1500, &lex));
536143731Sdougb	RETERR(isc_lex_openfile(lex, newfilename));
537143731Sdougb	isc_mem_put(mctx, newfilename, newfilenamelen);
538143731Sdougb
539224092Sdougb	RETERR(key->func->parse(key, lex, pubkey));
540143731Sdougb	isc_lex_destroy(&lex);
541143731Sdougb
542143731Sdougb	RETERR(computeid(key));
543143731Sdougb
544224092Sdougb	if (pubkey->key_id != key->key_id)
545143731Sdougb		RETERR(DST_R_INVALIDPRIVATEKEY);
546224092Sdougb	dst_key_free(&pubkey);
547143731Sdougb
548143731Sdougb	*keyp = key;
549143731Sdougb	return (ISC_R_SUCCESS);
550218384Sdougb
551143731Sdougb out:
552224092Sdougb	if (pubkey != NULL)
553224092Sdougb		dst_key_free(&pubkey);
554143731Sdougb	if (newfilename != NULL)
555143731Sdougb		isc_mem_put(mctx, newfilename, newfilenamelen);
556143731Sdougb	if (lex != NULL)
557143731Sdougb		isc_lex_destroy(&lex);
558143731Sdougb	dst_key_free(&key);
559143731Sdougb	return (result);
560143731Sdougb}
561143731Sdougb
562143731Sdougbisc_result_t
563143731Sdougbdst_key_todns(const dst_key_t *key, isc_buffer_t *target) {
564143731Sdougb	REQUIRE(dst_initialized == ISC_TRUE);
565143731Sdougb	REQUIRE(VALID_KEY(key));
566143731Sdougb	REQUIRE(target != NULL);
567143731Sdougb
568143731Sdougb	CHECKALG(key->key_alg);
569143731Sdougb
570143731Sdougb	if (key->func->todns == NULL)
571143731Sdougb		return (DST_R_UNSUPPORTEDALG);
572143731Sdougb
573143731Sdougb	if (isc_buffer_availablelength(target) < 4)
574143731Sdougb		return (ISC_R_NOSPACE);
575143731Sdougb	isc_buffer_putuint16(target, (isc_uint16_t)(key->key_flags & 0xffff));
576143731Sdougb	isc_buffer_putuint8(target, (isc_uint8_t)key->key_proto);
577143731Sdougb	isc_buffer_putuint8(target, (isc_uint8_t)key->key_alg);
578143731Sdougb
579143731Sdougb	if (key->key_flags & DNS_KEYFLAG_EXTENDED) {
580143731Sdougb		if (isc_buffer_availablelength(target) < 2)
581143731Sdougb			return (ISC_R_NOSPACE);
582143731Sdougb		isc_buffer_putuint16(target,
583143731Sdougb				     (isc_uint16_t)((key->key_flags >> 16)
584143731Sdougb						    & 0xffff));
585143731Sdougb	}
586143731Sdougb
587193149Sdougb	if (key->keydata.generic == NULL) /*%< NULL KEY */
588143731Sdougb		return (ISC_R_SUCCESS);
589143731Sdougb
590143731Sdougb	return (key->func->todns(key, target));
591143731Sdougb}
592143731Sdougb
593143731Sdougbisc_result_t
594143731Sdougbdst_key_fromdns(dns_name_t *name, dns_rdataclass_t rdclass,
595143731Sdougb		isc_buffer_t *source, isc_mem_t *mctx, dst_key_t **keyp)
596143731Sdougb{
597143731Sdougb	isc_uint8_t alg, proto;
598143731Sdougb	isc_uint32_t flags, extflags;
599143731Sdougb	dst_key_t *key = NULL;
600234010Sdougb	dns_keytag_t id, rid;
601143731Sdougb	isc_region_t r;
602143731Sdougb	isc_result_t result;
603143731Sdougb
604143731Sdougb	REQUIRE(dst_initialized);
605143731Sdougb
606143731Sdougb	isc_buffer_remainingregion(source, &r);
607143731Sdougb
608143731Sdougb	if (isc_buffer_remaininglength(source) < 4)
609143731Sdougb		return (DST_R_INVALIDPUBLICKEY);
610143731Sdougb	flags = isc_buffer_getuint16(source);
611143731Sdougb	proto = isc_buffer_getuint8(source);
612143731Sdougb	alg = isc_buffer_getuint8(source);
613143731Sdougb
614143731Sdougb	id = dst_region_computeid(&r, alg);
615234010Sdougb	rid = dst_region_computerid(&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;
629234010Sdougb	key->key_rid = rid;
630143731Sdougb
631143731Sdougb	*keyp = key;
632143731Sdougb	return (ISC_R_SUCCESS);
633143731Sdougb}
634143731Sdougb
635143731Sdougbisc_result_t
636143731Sdougbdst_key_frombuffer(dns_name_t *name, unsigned int alg,
637143731Sdougb		   unsigned int flags, unsigned int protocol,
638143731Sdougb		   dns_rdataclass_t rdclass,
639143731Sdougb		   isc_buffer_t *source, isc_mem_t *mctx, dst_key_t **keyp)
640143731Sdougb{
641143731Sdougb	dst_key_t *key = NULL;
642143731Sdougb	isc_result_t result;
643143731Sdougb
644143731Sdougb	REQUIRE(dst_initialized);
645143731Sdougb
646143731Sdougb	result = frombuffer(name, alg, flags, protocol, rdclass, source,
647143731Sdougb			    mctx, &key);
648143731Sdougb	if (result != ISC_R_SUCCESS)
649143731Sdougb		return (result);
650143731Sdougb
651143731Sdougb	result = computeid(key);
652143731Sdougb	if (result != ISC_R_SUCCESS) {
653143731Sdougb		dst_key_free(&key);
654143731Sdougb		return (result);
655143731Sdougb	}
656143731Sdougb
657143731Sdougb	*keyp = key;
658143731Sdougb	return (ISC_R_SUCCESS);
659143731Sdougb}
660143731Sdougb
661143731Sdougbisc_result_t
662143731Sdougbdst_key_tobuffer(const dst_key_t *key, isc_buffer_t *target) {
663143731Sdougb	REQUIRE(dst_initialized == ISC_TRUE);
664143731Sdougb	REQUIRE(VALID_KEY(key));
665143731Sdougb	REQUIRE(target != NULL);
666143731Sdougb
667143731Sdougb	CHECKALG(key->key_alg);
668143731Sdougb
669143731Sdougb	if (key->func->todns == NULL)
670143731Sdougb		return (DST_R_UNSUPPORTEDALG);
671143731Sdougb
672143731Sdougb	return (key->func->todns(key, target));
673143731Sdougb}
674143731Sdougb
675143731Sdougbisc_result_t
676143731Sdougbdst_key_privatefrombuffer(dst_key_t *key, isc_buffer_t *buffer) {
677143731Sdougb	isc_lex_t *lex = NULL;
678143731Sdougb	isc_result_t result = ISC_R_SUCCESS;
679143731Sdougb
680143731Sdougb	REQUIRE(dst_initialized == ISC_TRUE);
681143731Sdougb	REQUIRE(VALID_KEY(key));
682143731Sdougb	REQUIRE(!dst_key_isprivate(key));
683143731Sdougb	REQUIRE(buffer != NULL);
684143731Sdougb
685143731Sdougb	if (key->func->parse == NULL)
686143731Sdougb		RETERR(DST_R_UNSUPPORTEDALG);
687143731Sdougb
688143731Sdougb	RETERR(isc_lex_create(key->mctx, 1500, &lex));
689143731Sdougb	RETERR(isc_lex_openbuffer(lex, buffer));
690224092Sdougb	RETERR(key->func->parse(key, lex, NULL));
691143731Sdougb out:
692143731Sdougb	if (lex != NULL)
693143731Sdougb		isc_lex_destroy(&lex);
694143731Sdougb	return (result);
695143731Sdougb}
696143731Sdougb
697193149Sdougbgss_ctx_id_t
698193149Sdougbdst_key_getgssctx(const dst_key_t *key)
699193149Sdougb{
700193149Sdougb	REQUIRE(key != NULL);
701193149Sdougb
702193149Sdougb	return (key->keydata.gssctx);
703193149Sdougb}
704193149Sdougb
705143731Sdougbisc_result_t
706193149Sdougbdst_key_fromgssapi(dns_name_t *name, gss_ctx_id_t gssctx, isc_mem_t *mctx,
707224092Sdougb		   dst_key_t **keyp, isc_region_t *intoken)
708143731Sdougb{
709143731Sdougb	dst_key_t *key;
710224092Sdougb	isc_result_t result;
711143731Sdougb
712193149Sdougb	REQUIRE(gssctx != NULL);
713143731Sdougb	REQUIRE(keyp != NULL && *keyp == NULL);
714143731Sdougb
715143731Sdougb	key = get_key_struct(name, DST_ALG_GSSAPI, 0, DNS_KEYPROTO_DNSSEC,
716143731Sdougb			     0, dns_rdataclass_in, mctx);
717143731Sdougb	if (key == NULL)
718143731Sdougb		return (ISC_R_NOMEMORY);
719193149Sdougb
720224092Sdougb	if (intoken != NULL) {
721224092Sdougb		/*
722224092Sdougb		 * Keep the token for use by external ssu rules. They may need
723224092Sdougb		 * to examine the PAC in the kerberos ticket.
724224092Sdougb		 */
725224092Sdougb		RETERR(isc_buffer_allocate(key->mctx, &key->key_tkeytoken,
726224092Sdougb		       intoken->length));
727224092Sdougb		RETERR(isc_buffer_copyregion(key->key_tkeytoken, intoken));
728224092Sdougb	}
729224092Sdougb
730193149Sdougb	key->keydata.gssctx = gssctx;
731143731Sdougb	*keyp = key;
732224092Sdougb	result = ISC_R_SUCCESS;
733224092Sdougbout:
734224092Sdougb	return result;
735143731Sdougb}
736143731Sdougb
737143731Sdougbisc_result_t
738193149Sdougbdst_key_fromlabel(dns_name_t *name, int alg, unsigned int flags,
739193149Sdougb		  unsigned int protocol, dns_rdataclass_t rdclass,
740193149Sdougb		  const char *engine, const char *label, const char *pin,
741193149Sdougb		  isc_mem_t *mctx, dst_key_t **keyp)
742193149Sdougb{
743193149Sdougb	dst_key_t *key;
744193149Sdougb	isc_result_t result;
745193149Sdougb
746193149Sdougb	REQUIRE(dst_initialized == ISC_TRUE);
747193149Sdougb	REQUIRE(dns_name_isabsolute(name));
748193149Sdougb	REQUIRE(mctx != NULL);
749193149Sdougb	REQUIRE(keyp != NULL && *keyp == NULL);
750193149Sdougb	REQUIRE(label != NULL);
751193149Sdougb
752193149Sdougb	CHECKALG(alg);
753193149Sdougb
754193149Sdougb	key = get_key_struct(name, alg, flags, protocol, 0, rdclass, mctx);
755193149Sdougb	if (key == NULL)
756193149Sdougb		return (ISC_R_NOMEMORY);
757193149Sdougb
758193149Sdougb	if (key->func->fromlabel == NULL) {
759193149Sdougb		dst_key_free(&key);
760193149Sdougb		return (DST_R_UNSUPPORTEDALG);
761193149Sdougb	}
762193149Sdougb
763193149Sdougb	result = key->func->fromlabel(key, engine, label, pin);
764193149Sdougb	if (result != ISC_R_SUCCESS) {
765193149Sdougb		dst_key_free(&key);
766193149Sdougb		return (result);
767193149Sdougb	}
768193149Sdougb
769193149Sdougb	result = computeid(key);
770193149Sdougb	if (result != ISC_R_SUCCESS) {
771193149Sdougb		dst_key_free(&key);
772193149Sdougb		return (result);
773193149Sdougb	}
774193149Sdougb
775193149Sdougb	*keyp = key;
776193149Sdougb	return (ISC_R_SUCCESS);
777193149Sdougb}
778193149Sdougb
779193149Sdougbisc_result_t
780143731Sdougbdst_key_generate(dns_name_t *name, unsigned int alg,
781143731Sdougb		 unsigned int bits, unsigned int param,
782143731Sdougb		 unsigned int flags, unsigned int protocol,
783143731Sdougb		 dns_rdataclass_t rdclass,
784143731Sdougb		 isc_mem_t *mctx, dst_key_t **keyp)
785143731Sdougb{
786224092Sdougb	return (dst_key_generate2(name, alg, bits, param, flags, protocol,
787224092Sdougb				  rdclass, mctx, keyp, NULL));
788224092Sdougb}
789224092Sdougb
790224092Sdougbisc_result_t
791224092Sdougbdst_key_generate2(dns_name_t *name, unsigned int alg,
792224092Sdougb		  unsigned int bits, unsigned int param,
793224092Sdougb		  unsigned int flags, unsigned int protocol,
794224092Sdougb		  dns_rdataclass_t rdclass,
795224092Sdougb		  isc_mem_t *mctx, dst_key_t **keyp,
796224092Sdougb		  void (*callback)(int))
797224092Sdougb{
798143731Sdougb	dst_key_t *key;
799143731Sdougb	isc_result_t ret;
800143731Sdougb
801143731Sdougb	REQUIRE(dst_initialized == ISC_TRUE);
802143731Sdougb	REQUIRE(dns_name_isabsolute(name));
803143731Sdougb	REQUIRE(mctx != NULL);
804143731Sdougb	REQUIRE(keyp != NULL && *keyp == NULL);
805143731Sdougb
806143731Sdougb	CHECKALG(alg);
807143731Sdougb
808143731Sdougb	key = get_key_struct(name, alg, flags, protocol, bits, rdclass, mctx);
809143731Sdougb	if (key == NULL)
810143731Sdougb		return (ISC_R_NOMEMORY);
811143731Sdougb
812170222Sdougb	if (bits == 0) { /*%< NULL KEY */
813143731Sdougb		key->key_flags |= DNS_KEYTYPE_NOKEY;
814143731Sdougb		*keyp = key;
815143731Sdougb		return (ISC_R_SUCCESS);
816143731Sdougb	}
817143731Sdougb
818143731Sdougb	if (key->func->generate == NULL) {
819143731Sdougb		dst_key_free(&key);
820143731Sdougb		return (DST_R_UNSUPPORTEDALG);
821143731Sdougb	}
822143731Sdougb
823224092Sdougb	ret = key->func->generate(key, param, callback);
824143731Sdougb	if (ret != ISC_R_SUCCESS) {
825143731Sdougb		dst_key_free(&key);
826143731Sdougb		return (ret);
827143731Sdougb	}
828143731Sdougb
829143731Sdougb	ret = computeid(key);
830143731Sdougb	if (ret != ISC_R_SUCCESS) {
831143731Sdougb		dst_key_free(&key);
832143731Sdougb		return (ret);
833143731Sdougb	}
834143731Sdougb
835143731Sdougb	*keyp = key;
836143731Sdougb	return (ISC_R_SUCCESS);
837143731Sdougb}
838143731Sdougb
839224092Sdougbisc_result_t
840224092Sdougbdst_key_getnum(const dst_key_t *key, int type, isc_uint32_t *valuep)
841224092Sdougb{
842224092Sdougb	REQUIRE(VALID_KEY(key));
843224092Sdougb	REQUIRE(valuep != NULL);
844224092Sdougb	REQUIRE(type <= DST_MAX_NUMERIC);
845224092Sdougb	if (!key->numset[type])
846224092Sdougb		return (ISC_R_NOTFOUND);
847224092Sdougb	*valuep = key->nums[type];
848224092Sdougb	return (ISC_R_SUCCESS);
849224092Sdougb}
850224092Sdougb
851224092Sdougbvoid
852224092Sdougbdst_key_setnum(dst_key_t *key, int type, isc_uint32_t value)
853224092Sdougb{
854224092Sdougb	REQUIRE(VALID_KEY(key));
855224092Sdougb	REQUIRE(type <= DST_MAX_NUMERIC);
856224092Sdougb	key->nums[type] = value;
857224092Sdougb	key->numset[type] = ISC_TRUE;
858224092Sdougb}
859224092Sdougb
860224092Sdougbvoid
861224092Sdougbdst_key_unsetnum(dst_key_t *key, int type)
862224092Sdougb{
863224092Sdougb	REQUIRE(VALID_KEY(key));
864224092Sdougb	REQUIRE(type <= DST_MAX_NUMERIC);
865224092Sdougb	key->numset[type] = ISC_FALSE;
866224092Sdougb}
867224092Sdougb
868224092Sdougbisc_result_t
869224092Sdougbdst_key_gettime(const dst_key_t *key, int type, isc_stdtime_t *timep) {
870224092Sdougb	REQUIRE(VALID_KEY(key));
871224092Sdougb	REQUIRE(timep != NULL);
872224092Sdougb	REQUIRE(type <= DST_MAX_TIMES);
873224092Sdougb	if (!key->timeset[type])
874224092Sdougb		return (ISC_R_NOTFOUND);
875224092Sdougb	*timep = key->times[type];
876224092Sdougb	return (ISC_R_SUCCESS);
877224092Sdougb}
878224092Sdougb
879224092Sdougbvoid
880224092Sdougbdst_key_settime(dst_key_t *key, int type, isc_stdtime_t when) {
881224092Sdougb	REQUIRE(VALID_KEY(key));
882224092Sdougb	REQUIRE(type <= DST_MAX_TIMES);
883224092Sdougb	key->times[type] = when;
884224092Sdougb	key->timeset[type] = ISC_TRUE;
885224092Sdougb}
886224092Sdougb
887224092Sdougbvoid
888224092Sdougbdst_key_unsettime(dst_key_t *key, int type) {
889224092Sdougb	REQUIRE(VALID_KEY(key));
890224092Sdougb	REQUIRE(type <= DST_MAX_TIMES);
891224092Sdougb	key->timeset[type] = ISC_FALSE;
892224092Sdougb}
893224092Sdougb
894224092Sdougbisc_result_t
895224092Sdougbdst_key_getprivateformat(const dst_key_t *key, int *majorp, int *minorp) {
896224092Sdougb	REQUIRE(VALID_KEY(key));
897224092Sdougb	REQUIRE(majorp != NULL);
898224092Sdougb	REQUIRE(minorp != NULL);
899224092Sdougb	*majorp = key->fmt_major;
900224092Sdougb	*minorp = key->fmt_minor;
901224092Sdougb	return (ISC_R_SUCCESS);
902224092Sdougb}
903224092Sdougb
904224092Sdougbvoid
905224092Sdougbdst_key_setprivateformat(dst_key_t *key, int major, int minor) {
906224092Sdougb	REQUIRE(VALID_KEY(key));
907224092Sdougb	key->fmt_major = major;
908224092Sdougb	key->fmt_minor = minor;
909224092Sdougb}
910224092Sdougb
911224092Sdougbstatic isc_boolean_t
912224092Sdougbcomparekeys(const dst_key_t *key1, const dst_key_t *key2,
913224092Sdougb	    isc_boolean_t match_revoked_key,
914224092Sdougb	    isc_boolean_t (*compare)(const dst_key_t *key1,
915224092Sdougb				     const dst_key_t *key2))
916224092Sdougb{
917143731Sdougb	REQUIRE(dst_initialized == ISC_TRUE);
918143731Sdougb	REQUIRE(VALID_KEY(key1));
919143731Sdougb	REQUIRE(VALID_KEY(key2));
920143731Sdougb
921143731Sdougb	if (key1 == key2)
922143731Sdougb		return (ISC_TRUE);
923224092Sdougb
924143731Sdougb	if (key1 == NULL || key2 == NULL)
925143731Sdougb		return (ISC_FALSE);
926224092Sdougb
927224092Sdougb	if (key1->key_alg != key2->key_alg)
928224092Sdougb		return (ISC_FALSE);
929224092Sdougb
930224092Sdougb	if (key1->key_id != key2->key_id) {
931224092Sdougb		if (!match_revoked_key)
932224092Sdougb			return (ISC_FALSE);
933224092Sdougb		if (key1->key_alg == DST_ALG_RSAMD5)
934224092Sdougb			return (ISC_FALSE);
935224092Sdougb		if ((key1->key_flags & DNS_KEYFLAG_REVOKE) ==
936224092Sdougb		    (key2->key_flags & DNS_KEYFLAG_REVOKE))
937224092Sdougb			return (ISC_FALSE);
938234010Sdougb		if (key1->key_id != key2->key_rid &&
939234010Sdougb		    key1->key_rid != key2->key_id)
940224092Sdougb			return (ISC_FALSE);
941224092Sdougb	}
942224092Sdougb
943224092Sdougb	if (compare != NULL)
944224092Sdougb		return (compare(key1, key2));
945143731Sdougb	else
946143731Sdougb		return (ISC_FALSE);
947143731Sdougb}
948143731Sdougb
949224092Sdougb
950224092Sdougb/*
951224092Sdougb * Compares only the public portion of two keys, by converting them
952224092Sdougb * both to wire format and comparing the results.
953224092Sdougb */
954224092Sdougbstatic isc_boolean_t
955224092Sdougbpub_compare(const dst_key_t *key1, const dst_key_t *key2) {
956224092Sdougb	isc_result_t result;
957224092Sdougb	unsigned char buf1[DST_KEY_MAXSIZE], buf2[DST_KEY_MAXSIZE];
958224092Sdougb	isc_buffer_t b1, b2;
959224092Sdougb	isc_region_t r1, r2;
960224092Sdougb
961224092Sdougb	isc_buffer_init(&b1, buf1, sizeof(buf1));
962224092Sdougb	result = dst_key_todns(key1, &b1);
963224092Sdougb	if (result != ISC_R_SUCCESS)
964224092Sdougb		return (ISC_FALSE);
965224092Sdougb	/* Zero out flags. */
966224092Sdougb	buf1[0] = buf1[1] = 0;
967224092Sdougb	if ((key1->key_flags & DNS_KEYFLAG_EXTENDED) != 0)
968224092Sdougb		isc_buffer_subtract(&b1, 2);
969224092Sdougb
970224092Sdougb	isc_buffer_init(&b2, buf2, sizeof(buf2));
971224092Sdougb	result = dst_key_todns(key2, &b2);
972224092Sdougb	if (result != ISC_R_SUCCESS)
973224092Sdougb		return (ISC_FALSE);
974224092Sdougb	/* Zero out flags. */
975224092Sdougb	buf2[0] = buf2[1] = 0;
976224092Sdougb	if ((key2->key_flags & DNS_KEYFLAG_EXTENDED) != 0)
977224092Sdougb		isc_buffer_subtract(&b2, 2);
978224092Sdougb
979224092Sdougb	isc_buffer_usedregion(&b1, &r1);
980224092Sdougb	/* Remove extended flags. */
981224092Sdougb	if ((key1->key_flags & DNS_KEYFLAG_EXTENDED) != 0) {
982224092Sdougb		memmove(&buf1[4], &buf1[6], r1.length - 6);
983224092Sdougb		r1.length -= 2;
984224092Sdougb	}
985224092Sdougb
986224092Sdougb	isc_buffer_usedregion(&b2, &r2);
987224092Sdougb	/* Remove extended flags. */
988224092Sdougb	if ((key2->key_flags & DNS_KEYFLAG_EXTENDED) != 0) {
989224092Sdougb		memmove(&buf2[4], &buf2[6], r2.length - 6);
990224092Sdougb		r2.length -= 2;
991224092Sdougb	}
992224092Sdougb	return (ISC_TF(isc_region_compare(&r1, &r2) == 0));
993224092Sdougb}
994224092Sdougb
995143731Sdougbisc_boolean_t
996224092Sdougbdst_key_compare(const dst_key_t *key1, const dst_key_t *key2) {
997224092Sdougb	return (comparekeys(key1, key2, ISC_FALSE, key1->func->compare));
998224092Sdougb}
999224092Sdougb
1000224092Sdougbisc_boolean_t
1001224092Sdougbdst_key_pubcompare(const dst_key_t *key1, const dst_key_t *key2,
1002224092Sdougb		   isc_boolean_t match_revoked_key)
1003224092Sdougb{
1004224092Sdougb	return (comparekeys(key1, key2, match_revoked_key, pub_compare));
1005224092Sdougb}
1006224092Sdougb
1007224092Sdougb
1008224092Sdougbisc_boolean_t
1009143731Sdougbdst_key_paramcompare(const dst_key_t *key1, const dst_key_t *key2) {
1010143731Sdougb	REQUIRE(dst_initialized == ISC_TRUE);
1011143731Sdougb	REQUIRE(VALID_KEY(key1));
1012143731Sdougb	REQUIRE(VALID_KEY(key2));
1013143731Sdougb
1014143731Sdougb	if (key1 == key2)
1015143731Sdougb		return (ISC_TRUE);
1016143731Sdougb	if (key1 == NULL || key2 == NULL)
1017143731Sdougb		return (ISC_FALSE);
1018143731Sdougb	if (key1->key_alg == key2->key_alg &&
1019143731Sdougb	    key1->func->paramcompare != NULL &&
1020143731Sdougb	    key1->func->paramcompare(key1, key2) == ISC_TRUE)
1021143731Sdougb		return (ISC_TRUE);
1022143731Sdougb	else
1023143731Sdougb		return (ISC_FALSE);
1024143731Sdougb}
1025143731Sdougb
1026143731Sdougbvoid
1027218384Sdougbdst_key_attach(dst_key_t *source, dst_key_t **target) {
1028218384Sdougb
1029218384Sdougb	REQUIRE(dst_initialized == ISC_TRUE);
1030218384Sdougb	REQUIRE(target != NULL && *target == NULL);
1031218384Sdougb	REQUIRE(VALID_KEY(source));
1032218384Sdougb
1033218384Sdougb	isc_refcount_increment(&source->refs, NULL);
1034218384Sdougb	*target = source;
1035218384Sdougb}
1036218384Sdougb
1037218384Sdougbvoid
1038143731Sdougbdst_key_free(dst_key_t **keyp) {
1039143731Sdougb	isc_mem_t *mctx;
1040143731Sdougb	dst_key_t *key;
1041218384Sdougb	unsigned int refs;
1042143731Sdougb
1043143731Sdougb	REQUIRE(dst_initialized == ISC_TRUE);
1044143731Sdougb	REQUIRE(keyp != NULL && VALID_KEY(*keyp));
1045143731Sdougb
1046143731Sdougb	key = *keyp;
1047143731Sdougb	mctx = key->mctx;
1048143731Sdougb
1049218384Sdougb	isc_refcount_decrement(&key->refs, &refs);
1050218384Sdougb	if (refs != 0)
1051218384Sdougb		return;
1052218384Sdougb
1053218384Sdougb	isc_refcount_destroy(&key->refs);
1054193149Sdougb	if (key->keydata.generic != NULL) {
1055143731Sdougb		INSIST(key->func->destroy != NULL);
1056143731Sdougb		key->func->destroy(key);
1057143731Sdougb	}
1058193149Sdougb	if (key->engine != NULL)
1059193149Sdougb		isc_mem_free(mctx, key->engine);
1060193149Sdougb	if (key->label != NULL)
1061193149Sdougb		isc_mem_free(mctx, key->label);
1062143731Sdougb	dns_name_free(key->key_name, mctx);
1063143731Sdougb	isc_mem_put(mctx, key->key_name, sizeof(dns_name_t));
1064224092Sdougb	if (key->key_tkeytoken) {
1065224092Sdougb		isc_buffer_free(&key->key_tkeytoken);
1066224092Sdougb	}
1067143731Sdougb	memset(key, 0, sizeof(dst_key_t));
1068143731Sdougb	isc_mem_put(mctx, key, sizeof(dst_key_t));
1069143731Sdougb	*keyp = NULL;
1070143731Sdougb}
1071143731Sdougb
1072143731Sdougbisc_boolean_t
1073143731Sdougbdst_key_isprivate(const dst_key_t *key) {
1074143731Sdougb	REQUIRE(VALID_KEY(key));
1075143731Sdougb	INSIST(key->func->isprivate != NULL);
1076143731Sdougb	return (key->func->isprivate(key));
1077143731Sdougb}
1078143731Sdougb
1079143731Sdougbisc_result_t
1080143731Sdougbdst_key_buildfilename(const dst_key_t *key, int type,
1081143731Sdougb		      const char *directory, isc_buffer_t *out) {
1082143731Sdougb
1083143731Sdougb	REQUIRE(VALID_KEY(key));
1084143731Sdougb	REQUIRE(type == DST_TYPE_PRIVATE || type == DST_TYPE_PUBLIC ||
1085143731Sdougb		type == 0);
1086143731Sdougb
1087143731Sdougb	return (buildfilename(key->key_name, key->key_id, key->key_alg,
1088143731Sdougb			      type, directory, out));
1089143731Sdougb}
1090143731Sdougb
1091143731Sdougbisc_result_t
1092143731Sdougbdst_key_sigsize(const dst_key_t *key, unsigned int *n) {
1093143731Sdougb	REQUIRE(dst_initialized == ISC_TRUE);
1094143731Sdougb	REQUIRE(VALID_KEY(key));
1095143731Sdougb	REQUIRE(n != NULL);
1096143731Sdougb
1097143731Sdougb	/* XXXVIX this switch statement is too sparse to gen a jump table. */
1098143731Sdougb	switch (key->key_alg) {
1099143731Sdougb	case DST_ALG_RSAMD5:
1100143731Sdougb	case DST_ALG_RSASHA1:
1101193149Sdougb	case DST_ALG_NSEC3RSASHA1:
1102204619Sdougb	case DST_ALG_RSASHA256:
1103204619Sdougb	case DST_ALG_RSASHA512:
1104143731Sdougb		*n = (key->key_size + 7) / 8;
1105143731Sdougb		break;
1106143731Sdougb	case DST_ALG_DSA:
1107193149Sdougb	case DST_ALG_NSEC3DSA:
1108143731Sdougb		*n = DNS_SIG_DSASIGSIZE;
1109143731Sdougb		break;
1110224092Sdougb	case DST_ALG_ECCGOST:
1111224092Sdougb		*n = DNS_SIG_GOSTSIGSIZE;
1112224092Sdougb		break;
1113143731Sdougb	case DST_ALG_HMACMD5:
1114143731Sdougb		*n = 16;
1115143731Sdougb		break;
1116170222Sdougb	case DST_ALG_HMACSHA1:
1117170222Sdougb		*n = ISC_SHA1_DIGESTLENGTH;
1118170222Sdougb		break;
1119170222Sdougb	case DST_ALG_HMACSHA224:
1120170222Sdougb		*n = ISC_SHA224_DIGESTLENGTH;
1121170222Sdougb		break;
1122170222Sdougb	case DST_ALG_HMACSHA256:
1123170222Sdougb		*n = ISC_SHA256_DIGESTLENGTH;
1124170222Sdougb		break;
1125170222Sdougb	case DST_ALG_HMACSHA384:
1126170222Sdougb		*n = ISC_SHA384_DIGESTLENGTH;
1127170222Sdougb		break;
1128170222Sdougb	case DST_ALG_HMACSHA512:
1129170222Sdougb		*n = ISC_SHA512_DIGESTLENGTH;
1130170222Sdougb		break;
1131143731Sdougb	case DST_ALG_GSSAPI:
1132170222Sdougb		*n = 128; /*%< XXX */
1133143731Sdougb		break;
1134143731Sdougb	case DST_ALG_DH:
1135143731Sdougb	default:
1136143731Sdougb		return (DST_R_UNSUPPORTEDALG);
1137143731Sdougb	}
1138143731Sdougb	return (ISC_R_SUCCESS);
1139143731Sdougb}
1140143731Sdougb
1141143731Sdougbisc_result_t
1142143731Sdougbdst_key_secretsize(const dst_key_t *key, unsigned int *n) {
1143143731Sdougb	REQUIRE(dst_initialized == ISC_TRUE);
1144143731Sdougb	REQUIRE(VALID_KEY(key));
1145143731Sdougb	REQUIRE(n != NULL);
1146143731Sdougb
1147143731Sdougb	if (key->key_alg == DST_ALG_DH)
1148143731Sdougb		*n = (key->key_size + 7) / 8;
1149143731Sdougb	else
1150143731Sdougb		return (DST_R_UNSUPPORTEDALG);
1151143731Sdougb	return (ISC_R_SUCCESS);
1152143731Sdougb}
1153143731Sdougb
1154224092Sdougb/*%
1155224092Sdougb * Set the flags on a key, then recompute the key ID
1156224092Sdougb */
1157224092Sdougbisc_result_t
1158224092Sdougbdst_key_setflags(dst_key_t *key, isc_uint32_t flags) {
1159224092Sdougb	REQUIRE(VALID_KEY(key));
1160224092Sdougb	key->key_flags = flags;
1161224092Sdougb	return (computeid(key));
1162224092Sdougb}
1163224092Sdougb
1164224092Sdougbvoid
1165224092Sdougbdst_key_format(const dst_key_t *key, char *cp, unsigned int size) {
1166224092Sdougb	char namestr[DNS_NAME_FORMATSIZE];
1167224092Sdougb	char algstr[DNS_NAME_FORMATSIZE];
1168224092Sdougb
1169224092Sdougb	dns_name_format(dst_key_name(key), namestr, sizeof(namestr));
1170224092Sdougb	dns_secalg_format((dns_secalg_t) dst_key_alg(key), algstr,
1171224092Sdougb			  sizeof(algstr));
1172224092Sdougb	snprintf(cp, size, "%s/%s/%d", namestr, algstr, dst_key_id(key));
1173224092Sdougb}
1174224092Sdougb
1175224092Sdougbisc_result_t
1176224092Sdougbdst_key_dump(dst_key_t *key, isc_mem_t *mctx, char **buffer, int *length) {
1177224092Sdougb
1178224092Sdougb	REQUIRE(buffer != NULL && *buffer == NULL);
1179224092Sdougb	REQUIRE(length != NULL && *length == 0);
1180224092Sdougb	REQUIRE(VALID_KEY(key));
1181224092Sdougb
1182224092Sdougb	if (key->func->isprivate == NULL)
1183224092Sdougb		return (ISC_R_NOTIMPLEMENTED);
1184224092Sdougb	return (key->func->dump(key, mctx, buffer, length));
1185224092Sdougb}
1186224092Sdougb
1187224092Sdougbisc_result_t
1188224092Sdougbdst_key_restore(dns_name_t *name, unsigned int alg, unsigned int flags,
1189224092Sdougb		unsigned int protocol, dns_rdataclass_t rdclass,
1190224092Sdougb		isc_mem_t *mctx, const char *keystr, dst_key_t **keyp)
1191224092Sdougb{
1192224092Sdougb	isc_result_t result;
1193224092Sdougb	dst_key_t *key;
1194224092Sdougb
1195224092Sdougb	REQUIRE(dst_initialized == ISC_TRUE);
1196224092Sdougb	REQUIRE(keyp != NULL && *keyp == NULL);
1197224092Sdougb
1198224092Sdougb	if (alg >= DST_MAX_ALGS || dst_t_func[alg] == NULL)
1199224092Sdougb		return (DST_R_UNSUPPORTEDALG);
1200224092Sdougb
1201224092Sdougb	if (dst_t_func[alg]->restore == NULL)
1202224092Sdougb		return (ISC_R_NOTIMPLEMENTED);
1203224092Sdougb
1204224092Sdougb	key = get_key_struct(name, alg, flags, protocol, 0, rdclass, mctx);
1205224092Sdougb	if (key == NULL)
1206224092Sdougb		return (ISC_R_NOMEMORY);
1207224092Sdougb
1208224092Sdougb	result = (dst_t_func[alg]->restore)(key, keystr);
1209224092Sdougb	if (result == ISC_R_SUCCESS)
1210224092Sdougb		*keyp = key;
1211224092Sdougb	else
1212224092Sdougb		dst_key_free(&key);
1213224092Sdougb
1214224092Sdougb	return (result);
1215224092Sdougb}
1216224092Sdougb
1217143731Sdougb/***
1218143731Sdougb *** Static methods
1219143731Sdougb ***/
1220143731Sdougb
1221170222Sdougb/*%
1222143731Sdougb * Allocates a key structure and fills in some of the fields.
1223143731Sdougb */
1224143731Sdougbstatic dst_key_t *
1225143731Sdougbget_key_struct(dns_name_t *name, unsigned int alg,
1226143731Sdougb	       unsigned int flags, unsigned int protocol,
1227143731Sdougb	       unsigned int bits, dns_rdataclass_t rdclass,
1228143731Sdougb	       isc_mem_t *mctx)
1229143731Sdougb{
1230143731Sdougb	dst_key_t *key;
1231143731Sdougb	isc_result_t result;
1232224092Sdougb	int i;
1233143731Sdougb
1234143731Sdougb	key = (dst_key_t *) isc_mem_get(mctx, sizeof(dst_key_t));
1235143731Sdougb	if (key == NULL)
1236143731Sdougb		return (NULL);
1237143731Sdougb
1238143731Sdougb	memset(key, 0, sizeof(dst_key_t));
1239143731Sdougb	key->magic = KEY_MAGIC;
1240143731Sdougb
1241218384Sdougb	result = isc_refcount_init(&key->refs, 1);
1242218384Sdougb	if (result != ISC_R_SUCCESS) {
1243218384Sdougb		isc_mem_put(mctx, key, sizeof(dst_key_t));
1244218384Sdougb		return (NULL);
1245218384Sdougb	}
1246218384Sdougb
1247143731Sdougb	key->key_name = isc_mem_get(mctx, sizeof(dns_name_t));
1248143731Sdougb	if (key->key_name == NULL) {
1249218384Sdougb		isc_refcount_destroy(&key->refs);
1250143731Sdougb		isc_mem_put(mctx, key, sizeof(dst_key_t));
1251143731Sdougb		return (NULL);
1252143731Sdougb	}
1253143731Sdougb	dns_name_init(key->key_name, NULL);
1254143731Sdougb	result = dns_name_dup(name, mctx, key->key_name);
1255143731Sdougb	if (result != ISC_R_SUCCESS) {
1256218384Sdougb		isc_refcount_destroy(&key->refs);
1257143731Sdougb		isc_mem_put(mctx, key->key_name, sizeof(dns_name_t));
1258143731Sdougb		isc_mem_put(mctx, key, sizeof(dst_key_t));
1259143731Sdougb		return (NULL);
1260143731Sdougb	}
1261143731Sdougb	key->key_alg = alg;
1262143731Sdougb	key->key_flags = flags;
1263143731Sdougb	key->key_proto = protocol;
1264143731Sdougb	key->mctx = mctx;
1265193149Sdougb	key->keydata.generic = NULL;
1266143731Sdougb	key->key_size = bits;
1267143731Sdougb	key->key_class = rdclass;
1268143731Sdougb	key->func = dst_t_func[alg];
1269224092Sdougb	key->fmt_major = 0;
1270224092Sdougb	key->fmt_minor = 0;
1271224092Sdougb	for (i = 0; i < (DST_MAX_TIMES + 1); i++) {
1272224092Sdougb		key->times[i] = 0;
1273224092Sdougb		key->timeset[i] = ISC_FALSE;
1274224092Sdougb	}
1275143731Sdougb	return (key);
1276143731Sdougb}
1277143731Sdougb
1278170222Sdougb/*%
1279143731Sdougb * Reads a public key from disk
1280143731Sdougb */
1281170222Sdougbisc_result_t
1282170222Sdougbdst_key_read_public(const char *filename, int type,
1283170222Sdougb		    isc_mem_t *mctx, dst_key_t **keyp)
1284143731Sdougb{
1285143731Sdougb	u_char rdatabuf[DST_KEY_MAXSIZE];
1286143731Sdougb	isc_buffer_t b;
1287143731Sdougb	dns_fixedname_t name;
1288143731Sdougb	isc_lex_t *lex = NULL;
1289143731Sdougb	isc_token_t token;
1290143731Sdougb	isc_result_t ret;
1291143731Sdougb	dns_rdata_t rdata = DNS_RDATA_INIT;
1292143731Sdougb	unsigned int opt = ISC_LEXOPT_DNSMULTILINE;
1293143731Sdougb	dns_rdataclass_t rdclass = dns_rdataclass_in;
1294143731Sdougb	isc_lexspecials_t specials;
1295143731Sdougb	isc_uint32_t ttl;
1296143731Sdougb	isc_result_t result;
1297143731Sdougb	dns_rdatatype_t keytype;
1298143731Sdougb
1299143731Sdougb	/*
1300143731Sdougb	 * Open the file and read its formatted contents
1301143731Sdougb	 * File format:
1302170222Sdougb	 *    domain.name [ttl] [class] [KEY|DNSKEY] <flags> <protocol> <algorithm> <key>
1303143731Sdougb	 */
1304143731Sdougb
1305143731Sdougb	/* 1500 should be large enough for any key */
1306143731Sdougb	ret = isc_lex_create(mctx, 1500, &lex);
1307143731Sdougb	if (ret != ISC_R_SUCCESS)
1308143731Sdougb		goto cleanup;
1309143731Sdougb
1310143731Sdougb	memset(specials, 0, sizeof(specials));
1311143731Sdougb	specials['('] = 1;
1312143731Sdougb	specials[')'] = 1;
1313143731Sdougb	specials['"'] = 1;
1314143731Sdougb	isc_lex_setspecials(lex, specials);
1315143731Sdougb	isc_lex_setcomments(lex, ISC_LEXCOMMENT_DNSMASTERFILE);
1316143731Sdougb
1317170222Sdougb	ret = isc_lex_openfile(lex, filename);
1318143731Sdougb	if (ret != ISC_R_SUCCESS)
1319143731Sdougb		goto cleanup;
1320143731Sdougb
1321143731Sdougb#define NEXTTOKEN(lex, opt, token) { \
1322143731Sdougb	ret = isc_lex_gettoken(lex, opt, token); \
1323143731Sdougb	if (ret != ISC_R_SUCCESS) \
1324143731Sdougb		goto cleanup; \
1325143731Sdougb	}
1326143731Sdougb
1327143731Sdougb#define BADTOKEN() { \
1328143731Sdougb	ret = ISC_R_UNEXPECTEDTOKEN; \
1329143731Sdougb	goto cleanup; \
1330143731Sdougb	}
1331143731Sdougb
1332143731Sdougb	/* Read the domain name */
1333143731Sdougb	NEXTTOKEN(lex, opt, &token);
1334143731Sdougb	if (token.type != isc_tokentype_string)
1335143731Sdougb		BADTOKEN();
1336193149Sdougb
1337193149Sdougb	/*
1338193149Sdougb	 * We don't support "@" in .key files.
1339193149Sdougb	 */
1340193149Sdougb	if (!strcmp(DST_AS_STR(token), "@"))
1341193149Sdougb		BADTOKEN();
1342193149Sdougb
1343143731Sdougb	dns_fixedname_init(&name);
1344143731Sdougb	isc_buffer_init(&b, DST_AS_STR(token), strlen(DST_AS_STR(token)));
1345143731Sdougb	isc_buffer_add(&b, strlen(DST_AS_STR(token)));
1346143731Sdougb	ret = dns_name_fromtext(dns_fixedname_name(&name), &b, dns_rootname,
1347224092Sdougb				0, NULL);
1348143731Sdougb	if (ret != ISC_R_SUCCESS)
1349143731Sdougb		goto cleanup;
1350143731Sdougb
1351143731Sdougb	/* Read the next word: either TTL, class, or 'KEY' */
1352143731Sdougb	NEXTTOKEN(lex, opt, &token);
1353143731Sdougb
1354204619Sdougb	if (token.type != isc_tokentype_string)
1355204619Sdougb		BADTOKEN();
1356204619Sdougb
1357143731Sdougb	/* If it's a TTL, read the next one */
1358143731Sdougb	result = dns_ttl_fromtext(&token.value.as_textregion, &ttl);
1359143731Sdougb	if (result == ISC_R_SUCCESS)
1360143731Sdougb		NEXTTOKEN(lex, opt, &token);
1361143731Sdougb
1362143731Sdougb	if (token.type != isc_tokentype_string)
1363143731Sdougb		BADTOKEN();
1364143731Sdougb
1365143731Sdougb	ret = dns_rdataclass_fromtext(&rdclass, &token.value.as_textregion);
1366143731Sdougb	if (ret == ISC_R_SUCCESS)
1367143731Sdougb		NEXTTOKEN(lex, opt, &token);
1368143731Sdougb
1369143731Sdougb	if (token.type != isc_tokentype_string)
1370143731Sdougb		BADTOKEN();
1371143731Sdougb
1372143731Sdougb	if (strcasecmp(DST_AS_STR(token), "DNSKEY") == 0)
1373143731Sdougb		keytype = dns_rdatatype_dnskey;
1374143731Sdougb	else if (strcasecmp(DST_AS_STR(token), "KEY") == 0)
1375170222Sdougb		keytype = dns_rdatatype_key; /*%< SIG(0), TKEY */
1376143731Sdougb	else
1377143731Sdougb		BADTOKEN();
1378143731Sdougb
1379143731Sdougb	if (((type & DST_TYPE_KEY) != 0 && keytype != dns_rdatatype_key) ||
1380143731Sdougb	    ((type & DST_TYPE_KEY) == 0 && keytype != dns_rdatatype_dnskey)) {
1381143731Sdougb		ret = DST_R_BADKEYTYPE;
1382143731Sdougb		goto cleanup;
1383143731Sdougb	}
1384143731Sdougb
1385143731Sdougb	isc_buffer_init(&b, rdatabuf, sizeof(rdatabuf));
1386143731Sdougb	ret = dns_rdata_fromtext(&rdata, rdclass, keytype, lex, NULL,
1387143731Sdougb				 ISC_FALSE, mctx, &b, NULL);
1388143731Sdougb	if (ret != ISC_R_SUCCESS)
1389143731Sdougb		goto cleanup;
1390143731Sdougb
1391143731Sdougb	ret = dst_key_fromdns(dns_fixedname_name(&name), rdclass, &b, mctx,
1392143731Sdougb			      keyp);
1393143731Sdougb	if (ret != ISC_R_SUCCESS)
1394143731Sdougb		goto cleanup;
1395143731Sdougb
1396143731Sdougb cleanup:
1397143731Sdougb	if (lex != NULL)
1398143731Sdougb		isc_lex_destroy(&lex);
1399143731Sdougb	return (ret);
1400143731Sdougb}
1401143731Sdougb
1402143731Sdougbstatic isc_boolean_t
1403143731Sdougbissymmetric(const dst_key_t *key) {
1404143731Sdougb	REQUIRE(dst_initialized == ISC_TRUE);
1405143731Sdougb	REQUIRE(VALID_KEY(key));
1406143731Sdougb
1407143731Sdougb	/* XXXVIX this switch statement is too sparse to gen a jump table. */
1408143731Sdougb	switch (key->key_alg) {
1409143731Sdougb	case DST_ALG_RSAMD5:
1410143731Sdougb	case DST_ALG_RSASHA1:
1411193149Sdougb	case DST_ALG_NSEC3RSASHA1:
1412204619Sdougb	case DST_ALG_RSASHA256:
1413204619Sdougb	case DST_ALG_RSASHA512:
1414143731Sdougb	case DST_ALG_DSA:
1415193149Sdougb	case DST_ALG_NSEC3DSA:
1416143731Sdougb	case DST_ALG_DH:
1417224092Sdougb	case DST_ALG_ECCGOST:
1418143731Sdougb		return (ISC_FALSE);
1419143731Sdougb	case DST_ALG_HMACMD5:
1420143731Sdougb	case DST_ALG_GSSAPI:
1421143731Sdougb		return (ISC_TRUE);
1422143731Sdougb	default:
1423143731Sdougb		return (ISC_FALSE);
1424143731Sdougb	}
1425143731Sdougb}
1426143731Sdougb
1427170222Sdougb/*%
1428224092Sdougb * Write key timing metadata to a file pointer, preceded by 'tag'
1429224092Sdougb */
1430224092Sdougbstatic void
1431224092Sdougbprinttime(const dst_key_t *key, int type, const char *tag, FILE *stream) {
1432224092Sdougb	isc_result_t result;
1433224092Sdougb#ifdef ISC_PLATFORM_USETHREADS
1434224092Sdougb	char output[26]; /* Minimum buffer as per ctime_r() specification. */
1435224092Sdougb#else
1436224092Sdougb	const char *output;
1437224092Sdougb#endif
1438224092Sdougb	isc_stdtime_t when;
1439224092Sdougb	time_t t;
1440224092Sdougb	char utc[sizeof("YYYYMMDDHHSSMM")];
1441224092Sdougb	isc_buffer_t b;
1442224092Sdougb	isc_region_t r;
1443224092Sdougb
1444224092Sdougb	result = dst_key_gettime(key, type, &when);
1445224092Sdougb	if (result == ISC_R_NOTFOUND)
1446224092Sdougb		return;
1447224092Sdougb
1448224092Sdougb	/* time_t and isc_stdtime_t might be different sizes */
1449224092Sdougb	t = when;
1450224092Sdougb#ifdef ISC_PLATFORM_USETHREADS
1451224092Sdougb#ifdef WIN32
1452224092Sdougb	if (ctime_s(output, sizeof(output), &t) != 0)
1453224092Sdougb		goto error;
1454224092Sdougb#else
1455224092Sdougb	if (ctime_r(&t, output) == NULL)
1456224092Sdougb		goto error;
1457224092Sdougb#endif
1458224092Sdougb#else
1459224092Sdougb	output = ctime(&t);
1460224092Sdougb#endif
1461224092Sdougb
1462224092Sdougb	isc_buffer_init(&b, utc, sizeof(utc));
1463224092Sdougb	result = dns_time32_totext(when, &b);
1464224092Sdougb	if (result != ISC_R_SUCCESS)
1465224092Sdougb		goto error;
1466224092Sdougb
1467224092Sdougb	isc_buffer_usedregion(&b, &r);
1468224092Sdougb	fprintf(stream, "%s: %.*s (%.*s)\n", tag, (int)r.length, r.base,
1469224092Sdougb		 (int)strlen(output) - 1, output);
1470224092Sdougb	return;
1471224092Sdougb
1472224092Sdougb error:
1473224092Sdougb	fprintf(stream, "%s: (set, unable to display)\n", tag);
1474224092Sdougb}
1475224092Sdougb
1476224092Sdougb/*%
1477143731Sdougb * Writes a public key to disk in DNS format.
1478143731Sdougb */
1479143731Sdougbstatic isc_result_t
1480143731Sdougbwrite_public_key(const dst_key_t *key, int type, const char *directory) {
1481143731Sdougb	FILE *fp;
1482143731Sdougb	isc_buffer_t keyb, textb, fileb, classb;
1483143731Sdougb	isc_region_t r;
1484143731Sdougb	char filename[ISC_DIR_NAMEMAX];
1485143731Sdougb	unsigned char key_array[DST_KEY_MAXSIZE];
1486143731Sdougb	char text_array[DST_KEY_MAXTEXTSIZE];
1487143731Sdougb	char class_array[10];
1488143731Sdougb	isc_result_t ret;
1489143731Sdougb	dns_rdata_t rdata = DNS_RDATA_INIT;
1490143731Sdougb	isc_fsaccess_t access;
1491143731Sdougb
1492143731Sdougb	REQUIRE(VALID_KEY(key));
1493143731Sdougb
1494143731Sdougb	isc_buffer_init(&keyb, key_array, sizeof(key_array));
1495143731Sdougb	isc_buffer_init(&textb, text_array, sizeof(text_array));
1496143731Sdougb	isc_buffer_init(&classb, class_array, sizeof(class_array));
1497143731Sdougb
1498143731Sdougb	ret = dst_key_todns(key, &keyb);
1499143731Sdougb	if (ret != ISC_R_SUCCESS)
1500143731Sdougb		return (ret);
1501143731Sdougb
1502143731Sdougb	isc_buffer_usedregion(&keyb, &r);
1503143731Sdougb	dns_rdata_fromregion(&rdata, key->key_class, dns_rdatatype_dnskey, &r);
1504143731Sdougb
1505143731Sdougb	ret = dns_rdata_totext(&rdata, (dns_name_t *) NULL, &textb);
1506143731Sdougb	if (ret != ISC_R_SUCCESS)
1507143731Sdougb		return (DST_R_INVALIDPUBLICKEY);
1508143731Sdougb
1509143731Sdougb	ret = dns_rdataclass_totext(key->key_class, &classb);
1510143731Sdougb	if (ret != ISC_R_SUCCESS)
1511143731Sdougb		return (DST_R_INVALIDPUBLICKEY);
1512143731Sdougb
1513143731Sdougb	/*
1514143731Sdougb	 * Make the filename.
1515143731Sdougb	 */
1516143731Sdougb	isc_buffer_init(&fileb, filename, sizeof(filename));
1517143731Sdougb	ret = dst_key_buildfilename(key, DST_TYPE_PUBLIC, directory, &fileb);
1518143731Sdougb	if (ret != ISC_R_SUCCESS)
1519143731Sdougb		return (ret);
1520143731Sdougb
1521143731Sdougb	/*
1522143731Sdougb	 * Create public key file.
1523143731Sdougb	 */
1524143731Sdougb	if ((fp = fopen(filename, "w")) == NULL)
1525143731Sdougb		return (DST_R_WRITEERROR);
1526143731Sdougb
1527143731Sdougb	if (issymmetric(key)) {
1528143731Sdougb		access = 0;
1529143731Sdougb		isc_fsaccess_add(ISC_FSACCESS_OWNER,
1530143731Sdougb				 ISC_FSACCESS_READ | ISC_FSACCESS_WRITE,
1531143731Sdougb				 &access);
1532143731Sdougb		(void)isc_fsaccess_set(filename, access);
1533143731Sdougb	}
1534143731Sdougb
1535224092Sdougb	/* Write key information in comments */
1536224092Sdougb	if ((type & DST_TYPE_KEY) == 0) {
1537224092Sdougb		fprintf(fp, "; This is a %s%s-signing key, keyid %d, for ",
1538224092Sdougb			(key->key_flags & DNS_KEYFLAG_REVOKE) != 0 ?
1539224092Sdougb				"revoked " :
1540224092Sdougb				"",
1541224092Sdougb			(key->key_flags & DNS_KEYFLAG_KSK) != 0 ?
1542224092Sdougb				"key" :
1543224092Sdougb				"zone",
1544224092Sdougb			key->key_id);
1545224092Sdougb		ret = dns_name_print(key->key_name, fp);
1546224092Sdougb		if (ret != ISC_R_SUCCESS) {
1547224092Sdougb			fclose(fp);
1548224092Sdougb			return (ret);
1549224092Sdougb		}
1550224092Sdougb		fputc('\n', fp);
1551224092Sdougb
1552224092Sdougb		printtime(key, DST_TIME_CREATED, "; Created", fp);
1553224092Sdougb		printtime(key, DST_TIME_PUBLISH, "; Publish", fp);
1554224092Sdougb		printtime(key, DST_TIME_ACTIVATE, "; Activate", fp);
1555224092Sdougb		printtime(key, DST_TIME_REVOKE, "; Revoke", fp);
1556224092Sdougb		printtime(key, DST_TIME_INACTIVE, "; Inactive", fp);
1557224092Sdougb		printtime(key, DST_TIME_DELETE, "; Delete", fp);
1558165071Sdougb	}
1559143731Sdougb
1560224092Sdougb	/* Now print the actual key */
1561224092Sdougb	ret = dns_name_print(key->key_name, fp);
1562224092Sdougb
1563143731Sdougb	fprintf(fp, " ");
1564143731Sdougb
1565143731Sdougb	isc_buffer_usedregion(&classb, &r);
1566234010Sdougb	if ((unsigned) fwrite(r.base, 1, r.length, fp) != r.length)
1567234010Sdougb	       ret = DST_R_WRITEERROR;
1568143731Sdougb
1569143731Sdougb	if ((type & DST_TYPE_KEY) != 0)
1570143731Sdougb		fprintf(fp, " KEY ");
1571143731Sdougb	else
1572143731Sdougb		fprintf(fp, " DNSKEY ");
1573143731Sdougb
1574143731Sdougb	isc_buffer_usedregion(&textb, &r);
1575234010Sdougb	if ((unsigned) fwrite(r.base, 1, r.length, fp) != r.length)
1576234010Sdougb	       ret = DST_R_WRITEERROR;
1577143731Sdougb
1578143731Sdougb	fputc('\n', fp);
1579193149Sdougb	fflush(fp);
1580193149Sdougb	if (ferror(fp))
1581193149Sdougb		ret = DST_R_WRITEERROR;
1582143731Sdougb	fclose(fp);
1583143731Sdougb
1584193149Sdougb	return (ret);
1585143731Sdougb}
1586143731Sdougb
1587143731Sdougbstatic isc_result_t
1588143731Sdougbbuildfilename(dns_name_t *name, dns_keytag_t id,
1589143731Sdougb	      unsigned int alg, unsigned int type,
1590143731Sdougb	      const char *directory, isc_buffer_t *out)
1591143731Sdougb{
1592143731Sdougb	const char *suffix = "";
1593143731Sdougb	unsigned int len;
1594143731Sdougb	isc_result_t result;
1595143731Sdougb
1596143731Sdougb	REQUIRE(out != NULL);
1597143731Sdougb	if ((type & DST_TYPE_PRIVATE) != 0)
1598143731Sdougb		suffix = ".private";
1599143731Sdougb	else if (type == DST_TYPE_PUBLIC)
1600143731Sdougb		suffix = ".key";
1601143731Sdougb	if (directory != NULL) {
1602143731Sdougb		if (isc_buffer_availablelength(out) < strlen(directory))
1603143731Sdougb			return (ISC_R_NOSPACE);
1604143731Sdougb		isc_buffer_putstr(out, directory);
1605143731Sdougb		if (strlen(directory) > 0U &&
1606143731Sdougb		    directory[strlen(directory) - 1] != '/')
1607143731Sdougb			isc_buffer_putstr(out, "/");
1608143731Sdougb	}
1609143731Sdougb	if (isc_buffer_availablelength(out) < 1)
1610143731Sdougb		return (ISC_R_NOSPACE);
1611143731Sdougb	isc_buffer_putstr(out, "K");
1612143731Sdougb	result = dns_name_tofilenametext(name, ISC_FALSE, out);
1613143731Sdougb	if (result != ISC_R_SUCCESS)
1614143731Sdougb		return (result);
1615143731Sdougb	len = 1 + 3 + 1 + 5 + strlen(suffix) + 1;
1616143731Sdougb	if (isc_buffer_availablelength(out) < len)
1617143731Sdougb		return (ISC_R_NOSPACE);
1618193149Sdougb	sprintf((char *) isc_buffer_used(out), "+%03d+%05d%s", alg, id,
1619193149Sdougb		suffix);
1620143731Sdougb	isc_buffer_add(out, len);
1621193149Sdougb
1622143731Sdougb	return (ISC_R_SUCCESS);
1623143731Sdougb}
1624143731Sdougb
1625143731Sdougbstatic isc_result_t
1626143731Sdougbcomputeid(dst_key_t *key) {
1627143731Sdougb	isc_buffer_t dnsbuf;
1628143731Sdougb	unsigned char dns_array[DST_KEY_MAXSIZE];
1629143731Sdougb	isc_region_t r;
1630143731Sdougb	isc_result_t ret;
1631143731Sdougb
1632143731Sdougb	isc_buffer_init(&dnsbuf, dns_array, sizeof(dns_array));
1633143731Sdougb	ret = dst_key_todns(key, &dnsbuf);
1634143731Sdougb	if (ret != ISC_R_SUCCESS)
1635143731Sdougb		return (ret);
1636143731Sdougb
1637143731Sdougb	isc_buffer_usedregion(&dnsbuf, &r);
1638143731Sdougb	key->key_id = dst_region_computeid(&r, key->key_alg);
1639234010Sdougb	key->key_rid = dst_region_computerid(&r, key->key_alg);
1640143731Sdougb	return (ISC_R_SUCCESS);
1641143731Sdougb}
1642143731Sdougb
1643143731Sdougbstatic isc_result_t
1644143731Sdougbfrombuffer(dns_name_t *name, unsigned int alg, unsigned int flags,
1645143731Sdougb	   unsigned int protocol, dns_rdataclass_t rdclass,
1646143731Sdougb	   isc_buffer_t *source, isc_mem_t *mctx, dst_key_t **keyp)
1647143731Sdougb{
1648143731Sdougb	dst_key_t *key;
1649143731Sdougb	isc_result_t ret;
1650143731Sdougb
1651143731Sdougb	REQUIRE(dns_name_isabsolute(name));
1652143731Sdougb	REQUIRE(source != NULL);
1653143731Sdougb	REQUIRE(mctx != NULL);
1654143731Sdougb	REQUIRE(keyp != NULL && *keyp == NULL);
1655143731Sdougb
1656143731Sdougb	key = get_key_struct(name, alg, flags, protocol, 0, rdclass, mctx);
1657143731Sdougb	if (key == NULL)
1658143731Sdougb		return (ISC_R_NOMEMORY);
1659143731Sdougb
1660143731Sdougb	if (isc_buffer_remaininglength(source) > 0) {
1661143731Sdougb		ret = algorithm_status(alg);
1662143731Sdougb		if (ret != ISC_R_SUCCESS) {
1663143731Sdougb			dst_key_free(&key);
1664143731Sdougb			return (ret);
1665143731Sdougb		}
1666143731Sdougb		if (key->func->fromdns == NULL) {
1667143731Sdougb			dst_key_free(&key);
1668143731Sdougb			return (DST_R_UNSUPPORTEDALG);
1669143731Sdougb		}
1670143731Sdougb
1671143731Sdougb		ret = key->func->fromdns(key, source);
1672143731Sdougb		if (ret != ISC_R_SUCCESS) {
1673143731Sdougb			dst_key_free(&key);
1674143731Sdougb			return (ret);
1675143731Sdougb		}
1676143731Sdougb	}
1677143731Sdougb
1678143731Sdougb	*keyp = key;
1679143731Sdougb	return (ISC_R_SUCCESS);
1680143731Sdougb}
1681143731Sdougb
1682143731Sdougbstatic isc_result_t
1683143731Sdougbalgorithm_status(unsigned int alg) {
1684143731Sdougb	REQUIRE(dst_initialized == ISC_TRUE);
1685143731Sdougb
1686143731Sdougb	if (dst_algorithm_supported(alg))
1687143731Sdougb		return (ISC_R_SUCCESS);
1688143731Sdougb#ifndef OPENSSL
1689143731Sdougb	if (alg == DST_ALG_RSAMD5 || alg == DST_ALG_RSASHA1 ||
1690143731Sdougb	    alg == DST_ALG_DSA || alg == DST_ALG_DH ||
1691193149Sdougb	    alg == DST_ALG_HMACMD5 || alg == DST_ALG_NSEC3DSA ||
1692204619Sdougb	    alg == DST_ALG_NSEC3RSASHA1 ||
1693224092Sdougb	    alg == DST_ALG_RSASHA256 || alg == DST_ALG_RSASHA512 ||
1694224092Sdougb	    alg == DST_ALG_ECCGOST)
1695143731Sdougb		return (DST_R_NOCRYPTO);
1696143731Sdougb#endif
1697143731Sdougb	return (DST_R_UNSUPPORTEDALG);
1698143731Sdougb}
1699143731Sdougb
1700143731Sdougbstatic isc_result_t
1701224092Sdougbaddsuffix(char *filename, int len, const char *odirname,
1702224092Sdougb	  const char *ofilename, const char *suffix)
1703143731Sdougb{
1704143731Sdougb	int olen = strlen(ofilename);
1705143731Sdougb	int n;
1706143731Sdougb
1707143731Sdougb	if (olen > 1 && ofilename[olen - 1] == '.')
1708143731Sdougb		olen -= 1;
1709143731Sdougb	else if (olen > 8 && strcmp(ofilename + olen - 8, ".private") == 0)
1710143731Sdougb		olen -= 8;
1711143731Sdougb	else if (olen > 4 && strcmp(ofilename + olen - 4, ".key") == 0)
1712143731Sdougb		olen -= 4;
1713143731Sdougb
1714224092Sdougb	if (odirname == NULL)
1715224092Sdougb		n = snprintf(filename, len, "%.*s%s", olen, ofilename, suffix);
1716224092Sdougb	else
1717224092Sdougb		n = snprintf(filename, len, "%s/%.*s%s",
1718224092Sdougb			     odirname, olen, ofilename, suffix);
1719143731Sdougb	if (n < 0)
1720204619Sdougb		return (ISC_R_FAILURE);
1721224092Sdougb	if (n >= len)
1722143731Sdougb		return (ISC_R_NOSPACE);
1723143731Sdougb	return (ISC_R_SUCCESS);
1724143731Sdougb}
1725143731Sdougb
1726143731Sdougbisc_result_t
1727143731Sdougbdst__entropy_getdata(void *buf, unsigned int len, isc_boolean_t pseudo) {
1728224092Sdougb#ifdef BIND9
1729143731Sdougb	unsigned int flags = dst_entropy_flags;
1730204619Sdougb
1731204619Sdougb	if (len == 0)
1732204619Sdougb		return (ISC_R_SUCCESS);
1733143731Sdougb	if (pseudo)
1734143731Sdougb		flags &= ~ISC_ENTROPY_GOODONLY;
1735224092Sdougb	else
1736224092Sdougb		flags |= ISC_ENTROPY_BLOCKING;
1737143731Sdougb	return (isc_entropy_getdata(dst_entropy_pool, buf, len, NULL, flags));
1738224092Sdougb#else
1739224092Sdougb	UNUSED(buf);
1740224092Sdougb	UNUSED(len);
1741224092Sdougb	UNUSED(pseudo);
1742224092Sdougb
1743224092Sdougb	return (ISC_R_NOTIMPLEMENTED);
1744224092Sdougb#endif
1745143731Sdougb}
1746193149Sdougb
1747193149Sdougbunsigned int
1748193149Sdougbdst__entropy_status(void) {
1749224092Sdougb#ifdef BIND9
1750204619Sdougb#ifdef GSSAPI
1751204619Sdougb	unsigned int flags = dst_entropy_flags;
1752204619Sdougb	isc_result_t ret;
1753204619Sdougb	unsigned char buf[32];
1754204619Sdougb	static isc_boolean_t first = ISC_TRUE;
1755204619Sdougb
1756204619Sdougb	if (first) {
1757204619Sdougb		/* Someone believes RAND_status() initializes the PRNG */
1758204619Sdougb		flags &= ~ISC_ENTROPY_GOODONLY;
1759204619Sdougb		ret = isc_entropy_getdata(dst_entropy_pool, buf,
1760204619Sdougb					  sizeof(buf), NULL, flags);
1761204619Sdougb		INSIST(ret == ISC_R_SUCCESS);
1762204619Sdougb		isc_entropy_putdata(dst_entropy_pool, buf,
1763204619Sdougb				    sizeof(buf), 2 * sizeof(buf));
1764204619Sdougb		first = ISC_FALSE;
1765204619Sdougb	}
1766204619Sdougb#endif
1767193149Sdougb	return (isc_entropy_status(dst_entropy_pool));
1768224092Sdougb#else
1769224092Sdougb	return (0);
1770224092Sdougb#endif
1771193149Sdougb}
1772224092Sdougb
1773224092Sdougbisc_buffer_t *
1774224092Sdougbdst_key_tkeytoken(const dst_key_t *key) {
1775225361Sdougb	REQUIRE(VALID_KEY(key));
1776224092Sdougb	return (key->key_tkeytoken);
1777224092Sdougb}
1778