]> CyberLeo.Net >> Repos - FreeBSD/releng/9.2.git/blob - contrib/bind9/lib/dns/dst_parse.c
- Copy stable/9 to releng/9.2 as part of the 9.2-RELEASE cycle.
[FreeBSD/releng/9.2.git] / contrib / bind9 / lib / dns / dst_parse.c
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 /*%
33  * Principal Author: Brian Wellington
34  * $Id$
35  */
36
37 #include <config.h>
38
39 #include <isc/base64.h>
40 #include <isc/dir.h>
41 #include <isc/fsaccess.h>
42 #include <isc/lex.h>
43 #include <isc/mem.h>
44 #include <isc/stdtime.h>
45 #include <isc/string.h>
46 #include <isc/util.h>
47 #include <isc/file.h>
48
49 #include <dns/time.h>
50 #include <dns/log.h>
51
52 #include "dst_internal.h"
53 #include "dst_parse.h"
54 #include "dst/result.h"
55
56 #define DST_AS_STR(t) ((t).value.as_textregion.base)
57
58 #define PRIVATE_KEY_STR "Private-key-format:"
59 #define ALGORITHM_STR "Algorithm:"
60
61 #define TIMING_NTAGS (DST_MAX_TIMES + 1)
62 static const char *timetags[TIMING_NTAGS] = {
63         "Created:",
64         "Publish:",
65         "Activate:",
66         "Revoke:",
67         "Inactive:",
68         "Delete:",
69         "DSPublish:"
70 };
71
72 #define NUMERIC_NTAGS (DST_MAX_NUMERIC + 1)
73 static const char *numerictags[NUMERIC_NTAGS] = {
74         "Predecessor:",
75         "Successor:",
76         "MaxTTL:",
77         "RollPeriod:"
78 };
79
80 struct parse_map {
81         const int value;
82         const char *tag;
83 };
84
85 static struct parse_map map[] = {
86         {TAG_RSA_MODULUS, "Modulus:"},
87         {TAG_RSA_PUBLICEXPONENT, "PublicExponent:"},
88         {TAG_RSA_PRIVATEEXPONENT, "PrivateExponent:"},
89         {TAG_RSA_PRIME1, "Prime1:"},
90         {TAG_RSA_PRIME2, "Prime2:"},
91         {TAG_RSA_EXPONENT1, "Exponent1:"},
92         {TAG_RSA_EXPONENT2, "Exponent2:"},
93         {TAG_RSA_COEFFICIENT, "Coefficient:"},
94         {TAG_RSA_ENGINE, "Engine:" },
95         {TAG_RSA_LABEL, "Label:" },
96         {TAG_RSA_PIN, "PIN:" },
97
98         {TAG_DH_PRIME, "Prime(p):"},
99         {TAG_DH_GENERATOR, "Generator(g):"},
100         {TAG_DH_PRIVATE, "Private_value(x):"},
101         {TAG_DH_PUBLIC, "Public_value(y):"},
102
103         {TAG_DSA_PRIME, "Prime(p):"},
104         {TAG_DSA_SUBPRIME, "Subprime(q):"},
105         {TAG_DSA_BASE, "Base(g):"},
106         {TAG_DSA_PRIVATE, "Private_value(x):"},
107         {TAG_DSA_PUBLIC, "Public_value(y):"},
108
109         {TAG_GOST_PRIVASN1, "GostAsn1:"},
110
111         {TAG_ECDSA_PRIVATEKEY, "PrivateKey:"},
112
113         {TAG_HMACMD5_KEY, "Key:"},
114         {TAG_HMACMD5_BITS, "Bits:"},
115
116         {TAG_HMACSHA1_KEY, "Key:"},
117         {TAG_HMACSHA1_BITS, "Bits:"},
118
119         {TAG_HMACSHA224_KEY, "Key:"},
120         {TAG_HMACSHA224_BITS, "Bits:"},
121
122         {TAG_HMACSHA256_KEY, "Key:"},
123         {TAG_HMACSHA256_BITS, "Bits:"},
124
125         {TAG_HMACSHA384_KEY, "Key:"},
126         {TAG_HMACSHA384_BITS, "Bits:"},
127
128         {TAG_HMACSHA512_KEY, "Key:"},
129         {TAG_HMACSHA512_BITS, "Bits:"},
130
131         {0, NULL}
132 };
133
134 static int
135 find_value(const char *s, const unsigned int alg) {
136         int i;
137
138         for (i = 0; map[i].tag != NULL; i++) {
139                 if (strcasecmp(s, map[i].tag) == 0 &&
140                     (TAG_ALG(map[i].value) == alg))
141                         return (map[i].value);
142         }
143         return (-1);
144 }
145
146 static const char *
147 find_tag(const int value) {
148         int i;
149
150         for (i = 0; ; i++) {
151                 if (map[i].tag == NULL)
152                         return (NULL);
153                 else if (value == map[i].value)
154                         return (map[i].tag);
155         }
156 }
157
158 static int
159 find_metadata(const char *s, const char *tags[], int ntags) {
160         int i;
161
162         for (i = 0; i < ntags; i++) {
163                 if (strcasecmp(s, tags[i]) == 0)
164                         return (i);
165         }
166
167         return (-1);
168 }
169
170 static int
171 find_timedata(const char *s) {
172         return (find_metadata(s, timetags, TIMING_NTAGS));
173 }
174
175 static int
176 find_numericdata(const char *s) {
177         return (find_metadata(s, numerictags, NUMERIC_NTAGS));
178 }
179
180 static int
181 check_rsa(const dst_private_t *priv) {
182         int i, j;
183         isc_boolean_t have[RSA_NTAGS];
184         isc_boolean_t ok;
185         unsigned int mask;
186
187         for (i = 0; i < RSA_NTAGS; i++)
188                 have[i] = ISC_FALSE;
189         for (j = 0; j < priv->nelements; j++) {
190                 for (i = 0; i < RSA_NTAGS; i++)
191                         if (priv->elements[j].tag == TAG(DST_ALG_RSAMD5, i))
192                                 break;
193                 if (i == RSA_NTAGS)
194                         return (-1);
195                 have[i] = ISC_TRUE;
196         }
197
198         mask = ~0;
199         mask <<= sizeof(mask) * 8 - TAG_SHIFT;
200         mask >>= sizeof(mask) * 8 - TAG_SHIFT;
201
202         if (have[TAG_RSA_ENGINE & mask])
203                 ok = have[TAG_RSA_MODULUS & mask] &&
204                      have[TAG_RSA_PUBLICEXPONENT & mask] &&
205                      have[TAG_RSA_LABEL & mask];
206         else
207                 ok = have[TAG_RSA_MODULUS & mask] &&
208                      have[TAG_RSA_PUBLICEXPONENT & mask] &&
209                      have[TAG_RSA_PRIVATEEXPONENT & mask] &&
210                      have[TAG_RSA_PRIME1 & mask] &&
211                      have[TAG_RSA_PRIME2 & mask] &&
212                      have[TAG_RSA_EXPONENT1 & mask] &&
213                      have[TAG_RSA_EXPONENT2 & mask] &&
214                      have[TAG_RSA_COEFFICIENT & mask];
215         return (ok ? 0 : -1 );
216 }
217
218 static int
219 check_dh(const dst_private_t *priv) {
220         int i, j;
221         if (priv->nelements != DH_NTAGS)
222                 return (-1);
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                 if (j == priv->nelements)
228                         return (-1);
229         }
230         return (0);
231 }
232
233 static int
234 check_dsa(const dst_private_t *priv) {
235         int i, j;
236         if (priv->nelements != DSA_NTAGS)
237                 return (-1);
238         for (i = 0; i < DSA_NTAGS; i++) {
239                 for (j = 0; j < priv->nelements; j++)
240                         if (priv->elements[j].tag == TAG(DST_ALG_DSA, i))
241                                 break;
242                 if (j == priv->nelements)
243                         return (-1);
244         }
245         return (0);
246 }
247
248 static int
249 check_gost(const dst_private_t *priv) {
250         if (priv->nelements != GOST_NTAGS)
251                 return (-1);
252         if (priv->elements[0].tag != TAG(DST_ALG_ECCGOST, 0))
253                 return (-1);
254         return (0);
255 }
256
257 static int
258 check_ecdsa(const dst_private_t *priv) {
259         if (priv->nelements != ECDSA_NTAGS)
260                 return (-1);
261         if (priv->elements[0].tag != TAG(DST_ALG_ECDSA256, 0))
262                 return (-1);
263         return (0);
264 }
265
266 static int
267 check_hmac_md5(const dst_private_t *priv, isc_boolean_t old) {
268         int i, j;
269
270         if (priv->nelements != HMACMD5_NTAGS) {
271                 /*
272                  * If this is a good old format and we are accepting
273                  * the old format return success.
274                  */
275                 if (old && priv->nelements == OLD_HMACMD5_NTAGS &&
276                     priv->elements[0].tag == TAG_HMACMD5_KEY)
277                         return (0);
278                 return (-1);
279         }
280         /*
281          * We must be new format at this point.
282          */
283         for (i = 0; i < HMACMD5_NTAGS; i++) {
284                 for (j = 0; j < priv->nelements; j++)
285                         if (priv->elements[j].tag == TAG(DST_ALG_HMACMD5, i))
286                                 break;
287                 if (j == priv->nelements)
288                         return (-1);
289         }
290         return (0);
291 }
292
293 static int
294 check_hmac_sha(const dst_private_t *priv, unsigned int ntags,
295                unsigned int alg)
296 {
297         unsigned int i, j;
298         if (priv->nelements != ntags)
299                 return (-1);
300         for (i = 0; i < ntags; i++) {
301                 for (j = 0; j < priv->nelements; j++)
302                         if (priv->elements[j].tag == TAG(alg, i))
303                                 break;
304                 if (j == priv->nelements)
305                         return (-1);
306         }
307         return (0);
308 }
309
310 static int
311 check_data(const dst_private_t *priv, const unsigned int alg,
312            isc_boolean_t old)
313 {
314         /* XXXVIX this switch statement is too sparse to gen a jump table. */
315         switch (alg) {
316         case DST_ALG_RSAMD5:
317         case DST_ALG_RSASHA1:
318         case DST_ALG_NSEC3RSASHA1:
319         case DST_ALG_RSASHA256:
320         case DST_ALG_RSASHA512:
321                 return (check_rsa(priv));
322         case DST_ALG_DH:
323                 return (check_dh(priv));
324         case DST_ALG_DSA:
325         case DST_ALG_NSEC3DSA:
326                 return (check_dsa(priv));
327         case DST_ALG_ECCGOST:
328                 return (check_gost(priv));
329         case DST_ALG_ECDSA256:
330         case DST_ALG_ECDSA384:
331                 return (check_ecdsa(priv));
332         case DST_ALG_HMACMD5:
333                 return (check_hmac_md5(priv, old));
334         case DST_ALG_HMACSHA1:
335                 return (check_hmac_sha(priv, HMACSHA1_NTAGS, alg));
336         case DST_ALG_HMACSHA224:
337                 return (check_hmac_sha(priv, HMACSHA224_NTAGS, alg));
338         case DST_ALG_HMACSHA256:
339                 return (check_hmac_sha(priv, HMACSHA256_NTAGS, alg));
340         case DST_ALG_HMACSHA384:
341                 return (check_hmac_sha(priv, HMACSHA384_NTAGS, alg));
342         case DST_ALG_HMACSHA512:
343                 return (check_hmac_sha(priv, HMACSHA512_NTAGS, alg));
344         default:
345                 return (DST_R_UNSUPPORTEDALG);
346         }
347 }
348
349 void
350 dst__privstruct_free(dst_private_t *priv, isc_mem_t *mctx) {
351         int i;
352
353         if (priv == NULL)
354                 return;
355         for (i = 0; i < priv->nelements; i++) {
356                 if (priv->elements[i].data == NULL)
357                         continue;
358                 memset(priv->elements[i].data, 0, MAXFIELDSIZE);
359                 isc_mem_put(mctx, priv->elements[i].data, MAXFIELDSIZE);
360         }
361         priv->nelements = 0;
362 }
363
364 isc_result_t
365 dst__privstruct_parse(dst_key_t *key, unsigned int alg, isc_lex_t *lex,
366                       isc_mem_t *mctx, dst_private_t *priv)
367 {
368         int n = 0, major, minor, check;
369         isc_buffer_t b;
370         isc_token_t token;
371         unsigned char *data = NULL;
372         unsigned int opt = ISC_LEXOPT_EOL;
373         isc_stdtime_t when;
374         isc_result_t ret;
375
376         REQUIRE(priv != NULL);
377
378         priv->nelements = 0;
379         memset(priv->elements, 0, sizeof(priv->elements));
380
381 #define NEXTTOKEN(lex, opt, token)                              \
382         do {                                                    \
383                 ret = isc_lex_gettoken(lex, opt, token);        \
384                 if (ret != ISC_R_SUCCESS)                       \
385                         goto fail;                              \
386         } while (0)
387
388 #define READLINE(lex, opt, token)                               \
389         do {                                                    \
390                 ret = isc_lex_gettoken(lex, opt, token);        \
391                 if (ret == ISC_R_EOF)                           \
392                         break;                                  \
393                 else if (ret != ISC_R_SUCCESS)                  \
394                         goto fail;                              \
395         } while ((*token).type != isc_tokentype_eol)
396
397         /*
398          * Read the description line.
399          */
400         NEXTTOKEN(lex, opt, &token);
401         if (token.type != isc_tokentype_string ||
402             strcmp(DST_AS_STR(token), PRIVATE_KEY_STR) != 0)
403         {
404                 ret = DST_R_INVALIDPRIVATEKEY;
405                 goto fail;
406         }
407
408         NEXTTOKEN(lex, opt, &token);
409         if (token.type != isc_tokentype_string ||
410             (DST_AS_STR(token))[0] != 'v')
411         {
412                 ret = DST_R_INVALIDPRIVATEKEY;
413                 goto fail;
414         }
415         if (sscanf(DST_AS_STR(token), "v%d.%d", &major, &minor) != 2)
416         {
417                 ret = DST_R_INVALIDPRIVATEKEY;
418                 goto fail;
419         }
420
421         if (major > DST_MAJOR_VERSION) {
422                 ret = DST_R_INVALIDPRIVATEKEY;
423                 goto fail;
424         }
425
426         /*
427          * Store the private key format version number
428          */
429         dst_key_setprivateformat(key, major, minor);
430
431         READLINE(lex, opt, &token);
432
433         /*
434          * Read the algorithm line.
435          */
436         NEXTTOKEN(lex, opt, &token);
437         if (token.type != isc_tokentype_string ||
438             strcmp(DST_AS_STR(token), ALGORITHM_STR) != 0)
439         {
440                 ret = DST_R_INVALIDPRIVATEKEY;
441                 goto fail;
442         }
443
444         NEXTTOKEN(lex, opt | ISC_LEXOPT_NUMBER, &token);
445         if (token.type != isc_tokentype_number ||
446             token.value.as_ulong != (unsigned long) dst_key_alg(key))
447         {
448                 ret = DST_R_INVALIDPRIVATEKEY;
449                 goto fail;
450         }
451
452         READLINE(lex, opt, &token);
453
454         /*
455          * Read the key data.
456          */
457         for (n = 0; n < MAXFIELDS; n++) {
458                 int tag;
459                 isc_region_t r;
460                 do {
461                         ret = isc_lex_gettoken(lex, opt, &token);
462                         if (ret == ISC_R_EOF)
463                                 goto done;
464                         if (ret != ISC_R_SUCCESS)
465                                 goto fail;
466                 } while (token.type == isc_tokentype_eol);
467
468                 if (token.type != isc_tokentype_string) {
469                         ret = DST_R_INVALIDPRIVATEKEY;
470                         goto fail;
471                 }
472
473                 /* Numeric metadata */
474                 tag = find_numericdata(DST_AS_STR(token));
475                 if (tag >= 0) {
476                         INSIST(tag < NUMERIC_NTAGS);
477
478                         NEXTTOKEN(lex, opt | ISC_LEXOPT_NUMBER, &token);
479                         if (token.type != isc_tokentype_number) {
480                                 ret = DST_R_INVALIDPRIVATEKEY;
481                                 goto fail;
482                         }
483
484                         dst_key_setnum(key, tag, token.value.as_ulong);
485                         goto next;
486                 }
487
488                 /* Timing metadata */
489                 tag = find_timedata(DST_AS_STR(token));
490                 if (tag >= 0) {
491                         INSIST(tag < TIMING_NTAGS);
492
493                         NEXTTOKEN(lex, opt, &token);
494                         if (token.type != isc_tokentype_string) {
495                                 ret = DST_R_INVALIDPRIVATEKEY;
496                                 goto fail;
497                         }
498
499                         ret = dns_time32_fromtext(DST_AS_STR(token), &when);
500                         if (ret != ISC_R_SUCCESS)
501                                 goto fail;
502
503                         dst_key_settime(key, tag, when);
504
505                         goto next;
506                 }
507
508                 /* Key data */
509                 tag = find_value(DST_AS_STR(token), alg);
510                 if (tag < 0 && minor > DST_MINOR_VERSION)
511                         goto next;
512                 else if (tag < 0) {
513                         ret = DST_R_INVALIDPRIVATEKEY;
514                         goto fail;
515                 }
516
517                 priv->elements[n].tag = tag;
518
519                 data = (unsigned char *) isc_mem_get(mctx, MAXFIELDSIZE);
520                 if (data == NULL)
521                         goto fail;
522
523                 isc_buffer_init(&b, data, MAXFIELDSIZE);
524                 ret = isc_base64_tobuffer(lex, &b, -1);
525                 if (ret != ISC_R_SUCCESS)
526                         goto fail;
527
528                 isc_buffer_usedregion(&b, &r);
529                 priv->elements[n].length = r.length;
530                 priv->elements[n].data = r.base;
531                 priv->nelements++;
532
533           next:
534                 READLINE(lex, opt, &token);
535                 data = NULL;
536         }
537  done:
538         check = check_data(priv, alg, ISC_TRUE);
539         if (check < 0) {
540                 ret = DST_R_INVALIDPRIVATEKEY;
541                 goto fail;
542         } else if (check != ISC_R_SUCCESS) {
543                 ret = check;
544                 goto fail;
545         }
546
547         return (ISC_R_SUCCESS);
548
549 fail:
550         dst__privstruct_free(priv, mctx);
551         if (data != NULL)
552                 isc_mem_put(mctx, data, MAXFIELDSIZE);
553
554         return (ret);
555 }
556
557 isc_result_t
558 dst__privstruct_writefile(const dst_key_t *key, const dst_private_t *priv,
559                           const char *directory)
560 {
561         FILE *fp;
562         isc_result_t result;
563         char filename[ISC_DIR_NAMEMAX];
564         char buffer[MAXFIELDSIZE * 2];
565         isc_fsaccess_t access;
566         isc_stdtime_t when;
567         isc_uint32_t value;
568         isc_buffer_t b;
569         isc_region_t r;
570         int major, minor;
571         mode_t mode;
572         int i, ret;
573
574         REQUIRE(priv != NULL);
575
576         ret = check_data(priv, dst_key_alg(key), ISC_FALSE);
577         if (ret < 0)
578                 return (DST_R_INVALIDPRIVATEKEY);
579         else if (ret != ISC_R_SUCCESS)
580                 return (ret);
581
582         isc_buffer_init(&b, filename, sizeof(filename));
583         result = dst_key_buildfilename(key, DST_TYPE_PRIVATE, directory, &b);
584         if (result != ISC_R_SUCCESS)
585                 return (result);
586
587         result = isc_file_mode(filename, &mode);
588         if (result == ISC_R_SUCCESS && mode != 0600) {
589                 /* File exists; warn that we are changing its permissions */
590                 isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
591                               DNS_LOGMODULE_DNSSEC, ISC_LOG_WARNING,
592                               "Permissions on the file %s "
593                               "have changed from 0%o to 0600 as "
594                               "a result of this operation.",
595                               filename, (unsigned int)mode);
596         }
597
598         if ((fp = fopen(filename, "w")) == NULL)
599                 return (DST_R_WRITEERROR);
600
601         access = 0;
602         isc_fsaccess_add(ISC_FSACCESS_OWNER,
603                          ISC_FSACCESS_READ | ISC_FSACCESS_WRITE,
604                          &access);
605         (void)isc_fsaccess_set(filename, access);
606
607         dst_key_getprivateformat(key, &major, &minor);
608         if (major == 0 && minor == 0) {
609                 major = DST_MAJOR_VERSION;
610                 minor = DST_MINOR_VERSION;
611         }
612
613         /* XXXDCL return value should be checked for full filesystem */
614         fprintf(fp, "%s v%d.%d\n", PRIVATE_KEY_STR, major, minor);
615
616         fprintf(fp, "%s %d ", ALGORITHM_STR, dst_key_alg(key));
617
618         /* XXXVIX this switch statement is too sparse to gen a jump table. */
619         switch (dst_key_alg(key)) {
620         case DST_ALG_RSAMD5:
621                 fprintf(fp, "(RSA)\n");
622                 break;
623         case DST_ALG_DH:
624                 fprintf(fp, "(DH)\n");
625                 break;
626         case DST_ALG_DSA:
627                 fprintf(fp, "(DSA)\n");
628                 break;
629         case DST_ALG_RSASHA1:
630                 fprintf(fp, "(RSASHA1)\n");
631                 break;
632         case DST_ALG_NSEC3RSASHA1:
633                 fprintf(fp, "(NSEC3RSASHA1)\n");
634                 break;
635         case DST_ALG_NSEC3DSA:
636                 fprintf(fp, "(NSEC3DSA)\n");
637                 break;
638         case DST_ALG_RSASHA256:
639                 fprintf(fp, "(RSASHA256)\n");
640                 break;
641         case DST_ALG_RSASHA512:
642                 fprintf(fp, "(RSASHA512)\n");
643                 break;
644         case DST_ALG_ECCGOST:
645                 fprintf(fp, "(ECC-GOST)\n");
646                 break;
647         case DST_ALG_ECDSA256:
648                 fprintf(fp, "(ECDSAP256SHA256)\n");
649                 break;
650         case DST_ALG_ECDSA384:
651                 fprintf(fp, "(ECDSAP384SHA384)\n");
652                 break;
653         case DST_ALG_HMACMD5:
654                 fprintf(fp, "(HMAC_MD5)\n");
655                 break;
656         case DST_ALG_HMACSHA1:
657                 fprintf(fp, "(HMAC_SHA1)\n");
658                 break;
659         case DST_ALG_HMACSHA224:
660                 fprintf(fp, "(HMAC_SHA224)\n");
661                 break;
662         case DST_ALG_HMACSHA256:
663                 fprintf(fp, "(HMAC_SHA256)\n");
664                 break;
665         case DST_ALG_HMACSHA384:
666                 fprintf(fp, "(HMAC_SHA384)\n");
667                 break;
668         case DST_ALG_HMACSHA512:
669                 fprintf(fp, "(HMAC_SHA512)\n");
670                 break;
671         default:
672                 fprintf(fp, "(?)\n");
673                 break;
674         }
675
676         for (i = 0; i < priv->nelements; i++) {
677                 const char *s;
678
679                 s = find_tag(priv->elements[i].tag);
680
681                 r.base = priv->elements[i].data;
682                 r.length = priv->elements[i].length;
683                 isc_buffer_init(&b, buffer, sizeof(buffer));
684                 result = isc_base64_totext(&r, sizeof(buffer), "", &b);
685                 if (result != ISC_R_SUCCESS) {
686                         fclose(fp);
687                         return (DST_R_INVALIDPRIVATEKEY);
688                 }
689                 isc_buffer_usedregion(&b, &r);
690
691                fprintf(fp, "%s %.*s\n", s, (int)r.length, r.base);
692         }
693
694         /* Add the metadata tags */
695         if (major > 1 || (major == 1 && minor >= 3)) {
696                 for (i = 0; i < NUMERIC_NTAGS; i++) {
697                         result = dst_key_getnum(key, i, &value);
698                         if (result != ISC_R_SUCCESS)
699                                 continue;
700                         fprintf(fp, "%s %u\n", numerictags[i], value);
701                 }
702                 for (i = 0; i < TIMING_NTAGS; i++) {
703                         result = dst_key_gettime(key, i, &when);
704                         if (result != ISC_R_SUCCESS)
705                                 continue;
706
707                         isc_buffer_init(&b, buffer, sizeof(buffer));
708                         result = dns_time32_totext(when, &b);
709                        if (result != ISC_R_SUCCESS) {
710                                fclose(fp);
711                                return (DST_R_INVALIDPRIVATEKEY);
712                        }
713
714                         isc_buffer_usedregion(&b, &r);
715
716                        fprintf(fp, "%s %.*s\n", timetags[i], (int)r.length,
717                                 r.base);
718                 }
719         }
720
721         fflush(fp);
722         result = ferror(fp) ? DST_R_WRITEERROR : ISC_R_SUCCESS;
723         fclose(fp);
724         return (result);
725 }
726
727 /*! \file */