dst_api.c 44.4 KB
Newer Older
1
/*
Evan Hunt's avatar
Evan Hunt committed
2
 * Portions Copyright (C) Internet Systems Consortium, Inc. ("ISC")
Automatic Updater's avatar
Automatic Updater committed
3
 *
4 5 6
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
Automatic Updater's avatar
Automatic Updater committed
7
 *
8 9 10 11
 * See the COPYRIGHT file distributed with this work for additional
 * information regarding copyright ownership.
 *
 * Portions Copyright (C) Network Associates, Inc.
12
 *
Automatic Updater's avatar
Automatic Updater committed
13
 * Permission to use, copy, modify, and/or distribute this software for any
14 15
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
16
 *
Mark Andrews's avatar
Mark Andrews committed
17 18 19 20 21 22 23
 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC AND NETWORK ASSOCIATES DISCLAIMS
 * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE
 * FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
 * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
24 25
 */

26 27
/*! \file */

28 29
#include <config.h>

30
#include <inttypes.h>
31
#include <stdbool.h>
Brian Wellington's avatar
Brian Wellington committed
32
#include <stdlib.h>
33
#include <time.h>
Brian Wellington's avatar
Brian Wellington committed
34

35
#include <isc/buffer.h>
36
#include <isc/dir.h>
37
#include <isc/fsaccess.h>
38 39 40
#include <isc/lex.h>
#include <isc/mem.h>
#include <isc/once.h>
41
#include <isc/platform.h>
42
#include <isc/print.h>
43
#include <isc/refcount.h>
44
#include <isc/random.h>
45
#include <isc/safe.h>
46
#include <isc/string.h>
47
#include <isc/time.h>
Bob Halley's avatar
Bob Halley committed
48
#include <isc/util.h>
Curtis Blackburn's avatar
Curtis Blackburn committed
49
#include <isc/file.h>
Bob Halley's avatar
Bob Halley committed
50

51 52
#include <pk11/site.h>

53 54
#define DST_KEY_INTERNAL

Brian Wellington's avatar
Brian Wellington committed
55
#include <dns/fixedname.h>
56
#include <dns/keyvalues.h>
57
#include <dns/name.h>
58
#include <dns/rdata.h>
Brian Wellington's avatar
Brian Wellington committed
59
#include <dns/rdataclass.h>
60
#include <dns/ttl.h>
61
#include <dns/types.h>
62

63 64
#include <dst/result.h>

65 66
#include "dst_internal.h"

67 68
#define DST_AS_STR(t) ((t).value.as_textregion.base)

Brian Wellington's avatar
Brian Wellington committed
69
static dst_func_t *dst_t_func[DST_MAX_ALGS];
Evan Hunt's avatar
Evan Hunt committed
70

71
static bool dst_initialized = false;
72

73 74
void gss_log(int level, const char *fmt, ...) ISC_FORMAT_PRINTF(2, 3);

75
LIBDNS_EXTERNAL_DATA isc_mem_t *dst__memory_pool = NULL;
76

77 78 79
/*
 * Static functions.
 */
80
static dst_key_t *	get_key_struct(const dns_name_t *name,
Brian Wellington's avatar
Brian Wellington committed
81 82 83 84 85
				       unsigned int alg,
				       unsigned int flags,
				       unsigned int protocol,
				       unsigned int bits,
				       dns_rdataclass_t rdclass,
86
				       dns_ttl_t ttl,
87
				       isc_mem_t *mctx);
88
static isc_result_t	write_public_key(const dst_key_t *key, int type,
Brian Wellington's avatar
Brian Wellington committed
89 90
					 const char *directory);
static isc_result_t	buildfilename(dns_name_t *name,
Brian Wellington's avatar
Brian Wellington committed
91 92 93
				      dns_keytag_t id,
				      unsigned int alg,
				      unsigned int type,
Brian Wellington's avatar
Brian Wellington committed
94 95
				      const char *directory,
				      isc_buffer_t *out);
96
static isc_result_t	computeid(dst_key_t *key);
97
static isc_result_t	frombuffer(const dns_name_t *name,
Brian Wellington's avatar
Brian Wellington committed
98 99 100
				   unsigned int alg,
				   unsigned int flags,
				   unsigned int protocol,
101 102 103 104
				   dns_rdataclass_t rdclass,
				   isc_buffer_t *source,
				   isc_mem_t *mctx,
				   dst_key_t **keyp);
Brian Wellington's avatar
Brian Wellington committed
105

106 107
static isc_result_t	algorithm_status(unsigned int alg);

