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