1/* $NetBSD: dst_parse.c,v 1.11 2024/02/21 22:52:06 christos Exp $ */ 2 3/* 4 * Copyright (C) Internet Systems Consortium, Inc. ("ISC") 5 * 6 * SPDX-License-Identifier: MPL-2.0 AND ISC 7 * 8 * This Source Code Form is subject to the terms of the Mozilla Public 9 * License, v. 2.0. If a copy of the MPL was not distributed with this 10 * file, you can obtain one at https://mozilla.org/MPL/2.0/. 11 * 12 * See the COPYRIGHT file distributed with this work for additional 13 * information regarding copyright ownership. 14 */ 15 16/* 17 * Copyright (C) Network Associates, Inc. 18 * 19 * Permission to use, copy, modify, and/or distribute this software for any 20 * purpose with or without fee is hereby granted, provided that the above 21 * copyright notice and this permission notice appear in all copies. 22 * 23 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC AND NETWORK ASSOCIATES DISCLAIMS 24 * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED 25 * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE 26 * FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 27 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 28 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR 29 * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 30 */ 31 32#include "dst_parse.h" 33#include <inttypes.h> 34#include <stdbool.h> 35#include <unistd.h> 36 37#include <isc/base64.h> 38#include <isc/dir.h> 39#include <isc/file.h> 40#include <isc/lex.h> 41#include <isc/mem.h> 42#include <isc/print.h> 43#include <isc/stdtime.h> 44#include <isc/string.h> 45#include <isc/util.h> 46 47#include <dns/log.h> 48#include <dns/time.h> 49 50#include "dst_internal.h" 51#include "isc/result.h" 52 53#define DST_AS_STR(t) ((t).value.as_textregion.base) 54 55#define PRIVATE_KEY_STR "Private-key-format:" 56#define ALGORITHM_STR "Algorithm:" 57 58#define TIMING_NTAGS (DST_MAX_TIMES + 1) 59static const char *timetags[TIMING_NTAGS] = { 60 "Created:", "Publish:", "Activate:", "Revoke:", 61 "Inactive:", "Delete:", "DSPublish:", "SyncPublish:", 62 "SyncDelete:", NULL, NULL, NULL, 63 NULL 64}; 65 66#define NUMERIC_NTAGS (DST_MAX_NUMERIC + 1) 67static const char *numerictags[NUMERIC_NTAGS] = { 68 "Predecessor:", "Successor:", "MaxTTL:", "RollPeriod:", NULL, NULL, NULL 69}; 70 71struct parse_map { 72 const int value; 73 const char *tag; 74}; 75 76static struct parse_map map[] = { { TAG_RSA_MODULUS, "Modulus:" }, 77 { TAG_RSA_PUBLICEXPONENT, "PublicExponent:" }, 78 { TAG_RSA_PRIVATEEXPONENT, "PrivateExponent" 79 ":" }, 80 { TAG_RSA_PRIME1, "Prime1:" }, 81 { TAG_RSA_PRIME2, "Prime2:" }, 82 { TAG_RSA_EXPONENT1, "Exponent1:" }, 83 { TAG_RSA_EXPONENT2, "Exponent2:" }, 84 { TAG_RSA_COEFFICIENT, "Coefficient:" }, 85 { TAG_RSA_ENGINE, "Engine:" }, 86 { TAG_RSA_LABEL, "Label:" }, 87 88 { TAG_DH_PRIME, "Prime(p):" }, 89 { TAG_DH_GENERATOR, "Generator(g):" }, 90 { TAG_DH_PRIVATE, "Private_value(x):" }, 91 { TAG_DH_PUBLIC, "Public_value(y):" }, 92 93 { TAG_ECDSA_PRIVATEKEY, "PrivateKey:" }, 94 { TAG_ECDSA_ENGINE, "Engine:" }, 95 { TAG_ECDSA_LABEL, "Label:" }, 96 97 { TAG_EDDSA_PRIVATEKEY, "PrivateKey:" }, 98 { TAG_EDDSA_ENGINE, "Engine:" }, 99 { TAG_EDDSA_LABEL, "Label:" }, 100 101 { TAG_HMACMD5_KEY, "Key:" }, 102 { TAG_HMACMD5_BITS, "Bits:" }, 103 104 { TAG_HMACSHA1_KEY, "Key:" }, 105 { TAG_HMACSHA1_BITS, "Bits:" }, 106 107 { TAG_HMACSHA224_KEY, "Key:" }, 108 { TAG_HMACSHA224_BITS, "Bits:" }, 109 110 { TAG_HMACSHA256_KEY, "Key:" }, 111 { TAG_HMACSHA256_BITS, "Bits:" }, 112 113 { TAG_HMACSHA384_KEY, "Key:" }, 114 { TAG_HMACSHA384_BITS, "Bits:" }, 115 116 { TAG_HMACSHA512_KEY, "Key:" }, 117 { TAG_HMACSHA512_BITS, "Bits:" }, 118 119 { 0, NULL } }; 120 121static int 122find_value(const char *s, const unsigned int alg) { 123 int i; 124 125 for (i = 0; map[i].tag != NULL; i++) { 126 if (strcasecmp(s, map[i].tag) == 0 && 127 (TAG_ALG(map[i].value) == alg)) 128 { 129 return (map[i].value); 130 } 131 } 132 return (-1); 133} 134 135static const char * 136find_tag(const int value) { 137 int i; 138 139 for (i = 0;; i++) { 140 if (map[i].tag == NULL) { 141 return (NULL); 142 } else if (value == map[i].value) { 143 return (map[i].tag); 144 } 145 } 146} 147 148static int 149find_metadata(const char *s, const char *tags[], int ntags) { 150 int i; 151 152 for (i = 0; i < ntags; i++) { 153 if (tags[i] != NULL && strcasecmp(s, tags[i]) == 0) { 154 return (i); 155 } 156 } 157 158 return (-1); 159} 160 161static int 162find_timedata(const char *s) { 163 return (find_metadata(s, timetags, TIMING_NTAGS)); 164} 165 166static int 167find_numericdata(const char *s) { 168 return (find_metadata(s, numerictags, NUMERIC_NTAGS)); 169} 170 171static int 172check_rsa(const dst_private_t *priv, bool external) { 173 int i, j; 174 bool have[RSA_NTAGS]; 175 bool ok; 176 unsigned int mask; 177 178 if (external) { 179 return ((priv->nelements == 0) ? 0 : -1); 180 } 181 182 for (i = 0; i < RSA_NTAGS; i++) { 183 have[i] = false; 184 } 185 186 for (j = 0; j < priv->nelements; j++) { 187 for (i = 0; i < RSA_NTAGS; i++) { 188 if (priv->elements[j].tag == TAG(DST_ALG_RSA, i)) { 189 break; 190 } 191 } 192 if (i == RSA_NTAGS) { 193 return (-1); 194 } 195 have[i] = true; 196 } 197 198 mask = (1ULL << TAG_SHIFT) - 1; 199 200 if (have[TAG_RSA_ENGINE & mask]) { 201 ok = have[TAG_RSA_MODULUS & mask] && 202 have[TAG_RSA_PUBLICEXPONENT & mask] && 203 have[TAG_RSA_LABEL & mask]; 204 } else { 205 ok = have[TAG_RSA_MODULUS & mask] && 206 have[TAG_RSA_PUBLICEXPONENT & mask] && 207 have[TAG_RSA_PRIVATEEXPONENT & mask] && 208 have[TAG_RSA_PRIME1 & mask] && 209 have[TAG_RSA_PRIME2 & mask] && 210 have[TAG_RSA_EXPONENT1 & mask] && 211 have[TAG_RSA_EXPONENT2 & mask] && 212 have[TAG_RSA_COEFFICIENT & mask]; 213 } 214 return (ok ? 0 : -1); 215} 216 217static int 218check_dh(const dst_private_t *priv) { 219 int i, j; 220 if (priv->nelements != DH_NTAGS) { 221 return (-1); 222 } 223 for (i = 0; i < DH_NTAGS; i++) { 224 for (j = 0; j < priv->nelements; j++) { 225 if (priv->elements[j].tag == TAG(DST_ALG_DH, i)) { 226 break; 227 } 228 } 229 if (j == priv->nelements) { 230 return (-1); 231 } 232 } 233 return (0); 234} 235 236static int 237check_ecdsa(const dst_private_t *priv, bool external) { 238 int i, j; 239 bool have[ECDSA_NTAGS]; 240 bool ok; 241 unsigned int mask; 242 243 if (external) { 244 return ((priv->nelements == 0) ? 0 : -1); 245 } 246 247 for (i = 0; i < ECDSA_NTAGS; i++) { 248 have[i] = false; 249 } 250 for (j = 0; j < priv->nelements; j++) { 251 for (i = 0; i < ECDSA_NTAGS; i++) { 252 if (priv->elements[j].tag == TAG(DST_ALG_ECDSA256, i)) { 253 break; 254 } 255 } 256 if (i == ECDSA_NTAGS) { 257 return (-1); 258 } 259 have[i] = true; 260 } 261 262 mask = (1ULL << TAG_SHIFT) - 1; 263 264 if (have[TAG_ECDSA_ENGINE & mask]) { 265 ok = have[TAG_ECDSA_LABEL & mask]; 266 } else { 267 ok = have[TAG_ECDSA_PRIVATEKEY & mask]; 268 } 269 return (ok ? 0 : -1); 270} 271 272static int 273check_eddsa(const dst_private_t *priv, bool external) { 274 int i, j; 275 bool have[EDDSA_NTAGS]; 276 bool ok; 277 unsigned int mask; 278 279 if (external) { 280 return ((priv->nelements == 0) ? 0 : -1); 281 } 282 283 for (i = 0; i < EDDSA_NTAGS; i++) { 284 have[i] = false; 285 } 286 for (j = 0; j < priv->nelements; j++) { 287 for (i = 0; i < EDDSA_NTAGS; i++) { 288 if (priv->elements[j].tag == TAG(DST_ALG_ED25519, i)) { 289 break; 290 } 291 } 292 if (i == EDDSA_NTAGS) { 293 return (-1); 294 } 295 have[i] = true; 296 } 297 298 mask = (1ULL << TAG_SHIFT) - 1; 299 300 if (have[TAG_EDDSA_ENGINE & mask]) { 301 ok = have[TAG_EDDSA_LABEL & mask]; 302 } else { 303 ok = have[TAG_EDDSA_PRIVATEKEY & mask]; 304 } 305 return (ok ? 0 : -1); 306} 307 308static int 309check_hmac_md5(const dst_private_t *priv, bool old) { 310 int i, j; 311 312 if (priv->nelements != HMACMD5_NTAGS) { 313 /* 314 * If this is a good old format and we are accepting 315 * the old format return success. 316 */ 317 if (old && priv->nelements == OLD_HMACMD5_NTAGS && 318 priv->elements[0].tag == TAG_HMACMD5_KEY) 319 { 320 return (0); 321 } 322 return (-1); 323 } 324 /* 325 * We must be new format at this point. 326 */ 327 for (i = 0; i < HMACMD5_NTAGS; i++) { 328 for (j = 0; j < priv->nelements; j++) { 329 if (priv->elements[j].tag == TAG(DST_ALG_HMACMD5, i)) { 330 break; 331 } 332 } 333 if (j == priv->nelements) { 334 return (-1); 335 } 336 } 337 return (0); 338} 339 340static int 341check_hmac_sha(const dst_private_t *priv, unsigned int ntags, 342 unsigned int alg) { 343 unsigned int i, j; 344 if (priv->nelements != ntags) { 345 return (-1); 346 } 347 for (i = 0; i < ntags; i++) { 348 for (j = 0; j < priv->nelements; j++) { 349 if (priv->elements[j].tag == TAG(alg, i)) { 350 break; 351 } 352 } 353 if (j == priv->nelements) { 354 return (-1); 355 } 356 } 357 return (0); 358} 359 360static int 361check_data(const dst_private_t *priv, const unsigned int alg, bool old, 362 bool external) { 363 /* XXXVIX this switch statement is too sparse to gen a jump table. */ 364 switch (alg) { 365 case DST_ALG_RSA: 366 case DST_ALG_RSASHA1: 367 case DST_ALG_NSEC3RSASHA1: 368 case DST_ALG_RSASHA256: 369 case DST_ALG_RSASHA512: 370 return (check_rsa(priv, external)); 371 case DST_ALG_DH: 372 return (check_dh(priv)); 373 case DST_ALG_ECDSA256: 374 case DST_ALG_ECDSA384: 375 return (check_ecdsa(priv, external)); 376 case DST_ALG_ED25519: 377 case DST_ALG_ED448: 378 return (check_eddsa(priv, external)); 379 case DST_ALG_HMACMD5: 380 return (check_hmac_md5(priv, old)); 381 case DST_ALG_HMACSHA1: 382 return (check_hmac_sha(priv, HMACSHA1_NTAGS, alg)); 383 case DST_ALG_HMACSHA224: 384 return (check_hmac_sha(priv, HMACSHA224_NTAGS, alg)); 385 case DST_ALG_HMACSHA256: 386 return (check_hmac_sha(priv, HMACSHA256_NTAGS, alg)); 387 case DST_ALG_HMACSHA384: 388 return (check_hmac_sha(priv, HMACSHA384_NTAGS, alg)); 389 case DST_ALG_HMACSHA512: 390 return (check_hmac_sha(priv, HMACSHA512_NTAGS, alg)); 391 default: 392 return (DST_R_UNSUPPORTEDALG); 393 } 394} 395 396void 397dst__privstruct_free(dst_private_t *priv, isc_mem_t *mctx) { 398 int i; 399 400 if (priv == NULL) { 401 return; 402 } 403 for (i = 0; i < priv->nelements; i++) { 404 if (priv->elements[i].data == NULL) { 405 continue; 406 } 407 memset(priv->elements[i].data, 0, MAXFIELDSIZE); 408 isc_mem_put(mctx, priv->elements[i].data, MAXFIELDSIZE); 409 } 410 priv->nelements = 0; 411} 412 413isc_result_t 414dst__privstruct_parse(dst_key_t *key, unsigned int alg, isc_lex_t *lex, 415 isc_mem_t *mctx, dst_private_t *priv) { 416 int n = 0, major, minor, check; 417 isc_buffer_t b; 418 isc_token_t token; 419 unsigned char *data = NULL; 420 unsigned int opt = ISC_LEXOPT_EOL; 421 isc_stdtime_t when; 422 isc_result_t ret; 423 bool external = false; 424 425 REQUIRE(priv != NULL); 426 427 priv->nelements = 0; 428 memset(priv->elements, 0, sizeof(priv->elements)); 429 430#define NEXTTOKEN(lex, opt, token) \ 431 do { \ 432 ret = isc_lex_gettoken(lex, opt, token); \ 433 if (ret != ISC_R_SUCCESS) \ 434 goto fail; \ 435 } while (0) 436 437#define READLINE(lex, opt, token) \ 438 do { \ 439 ret = isc_lex_gettoken(lex, opt, token); \ 440 if (ret == ISC_R_EOF) \ 441 break; \ 442 else if (ret != ISC_R_SUCCESS) \ 443 goto fail; \ 444 } while ((*token).type != isc_tokentype_eol) 445 446 /* 447 * Read the description line. 448 */ 449 NEXTTOKEN(lex, opt, &token); 450 if (token.type != isc_tokentype_string || 451 strcmp(DST_AS_STR(token), PRIVATE_KEY_STR) != 0) 452 { 453 ret = DST_R_INVALIDPRIVATEKEY; 454 goto fail; 455 } 456 457 NEXTTOKEN(lex, opt, &token); 458 if (token.type != isc_tokentype_string || (DST_AS_STR(token))[0] != 'v') 459 { 460 ret = DST_R_INVALIDPRIVATEKEY; 461 goto fail; 462 } 463 if (sscanf(DST_AS_STR(token), "v%d.%d", &major, &minor) != 2) { 464 ret = DST_R_INVALIDPRIVATEKEY; 465 goto fail; 466 } 467 468 if (major > DST_MAJOR_VERSION) { 469 ret = DST_R_INVALIDPRIVATEKEY; 470 goto fail; 471 } 472 473 /* 474 * Store the private key format version number 475 */ 476 dst_key_setprivateformat(key, major, minor); 477 478 READLINE(lex, opt, &token); 479 480 /* 481 * Read the algorithm line. 482 */ 483 NEXTTOKEN(lex, opt, &token); 484 if (token.type != isc_tokentype_string || 485 strcmp(DST_AS_STR(token), ALGORITHM_STR) != 0) 486 { 487 ret = DST_R_INVALIDPRIVATEKEY; 488 goto fail; 489 } 490 491 NEXTTOKEN(lex, opt | ISC_LEXOPT_NUMBER, &token); 492 if (token.type != isc_tokentype_number || 493 token.value.as_ulong != (unsigned long)dst_key_alg(key)) 494 { 495 ret = DST_R_INVALIDPRIVATEKEY; 496 goto fail; 497 } 498 499 READLINE(lex, opt, &token); 500 501 /* 502 * Read the key data. 503 */ 504 for (n = 0; n < MAXFIELDS; n++) { 505 int tag; 506 isc_region_t r; 507 do { 508 ret = isc_lex_gettoken(lex, opt, &token); 509 if (ret == ISC_R_EOF) { 510 goto done; 511 } 512 if (ret != ISC_R_SUCCESS) { 513 goto fail; 514 } 515 } while (token.type == isc_tokentype_eol); 516 517 if (token.type != isc_tokentype_string) { 518 ret = DST_R_INVALIDPRIVATEKEY; 519 goto fail; 520 } 521 522 if (strcmp(DST_AS_STR(token), "External:") == 0) { 523 external = true; 524 goto next; 525 } 526 527 /* Numeric metadata */ 528 tag = find_numericdata(DST_AS_STR(token)); 529 if (tag >= 0) { 530 INSIST(tag < NUMERIC_NTAGS); 531 532 NEXTTOKEN(lex, opt | ISC_LEXOPT_NUMBER, &token); 533 if (token.type != isc_tokentype_number) { 534 ret = DST_R_INVALIDPRIVATEKEY; 535 goto fail; 536 } 537 538 dst_key_setnum(key, tag, token.value.as_ulong); 539 goto next; 540 } 541 542 /* Timing metadata */ 543 tag = find_timedata(DST_AS_STR(token)); 544 if (tag >= 0) { 545 INSIST(tag < TIMING_NTAGS); 546 547 NEXTTOKEN(lex, opt, &token); 548 if (token.type != isc_tokentype_string) { 549 ret = DST_R_INVALIDPRIVATEKEY; 550 goto fail; 551 } 552 553 ret = dns_time32_fromtext(DST_AS_STR(token), &when); 554 if (ret != ISC_R_SUCCESS) { 555 goto fail; 556 } 557 558 dst_key_settime(key, tag, when); 559 560 goto next; 561 } 562 563 /* Key data */ 564 tag = find_value(DST_AS_STR(token), alg); 565 if (tag < 0 && minor > DST_MINOR_VERSION) { 566 goto next; 567 } else if (tag < 0) { 568 ret = DST_R_INVALIDPRIVATEKEY; 569 goto fail; 570 } 571 572 priv->elements[n].tag = tag; 573 574 data = isc_mem_get(mctx, MAXFIELDSIZE); 575 576 isc_buffer_init(&b, data, MAXFIELDSIZE); 577 ret = isc_base64_tobuffer(lex, &b, -1); 578 if (ret != ISC_R_SUCCESS) { 579 goto fail; 580 } 581 582 isc_buffer_usedregion(&b, &r); 583 priv->elements[n].length = r.length; 584 priv->elements[n].data = r.base; 585 priv->nelements++; 586 587 next: 588 READLINE(lex, opt, &token); 589 data = NULL; 590 } 591 592done: 593 if (external && priv->nelements != 0) { 594 ret = DST_R_INVALIDPRIVATEKEY; 595 goto fail; 596 } 597 598 check = check_data(priv, alg, true, external); 599 if (check < 0) { 600 ret = DST_R_INVALIDPRIVATEKEY; 601 goto fail; 602 } else if (check != ISC_R_SUCCESS) { 603 ret = check; 604 goto fail; 605 } 606 607 key->external = external; 608 609 return (ISC_R_SUCCESS); 610 611fail: 612 dst__privstruct_free(priv, mctx); 613 if (data != NULL) { 614 isc_mem_put(mctx, data, MAXFIELDSIZE); 615 } 616 617 return (ret); 618} 619 620isc_result_t 621dst__privstruct_writefile(const dst_key_t *key, const dst_private_t *priv, 622 const char *directory) { 623 FILE *fp; 624 isc_result_t result; 625 char filename[NAME_MAX]; 626 char tmpname[NAME_MAX]; 627 char buffer[MAXFIELDSIZE * 2]; 628 isc_stdtime_t when; 629 uint32_t value; 630 isc_buffer_t b; 631 isc_buffer_t fileb; 632 isc_buffer_t tmpb; 633 isc_region_t r; 634 int major, minor; 635 mode_t mode; 636 int i, ret; 637 638 REQUIRE(priv != NULL); 639 640 ret = check_data(priv, dst_key_alg(key), false, key->external); 641 if (ret < 0) { 642 return (DST_R_INVALIDPRIVATEKEY); 643 } else if (ret != ISC_R_SUCCESS) { 644 return (ret); 645 } 646 647 isc_buffer_init(&fileb, filename, sizeof(filename)); 648 result = dst_key_buildfilename(key, DST_TYPE_PRIVATE, directory, 649 &fileb); 650 if (result != ISC_R_SUCCESS) { 651 return (result); 652 } 653 654 result = isc_file_mode(filename, &mode); 655 if (result == ISC_R_SUCCESS && mode != (S_IRUSR | S_IWUSR)) { 656 /* File exists; warn that we are changing its permissions */ 657 int level; 658 659 level = ISC_LOG_WARNING; 660 isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL, 661 DNS_LOGMODULE_DNSSEC, level, 662 "Permissions on the file %s " 663 "have changed from 0%o to 0600 as " 664 "a result of this operation.", 665 filename, (unsigned int)mode); 666 } 667 668 isc_buffer_init(&tmpb, tmpname, sizeof(tmpname)); 669 result = dst_key_buildfilename(key, DST_TYPE_TEMPLATE, directory, 670 &tmpb); 671 if (result != ISC_R_SUCCESS) { 672 return (result); 673 } 674 675 fp = dst_key_open(tmpname, S_IRUSR | S_IWUSR); 676 if (fp == NULL) { 677 return (DST_R_WRITEERROR); 678 } 679 680 dst_key_getprivateformat(key, &major, &minor); 681 if (major == 0 && minor == 0) { 682 major = DST_MAJOR_VERSION; 683 minor = DST_MINOR_VERSION; 684 } 685 686 /* XXXDCL return value should be checked for full filesystem */ 687 fprintf(fp, "%s v%d.%d\n", PRIVATE_KEY_STR, major, minor); 688 689 fprintf(fp, "%s %u ", ALGORITHM_STR, dst_key_alg(key)); 690 691 /* XXXVIX this switch statement is too sparse to gen a jump table. */ 692 switch (dst_key_alg(key)) { 693 case DST_ALG_DH: 694 fprintf(fp, "(DH)\n"); 695 break; 696 case DST_ALG_RSASHA1: 697 fprintf(fp, "(RSASHA1)\n"); 698 break; 699 case DST_ALG_NSEC3RSASHA1: 700 fprintf(fp, "(NSEC3RSASHA1)\n"); 701 break; 702 case DST_ALG_RSASHA256: 703 fprintf(fp, "(RSASHA256)\n"); 704 break; 705 case DST_ALG_RSASHA512: 706 fprintf(fp, "(RSASHA512)\n"); 707 break; 708 case DST_ALG_ECDSA256: 709 fprintf(fp, "(ECDSAP256SHA256)\n"); 710 break; 711 case DST_ALG_ECDSA384: 712 fprintf(fp, "(ECDSAP384SHA384)\n"); 713 break; 714 case DST_ALG_ED25519: 715 fprintf(fp, "(ED25519)\n"); 716 break; 717 case DST_ALG_ED448: 718 fprintf(fp, "(ED448)\n"); 719 break; 720 case DST_ALG_HMACMD5: 721 fprintf(fp, "(HMAC_MD5)\n"); 722 break; 723 case DST_ALG_HMACSHA1: 724 fprintf(fp, "(HMAC_SHA1)\n"); 725 break; 726 case DST_ALG_HMACSHA224: 727 fprintf(fp, "(HMAC_SHA224)\n"); 728 break; 729 case DST_ALG_HMACSHA256: 730 fprintf(fp, "(HMAC_SHA256)\n"); 731 break; 732 case DST_ALG_HMACSHA384: 733 fprintf(fp, "(HMAC_SHA384)\n"); 734 break; 735 case DST_ALG_HMACSHA512: 736 fprintf(fp, "(HMAC_SHA512)\n"); 737 break; 738 default: 739 fprintf(fp, "(?)\n"); 740 break; 741 } 742 743 for (i = 0; i < priv->nelements; i++) { 744 const char *s; 745 746 s = find_tag(priv->elements[i].tag); 747 748 r.base = priv->elements[i].data; 749 r.length = priv->elements[i].length; 750 isc_buffer_init(&b, buffer, sizeof(buffer)); 751 result = isc_base64_totext(&r, sizeof(buffer), "", &b); 752 if (result != ISC_R_SUCCESS) { 753 return (dst_key_cleanup(tmpname, fp)); 754 } 755 isc_buffer_usedregion(&b, &r); 756 757 fprintf(fp, "%s %.*s\n", s, (int)r.length, r.base); 758 } 759 760 if (key->external) { 761 fprintf(fp, "External:\n"); 762 } 763 764 /* Add the metadata tags */ 765 if (major > 1 || (major == 1 && minor >= 3)) { 766 for (i = 0; i < NUMERIC_NTAGS; i++) { 767 result = dst_key_getnum(key, i, &value); 768 if (result != ISC_R_SUCCESS) { 769 continue; 770 } 771 if (numerictags[i] != NULL) { 772 fprintf(fp, "%s %u\n", numerictags[i], value); 773 } 774 } 775 for (i = 0; i < TIMING_NTAGS; i++) { 776 result = dst_key_gettime(key, i, &when); 777 if (result != ISC_R_SUCCESS) { 778 continue; 779 } 780 781 isc_buffer_init(&b, buffer, sizeof(buffer)); 782 result = dns_time32_totext(when, &b); 783 if (result != ISC_R_SUCCESS) { 784 return (dst_key_cleanup(tmpname, fp)); 785 } 786 787 isc_buffer_usedregion(&b, &r); 788 789 if (timetags[i] != NULL) { 790 fprintf(fp, "%s %.*s\n", timetags[i], 791 (int)r.length, r.base); 792 } 793 } 794 } 795 796 result = dst_key_close(tmpname, fp, filename); 797 return (result); 798} 799 800/*! \file */ 801