]> CyberLeo.Net >> Repos - FreeBSD/releng/9.3.git/blob - contrib/bind9/lib/dns/dst_parse.c
Fix BIND remote denial of service vulnerability. [SA-15:27]
[FreeBSD/releng/9.3.git] / contrib / bind9 / lib / dns / dst_parse.c
1 /*
2  * Portions Copyright (C) 2004-2013  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: dst_parse.c,v 1.29 2011/08/18 23:46:35 tbox Exp $
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, isc_boolean_t external) {
182         int i, j;
183         isc_boolean_t have[RSA_NTAGS];
184         isc_boolean_t ok;
185         unsigned int mask;
186
187         if (external)
188                 return ((priv->nelements == 0) ? 0 : -1);
189
190         for (i = 0; i < RSA_NTAGS; i++)
191                 have[i] = ISC_FALSE;
192
193         for (j = 0; j < priv->nelements; j++) {
194                 for (i = 0; i < RSA_NTAGS; i++)
195                         if (priv->elements[j].tag == TAG(DST_ALG_RSAMD5, i))
196                                 break;
197                 if (i == RSA_NTAGS)
198                         return (-1);
199                 have[i] = ISC_TRUE;
200         }
201
202         mask = ~0;
203         mask <<= sizeof(mask) * 8 - TAG_SHIFT;
204         mask >>= sizeof(mask) * 8 - TAG_SHIFT;
205
206         if (have[TAG_RSA_ENGINE & mask])
207                 ok = have[TAG_RSA_MODULUS & mask] &&
208                      have[TAG_RSA_PUBLICEXPONENT & mask] &&
209                      have[TAG_RSA_LABEL & mask];
210         else
211                 ok = have[TAG_RSA_MODULUS & mask] &&
212                      have[TAG_RSA_PUBLICEXPONENT & mask] &&
213                      have[TAG_RSA_PRIVATEEXPONENT & mask] &&
214                      have[TAG_RSA_PRIME1 & mask] &&
215                      have[TAG_RSA_PRIME2 & mask] &&
216                      have[TAG_RSA_EXPONENT1 & mask] &&
217                      have[TAG_RSA_EXPONENT2 & mask] &&
218                      have[TAG_RSA_COEFFICIENT & mask];
219         return (ok ? 0 : -1 );
220 }
221
222 static int
223 check_dh(const dst_private_t *priv) {
224         int i, j;
225         if (priv->nelements != DH_NTAGS)
226                 return (-1);
227         for (i = 0; i < DH_NTAGS; i++) {
228                 for (j = 0; j < priv->nelements; j++)
229                         if (priv->elements[j].tag == TAG(DST_ALG_DH, i))
230                                 break;
231                 if (j == priv->nelements)
232                         return (-1);
233         }
234         return (0);
235 }
236
237 static int
238 check_dsa(const dst_private_t *priv, isc_boolean_t external) {
239         int i, j;
240
241         if (external)
242                 return ((priv->nelements == 0)? 0 : -1);
243
244         if (priv->nelements != DSA_NTAGS)
245                 return (-1);
246
247         for (i = 0; i < DSA_NTAGS; i++) {
248                 for (j = 0; j < priv->nelements; j++)
249                         if (priv->elements[j].tag == TAG(DST_ALG_DSA, i))
250                                 break;
251                 if (j == priv->nelements)
252                         return (-1);
253         }
254         return (0);
255 }
256
257 static int
258 check_gost(const dst_private_t *priv, isc_boolean_t external) {
259
260         if (external)
261                 return ((priv->nelements == 0)? 0 : -1);
262
263         if (priv->nelements != GOST_NTAGS)
264                 return (-1);
265         if (priv->elements[0].tag != TAG(DST_ALG_ECCGOST, 0))
266                 return (-1);
267         return (0);
268 }
269
270 static int
271 check_ecdsa(const dst_private_t *priv, isc_boolean_t external) {
272
273         if (external)
274                 return ((priv->nelements == 0) ? 0 : -1);
275
276         if (priv->nelements != ECDSA_NTAGS)
277                 return (-1);
278         if (priv->elements[0].tag != TAG(DST_ALG_ECDSA256, 0))
279                 return (-1);
280         return (0);
281 }
282
283 static int
284 check_hmac_md5(const dst_private_t *priv, isc_boolean_t old) {
285         int i, j;
286
287         if (priv->nelements != HMACMD5_NTAGS) {
288                 /*
289                  * If this is a good old format and we are accepting
290                  * the old format return success.
291                  */
292                 if (old && priv->nelements == OLD_HMACMD5_NTAGS &&
293                     priv->elements[0].tag == TAG_HMACMD5_KEY)
294                         return (0);
295                 return (-1);
296         }
297         /*
298          * We must be new format at this point.
299          */
300         for (i = 0; i < HMACMD5_NTAGS; i++) {
301                 for (j = 0; j < priv->nelements; j++)
302                         if (priv->elements[j].tag == TAG(DST_ALG_HMACMD5, i))
303                                 break;
304                 if (j == priv->nelements)
305                         return (-1);
306         }
307         return (0);
308 }
309
310 static int
311 check_hmac_sha(const dst_private_t *priv, unsigned int ntags,
312                unsigned int alg)
313 {
314         unsigned int i, j;
315         if (priv->nelements != ntags)
316                 return (-1);
317         for (i = 0; i < ntags; i++) {
318                 for (j = 0; j < priv->nelements; j++)
319                         if (priv->elements[j].tag == TAG(alg, i))
320                                 break;
321                 if (j == priv->nelements)
322                         return (-1);
323         }
324         return (0);
325 }
326
327 static int
328 check_data(const dst_private_t *priv, const unsigned int alg,
329            isc_boolean_t old, isc_boolean_t external)
330 {
331         /* XXXVIX this switch statement is too sparse to gen a jump table. */
332         switch (alg) {
333         case DST_ALG_RSAMD5:
334         case DST_ALG_RSASHA1:
335         case DST_ALG_NSEC3RSASHA1:
336         case DST_ALG_RSASHA256:
337         case DST_ALG_RSASHA512:
338                 return (check_rsa(priv, external));
339         case DST_ALG_DH:
340                 return (check_dh(priv));
341         case DST_ALG_DSA:
342         case DST_ALG_NSEC3DSA:
343                 return (check_dsa(priv, external));
344         case DST_ALG_ECCGOST:
345                 return (check_gost(priv, external));
346         case DST_ALG_ECDSA256:
347         case DST_ALG_ECDSA384:
348                 return (check_ecdsa(priv, external));
349         case DST_ALG_HMACMD5:
350                 return (check_hmac_md5(priv, old));
351         case DST_ALG_HMACSHA1:
352                 return (check_hmac_sha(priv, HMACSHA1_NTAGS, alg));
353         case DST_ALG_HMACSHA224:
354                 return (check_hmac_sha(priv, HMACSHA224_NTAGS, alg));
355         case DST_ALG_HMACSHA256:
356                 return (check_hmac_sha(priv, HMACSHA256_NTAGS, alg));
357         case DST_ALG_HMACSHA384:
358                 return (check_hmac_sha(priv, HMACSHA384_NTAGS, alg));
359         case DST_ALG_HMACSHA512:
360                 return (check_hmac_sha(priv, HMACSHA512_NTAGS, alg));
361         default:
362                 return (DST_R_UNSUPPORTEDALG);
363         }
364 }
365
366 void
367 dst__privstruct_free(dst_private_t *priv, isc_mem_t *mctx) {
368         int i;
369
370         if (priv == NULL)
371                 return;
372         for (i = 0; i < priv->nelements; i++) {
373                 if (priv->elements[i].data == NULL)
374                         continue;
375                 memset(priv->elements[i].data, 0, MAXFIELDSIZE);
376                 isc_mem_put(mctx, priv->elements[i].data, MAXFIELDSIZE);
377         }
378         priv->nelements = 0;
379 }
380
381 isc_result_t
382 dst__privstruct_parse(dst_key_t *key, unsigned int alg, isc_lex_t *lex,
383                       isc_mem_t *mctx, dst_private_t *priv)
384 {
385         int n = 0, major, minor, check;
386         isc_buffer_t b;
387         isc_token_t token;
388         unsigned char *data = NULL;
389         unsigned int opt = ISC_LEXOPT_EOL;
390         isc_stdtime_t when;
391         isc_result_t ret;
392         isc_boolean_t external = ISC_FALSE;
393
394         REQUIRE(priv != NULL);
395
396         priv->nelements = 0;
397         memset(priv->elements, 0, sizeof(priv->elements));
398
399 #define NEXTTOKEN(lex, opt, token)                              \
400         do {                                                    \
401                 ret = isc_lex_gettoken(lex, opt, token);        \
402                 if (ret != ISC_R_SUCCESS)                       \
403                         goto fail;                              \
404         } while (0)
405
406 #define READLINE(lex, opt, token)                               \
407         do {                                                    \
408                 ret = isc_lex_gettoken(lex, opt, token);        \
409                 if (ret == ISC_R_EOF)                           \
410                         break;                                  \
411                 else if (ret != ISC_R_SUCCESS)                  \
412                         goto fail;                              \
413         } while ((*token).type != isc_tokentype_eol)
414
415         /*
416          * Read the description line.
417          */
418         NEXTTOKEN(lex, opt, &token);
419         if (token.type != isc_tokentype_string ||
420             strcmp(DST_AS_STR(token), PRIVATE_KEY_STR) != 0)
421         {
422                 ret = DST_R_INVALIDPRIVATEKEY;
423                 goto fail;
424         }
425
426         NEXTTOKEN(lex, opt, &token);
427         if (token.type != isc_tokentype_string ||
428             (DST_AS_STR(token))[0] != 'v')
429         {
430                 ret = DST_R_INVALIDPRIVATEKEY;
431                 goto fail;
432         }
433         if (sscanf(DST_AS_STR(token), "v%d.%d", &major, &minor) != 2)
434         {
435                 ret = DST_R_INVALIDPRIVATEKEY;
436                 goto fail;
437         }
438
439         if (major > DST_MAJOR_VERSION) {
440                 ret = DST_R_INVALIDPRIVATEKEY;
441                 goto fail;
442         }
443
444         /*
445          * Store the private key format version number
446          */
447         dst_key_setprivateformat(key, major, minor);
448
449         READLINE(lex, opt, &token);
450
451         /*
452          * Read the algorithm line.
453          */
454         NEXTTOKEN(lex, opt, &token);
455         if (token.type != isc_tokentype_string ||
456             strcmp(DST_AS_STR(token), ALGORITHM_STR) != 0)
457         {
458                 ret = DST_R_INVALIDPRIVATEKEY;
459                 goto fail;
460         }
461
462         NEXTTOKEN(lex, opt | ISC_LEXOPT_NUMBER, &token);
463         if (token.type != isc_tokentype_number ||
464             token.value.as_ulong != (unsigned long) dst_key_alg(key))
465         {
466                 ret = DST_R_INVALIDPRIVATEKEY;
467                 goto fail;
468         }
469
470         READLINE(lex, opt, &token);
471
472         /*
473          * Read the key data.
474          */
475         for (n = 0; n < MAXFIELDS; n++) {
476                 int tag;
477                 isc_region_t r;
478                 do {
479                         ret = isc_lex_gettoken(lex, opt, &token);
480                         if (ret == ISC_R_EOF)
481                                 goto done;
482                         if (ret != ISC_R_SUCCESS)
483                                 goto fail;
484                 } while (token.type == isc_tokentype_eol);
485
486                 if (token.type != isc_tokentype_string) {
487                         ret = DST_R_INVALIDPRIVATEKEY;
488                         goto fail;
489                 }
490
491                 if (strcmp(DST_AS_STR(token), "External:") == 0) {
492                         external = ISC_TRUE;
493                         goto next;
494                 }
495
496                 /* Numeric metadata */
497                 tag = find_numericdata(DST_AS_STR(token));
498                 if (tag >= 0) {
499                         INSIST(tag < NUMERIC_NTAGS);
500
501                         NEXTTOKEN(lex, opt | ISC_LEXOPT_NUMBER, &token);
502                         if (token.type != isc_tokentype_number) {
503                                 ret = DST_R_INVALIDPRIVATEKEY;
504                                 goto fail;
505                         }
506
507                         dst_key_setnum(key, tag, token.value.as_ulong);
508                         goto next;
509                 }
510
511                 /* Timing metadata */
512                 tag = find_timedata(DST_AS_STR(token));
513                 if (tag >= 0) {
514                         INSIST(tag < TIMING_NTAGS);
515
516                         NEXTTOKEN(lex, opt, &token);
517                         if (token.type != isc_tokentype_string) {
518                                 ret = DST_R_INVALIDPRIVATEKEY;
519                                 goto fail;
520                         }
521
522                         ret = dns_time32_fromtext(DST_AS_STR(token), &when);
523                         if (ret != ISC_R_SUCCESS)
524                                 goto fail;
525
526                         dst_key_settime(key, tag, when);
527
528                         goto next;
529                 }
530
531                 /* Key data */
532                 tag = find_value(DST_AS_STR(token), alg);
533                 if (tag < 0 && minor > DST_MINOR_VERSION)
534                         goto next;
535                 else if (tag < 0) {
536                         ret = DST_R_INVALIDPRIVATEKEY;
537                         goto fail;
538                 }
539
540                 priv->elements[n].tag = tag;
541
542                 data = (unsigned char *) isc_mem_get(mctx, MAXFIELDSIZE);
543                 if (data == NULL)
544                         goto fail;
545
546                 isc_buffer_init(&b, data, MAXFIELDSIZE);
547                 ret = isc_base64_tobuffer(lex, &b, -1);
548                 if (ret != ISC_R_SUCCESS)
549                         goto fail;
550
551                 isc_buffer_usedregion(&b, &r);
552                 priv->elements[n].length = r.length;
553                 priv->elements[n].data = r.base;
554                 priv->nelements++;
555
556           next:
557                 READLINE(lex, opt, &token);
558                 data = NULL;
559         }
560
561  done:
562         if (external && priv->nelements != 0) {
563                 ret = DST_R_INVALIDPRIVATEKEY;
564                 goto fail;
565         }
566
567         check = check_data(priv, alg, ISC_TRUE, external);
568         if (check < 0) {
569                 ret = DST_R_INVALIDPRIVATEKEY;
570                 goto fail;
571         } else if (check != ISC_R_SUCCESS) {
572                 ret = check;
573                 goto fail;
574         }
575
576         key->external = external;
577
578         return (ISC_R_SUCCESS);
579
580 fail:
581         dst__privstruct_free(priv, mctx);
582         if (data != NULL)
583                 isc_mem_put(mctx, data, MAXFIELDSIZE);
584
585         return (ret);
586 }
587
588 isc_result_t
589 dst__privstruct_writefile(const dst_key_t *key, const dst_private_t *priv,
590                           const char *directory)
591 {
592         FILE *fp;
593         isc_result_t result;
594         char filename[ISC_DIR_NAMEMAX];
595         char buffer[MAXFIELDSIZE * 2];
596         isc_fsaccess_t access;
597         isc_stdtime_t when;
598         isc_uint32_t value;
599         isc_buffer_t b;
600         isc_region_t r;
601         int major, minor;
602         mode_t mode;
603         int i, ret;
604
605         REQUIRE(priv != NULL);
606
607         ret = check_data(priv, dst_key_alg(key), ISC_FALSE, key->external);
608         if (ret < 0)
609                 return (DST_R_INVALIDPRIVATEKEY);
610         else if (ret != ISC_R_SUCCESS)
611                 return (ret);
612
613         isc_buffer_init(&b, filename, sizeof(filename));
614         result = dst_key_buildfilename(key, DST_TYPE_PRIVATE, directory, &b);
615         if (result != ISC_R_SUCCESS)
616                 return (result);
617
618         result = isc_file_mode(filename, &mode);
619         if (result == ISC_R_SUCCESS && mode != 0600) {
620                 /* File exists; warn that we are changing its permissions */
621                 isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
622                               DNS_LOGMODULE_DNSSEC, ISC_LOG_WARNING,
623                               "Permissions on the file %s "
624                               "have changed from 0%o to 0600 as "
625                               "a result of this operation.",
626                               filename, (unsigned int)mode);
627         }
628
629         if ((fp = fopen(filename, "w")) == NULL)
630                 return (DST_R_WRITEERROR);
631
632         access = 0;
633         isc_fsaccess_add(ISC_FSACCESS_OWNER,
634                          ISC_FSACCESS_READ | ISC_FSACCESS_WRITE,
635                          &access);
636         (void)isc_fsaccess_set(filename, access);
637
638         dst_key_getprivateformat(key, &major, &minor);
639         if (major == 0 && minor == 0) {
640                 major = DST_MAJOR_VERSION;
641                 minor = DST_MINOR_VERSION;
642         }
643
644         /* XXXDCL return value should be checked for full filesystem */
645         fprintf(fp, "%s v%d.%d\n", PRIVATE_KEY_STR, major, minor);
646
647         fprintf(fp, "%s %d ", ALGORITHM_STR, dst_key_alg(key));
648
649         /* XXXVIX this switch statement is too sparse to gen a jump table. */
650         switch (dst_key_alg(key)) {
651         case DST_ALG_RSAMD5:
652                 fprintf(fp, "(RSA)\n");
653                 break;
654         case DST_ALG_DH:
655                 fprintf(fp, "(DH)\n");
656                 break;
657         case DST_ALG_DSA:
658                 fprintf(fp, "(DSA)\n");
659                 break;
660         case DST_ALG_RSASHA1:
661                 fprintf(fp, "(RSASHA1)\n");
662                 break;
663         case DST_ALG_NSEC3RSASHA1:
664                 fprintf(fp, "(NSEC3RSASHA1)\n");
665                 break;
666         case DST_ALG_NSEC3DSA:
667                 fprintf(fp, "(NSEC3DSA)\n");
668                 break;
669         case DST_ALG_RSASHA256:
670                 fprintf(fp, "(RSASHA256)\n");
671                 break;
672         case DST_ALG_RSASHA512:
673                 fprintf(fp, "(RSASHA512)\n");
674                 break;
675         case DST_ALG_ECCGOST:
676                 fprintf(fp, "(ECC-GOST)\n");
677                 break;
678         case DST_ALG_ECDSA256:
679                 fprintf(fp, "(ECDSAP256SHA256)\n");
680                 break;
681         case DST_ALG_ECDSA384:
682                 fprintf(fp, "(ECDSAP384SHA384)\n");
683                 break;
684         case DST_ALG_HMACMD5:
685                 fprintf(fp, "(HMAC_MD5)\n");
686                 break;
687         case DST_ALG_HMACSHA1:
688                 fprintf(fp, "(HMAC_SHA1)\n");
689                 break;
690         case DST_ALG_HMACSHA224:
691                 fprintf(fp, "(HMAC_SHA224)\n");
692                 break;
693         case DST_ALG_HMACSHA256:
694                 fprintf(fp, "(HMAC_SHA256)\n");
695                 break;
696         case DST_ALG_HMACSHA384:
697                 fprintf(fp, "(HMAC_SHA384)\n");
698                 break;
699         case DST_ALG_HMACSHA512:
700                 fprintf(fp, "(HMAC_SHA512)\n");
701                 break;
702         default:
703                 fprintf(fp, "(?)\n");
704                 break;
705         }
706
707         for (i = 0; i < priv->nelements; i++) {
708                 const char *s;
709
710                 s = find_tag(priv->elements[i].tag);
711
712                 r.base = priv->elements[i].data;
713                 r.length = priv->elements[i].length;
714                 isc_buffer_init(&b, buffer, sizeof(buffer));
715                 result = isc_base64_totext(&r, sizeof(buffer), "", &b);
716                 if (result != ISC_R_SUCCESS) {
717                         fclose(fp);
718                         return (DST_R_INVALIDPRIVATEKEY);
719                 }
720                 isc_buffer_usedregion(&b, &r);
721
722                fprintf(fp, "%s %.*s\n", s, (int)r.length, r.base);
723         }
724
725         if (key->external)
726                fprintf(fp, "External:\n");
727
728         /* Add the metadata tags */
729         if (major > 1 || (major == 1 && minor >= 3)) {
730                 for (i = 0; i < NUMERIC_NTAGS; i++) {
731                         result = dst_key_getnum(key, i, &value);
732                         if (result != ISC_R_SUCCESS)
733                                 continue;
734                         fprintf(fp, "%s %u\n", numerictags[i], value);
735                 }
736                 for (i = 0; i < TIMING_NTAGS; i++) {
737                         result = dst_key_gettime(key, i, &when);
738                         if (result != ISC_R_SUCCESS)
739                                 continue;
740
741                         isc_buffer_init(&b, buffer, sizeof(buffer));
742                         result = dns_time32_totext(when, &b);
743                         if (result != ISC_R_SUCCESS) {
744                                fclose(fp);
745                                return (DST_R_INVALIDPRIVATEKEY);
746                         }
747
748                         isc_buffer_usedregion(&b, &r);
749
750                         fprintf(fp, "%s %.*s\n", timetags[i], (int)r.length,
751                                 r.base);
752                 }
753         }
754
755         fflush(fp);
756         result = ferror(fp) ? DST_R_WRITEERROR : ISC_R_SUCCESS;
757         fclose(fp);
758         return (result);
759 }
760
761 /*! \file */