108
static isc_result_t	addsuffix(char *filename, int len,
109 110
				  const char *dirname, const char *ofilename,
				  const char *suffix);
111

112
#define RETERR(x)				\
Brian Wellington's avatar
Brian Wellington committed
113 114 115 116
	do {					\
		result = (x);			\
		if (result != ISC_R_SUCCESS)	\
			goto out;		\
Brian Wellington's avatar
Brian Wellington committed
117 118
	} while (0)

119 120 121 122 123 124 125 126
#define CHECKALG(alg)				\
	do {					\
		isc_result_t _r;		\
		_r = algorithm_status(alg);	\
		if (_r != ISC_R_SUCCESS)	\
			return (_r);		\
	} while (0);				\

127 128
static void *
default_memalloc(void *arg, size_t size) {
129 130 131 132
	UNUSED(arg);
	if (size == 0U)
		size = 1;
	return (malloc(size));
133 134 135 136
}

static void
default_memfree(void *arg, void *ptr) {
137 138
	UNUSED(arg);
	free(ptr);
139 140
}

Brian Wellington's avatar
Brian Wellington committed
141
isc_result_t
142
dst_lib_init(isc_mem_t *mctx, const char *engine) {
Brian Wellington's avatar
Brian Wellington committed
143 144
	isc_result_t result;

145
	REQUIRE(mctx != NULL);
146
	REQUIRE(dst_initialized == false);
Brian Wellington's avatar
Brian Wellington committed
147

148 149
	UNUSED(engine);

150
	dst__memory_pool = NULL;
Mark Andrews's avatar
Mark Andrews committed
151 152 153 154 155 156

	UNUSED(mctx);
	/*
	 * When using --with-openssl, there seems to be no good way of not
	 * leaking memory due to the openssl error handling mechanism.
	 * Avoid assertions by using a local memory context and not checking
157 158 159
	 * for leaks on exit.  Note: as there are leaks we cannot use
	 * ISC_MEMFLAG_INTERNAL as it will free up memory still being used
	 * by libcrypto.
Mark Andrews's avatar
Mark Andrews committed
160
	 */
161 162
	result = isc_mem_createx(0, 0, default_memalloc, default_memfree,
				 NULL, &dst__memory_pool, 0);
Mark Andrews's avatar
Mark Andrews committed
163 164
	if (result != ISC_R_SUCCESS)
		return (result);
165
	isc_mem_setname(dst__memory_pool, "dst", NULL);
Francis Dupont's avatar
Francis Dupont committed
166
#ifndef OPENSSL_LEAKS
167
	isc_mem_setdestroycheck(dst__memory_pool, false);
Francis Dupont's avatar
Francis Dupont committed
168
#endif
Brian Wellington's avatar
Brian Wellington committed
169 170 171 172 173

	dst_result_register();

	memset(dst_t_func, 0, sizeof(dst_t_func));
	RETERR(dst__hmacmd5_init(&dst_t_func[DST_ALG_HMACMD5]));
174 175 176 177 178
	RETERR(dst__hmacsha1_init(&dst_t_func[DST_ALG_HMACSHA1]));
	RETERR(dst__hmacsha224_init(&dst_t_func[DST_ALG_HMACSHA224]));
	RETERR(dst__hmacsha256_init(&dst_t_func[DST_ALG_HMACSHA256]));
	RETERR(dst__hmacsha384_init(&dst_t_func[DST_ALG_HMACSHA384]));
	RETERR(dst__hmacsha512_init(&dst_t_func[DST_ALG_HMACSHA512]));
Francis Dupont's avatar
Francis Dupont committed
179
	RETERR(dst__openssl_init(engine));
180
	RETERR(dst__openssldh_init(&dst_t_func[DST_ALG_DH]));
Ondřej Surý's avatar
Ondřej Surý committed
181
#if USE_OPENSSL
182 183 184 185 186 187 188 189 190 191
	RETERR(dst__opensslrsa_init(&dst_t_func[DST_ALG_RSAMD5],
				    DST_ALG_RSAMD5));
	RETERR(dst__opensslrsa_init(&dst_t_func[DST_ALG_RSASHA1],
				    DST_ALG_RSASHA1));
	RETERR(dst__opensslrsa_init(&dst_t_func[DST_ALG_NSEC3RSASHA1],
				    DST_ALG_NSEC3RSASHA1));
	RETERR(dst__opensslrsa_init(&dst_t_func[DST_ALG_RSASHA256],
				    DST_ALG_RSASHA256));
	RETERR(dst__opensslrsa_init(&dst_t_func[DST_ALG_RSASHA512],
				    DST_ALG_RSASHA512));
192 193
	RETERR(dst__opensslecdsa_init(&dst_t_func[DST_ALG_ECDSA256]));
	RETERR(dst__opensslecdsa_init(&dst_t_func[DST_ALG_ECDSA384]));
194 195 196 197 198 199
#ifdef HAVE_OPENSSL_ED25519
	RETERR(dst__openssleddsa_init(&dst_t_func[DST_ALG_ED25519]));
#endif
#ifdef HAVE_OPENSSL_ED448
	RETERR(dst__openssleddsa_init(&dst_t_func[DST_ALG_ED448]));
#endif
Ondřej Surý's avatar
Ondřej Surý committed
200 201 202
#endif /* USE_OPENSSL */

#if USE_PKCS11
203
	RETERR(dst__pkcs11_init(mctx, engine));
Evan Hunt's avatar
Evan Hunt committed
204 205 206 207 208 209 210
	RETERR(dst__pkcs11rsa_init(&dst_t_func[DST_ALG_RSAMD5]));
	RETERR(dst__pkcs11rsa_init(&dst_t_func[DST_ALG_RSASHA1]));
	RETERR(dst__pkcs11rsa_init(&dst_t_func[DST_ALG_NSEC3RSASHA1]));
	RETERR(dst__pkcs11rsa_init(&dst_t_func[DST_ALG_RSASHA256]));
	RETERR(dst__pkcs11rsa_init(&dst_t_func[DST_ALG_RSASHA512]));
	RETERR(dst__pkcs11ecdsa_init(&dst_t_func[DST_ALG_ECDSA256]));
	RETERR(dst__pkcs11ecdsa_init(&dst_t_func[DST_ALG_ECDSA384]));
211 212 213 214 215 216
#ifdef HAVE_PKCS11_ED25519
	RETERR(dst__pkcs11eddsa_init(&dst_t_func[DST_ALG_ED25519]));
#endif
#ifdef HAVE_PKCS11_ED448
	RETERR(dst__pkcs11eddsa_init(&dst_t_func[DST_ALG_ED448]));
#endif
Ondřej Surý's avatar
Ondřej Surý committed
217
#endif /* USE_PKCS11 */
Mark Andrews's avatar
Mark Andrews committed
218 219 220
#ifdef GSSAPI
	RETERR(dst__gssapi_init(&dst_t_func[DST_ALG_GSSAPI]));
#endif
221

222
	dst_initialized = true;
Brian Wellington's avatar
Brian Wellington committed
223 224 225
	return (ISC_R_SUCCESS);

 out:
Francis Dupont's avatar
Francis Dupont committed
226
	/* avoid immediate crash! */
227
	dst_initialized = true;
Brian Wellington's avatar
Brian Wellington committed
228 229
	dst_lib_destroy();
	return (result);
Brian Wellington's avatar
Brian Wellington committed
230 231 232
}

