1*bcda20f6Schristos /* $NetBSD: skr.c,v 1.2 2025/01/26 16:25:25 christos Exp $ */ 29689912eSchristos 39689912eSchristos /* 49689912eSchristos * Copyright (C) Internet Systems Consortium, Inc. ("ISC") 59689912eSchristos * 69689912eSchristos * SPDX-License-Identifier: MPL-2.0 79689912eSchristos * 89689912eSchristos * This Source Code Form is subject to the terms of the Mozilla Public 99689912eSchristos * License, v. 2.0. If a copy of the MPL was not distributed with this 109689912eSchristos * file, you can obtain one at https://mozilla.org/MPL/2.0/. 119689912eSchristos * 129689912eSchristos * See the COPYRIGHT file distributed with this work for additional 139689912eSchristos * information regarding copyright ownership. 149689912eSchristos */ 159689912eSchristos 169689912eSchristos /*! \file */ 179689912eSchristos 189689912eSchristos #include <isc/lex.h> 199689912eSchristos #include <isc/log.h> 209689912eSchristos 219689912eSchristos #include <dns/callbacks.h> 229689912eSchristos #include <dns/fixedname.h> 239689912eSchristos #include <dns/rdata.h> 249689912eSchristos #include <dns/rdataclass.h> 259689912eSchristos #include <dns/rdatatype.h> 269689912eSchristos #include <dns/skr.h> 279689912eSchristos #include <dns/time.h> 289689912eSchristos #include <dns/ttl.h> 299689912eSchristos 309689912eSchristos #define CHECK(op) \ 319689912eSchristos do { \ 329689912eSchristos result = (op); \ 339689912eSchristos if (result != ISC_R_SUCCESS) \ 349689912eSchristos goto failure; \ 359689912eSchristos } while (0) 369689912eSchristos 379689912eSchristos #define READLINE(lex, opt, token) 389689912eSchristos 399689912eSchristos #define NEXTTOKEN(lex, opt, token) \ 409689912eSchristos { \ 419689912eSchristos ret = isc_lex_gettoken(lex, opt, token); \ 429689912eSchristos if (ret != ISC_R_SUCCESS) \ 439689912eSchristos goto cleanup; \ 449689912eSchristos } 459689912eSchristos 469689912eSchristos #define BADTOKEN() \ 479689912eSchristos { \ 489689912eSchristos ret = ISC_R_UNEXPECTEDTOKEN; \ 499689912eSchristos goto cleanup; \ 509689912eSchristos } 519689912eSchristos 529689912eSchristos #define TOKENSIZ (8 * 1024) 539689912eSchristos #define STR(t) ((t).value.as_textregion.base) 549689912eSchristos 559689912eSchristos static isc_result_t 569689912eSchristos parse_rr(isc_lex_t *lex, isc_mem_t *mctx, char *owner, dns_name_t *origin, 579689912eSchristos dns_rdataclass_t rdclass, isc_buffer_t *buf, dns_ttl_t *ttl, 589689912eSchristos dns_rdatatype_t *rdtype, dns_rdata_t **rdata) { 599689912eSchristos dns_rdatacallbacks_t callbacks; 609689912eSchristos dns_fixedname_t dfname; 619689912eSchristos dns_name_t *dname = NULL; 629689912eSchristos dns_rdataclass_t clas; 639689912eSchristos isc_buffer_t b; 649689912eSchristos isc_token_t token; 659689912eSchristos unsigned int opt = ISC_LEXOPT_EOL; 669689912eSchristos isc_result_t ret = ISC_R_SUCCESS; 679689912eSchristos 689689912eSchristos isc_lex_setcomments(lex, ISC_LEXCOMMENT_DNSMASTERFILE); 699689912eSchristos 709689912eSchristos /* Read the domain name */ 719689912eSchristos if (!strcmp(owner, "@")) { 729689912eSchristos BADTOKEN(); 739689912eSchristos } 749689912eSchristos dname = dns_fixedname_initname(&dfname); 759689912eSchristos isc_buffer_init(&b, owner, strlen(owner)); 769689912eSchristos isc_buffer_add(&b, strlen(owner)); 779689912eSchristos ret = dns_name_fromtext(dname, &b, dns_rootname, 0, NULL); 789689912eSchristos if (ret != ISC_R_SUCCESS) { 799689912eSchristos return ret; 809689912eSchristos } 819689912eSchristos if (dns_name_compare(dname, origin) != 0) { 829689912eSchristos return DNS_R_BADOWNERNAME; 839689912eSchristos } 849689912eSchristos isc_buffer_clear(&b); 859689912eSchristos 869689912eSchristos /* Read the next word: either TTL, class, or type */ 879689912eSchristos NEXTTOKEN(lex, opt, &token); 889689912eSchristos if (token.type != isc_tokentype_string) { 899689912eSchristos BADTOKEN(); 909689912eSchristos } 919689912eSchristos 929689912eSchristos /* If it's a TTL, read the next one */ 939689912eSchristos ret = dns_ttl_fromtext(&token.value.as_textregion, ttl); 949689912eSchristos if (ret == ISC_R_SUCCESS) { 959689912eSchristos NEXTTOKEN(lex, opt, &token); 969689912eSchristos } 979689912eSchristos if (token.type != isc_tokentype_string) { 989689912eSchristos BADTOKEN(); 999689912eSchristos } 1009689912eSchristos 1019689912eSchristos /* If it's a class, read the next one */ 1029689912eSchristos ret = dns_rdataclass_fromtext(&clas, &token.value.as_textregion); 1039689912eSchristos if (ret == ISC_R_SUCCESS) { 1049689912eSchristos if (clas != rdclass) { 1059689912eSchristos BADTOKEN(); 1069689912eSchristos } 1079689912eSchristos NEXTTOKEN(lex, opt, &token); 1089689912eSchristos } 1099689912eSchristos if (token.type != isc_tokentype_string) { 1109689912eSchristos BADTOKEN(); 1119689912eSchristos } 1129689912eSchristos 1139689912eSchristos /* Must be the record type */ 1149689912eSchristos ret = dns_rdatatype_fromtext(rdtype, &token.value.as_textregion); 1159689912eSchristos if (ret != ISC_R_SUCCESS) { 1169689912eSchristos BADTOKEN(); 1179689912eSchristos } 1189689912eSchristos switch (*rdtype) { 1199689912eSchristos case dns_rdatatype_dnskey: 1209689912eSchristos case dns_rdatatype_cdnskey: 1219689912eSchristos case dns_rdatatype_cds: 1229689912eSchristos case dns_rdatatype_rrsig: 1239689912eSchristos /* Allowed record types */ 1249689912eSchristos break; 1259689912eSchristos default: 1269689912eSchristos BADTOKEN(); 1279689912eSchristos } 1289689912eSchristos 1299689912eSchristos dns_rdatacallbacks_init(&callbacks); 1309689912eSchristos ret = dns_rdata_fromtext(*rdata, rdclass, *rdtype, lex, dname, 0, mctx, 1319689912eSchristos buf, &callbacks); 1329689912eSchristos cleanup: 1339689912eSchristos isc_lex_setcomments(lex, 0); 1349689912eSchristos return ret; 1359689912eSchristos } 1369689912eSchristos 1379689912eSchristos static void 1389689912eSchristos skrbundle_create(isc_mem_t *mctx, isc_stdtime_t inception, 1399689912eSchristos dns_skrbundle_t **bp) { 1409689912eSchristos dns_skrbundle_t *b; 1419689912eSchristos 1429689912eSchristos REQUIRE(bp != NULL && *bp == NULL); 1439689912eSchristos 1449689912eSchristos b = isc_mem_get(mctx, sizeof(*b)); 1459689912eSchristos b->magic = DNS_SKRBUNDLE_MAGIC; 1469689912eSchristos b->inception = inception; 1479689912eSchristos dns_diff_init(mctx, &b->diff); 1489689912eSchristos 1499689912eSchristos ISC_LINK_INIT(b, link); 1509689912eSchristos 1519689912eSchristos *bp = b; 1529689912eSchristos } 1539689912eSchristos 1549689912eSchristos static void 1559689912eSchristos skrbundle_addtuple(dns_skrbundle_t *bundle, dns_difftuple_t **tuple) { 1569689912eSchristos REQUIRE(DNS_DIFFTUPLE_VALID(*tuple)); 1579689912eSchristos REQUIRE(DNS_SKRBUNDLE_VALID(bundle)); 1589689912eSchristos REQUIRE(DNS_DIFF_VALID(&bundle->diff)); 1599689912eSchristos 1609689912eSchristos dns_diff_append(&bundle->diff, tuple); 1619689912eSchristos } 1629689912eSchristos 1639689912eSchristos isc_result_t 1649689912eSchristos dns_skrbundle_getsig(dns_skrbundle_t *bundle, dst_key_t *key, 1659689912eSchristos dns_rdatatype_t covering_type, dns_rdata_t *sigrdata) { 1669689912eSchristos isc_result_t result = ISC_R_SUCCESS; 1679689912eSchristos 1689689912eSchristos REQUIRE(DNS_SKRBUNDLE_VALID(bundle)); 1699689912eSchristos REQUIRE(DNS_DIFF_VALID(&bundle->diff)); 1709689912eSchristos 1719689912eSchristos dns_difftuple_t *tuple = ISC_LIST_HEAD(bundle->diff.tuples); 1729689912eSchristos while (tuple != NULL) { 1739689912eSchristos dns_rdata_rrsig_t rrsig; 1749689912eSchristos 1759689912eSchristos if (tuple->op != DNS_DIFFOP_ADDRESIGN) { 1769689912eSchristos tuple = ISC_LIST_NEXT(tuple, link); 1779689912eSchristos continue; 1789689912eSchristos } 1799689912eSchristos INSIST(tuple->rdata.type == dns_rdatatype_rrsig); 1809689912eSchristos 1819689912eSchristos result = dns_rdata_tostruct(&tuple->rdata, &rrsig, NULL); 1829689912eSchristos if (result != ISC_R_SUCCESS) { 1839689912eSchristos return result; 1849689912eSchristos } 1859689912eSchristos 1869689912eSchristos /* 1879689912eSchristos * Check if covering type matches, and if the signature is 1889689912eSchristos * generated by 'key'. 1899689912eSchristos */ 1909689912eSchristos if (rrsig.covered == covering_type && 1919689912eSchristos rrsig.keyid == dst_key_id(key)) 1929689912eSchristos { 1939689912eSchristos dns_rdata_clone(&tuple->rdata, sigrdata); 1949689912eSchristos return ISC_R_SUCCESS; 1959689912eSchristos } 1969689912eSchristos 1979689912eSchristos tuple = ISC_LIST_NEXT(tuple, link); 1989689912eSchristos } 1999689912eSchristos 2009689912eSchristos return ISC_R_NOTFOUND; 2019689912eSchristos } 2029689912eSchristos 2039689912eSchristos void 2049689912eSchristos dns_skr_create(isc_mem_t *mctx, const char *filename, dns_name_t *origin, 2059689912eSchristos dns_rdataclass_t rdclass, dns_skr_t **skrp) { 2069689912eSchristos isc_time_t now; 2079689912eSchristos dns_skr_t *skr = NULL; 2089689912eSchristos 2099689912eSchristos REQUIRE(skrp != NULL && *skrp == NULL); 2109689912eSchristos REQUIRE(mctx != NULL); 2119689912eSchristos 2129689912eSchristos UNUSED(origin); 2139689912eSchristos UNUSED(rdclass); 2149689912eSchristos 2159689912eSchristos now = isc_time_now(); 2169689912eSchristos skr = isc_mem_get(mctx, sizeof(*skr)); 2179689912eSchristos *skr = (dns_skr_t){ 2189689912eSchristos .magic = DNS_SKR_MAGIC, 2199689912eSchristos .filename = isc_mem_strdup(mctx, filename), 2209689912eSchristos .loadtime = now, 2219689912eSchristos }; 2229689912eSchristos /* 2239689912eSchristos * A list is not the best structure to store bundles that 2249689912eSchristos * we need to look up, but we don't expect many bundles 2259689912eSchristos * per SKR so it is acceptable for now. 2269689912eSchristos */ 2279689912eSchristos ISC_LIST_INIT(skr->bundles); 2289689912eSchristos 2299689912eSchristos isc_mem_attach(mctx, &skr->mctx); 2309689912eSchristos isc_refcount_init(&skr->references, 1); 2319689912eSchristos *skrp = skr; 2329689912eSchristos } 2339689912eSchristos 2349689912eSchristos static void 2359689912eSchristos addbundle(dns_skr_t *skr, dns_skrbundle_t **bundlep) { 2369689912eSchristos REQUIRE(DNS_SKR_VALID(skr)); 2379689912eSchristos REQUIRE(DNS_SKRBUNDLE_VALID(*bundlep)); 2389689912eSchristos 2399689912eSchristos ISC_LIST_APPEND(skr->bundles, *bundlep, link); 2409689912eSchristos *bundlep = NULL; 2419689912eSchristos } 2429689912eSchristos 2439689912eSchristos isc_result_t 2449689912eSchristos dns_skr_read(isc_mem_t *mctx, const char *filename, dns_name_t *origin, 2459689912eSchristos dns_rdataclass_t rdclass, dns_ttl_t dnskeyttl, dns_skr_t **skrp) { 2469689912eSchristos isc_result_t result; 2479689912eSchristos dns_skrbundle_t *bundle = NULL; 2489689912eSchristos char bundlebuf[1024]; 2499689912eSchristos uint32_t bundle_id; 2509689912eSchristos isc_lex_t *lex = NULL; 2519689912eSchristos isc_lexspecials_t specials; 2529689912eSchristos isc_token_t token; 2539689912eSchristos unsigned int opt = ISC_LEXOPT_EOL; 2549689912eSchristos 2559689912eSchristos REQUIRE(DNS_SKR_VALID(*skrp)); 2569689912eSchristos 2579689912eSchristos isc_lex_create(mctx, TOKENSIZ, &lex); 2589689912eSchristos memset(specials, 0, sizeof(specials)); 2599689912eSchristos specials['('] = 1; 2609689912eSchristos specials[')'] = 1; 2619689912eSchristos specials['"'] = 1; 2629689912eSchristos isc_lex_setspecials(lex, specials); 2639689912eSchristos result = isc_lex_openfile(lex, filename); 2649689912eSchristos if (result != ISC_R_SUCCESS) { 2659689912eSchristos isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL, 2669689912eSchristos DNS_LOGMODULE_ZONE, ISC_LOG_ERROR, 2679689912eSchristos "unable to open ksr file %s: %s", filename, 2689689912eSchristos isc_result_totext(result)); 2699689912eSchristos isc_lex_destroy(&lex); 2709689912eSchristos return result; 2719689912eSchristos } 2729689912eSchristos 2739689912eSchristos for (result = isc_lex_gettoken(lex, opt, &token); 2749689912eSchristos result == ISC_R_SUCCESS; 2759689912eSchristos result = isc_lex_gettoken(lex, opt, &token)) 2769689912eSchristos { 2779689912eSchristos if (token.type == isc_tokentype_eol) { 2789689912eSchristos continue; 2799689912eSchristos } 2809689912eSchristos 2819689912eSchristos if (token.type != isc_tokentype_string) { 2829689912eSchristos CHECK(DNS_R_SYNTAX); 2839689912eSchristos } 2849689912eSchristos 2859689912eSchristos if (strcmp(STR(token), ";;") == 0) { 2869689912eSchristos /* New bundle */ 2879689912eSchristos CHECK(isc_lex_gettoken(lex, opt, &token)); 2889689912eSchristos if (token.type != isc_tokentype_string || 2899689912eSchristos strcmp(STR(token), "SignedKeyResponse") != 0) 2909689912eSchristos { 2919689912eSchristos CHECK(DNS_R_SYNTAX); 2929689912eSchristos } 2939689912eSchristos 2949689912eSchristos /* Version */ 2959689912eSchristos CHECK(isc_lex_gettoken(lex, opt, &token)); 2969689912eSchristos if (token.type != isc_tokentype_string || 2979689912eSchristos strcmp(STR(token), "1.0") != 0) 2989689912eSchristos { 2999689912eSchristos CHECK(DNS_R_SYNTAX); 3009689912eSchristos } 3019689912eSchristos 3029689912eSchristos /* Date and time of bundle */ 3039689912eSchristos CHECK(isc_lex_gettoken(lex, opt, &token)); 3049689912eSchristos if (token.type != isc_tokentype_string) { 3059689912eSchristos CHECK(DNS_R_SYNTAX); 3069689912eSchristos } 3079689912eSchristos if (strcmp(STR(token), "generated") == 0) { 3089689912eSchristos /* Final bundle */ 3099689912eSchristos goto readline; 3109689912eSchristos } 3119689912eSchristos if (token.type != isc_tokentype_string) { 3129689912eSchristos CHECK(DNS_R_SYNTAX); 3139689912eSchristos } 3149689912eSchristos 3159689912eSchristos /* Add previous bundle */ 3169689912eSchristos if (bundle != NULL) { 3179689912eSchristos addbundle(*skrp, &bundle); 3189689912eSchristos } 3199689912eSchristos 3209689912eSchristos /* Create new bundle */ 3219689912eSchristos sscanf(STR(token), "%s", bundlebuf); 3229689912eSchristos CHECK(dns_time32_fromtext(bundlebuf, &bundle_id)); 3239689912eSchristos bundle = NULL; 3249689912eSchristos skrbundle_create(mctx, (isc_stdtime_t)bundle_id, 3259689912eSchristos &bundle); 3269689912eSchristos 3279689912eSchristos readline: 3289689912eSchristos /* Read remainder of header line */ 3299689912eSchristos do { 3309689912eSchristos CHECK(isc_lex_gettoken(lex, opt, &token)); 3319689912eSchristos } while (token.type != isc_tokentype_eol); 3329689912eSchristos } else { 3339689912eSchristos isc_buffer_t buf; 3349689912eSchristos dns_rdata_t *rdata = NULL; 3359689912eSchristos u_char rdatabuf[DST_KEY_MAXSIZE]; 3369689912eSchristos dns_rdatatype_t rdtype; 3379689912eSchristos 3389689912eSchristos /* Parse record */ 3399689912eSchristos rdata = isc_mem_get(mctx, sizeof(*rdata)); 3409689912eSchristos dns_rdata_init(rdata); 3419689912eSchristos isc_buffer_init(&buf, rdatabuf, sizeof(rdatabuf)); 3429689912eSchristos result = parse_rr(lex, mctx, STR(token), origin, 3439689912eSchristos rdclass, &buf, &dnskeyttl, &rdtype, 3449689912eSchristos &rdata); 3459689912eSchristos if (result != ISC_R_SUCCESS) { 3469689912eSchristos isc_log_write( 3479689912eSchristos dns_lctx, DNS_LOGCATEGORY_GENERAL, 3489689912eSchristos DNS_LOGMODULE_ZONE, ISC_LOG_DEBUG(1), 3499689912eSchristos "read skr file %s(%lu) parse rr " 3509689912eSchristos "failed: %s", 3519689912eSchristos filename, isc_lex_getsourceline(lex), 3529689912eSchristos isc_result_totext(result)); 3539689912eSchristos isc_mem_put(mctx, rdata, sizeof(*rdata)); 3549689912eSchristos goto failure; 3559689912eSchristos } 3569689912eSchristos 3579689912eSchristos /* Create new diff tuple */ 3589689912eSchristos dns_diffop_t op = (rdtype == dns_rdatatype_rrsig) 3599689912eSchristos ? DNS_DIFFOP_ADDRESIGN 3609689912eSchristos : DNS_DIFFOP_ADD; 3619689912eSchristos dns_difftuple_t *tuple = NULL; 3629689912eSchristos 3639689912eSchristos dns_difftuple_create((*skrp)->mctx, op, origin, 3649689912eSchristos dnskeyttl, rdata, &tuple); 3659689912eSchristos 3669689912eSchristos skrbundle_addtuple(bundle, &tuple); 3679689912eSchristos INSIST(tuple == NULL); 3689689912eSchristos 3699689912eSchristos isc_mem_put(mctx, rdata, sizeof(*rdata)); 3709689912eSchristos } 3719689912eSchristos } 3729689912eSchristos 3739689912eSchristos if (result != ISC_R_EOF) { 3749689912eSchristos CHECK(DNS_R_SYNTAX); 3759689912eSchristos } 3769689912eSchristos result = ISC_R_SUCCESS; 3779689912eSchristos 3789689912eSchristos /* Add final bundle */ 3799689912eSchristos if (bundle != NULL) { 3809689912eSchristos addbundle(*skrp, &bundle); 3819689912eSchristos } 3829689912eSchristos 3839689912eSchristos failure: 3849689912eSchristos if (result != ISC_R_SUCCESS) { 3859689912eSchristos isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL, 3869689912eSchristos DNS_LOGMODULE_ZONE, ISC_LOG_DEBUG(1), 3879689912eSchristos "read skr file %s(%lu) failed: %s", filename, 3889689912eSchristos isc_lex_getsourceline(lex), 3899689912eSchristos isc_result_totext(result)); 3909689912eSchristos } 3919689912eSchristos 3929689912eSchristos /* Clean up */ 3939689912eSchristos isc_lex_destroy(&lex); 3949689912eSchristos return result; 3959689912eSchristos } 3969689912eSchristos 3979689912eSchristos dns_skrbundle_t * 3989689912eSchristos dns_skr_lookup(dns_skr_t *skr, isc_stdtime_t time, uint32_t sigval) { 3999689912eSchristos dns_skrbundle_t *b, *next; 4009689912eSchristos 4019689912eSchristos REQUIRE(DNS_SKR_VALID(skr)); 4029689912eSchristos 4039689912eSchristos for (b = ISC_LIST_HEAD(skr->bundles); b != NULL; b = next) { 4049689912eSchristos next = ISC_LIST_NEXT(b, link); 4059689912eSchristos if (next == NULL) { 4069689912eSchristos isc_stdtime_t expired = b->inception + sigval; 4079689912eSchristos if (b->inception <= time && time < expired) { 4089689912eSchristos return b; 4099689912eSchristos } 4109689912eSchristos return NULL; 4119689912eSchristos } 4129689912eSchristos if (b->inception <= time && time < next->inception) { 4139689912eSchristos return b; 4149689912eSchristos } 4159689912eSchristos } 4169689912eSchristos 4179689912eSchristos return NULL; 4189689912eSchristos } 4199689912eSchristos 4209689912eSchristos void 4219689912eSchristos dns_skr_attach(dns_skr_t *source, dns_skr_t **targetp) { 4229689912eSchristos REQUIRE(DNS_SKR_VALID(source)); 4239689912eSchristos REQUIRE(targetp != NULL && *targetp == NULL); 4249689912eSchristos 4259689912eSchristos isc_refcount_increment(&source->references); 4269689912eSchristos *targetp = source; 4279689912eSchristos } 4289689912eSchristos 4299689912eSchristos void 4309689912eSchristos dns_skr_detach(dns_skr_t **skrp) { 4319689912eSchristos REQUIRE(skrp != NULL && DNS_SKR_VALID(*skrp)); 4329689912eSchristos 4339689912eSchristos dns_skr_t *skr = *skrp; 4349689912eSchristos *skrp = NULL; 4359689912eSchristos 4369689912eSchristos if (isc_refcount_decrement(&skr->references) == 1) { 4379689912eSchristos dns_skr_destroy(skr); 4389689912eSchristos } 4399689912eSchristos } 4409689912eSchristos 4419689912eSchristos void 4429689912eSchristos dns_skr_destroy(dns_skr_t *skr) { 4439689912eSchristos dns_skrbundle_t *b, *next; 4449689912eSchristos 4459689912eSchristos REQUIRE(DNS_SKR_VALID(skr)); 4469689912eSchristos 4479689912eSchristos for (b = ISC_LIST_HEAD(skr->bundles); b != NULL; b = next) { 4489689912eSchristos next = ISC_LIST_NEXT(b, link); 4499689912eSchristos ISC_LIST_UNLINK(skr->bundles, b, link); 4509689912eSchristos dns_diff_clear(&b->diff); 4519689912eSchristos isc_mem_put(skr->mctx, b, sizeof(*b)); 4529689912eSchristos } 4539689912eSchristos INSIST(ISC_LIST_EMPTY(skr->bundles)); 4549689912eSchristos 4559689912eSchristos isc_mem_free(skr->mctx, skr->filename); 4569689912eSchristos isc_mem_putanddetach(&skr->mctx, skr, sizeof(*skr)); 4579689912eSchristos } 458