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