void
233
dst_lib_destroy(void) {
234
	int i;
235 236
	RUNTIME_CHECK(dst_initialized == true);
	dst_initialized = false;
Brian Wellington's avatar
Brian Wellington committed
237

238 239 240
	for (i = 0; i < DST_MAX_ALGS; i++)
		if (dst_t_func[i] != NULL && dst_t_func[i]->cleanup != NULL)
			dst_t_func[i]->cleanup();
241
	dst__openssl_destroy();
Ondřej Surý's avatar
Ondřej Surý committed
242
#if USE_PKCS11
Evan Hunt's avatar
Evan Hunt committed
243
	(void) dst__pkcs11_destroy();
Ondřej Surý's avatar
Ondřej Surý committed
244
#endif /* USE_PKCS11 */
245 246
	if (dst__memory_pool != NULL)
		isc_mem_detach(&dst__memory_pool);
Brian Wellington's avatar
Brian Wellington committed
247
}
248

249
bool
Brian Wellington's avatar
Brian Wellington committed
250
dst_algorithm_supported(unsigned int alg) {
251
	REQUIRE(dst_initialized == true);
Brian Wellington's avatar
Brian Wellington committed
252

253
	if (alg >= DST_MAX_ALGS || dst_t_func[alg] == NULL)
254 255
		return (false);
	return (true);
256 257
}

258
bool
259
dst_ds_digest_supported(unsigned int digest_type) {
260 261 262
	return  (digest_type == DNS_DSDIGEST_SHA1 ||
		 digest_type == DNS_DSDIGEST_SHA256 ||
		 digest_type == DNS_DSDIGEST_SHA384);
263 264
}

