/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 */ |