dst_api.c revision 165071
1143731Sdougb/*
2165071Sdougb * Portions Copyright (C) 2004, 2006  Internet Systems Consortium, Inc. ("ISC")
3143731Sdougb * Portions Copyright (C) 1999-2003  Internet Software Consortium.
4143731Sdougb * Portions Copyright (C) 1995-2000 by Network Associates, Inc.
5143731Sdougb *
6143731Sdougb * Permission to use, copy, modify, and distribute this software for any
7143731Sdougb * purpose with or without fee is hereby granted, provided that the above
8143731Sdougb * copyright notice and this permission notice appear in all copies.
9143731Sdougb *
10143731Sdougb * THE SOFTWARE IS PROVIDED "AS IS" AND ISC AND NETWORK ASSOCIATES DISCLAIMS
11143731Sdougb * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
12143731Sdougb * WARRANTIES OF MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE
13143731Sdougb * FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14143731Sdougb * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15143731Sdougb * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
16143731Sdougb * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17143731Sdougb */
18143731Sdougb
19143731Sdougb/*
20143731Sdougb * Principal Author: Brian Wellington
21165071Sdougb * $Id: dst_api.c,v 1.1.4.3 2006/01/04 23:50:20 marka Exp $
22143731Sdougb */
23143731Sdougb
24143731Sdougb#include <config.h>
25143731Sdougb
26143731Sdougb#include <stdlib.h>
27143731Sdougb
28143731Sdougb#include <isc/buffer.h>
29143731Sdougb#include <isc/dir.h>
30143731Sdougb#include <isc/entropy.h>
31143731Sdougb#include <isc/fsaccess.h>
32143731Sdougb#include <isc/lex.h>
33143731Sdougb#include <isc/mem.h>
34143731Sdougb#include <isc/once.h>
35143731Sdougb#include <isc/print.h>
36143731Sdougb#include <isc/random.h>
37143731Sdougb#include <isc/string.h>
38143731Sdougb#include <isc/time.h>
39143731Sdougb#include <isc/util.h>
40143731Sdougb
41143731Sdougb#include <dns/fixedname.h>
42143731Sdougb#include <dns/keyvalues.h>
43143731Sdougb#include <dns/name.h>
44143731Sdougb#include <dns/rdata.h>
45143731Sdougb#include <dns/rdataclass.h>
46143731Sdougb#include <dns/ttl.h>
47143731Sdougb#include <dns/types.h>
48143731Sdougb
49143731Sdougb#include <dst/result.h>
50143731Sdougb
51143731Sdougb#include "dst_internal.h"
52143731Sdougb
53143731Sdougb#define DST_AS_STR(t) ((t).value.as_textregion.base)
54143731Sdougb
55143731Sdougbstatic dst_func_t *dst_t_func[DST_MAX_ALGS];
56143731Sdougbstatic isc_entropy_t *dst_entropy_pool = NULL;
57143731Sdougbstatic unsigned int dst_entropy_flags = 0;
58143731Sdougbstatic isc_boolean_t dst_initialized = ISC_FALSE;
59143731Sdougb
60143731Sdougbisc_mem_t *dst__memory_pool = NULL;
61143731Sdougb
62143731Sdougb/*
63143731Sdougb * Static functions.
64143731Sdougb */
65143731Sdougbstatic dst_key_t *	get_key_struct(dns_name_t *name,
66143731Sdougb				       unsigned int alg,
67143731Sdougb				       unsigned int flags,
68143731Sdougb				       unsigned int protocol,
69143731Sdougb				       unsigned int bits,
70143731Sdougb				       dns_rdataclass_t rdclass,
71143731Sdougb				       isc_mem_t *mctx);
72143731Sdougbstatic isc_result_t	read_public_key(const char *filename,
73143731Sdougb					int type,
74143731Sdougb					isc_mem_t *mctx,
75143731Sdougb					dst_key_t **keyp);
76143731Sdougbstatic isc_result_t	write_public_key(const dst_key_t *key, int type,
77143731Sdougb					 const char *directory);
78143731Sdougbstatic isc_result_t	buildfilename(dns_name_t *name,
79143731Sdougb				      dns_keytag_t id,
80143731Sdougb				      unsigned int alg,
81143731Sdougb				      unsigned int type,
82143731Sdougb				      const char *directory,
83143731Sdougb				      isc_buffer_t *out);
84143731Sdougbstatic isc_result_t	computeid(dst_key_t *key);
85143731Sdougbstatic isc_result_t	frombuffer(dns_name_t *name,
86143731Sdougb				   unsigned int alg,
87143731Sdougb				   unsigned int flags,
88143731Sdougb				   unsigned int protocol,
89143731Sdougb				   dns_rdataclass_t rdclass,
90143731Sdougb				   isc_buffer_t *source,
91143731Sdougb				   isc_mem_t *mctx,
92143731Sdougb				   dst_key_t **keyp);
93143731Sdougb
94143731Sdougbstatic isc_result_t	algorithm_status(unsigned int alg);
95143731Sdougb
96143731Sdougbstatic isc_result_t	addsuffix(char *filename, unsigned int len,
97143731Sdougb				  const char *ofilename, const char *suffix);
98143731Sdougb
99143731Sdougb#define RETERR(x) 				\
100143731Sdougb	do {					\
101143731Sdougb		result = (x);			\
102143731Sdougb		if (result != ISC_R_SUCCESS)	\
103143731Sdougb			goto out;		\
104143731Sdougb	} while (0)
105143731Sdougb
106143731Sdougb#define CHECKALG(alg)				\
107143731Sdougb	do {					\
108143731Sdougb		isc_result_t _r;		\
109143731Sdougb		_r = algorithm_status(alg);	\
110143731Sdougb		if (_r != ISC_R_SUCCESS)	\
111143731Sdougb			return (_r);		\
112143731Sdougb	} while (0);				\
113143731Sdougb
114143731Sdougbisc_result_t
115143731Sdougbdst_lib_init(isc_mem_t *mctx, isc_entropy_t *ectx, unsigned int eflags) {
116143731Sdougb	isc_result_t result;
117143731Sdougb
118143731Sdougb	REQUIRE(mctx != NULL && ectx != NULL);
119143731Sdougb	REQUIRE(dst_initialized == ISC_FALSE);
120143731Sdougb
121143731Sdougb	dst__memory_pool = NULL;
122143731Sdougb
123143731Sdougb#ifdef OPENSSL
124143731Sdougb	UNUSED(mctx);
125143731Sdougb	/*
126143731Sdougb	 * When using --with-openssl, there seems to be no good way of not
127143731Sdougb	 * leaking memory due to the openssl error handling mechanism.
128143731Sdougb	 * Avoid assertions by using a local memory context and not checking
129143731Sdougb	 * for leaks on exit.
130143731Sdougb	 */
131143731Sdougb	result = isc_mem_create(0, 0, &dst__memory_pool);
132143731Sdougb	if (result != ISC_R_SUCCESS)
133143731Sdougb		return (result);
134143731Sdougb	isc_mem_setdestroycheck(dst__memory_pool, ISC_FALSE);
135143731Sdougb#else
136143731Sdougb	isc_mem_attach(mctx, &dst__memory_pool);
137143731Sdougb#endif
138143731Sdougb	isc_entropy_attach(ectx, &dst_entropy_pool);
139143731Sdougb	dst_entropy_flags = eflags;
140143731Sdougb
141143731Sdougb	dst_result_register();
142143731Sdougb
143143731Sdougb	memset(dst_t_func, 0, sizeof(dst_t_func));
144143731Sdougb	RETERR(dst__hmacmd5_init(&dst_t_func[DST_ALG_HMACMD5]));
145143731Sdougb#ifdef OPENSSL
146143731Sdougb	RETERR(dst__openssl_init());
147143731Sdougb	RETERR(dst__opensslrsa_init(&dst_t_func[DST_ALG_RSAMD5]));
148143731Sdougb	RETERR(dst__opensslrsa_init(&dst_t_func[DST_ALG_RSASHA1]));
149143731Sdougb#ifdef HAVE_OPENSSL_DSA
150143731Sdougb	RETERR(dst__openssldsa_init(&dst_t_func[DST_ALG_DSA]));
151143731Sdougb#endif
152143731Sdougb	RETERR(dst__openssldh_init(&dst_t_func[DST_ALG_DH]));
153143731Sdougb#endif /* OPENSSL */
154143731Sdougb#ifdef GSSAPI
155143731Sdougb	RETERR(dst__gssapi_init(&dst_t_func[DST_ALG_GSSAPI]));
156143731Sdougb#endif
157143731Sdougb	dst_initialized = ISC_TRUE;
158143731Sdougb	return (ISC_R_SUCCESS);
159143731Sdougb
160143731Sdougb out:
161143731Sdougb	dst_lib_destroy();
162143731Sdougb	return (result);
163143731Sdougb}
164143731Sdougb
165143731Sdougbvoid
166143731Sdougbdst_lib_destroy(void) {
167143731Sdougb	int i;
168143731Sdougb	RUNTIME_CHECK(dst_initialized == ISC_TRUE);
169143731Sdougb	dst_initialized = ISC_FALSE;
170143731Sdougb
171143731Sdougb	for (i = 0; i < DST_MAX_ALGS; i++)
172143731Sdougb		if (dst_t_func[i] != NULL && dst_t_func[i]->cleanup != NULL)
173143731Sdougb			dst_t_func[i]->cleanup();
174143731Sdougb#ifdef OPENSSL
175143731Sdougb	dst__openssl_destroy();
176143731Sdougb#endif
177143731Sdougb	if (dst__memory_pool != NULL)
178143731Sdougb		isc_mem_detach(&dst__memory_pool);
179143731Sdougb	if (dst_entropy_pool != NULL)
180143731Sdougb		isc_entropy_detach(&dst_entropy_pool);
181143731Sdougb
182143731Sdougb}
183143731Sdougb
184143731Sdougbisc_boolean_t
185143731Sdougbdst_algorithm_supported(unsigned int alg) {
186143731Sdougb	REQUIRE(dst_initialized == ISC_TRUE);
187143731Sdougb
188143731Sdougb	if (alg >= DST_MAX_ALGS || dst_t_func[alg] == NULL)
189143731Sdougb		return (ISC_FALSE);
190143731Sdougb	return (ISC_TRUE);
191143731Sdougb}
192143731Sdougb
193143731Sdougbisc_result_t
194143731Sdougbdst_context_create(dst_key_t *key, isc_mem_t *mctx, dst_context_t **dctxp) {
195143731Sdougb	dst_context_t *dctx;
196143731Sdougb	isc_result_t result;
197143731Sdougb
198143731Sdougb	REQUIRE(dst_initialized == ISC_TRUE);
199143731Sdougb	REQUIRE(VALID_KEY(key));
200143731Sdougb	REQUIRE(mctx != NULL);
201143731Sdougb	REQUIRE(dctxp != NULL && *dctxp == NULL);
202143731Sdougb
203143731Sdougb	if (key->func->createctx == NULL)
204143731Sdougb		return (DST_R_UNSUPPORTEDALG);
205143731Sdougb	if (key->opaque == NULL)
206143731Sdougb		return (DST_R_NULLKEY);
207143731Sdougb
208143731Sdougb	dctx = isc_mem_get(mctx, sizeof(dst_context_t));
209143731Sdougb	if (dctx == NULL)
210143731Sdougb		return (ISC_R_NOMEMORY);
211143731Sdougb	dctx->key = key;
212143731Sdougb	dctx->mctx = mctx;
213143731Sdougb	result = key->func->createctx(key, dctx);
214143731Sdougb	if (result != ISC_R_SUCCESS) {
215143731Sdougb		isc_mem_put(mctx, dctx, sizeof(dst_context_t));
216143731Sdougb		return (result);
217143731Sdougb	}
218143731Sdougb	dctx->magic = CTX_MAGIC;
219143731Sdougb	*dctxp = dctx;
220143731Sdougb	return (ISC_R_SUCCESS);
221143731Sdougb}
222143731Sdougb
223143731Sdougbvoid
224143731Sdougbdst_context_destroy(dst_context_t **dctxp) {
225143731Sdougb	dst_context_t *dctx;
226143731Sdougb
227143731Sdougb	REQUIRE(dctxp != NULL && VALID_CTX(*dctxp));
228143731Sdougb
229143731Sdougb	dctx = *dctxp;
230143731Sdougb	INSIST(dctx->key->func->destroyctx != NULL);
231143731Sdougb	dctx->key->func->destroyctx(dctx);
232143731Sdougb	dctx->magic = 0;
233143731Sdougb	isc_mem_put(dctx->mctx, dctx, sizeof(dst_context_t));
234143731Sdougb	*dctxp = NULL;
235143731Sdougb}
236143731Sdougb
237143731Sdougbisc_result_t
238143731Sdougbdst_context_adddata(dst_context_t *dctx, const isc_region_t *data) {
239143731Sdougb	REQUIRE(VALID_CTX(dctx));
240143731Sdougb	REQUIRE(data != NULL);
241143731Sdougb	INSIST(dctx->key->func->adddata != NULL);
242143731Sdougb
243143731Sdougb	return (dctx->key->func->adddata(dctx, data));
244143731Sdougb}
245143731Sdougb
246143731Sdougbisc_result_t
247143731Sdougbdst_context_sign(dst_context_t *dctx, isc_buffer_t *sig) {
248143731Sdougb	dst_key_t *key;
249143731Sdougb
250143731Sdougb	REQUIRE(VALID_CTX(dctx));
251143731Sdougb	REQUIRE(sig != NULL);
252143731Sdougb
253143731Sdougb	key = dctx->key;
254143731Sdougb	CHECKALG(key->key_alg);
255143731Sdougb	if (key->opaque == NULL)
256143731Sdougb		return (DST_R_NULLKEY);
257143731Sdougb	if (key->func->sign == NULL)
258143731Sdougb		return (DST_R_NOTPRIVATEKEY);
259143731Sdougb	if (key->func->isprivate == NULL ||
260143731Sdougb	    key->func->isprivate(key) == ISC_FALSE)
261143731Sdougb		return (DST_R_NOTPRIVATEKEY);
262143731Sdougb
263143731Sdougb	return (key->func->sign(dctx, sig));
264143731Sdougb}
265143731Sdougb
266143731Sdougbisc_result_t
267143731Sdougbdst_context_verify(dst_context_t *dctx, isc_region_t *sig) {
268143731Sdougb	REQUIRE(VALID_CTX(dctx));
269143731Sdougb	REQUIRE(sig != NULL);
270143731Sdougb
271143731Sdougb	CHECKALG(dctx->key->key_alg);
272143731Sdougb	if (dctx->key->opaque == NULL)
273143731Sdougb		return (DST_R_NULLKEY);
274143731Sdougb	if (dctx->key->func->verify == NULL)
275143731Sdougb		return (DST_R_NOTPUBLICKEY);
276143731Sdougb
277143731Sdougb	return (dctx->key->func->verify(dctx, sig));
278143731Sdougb}
279143731Sdougb
280143731Sdougbisc_result_t
281143731Sdougbdst_key_computesecret(const dst_key_t *pub, const dst_key_t *priv,
282143731Sdougb		      isc_buffer_t *secret)
283143731Sdougb{
284143731Sdougb	REQUIRE(dst_initialized == ISC_TRUE);
285143731Sdougb	REQUIRE(VALID_KEY(pub) && VALID_KEY(priv));
286143731Sdougb	REQUIRE(secret != NULL);
287143731Sdougb
288143731Sdougb	CHECKALG(pub->key_alg);
289143731Sdougb	CHECKALG(priv->key_alg);
290143731Sdougb
291143731Sdougb	if (pub->opaque == NULL || priv->opaque == NULL)
292143731Sdougb		return (DST_R_NULLKEY);
293143731Sdougb
294143731Sdougb	if (pub->key_alg != priv->key_alg ||
295143731Sdougb	    pub->func->computesecret == NULL ||
296143731Sdougb	    priv->func->computesecret == NULL)
297143731Sdougb		return (DST_R_KEYCANNOTCOMPUTESECRET);
298143731Sdougb
299143731Sdougb	if (dst_key_isprivate(priv) == ISC_FALSE)
300143731Sdougb		return (DST_R_NOTPRIVATEKEY);
301143731Sdougb
302143731Sdougb	return (pub->func->computesecret(pub, priv, secret));
303143731Sdougb}
304143731Sdougb
305143731Sdougbisc_result_t
306143731Sdougbdst_key_tofile(const dst_key_t *key, int type, const char *directory) {
307143731Sdougb	isc_result_t ret = ISC_R_SUCCESS;
308143731Sdougb
309143731Sdougb	REQUIRE(dst_initialized == ISC_TRUE);
310143731Sdougb	REQUIRE(VALID_KEY(key));
311143731Sdougb	REQUIRE((type & (DST_TYPE_PRIVATE | DST_TYPE_PUBLIC)) != 0);
312143731Sdougb
313143731Sdougb	CHECKALG(key->key_alg);
314143731Sdougb
315143731Sdougb	if (key->func->tofile == NULL)
316143731Sdougb		return (DST_R_UNSUPPORTEDALG);
317143731Sdougb
318143731Sdougb	if (type & DST_TYPE_PUBLIC) {
319143731Sdougb		ret = write_public_key(key, type, directory);
320143731Sdougb		if (ret != ISC_R_SUCCESS)
321143731Sdougb			return (ret);
322143731Sdougb	}
323143731Sdougb
324143731Sdougb	if ((type & DST_TYPE_PRIVATE) &&
325143731Sdougb	    (key->key_flags & DNS_KEYFLAG_TYPEMASK) != DNS_KEYTYPE_NOKEY)
326143731Sdougb		return (key->func->tofile(key, directory));
327143731Sdougb	else
328143731Sdougb		return (ISC_R_SUCCESS);
329143731Sdougb}
330143731Sdougb
331143731Sdougbisc_result_t
332143731Sdougbdst_key_fromfile(dns_name_t *name, dns_keytag_t id,
333143731Sdougb		 unsigned int alg, int type, const char *directory,
334143731Sdougb		 isc_mem_t *mctx, dst_key_t **keyp)
335143731Sdougb{
336143731Sdougb	char filename[ISC_DIR_NAMEMAX];
337143731Sdougb	isc_buffer_t b;
338143731Sdougb	dst_key_t *key;
339143731Sdougb	isc_result_t result;
340143731Sdougb
341143731Sdougb	REQUIRE(dst_initialized == ISC_TRUE);
342143731Sdougb	REQUIRE(dns_name_isabsolute(name));
343143731Sdougb	REQUIRE((type & (DST_TYPE_PRIVATE | DST_TYPE_PUBLIC)) != 0);
344143731Sdougb	REQUIRE(mctx != NULL);
345143731Sdougb	REQUIRE(keyp != NULL && *keyp == NULL);
346143731Sdougb
347143731Sdougb	CHECKALG(alg);
348143731Sdougb
349143731Sdougb	isc_buffer_init(&b, filename, sizeof(filename));
350143731Sdougb	result = buildfilename(name, id, alg, type, directory, &b);
351143731Sdougb	if (result != ISC_R_SUCCESS)
352143731Sdougb		return (result);
353143731Sdougb
354143731Sdougb	key = NULL;
355143731Sdougb	result = dst_key_fromnamedfile(filename, type, mctx, &key);
356143731Sdougb	if (result != ISC_R_SUCCESS)
357143731Sdougb		return (result);
358143731Sdougb
359143731Sdougb	result = computeid(key);
360143731Sdougb	if (result != ISC_R_SUCCESS) {
361143731Sdougb		dst_key_free(&key);
362143731Sdougb		return (result);
363143731Sdougb	}
364143731Sdougb
365143731Sdougb	if (!dns_name_equal(name, key->key_name) ||
366143731Sdougb	    id != key->key_id ||
367143731Sdougb	    alg != key->key_alg)
368143731Sdougb	{
369143731Sdougb		dst_key_free(&key);
370143731Sdougb		return (DST_R_INVALIDPRIVATEKEY);
371143731Sdougb	}
372143731Sdougb	key->key_id = id;
373143731Sdougb
374143731Sdougb	*keyp = key;
375143731Sdougb	return (ISC_R_SUCCESS);
376143731Sdougb}
377143731Sdougb
378143731Sdougbisc_result_t
379143731Sdougbdst_key_fromnamedfile(const char *filename, int type, isc_mem_t *mctx,
380143731Sdougb		      dst_key_t **keyp)
381143731Sdougb{
382143731Sdougb	isc_result_t result;
383143731Sdougb	dst_key_t *pubkey = NULL, *key = NULL;
384143731Sdougb	dns_keytag_t id;
385143731Sdougb	char *newfilename = NULL;
386143731Sdougb	int newfilenamelen = 0;
387143731Sdougb	isc_lex_t *lex = NULL;
388143731Sdougb
389143731Sdougb	REQUIRE(dst_initialized == ISC_TRUE);
390143731Sdougb	REQUIRE(filename != NULL);
391143731Sdougb	REQUIRE((type & (DST_TYPE_PRIVATE | DST_TYPE_PUBLIC)) != 0);
392143731Sdougb	REQUIRE(mctx != NULL);
393143731Sdougb	REQUIRE(keyp != NULL && *keyp == NULL);
394143731Sdougb
395143731Sdougb	result = read_public_key(filename, type, mctx, &pubkey);
396143731Sdougb	if (result != ISC_R_SUCCESS)
397143731Sdougb		return (result);
398143731Sdougb
399143731Sdougb	if ((type & (DST_TYPE_PRIVATE | DST_TYPE_PUBLIC)) == DST_TYPE_PUBLIC ||
400143731Sdougb	    (pubkey->key_flags & DNS_KEYFLAG_TYPEMASK) == DNS_KEYTYPE_NOKEY)
401143731Sdougb	{
402143731Sdougb		result = computeid(pubkey);
403143731Sdougb		if (result != ISC_R_SUCCESS) {
404143731Sdougb			dst_key_free(&pubkey);
405143731Sdougb			return (result);
406143731Sdougb		}
407143731Sdougb
408143731Sdougb		*keyp = pubkey;
409143731Sdougb		return (ISC_R_SUCCESS);
410143731Sdougb	}
411143731Sdougb
412143731Sdougb	result = algorithm_status(pubkey->key_alg);
413143731Sdougb	if (result != ISC_R_SUCCESS) {
414143731Sdougb		dst_key_free(&pubkey);
415143731Sdougb		return (result);
416143731Sdougb	}
417143731Sdougb
418143731Sdougb	key = get_key_struct(pubkey->key_name, pubkey->key_alg,
419143731Sdougb			     pubkey->key_flags, pubkey->key_proto, 0,
420143731Sdougb			     pubkey->key_class, mctx);
421143731Sdougb	id = pubkey->key_id;
422143731Sdougb	dst_key_free(&pubkey);
423143731Sdougb
424143731Sdougb	if (key == NULL)
425143731Sdougb		return (ISC_R_NOMEMORY);
426143731Sdougb
427143731Sdougb	if (key->func->parse == NULL)
428143731Sdougb		RETERR(DST_R_UNSUPPORTEDALG);
429143731Sdougb
430143731Sdougb	newfilenamelen = strlen(filename) + 9;
431143731Sdougb	newfilename = isc_mem_get(mctx, newfilenamelen);
432143731Sdougb	if (newfilename == NULL)
433143731Sdougb		RETERR(ISC_R_NOMEMORY);
434143731Sdougb	result = addsuffix(newfilename, newfilenamelen, filename, ".private");
435143731Sdougb	INSIST(result == ISC_R_SUCCESS);
436143731Sdougb
437143731Sdougb	RETERR(isc_lex_create(mctx, 1500, &lex));
438143731Sdougb	RETERR(isc_lex_openfile(lex, newfilename));
439143731Sdougb	isc_mem_put(mctx, newfilename, newfilenamelen);
440143731Sdougb
441143731Sdougb	RETERR(key->func->parse(key, lex));
442143731Sdougb	isc_lex_destroy(&lex);
443143731Sdougb
444143731Sdougb	RETERR(computeid(key));
445143731Sdougb
446143731Sdougb	if (id != key->key_id)
447143731Sdougb		RETERR(DST_R_INVALIDPRIVATEKEY);
448143731Sdougb
449143731Sdougb	*keyp = key;
450143731Sdougb	return (ISC_R_SUCCESS);
451143731Sdougb out:
452143731Sdougb	if (newfilename != NULL)
453143731Sdougb		isc_mem_put(mctx, newfilename, newfilenamelen);
454143731Sdougb	if (lex != NULL)
455143731Sdougb		isc_lex_destroy(&lex);
456143731Sdougb	dst_key_free(&key);
457143731Sdougb	return (result);
458143731Sdougb}
459143731Sdougb
460143731Sdougbisc_result_t
461143731Sdougbdst_key_todns(const dst_key_t *key, isc_buffer_t *target) {
462143731Sdougb	REQUIRE(dst_initialized == ISC_TRUE);
463143731Sdougb	REQUIRE(VALID_KEY(key));
464143731Sdougb	REQUIRE(target != NULL);
465143731Sdougb
466143731Sdougb	CHECKALG(key->key_alg);
467143731Sdougb
468143731Sdougb	if (key->func->todns == NULL)
469143731Sdougb		return (DST_R_UNSUPPORTEDALG);
470143731Sdougb
471143731Sdougb	if (isc_buffer_availablelength(target) < 4)
472143731Sdougb		return (ISC_R_NOSPACE);
473143731Sdougb	isc_buffer_putuint16(target, (isc_uint16_t)(key->key_flags & 0xffff));
474143731Sdougb	isc_buffer_putuint8(target, (isc_uint8_t)key->key_proto);
475143731Sdougb	isc_buffer_putuint8(target, (isc_uint8_t)key->key_alg);
476143731Sdougb
477143731Sdougb	if (key->key_flags & DNS_KEYFLAG_EXTENDED) {
478143731Sdougb		if (isc_buffer_availablelength(target) < 2)
479143731Sdougb			return (ISC_R_NOSPACE);
480143731Sdougb		isc_buffer_putuint16(target,
481143731Sdougb				     (isc_uint16_t)((key->key_flags >> 16)
482143731Sdougb						    & 0xffff));
483143731Sdougb	}
484143731Sdougb
485143731Sdougb	if (key->opaque == NULL) /* NULL KEY */
486143731Sdougb		return (ISC_R_SUCCESS);
487143731Sdougb
488143731Sdougb	return (key->func->todns(key, target));
489143731Sdougb}
490143731Sdougb
491143731Sdougbisc_result_t
492143731Sdougbdst_key_fromdns(dns_name_t *name, dns_rdataclass_t rdclass,
493143731Sdougb		isc_buffer_t *source, isc_mem_t *mctx, dst_key_t **keyp)
494143731Sdougb{
495143731Sdougb	isc_uint8_t alg, proto;
496143731Sdougb	isc_uint32_t flags, extflags;
497143731Sdougb	dst_key_t *key = NULL;
498143731Sdougb	dns_keytag_t id;
499143731Sdougb	isc_region_t r;
500143731Sdougb	isc_result_t result;
501143731Sdougb
502143731Sdougb	REQUIRE(dst_initialized);
503143731Sdougb
504143731Sdougb	isc_buffer_remainingregion(source, &r);
505143731Sdougb
506143731Sdougb	if (isc_buffer_remaininglength(source) < 4)
507143731Sdougb		return (DST_R_INVALIDPUBLICKEY);
508143731Sdougb	flags = isc_buffer_getuint16(source);
509143731Sdougb	proto = isc_buffer_getuint8(source);
510143731Sdougb	alg = isc_buffer_getuint8(source);
511143731Sdougb
512143731Sdougb	id = dst_region_computeid(&r, alg);
513143731Sdougb
514143731Sdougb	if (flags & DNS_KEYFLAG_EXTENDED) {
515143731Sdougb		if (isc_buffer_remaininglength(source) < 2)
516143731Sdougb			return (DST_R_INVALIDPUBLICKEY);
517143731Sdougb		extflags = isc_buffer_getuint16(source);
518143731Sdougb		flags |= (extflags << 16);
519143731Sdougb	}
520143731Sdougb
521143731Sdougb	result = frombuffer(name, alg, flags, proto, rdclass, source,
522143731Sdougb			    mctx, &key);
523143731Sdougb	if (result != ISC_R_SUCCESS)
524143731Sdougb		return (result);
525143731Sdougb	key->key_id = id;
526143731Sdougb
527143731Sdougb	*keyp = key;
528143731Sdougb	return (ISC_R_SUCCESS);
529143731Sdougb}
530143731Sdougb
531143731Sdougbisc_result_t
532143731Sdougbdst_key_frombuffer(dns_name_t *name, unsigned int alg,
533143731Sdougb		   unsigned int flags, unsigned int protocol,
534143731Sdougb		   dns_rdataclass_t rdclass,
535143731Sdougb		   isc_buffer_t *source, isc_mem_t *mctx, dst_key_t **keyp)
536143731Sdougb{
537143731Sdougb	dst_key_t *key = NULL;
538143731Sdougb	isc_result_t result;
539143731Sdougb
540143731Sdougb	REQUIRE(dst_initialized);
541143731Sdougb
542143731Sdougb	result = frombuffer(name, alg, flags, protocol, rdclass, source,
543143731Sdougb			    mctx, &key);
544143731Sdougb	if (result != ISC_R_SUCCESS)
545143731Sdougb		return (result);
546143731Sdougb
547143731Sdougb	result = computeid(key);
548143731Sdougb	if (result != ISC_R_SUCCESS) {
549143731Sdougb		dst_key_free(&key);
550143731Sdougb		return (result);
551143731Sdougb	}
552143731Sdougb
553143731Sdougb	*keyp = key;
554143731Sdougb	return (ISC_R_SUCCESS);
555143731Sdougb}
556143731Sdougb
557143731Sdougbisc_result_t
558143731Sdougbdst_key_tobuffer(const dst_key_t *key, isc_buffer_t *target) {
559143731Sdougb	REQUIRE(dst_initialized == ISC_TRUE);
560143731Sdougb	REQUIRE(VALID_KEY(key));
561143731Sdougb	REQUIRE(target != NULL);
562143731Sdougb
563143731Sdougb	CHECKALG(key->key_alg);
564143731Sdougb
565143731Sdougb	if (key->func->todns == NULL)
566143731Sdougb		return (DST_R_UNSUPPORTEDALG);
567143731Sdougb
568143731Sdougb	return (key->func->todns(key, target));
569143731Sdougb}
570143731Sdougb
571143731Sdougbisc_result_t
572143731Sdougbdst_key_privatefrombuffer(dst_key_t *key, isc_buffer_t *buffer) {
573143731Sdougb	isc_lex_t *lex = NULL;
574143731Sdougb	isc_result_t result = ISC_R_SUCCESS;
575143731Sdougb
576143731Sdougb	REQUIRE(dst_initialized == ISC_TRUE);
577143731Sdougb	REQUIRE(VALID_KEY(key));
578143731Sdougb	REQUIRE(!dst_key_isprivate(key));
579143731Sdougb	REQUIRE(buffer != NULL);
580143731Sdougb
581143731Sdougb	if (key->func->parse == NULL)
582143731Sdougb		RETERR(DST_R_UNSUPPORTEDALG);
583143731Sdougb
584143731Sdougb	RETERR(isc_lex_create(key->mctx, 1500, &lex));
585143731Sdougb	RETERR(isc_lex_openbuffer(lex, buffer));
586143731Sdougb	RETERR(key->func->parse(key, lex));
587143731Sdougb out:
588143731Sdougb	if (lex != NULL)
589143731Sdougb		isc_lex_destroy(&lex);
590143731Sdougb	return (result);
591143731Sdougb}
592143731Sdougb
593143731Sdougbisc_result_t
594143731Sdougbdst_key_fromgssapi(dns_name_t *name, void *opaque, isc_mem_t *mctx,
595143731Sdougb		   dst_key_t **keyp)
596143731Sdougb{
597143731Sdougb	dst_key_t *key;
598143731Sdougb
599143731Sdougb	REQUIRE(opaque != NULL);
600143731Sdougb	REQUIRE(keyp != NULL && *keyp == NULL);
601143731Sdougb
602143731Sdougb	key = get_key_struct(name, DST_ALG_GSSAPI, 0, DNS_KEYPROTO_DNSSEC,
603143731Sdougb			     0, dns_rdataclass_in, mctx);
604143731Sdougb	if (key == NULL)
605143731Sdougb		return (ISC_R_NOMEMORY);
606143731Sdougb	key->opaque = opaque;
607143731Sdougb	*keyp = key;
608143731Sdougb	return (ISC_R_SUCCESS);
609143731Sdougb}
610143731Sdougb
611143731Sdougbisc_result_t
612143731Sdougbdst_key_generate(dns_name_t *name, unsigned int alg,
613143731Sdougb		 unsigned int bits, unsigned int param,
614143731Sdougb		 unsigned int flags, unsigned int protocol,
615143731Sdougb		 dns_rdataclass_t rdclass,
616143731Sdougb		 isc_mem_t *mctx, dst_key_t **keyp)
617143731Sdougb{
618143731Sdougb	dst_key_t *key;
619143731Sdougb	isc_result_t ret;
620143731Sdougb
621143731Sdougb	REQUIRE(dst_initialized == ISC_TRUE);
622143731Sdougb	REQUIRE(dns_name_isabsolute(name));
623143731Sdougb	REQUIRE(mctx != NULL);
624143731Sdougb	REQUIRE(keyp != NULL && *keyp == NULL);
625143731Sdougb
626143731Sdougb	CHECKALG(alg);
627143731Sdougb
628143731Sdougb	key = get_key_struct(name, alg, flags, protocol, bits, rdclass, mctx);
629143731Sdougb	if (key == NULL)
630143731Sdougb		return (ISC_R_NOMEMORY);
631143731Sdougb
632143731Sdougb	if (bits == 0) { /* NULL KEY */
633143731Sdougb		key->key_flags |= DNS_KEYTYPE_NOKEY;
634143731Sdougb		*keyp = key;
635143731Sdougb		return (ISC_R_SUCCESS);
636143731Sdougb	}
637143731Sdougb
638143731Sdougb	if (key->func->generate == NULL) {
639143731Sdougb		dst_key_free(&key);
640143731Sdougb		return (DST_R_UNSUPPORTEDALG);
641143731Sdougb	}
642143731Sdougb
643143731Sdougb	ret = key->func->generate(key, param);
644143731Sdougb	if (ret != ISC_R_SUCCESS) {
645143731Sdougb		dst_key_free(&key);
646143731Sdougb		return (ret);
647143731Sdougb	}
648143731Sdougb
649143731Sdougb	ret = computeid(key);
650143731Sdougb	if (ret != ISC_R_SUCCESS) {
651143731Sdougb		dst_key_free(&key);
652143731Sdougb		return (ret);
653143731Sdougb	}
654143731Sdougb
655143731Sdougb	*keyp = key;
656143731Sdougb	return (ISC_R_SUCCESS);
657143731Sdougb}
658143731Sdougb
659143731Sdougbisc_boolean_t
660143731Sdougbdst_key_compare(const dst_key_t *key1, const dst_key_t *key2) {
661143731Sdougb	REQUIRE(dst_initialized == ISC_TRUE);
662143731Sdougb	REQUIRE(VALID_KEY(key1));
663143731Sdougb	REQUIRE(VALID_KEY(key2));
664143731Sdougb
665143731Sdougb	if (key1 == key2)
666143731Sdougb		return (ISC_TRUE);
667143731Sdougb	if (key1 == NULL || key2 == NULL)
668143731Sdougb		return (ISC_FALSE);
669143731Sdougb	if (key1->key_alg == key2->key_alg &&
670143731Sdougb	    key1->key_id == key2->key_id &&
671143731Sdougb	    key1->func->compare != NULL &&
672143731Sdougb	    key1->func->compare(key1, key2) == ISC_TRUE)
673143731Sdougb		return (ISC_TRUE);
674143731Sdougb	else
675143731Sdougb		return (ISC_FALSE);
676143731Sdougb}
677143731Sdougb
678143731Sdougbisc_boolean_t
679143731Sdougbdst_key_paramcompare(const dst_key_t *key1, const dst_key_t *key2) {
680143731Sdougb	REQUIRE(dst_initialized == ISC_TRUE);
681143731Sdougb	REQUIRE(VALID_KEY(key1));
682143731Sdougb	REQUIRE(VALID_KEY(key2));
683143731Sdougb
684143731Sdougb	if (key1 == key2)
685143731Sdougb		return (ISC_TRUE);
686143731Sdougb	if (key1 == NULL || key2 == NULL)
687143731Sdougb		return (ISC_FALSE);
688143731Sdougb	if (key1->key_alg == key2->key_alg &&
689143731Sdougb	    key1->func->paramcompare != NULL &&
690143731Sdougb	    key1->func->paramcompare(key1, key2) == ISC_TRUE)
691143731Sdougb		return (ISC_TRUE);
692143731Sdougb	else
693143731Sdougb		return (ISC_FALSE);
694143731Sdougb}
695143731Sdougb
696143731Sdougbvoid
697143731Sdougbdst_key_free(dst_key_t **keyp) {
698143731Sdougb	isc_mem_t *mctx;
699143731Sdougb	dst_key_t *key;
700143731Sdougb
701143731Sdougb	REQUIRE(dst_initialized == ISC_TRUE);
702143731Sdougb	REQUIRE(keyp != NULL && VALID_KEY(*keyp));
703143731Sdougb
704143731Sdougb	key = *keyp;
705143731Sdougb	mctx = key->mctx;
706143731Sdougb
707143731Sdougb	if (key->opaque != NULL) {
708143731Sdougb		INSIST(key->func->destroy != NULL);
709143731Sdougb		key->func->destroy(key);
710143731Sdougb	}
711143731Sdougb
712143731Sdougb	dns_name_free(key->key_name, mctx);
713143731Sdougb	isc_mem_put(mctx, key->key_name, sizeof(dns_name_t));
714143731Sdougb	memset(key, 0, sizeof(dst_key_t));
715143731Sdougb	isc_mem_put(mctx, key, sizeof(dst_key_t));
716143731Sdougb	*keyp = NULL;
717143731Sdougb}
718143731Sdougb
719143731Sdougbisc_boolean_t
720143731Sdougbdst_key_isprivate(const dst_key_t *key) {
721143731Sdougb	REQUIRE(VALID_KEY(key));
722143731Sdougb	INSIST(key->func->isprivate != NULL);
723143731Sdougb	return (key->func->isprivate(key));
724143731Sdougb}
725143731Sdougb
726143731Sdougbisc_result_t
727143731Sdougbdst_key_buildfilename(const dst_key_t *key, int type,
728143731Sdougb		      const char *directory, isc_buffer_t *out) {
729143731Sdougb
730143731Sdougb	REQUIRE(VALID_KEY(key));
731143731Sdougb	REQUIRE(type == DST_TYPE_PRIVATE || type == DST_TYPE_PUBLIC ||
732143731Sdougb		type == 0);
733143731Sdougb
734143731Sdougb	return (buildfilename(key->key_name, key->key_id, key->key_alg,
735143731Sdougb			      type, directory, out));
736143731Sdougb}
737143731Sdougb
738143731Sdougbisc_result_t
739143731Sdougbdst_key_sigsize(const dst_key_t *key, unsigned int *n) {
740143731Sdougb	REQUIRE(dst_initialized == ISC_TRUE);
741143731Sdougb	REQUIRE(VALID_KEY(key));
742143731Sdougb	REQUIRE(n != NULL);
743143731Sdougb
744143731Sdougb	/* XXXVIX this switch statement is too sparse to gen a jump table. */
745143731Sdougb	switch (key->key_alg) {
746143731Sdougb	case DST_ALG_RSAMD5:
747143731Sdougb	case DST_ALG_RSASHA1:
748143731Sdougb		*n = (key->key_size + 7) / 8;
749143731Sdougb		break;
750143731Sdougb	case DST_ALG_DSA:
751143731Sdougb		*n = DNS_SIG_DSASIGSIZE;
752143731Sdougb		break;
753143731Sdougb	case DST_ALG_HMACMD5:
754143731Sdougb		*n = 16;
755143731Sdougb		break;
756143731Sdougb	case DST_ALG_GSSAPI:
757143731Sdougb		*n = 128; /* XXX */
758143731Sdougb		break;
759143731Sdougb	case DST_ALG_DH:
760143731Sdougb	default:
761143731Sdougb		return (DST_R_UNSUPPORTEDALG);
762143731Sdougb	}
763143731Sdougb	return (ISC_R_SUCCESS);
764143731Sdougb}
765143731Sdougb
766143731Sdougbisc_result_t
767143731Sdougbdst_key_secretsize(const dst_key_t *key, unsigned int *n) {
768143731Sdougb	REQUIRE(dst_initialized == ISC_TRUE);
769143731Sdougb	REQUIRE(VALID_KEY(key));
770143731Sdougb	REQUIRE(n != NULL);
771143731Sdougb
772143731Sdougb	if (key->key_alg == DST_ALG_DH)
773143731Sdougb		*n = (key->key_size + 7) / 8;
774143731Sdougb	else
775143731Sdougb		return (DST_R_UNSUPPORTEDALG);
776143731Sdougb	return (ISC_R_SUCCESS);
777143731Sdougb}
778143731Sdougb
779143731Sdougb/***
780143731Sdougb *** Static methods
781143731Sdougb ***/
782143731Sdougb
783143731Sdougb/*
784143731Sdougb * Allocates a key structure and fills in some of the fields.
785143731Sdougb */
786143731Sdougbstatic dst_key_t *
787143731Sdougbget_key_struct(dns_name_t *name, unsigned int alg,
788143731Sdougb	       unsigned int flags, unsigned int protocol,
789143731Sdougb	       unsigned int bits, dns_rdataclass_t rdclass,
790143731Sdougb	       isc_mem_t *mctx)
791143731Sdougb{
792143731Sdougb	dst_key_t *key;
793143731Sdougb	isc_result_t result;
794143731Sdougb
795143731Sdougb	key = (dst_key_t *) isc_mem_get(mctx, sizeof(dst_key_t));
796143731Sdougb	if (key == NULL)
797143731Sdougb		return (NULL);
798143731Sdougb
799143731Sdougb	memset(key, 0, sizeof(dst_key_t));
800143731Sdougb	key->magic = KEY_MAGIC;
801143731Sdougb
802143731Sdougb	key->key_name = isc_mem_get(mctx, sizeof(dns_name_t));
803143731Sdougb	if (key->key_name == NULL) {
804143731Sdougb		isc_mem_put(mctx, key, sizeof(dst_key_t));
805143731Sdougb		return (NULL);
806143731Sdougb	}
807143731Sdougb	dns_name_init(key->key_name, NULL);
808143731Sdougb	result = dns_name_dup(name, mctx, key->key_name);
809143731Sdougb	if (result != ISC_R_SUCCESS) {
810143731Sdougb		isc_mem_put(mctx, key->key_name, sizeof(dns_name_t));
811143731Sdougb		isc_mem_put(mctx, key, sizeof(dst_key_t));
812143731Sdougb		return (NULL);
813143731Sdougb	}
814143731Sdougb	key->key_alg = alg;
815143731Sdougb	key->key_flags = flags;
816143731Sdougb	key->key_proto = protocol;
817143731Sdougb	key->mctx = mctx;
818143731Sdougb	key->opaque = NULL;
819143731Sdougb	key->key_size = bits;
820143731Sdougb	key->key_class = rdclass;
821143731Sdougb	key->func = dst_t_func[alg];
822143731Sdougb	return (key);
823143731Sdougb}
824143731Sdougb
825143731Sdougb/*
826143731Sdougb * Reads a public key from disk
827143731Sdougb */
828143731Sdougbstatic isc_result_t
829143731Sdougbread_public_key(const char *filename, int type,
830143731Sdougb		isc_mem_t *mctx, dst_key_t **keyp)
831143731Sdougb{
832143731Sdougb	u_char rdatabuf[DST_KEY_MAXSIZE];
833143731Sdougb	isc_buffer_t b;
834143731Sdougb	dns_fixedname_t name;
835143731Sdougb	isc_lex_t *lex = NULL;
836143731Sdougb	isc_token_t token;
837143731Sdougb	isc_result_t ret;
838143731Sdougb	dns_rdata_t rdata = DNS_RDATA_INIT;
839143731Sdougb	unsigned int opt = ISC_LEXOPT_DNSMULTILINE;
840143731Sdougb	char *newfilename;
841143731Sdougb	unsigned int newfilenamelen;
842143731Sdougb	dns_rdataclass_t rdclass = dns_rdataclass_in;
843143731Sdougb	isc_lexspecials_t specials;
844143731Sdougb	isc_uint32_t ttl;
845143731Sdougb	isc_result_t result;
846143731Sdougb	dns_rdatatype_t keytype;
847143731Sdougb
848143731Sdougb	newfilenamelen = strlen(filename) + 5;
849143731Sdougb	newfilename = isc_mem_get(mctx, newfilenamelen);
850143731Sdougb	if (newfilename == NULL)
851143731Sdougb		return (ISC_R_NOMEMORY);
852143731Sdougb	ret = addsuffix(newfilename, newfilenamelen, filename, ".key");
853143731Sdougb	INSIST(ret == ISC_R_SUCCESS);
854143731Sdougb
855143731Sdougb	/*
856143731Sdougb	 * Open the file and read its formatted contents
857143731Sdougb	 * File format:
858143731Sdougb	 *    domain.name [ttl] [class] KEY <flags> <protocol> <algorithm> <key>
859143731Sdougb	 */
860143731Sdougb
861143731Sdougb	/* 1500 should be large enough for any key */
862143731Sdougb	ret = isc_lex_create(mctx, 1500, &lex);
863143731Sdougb	if (ret != ISC_R_SUCCESS)
864143731Sdougb		goto cleanup;
865143731Sdougb
866143731Sdougb	memset(specials, 0, sizeof(specials));
867143731Sdougb	specials['('] = 1;
868143731Sdougb	specials[')'] = 1;
869143731Sdougb	specials['"'] = 1;
870143731Sdougb	isc_lex_setspecials(lex, specials);
871143731Sdougb	isc_lex_setcomments(lex, ISC_LEXCOMMENT_DNSMASTERFILE);
872143731Sdougb
873143731Sdougb	ret = isc_lex_openfile(lex, newfilename);
874143731Sdougb	if (ret != ISC_R_SUCCESS)
875143731Sdougb		goto cleanup;
876143731Sdougb
877143731Sdougb#define NEXTTOKEN(lex, opt, token) { \
878143731Sdougb	ret = isc_lex_gettoken(lex, opt, token); \
879143731Sdougb	if (ret != ISC_R_SUCCESS) \
880143731Sdougb		goto cleanup; \
881143731Sdougb	}
882143731Sdougb
883143731Sdougb#define BADTOKEN() { \
884143731Sdougb	ret = ISC_R_UNEXPECTEDTOKEN; \
885143731Sdougb	goto cleanup; \
886143731Sdougb	}
887143731Sdougb
888143731Sdougb	/* Read the domain name */
889143731Sdougb	NEXTTOKEN(lex, opt, &token);
890143731Sdougb	if (token.type != isc_tokentype_string)
891143731Sdougb		BADTOKEN();
892143731Sdougb	dns_fixedname_init(&name);
893143731Sdougb	isc_buffer_init(&b, DST_AS_STR(token), strlen(DST_AS_STR(token)));
894143731Sdougb	isc_buffer_add(&b, strlen(DST_AS_STR(token)));
895143731Sdougb	ret = dns_name_fromtext(dns_fixedname_name(&name), &b, dns_rootname,
896143731Sdougb				ISC_FALSE, NULL);
897143731Sdougb	if (ret != ISC_R_SUCCESS)
898143731Sdougb		goto cleanup;
899143731Sdougb
900143731Sdougb	/* Read the next word: either TTL, class, or 'KEY' */
901143731Sdougb	NEXTTOKEN(lex, opt, &token);
902143731Sdougb
903143731Sdougb	/* If it's a TTL, read the next one */
904143731Sdougb	result = dns_ttl_fromtext(&token.value.as_textregion, &ttl);
905143731Sdougb	if (result == ISC_R_SUCCESS)
906143731Sdougb		NEXTTOKEN(lex, opt, &token);
907143731Sdougb
908143731Sdougb	if (token.type != isc_tokentype_string)
909143731Sdougb		BADTOKEN();
910143731Sdougb
911143731Sdougb	ret = dns_rdataclass_fromtext(&rdclass, &token.value.as_textregion);
912143731Sdougb	if (ret == ISC_R_SUCCESS)
913143731Sdougb		NEXTTOKEN(lex, opt, &token);
914143731Sdougb
915143731Sdougb	if (token.type != isc_tokentype_string)
916143731Sdougb		BADTOKEN();
917143731Sdougb
918143731Sdougb	if (strcasecmp(DST_AS_STR(token), "DNSKEY") == 0)
919143731Sdougb		keytype = dns_rdatatype_dnskey;
920143731Sdougb	else if (strcasecmp(DST_AS_STR(token), "KEY") == 0)
921143731Sdougb		keytype = dns_rdatatype_key; /* SIG(0), TKEY */
922143731Sdougb	else
923143731Sdougb		BADTOKEN();
924143731Sdougb
925143731Sdougb	if (((type & DST_TYPE_KEY) != 0 && keytype != dns_rdatatype_key) ||
926143731Sdougb	    ((type & DST_TYPE_KEY) == 0 && keytype != dns_rdatatype_dnskey)) {
927143731Sdougb		ret = DST_R_BADKEYTYPE;
928143731Sdougb		goto cleanup;
929143731Sdougb	}
930143731Sdougb
931143731Sdougb	isc_buffer_init(&b, rdatabuf, sizeof(rdatabuf));
932143731Sdougb	ret = dns_rdata_fromtext(&rdata, rdclass, keytype, lex, NULL,
933143731Sdougb				 ISC_FALSE, mctx, &b, NULL);
934143731Sdougb	if (ret != ISC_R_SUCCESS)
935143731Sdougb		goto cleanup;
936143731Sdougb
937143731Sdougb	ret = dst_key_fromdns(dns_fixedname_name(&name), rdclass, &b, mctx,
938143731Sdougb			      keyp);
939143731Sdougb	if (ret != ISC_R_SUCCESS)
940143731Sdougb		goto cleanup;
941143731Sdougb
942143731Sdougb cleanup:
943143731Sdougb	if (lex != NULL)
944143731Sdougb		isc_lex_destroy(&lex);
945143731Sdougb	isc_mem_put(mctx, newfilename, newfilenamelen);
946143731Sdougb
947143731Sdougb	return (ret);
948143731Sdougb}
949143731Sdougb
950143731Sdougbstatic isc_boolean_t
951143731Sdougbissymmetric(const dst_key_t *key) {
952143731Sdougb	REQUIRE(dst_initialized == ISC_TRUE);
953143731Sdougb	REQUIRE(VALID_KEY(key));
954143731Sdougb
955143731Sdougb	/* XXXVIX this switch statement is too sparse to gen a jump table. */
956143731Sdougb	switch (key->key_alg) {
957143731Sdougb	case DST_ALG_RSAMD5:
958143731Sdougb	case DST_ALG_RSASHA1:
959143731Sdougb	case DST_ALG_DSA:
960143731Sdougb	case DST_ALG_DH:
961143731Sdougb		return (ISC_FALSE);
962143731Sdougb	case DST_ALG_HMACMD5:
963143731Sdougb	case DST_ALG_GSSAPI:
964143731Sdougb		return (ISC_TRUE);
965143731Sdougb	default:
966143731Sdougb		return (ISC_FALSE);
967143731Sdougb	}
968143731Sdougb}
969143731Sdougb
970143731Sdougb/*
971143731Sdougb * Writes a public key to disk in DNS format.
972143731Sdougb */
973143731Sdougbstatic isc_result_t
974143731Sdougbwrite_public_key(const dst_key_t *key, int type, const char *directory) {
975143731Sdougb	FILE *fp;
976143731Sdougb	isc_buffer_t keyb, textb, fileb, classb;
977143731Sdougb	isc_region_t r;
978143731Sdougb	char filename[ISC_DIR_NAMEMAX];
979143731Sdougb	unsigned char key_array[DST_KEY_MAXSIZE];
980143731Sdougb	char text_array[DST_KEY_MAXTEXTSIZE];
981143731Sdougb	char class_array[10];
982143731Sdougb	isc_result_t ret;
983143731Sdougb	dns_rdata_t rdata = DNS_RDATA_INIT;
984143731Sdougb	isc_fsaccess_t access;
985143731Sdougb
986143731Sdougb	REQUIRE(VALID_KEY(key));
987143731Sdougb
988143731Sdougb	isc_buffer_init(&keyb, key_array, sizeof(key_array));
989143731Sdougb	isc_buffer_init(&textb, text_array, sizeof(text_array));
990143731Sdougb	isc_buffer_init(&classb, class_array, sizeof(class_array));
991143731Sdougb
992143731Sdougb	ret = dst_key_todns(key, &keyb);
993143731Sdougb	if (ret != ISC_R_SUCCESS)
994143731Sdougb		return (ret);
995143731Sdougb
996143731Sdougb	isc_buffer_usedregion(&keyb, &r);
997143731Sdougb	dns_rdata_fromregion(&rdata, key->key_class, dns_rdatatype_dnskey, &r);
998143731Sdougb
999143731Sdougb	ret = dns_rdata_totext(&rdata, (dns_name_t *) NULL, &textb);
1000143731Sdougb	if (ret != ISC_R_SUCCESS)
1001143731Sdougb		return (DST_R_INVALIDPUBLICKEY);
1002143731Sdougb
1003143731Sdougb	ret = dns_rdataclass_totext(key->key_class, &classb);
1004143731Sdougb	if (ret != ISC_R_SUCCESS)
1005143731Sdougb		return (DST_R_INVALIDPUBLICKEY);
1006143731Sdougb
1007143731Sdougb	/*
1008143731Sdougb	 * Make the filename.
1009143731Sdougb	 */
1010143731Sdougb	isc_buffer_init(&fileb, filename, sizeof(filename));
1011143731Sdougb	ret = dst_key_buildfilename(key, DST_TYPE_PUBLIC, directory, &fileb);
1012143731Sdougb	if (ret != ISC_R_SUCCESS)
1013143731Sdougb		return (ret);
1014143731Sdougb
1015143731Sdougb	/*
1016143731Sdougb	 * Create public key file.
1017143731Sdougb	 */
1018143731Sdougb	if ((fp = fopen(filename, "w")) == NULL)
1019143731Sdougb		return (DST_R_WRITEERROR);
1020143731Sdougb
1021143731Sdougb	if (issymmetric(key)) {
1022143731Sdougb		access = 0;
1023143731Sdougb		isc_fsaccess_add(ISC_FSACCESS_OWNER,
1024143731Sdougb				 ISC_FSACCESS_READ | ISC_FSACCESS_WRITE,
1025143731Sdougb				 &access);
1026143731Sdougb		(void)isc_fsaccess_set(filename, access);
1027143731Sdougb	}
1028143731Sdougb
1029143731Sdougb	ret = dns_name_print(key->key_name, fp);
1030165071Sdougb	if (ret != ISC_R_SUCCESS) {
1031165071Sdougb		fclose(fp);
1032143731Sdougb		return (ret);
1033165071Sdougb	}
1034143731Sdougb
1035143731Sdougb	fprintf(fp, " ");
1036143731Sdougb
1037143731Sdougb	isc_buffer_usedregion(&classb, &r);
1038143731Sdougb	fwrite(r.base, 1, r.length, fp);
1039143731Sdougb
1040143731Sdougb	if ((type & DST_TYPE_KEY) != 0)
1041143731Sdougb		fprintf(fp, " KEY ");
1042143731Sdougb	else
1043143731Sdougb		fprintf(fp, " DNSKEY ");
1044143731Sdougb
1045143731Sdougb	isc_buffer_usedregion(&textb, &r);
1046143731Sdougb	fwrite(r.base, 1, r.length, fp);
1047143731Sdougb
1048143731Sdougb	fputc('\n', fp);
1049143731Sdougb	fclose(fp);
1050143731Sdougb
1051143731Sdougb	return (ISC_R_SUCCESS);
1052143731Sdougb}
1053143731Sdougb
1054143731Sdougbstatic isc_result_t
1055143731Sdougbbuildfilename(dns_name_t *name, dns_keytag_t id,
1056143731Sdougb	      unsigned int alg, unsigned int type,
1057143731Sdougb	      const char *directory, isc_buffer_t *out)
1058143731Sdougb{
1059143731Sdougb	const char *suffix = "";
1060143731Sdougb	unsigned int len;
1061143731Sdougb	isc_result_t result;
1062143731Sdougb
1063143731Sdougb	REQUIRE(out != NULL);
1064143731Sdougb	if ((type & DST_TYPE_PRIVATE) != 0)
1065143731Sdougb		suffix = ".private";
1066143731Sdougb	else if (type == DST_TYPE_PUBLIC)
1067143731Sdougb		suffix = ".key";
1068143731Sdougb	if (directory != NULL) {
1069143731Sdougb		if (isc_buffer_availablelength(out) < strlen(directory))
1070143731Sdougb			return (ISC_R_NOSPACE);
1071143731Sdougb		isc_buffer_putstr(out, directory);
1072143731Sdougb		if (strlen(directory) > 0U &&
1073143731Sdougb		    directory[strlen(directory) - 1] != '/')
1074143731Sdougb			isc_buffer_putstr(out, "/");
1075143731Sdougb	}
1076143731Sdougb	if (isc_buffer_availablelength(out) < 1)
1077143731Sdougb		return (ISC_R_NOSPACE);
1078143731Sdougb	isc_buffer_putstr(out, "K");
1079143731Sdougb	result = dns_name_tofilenametext(name, ISC_FALSE, out);
1080143731Sdougb	if (result != ISC_R_SUCCESS)
1081143731Sdougb		return (result);
1082143731Sdougb	len = 1 + 3 + 1 + 5 + strlen(suffix) + 1;
1083143731Sdougb	if (isc_buffer_availablelength(out) < len)
1084143731Sdougb		return (ISC_R_NOSPACE);
1085143731Sdougb	sprintf((char *) isc_buffer_used(out), "+%03d+%05d%s", alg, id, suffix);
1086143731Sdougb	isc_buffer_add(out, len);
1087143731Sdougb	return (ISC_R_SUCCESS);
1088143731Sdougb}
1089143731Sdougb
1090143731Sdougbstatic isc_result_t
1091143731Sdougbcomputeid(dst_key_t *key) {
1092143731Sdougb	isc_buffer_t dnsbuf;
1093143731Sdougb	unsigned char dns_array[DST_KEY_MAXSIZE];
1094143731Sdougb	isc_region_t r;
1095143731Sdougb	isc_result_t ret;
1096143731Sdougb
1097143731Sdougb	isc_buffer_init(&dnsbuf, dns_array, sizeof(dns_array));
1098143731Sdougb	ret = dst_key_todns(key, &dnsbuf);
1099143731Sdougb	if (ret != ISC_R_SUCCESS)
1100143731Sdougb		return (ret);
1101143731Sdougb
1102143731Sdougb	isc_buffer_usedregion(&dnsbuf, &r);
1103143731Sdougb	key->key_id = dst_region_computeid(&r, key->key_alg);
1104143731Sdougb	return (ISC_R_SUCCESS);
1105143731Sdougb}
1106143731Sdougb
1107143731Sdougbstatic isc_result_t
1108143731Sdougbfrombuffer(dns_name_t *name, unsigned int alg, unsigned int flags,
1109143731Sdougb	   unsigned int protocol, dns_rdataclass_t rdclass,
1110143731Sdougb	   isc_buffer_t *source, isc_mem_t *mctx, dst_key_t **keyp)
1111143731Sdougb{
1112143731Sdougb	dst_key_t *key;
1113143731Sdougb	isc_result_t ret;
1114143731Sdougb
1115143731Sdougb	REQUIRE(dns_name_isabsolute(name));
1116143731Sdougb	REQUIRE(source != NULL);
1117143731Sdougb	REQUIRE(mctx != NULL);
1118143731Sdougb	REQUIRE(keyp != NULL && *keyp == NULL);
1119143731Sdougb
1120143731Sdougb	key = get_key_struct(name, alg, flags, protocol, 0, rdclass, mctx);
1121143731Sdougb	if (key == NULL)
1122143731Sdougb		return (ISC_R_NOMEMORY);
1123143731Sdougb
1124143731Sdougb	if (isc_buffer_remaininglength(source) > 0) {
1125143731Sdougb		ret = algorithm_status(alg);
1126143731Sdougb		if (ret != ISC_R_SUCCESS) {
1127143731Sdougb			dst_key_free(&key);
1128143731Sdougb			return (ret);
1129143731Sdougb		}
1130143731Sdougb		if (key->func->fromdns == NULL) {
1131143731Sdougb			dst_key_free(&key);
1132143731Sdougb			return (DST_R_UNSUPPORTEDALG);
1133143731Sdougb		}
1134143731Sdougb
1135143731Sdougb		ret = key->func->fromdns(key, source);
1136143731Sdougb		if (ret != ISC_R_SUCCESS) {
1137143731Sdougb			dst_key_free(&key);
1138143731Sdougb			return (ret);
1139143731Sdougb		}
1140143731Sdougb	}
1141143731Sdougb
1142143731Sdougb	*keyp = key;
1143143731Sdougb	return (ISC_R_SUCCESS);
1144143731Sdougb}
1145143731Sdougb
1146143731Sdougbstatic isc_result_t
1147143731Sdougbalgorithm_status(unsigned int alg) {
1148143731Sdougb	REQUIRE(dst_initialized == ISC_TRUE);
1149143731Sdougb
1150143731Sdougb	if (dst_algorithm_supported(alg))
1151143731Sdougb		return (ISC_R_SUCCESS);
1152143731Sdougb#ifndef OPENSSL
1153143731Sdougb	if (alg == DST_ALG_RSAMD5 || alg == DST_ALG_RSASHA1 ||
1154143731Sdougb	    alg == DST_ALG_DSA || alg == DST_ALG_DH ||
1155143731Sdougb	    alg == DST_ALG_HMACMD5)
1156143731Sdougb		return (DST_R_NOCRYPTO);
1157143731Sdougb#endif
1158143731Sdougb	return (DST_R_UNSUPPORTEDALG);
1159143731Sdougb}
1160143731Sdougb
1161143731Sdougbstatic isc_result_t
1162143731Sdougbaddsuffix(char *filename, unsigned int len, const char *ofilename,
1163143731Sdougb	  const char *suffix)
1164143731Sdougb{
1165143731Sdougb	int olen = strlen(ofilename);
1166143731Sdougb	int n;
1167143731Sdougb
1168143731Sdougb	if (olen > 1 && ofilename[olen - 1] == '.')
1169143731Sdougb		olen -= 1;
1170143731Sdougb	else if (olen > 8 && strcmp(ofilename + olen - 8, ".private") == 0)
1171143731Sdougb		olen -= 8;
1172143731Sdougb	else if (olen > 4 && strcmp(ofilename + olen - 4, ".key") == 0)
1173143731Sdougb		olen -= 4;
1174143731Sdougb
1175143731Sdougb	n = snprintf(filename, len, "%.*s%s", olen, ofilename, suffix);
1176143731Sdougb	if (n < 0)
1177143731Sdougb		return (ISC_R_NOSPACE);
1178143731Sdougb	return (ISC_R_SUCCESS);
1179143731Sdougb}
1180143731Sdougb
1181143731Sdougbisc_result_t
1182143731Sdougbdst__entropy_getdata(void *buf, unsigned int len, isc_boolean_t pseudo) {
1183143731Sdougb	unsigned int flags = dst_entropy_flags;
1184143731Sdougb	if (pseudo)
1185143731Sdougb		flags &= ~ISC_ENTROPY_GOODONLY;
1186143731Sdougb	return (isc_entropy_getdata(dst_entropy_pool, buf, len, NULL, flags));
1187143731Sdougb}
1188