265
isc_result_t
266
dst_context_create(dst_key_t *key, isc_mem_t *mctx,
267
		   isc_logcategory_t *category, bool useforsigning,
268
		   int maxbits, dst_context_t **dctxp)
Evan Hunt's avatar
Evan Hunt committed
269
{
270 271 272
	dst_context_t *dctx;
	isc_result_t result;

273
	REQUIRE(dst_initialized == true);
274
	REQUIRE(VALID_KEY(key));
275 276
	REQUIRE(mctx != NULL);
	REQUIRE(dctxp != NULL && *dctxp == NULL);
277

Evan Hunt's avatar
Evan Hunt committed
278 279
	if (key->func->createctx == NULL &&
	    key->func->createctx2 == NULL)
Brian Wellington's avatar
Brian Wellington committed
280
		return (DST_R_UNSUPPORTEDALG);
281
	if (key->keydata.generic == NULL)
282
		return (DST_R_NULLKEY);
Brian Wellington's avatar
Brian Wellington committed
283

284 285 286
	dctx = isc_mem_get(mctx, sizeof(dst_context_t));
	if (dctx == NULL)
		return (ISC_R_NOMEMORY);
287 288 289
	memset(dctx, 0, sizeof(*dctx));
	dst_key_attach(key, &dctx->key);
	isc_mem_attach(mctx, &dctx->mctx);
290
	dctx->category = category;
Evan Hunt's avatar
Evan Hunt committed
291 292 293 294 295 296 297 298
	if (useforsigning)
		dctx->use = DO_SIGN;
	else
		dctx->use = DO_VERIFY;
	if (key->func->createctx2 != NULL)
		result = key->func->createctx2(key, maxbits, dctx);
	else
		result = key->func->createctx(key, dctx);
299
	if (result != ISC_R_SUCCESS) {
300 301 302
		if (dctx->key != NULL)
			dst_key_free(&dctx->key);
		isc_mem_putanddetach(&dctx->mctx, dctx, sizeof(dst_context_t));
303 304 305 306 307 308
		return (result);
	}
	dctx->magic = CTX_MAGIC;
	*dctxp = dctx;
	return (ISC_R_SUCCESS);
}
309

310 311 312
void
dst_context_destroy(dst_context_t **dctxp) {
	dst_context_t *dctx;
313

314
	REQUIRE(dctxp != NULL && VALID_CTX(*dctxp));
315

316
	dctx = *dctxp;
Brian Wellington's avatar
Brian Wellington committed
317
	INSIST(dctx->key->func->destroyctx != NULL);
318
	dctx->key->func->destroyctx(dctx);
319 320
	if (dctx->key != NULL)
		dst_key_free(&dctx->key);
321
	dctx->magic = 0;
322
	isc_mem_putanddetach(&dctx->mctx, dctx, sizeof(dst_context_t));
323
	*dctxp = NULL;
324 325
}

326 327 328 329
isc_result_t
dst_context_adddata(dst_context_t *dctx, const isc_region_t *data) {
	REQUIRE(VALID_CTX(dctx));
	REQUIRE(data != NULL);
Brian Wellington's avatar
Brian Wellington committed
330
	INSIST(dctx->key->func->adddata != NULL);
331

332 333
	return (dctx->key->func->adddata(dctx, data));
}
334

335
isc_result_t
336
dst_context_sign(dst_context_t *dctx, isc_buffer_t *sig) {
337 338
	dst_key_t *key;

339 340
	REQUIRE(VALID_CTX(dctx));
	REQUIRE(sig != NULL);
341

342 343
	key = dctx->key;
	CHECKALG(key->key_alg);
344
	if (key->keydata.generic == NULL)
345
		return (DST_R_NULLKEY);
Francis Dupont's avatar
Francis Dupont committed
346

347 348 349
	if (key->func->sign == NULL)
		return (DST_R_NOTPRIVATEKEY);
	if (key->func->isprivate == NULL ||
350
	    key->func->isprivate(key) == false)
351
		return (DST_R_NOTPRIVATEKEY);
352

353
	return (key->func->sign(dctx, sig));
354
}
355

356 357 358 359 360
isc_result_t
dst_context_verify(dst_context_t *dctx, isc_region_t *sig) {
	REQUIRE(VALID_CTX(dctx));
	REQUIRE(sig != NULL);

361
	CHECKALG(dctx->key->key_alg);
362
	if (dctx->key->keydata.generic == NULL)
Brian Wellington's avatar
Brian Wellington committed
363
		return (DST_R_NULLKEY);
364
	if (dctx->key->func->verify == NULL)
365
		return (DST_R_NOTPUBLICKEY);
366

367
	return (dctx->key->func->verify(dctx, sig));
368 369
}

