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