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