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