/contrib/bind9/lib/dns/dst_parse.c

https://bitbucket.org/freebsd/freebsd-head/ · C · 680 lines · 524 code · 95 blank · 61 comment · 148 complexity · 4576770070b2716cbdad1d2e7bd02e74 MD5 · raw file

  1. /*
  2. * Portions Copyright (C) 2004-2012 Internet Systems Consortium, Inc. ("ISC")
  3. * Portions Copyright (C) 1999-2002 Internet Software Consortium.
  4. *
  5. * Permission to use, copy, modify, and/or distribute this software for any
  6. * purpose with or without fee is hereby granted, provided that the above
  7. * copyright notice and this permission notice appear in all copies.
  8. *
  9. * THE SOFTWARE IS PROVIDED "AS IS" AND ISC AND NETWORK ASSOCIATES DISCLAIMS
  10. * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
  11. * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE
  12. * FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  13. * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  14. * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
  15. * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  16. *
  17. * Portions Copyright (C) 1995-2000 by 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. * Principal Author: Brian Wellington
  33. * $Id$
  34. */
  35. #include <config.h>
  36. #include <isc/base64.h>
  37. #include <isc/dir.h>
  38. #include <isc/fsaccess.h>
  39. #include <isc/lex.h>
  40. #include <isc/mem.h>
  41. #include <isc/stdtime.h>
  42. #include <isc/string.h>
  43. #include <isc/util.h>
  44. #include <dns/time.h>
  45. #include "dst_internal.h"
  46. #include "dst_parse.h"
  47. #include "dst/result.h"
  48. #define DST_AS_STR(t) ((t).value.as_textregion.base)
  49. #define PRIVATE_KEY_STR "Private-key-format:"
  50. #define ALGORITHM_STR "Algorithm:"
  51. #define TIMING_NTAGS (DST_MAX_TIMES + 1)
  52. static const char *timetags[TIMING_NTAGS] = {
  53. "Created:",
  54. "Publish:",
  55. "Activate:",
  56. "Revoke:",
  57. "Inactive:",
  58. "Delete:",
  59. "DSPublish:"
  60. };
  61. #define NUMERIC_NTAGS (DST_MAX_NUMERIC + 1)
  62. static const char *numerictags[NUMERIC_NTAGS] = {
  63. "Predecessor:",
  64. "Successor:",
  65. "MaxTTL:",
  66. "RollPeriod:"
  67. };
  68. struct parse_map {
  69. const int value;
  70. const char *tag;
  71. };
  72. static struct parse_map map[] = {
  73. {TAG_RSA_MODULUS, "Modulus:"},
  74. {TAG_RSA_PUBLICEXPONENT, "PublicExponent:"},
  75. {TAG_RSA_PRIVATEEXPONENT, "PrivateExponent:"},
  76. {TAG_RSA_PRIME1, "Prime1:"},
  77. {TAG_RSA_PRIME2, "Prime2:"},
  78. {TAG_RSA_EXPONENT1, "Exponent1:"},
  79. {TAG_RSA_EXPONENT2, "Exponent2:"},
  80. {TAG_RSA_COEFFICIENT, "Coefficient:"},
  81. {TAG_RSA_ENGINE, "Engine:" },
  82. {TAG_RSA_LABEL, "Label:" },
  83. {TAG_RSA_PIN, "PIN:" },
  84. {TAG_DH_PRIME, "Prime(p):"},
  85. {TAG_DH_GENERATOR, "Generator(g):"},
  86. {TAG_DH_PRIVATE, "Private_value(x):"},
  87. {TAG_DH_PUBLIC, "Public_value(y):"},
  88. {TAG_DSA_PRIME, "Prime(p):"},
  89. {TAG_DSA_SUBPRIME, "Subprime(q):"},
  90. {TAG_DSA_BASE, "Base(g):"},
  91. {TAG_DSA_PRIVATE, "Private_value(x):"},
  92. {TAG_DSA_PUBLIC, "Public_value(y):"},
  93. {TAG_GOST_PRIVASN1, "GostAsn1:"},
  94. {TAG_HMACMD5_KEY, "Key:"},
  95. {TAG_HMACMD5_BITS, "Bits:"},
  96. {TAG_HMACSHA1_KEY, "Key:"},
  97. {TAG_HMACSHA1_BITS, "Bits:"},
  98. {TAG_HMACSHA224_KEY, "Key:"},
  99. {TAG_HMACSHA224_BITS, "Bits:"},
  100. {TAG_HMACSHA256_KEY, "Key:"},
  101. {TAG_HMACSHA256_BITS, "Bits:"},
  102. {TAG_HMACSHA384_KEY, "Key:"},
  103. {TAG_HMACSHA384_BITS, "Bits:"},
  104. {TAG_HMACSHA512_KEY, "Key:"},
  105. {TAG_HMACSHA512_BITS, "Bits:"},
  106. {0, NULL}
  107. };
  108. static int
  109. find_value(const char *s, const unsigned int alg) {
  110. int i;
  111. for (i = 0; map[i].tag != NULL; i++) {
  112. if (strcasecmp(s, map[i].tag) == 0 &&
  113. (TAG_ALG(map[i].value) == alg))
  114. return (map[i].value);
  115. }
  116. return (-1);
  117. }
  118. static const char *
  119. find_tag(const int value) {
  120. int i;
  121. for (i = 0; ; i++) {
  122. if (map[i].tag == NULL)
  123. return (NULL);
  124. else if (value == map[i].value)
  125. return (map[i].tag);
  126. }
  127. }
  128. static int
  129. find_metadata(const char *s, const char *tags[], int ntags) {
  130. int i;
  131. for (i = 0; i < ntags; i++) {
  132. if (strcasecmp(s, tags[i]) == 0)
  133. return (i);
  134. }
  135. return (-1);
  136. }
  137. static int
  138. find_timedata(const char *s) {
  139. return (find_metadata(s, timetags, TIMING_NTAGS));
  140. }
  141. static int
  142. find_numericdata(const char *s) {
  143. return (find_metadata(s, numerictags, NUMERIC_NTAGS));
  144. }
  145. static int
  146. check_rsa(const dst_private_t *priv) {
  147. int i, j;
  148. isc_boolean_t have[RSA_NTAGS];
  149. isc_boolean_t ok;
  150. unsigned int mask;
  151. for (i = 0; i < RSA_NTAGS; i++)
  152. have[i] = ISC_FALSE;
  153. for (j = 0; j < priv->nelements; j++) {
  154. for (i = 0; i < RSA_NTAGS; i++)
  155. if (priv->elements[j].tag == TAG(DST_ALG_RSAMD5, i))
  156. break;
  157. if (i == RSA_NTAGS)
  158. return (-1);
  159. have[i] = ISC_TRUE;
  160. }
  161. mask = ~0;
  162. mask <<= sizeof(mask) * 8 - TAG_SHIFT;
  163. mask >>= sizeof(mask) * 8 - TAG_SHIFT;
  164. if (have[TAG_RSA_ENGINE & mask])
  165. ok = have[TAG_RSA_MODULUS & mask] &&
  166. have[TAG_RSA_PUBLICEXPONENT & mask] &&
  167. have[TAG_RSA_LABEL & mask];
  168. else
  169. ok = have[TAG_RSA_MODULUS & mask] &&
  170. have[TAG_RSA_PUBLICEXPONENT & mask] &&
  171. have[TAG_RSA_PRIVATEEXPONENT & mask] &&
  172. have[TAG_RSA_PRIME1 & mask] &&
  173. have[TAG_RSA_PRIME2 & mask] &&
  174. have[TAG_RSA_EXPONENT1 & mask] &&
  175. have[TAG_RSA_EXPONENT2 & mask] &&
  176. have[TAG_RSA_COEFFICIENT & mask];
  177. return (ok ? 0 : -1 );
  178. }
  179. static int
  180. check_dh(const dst_private_t *priv) {
  181. int i, j;
  182. if (priv->nelements != DH_NTAGS)
  183. return (-1);
  184. for (i = 0; i < DH_NTAGS; i++) {
  185. for (j = 0; j < priv->nelements; j++)
  186. if (priv->elements[j].tag == TAG(DST_ALG_DH, i))
  187. break;
  188. if (j == priv->nelements)
  189. return (-1);
  190. }
  191. return (0);
  192. }
  193. static int
  194. check_dsa(const dst_private_t *priv) {
  195. int i, j;
  196. if (priv->nelements != DSA_NTAGS)
  197. return (-1);
  198. for (i = 0; i < DSA_NTAGS; i++) {
  199. for (j = 0; j < priv->nelements; j++)
  200. if (priv->elements[j].tag == TAG(DST_ALG_DSA, i))
  201. break;
  202. if (j == priv->nelements)
  203. return (-1);
  204. }
  205. return (0);
  206. }
  207. static int
  208. check_gost(const dst_private_t *priv) {
  209. if (priv->nelements != GOST_NTAGS)
  210. return (-1);
  211. if (priv->elements[0].tag != TAG(DST_ALG_ECCGOST, 0))
  212. return (-1);
  213. return (0);
  214. }
  215. static int
  216. check_hmac_md5(const dst_private_t *priv, isc_boolean_t old) {
  217. int i, j;
  218. if (priv->nelements != HMACMD5_NTAGS) {
  219. /*
  220. * If this is a good old format and we are accepting
  221. * the old format return success.
  222. */
  223. if (old && priv->nelements == OLD_HMACMD5_NTAGS &&
  224. priv->elements[0].tag == TAG_HMACMD5_KEY)
  225. return (0);
  226. return (-1);
  227. }
  228. /*
  229. * We must be new format at this point.
  230. */
  231. for (i = 0; i < HMACMD5_NTAGS; i++) {
  232. for (j = 0; j < priv->nelements; j++)
  233. if (priv->elements[j].tag == TAG(DST_ALG_HMACMD5, i))
  234. break;
  235. if (j == priv->nelements)
  236. return (-1);
  237. }
  238. return (0);
  239. }
  240. static int
  241. check_hmac_sha(const dst_private_t *priv, unsigned int ntags,
  242. unsigned int alg)
  243. {
  244. unsigned int i, j;
  245. if (priv->nelements != ntags)
  246. return (-1);
  247. for (i = 0; i < ntags; i++) {
  248. for (j = 0; j < priv->nelements; j++)
  249. if (priv->elements[j].tag == TAG(alg, i))
  250. break;
  251. if (j == priv->nelements)
  252. return (-1);
  253. }
  254. return (0);
  255. }
  256. static int
  257. check_data(const dst_private_t *priv, const unsigned int alg,
  258. isc_boolean_t old)
  259. {
  260. /* XXXVIX this switch statement is too sparse to gen a jump table. */
  261. switch (alg) {
  262. case DST_ALG_RSAMD5:
  263. case DST_ALG_RSASHA1:
  264. return (check_rsa(priv));
  265. case DST_ALG_DH:
  266. return (check_dh(priv));
  267. case DST_ALG_DSA:
  268. return (check_dsa(priv));
  269. case DST_ALG_ECCGOST:
  270. return (check_gost(priv));
  271. case DST_ALG_HMACMD5:
  272. return (check_hmac_md5(priv, old));
  273. case DST_ALG_HMACSHA1:
  274. return (check_hmac_sha(priv, HMACSHA1_NTAGS, alg));
  275. case DST_ALG_HMACSHA224:
  276. return (check_hmac_sha(priv, HMACSHA224_NTAGS, alg));
  277. case DST_ALG_HMACSHA256:
  278. return (check_hmac_sha(priv, HMACSHA256_NTAGS, alg));
  279. case DST_ALG_HMACSHA384:
  280. return (check_hmac_sha(priv, HMACSHA384_NTAGS, alg));
  281. case DST_ALG_HMACSHA512:
  282. return (check_hmac_sha(priv, HMACSHA512_NTAGS, alg));
  283. default:
  284. return (DST_R_UNSUPPORTEDALG);
  285. }
  286. }
  287. void
  288. dst__privstruct_free(dst_private_t *priv, isc_mem_t *mctx) {
  289. int i;
  290. if (priv == NULL)
  291. return;
  292. for (i = 0; i < priv->nelements; i++) {
  293. if (priv->elements[i].data == NULL)
  294. continue;
  295. memset(priv->elements[i].data, 0, MAXFIELDSIZE);
  296. isc_mem_put(mctx, priv->elements[i].data, MAXFIELDSIZE);
  297. }
  298. priv->nelements = 0;
  299. }
  300. isc_result_t
  301. dst__privstruct_parse(dst_key_t *key, unsigned int alg, isc_lex_t *lex,
  302. isc_mem_t *mctx, dst_private_t *priv)
  303. {
  304. int n = 0, major, minor;
  305. isc_buffer_t b;
  306. isc_token_t token;
  307. unsigned char *data = NULL;
  308. unsigned int opt = ISC_LEXOPT_EOL;
  309. isc_stdtime_t when;
  310. isc_result_t ret;
  311. REQUIRE(priv != NULL);
  312. priv->nelements = 0;
  313. memset(priv->elements, 0, sizeof(priv->elements));
  314. #define NEXTTOKEN(lex, opt, token) \
  315. do { \
  316. ret = isc_lex_gettoken(lex, opt, token); \
  317. if (ret != ISC_R_SUCCESS) \
  318. goto fail; \
  319. } while (0)
  320. #define READLINE(lex, opt, token) \
  321. do { \
  322. ret = isc_lex_gettoken(lex, opt, token); \
  323. if (ret == ISC_R_EOF) \
  324. break; \
  325. else if (ret != ISC_R_SUCCESS) \
  326. goto fail; \
  327. } while ((*token).type != isc_tokentype_eol)
  328. /*
  329. * Read the description line.
  330. */
  331. NEXTTOKEN(lex, opt, &token);
  332. if (token.type != isc_tokentype_string ||
  333. strcmp(DST_AS_STR(token), PRIVATE_KEY_STR) != 0)
  334. {
  335. ret = DST_R_INVALIDPRIVATEKEY;
  336. goto fail;
  337. }
  338. NEXTTOKEN(lex, opt, &token);
  339. if (token.type != isc_tokentype_string ||
  340. (DST_AS_STR(token))[0] != 'v')
  341. {
  342. ret = DST_R_INVALIDPRIVATEKEY;
  343. goto fail;
  344. }
  345. if (sscanf(DST_AS_STR(token), "v%d.%d", &major, &minor) != 2)
  346. {
  347. ret = DST_R_INVALIDPRIVATEKEY;
  348. goto fail;
  349. }
  350. if (major > DST_MAJOR_VERSION) {
  351. ret = DST_R_INVALIDPRIVATEKEY;
  352. goto fail;
  353. }
  354. /*
  355. * Store the private key format version number
  356. */
  357. dst_key_setprivateformat(key, major, minor);
  358. READLINE(lex, opt, &token);
  359. /*
  360. * Read the algorithm line.
  361. */
  362. NEXTTOKEN(lex, opt, &token);
  363. if (token.type != isc_tokentype_string ||
  364. strcmp(DST_AS_STR(token), ALGORITHM_STR) != 0)
  365. {
  366. ret = DST_R_INVALIDPRIVATEKEY;
  367. goto fail;
  368. }
  369. NEXTTOKEN(lex, opt | ISC_LEXOPT_NUMBER, &token);
  370. if (token.type != isc_tokentype_number ||
  371. token.value.as_ulong != (unsigned long) dst_key_alg(key))
  372. {
  373. ret = DST_R_INVALIDPRIVATEKEY;
  374. goto fail;
  375. }
  376. READLINE(lex, opt, &token);
  377. /*
  378. * Read the key data.
  379. */
  380. for (n = 0; n < MAXFIELDS; n++) {
  381. int tag;
  382. isc_region_t r;
  383. do {
  384. ret = isc_lex_gettoken(lex, opt, &token);
  385. if (ret == ISC_R_EOF)
  386. goto done;
  387. if (ret != ISC_R_SUCCESS)
  388. goto fail;
  389. } while (token.type == isc_tokentype_eol);
  390. if (token.type != isc_tokentype_string) {
  391. ret = DST_R_INVALIDPRIVATEKEY;
  392. goto fail;
  393. }
  394. /* Numeric metadata */
  395. tag = find_numericdata(DST_AS_STR(token));
  396. if (tag >= 0) {
  397. INSIST(tag < NUMERIC_NTAGS);
  398. NEXTTOKEN(lex, opt | ISC_LEXOPT_NUMBER, &token);
  399. if (token.type != isc_tokentype_number) {
  400. ret = DST_R_INVALIDPRIVATEKEY;
  401. goto fail;
  402. }
  403. dst_key_setnum(key, tag, token.value.as_ulong);
  404. goto next;
  405. }
  406. /* Timing metadata */
  407. tag = find_timedata(DST_AS_STR(token));
  408. if (tag >= 0) {
  409. INSIST(tag < TIMING_NTAGS);
  410. NEXTTOKEN(lex, opt, &token);
  411. if (token.type != isc_tokentype_string) {
  412. ret = DST_R_INVALIDPRIVATEKEY;
  413. goto fail;
  414. }
  415. ret = dns_time32_fromtext(DST_AS_STR(token), &when);
  416. if (ret != ISC_R_SUCCESS)
  417. goto fail;
  418. dst_key_settime(key, tag, when);
  419. goto next;
  420. }
  421. /* Key data */
  422. tag = find_value(DST_AS_STR(token), alg);
  423. if (tag < 0 && minor > DST_MINOR_VERSION)
  424. goto next;
  425. else if (tag < 0) {
  426. ret = DST_R_INVALIDPRIVATEKEY;
  427. goto fail;
  428. }
  429. priv->elements[n].tag = tag;
  430. data = (unsigned char *) isc_mem_get(mctx, MAXFIELDSIZE);
  431. if (data == NULL)
  432. goto fail;
  433. isc_buffer_init(&b, data, MAXFIELDSIZE);
  434. ret = isc_base64_tobuffer(lex, &b, -1);
  435. if (ret != ISC_R_SUCCESS)
  436. goto fail;
  437. isc_buffer_usedregion(&b, &r);
  438. priv->elements[n].length = r.length;
  439. priv->elements[n].data = r.base;
  440. priv->nelements++;
  441. next:
  442. READLINE(lex, opt, &token);
  443. data = NULL;
  444. }
  445. done:
  446. if (check_data(priv, alg, ISC_TRUE) < 0)
  447. goto fail;
  448. return (ISC_R_SUCCESS);
  449. fail:
  450. dst__privstruct_free(priv, mctx);
  451. if (data != NULL)
  452. isc_mem_put(mctx, data, MAXFIELDSIZE);
  453. return (ret);
  454. }
  455. isc_result_t
  456. dst__privstruct_writefile(const dst_key_t *key, const dst_private_t *priv,
  457. const char *directory)
  458. {
  459. FILE *fp;
  460. int ret, i;
  461. isc_result_t result;
  462. char filename[ISC_DIR_NAMEMAX];
  463. char buffer[MAXFIELDSIZE * 2];
  464. isc_fsaccess_t access;
  465. isc_stdtime_t when;
  466. isc_uint32_t value;
  467. isc_buffer_t b;
  468. isc_region_t r;
  469. int major, minor;
  470. REQUIRE(priv != NULL);
  471. if (check_data(priv, dst_key_alg(key), ISC_FALSE) < 0)
  472. return (DST_R_INVALIDPRIVATEKEY);
  473. isc_buffer_init(&b, filename, sizeof(filename));
  474. ret = dst_key_buildfilename(key, DST_TYPE_PRIVATE, directory, &b);
  475. if (ret != ISC_R_SUCCESS)
  476. return (ret);
  477. if ((fp = fopen(filename, "w")) == NULL)
  478. return (DST_R_WRITEERROR);
  479. access = 0;
  480. isc_fsaccess_add(ISC_FSACCESS_OWNER,
  481. ISC_FSACCESS_READ | ISC_FSACCESS_WRITE,
  482. &access);
  483. (void)isc_fsaccess_set(filename, access);
  484. dst_key_getprivateformat(key, &major, &minor);
  485. if (major == 0 && minor == 0) {
  486. major = DST_MAJOR_VERSION;
  487. minor = DST_MINOR_VERSION;
  488. }
  489. /* XXXDCL return value should be checked for full filesystem */
  490. fprintf(fp, "%s v%d.%d\n", PRIVATE_KEY_STR, major, minor);
  491. fprintf(fp, "%s %d ", ALGORITHM_STR, dst_key_alg(key));
  492. /* XXXVIX this switch statement is too sparse to gen a jump table. */
  493. switch (dst_key_alg(key)) {
  494. case DST_ALG_RSAMD5:
  495. fprintf(fp, "(RSA)\n");
  496. break;
  497. case DST_ALG_DH:
  498. fprintf(fp, "(DH)\n");
  499. break;
  500. case DST_ALG_DSA:
  501. fprintf(fp, "(DSA)\n");
  502. break;
  503. case DST_ALG_RSASHA1:
  504. fprintf(fp, "(RSASHA1)\n");
  505. break;
  506. case DST_ALG_NSEC3RSASHA1:
  507. fprintf(fp, "(NSEC3RSASHA1)\n");
  508. break;
  509. case DST_ALG_NSEC3DSA:
  510. fprintf(fp, "(NSEC3DSA)\n");
  511. break;
  512. case DST_ALG_RSASHA256:
  513. fprintf(fp, "(RSASHA256)\n");
  514. break;
  515. case DST_ALG_RSASHA512:
  516. fprintf(fp, "(RSASHA512)\n");
  517. break;
  518. case DST_ALG_ECCGOST:
  519. fprintf(fp, "(ECC-GOST)\n");
  520. break;
  521. case DST_ALG_HMACMD5:
  522. fprintf(fp, "(HMAC_MD5)\n");
  523. break;
  524. case DST_ALG_HMACSHA1:
  525. fprintf(fp, "(HMAC_SHA1)\n");
  526. break;
  527. case DST_ALG_HMACSHA224:
  528. fprintf(fp, "(HMAC_SHA224)\n");
  529. break;
  530. case DST_ALG_HMACSHA256:
  531. fprintf(fp, "(HMAC_SHA256)\n");
  532. break;
  533. case DST_ALG_HMACSHA384:
  534. fprintf(fp, "(HMAC_SHA384)\n");
  535. break;
  536. case DST_ALG_HMACSHA512:
  537. fprintf(fp, "(HMAC_SHA512)\n");
  538. break;
  539. default:
  540. fprintf(fp, "(?)\n");
  541. break;
  542. }
  543. for (i = 0; i < priv->nelements; i++) {
  544. const char *s;
  545. s = find_tag(priv->elements[i].tag);
  546. r.base = priv->elements[i].data;
  547. r.length = priv->elements[i].length;
  548. isc_buffer_init(&b, buffer, sizeof(buffer));
  549. result = isc_base64_totext(&r, sizeof(buffer), "", &b);
  550. if (result != ISC_R_SUCCESS) {
  551. fclose(fp);
  552. return (DST_R_INVALIDPRIVATEKEY);
  553. }
  554. isc_buffer_usedregion(&b, &r);
  555. fprintf(fp, "%s %.*s\n", s, (int)r.length, r.base);
  556. }
  557. /* Add the metadata tags */
  558. if (major > 1 || (major == 1 && minor >= 3)) {
  559. for (i = 0; i < NUMERIC_NTAGS; i++) {
  560. result = dst_key_getnum(key, i, &value);
  561. if (result != ISC_R_SUCCESS)
  562. continue;
  563. fprintf(fp, "%s %u\n", numerictags[i], value);
  564. }
  565. for (i = 0; i < TIMING_NTAGS; i++) {
  566. result = dst_key_gettime(key, i, &when);
  567. if (result != ISC_R_SUCCESS)
  568. continue;
  569. isc_buffer_init(&b, buffer, sizeof(buffer));
  570. result = dns_time32_totext(when, &b);
  571. if (result != ISC_R_SUCCESS) {
  572. fclose(fp);
  573. return (DST_R_INVALIDPRIVATEKEY);
  574. }
  575. isc_buffer_usedregion(&b, &r);
  576. fprintf(fp, "%s %.*s\n", timetags[i], (int)r.length,
  577. r.base);
  578. }
  579. }
  580. fflush(fp);
  581. result = ferror(fp) ? DST_R_WRITEERROR : ISC_R_SUCCESS;
  582. fclose(fp);
  583. return (result);
  584. }
  585. /*! \file */