Coverage Report

Created: 2026-01-24 06:24

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