370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388
isc_result_t
dst_context_verify2(dst_context_t *dctx, unsigned int maxbits,
		    isc_region_t *sig)
{
	REQUIRE(VALID_CTX(dctx));
	REQUIRE(sig != NULL);

	CHECKALG(dctx->key->key_alg);
	if (dctx->key->keydata.generic == NULL)
		return (DST_R_NULLKEY);
	if (dctx->key->func->verify == NULL &&
	    dctx->key->func->verify2 == NULL)
		return (DST_R_NOTPUBLICKEY);

	return (dctx->key->func->verify2 != NULL ?
		dctx->key->func->verify2(dctx, maxbits, sig) :
		dctx->key->func->verify(dctx, sig));
}

389
isc_result_t
Brian Wellington's avatar
Brian Wellington committed
390
dst_key_computesecret(const dst_key_t *pub, const dst_key_t *priv,
Brian Wellington's avatar
Brian Wellington committed
391
		      isc_buffer_t *secret)
392
{
393
	REQUIRE(dst_initialized == true);
394 395 396
	REQUIRE(VALID_KEY(pub) && VALID_KEY(priv));
	REQUIRE(secret != NULL);

397 398
	CHECKALG(pub->key_alg);
	CHECKALG(priv->key_alg);
399

400
	if (pub->keydata.generic == NULL || priv->keydata.generic == NULL)
401 402 403 404 405 406 407
		return (DST_R_NULLKEY);

	if (pub->key_alg != priv->key_alg ||
	    pub->func->computesecret == NULL ||
	    priv->func->computesecret == NULL)
		return (DST_R_KEYCANNOTCOMPUTESECRET);

408
	if (dst_key_isprivate(priv) == false)
409 410 411 412 413
		return (DST_R_NOTPRIVATEKEY);

	return (pub->func->computesecret(pub, priv, secret));
}

414
isc_result_t
Brian Wellington's avatar
Brian Wellington committed
415
dst_key_tofile(const dst_key_t *key, int type, const char *directory) {
416
	isc_result_t ret = ISC_R_SUCCESS;
417

418
	REQUIRE(dst_initialized == true);
419
	REQUIRE(VALID_KEY(key));
Brian Wellington's avatar
Brian Wellington committed
420
	REQUIRE((type & (DST_TYPE_PRIVATE | DST_TYPE_PUBLIC)) != 0);
421

422
	CHECKALG(key->key_alg);
423

Brian Wellington's avatar
Brian Wellington committed
424 425 426 427
	if (key->func->tofile == NULL)
		return (DST_R_UNSUPPORTEDALG);

	if (type & DST_TYPE_PUBLIC) {
428
		ret = write_public_key(key, type, directory);
429
		if (ret != ISC_R_SUCCESS)
430 431 432
			return (ret);
	}

Brian Wellington's avatar
typos  
Brian Wellington committed
433 434
	if ((type & DST_TYPE_PRIVATE) &&
	    (key->key_flags & DNS_KEYFLAG_TYPEMASK) != DNS_KEYTYPE_NOKEY)
Brian Wellington's avatar
Brian Wellington committed
435
		return (key->func->tofile(key, directory));
Brian Wellington's avatar
Brian Wellington committed
436 437
	else
		return (ISC_R_SUCCESS);
438 439
}

440
void
441
dst_key_setexternal(dst_key_t *key, bool value) {
442 443 444
	key->external = value;
}

445
bool
446 447 448 449
dst_key_isexternal(dst_key_t *key) {
	return (key->external);
}

450 451 452 453 454 455 456
isc_result_t
dst_key_getfilename(dns_name_t *name, dns_keytag_t id,
		    unsigned int alg, int type, const char *directory,
		    isc_mem_t *mctx, isc_buffer_t *buf)
{
	isc_result_t result;

457
	REQUIRE(dst_initialized == true);
458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475
	REQUIRE(dns_name_isabsolute(name));
	REQUIRE((type & (DST_TYPE_PRIVATE | DST_TYPE_PUBLIC)) != 0);
	REQUIRE(mctx != NULL);
	REQUIRE(buf != NULL);

	CHECKALG(alg);

	result = buildfilename(name, id, alg, type, directory, buf);
	if (result == ISC_R_SUCCESS) {
		if (isc_buffer_availablelength(buf) > 0)
			isc_buffer_putuint8(buf, 0);
		else
			result = ISC_R_NOSPACE;
	}

	return (result);
}

