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