xref: /netbsd-src/external/mpl/bind/dist/lib/dns/skr.c (revision bcda20f65a8566e103791ec395f7f499ef322704)
1 /*	$NetBSD: skr.c,v 1.2 2025/01/26 16:25:25 christos Exp $	*/
2 
3 /*
4  * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
5  *
6  * SPDX-License-Identifier: MPL-2.0
7  *
8  * This Source Code Form is subject to the terms of the Mozilla Public
9  * License, v. 2.0. If a copy of the MPL was not distributed with this
10  * file, you can obtain one at https://mozilla.org/MPL/2.0/.
11  *
12  * See the COPYRIGHT file distributed with this work for additional
13  * information regarding copyright ownership.
14  */
15 
16 /*! \file */
17 
18 #include <isc/lex.h>
19 #include <isc/log.h>
20 
21 #include <dns/callbacks.h>
22 #include <dns/fixedname.h>
23 #include <dns/rdata.h>
24 #include <dns/rdataclass.h>
25 #include <dns/rdatatype.h>
26 #include <dns/skr.h>
27 #include <dns/time.h>
28 #include <dns/ttl.h>
29 
30 #define CHECK(op)                            \
31 	do {                                 \
32 		result = (op);               \
33 		if (result != ISC_R_SUCCESS) \
34 			goto failure;        \
35 	} while (0)
36 
37 #define READLINE(lex, opt, token)
38 
39 #define NEXTTOKEN(lex, opt, token)                       \
40 	{                                                \
41 		ret = isc_lex_gettoken(lex, opt, token); \
42 		if (ret != ISC_R_SUCCESS)                \
43 			goto cleanup;                    \
44 	}
45 
46 #define BADTOKEN()                           \
47 	{                                    \
48 		ret = ISC_R_UNEXPECTEDTOKEN; \
49 		goto cleanup;                \
50 	}
51 
52 #define TOKENSIZ (8 * 1024)
53 #define STR(t)	 ((t).value.as_textregion.base)
54 
55 static isc_result_t
56 parse_rr(isc_lex_t *lex, isc_mem_t *mctx, char *owner, dns_name_t *origin,
57 	 dns_rdataclass_t rdclass, isc_buffer_t *buf, dns_ttl_t *ttl,
58 	 dns_rdatatype_t *rdtype, dns_rdata_t **rdata) {
59 	dns_rdatacallbacks_t callbacks;
60 	dns_fixedname_t dfname;
61 	dns_name_t *dname = NULL;
62 	dns_rdataclass_t clas;
63 	isc_buffer_t b;
64 	isc_token_t token;
65 	unsigned int opt = ISC_LEXOPT_EOL;
66 	isc_result_t ret = ISC_R_SUCCESS;
67 
68 	isc_lex_setcomments(lex, ISC_LEXCOMMENT_DNSMASTERFILE);
69 
70 	/* Read the domain name */
71 	if (!strcmp(owner, "@")) {
72 		BADTOKEN();
73 	}
74 	dname = dns_fixedname_initname(&dfname);
75 	isc_buffer_init(&b, owner, strlen(owner));
76 	isc_buffer_add(&b, strlen(owner));
77 	ret = dns_name_fromtext(dname, &b, dns_rootname, 0, NULL);
78 	if (ret != ISC_R_SUCCESS) {
79 		return ret;
80 	}
81 	if (dns_name_compare(dname, origin) != 0) {
82 		return DNS_R_BADOWNERNAME;
83 	}
84 	isc_buffer_clear(&b);
85 
86 	/* Read the next word: either TTL, class, or type */
87 	NEXTTOKEN(lex, opt, &token);
88 	if (token.type != isc_tokentype_string) {
89 		BADTOKEN();
90 	}
91 
92 	/* If it's a TTL, read the next one */
93 	ret = dns_ttl_fromtext(&token.value.as_textregion, ttl);
94 	if (ret == ISC_R_SUCCESS) {
95 		NEXTTOKEN(lex, opt, &token);
96 	}
97 	if (token.type != isc_tokentype_string) {
98 		BADTOKEN();
99 	}
100 
101 	/* If it's a class, read the next one */
102 	ret = dns_rdataclass_fromtext(&clas, &token.value.as_textregion);
103 	if (ret == ISC_R_SUCCESS) {
104 		if (clas != rdclass) {
105 			BADTOKEN();
106 		}
107 		NEXTTOKEN(lex, opt, &token);
108 	}
109 	if (token.type != isc_tokentype_string) {
110 		BADTOKEN();
111 	}
112 
113 	/* Must be the record type */
114 	ret = dns_rdatatype_fromtext(rdtype, &token.value.as_textregion);
115 	if (ret != ISC_R_SUCCESS) {
116 		BADTOKEN();
117 	}
118 	switch (*rdtype) {
119 	case dns_rdatatype_dnskey:
120 	case dns_rdatatype_cdnskey:
121 	case dns_rdatatype_cds:
122 	case dns_rdatatype_rrsig:
123 		/* Allowed record types */
124 		break;
125 	default:
126 		BADTOKEN();
127 	}
128 
129 	dns_rdatacallbacks_init(&callbacks);
130 	ret = dns_rdata_fromtext(*rdata, rdclass, *rdtype, lex, dname, 0, mctx,
131 				 buf, &callbacks);
132 cleanup:
133 	isc_lex_setcomments(lex, 0);
134 	return ret;
135 }
136 
137 static void
138 skrbundle_create(isc_mem_t *mctx, isc_stdtime_t inception,
139 		 dns_skrbundle_t **bp) {
140 	dns_skrbundle_t *b;
141 
142 	REQUIRE(bp != NULL && *bp == NULL);
143 
144 	b = isc_mem_get(mctx, sizeof(*b));
145 	b->magic = DNS_SKRBUNDLE_MAGIC;
146 	b->inception = inception;
147 	dns_diff_init(mctx, &b->diff);
148 
149 	ISC_LINK_INIT(b, link);
150 
151 	*bp = b;
152 }
153 
154 static void
155 skrbundle_addtuple(dns_skrbundle_t *bundle, dns_difftuple_t **tuple) {
156 	REQUIRE(DNS_DIFFTUPLE_VALID(*tuple));
157 	REQUIRE(DNS_SKRBUNDLE_VALID(bundle));
158 	REQUIRE(DNS_DIFF_VALID(&bundle->diff));
159 
160 	dns_diff_append(&bundle->diff, tuple);
161 }
162 
163 isc_result_t
164 dns_skrbundle_getsig(dns_skrbundle_t *bundle, dst_key_t *key,
165 		     dns_rdatatype_t covering_type, dns_rdata_t *sigrdata) {
166 	isc_result_t result = ISC_R_SUCCESS;
167 
168 	REQUIRE(DNS_SKRBUNDLE_VALID(bundle));
169 	REQUIRE(DNS_DIFF_VALID(&bundle->diff));
170 
171 	dns_difftuple_t *tuple = ISC_LIST_HEAD(bundle->diff.tuples);
172 	while (tuple != NULL) {
173 		dns_rdata_rrsig_t rrsig;
174 
175 		if (tuple->op != DNS_DIFFOP_ADDRESIGN) {
176 			tuple = ISC_LIST_NEXT(tuple, link);
177 			continue;
178 		}
179 		INSIST(tuple->rdata.type == dns_rdatatype_rrsig);
180 
181 		result = dns_rdata_tostruct(&tuple->rdata, &rrsig, NULL);
182 		if (result != ISC_R_SUCCESS) {
183 			return result;
184 		}
185 
186 		/*
187 		 * Check if covering type matches, and if the signature is
188 		 * generated by 'key'.
189 		 */
190 		if (rrsig.covered == covering_type &&
191 		    rrsig.keyid == dst_key_id(key))
192 		{
193 			dns_rdata_clone(&tuple->rdata, sigrdata);
194 			return ISC_R_SUCCESS;
195 		}
196 
197 		tuple = ISC_LIST_NEXT(tuple, link);
198 	}
199 
200 	return ISC_R_NOTFOUND;
201 }
202 
203 void
204 dns_skr_create(isc_mem_t *mctx, const char *filename, dns_name_t *origin,
205 	       dns_rdataclass_t rdclass, dns_skr_t **skrp) {
206 	isc_time_t now;
207 	dns_skr_t *skr = NULL;
208 
209 	REQUIRE(skrp != NULL && *skrp == NULL);
210 	REQUIRE(mctx != NULL);
211 
212 	UNUSED(origin);
213 	UNUSED(rdclass);
214 
215 	now = isc_time_now();
216 	skr = isc_mem_get(mctx, sizeof(*skr));
217 	*skr = (dns_skr_t){
218 		.magic = DNS_SKR_MAGIC,
219 		.filename = isc_mem_strdup(mctx, filename),
220 		.loadtime = now,
221 	};
222 	/*
223 	 * A list is not the best structure to store bundles that
224 	 * we need to look up, but we don't expect many bundles
225 	 * per SKR so it is acceptable for now.
226 	 */
227 	ISC_LIST_INIT(skr->bundles);
228 
229 	isc_mem_attach(mctx, &skr->mctx);
230 	isc_refcount_init(&skr->references, 1);
231 	*skrp = skr;
232 }
233 
234 static void
235 addbundle(dns_skr_t *skr, dns_skrbundle_t **bundlep) {
236 	REQUIRE(DNS_SKR_VALID(skr));
237 	REQUIRE(DNS_SKRBUNDLE_VALID(*bundlep));
238 
239 	ISC_LIST_APPEND(skr->bundles, *bundlep, link);
240 	*bundlep = NULL;
241 }
242 
243 isc_result_t
244 dns_skr_read(isc_mem_t *mctx, const char *filename, dns_name_t *origin,
245 	     dns_rdataclass_t rdclass, dns_ttl_t dnskeyttl, dns_skr_t **skrp) {
246 	isc_result_t result;
247 	dns_skrbundle_t *bundle = NULL;
248 	char bundlebuf[1024];
249 	uint32_t bundle_id;
250 	isc_lex_t *lex = NULL;
251 	isc_lexspecials_t specials;
252 	isc_token_t token;
253 	unsigned int opt = ISC_LEXOPT_EOL;
254 
255 	REQUIRE(DNS_SKR_VALID(*skrp));
256 
257 	isc_lex_create(mctx, TOKENSIZ, &lex);
258 	memset(specials, 0, sizeof(specials));
259 	specials['('] = 1;
260 	specials[')'] = 1;
261 	specials['"'] = 1;
262 	isc_lex_setspecials(lex, specials);
263 	result = isc_lex_openfile(lex, filename);
264 	if (result != ISC_R_SUCCESS) {
265 		isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
266 			      DNS_LOGMODULE_ZONE, ISC_LOG_ERROR,
267 			      "unable to open ksr file %s: %s", filename,
268 			      isc_result_totext(result));
269 		isc_lex_destroy(&lex);
270 		return result;
271 	}
272 
273 	for (result = isc_lex_gettoken(lex, opt, &token);
274 	     result == ISC_R_SUCCESS;
275 	     result = isc_lex_gettoken(lex, opt, &token))
276 	{
277 		if (token.type == isc_tokentype_eol) {
278 			continue;
279 		}
280 
281 		if (token.type != isc_tokentype_string) {
282 			CHECK(DNS_R_SYNTAX);
283 		}
284 
285 		if (strcmp(STR(token), ";;") == 0) {
286 			/* New bundle */
287 			CHECK(isc_lex_gettoken(lex, opt, &token));
288 			if (token.type != isc_tokentype_string ||
289 			    strcmp(STR(token), "SignedKeyResponse") != 0)
290 			{
291 				CHECK(DNS_R_SYNTAX);
292 			}
293 
294 			/* Version */
295 			CHECK(isc_lex_gettoken(lex, opt, &token));
296 			if (token.type != isc_tokentype_string ||
297 			    strcmp(STR(token), "1.0") != 0)
298 			{
299 				CHECK(DNS_R_SYNTAX);
300 			}
301 
302 			/* Date and time of bundle */
303 			CHECK(isc_lex_gettoken(lex, opt, &token));
304 			if (token.type != isc_tokentype_string) {
305 				CHECK(DNS_R_SYNTAX);
306 			}
307 			if (strcmp(STR(token), "generated") == 0) {
308 				/* Final bundle */
309 				goto readline;
310 			}
311 			if (token.type != isc_tokentype_string) {
312 				CHECK(DNS_R_SYNTAX);
313 			}
314 
315 			/* Add previous bundle */
316 			if (bundle != NULL) {
317 				addbundle(*skrp, &bundle);
318 			}
319 
320 			/* Create new bundle */
321 			sscanf(STR(token), "%s", bundlebuf);
322 			CHECK(dns_time32_fromtext(bundlebuf, &bundle_id));
323 			bundle = NULL;
324 			skrbundle_create(mctx, (isc_stdtime_t)bundle_id,
325 					 &bundle);
326 
327 		readline:
328 			/* Read remainder of header line */
329 			do {
330 				CHECK(isc_lex_gettoken(lex, opt, &token));
331 			} while (token.type != isc_tokentype_eol);
332 		} else {
333 			isc_buffer_t buf;
334 			dns_rdata_t *rdata = NULL;
335 			u_char rdatabuf[DST_KEY_MAXSIZE];
336 			dns_rdatatype_t rdtype;
337 
338 			/* Parse record */
339 			rdata = isc_mem_get(mctx, sizeof(*rdata));
340 			dns_rdata_init(rdata);
341 			isc_buffer_init(&buf, rdatabuf, sizeof(rdatabuf));
342 			result = parse_rr(lex, mctx, STR(token), origin,
343 					  rdclass, &buf, &dnskeyttl, &rdtype,
344 					  &rdata);
345 			if (result != ISC_R_SUCCESS) {
346 				isc_log_write(
347 					dns_lctx, DNS_LOGCATEGORY_GENERAL,
348 					DNS_LOGMODULE_ZONE, ISC_LOG_DEBUG(1),
349 					"read skr file %s(%lu) parse rr "
350 					"failed: %s",
351 					filename, isc_lex_getsourceline(lex),
352 					isc_result_totext(result));
353 				isc_mem_put(mctx, rdata, sizeof(*rdata));
354 				goto failure;
355 			}
356 
357 			/* Create new diff tuple */
358 			dns_diffop_t op = (rdtype == dns_rdatatype_rrsig)
359 						  ? DNS_DIFFOP_ADDRESIGN
360 						  : DNS_DIFFOP_ADD;
361 			dns_difftuple_t *tuple = NULL;
362 
363 			dns_difftuple_create((*skrp)->mctx, op, origin,
364 					     dnskeyttl, rdata, &tuple);
365 
366 			skrbundle_addtuple(bundle, &tuple);
367 			INSIST(tuple == NULL);
368 
369 			isc_mem_put(mctx, rdata, sizeof(*rdata));
370 		}
371 	}
372 
373 	if (result != ISC_R_EOF) {
374 		CHECK(DNS_R_SYNTAX);
375 	}
376 	result = ISC_R_SUCCESS;
377 
378 	/* Add final bundle */
379 	if (bundle != NULL) {
380 		addbundle(*skrp, &bundle);
381 	}
382 
383 failure:
384 	if (result != ISC_R_SUCCESS) {
385 		isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
386 			      DNS_LOGMODULE_ZONE, ISC_LOG_DEBUG(1),
387 			      "read skr file %s(%lu) failed: %s", filename,
388 			      isc_lex_getsourceline(lex),
389 			      isc_result_totext(result));
390 	}
391 
392 	/* Clean up */
393 	isc_lex_destroy(&lex);
394 	return result;
395 }
396 
397 dns_skrbundle_t *
398 dns_skr_lookup(dns_skr_t *skr, isc_stdtime_t time, uint32_t sigval) {
399 	dns_skrbundle_t *b, *next;
400 
401 	REQUIRE(DNS_SKR_VALID(skr));
402 
403 	for (b = ISC_LIST_HEAD(skr->bundles); b != NULL; b = next) {
404 		next = ISC_LIST_NEXT(b, link);
405 		if (next == NULL) {
406 			isc_stdtime_t expired = b->inception + sigval;
407 			if (b->inception <= time && time < expired) {
408 				return b;
409 			}
410 			return NULL;
411 		}
412 		if (b->inception <= time && time < next->inception) {
413 			return b;
414 		}
415 	}
416 
417 	return NULL;
418 }
419 
420 void
421 dns_skr_attach(dns_skr_t *source, dns_skr_t **targetp) {
422 	REQUIRE(DNS_SKR_VALID(source));
423 	REQUIRE(targetp != NULL && *targetp == NULL);
424 
425 	isc_refcount_increment(&source->references);
426 	*targetp = source;
427 }
428 
429 void
430 dns_skr_detach(dns_skr_t **skrp) {
431 	REQUIRE(skrp != NULL && DNS_SKR_VALID(*skrp));
432 
433 	dns_skr_t *skr = *skrp;
434 	*skrp = NULL;
435 
436 	if (isc_refcount_decrement(&skr->references) == 1) {
437 		dns_skr_destroy(skr);
438 	}
439 }
440 
441 void
442 dns_skr_destroy(dns_skr_t *skr) {
443 	dns_skrbundle_t *b, *next;
444 
445 	REQUIRE(DNS_SKR_VALID(skr));
446 
447 	for (b = ISC_LIST_HEAD(skr->bundles); b != NULL; b = next) {
448 		next = ISC_LIST_NEXT(b, link);
449 		ISC_LIST_UNLINK(skr->bundles, b, link);
450 		dns_diff_clear(&b->diff);
451 		isc_mem_put(skr->mctx, b, sizeof(*b));
452 	}
453 	INSIST(ISC_LIST_EMPTY(skr->bundles));
454 
455 	isc_mem_free(skr->mctx, skr->filename);
456 	isc_mem_putanddetach(&skr->mctx, skr, sizeof(*skr));
457 }
458