476
isc_result_t
Brian Wellington's avatar
Brian Wellington committed
477 478
dst_key_fromfile(dns_name_t *name, dns_keytag_t id,
		 unsigned int alg, int type, const char *directory,
Brian Wellington's avatar
Brian Wellington committed
479
		 isc_mem_t *mctx, dst_key_t **keyp)
480
{
481
	isc_result_t result;
Ondřej Surý's avatar
Ondřej Surý committed
482
	char filename[NAME_MAX];
483
	isc_buffer_t buf;
Brian Wellington's avatar
Brian Wellington committed
484
	dst_key_t *key;
485

486
	REQUIRE(dst_initialized == true);
487
	REQUIRE(dns_name_isabsolute(name));
Brian Wellington's avatar
Brian Wellington committed
488
	REQUIRE((type & (DST_TYPE_PRIVATE | DST_TYPE_PUBLIC)) != 0);
489
	REQUIRE(mctx != NULL);
490
	REQUIRE(keyp != NULL && *keyp == NULL);
491

492
	CHECKALG(alg);
493

494 495
	key = NULL;

Ondřej Surý's avatar
Ondřej Surý committed
496
	isc_buffer_init(&buf, filename, NAME_MAX);
497
	result = dst_key_getfilename(name, id, alg, type, NULL, mctx, &buf);
Brian Wellington's avatar
Brian Wellington committed
498
	if (result != ISC_R_SUCCESS)
499
		goto out;
500

501
	result = dst_key_fromnamedfile(filename, directory, type, mctx, &key);
Brian Wellington's avatar
Brian Wellington committed
502
	if (result != ISC_R_SUCCESS)
503
		goto out;
Brian Wellington's avatar
Brian Wellington committed
504

505
	result = computeid(key);
506 507
	if (result != ISC_R_SUCCESS)
		goto out;
508

509 510
	if (!dns_name_equal(name, key->key_name) || id != key->key_id ||
	    alg != key->key_alg) {
511 512
		result = DST_R_INVALIDPRIVATEKEY;
		goto out;
Brian Wellington's avatar
Brian Wellington committed
513
	}
514

Brian Wellington's avatar
Brian Wellington committed
515
	*keyp = key;
516 517 518 519 520 521 522
	result = ISC_R_SUCCESS;

 out:
	if ((key != NULL) && (result != ISC_R_SUCCESS))
		dst_key_free(&key);

	return (result);
Brian Wellington's avatar
Brian Wellington committed
523 524 525
}

isc_result_t
526 527
dst_key_fromnamedfile(const char *filename, const char *dirname,
		      int type, isc_mem_t *mctx, dst_key_t **keyp)
