xref: /netbsd-src/external/mpl/bind/dist/lib/dns/dst_parse.c (revision bcda20f65a8566e103791ec395f7f499ef322704)
1*bcda20f6Schristos /*	$NetBSD: dst_parse.c,v 1.12 2025/01/26 16:25:22 christos Exp $	*/
2d68c78b8Schristos 
3d68c78b8Schristos /*
48596601aSchristos  * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
58596601aSchristos  *
68596601aSchristos  * SPDX-License-Identifier: MPL-2.0 AND ISC
7d68c78b8Schristos  *
8d68c78b8Schristos  * This Source Code Form is subject to the terms of the Mozilla Public
9d68c78b8Schristos  * License, v. 2.0. If a copy of the MPL was not distributed with this
10fce770bdSchristos  * file, you can obtain one at https://mozilla.org/MPL/2.0/.
11d68c78b8Schristos  *
12d68c78b8Schristos  * See the COPYRIGHT file distributed with this work for additional
13d68c78b8Schristos  * information regarding copyright ownership.
148596601aSchristos  */
158596601aSchristos 
168596601aSchristos /*
178596601aSchristos  * Copyright (C) Network Associates, Inc.
18d68c78b8Schristos  *
19d68c78b8Schristos  * Permission to use, copy, modify, and/or distribute this software for any
20d68c78b8Schristos  * purpose with or without fee is hereby granted, provided that the above
21d68c78b8Schristos  * copyright notice and this permission notice appear in all copies.
22d68c78b8Schristos  *
23d68c78b8Schristos  * THE SOFTWARE IS PROVIDED "AS IS" AND ISC AND NETWORK ASSOCIATES DISCLAIMS
24d68c78b8Schristos  * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
25d68c78b8Schristos  * WARRANTIES OF MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE
26d68c78b8Schristos  * FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
27d68c78b8Schristos  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
28d68c78b8Schristos  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
29d68c78b8Schristos  * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
30d68c78b8Schristos  */
31d68c78b8Schristos 
325606745fSchristos #include "dst_parse.h"
33d4a20c3eSchristos #include <inttypes.h>
34d4a20c3eSchristos #include <stdbool.h>
35bb5aa156Schristos #include <unistd.h>
36d4a20c3eSchristos 
37d68c78b8Schristos #include <isc/base64.h>
38d68c78b8Schristos #include <isc/dir.h>
39d68c78b8Schristos #include <isc/file.h>
40d68c78b8Schristos #include <isc/lex.h>
41d68c78b8Schristos #include <isc/mem.h>
42d68c78b8Schristos #include <isc/stdtime.h>
43d68c78b8Schristos #include <isc/string.h>
44d68c78b8Schristos #include <isc/util.h>
45d68c78b8Schristos 
46d68c78b8Schristos #include <dns/log.h>
475606745fSchristos #include <dns/time.h>
48d68c78b8Schristos 
495606745fSchristos #include "dst_internal.h"
50bb5aa156Schristos #include "isc/result.h"
51d68c78b8Schristos 
52d68c78b8Schristos #define DST_AS_STR(t) ((t).value.as_textregion.base)
53d68c78b8Schristos 
54d68c78b8Schristos #define PRIVATE_KEY_STR "Private-key-format:"
55d68c78b8Schristos #define ALGORITHM_STR	"Algorithm:"
56d68c78b8Schristos 
57d68c78b8Schristos #define TIMING_NTAGS (DST_MAX_TIMES + 1)
58d68c78b8Schristos static const char *timetags[TIMING_NTAGS] = {
595606745fSchristos 	"Created:",    "Publish:", "Activate:",	 "Revoke:",
605606745fSchristos 	"Inactive:",   "Delete:",  "DSPublish:", "SyncPublish:",
615606745fSchristos 	"SyncDelete:", NULL,	   NULL,	 NULL,
625606745fSchristos 	NULL
63d68c78b8Schristos };
64d68c78b8Schristos 
65d68c78b8Schristos #define NUMERIC_NTAGS (DST_MAX_NUMERIC + 1)
66d68c78b8Schristos static const char *numerictags[NUMERIC_NTAGS] = {
6709f4e0c3Schristos 	"Predecessor:", "Successor:", "MaxTTL:", "RollPeriod:", NULL, NULL, NULL
68d68c78b8Schristos };
69d68c78b8Schristos 
70d68c78b8Schristos struct parse_map {
71d68c78b8Schristos 	const int value;
72d68c78b8Schristos 	const char *tag;
73d68c78b8Schristos };
74d68c78b8Schristos 
755606745fSchristos static struct parse_map map[] = { { TAG_RSA_MODULUS, "Modulus:" },
76d68c78b8Schristos 				  { TAG_RSA_PUBLICEXPONENT, "PublicExponent:" },
775606745fSchristos 				  { TAG_RSA_PRIVATEEXPONENT, "PrivateExponent"
785606745fSchristos 							     ":" },
79d68c78b8Schristos 				  { TAG_RSA_PRIME1, "Prime1:" },
80d68c78b8Schristos 				  { TAG_RSA_PRIME2, "Prime2:" },
81d68c78b8Schristos 				  { TAG_RSA_EXPONENT1, "Exponent1:" },
82d68c78b8Schristos 				  { TAG_RSA_EXPONENT2, "Exponent2:" },
83d68c78b8Schristos 				  { TAG_RSA_COEFFICIENT, "Coefficient:" },
84d68c78b8Schristos 				  { TAG_RSA_ENGINE, "Engine:" },
85d68c78b8Schristos 				  { TAG_RSA_LABEL, "Label:" },
86d68c78b8Schristos 
87d68c78b8Schristos 				  { TAG_ECDSA_PRIVATEKEY, "PrivateKey:" },
88d68c78b8Schristos 				  { TAG_ECDSA_ENGINE, "Engine:" },
89d68c78b8Schristos 				  { TAG_ECDSA_LABEL, "Label:" },
90d68c78b8Schristos 
91d68c78b8Schristos 				  { TAG_EDDSA_PRIVATEKEY, "PrivateKey:" },
92d68c78b8Schristos 				  { TAG_EDDSA_ENGINE, "Engine:" },
93d68c78b8Schristos 				  { TAG_EDDSA_LABEL, "Label:" },
94d68c78b8Schristos 
95d68c78b8Schristos 				  { TAG_HMACMD5_KEY, "Key:" },
96d68c78b8Schristos 				  { TAG_HMACMD5_BITS, "Bits:" },
97d68c78b8Schristos 
98d68c78b8Schristos 				  { TAG_HMACSHA1_KEY, "Key:" },
99d68c78b8Schristos 				  { TAG_HMACSHA1_BITS, "Bits:" },
100d68c78b8Schristos 
101d68c78b8Schristos 				  { TAG_HMACSHA224_KEY, "Key:" },
102d68c78b8Schristos 				  { TAG_HMACSHA224_BITS, "Bits:" },
103d68c78b8Schristos 
104d68c78b8Schristos 				  { TAG_HMACSHA256_KEY, "Key:" },
105d68c78b8Schristos 				  { TAG_HMACSHA256_BITS, "Bits:" },
106d68c78b8Schristos 
107d68c78b8Schristos 				  { TAG_HMACSHA384_KEY, "Key:" },
108d68c78b8Schristos 				  { TAG_HMACSHA384_BITS, "Bits:" },
109d68c78b8Schristos 
110d68c78b8Schristos 				  { TAG_HMACSHA512_KEY, "Key:" },
111d68c78b8Schristos 				  { TAG_HMACSHA512_BITS, "Bits:" },
112d68c78b8Schristos 
1135606745fSchristos 				  { 0, NULL } };
114d68c78b8Schristos 
115d68c78b8Schristos static int
116d68c78b8Schristos find_value(const char *s, const unsigned int alg) {
117d68c78b8Schristos 	int i;
118d68c78b8Schristos 
119d68c78b8Schristos 	for (i = 0; map[i].tag != NULL; i++) {
120d68c78b8Schristos 		if (strcasecmp(s, map[i].tag) == 0 &&
121903adeddSchristos 		    (TAG_ALG(map[i].value) == alg))
122903adeddSchristos 		{
123*bcda20f6Schristos 			return map[i].value;
124d68c78b8Schristos 		}
1255606745fSchristos 	}
126*bcda20f6Schristos 	return -1;
127d68c78b8Schristos }
128d68c78b8Schristos 
129d68c78b8Schristos static const char *
130d68c78b8Schristos find_tag(const int value) {
131d68c78b8Schristos 	int i;
132d68c78b8Schristos 
133d68c78b8Schristos 	for (i = 0;; i++) {
1345606745fSchristos 		if (map[i].tag == NULL) {
135*bcda20f6Schristos 			return NULL;
1365606745fSchristos 		} else if (value == map[i].value) {
137*bcda20f6Schristos 			return map[i].tag;
138d68c78b8Schristos 		}
139d68c78b8Schristos 	}
1405606745fSchristos }
141d68c78b8Schristos 
142d68c78b8Schristos static int
143d68c78b8Schristos find_metadata(const char *s, const char *tags[], int ntags) {
144d68c78b8Schristos 	int i;
145d68c78b8Schristos 
146d68c78b8Schristos 	for (i = 0; i < ntags; i++) {
1475606745fSchristos 		if (tags[i] != NULL && strcasecmp(s, tags[i]) == 0) {
148*bcda20f6Schristos 			return i;
149d68c78b8Schristos 		}
1505606745fSchristos 	}
151d68c78b8Schristos 
152*bcda20f6Schristos 	return -1;
153d68c78b8Schristos }
154d68c78b8Schristos 
155d68c78b8Schristos static int
156d68c78b8Schristos find_timedata(const char *s) {
157*bcda20f6Schristos 	return find_metadata(s, timetags, TIMING_NTAGS);
158d68c78b8Schristos }
159d68c78b8Schristos 
160d68c78b8Schristos static int
161d68c78b8Schristos find_numericdata(const char *s) {
162*bcda20f6Schristos 	return find_metadata(s, numerictags, NUMERIC_NTAGS);
163d68c78b8Schristos }
164d68c78b8Schristos 
165d68c78b8Schristos static int
166d4a20c3eSchristos check_rsa(const dst_private_t *priv, bool external) {
167d68c78b8Schristos 	int i, j;
168d4a20c3eSchristos 	bool have[RSA_NTAGS];
169d4a20c3eSchristos 	bool ok;
170d68c78b8Schristos 	unsigned int mask;
171d68c78b8Schristos 
1725606745fSchristos 	if (external) {
173*bcda20f6Schristos 		return (priv->nelements == 0) ? 0 : -1;
1745606745fSchristos 	}
175d68c78b8Schristos 
1765606745fSchristos 	for (i = 0; i < RSA_NTAGS; i++) {
177d4a20c3eSchristos 		have[i] = false;
1785606745fSchristos 	}
179d68c78b8Schristos 
180d68c78b8Schristos 	for (j = 0; j < priv->nelements; j++) {
1815606745fSchristos 		for (i = 0; i < RSA_NTAGS; i++) {
1825606745fSchristos 			if (priv->elements[j].tag == TAG(DST_ALG_RSA, i)) {
183d68c78b8Schristos 				break;
1845606745fSchristos 			}
1855606745fSchristos 		}
1865606745fSchristos 		if (i == RSA_NTAGS) {
187*bcda20f6Schristos 			return -1;
1885606745fSchristos 		}
189d4a20c3eSchristos 		have[i] = true;
190d68c78b8Schristos 	}
191d68c78b8Schristos 
192d68c78b8Schristos 	mask = (1ULL << TAG_SHIFT) - 1;
193d68c78b8Schristos 
194*bcda20f6Schristos 	if (have[TAG_RSA_LABEL & mask]) {
195d68c78b8Schristos 		ok = have[TAG_RSA_MODULUS & mask] &&
196*bcda20f6Schristos 		     have[TAG_RSA_PUBLICEXPONENT & mask];
1975606745fSchristos 	} else {
198d68c78b8Schristos 		ok = have[TAG_RSA_MODULUS & mask] &&
199d68c78b8Schristos 		     have[TAG_RSA_PUBLICEXPONENT & mask] &&
200d68c78b8Schristos 		     have[TAG_RSA_PRIVATEEXPONENT & mask] &&
201d68c78b8Schristos 		     have[TAG_RSA_PRIME1 & mask] &&
202d68c78b8Schristos 		     have[TAG_RSA_PRIME2 & mask] &&
203d68c78b8Schristos 		     have[TAG_RSA_EXPONENT1 & mask] &&
204d68c78b8Schristos 		     have[TAG_RSA_EXPONENT2 & mask] &&
205d68c78b8Schristos 		     have[TAG_RSA_COEFFICIENT & mask];
2065606745fSchristos 	}
207*bcda20f6Schristos 	return ok ? 0 : -1;
208d68c78b8Schristos }
209d68c78b8Schristos 
210d68c78b8Schristos static int
211d4a20c3eSchristos check_ecdsa(const dst_private_t *priv, bool external) {
212d68c78b8Schristos 	int i, j;
213d4a20c3eSchristos 	bool have[ECDSA_NTAGS];
214d4a20c3eSchristos 	bool ok;
215d68c78b8Schristos 	unsigned int mask;
216d68c78b8Schristos 
2175606745fSchristos 	if (external) {
218*bcda20f6Schristos 		return (priv->nelements == 0) ? 0 : -1;
2195606745fSchristos 	}
220d68c78b8Schristos 
2215606745fSchristos 	for (i = 0; i < ECDSA_NTAGS; i++) {
222d4a20c3eSchristos 		have[i] = false;
2235606745fSchristos 	}
224d68c78b8Schristos 	for (j = 0; j < priv->nelements; j++) {
2255606745fSchristos 		for (i = 0; i < ECDSA_NTAGS; i++) {
2265606745fSchristos 			if (priv->elements[j].tag == TAG(DST_ALG_ECDSA256, i)) {
227d68c78b8Schristos 				break;
2285606745fSchristos 			}
2295606745fSchristos 		}
2305606745fSchristos 		if (i == ECDSA_NTAGS) {
231*bcda20f6Schristos 			return -1;
2325606745fSchristos 		}
233d4a20c3eSchristos 		have[i] = true;
234d68c78b8Schristos 	}
235d68c78b8Schristos 
236d68c78b8Schristos 	mask = (1ULL << TAG_SHIFT) - 1;
237d68c78b8Schristos 
238*bcda20f6Schristos 	ok = have[TAG_ECDSA_LABEL & mask] || have[TAG_ECDSA_PRIVATEKEY & mask];
239*bcda20f6Schristos 
240*bcda20f6Schristos 	return ok ? 0 : -1;
241d68c78b8Schristos }
242d68c78b8Schristos 
243d68c78b8Schristos static int
244d4a20c3eSchristos check_eddsa(const dst_private_t *priv, bool external) {
245d68c78b8Schristos 	int i, j;
246d4a20c3eSchristos 	bool have[EDDSA_NTAGS];
247d4a20c3eSchristos 	bool ok;
248d68c78b8Schristos 	unsigned int mask;
249d68c78b8Schristos 
2505606745fSchristos 	if (external) {
251*bcda20f6Schristos 		return (priv->nelements == 0) ? 0 : -1;
2525606745fSchristos 	}
253d68c78b8Schristos 
2545606745fSchristos 	for (i = 0; i < EDDSA_NTAGS; i++) {
255d4a20c3eSchristos 		have[i] = false;
2565606745fSchristos 	}
257d68c78b8Schristos 	for (j = 0; j < priv->nelements; j++) {
2585606745fSchristos 		for (i = 0; i < EDDSA_NTAGS; i++) {
2595606745fSchristos 			if (priv->elements[j].tag == TAG(DST_ALG_ED25519, i)) {
260d68c78b8Schristos 				break;
2615606745fSchristos 			}
2625606745fSchristos 		}
2635606745fSchristos 		if (i == EDDSA_NTAGS) {
264*bcda20f6Schristos 			return -1;
2655606745fSchristos 		}
266d4a20c3eSchristos 		have[i] = true;
267d68c78b8Schristos 	}
268d68c78b8Schristos 
269d68c78b8Schristos 	mask = (1ULL << TAG_SHIFT) - 1;
270d68c78b8Schristos 
271*bcda20f6Schristos 	ok = have[TAG_EDDSA_LABEL & mask] || have[TAG_EDDSA_PRIVATEKEY & mask];
272*bcda20f6Schristos 
273*bcda20f6Schristos 	return ok ? 0 : -1;
274d68c78b8Schristos }
275d68c78b8Schristos 
276d68c78b8Schristos static int
277d4a20c3eSchristos check_hmac_md5(const dst_private_t *priv, bool old) {
278d68c78b8Schristos 	int i, j;
279d68c78b8Schristos 
280d68c78b8Schristos 	if (priv->nelements != HMACMD5_NTAGS) {
281d68c78b8Schristos 		/*
282d68c78b8Schristos 		 * If this is a good old format and we are accepting
283d68c78b8Schristos 		 * the old format return success.
284d68c78b8Schristos 		 */
285d68c78b8Schristos 		if (old && priv->nelements == OLD_HMACMD5_NTAGS &&
286d68c78b8Schristos 		    priv->elements[0].tag == TAG_HMACMD5_KEY)
2875606745fSchristos 		{
288*bcda20f6Schristos 			return 0;
2895606745fSchristos 		}
290*bcda20f6Schristos 		return -1;
291d68c78b8Schristos 	}
292d68c78b8Schristos 	/*
293d68c78b8Schristos 	 * We must be new format at this point.
294d68c78b8Schristos 	 */
295d68c78b8Schristos 	for (i = 0; i < HMACMD5_NTAGS; i++) {
2965606745fSchristos 		for (j = 0; j < priv->nelements; j++) {
2975606745fSchristos 			if (priv->elements[j].tag == TAG(DST_ALG_HMACMD5, i)) {
298d68c78b8Schristos 				break;
2995606745fSchristos 			}
3005606745fSchristos 		}
3015606745fSchristos 		if (j == priv->nelements) {
302*bcda20f6Schristos 			return -1;
303d68c78b8Schristos 		}
3045606745fSchristos 	}
305*bcda20f6Schristos 	return 0;
306d68c78b8Schristos }
307d68c78b8Schristos 
308d68c78b8Schristos static int
309d68c78b8Schristos check_hmac_sha(const dst_private_t *priv, unsigned int ntags,
3105606745fSchristos 	       unsigned int alg) {
311d68c78b8Schristos 	unsigned int i, j;
3125606745fSchristos 	if (priv->nelements != ntags) {
313*bcda20f6Schristos 		return -1;
3145606745fSchristos 	}
315d68c78b8Schristos 	for (i = 0; i < ntags; i++) {
3165606745fSchristos 		for (j = 0; j < priv->nelements; j++) {
3175606745fSchristos 			if (priv->elements[j].tag == TAG(alg, i)) {
318d68c78b8Schristos 				break;
3195606745fSchristos 			}
3205606745fSchristos 		}
3215606745fSchristos 		if (j == priv->nelements) {
322*bcda20f6Schristos 			return -1;
323d68c78b8Schristos 		}
3245606745fSchristos 	}
325*bcda20f6Schristos 	return 0;
326d68c78b8Schristos }
327d68c78b8Schristos 
328d68c78b8Schristos static int
3295606745fSchristos check_data(const dst_private_t *priv, const unsigned int alg, bool old,
3305606745fSchristos 	   bool external) {
331d68c78b8Schristos 	switch (alg) {
3325e267ba4Schristos 	case DST_ALG_RSA:
333d68c78b8Schristos 	case DST_ALG_RSASHA1:
334d68c78b8Schristos 	case DST_ALG_NSEC3RSASHA1:
335d68c78b8Schristos 	case DST_ALG_RSASHA256:
336d68c78b8Schristos 	case DST_ALG_RSASHA512:
337*bcda20f6Schristos 		return check_rsa(priv, external);
338d68c78b8Schristos 	case DST_ALG_ECDSA256:
339d68c78b8Schristos 	case DST_ALG_ECDSA384:
340*bcda20f6Schristos 		return check_ecdsa(priv, external);
341d68c78b8Schristos 	case DST_ALG_ED25519:
342d68c78b8Schristos 	case DST_ALG_ED448:
343*bcda20f6Schristos 		return check_eddsa(priv, external);
344d68c78b8Schristos 	case DST_ALG_HMACMD5:
345*bcda20f6Schristos 		return check_hmac_md5(priv, old);
346d68c78b8Schristos 	case DST_ALG_HMACSHA1:
347*bcda20f6Schristos 		return check_hmac_sha(priv, HMACSHA1_NTAGS, alg);
348d68c78b8Schristos 	case DST_ALG_HMACSHA224:
349*bcda20f6Schristos 		return check_hmac_sha(priv, HMACSHA224_NTAGS, alg);
350d68c78b8Schristos 	case DST_ALG_HMACSHA256:
351*bcda20f6Schristos 		return check_hmac_sha(priv, HMACSHA256_NTAGS, alg);
352d68c78b8Schristos 	case DST_ALG_HMACSHA384:
353*bcda20f6Schristos 		return check_hmac_sha(priv, HMACSHA384_NTAGS, alg);
354d68c78b8Schristos 	case DST_ALG_HMACSHA512:
355*bcda20f6Schristos 		return check_hmac_sha(priv, HMACSHA512_NTAGS, alg);
356d68c78b8Schristos 	default:
357*bcda20f6Schristos 		return DST_R_UNSUPPORTEDALG;
358d68c78b8Schristos 	}
359d68c78b8Schristos }
360d68c78b8Schristos 
361d68c78b8Schristos void
362d68c78b8Schristos dst__privstruct_free(dst_private_t *priv, isc_mem_t *mctx) {
363d68c78b8Schristos 	int i;
364d68c78b8Schristos 
3655606745fSchristos 	if (priv == NULL) {
366d68c78b8Schristos 		return;
3675606745fSchristos 	}
368d68c78b8Schristos 	for (i = 0; i < priv->nelements; i++) {
3695606745fSchristos 		if (priv->elements[i].data == NULL) {
370d68c78b8Schristos 			continue;
3715606745fSchristos 		}
372d68c78b8Schristos 		memset(priv->elements[i].data, 0, MAXFIELDSIZE);
373d68c78b8Schristos 		isc_mem_put(mctx, priv->elements[i].data, MAXFIELDSIZE);
374d68c78b8Schristos 	}
375d68c78b8Schristos 	priv->nelements = 0;
376d68c78b8Schristos }
377d68c78b8Schristos 
378d68c78b8Schristos isc_result_t
379d68c78b8Schristos dst__privstruct_parse(dst_key_t *key, unsigned int alg, isc_lex_t *lex,
3805606745fSchristos 		      isc_mem_t *mctx, dst_private_t *priv) {
381d68c78b8Schristos 	int n = 0, major, minor, check;
382d68c78b8Schristos 	isc_buffer_t b;
383d68c78b8Schristos 	isc_token_t token;
384d68c78b8Schristos 	unsigned char *data = NULL;
385d68c78b8Schristos 	unsigned int opt = ISC_LEXOPT_EOL;
386d68c78b8Schristos 	isc_stdtime_t when;
387d68c78b8Schristos 	isc_result_t ret;
388d4a20c3eSchristos 	bool external = false;
389d68c78b8Schristos 
390d68c78b8Schristos 	REQUIRE(priv != NULL);
391d68c78b8Schristos 
392d68c78b8Schristos 	priv->nelements = 0;
393d68c78b8Schristos 	memset(priv->elements, 0, sizeof(priv->elements));
394d68c78b8Schristos 
395d68c78b8Schristos #define NEXTTOKEN(lex, opt, token)                       \
396d68c78b8Schristos 	do {                                             \
397d68c78b8Schristos 		ret = isc_lex_gettoken(lex, opt, token); \
398d68c78b8Schristos 		if (ret != ISC_R_SUCCESS)                \
399d68c78b8Schristos 			goto fail;                       \
40053cc4e50Srillig 	} while (0)
401d68c78b8Schristos 
402d68c78b8Schristos #define READLINE(lex, opt, token)                        \
403d68c78b8Schristos 	do {                                             \
404d68c78b8Schristos 		ret = isc_lex_gettoken(lex, opt, token); \
405d68c78b8Schristos 		if (ret == ISC_R_EOF)                    \
406d68c78b8Schristos 			break;                           \
407d68c78b8Schristos 		else if (ret != ISC_R_SUCCESS)           \
408d68c78b8Schristos 			goto fail;                       \
409d68c78b8Schristos 	} while ((*token).type != isc_tokentype_eol)
410d68c78b8Schristos 
411d68c78b8Schristos 	/*
412d68c78b8Schristos 	 * Read the description line.
413d68c78b8Schristos 	 */
414d68c78b8Schristos 	NEXTTOKEN(lex, opt, &token);
415d68c78b8Schristos 	if (token.type != isc_tokentype_string ||
416d68c78b8Schristos 	    strcmp(DST_AS_STR(token), PRIVATE_KEY_STR) != 0)
417d68c78b8Schristos 	{
418d68c78b8Schristos 		ret = DST_R_INVALIDPRIVATEKEY;
419d68c78b8Schristos 		goto fail;
420d68c78b8Schristos 	}
421d68c78b8Schristos 
422d68c78b8Schristos 	NEXTTOKEN(lex, opt, &token);
4235606745fSchristos 	if (token.type != isc_tokentype_string || (DST_AS_STR(token))[0] != 'v')
424d68c78b8Schristos 	{
425d68c78b8Schristos 		ret = DST_R_INVALIDPRIVATEKEY;
426d68c78b8Schristos 		goto fail;
427d68c78b8Schristos 	}
4285606745fSchristos 	if (sscanf(DST_AS_STR(token), "v%d.%d", &major, &minor) != 2) {
429d68c78b8Schristos 		ret = DST_R_INVALIDPRIVATEKEY;
430d68c78b8Schristos 		goto fail;
431d68c78b8Schristos 	}
432d68c78b8Schristos 
433d68c78b8Schristos 	if (major > DST_MAJOR_VERSION) {
434d68c78b8Schristos 		ret = DST_R_INVALIDPRIVATEKEY;
435d68c78b8Schristos 		goto fail;
436d68c78b8Schristos 	}
437d68c78b8Schristos 
438d68c78b8Schristos 	/*
439d68c78b8Schristos 	 * Store the private key format version number
440d68c78b8Schristos 	 */
441d68c78b8Schristos 	dst_key_setprivateformat(key, major, minor);
442d68c78b8Schristos 
443d68c78b8Schristos 	READLINE(lex, opt, &token);
444d68c78b8Schristos 
445d68c78b8Schristos 	/*
446d68c78b8Schristos 	 * Read the algorithm line.
447d68c78b8Schristos 	 */
448d68c78b8Schristos 	NEXTTOKEN(lex, opt, &token);
449d68c78b8Schristos 	if (token.type != isc_tokentype_string ||
450d68c78b8Schristos 	    strcmp(DST_AS_STR(token), ALGORITHM_STR) != 0)
451d68c78b8Schristos 	{
452d68c78b8Schristos 		ret = DST_R_INVALIDPRIVATEKEY;
453d68c78b8Schristos 		goto fail;
454d68c78b8Schristos 	}
455d68c78b8Schristos 
456d68c78b8Schristos 	NEXTTOKEN(lex, opt | ISC_LEXOPT_NUMBER, &token);
457d68c78b8Schristos 	if (token.type != isc_tokentype_number ||
458d68c78b8Schristos 	    token.value.as_ulong != (unsigned long)dst_key_alg(key))
459d68c78b8Schristos 	{
460d68c78b8Schristos 		ret = DST_R_INVALIDPRIVATEKEY;
461d68c78b8Schristos 		goto fail;
462d68c78b8Schristos 	}
463d68c78b8Schristos 
464d68c78b8Schristos 	READLINE(lex, opt, &token);
465d68c78b8Schristos 
466d68c78b8Schristos 	/*
467d68c78b8Schristos 	 * Read the key data.
468d68c78b8Schristos 	 */
469d68c78b8Schristos 	for (n = 0; n < MAXFIELDS; n++) {
470d68c78b8Schristos 		int tag;
471d68c78b8Schristos 		isc_region_t r;
472d68c78b8Schristos 		do {
473d68c78b8Schristos 			ret = isc_lex_gettoken(lex, opt, &token);
4745606745fSchristos 			if (ret == ISC_R_EOF) {
475d68c78b8Schristos 				goto done;
4765606745fSchristos 			}
4775606745fSchristos 			if (ret != ISC_R_SUCCESS) {
478d68c78b8Schristos 				goto fail;
4795606745fSchristos 			}
480d68c78b8Schristos 		} while (token.type == isc_tokentype_eol);
481d68c78b8Schristos 
482d68c78b8Schristos 		if (token.type != isc_tokentype_string) {
483d68c78b8Schristos 			ret = DST_R_INVALIDPRIVATEKEY;
484d68c78b8Schristos 			goto fail;
485d68c78b8Schristos 		}
486d68c78b8Schristos 
487d68c78b8Schristos 		if (strcmp(DST_AS_STR(token), "External:") == 0) {
488d4a20c3eSchristos 			external = true;
489d68c78b8Schristos 			goto next;
490d68c78b8Schristos 		}
491d68c78b8Schristos 
492d68c78b8Schristos 		/* Numeric metadata */
493d68c78b8Schristos 		tag = find_numericdata(DST_AS_STR(token));
494d68c78b8Schristos 		if (tag >= 0) {
495d68c78b8Schristos 			INSIST(tag < NUMERIC_NTAGS);
496d68c78b8Schristos 
497d68c78b8Schristos 			NEXTTOKEN(lex, opt | ISC_LEXOPT_NUMBER, &token);
498d68c78b8Schristos 			if (token.type != isc_tokentype_number) {
499d68c78b8Schristos 				ret = DST_R_INVALIDPRIVATEKEY;
500d68c78b8Schristos 				goto fail;
501d68c78b8Schristos 			}
502d68c78b8Schristos 
503d68c78b8Schristos 			dst_key_setnum(key, tag, token.value.as_ulong);
504d68c78b8Schristos 			goto next;
505d68c78b8Schristos 		}
506d68c78b8Schristos 
507d68c78b8Schristos 		/* Timing metadata */
508d68c78b8Schristos 		tag = find_timedata(DST_AS_STR(token));
509d68c78b8Schristos 		if (tag >= 0) {
510d68c78b8Schristos 			INSIST(tag < TIMING_NTAGS);
511d68c78b8Schristos 
512d68c78b8Schristos 			NEXTTOKEN(lex, opt, &token);
513d68c78b8Schristos 			if (token.type != isc_tokentype_string) {
514d68c78b8Schristos 				ret = DST_R_INVALIDPRIVATEKEY;
515d68c78b8Schristos 				goto fail;
516d68c78b8Schristos 			}
517d68c78b8Schristos 
518d68c78b8Schristos 			ret = dns_time32_fromtext(DST_AS_STR(token), &when);
5195606745fSchristos 			if (ret != ISC_R_SUCCESS) {
520d68c78b8Schristos 				goto fail;
5215606745fSchristos 			}
522d68c78b8Schristos 
523d68c78b8Schristos 			dst_key_settime(key, tag, when);
524d68c78b8Schristos 
525d68c78b8Schristos 			goto next;
526d68c78b8Schristos 		}
527d68c78b8Schristos 
528d68c78b8Schristos 		/* Key data */
529d68c78b8Schristos 		tag = find_value(DST_AS_STR(token), alg);
5305606745fSchristos 		if (tag < 0 && minor > DST_MINOR_VERSION) {
531d68c78b8Schristos 			goto next;
5325606745fSchristos 		} else if (tag < 0) {
533d68c78b8Schristos 			ret = DST_R_INVALIDPRIVATEKEY;
534d68c78b8Schristos 			goto fail;
535d68c78b8Schristos 		}
536d68c78b8Schristos 
537d68c78b8Schristos 		priv->elements[n].tag = tag;
538d68c78b8Schristos 
5395606745fSchristos 		data = isc_mem_get(mctx, MAXFIELDSIZE);
540d68c78b8Schristos 
541d68c78b8Schristos 		isc_buffer_init(&b, data, MAXFIELDSIZE);
542d68c78b8Schristos 		ret = isc_base64_tobuffer(lex, &b, -1);
5435606745fSchristos 		if (ret != ISC_R_SUCCESS) {
544d68c78b8Schristos 			goto fail;
5455606745fSchristos 		}
546d68c78b8Schristos 
547d68c78b8Schristos 		isc_buffer_usedregion(&b, &r);
548d68c78b8Schristos 		priv->elements[n].length = r.length;
549d68c78b8Schristos 		priv->elements[n].data = r.base;
550d68c78b8Schristos 		priv->nelements++;
551d68c78b8Schristos 
552d68c78b8Schristos 	next:
553d68c78b8Schristos 		READLINE(lex, opt, &token);
554d68c78b8Schristos 		data = NULL;
555d68c78b8Schristos 	}
556d68c78b8Schristos 
557d68c78b8Schristos done:
558d68c78b8Schristos 	if (external && priv->nelements != 0) {
559d68c78b8Schristos 		ret = DST_R_INVALIDPRIVATEKEY;
560d68c78b8Schristos 		goto fail;
561d68c78b8Schristos 	}
562d68c78b8Schristos 
563d4a20c3eSchristos 	check = check_data(priv, alg, true, external);
564d68c78b8Schristos 	if (check < 0) {
565d68c78b8Schristos 		ret = DST_R_INVALIDPRIVATEKEY;
566d68c78b8Schristos 		goto fail;
567d68c78b8Schristos 	} else if (check != ISC_R_SUCCESS) {
568d68c78b8Schristos 		ret = check;
569d68c78b8Schristos 		goto fail;
570d68c78b8Schristos 	}
571d68c78b8Schristos 
572d68c78b8Schristos 	key->external = external;
573d68c78b8Schristos 
574*bcda20f6Schristos 	return ISC_R_SUCCESS;
575d68c78b8Schristos 
576d68c78b8Schristos fail:
577d68c78b8Schristos 	dst__privstruct_free(priv, mctx);
5785606745fSchristos 	if (data != NULL) {
579d68c78b8Schristos 		isc_mem_put(mctx, data, MAXFIELDSIZE);
5805606745fSchristos 	}
581d68c78b8Schristos 
582*bcda20f6Schristos 	return ret;
583d68c78b8Schristos }
584d68c78b8Schristos 
585d68c78b8Schristos isc_result_t
586d68c78b8Schristos dst__privstruct_writefile(const dst_key_t *key, const dst_private_t *priv,
5875606745fSchristos 			  const char *directory) {
588d68c78b8Schristos 	FILE *fp;
589d68c78b8Schristos 	isc_result_t result;
590d4a20c3eSchristos 	char filename[NAME_MAX];
591bb5aa156Schristos 	char tmpname[NAME_MAX];
592d68c78b8Schristos 	char buffer[MAXFIELDSIZE * 2];
593d68c78b8Schristos 	isc_stdtime_t when;
594d4a20c3eSchristos 	uint32_t value;
595d68c78b8Schristos 	isc_buffer_t b;
596bb5aa156Schristos 	isc_buffer_t fileb;
597bb5aa156Schristos 	isc_buffer_t tmpb;
598d68c78b8Schristos 	isc_region_t r;
599d68c78b8Schristos 	int major, minor;
600d68c78b8Schristos 	mode_t mode;
601d68c78b8Schristos 	int i, ret;
602d68c78b8Schristos 
603d68c78b8Schristos 	REQUIRE(priv != NULL);
604d68c78b8Schristos 
605d4a20c3eSchristos 	ret = check_data(priv, dst_key_alg(key), false, key->external);
6065606745fSchristos 	if (ret < 0) {
607*bcda20f6Schristos 		return DST_R_INVALIDPRIVATEKEY;
6085606745fSchristos 	} else if (ret != ISC_R_SUCCESS) {
609*bcda20f6Schristos 		return ret;
6105606745fSchristos 	}
611d68c78b8Schristos 
612bb5aa156Schristos 	isc_buffer_init(&fileb, filename, sizeof(filename));
613bb5aa156Schristos 	result = dst_key_buildfilename(key, DST_TYPE_PRIVATE, directory,
614bb5aa156Schristos 				       &fileb);
6155606745fSchristos 	if (result != ISC_R_SUCCESS) {
616*bcda20f6Schristos 		return result;
6175606745fSchristos 	}
618d68c78b8Schristos 
619d68c78b8Schristos 	result = isc_file_mode(filename, &mode);
620bb5aa156Schristos 	if (result == ISC_R_SUCCESS && mode != (S_IRUSR | S_IWUSR)) {
621d68c78b8Schristos 		/* File exists; warn that we are changing its permissions */
622d68c78b8Schristos 		int level;
623d68c78b8Schristos 
624d68c78b8Schristos 		level = ISC_LOG_WARNING;
625d68c78b8Schristos 		isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
626d68c78b8Schristos 			      DNS_LOGMODULE_DNSSEC, level,
627d68c78b8Schristos 			      "Permissions on the file %s "
628d68c78b8Schristos 			      "have changed from 0%o to 0600 as "
629d68c78b8Schristos 			      "a result of this operation.",
630d68c78b8Schristos 			      filename, (unsigned int)mode);
631d68c78b8Schristos 	}
632d68c78b8Schristos 
633bb5aa156Schristos 	isc_buffer_init(&tmpb, tmpname, sizeof(tmpname));
634bb5aa156Schristos 	result = dst_key_buildfilename(key, DST_TYPE_TEMPLATE, directory,
635bb5aa156Schristos 				       &tmpb);
636bb5aa156Schristos 	if (result != ISC_R_SUCCESS) {
637*bcda20f6Schristos 		return result;
6385606745fSchristos 	}
639d68c78b8Schristos 
640bb5aa156Schristos 	fp = dst_key_open(tmpname, S_IRUSR | S_IWUSR);
641bb5aa156Schristos 	if (fp == NULL) {
642*bcda20f6Schristos 		return DST_R_WRITEERROR;
643bb5aa156Schristos 	}
644d68c78b8Schristos 
645d68c78b8Schristos 	dst_key_getprivateformat(key, &major, &minor);
646d68c78b8Schristos 	if (major == 0 && minor == 0) {
647d68c78b8Schristos 		major = DST_MAJOR_VERSION;
648d68c78b8Schristos 		minor = DST_MINOR_VERSION;
649d68c78b8Schristos 	}
650d68c78b8Schristos 
651d68c78b8Schristos 	/* XXXDCL return value should be checked for full filesystem */
652d68c78b8Schristos 	fprintf(fp, "%s v%d.%d\n", PRIVATE_KEY_STR, major, minor);
653d68c78b8Schristos 
654d68c78b8Schristos 	fprintf(fp, "%s %u ", ALGORITHM_STR, dst_key_alg(key));
655d68c78b8Schristos 
656d68c78b8Schristos 	switch (dst_key_alg(key)) {
657d68c78b8Schristos 	case DST_ALG_RSASHA1:
658d68c78b8Schristos 		fprintf(fp, "(RSASHA1)\n");
659d68c78b8Schristos 		break;
660d68c78b8Schristos 	case DST_ALG_NSEC3RSASHA1:
661d68c78b8Schristos 		fprintf(fp, "(NSEC3RSASHA1)\n");
662d68c78b8Schristos 		break;
663d68c78b8Schristos 	case DST_ALG_RSASHA256:
664d68c78b8Schristos 		fprintf(fp, "(RSASHA256)\n");
665d68c78b8Schristos 		break;
666d68c78b8Schristos 	case DST_ALG_RSASHA512:
667d68c78b8Schristos 		fprintf(fp, "(RSASHA512)\n");
668d68c78b8Schristos 		break;
669d68c78b8Schristos 	case DST_ALG_ECDSA256:
670d68c78b8Schristos 		fprintf(fp, "(ECDSAP256SHA256)\n");
671d68c78b8Schristos 		break;
672d68c78b8Schristos 	case DST_ALG_ECDSA384:
673d68c78b8Schristos 		fprintf(fp, "(ECDSAP384SHA384)\n");
674d68c78b8Schristos 		break;
675d68c78b8Schristos 	case DST_ALG_ED25519:
676d68c78b8Schristos 		fprintf(fp, "(ED25519)\n");
677d68c78b8Schristos 		break;
678d68c78b8Schristos 	case DST_ALG_ED448:
679d68c78b8Schristos 		fprintf(fp, "(ED448)\n");
680d68c78b8Schristos 		break;
681d68c78b8Schristos 	case DST_ALG_HMACMD5:
682d68c78b8Schristos 		fprintf(fp, "(HMAC_MD5)\n");
683d68c78b8Schristos 		break;
684d68c78b8Schristos 	case DST_ALG_HMACSHA1:
685d68c78b8Schristos 		fprintf(fp, "(HMAC_SHA1)\n");
686d68c78b8Schristos 		break;
687d68c78b8Schristos 	case DST_ALG_HMACSHA224:
688d68c78b8Schristos 		fprintf(fp, "(HMAC_SHA224)\n");
689d68c78b8Schristos 		break;
690d68c78b8Schristos 	case DST_ALG_HMACSHA256:
691d68c78b8Schristos 		fprintf(fp, "(HMAC_SHA256)\n");
692d68c78b8Schristos 		break;
693d68c78b8Schristos 	case DST_ALG_HMACSHA384:
694d68c78b8Schristos 		fprintf(fp, "(HMAC_SHA384)\n");
695d68c78b8Schristos 		break;
696d68c78b8Schristos 	case DST_ALG_HMACSHA512:
697d68c78b8Schristos 		fprintf(fp, "(HMAC_SHA512)\n");
698d68c78b8Schristos 		break;
699d68c78b8Schristos 	default:
700d68c78b8Schristos 		fprintf(fp, "(?)\n");
701d68c78b8Schristos 		break;
702d68c78b8Schristos 	}
703d68c78b8Schristos 
704d68c78b8Schristos 	for (i = 0; i < priv->nelements; i++) {
705d68c78b8Schristos 		const char *s;
706d68c78b8Schristos 
707d68c78b8Schristos 		s = find_tag(priv->elements[i].tag);
708d68c78b8Schristos 
709d68c78b8Schristos 		r.base = priv->elements[i].data;
710d68c78b8Schristos 		r.length = priv->elements[i].length;
711d68c78b8Schristos 		isc_buffer_init(&b, buffer, sizeof(buffer));
712d68c78b8Schristos 		result = isc_base64_totext(&r, sizeof(buffer), "", &b);
713d68c78b8Schristos 		if (result != ISC_R_SUCCESS) {
714*bcda20f6Schristos 			return dst_key_cleanup(tmpname, fp);
715d68c78b8Schristos 		}
716d68c78b8Schristos 		isc_buffer_usedregion(&b, &r);
717d68c78b8Schristos 
718d68c78b8Schristos 		fprintf(fp, "%s %.*s\n", s, (int)r.length, r.base);
719d68c78b8Schristos 	}
720d68c78b8Schristos 
7215606745fSchristos 	if (key->external) {
722d68c78b8Schristos 		fprintf(fp, "External:\n");
7235606745fSchristos 	}
724d68c78b8Schristos 
725d68c78b8Schristos 	/* Add the metadata tags */
726d68c78b8Schristos 	if (major > 1 || (major == 1 && minor >= 3)) {
727d68c78b8Schristos 		for (i = 0; i < NUMERIC_NTAGS; i++) {
728d68c78b8Schristos 			result = dst_key_getnum(key, i, &value);
7295606745fSchristos 			if (result != ISC_R_SUCCESS) {
730d68c78b8Schristos 				continue;
7315606745fSchristos 			}
7325606745fSchristos 			if (numerictags[i] != NULL) {
733d68c78b8Schristos 				fprintf(fp, "%s %u\n", numerictags[i], value);
734d68c78b8Schristos 			}
7355606745fSchristos 		}
736d68c78b8Schristos 		for (i = 0; i < TIMING_NTAGS; i++) {
737d68c78b8Schristos 			result = dst_key_gettime(key, i, &when);
7385606745fSchristos 			if (result != ISC_R_SUCCESS) {
739d68c78b8Schristos 				continue;
7405606745fSchristos 			}
741d68c78b8Schristos 
742d68c78b8Schristos 			isc_buffer_init(&b, buffer, sizeof(buffer));
743d68c78b8Schristos 			result = dns_time32_totext(when, &b);
744d68c78b8Schristos 			if (result != ISC_R_SUCCESS) {
745*bcda20f6Schristos 				return dst_key_cleanup(tmpname, fp);
746d68c78b8Schristos 			}
747d68c78b8Schristos 
748d68c78b8Schristos 			isc_buffer_usedregion(&b, &r);
749d68c78b8Schristos 
7505606745fSchristos 			if (timetags[i] != NULL) {
7515606745fSchristos 				fprintf(fp, "%s %.*s\n", timetags[i],
7525606745fSchristos 					(int)r.length, r.base);
7535606745fSchristos 			}
754d68c78b8Schristos 		}
755d68c78b8Schristos 	}
756d68c78b8Schristos 
757bb5aa156Schristos 	result = dst_key_close(tmpname, fp, filename);
758*bcda20f6Schristos 	return result;
759d68c78b8Schristos }
760d68c78b8Schristos 
761d68c78b8Schristos /*! \file */
762