Brian Wellington's avatar
Brian Wellington committed
528 529 530
{
	isc_result_t result;
	dst_key_t *pubkey = NULL, *key = NULL;
531 532 533
	char *newfilename = NULL;
	int newfilenamelen = 0;
	isc_lex_t *lex = NULL;
Brian Wellington's avatar
Brian Wellington committed
534

535
	REQUIRE(dst_initialized == true);
Brian Wellington's avatar
Brian Wellington committed
536 537 538 539 540
	REQUIRE(filename != NULL);
	REQUIRE((type & (DST_TYPE_PRIVATE | DST_TYPE_PUBLIC)) != 0);
	REQUIRE(mctx != NULL);
	REQUIRE(keyp != NULL && *keyp == NULL);

541 542 543 544 545 546 547 548 549
	/* If an absolute path is specified, don't use the key directory */
#ifndef WIN32
	if (filename[0] == '/')
		dirname = NULL;
#else /* WIN32 */
	if (filename[0] == '/' || filename[0] == '\\')
		dirname = NULL;
#endif

550
	newfilenamelen = strlen(filename) + 5;
551 552
	if (dirname != NULL)
		newfilenamelen += strlen(dirname) + 1;
553 554 555
	newfilename = isc_mem_get(mctx, newfilenamelen);
	if (newfilename == NULL)
		return (ISC_R_NOMEMORY);
556 557
	result = addsuffix(newfilename, newfilenamelen,
			   dirname, filename, ".key");
558 559 560 561
	INSIST(result == ISC_R_SUCCESS);

	result = dst_key_read_public(newfilename, type, mctx, &pubkey);
	isc_mem_put(mctx, newfilename, newfilenamelen);
562
	newfilename = NULL;
Evan Hunt's avatar
Evan Hunt committed
563
	RETERR(result);
Brian Wellington's avatar
Brian Wellington committed
564

565
	if ((type & (DST_TYPE_PRIVATE | DST_TYPE_PUBLIC)) == DST_TYPE_PUBLIC ||
566
	    (pubkey->key_flags & DNS_KEYFLAG_TYPEMASK) == DNS_KEYTYPE_NOKEY) {
567 568 569 570 571 572
		result = computeid(pubkey);
		if (result != ISC_R_SUCCESS) {
			dst_key_free(&pubkey);
			return (result);
		}

Brian Wellington's avatar
Brian Wellington committed
573 574
		*keyp = pubkey;
		return (ISC_R_SUCCESS);
575
	}
576

577 578 579 580 581 582
	result = algorithm_status(pubkey->key_alg);
	if (result != ISC_R_SUCCESS) {
		dst_key_free(&pubkey);
		return (result);
	}

Brian Wellington's avatar
Brian Wellington committed
583
	key = get_key_struct(pubkey->key_name, pubkey->key_alg,
Brian Wellington's avatar
Brian Wellington committed
584
			     pubkey->key_flags, pubkey->key_proto, 0,
585
			     pubkey->key_class, pubkey->key_ttl, mctx);
586 587
	if (key == NULL) {
		dst_key_free(&pubkey);
Brian Wellington's avatar
Brian Wellington committed
588
		return (ISC_R_NOMEMORY);
589
	}
590

591 592
	if (key->func->parse == NULL)
		RETERR(DST_R_UNSUPPORTEDALG);
593

594
	newfilenamelen = strlen(filename) + 9;
595 596
	if (dirname != NULL)
		newfilenamelen += strlen(dirname) + 1;
597 598 599
	newfilename = isc_mem_get(mctx, newfilenamelen);
	if (newfilename == NULL)
		RETERR(ISC_R_NOMEMORY);
600 601
	result = addsuffix(newfilename, newfilenamelen,
			   dirname, filename, ".private");
602
	INSIST(result == ISC_R_SUCCESS);
603

604 605 606
	RETERR(isc_lex_create(mctx, 1500, &lex));
	RETERR(isc_lex_openfile(lex, newfilename));
	isc_mem_put(mctx, newfilename, newfilenamelen);
607

608
	RETERR(key->func->parse(key, lex, pubkey));
609 610 611 612
	isc_lex_destroy(&lex);

	RETERR(computeid(key));

613
	if (pubkey->key_id != key->key_id)
614
		RETERR(DST_R_INVALIDPRIVATEKEY);
615
	dst_key_free(&pubkey);
616

617
	*keyp = key;
618
	return (ISC_R_SUCCESS);
619

620
 out:
621 622
	if (pubkey != NULL)
		dst_key_free(&pubkey);
623 624 625 626
	if (newfilename != NULL)
		isc_mem_put(mctx, newfilename, newfilenamelen);
	if (lex != NULL)
		isc_lex_destroy(&lex);
Evan Hunt's avatar
Evan Hunt committed
627 628
	if (key != NULL)
		dst_key_free(&key);
629
	return (result);
630 631
}

632
isc_result_t
633
dst_key_todns(const dst_key_t *key, isc_buffer_t *target) {
634
	REQUIRE(dst_initialized == true);
635 636 637
	REQUIRE(VALID_KEY(key));
	REQUIRE(target != NULL);

638
	CHECKALG(key->key_alg);
639

Brian Wellington's avatar
Brian Wellington committed
640 641 642 643
	if (key->func->todns == NULL)
		return (DST_R_UNSUPPORTEDALG);

	if (isc_buffer_availablelength(target) < 4)
Brian Wellington's avatar
Brian Wellington committed
644
		return (ISC_R_NOSPACE);
645 646 647
	isc_buffer_putuint16(target, (uint16_t)(key->key_flags & 0xffff));
	isc_buffer_putuint8(target, (uint8_t)key->key_proto);
	isc_buffer_putuint8(target, (uint8_t)key->key_alg);
648

Brian Wellington's avatar
Brian Wellington committed
649
	if (key->key_flags & DNS_KEYFLAG_EXTENDED) {
Brian Wellington's avatar
Brian Wellington committed
650
		if (isc_buffer_availablelength(target) < 2)
Brian Wellington's avatar
Brian Wellington committed
651
			return (ISC_R_NOSPACE);
652
		isc_buffer_putuint16(target,
653
				     (uint16_t)((key->key_flags >> 16)
654
						    & 0xffff));
655 656
	}

657
	if (key->keydata.generic == NULL) /*%< NULL KEY */
658
		return (ISC_R_SUCCESS);
659

660
	return (key->func->todns(key, target));
661 662
}

663
isc_result_t
664
dst_key_fromdns(const dns_name_t *name, dns_rdataclass_t rdclass,
Brian Wellington's avatar
Brian Wellington committed
665
		isc_buffer_t *source, isc_mem_t *mctx, dst_key_t **keyp)
666
{
667 668
	uint8_t alg, proto;
	uint32_t flags, extflags;
669
	dst_key_t *key = NULL;
670
	dns_keytag_t id, rid;
671 672
	isc_region_t r;
	isc_result_t result;
673

674 675 676
	REQUIRE(dst_initialized);

	isc_buffer_remainingregion(source, &r);
677

Brian Wellington's avatar
Brian Wellington committed
678
	if (isc_buffer_remaininglength(source) < 4)
Brian Wellington's avatar
Brian Wellington committed
679
		return (DST_R_INVALIDPUBLICKEY);
680 681 682 683
	flags = isc_buffer_getuint16(source);
	proto = isc_buffer_getuint8(source);
	alg = isc_buffer_getuint8(source);

684
	id = dst_region_computeid(&r, alg);
685
	rid = dst_region_computerid(&r, alg);
686

Brian Wellington's avatar
Brian Wellington committed
687
	if (flags & DNS_KEYFLAG_EXTENDED) {
Brian Wellington's avatar
Brian Wellington committed
688
		if (isc_buffer_remaininglength(source) < 2)
Brian Wellington's avatar
Brian Wellington committed
689
			return (DST_R_INVALIDPUBLICKEY);
690 691 692 693
		extflags = isc_buffer_getuint16(source);
		flags |= (extflags << 16);
	}

694 695 696 697 698
	result = frombuffer(name, alg, flags, proto, rdclass, source,
			    mctx, &key);
	if (result != ISC_R_SUCCESS)
		return (result);
	key->key_id = id;
699
	key->key_rid = rid;
700 701 702

	*keyp = key;
	return (ISC_R_SUCCESS);
703 704
}

705
isc_result_t
706
dst_key_frombuffer(const dns_name_t *name, unsigned int alg,
Brian Wellington's avatar
Brian Wellington committed
707
		   unsigned int flags, unsigned int protocol,
Brian Wellington's avatar
Brian Wellington committed
708
		   dns_rdataclass_t rdclass,
709
		   isc_buffer_t *source, isc_mem_t *mctx, dst_key_t **keyp)
710
{
711 712
	dst_key_t *key = NULL;
	isc_result_t result;
713

714
	REQUIRE(dst_initialized);
715

716 717 718 719
	result = frombuffer(name, alg, flags, protocol, rdclass, source,
			    mctx, &key);
	if (result != ISC_R_SUCCESS)
		return (result);
Brian Wellington's avatar
Brian Wellington committed
720

721 722
	result = computeid(key);
	if (result != ISC_R_SUCCESS) {
723
		dst_key_free(&key);
724
		return (result);
725
	}
726 727

	*keyp = key;
728
	return (ISC_R_SUCCESS);
729 730
}

731
isc_result_t
732
dst_key_tobuffer(const dst_key_t *key, isc_buffer_t *target) {
733
	REQUIRE(dst_initialized == true);
734 735 736
	REQUIRE(VALID_KEY(key));
	REQUIRE(target != NULL);

737
	CHECKALG(key->key_alg);
738

Brian Wellington's avatar
Brian Wellington committed
739 740 741
	if (key->func->todns == NULL)
		return (DST_R_UNSUPPORTEDALG);

742
	return (key->func->todns(key, target));
743 744
}

745 746 747 748 749
isc_result_t
dst_key_privatefrombuffer(dst_key_t *key, isc_buffer_t *buffer) {
	isc_lex_t *lex = NULL;
	isc_result_t result = ISC_R_SUCCESS;

750
	REQUIRE(dst_initialized == true);
751 752 753 754 755 756 757 758 759
	REQUIRE(VALID_KEY(key));
	REQUIRE(!dst_key_isprivate(key));
	REQUIRE(buffer != NULL);

	if (key->func->parse == NULL)
		RETERR(DST_R_UNSUPPORTEDALG);

	RETERR(isc_lex_create(key->mctx, 1500, &lex));
	RETERR(isc_lex_openbuffer(lex, buffer));
760
	RETERR(key->func->parse(key, lex, NULL));
761 762 763 764 765 766
 out:
	if (lex != NULL)
		isc_lex_destroy(&lex);
	return (result);
}