From ac0d3c21c6d9cfa4390bcf2888c88c0fa7265bfa Mon Sep 17 00:00:00 2001 From: Evan Hunt Date: Tue, 17 Sep 2019 07:19:32 -0700 Subject: [PATCH 01/13] add a global function to match a DS rdata to a DNSKEY --- lib/dns/dnssec.c | 50 ++++++++++++++++++++++++++++++++ lib/dns/include/dns/dnssec.h | 12 ++++++++ lib/dns/validator.c | 55 +++--------------------------------- lib/dns/win32/libdns.def.in | 1 + 4 files changed, 67 insertions(+), 51 deletions(-) diff --git a/lib/dns/dnssec.c b/lib/dns/dnssec.c index 9e21cc35b1..d2d57448b8 100644 --- a/lib/dns/dnssec.c +++ b/lib/dns/dnssec.c @@ -2270,3 +2270,53 @@ dns_dnssec_updatekeys(dns_dnsseckeylist_t *keys, dns_dnsseckeylist_t *newkeys, failure: return (result); } + +isc_result_t +dns_dnssec_matchdskey(dns_name_t *name, dns_rdata_t *dsrdata, + dns_rdataset_t *keyset, dns_rdata_t *keyrdata) +{ + isc_result_t result; + unsigned char buf[DNS_DS_BUFFERSIZE]; + dns_keytag_t keytag; + dns_rdata_dnskey_t key; + dns_rdata_ds_t ds; + isc_region_t r; + + result = dns_rdata_tostruct(dsrdata, &ds, NULL); + RUNTIME_CHECK(result == ISC_R_SUCCESS); + + for (result = dns_rdataset_first(keyset); + result == ISC_R_SUCCESS; + result = dns_rdataset_next(keyset)) + { + dns_rdata_t newdsrdata = DNS_RDATA_INIT; + + dns_rdata_reset(keyrdata); + dns_rdataset_current(keyset, keyrdata); + + result = dns_rdata_tostruct(keyrdata, &key, NULL); + RUNTIME_CHECK(result == ISC_R_SUCCESS); + + dns_rdata_toregion(keyrdata, &r); + keytag = dst_region_computeid(&r); + + if (ds.key_tag != keytag || ds.algorithm != key.algorithm) { + continue; + } + + result = dns_ds_buildrdata(name, keyrdata, ds.digest_type, + buf, &newdsrdata); + if (result != ISC_R_SUCCESS) { + continue; + } + + if (dns_rdata_compare(dsrdata, &newdsrdata) == 0) { + break; + } + } + if (result == ISC_R_NOMORE) { + result = ISC_R_NOTFOUND; + } + + return (result); +} diff --git a/lib/dns/include/dns/dnssec.h b/lib/dns/include/dns/dnssec.h index 767e5e9b23..eb6cbb18c4 100644 --- a/lib/dns/include/dns/dnssec.h +++ b/lib/dns/include/dns/dnssec.h @@ -360,6 +360,18 @@ dns_dnssec_syncupdate(dns_dnsseckeylist_t *keys, dns_dnsseckeylist_t *rmkeys, * Update the CDS and CDNSKEY RRsets, adding and removing keys as needed. */ +isc_result_t +dns_dnssec_matchdskey(dns_name_t *name, dns_rdata_t *dsrdata, + dns_rdataset_t *keyset, dns_rdata_t *keyrdata); +/*%< + * Given a DS rdata and a DNSKEY RRset, find the DNSKEY rdata that matches + * the DS, and place it in 'keyrdata'. + * + * Returns: + *\li ISC_R_SUCCESS + *\li ISC_R_NOTFOUND + *\li Other values indicate error + */ ISC_LANG_ENDDECLS #endif /* DNS_DNSSEC_H */ diff --git a/lib/dns/validator.c b/lib/dns/validator.c index 11bafb567e..6f5306b515 100644 --- a/lib/dns/validator.c +++ b/lib/dns/validator.c @@ -1684,49 +1684,6 @@ check_signer(dns_validator_t *val, dns_rdata_t *keyrdata, return (result); } -/*% - * Find the DNSKEY that corresponds to the DS. - */ -static isc_result_t -dnskey_for_ds(dns_validator_t *val, dns_rdataset_t *rdataset, - dns_rdata_t *dsrdata, dns_rdata_ds_t *ds, dns_rdata_t *keyrdata) -{ - dns_keytag_t keytag; - dns_rdata_dnskey_t key; - isc_result_t result; - unsigned char dsbuf[DNS_DS_BUFFERSIZE]; - - for (result = dns_rdataset_first(rdataset); - result == ISC_R_SUCCESS; - result = dns_rdataset_next(rdataset)) - { - dns_rdata_t newdsrdata = DNS_RDATA_INIT; - - dns_rdata_reset(keyrdata); - dns_rdataset_current(rdataset, keyrdata); - result = dns_rdata_tostruct(keyrdata, &key, NULL); - RUNTIME_CHECK(result == ISC_R_SUCCESS); - keytag = compute_keytag(keyrdata); - if (ds->key_tag != keytag || ds->algorithm != key.algorithm) { - continue; - } - dns_rdata_reset(&newdsrdata); - result = dns_ds_buildrdata(val->event->name, keyrdata, - ds->digest_type, - dsbuf, &newdsrdata); - if (result != ISC_R_SUCCESS) { - validator_log(val, ISC_LOG_DEBUG(3), - "dns_ds_buildrdata() -> %s", - dns_result_totext(result)); - continue; - } - if (dns_rdata_compare(dsrdata, &newdsrdata) == 0) { - break; - } - } - return (result); -} - static isc_result_t anchor_signed(dns_validator_t *val, isc_result_t *resp) { isc_result_t result; @@ -1935,7 +1892,6 @@ get_dsset(dns_validator_t *val, dns_name_t *tname, isc_result_t *resp) { static isc_result_t validate_dnskey(dns_validator_t *val) { isc_result_t result; - dns_rdataset_t trdataset; dns_rdata_t dsrdata = DNS_RDATA_INIT; dns_rdata_t keyrdata = DNS_RDATA_INIT; dns_rdata_ds_t ds; @@ -2069,16 +2025,14 @@ validate_dnskey(dns_validator_t *val) { supported_algorithm = true; - dns_rdataset_init(&trdataset); - dns_rdataset_clone(val->event->rdataset, &trdataset); - /* * Find the DNSKEY matching the DS... */ - result = dnskey_for_ds(val, &trdataset, &dsrdata, - &ds, &keyrdata); + result = dns_dnssec_matchdskey(val->event->name, + &dsrdata, + val->event->rdataset, + &keyrdata); if (result != ISC_R_SUCCESS) { - dns_rdataset_disassociate(&trdataset); validator_log(val, ISC_LOG_DEBUG(3), "no DNSKEY matching DS"); continue; @@ -2088,7 +2042,6 @@ validate_dnskey(dns_validator_t *val) { * ... and check that it signed the DNSKEY RRset. */ result = check_signer(val, &keyrdata, ds.key_tag, ds.algorithm); - dns_rdataset_disassociate(&trdataset); if (result == ISC_R_SUCCESS) { break; } diff --git a/lib/dns/win32/libdns.def.in b/lib/dns/win32/libdns.def.in index 31af4b1c1e..0988db5d27 100644 --- a/lib/dns/win32/libdns.def.in +++ b/lib/dns/win32/libdns.def.in @@ -320,6 +320,7 @@ dns_dnssec_get_hints dns_dnssec_keyactive dns_dnssec_keyfromrdata dns_dnssec_keylistfromrdataset +dns_dnssec_matchdskey dns_dnssec_selfsigns dns_dnssec_sign dns_dnssec_signmessage From 3fede8a7e9964eb47116ea3a191491d4dbc71909 Mon Sep 17 00:00:00 2001 From: Evan Hunt Date: Sun, 15 Sep 2019 22:15:29 -0700 Subject: [PATCH 02/13] add "static-ds" and "initial-ds" keywords to config parser --- bin/delv/delv.c | 33 +- bin/named/server.c | 216 +++++++------ bin/tests/system/dnssec/ns8/named.conf.in | 1 - bin/tests/system/dnssec/ns9/named.conf.in | 1 - lib/bind9/check.c | 352 +++++++++++++--------- lib/isccfg/namedconf.c | 38 +-- 6 files changed, 376 insertions(+), 265 deletions(-) diff --git a/bin/delv/delv.c b/bin/delv/delv.c index f3127ab9e8..c02dfb9905 100644 --- a/bin/delv/delv.c +++ b/bin/delv/delv.c @@ -609,7 +609,7 @@ convert_name(dns_fixedname_t *fn, dns_name_t **name, const char *text) { static isc_result_t key_fromconfig(const cfg_obj_t *key, dns_client_t *client) { dns_rdata_dnskey_t keystruct; - uint32_t flags, proto, alg; + uint32_t n1, n2, n3; const char *keystr, *keynamestr; unsigned char keydata[4096]; isc_buffer_t keydatabuf; @@ -642,9 +642,14 @@ key_fromconfig(const cfg_obj_t *key, dns_client_t *client) { delv_log(ISC_LOG_DEBUG(3), "adding trust anchor %s", trust_anchor); - flags = cfg_obj_asuint32(cfg_tuple_get(key, "flags")); - proto = cfg_obj_asuint32(cfg_tuple_get(key, "protocol")); - alg = cfg_obj_asuint32(cfg_tuple_get(key, "algorithm")); + /* if DNSKEY, flags; if DS, key tag */ + n1 = cfg_obj_asuint32(cfg_tuple_get(key, "n1")); + + /* if DNSKEY, protocol; if DS, algorithm */ + n2 = cfg_obj_asuint32(cfg_tuple_get(key, "n2")); + + /* if DNSKEY, algorithm; if DS, digest type */ + n3 = cfg_obj_asuint32(cfg_tuple_get(key, "n3")); keystruct.common.rdclass = dns_rdataclass_in; keystruct.common.rdtype = dns_rdatatype_dnskey; @@ -655,28 +660,30 @@ key_fromconfig(const cfg_obj_t *key, dns_client_t *client) { ISC_LINK_INIT(&keystruct.common, link); - if (flags > 0xffff) + if (n1 > 0xffff) { CHECK(ISC_R_RANGE); - if (proto > 0xff) + } + if (n2 > 0xff) { CHECK(ISC_R_RANGE); - if (alg > 0xff) + } + if (n3 > 0xff) { CHECK(ISC_R_RANGE); + } - keystruct.flags = (uint16_t)flags; - keystruct.protocol = (uint8_t)proto; - keystruct.algorithm = (uint8_t)alg; + keystruct.flags = (uint16_t)n1; + keystruct.protocol = (uint8_t)n2; + keystruct.algorithm = (uint8_t)n3; isc_buffer_init(&keydatabuf, keydata, sizeof(keydata)); isc_buffer_init(&rrdatabuf, rrdata, sizeof(rrdata)); - keystr = cfg_obj_asstring(cfg_tuple_get(key, "key")); + keystr = cfg_obj_asstring(cfg_tuple_get(key, "data")); CHECK(isc_base64_decodestring(keystr, &keydatabuf)); isc_buffer_usedregion(&keydatabuf, &r); keystruct.datalen = r.length; keystruct.data = r.base; - CHECK(dns_rdata_fromstruct(NULL, - keystruct.common.rdclass, + CHECK(dns_rdata_fromstruct(NULL, keystruct.common.rdclass, keystruct.common.rdtype, &keystruct, &rrdatabuf)); diff --git a/bin/named/server.c b/bin/named/server.c index 337471dd9d..d68d01ae7e 100644 --- a/bin/named/server.c +++ b/bin/named/server.c @@ -698,104 +698,143 @@ configure_view_nametable(const cfg_obj_t *vconfig, const cfg_obj_t *config, } static isc_result_t -dstkey_fromconfig(const cfg_obj_t *key, bool *initialp, dst_key_t **target, - const char **keynamestrp, isc_mem_t *mctx) +ta_fromconfig(const cfg_obj_t *key, bool *initialp, dst_key_t **target, + const char **keynamestrp, isc_mem_t *mctx) { dns_rdata_dnskey_t keystruct; - uint32_t flags, proto, alg; - const char *keystr, *keynamestr; - unsigned char keydata[4096]; - isc_buffer_t keydatabuf; + uint32_t n1, n2, n3; + const char *datastr = NULL, *keynamestr = NULL; + unsigned char data[4096]; + isc_buffer_t databuf; unsigned char rrdata[4096]; isc_buffer_t rrdatabuf; isc_region_t r; dns_fixedname_t fkeyname; - dns_name_t *keyname; + dns_name_t *keyname = NULL; isc_buffer_t namebuf; isc_result_t result; dst_key_t *dstkey = NULL; + const char *atstr = NULL; + enum { + INIT_DNSKEY, + STATIC_DNSKEY, + INIT_DS, + STATIC_DS, + TRUSTED + } anchortype; - INSIST(target != NULL && *target == NULL); - INSIST(keynamestrp != NULL && *keynamestrp == NULL); + REQUIRE(target != NULL && *target == NULL); + REQUIRE(keynamestrp != NULL && *keynamestrp == NULL); + + /* if DNSKEY, flags; if DS, key tag */ + n1 = cfg_obj_asuint32(cfg_tuple_get(key, "n1")); + + /* if DNSKEY, protocol; if DS, algorithm */ + n2 = cfg_obj_asuint32(cfg_tuple_get(key, "n2")); + + /* if DNSKEY, algorithm; if DS, digest type */ + n3 = cfg_obj_asuint32(cfg_tuple_get(key, "n3")); - flags = cfg_obj_asuint32(cfg_tuple_get(key, "flags")); - proto = cfg_obj_asuint32(cfg_tuple_get(key, "protocol")); - alg = cfg_obj_asuint32(cfg_tuple_get(key, "algorithm")); - keyname = dns_fixedname_name(&fkeyname); keynamestr = cfg_obj_asstring(cfg_tuple_get(key, "name")); *keynamestrp = keynamestr; - if (*initialp) { - const char *initmethod; - initmethod = cfg_obj_asstring(cfg_tuple_get(key, "init")); - - if (strcasecmp(initmethod, "static-key") == 0) { - *initialp = false; - } else if (strcasecmp(initmethod, "initial-key") != 0) { - cfg_obj_log(key, named_g_lctx, ISC_LOG_ERROR, - "key '%s': " - "invalid initialization method '%s'", - keynamestr, initmethod); - result = ISC_R_FAILURE; - goto cleanup; - } - } - - /* - * This function should never be reached for non-IN classes. - */ - keystruct.common.rdclass = dns_rdataclass_in; - keystruct.common.rdtype = dns_rdatatype_dnskey; - - /* - * The key data in keystruct is not dynamically allocated. - */ - keystruct.mctx = NULL; - - ISC_LINK_INIT(&keystruct.common, link); - - if (flags > 0xffff) - CHECKM(ISC_R_RANGE, "key flags"); - if (flags & DNS_KEYFLAG_REVOKE) - CHECKM(DST_R_BADKEYTYPE, "key flags revoke bit set"); - if (proto > 0xff) - CHECKM(ISC_R_RANGE, "key protocol"); - if (alg > 0xff) - CHECKM(ISC_R_RANGE, "key algorithm"); - keystruct.flags = (uint16_t)flags; - keystruct.protocol = (uint8_t)proto; - keystruct.algorithm = (uint8_t)alg; - - isc_buffer_init(&keydatabuf, keydata, sizeof(keydata)); - isc_buffer_init(&rrdatabuf, rrdata, sizeof(rrdata)); - - keystr = cfg_obj_asstring(cfg_tuple_get(key, "key")); - CHECK(isc_base64_decodestring(keystr, &keydatabuf)); - isc_buffer_usedregion(&keydatabuf, &r); - keystruct.datalen = r.length; - keystruct.data = r.base; - - if ((keystruct.algorithm == DST_ALG_RSASHA1) && - r.length > 1 && r.base[0] == 1 && r.base[1] == 3) - { - cfg_obj_log(key, named_g_lctx, ISC_LOG_WARNING, - "%s '%s' has a weak exponent", - *initialp ? "initial-key" : "static-key", - keynamestr); - } - - CHECK(dns_rdata_fromstruct(NULL, - keystruct.common.rdclass, - keystruct.common.rdtype, - &keystruct, &rrdatabuf)); - dns_fixedname_init(&fkeyname); + keyname = dns_fixedname_initname(&fkeyname); isc_buffer_constinit(&namebuf, keynamestr, strlen(keynamestr)); isc_buffer_add(&namebuf, strlen(keynamestr)); CHECK(dns_name_fromtext(keyname, &namebuf, dns_rootname, 0, NULL)); - CHECK(dst_key_fromdns(keyname, dns_rdataclass_in, &rrdatabuf, - mctx, &dstkey)); - *target = dstkey; + if (*initialp) { + atstr = cfg_obj_asstring(cfg_tuple_get(key, "anchortype")); + + if (strcasecmp(atstr, "static-key") == 0) { + *initialp = false; + anchortype = STATIC_DNSKEY; + } else if (strcasecmp(atstr, "static-ds") == 0) { + *initialp = false; + anchortype = STATIC_DS; + } else if (strcasecmp(atstr, "initial-key") == 0) { + anchortype = INIT_DNSKEY; + } else if (strcasecmp(atstr, "initial-ds") == 0) { + anchortype = INIT_DS; + } else { + cfg_obj_log(key, named_g_lctx, ISC_LOG_ERROR, + "key '%s': " + "invalid initialization method '%s'", + keynamestr, atstr); + result = ISC_R_FAILURE; + goto cleanup; + } + } else { + anchortype = TRUSTED; + } + + switch(anchortype) { + case INIT_DNSKEY: + case STATIC_DNSKEY: + case TRUSTED: + /* + * This function should never be reached for view + * class other than IN + */ + keystruct.common.rdclass = dns_rdataclass_in; + keystruct.common.rdtype = dns_rdatatype_dnskey; + + /* + * The key data in keystruct is not dynamically allocated. + */ + keystruct.mctx = NULL; + + ISC_LINK_INIT(&keystruct.common, link); + + if (n1 > 0xffff) { + CHECKM(ISC_R_RANGE, "key flags"); + } + if (n1 & DNS_KEYFLAG_REVOKE) { + CHECKM(DST_R_BADKEYTYPE, "key flags revoke bit set"); + } + if (n2 > 0xff) { + CHECKM(ISC_R_RANGE, "key protocol"); + } + if (n3> 0xff) { + CHECKM(ISC_R_RANGE, "key algorithm"); + } + + keystruct.flags = (uint16_t)n1; + keystruct.protocol = (uint8_t)n2; + keystruct.algorithm = (uint8_t)n3; + + isc_buffer_init(&databuf, data, sizeof(data)); + isc_buffer_init(&rrdatabuf, rrdata, sizeof(rrdata)); + + datastr = cfg_obj_asstring(cfg_tuple_get(key, "data")); + CHECK(isc_base64_decodestring(datastr, &databuf)); + isc_buffer_usedregion(&databuf, &r); + keystruct.datalen = r.length; + keystruct.data = r.base; + + CHECK(dns_rdata_fromstruct(NULL, keystruct.common.rdclass, + keystruct.common.rdtype, + &keystruct, &rrdatabuf)); + CHECK(dst_key_fromdns(keyname, dns_rdataclass_in, + &rrdatabuf, mctx, &dstkey)); + + *target = dstkey; + break; + + case INIT_DS: + case STATIC_DS: + cfg_obj_log(key, named_g_lctx, ISC_LOG_ERROR, + "key '%s': " + "initialization method '%s' is " + "not yet supported", keynamestr, atstr); + result = ISC_R_FAILURE; + goto cleanup; + + default: + INSIST(0); + ISC_UNREACHABLE(); + } + return (ISC_R_SUCCESS); cleanup: @@ -829,8 +868,7 @@ process_key(const cfg_obj_t *key, dns_keytable_t *secroots, isc_result_t result; bool initializing = managed; - result = dstkey_fromconfig(key, &initializing, - &dstkey, &keynamestr, mctx); + result = ta_fromconfig(key, &initializing, &dstkey, &keynamestr, mctx); switch (result) { case ISC_R_SUCCESS: @@ -1117,16 +1155,18 @@ configure_view_dnsseckeys(dns_view_t *view, const cfg_obj_t *vconfig, } } - CHECK(load_view_keys(view_keys, view, false, NULL, mctx)); - CHECK(load_view_keys(view_managed_keys, view, true, NULL, mctx)); - CHECK(load_view_keys(view_dnssec_keys, view, true, NULL, mctx)); - if (view->rdclass == dns_rdataclass_in) { + CHECK(load_view_keys(view_keys, view, false, NULL, mctx)); + CHECK(load_view_keys(view_dnssec_keys, view, true, NULL, + mctx)); + CHECK(load_view_keys(view_managed_keys, view, true, NULL, + mctx)); + CHECK(load_view_keys(global_keys, view, false, NULL, mctx)); - CHECK(load_view_keys(global_managed_keys, view, true, - NULL, mctx)); CHECK(load_view_keys(global_dnssec_keys, view, true, NULL, mctx)); + CHECK(load_view_keys(global_managed_keys, view, true, + NULL, mctx)); } /* diff --git a/bin/tests/system/dnssec/ns8/named.conf.in b/bin/tests/system/dnssec/ns8/named.conf.in index de95e26301..4cf0753e96 100644 --- a/bin/tests/system/dnssec/ns8/named.conf.in +++ b/bin/tests/system/dnssec/ns8/named.conf.in @@ -20,7 +20,6 @@ options { listen-on { 10.53.0.8; }; listen-on-v6 { none; }; recursion yes; - dnssec-enable yes; dnssec-validation yes; minimal-responses no; disable-algorithms "disabled.managed." { @DISABLED_ALGORITHM@; }; diff --git a/bin/tests/system/dnssec/ns9/named.conf.in b/bin/tests/system/dnssec/ns9/named.conf.in index d655112a70..1933c76700 100644 --- a/bin/tests/system/dnssec/ns9/named.conf.in +++ b/bin/tests/system/dnssec/ns9/named.conf.in @@ -20,7 +20,6 @@ options { listen-on { 10.53.0.9; }; listen-on-v6 { none; }; recursion yes; - dnssec-enable yes; dnssec-validation yes; forward only; forwarders { 10.53.0.4; }; diff --git a/lib/bind9/check.c b/lib/bind9/check.c index 402a679053..302c3dcc00 100644 --- a/lib/bind9/check.c +++ b/lib/bind9/check.c @@ -40,6 +40,7 @@ #include #include #include +#include #include #include #include @@ -48,6 +49,7 @@ #include #include +#include #include #include @@ -3120,23 +3122,113 @@ static isc_result_t check_trusted_key(const cfg_obj_t *key, bool managed, unsigned int *keyflags, isc_log_t *logctx) { - const char *keystr, *keynamestr; + const char *keystr = NULL, *keynamestr = NULL; dns_fixedname_t fkeyname; - dns_name_t *keyname; + dns_name_t *keyname = NULL; isc_buffer_t b; isc_region_t r; isc_result_t result = ISC_R_SUCCESS; isc_result_t tresult; - uint32_t flags, proto, alg; + uint32_t n1, n2, n3; unsigned char keydata[4096]; + const char *atstr = NULL; + enum { + INIT_DNSKEY, + STATIC_DNSKEY, + INIT_DS, + STATIC_DS, + TRUSTED + } anchortype; - flags = cfg_obj_asuint32(cfg_tuple_get(key, "flags")); - proto = cfg_obj_asuint32(cfg_tuple_get(key, "protocol")); - alg = cfg_obj_asuint32(cfg_tuple_get(key, "algorithm")); + /* + * The 2010 and 2017 IANA root keys - these are used below + * to check the contents of trusted-key, initial-key and + * static-key configurations. + */ + static const unsigned char root_ksk_2010[] = { + 0x03, 0x01, 0x00, 0x01, 0xa8, 0x00, 0x20, 0xa9, + 0x55, 0x66, 0xba, 0x42, 0xe8, 0x86, 0xbb, 0x80, + 0x4c, 0xda, 0x84, 0xe4, 0x7e, 0xf5, 0x6d, 0xbd, + 0x7a, 0xec, 0x61, 0x26, 0x15, 0x55, 0x2c, 0xec, + 0x90, 0x6d, 0x21, 0x16, 0xd0, 0xef, 0x20, 0x70, + 0x28, 0xc5, 0x15, 0x54, 0x14, 0x4d, 0xfe, 0xaf, + 0xe7, 0xc7, 0xcb, 0x8f, 0x00, 0x5d, 0xd1, 0x82, + 0x34, 0x13, 0x3a, 0xc0, 0x71, 0x0a, 0x81, 0x18, + 0x2c, 0xe1, 0xfd, 0x14, 0xad, 0x22, 0x83, 0xbc, + 0x83, 0x43, 0x5f, 0x9d, 0xf2, 0xf6, 0x31, 0x32, + 0x51, 0x93, 0x1a, 0x17, 0x6d, 0xf0, 0xda, 0x51, + 0xe5, 0x4f, 0x42, 0xe6, 0x04, 0x86, 0x0d, 0xfb, + 0x35, 0x95, 0x80, 0x25, 0x0f, 0x55, 0x9c, 0xc5, + 0x43, 0xc4, 0xff, 0xd5, 0x1c, 0xbe, 0x3d, 0xe8, + 0xcf, 0xd0, 0x67, 0x19, 0x23, 0x7f, 0x9f, 0xc4, + 0x7e, 0xe7, 0x29, 0xda, 0x06, 0x83, 0x5f, 0xa4, + 0x52, 0xe8, 0x25, 0xe9, 0xa1, 0x8e, 0xbc, 0x2e, + 0xcb, 0xcf, 0x56, 0x34, 0x74, 0x65, 0x2c, 0x33, + 0xcf, 0x56, 0xa9, 0x03, 0x3b, 0xcd, 0xf5, 0xd9, + 0x73, 0x12, 0x17, 0x97, 0xec, 0x80, 0x89, 0x04, + 0x1b, 0x6e, 0x03, 0xa1, 0xb7, 0x2d, 0x0a, 0x73, + 0x5b, 0x98, 0x4e, 0x03, 0x68, 0x73, 0x09, 0x33, + 0x23, 0x24, 0xf2, 0x7c, 0x2d, 0xba, 0x85, 0xe9, + 0xdb, 0x15, 0xe8, 0x3a, 0x01, 0x43, 0x38, 0x2e, + 0x97, 0x4b, 0x06, 0x21, 0xc1, 0x8e, 0x62, 0x5e, + 0xce, 0xc9, 0x07, 0x57, 0x7d, 0x9e, 0x7b, 0xad, + 0xe9, 0x52, 0x41, 0xa8, 0x1e, 0xbb, 0xe8, 0xa9, + 0x01, 0xd4, 0xd3, 0x27, 0x6e, 0x40, 0xb1, 0x14, + 0xc0, 0xa2, 0xe6, 0xfc, 0x38, 0xd1, 0x9c, 0x2e, + 0x6a, 0xab, 0x02, 0x64, 0x4b, 0x28, 0x13, 0xf5, + 0x75, 0xfc, 0x21, 0x60, 0x1e, 0x0d, 0xee, 0x49, + 0xcd, 0x9e, 0xe9, 0x6a, 0x43, 0x10, 0x3e, 0x52, + 0x4d, 0x62, 0x87, 0x3d + }; + static const unsigned char root_ksk_2017[] = { + 0x03, 0x01, 0x00, 0x01, 0xac, 0xff, 0xb4, 0x09, + 0xbc, 0xc9, 0x39, 0xf8, 0x31, 0xf7, 0xa1, 0xe5, + 0xec, 0x88, 0xf7, 0xa5, 0x92, 0x55, 0xec, 0x53, + 0x04, 0x0b, 0xe4, 0x32, 0x02, 0x73, 0x90, 0xa4, + 0xce, 0x89, 0x6d, 0x6f, 0x90, 0x86, 0xf3, 0xc5, + 0xe1, 0x77, 0xfb, 0xfe, 0x11, 0x81, 0x63, 0xaa, + 0xec, 0x7a, 0xf1, 0x46, 0x2c, 0x47, 0x94, 0x59, + 0x44, 0xc4, 0xe2, 0xc0, 0x26, 0xbe, 0x5e, 0x98, + 0xbb, 0xcd, 0xed, 0x25, 0x97, 0x82, 0x72, 0xe1, + 0xe3, 0xe0, 0x79, 0xc5, 0x09, 0x4d, 0x57, 0x3f, + 0x0e, 0x83, 0xc9, 0x2f, 0x02, 0xb3, 0x2d, 0x35, + 0x13, 0xb1, 0x55, 0x0b, 0x82, 0x69, 0x29, 0xc8, + 0x0d, 0xd0, 0xf9, 0x2c, 0xac, 0x96, 0x6d, 0x17, + 0x76, 0x9f, 0xd5, 0x86, 0x7b, 0x64, 0x7c, 0x3f, + 0x38, 0x02, 0x9a, 0xbd, 0xc4, 0x81, 0x52, 0xeb, + 0x8f, 0x20, 0x71, 0x59, 0xec, 0xc5, 0xd2, 0x32, + 0xc7, 0xc1, 0x53, 0x7c, 0x79, 0xf4, 0xb7, 0xac, + 0x28, 0xff, 0x11, 0x68, 0x2f, 0x21, 0x68, 0x1b, + 0xf6, 0xd6, 0xab, 0xa5, 0x55, 0x03, 0x2b, 0xf6, + 0xf9, 0xf0, 0x36, 0xbe, 0xb2, 0xaa, 0xa5, 0xb3, + 0x77, 0x8d, 0x6e, 0xeb, 0xfb, 0xa6, 0xbf, 0x9e, + 0xa1, 0x91, 0xbe, 0x4a, 0xb0, 0xca, 0xea, 0x75, + 0x9e, 0x2f, 0x77, 0x3a, 0x1f, 0x90, 0x29, 0xc7, + 0x3e, 0xcb, 0x8d, 0x57, 0x35, 0xb9, 0x32, 0x1d, + 0xb0, 0x85, 0xf1, 0xb8, 0xe2, 0xd8, 0x03, 0x8f, + 0xe2, 0x94, 0x19, 0x92, 0x54, 0x8c, 0xee, 0x0d, + 0x67, 0xdd, 0x45, 0x47, 0xe1, 0x1d, 0xd6, 0x3a, + 0xf9, 0xc9, 0xfc, 0x1c, 0x54, 0x66, 0xfb, 0x68, + 0x4c, 0xf0, 0x09, 0xd7, 0x19, 0x7c, 0x2c, 0xf7, + 0x9e, 0x79, 0x2a, 0xb5, 0x01, 0xe6, 0xa8, 0xa1, + 0xca, 0x51, 0x9a, 0xf2, 0xcb, 0x9b, 0x5f, 0x63, + 0x67, 0xe9, 0x4c, 0x0d, 0x47, 0x50, 0x24, 0x51, + 0x35, 0x7b, 0xe1, 0xb5 + }; + + + /* if DNSKEY, flags; if DS, key tag */ + n1 = cfg_obj_asuint32(cfg_tuple_get(key, "n1")); + + /* if DNSKEY, protocol; if DS, algorithm */ + n2 = cfg_obj_asuint32(cfg_tuple_get(key, "n2")); + + /* if DNSKEY, algorithm; if DS, digest type */ + n3 = cfg_obj_asuint32(cfg_tuple_get(key, "n3")); - keyname = dns_fixedname_initname(&fkeyname); keynamestr = cfg_obj_asstring(cfg_tuple_get(key, "name")); + keyname = dns_fixedname_initname(&fkeyname); isc_buffer_constinit(&b, keynamestr, strlen(keynamestr)); isc_buffer_add(&b, strlen(keynamestr)); result = dns_name_fromtext(keyname, &b, dns_rootname, 0, NULL); @@ -3146,151 +3238,120 @@ check_trusted_key(const cfg_obj_t *key, bool managed, result = ISC_R_FAILURE; } - if (flags > 0xffff) { - cfg_obj_log(key, logctx, ISC_LOG_WARNING, - "flags too big: %u\n", flags); - result = ISC_R_FAILURE; - } - if (proto > 0xff) { - cfg_obj_log(key, logctx, ISC_LOG_WARNING, - "protocol too big: %u\n", proto); - result = ISC_R_FAILURE; - } - if (alg > 0xff) { - cfg_obj_log(key, logctx, ISC_LOG_WARNING, - "algorithm too big: %u\n", alg); - result = ISC_R_FAILURE; - } - if (managed) { - const char *initmethod; - initmethod = cfg_obj_asstring(cfg_tuple_get(key, "init")); + atstr = cfg_obj_asstring(cfg_tuple_get(key, "anchortype")); - if (strcasecmp(initmethod, "static-key") == 0) { + if (strcasecmp(atstr, "static-key") == 0) { managed = false; - } else if (strcasecmp(initmethod, "initial-key") != 0) { + anchortype = STATIC_DNSKEY; + } else if (strcasecmp(atstr, "static-ds") == 0) { + managed = false; + anchortype = STATIC_DS; + } else if (strcasecmp(atstr, "initial-key") == 0) { + anchortype = INIT_DNSKEY; + } else if (strcasecmp(atstr, "initial-ds") == 0) { + anchortype = INIT_DS; + } else { cfg_obj_log(key, logctx, ISC_LOG_ERROR, - "managed key '%s': " + "key '%s': " "invalid initialization method '%s'", - keynamestr, initmethod); + keynamestr, atstr); result = ISC_R_FAILURE; + + /* + * We can't interpret the trust anchor, so + * we skip all other checks. + */ + goto cleanup; } - } - - isc_buffer_init(&b, keydata, sizeof(keydata)); - - keystr = cfg_obj_asstring(cfg_tuple_get(key, "key")); - tresult = isc_base64_decodestring(keystr, &b); - - if (tresult != ISC_R_SUCCESS) { - cfg_obj_log(key, logctx, ISC_LOG_ERROR, - "%s", isc_result_totext(tresult)); - result = ISC_R_FAILURE; } else { - isc_buffer_usedregion(&b, &r); + atstr = "trusted-key"; + anchortype = TRUSTED; + } - if ((alg == DST_ALG_RSASHA1) && - r.length > 1 && r.base[0] == 1 && r.base[1] == 3) - { + switch(anchortype) { + case INIT_DNSKEY: + case STATIC_DNSKEY: + case TRUSTED: + if (n1 > 0xffff) { + cfg_obj_log(key, logctx, ISC_LOG_ERROR, + "flags too big: %u", n1); + result = ISC_R_RANGE; + } + if (n1 & DNS_KEYFLAG_REVOKE) { cfg_obj_log(key, logctx, ISC_LOG_WARNING, - "%s key '%s' has a weak exponent", - managed ? "initializing" : "static", - keynamestr); - } - } - - if (result == ISC_R_SUCCESS && dns_name_equal(keyname, dns_rootname)) { - static const unsigned char root_ksk_2010[] = { - 0x03, 0x01, 0x00, 0x01, 0xa8, 0x00, 0x20, 0xa9, - 0x55, 0x66, 0xba, 0x42, 0xe8, 0x86, 0xbb, 0x80, - 0x4c, 0xda, 0x84, 0xe4, 0x7e, 0xf5, 0x6d, 0xbd, - 0x7a, 0xec, 0x61, 0x26, 0x15, 0x55, 0x2c, 0xec, - 0x90, 0x6d, 0x21, 0x16, 0xd0, 0xef, 0x20, 0x70, - 0x28, 0xc5, 0x15, 0x54, 0x14, 0x4d, 0xfe, 0xaf, - 0xe7, 0xc7, 0xcb, 0x8f, 0x00, 0x5d, 0xd1, 0x82, - 0x34, 0x13, 0x3a, 0xc0, 0x71, 0x0a, 0x81, 0x18, - 0x2c, 0xe1, 0xfd, 0x14, 0xad, 0x22, 0x83, 0xbc, - 0x83, 0x43, 0x5f, 0x9d, 0xf2, 0xf6, 0x31, 0x32, - 0x51, 0x93, 0x1a, 0x17, 0x6d, 0xf0, 0xda, 0x51, - 0xe5, 0x4f, 0x42, 0xe6, 0x04, 0x86, 0x0d, 0xfb, - 0x35, 0x95, 0x80, 0x25, 0x0f, 0x55, 0x9c, 0xc5, - 0x43, 0xc4, 0xff, 0xd5, 0x1c, 0xbe, 0x3d, 0xe8, - 0xcf, 0xd0, 0x67, 0x19, 0x23, 0x7f, 0x9f, 0xc4, - 0x7e, 0xe7, 0x29, 0xda, 0x06, 0x83, 0x5f, 0xa4, - 0x52, 0xe8, 0x25, 0xe9, 0xa1, 0x8e, 0xbc, 0x2e, - 0xcb, 0xcf, 0x56, 0x34, 0x74, 0x65, 0x2c, 0x33, - 0xcf, 0x56, 0xa9, 0x03, 0x3b, 0xcd, 0xf5, 0xd9, - 0x73, 0x12, 0x17, 0x97, 0xec, 0x80, 0x89, 0x04, - 0x1b, 0x6e, 0x03, 0xa1, 0xb7, 0x2d, 0x0a, 0x73, - 0x5b, 0x98, 0x4e, 0x03, 0x68, 0x73, 0x09, 0x33, - 0x23, 0x24, 0xf2, 0x7c, 0x2d, 0xba, 0x85, 0xe9, - 0xdb, 0x15, 0xe8, 0x3a, 0x01, 0x43, 0x38, 0x2e, - 0x97, 0x4b, 0x06, 0x21, 0xc1, 0x8e, 0x62, 0x5e, - 0xce, 0xc9, 0x07, 0x57, 0x7d, 0x9e, 0x7b, 0xad, - 0xe9, 0x52, 0x41, 0xa8, 0x1e, 0xbb, 0xe8, 0xa9, - 0x01, 0xd4, 0xd3, 0x27, 0x6e, 0x40, 0xb1, 0x14, - 0xc0, 0xa2, 0xe6, 0xfc, 0x38, 0xd1, 0x9c, 0x2e, - 0x6a, 0xab, 0x02, 0x64, 0x4b, 0x28, 0x13, 0xf5, - 0x75, 0xfc, 0x21, 0x60, 0x1e, 0x0d, 0xee, 0x49, - 0xcd, 0x9e, 0xe9, 0x6a, 0x43, 0x10, 0x3e, 0x52, - 0x4d, 0x62, 0x87, 0x3d - }; - static const unsigned char root_ksk_2017[] = { - 0x03, 0x01, 0x00, 0x01, 0xac, 0xff, 0xb4, 0x09, - 0xbc, 0xc9, 0x39, 0xf8, 0x31, 0xf7, 0xa1, 0xe5, - 0xec, 0x88, 0xf7, 0xa5, 0x92, 0x55, 0xec, 0x53, - 0x04, 0x0b, 0xe4, 0x32, 0x02, 0x73, 0x90, 0xa4, - 0xce, 0x89, 0x6d, 0x6f, 0x90, 0x86, 0xf3, 0xc5, - 0xe1, 0x77, 0xfb, 0xfe, 0x11, 0x81, 0x63, 0xaa, - 0xec, 0x7a, 0xf1, 0x46, 0x2c, 0x47, 0x94, 0x59, - 0x44, 0xc4, 0xe2, 0xc0, 0x26, 0xbe, 0x5e, 0x98, - 0xbb, 0xcd, 0xed, 0x25, 0x97, 0x82, 0x72, 0xe1, - 0xe3, 0xe0, 0x79, 0xc5, 0x09, 0x4d, 0x57, 0x3f, - 0x0e, 0x83, 0xc9, 0x2f, 0x02, 0xb3, 0x2d, 0x35, - 0x13, 0xb1, 0x55, 0x0b, 0x82, 0x69, 0x29, 0xc8, - 0x0d, 0xd0, 0xf9, 0x2c, 0xac, 0x96, 0x6d, 0x17, - 0x76, 0x9f, 0xd5, 0x86, 0x7b, 0x64, 0x7c, 0x3f, - 0x38, 0x02, 0x9a, 0xbd, 0xc4, 0x81, 0x52, 0xeb, - 0x8f, 0x20, 0x71, 0x59, 0xec, 0xc5, 0xd2, 0x32, - 0xc7, 0xc1, 0x53, 0x7c, 0x79, 0xf4, 0xb7, 0xac, - 0x28, 0xff, 0x11, 0x68, 0x2f, 0x21, 0x68, 0x1b, - 0xf6, 0xd6, 0xab, 0xa5, 0x55, 0x03, 0x2b, 0xf6, - 0xf9, 0xf0, 0x36, 0xbe, 0xb2, 0xaa, 0xa5, 0xb3, - 0x77, 0x8d, 0x6e, 0xeb, 0xfb, 0xa6, 0xbf, 0x9e, - 0xa1, 0x91, 0xbe, 0x4a, 0xb0, 0xca, 0xea, 0x75, - 0x9e, 0x2f, 0x77, 0x3a, 0x1f, 0x90, 0x29, 0xc7, - 0x3e, 0xcb, 0x8d, 0x57, 0x35, 0xb9, 0x32, 0x1d, - 0xb0, 0x85, 0xf1, 0xb8, 0xe2, 0xd8, 0x03, 0x8f, - 0xe2, 0x94, 0x19, 0x92, 0x54, 0x8c, 0xee, 0x0d, - 0x67, 0xdd, 0x45, 0x47, 0xe1, 0x1d, 0xd6, 0x3a, - 0xf9, 0xc9, 0xfc, 0x1c, 0x54, 0x66, 0xfb, 0x68, - 0x4c, 0xf0, 0x09, 0xd7, 0x19, 0x7c, 0x2c, 0xf7, - 0x9e, 0x79, 0x2a, 0xb5, 0x01, 0xe6, 0xa8, 0xa1, - 0xca, 0x51, 0x9a, 0xf2, 0xcb, 0x9b, 0x5f, 0x63, - 0x67, 0xe9, 0x4c, 0x0d, 0x47, 0x50, 0x24, 0x51, - 0x35, 0x7b, 0xe1, 0xb5 - }; - - /* - * Flag any use of a root key, regardless of content. - */ - *keyflags |= (managed ? ROOT_KSK_MANAGED : ROOT_KSK_STATIC); - - if (flags == 257 && proto == 3 && alg == 8 && - isc_buffer_usedlength(&b) == sizeof(root_ksk_2010) && - !memcmp(keydata, root_ksk_2010, sizeof(root_ksk_2010))) - { - *keyflags |= ROOT_KSK_2010; - } - - if (flags == 257 && proto == 3 && alg == 8 && - isc_buffer_usedlength(&b) == sizeof(root_ksk_2017) && - !memcmp(keydata, root_ksk_2017, sizeof(root_ksk_2017))) - { - *keyflags |= ROOT_KSK_2017; + "key flags revoke bit set"); } + if (n2 > 0xff) { + cfg_obj_log(key, logctx, ISC_LOG_ERROR, + "protocol too big: %u", n2); + result = ISC_R_RANGE; + } + if (n3 > 0xff) { + cfg_obj_log(key, logctx, ISC_LOG_ERROR, + "algorithm too big: %u\n", n3); + result = ISC_R_RANGE; + } + + isc_buffer_init(&b, keydata, sizeof(keydata)); + + keystr = cfg_obj_asstring(cfg_tuple_get(key, "data")); + tresult = isc_base64_decodestring(keystr, &b); + + if (tresult != ISC_R_SUCCESS) { + cfg_obj_log(key, logctx, ISC_LOG_ERROR, + "%s", isc_result_totext(tresult)); + result = ISC_R_FAILURE; + } else { + isc_buffer_usedregion(&b, &r); + + if ((n3 == DST_ALG_RSASHA1) && + r.length > 1 && r.base[0] == 1 && r.base[1] == 3) + { + cfg_obj_log(key, logctx, ISC_LOG_WARNING, + "%s '%s' has a weak exponent", + atstr, keynamestr); + } + } + + if (result == ISC_R_SUCCESS && + dns_name_equal(keyname, dns_rootname)) { + /* + * Flag any use of a root key, regardless of content. + */ + *keyflags |= + (managed ? ROOT_KSK_MANAGED : ROOT_KSK_STATIC); + + if (n1 == 257 && n2 == 3 && n3 == 8 && + (isc_buffer_usedlength(&b) == + sizeof(root_ksk_2010)) && + memcmp(keydata, root_ksk_2010, + sizeof(root_ksk_2010)) == 0) + { + *keyflags |= ROOT_KSK_2010; + } + + if (n1 == 257 && n2 == 3 && n3 == 8 && + (isc_buffer_usedlength(&b) == + sizeof(root_ksk_2017)) && + memcmp(keydata, root_ksk_2017, + sizeof(root_ksk_2017)) == 0) + { + *keyflags |= ROOT_KSK_2017; + } + } + break; + + case INIT_DS: + case STATIC_DS: + cfg_obj_log(key, logctx, ISC_LOG_ERROR, + "key '%s': " + "initialization method '%s' is " + "not yet supported", keynamestr, atstr); + result = ISC_R_FAILURE; } + cleanup: return (result); } @@ -3322,13 +3383,17 @@ record_static_keys(isc_symtab_t *symtab, isc_mem_t *mctx, continue; } - init = cfg_tuple_get(obj, "init"); + init = cfg_tuple_get(obj, "anchortype"); if (!cfg_obj_isvoid(init)) { initmethod = cfg_obj_asstring(init); if (strcasecmp(initmethod, "initial-key") == 0) { /* initializing key, skip it */ continue; } + if (strcasecmp(initmethod, "initial-ds") == 0) { + /* initializing key, skip it */ + continue; + } } dns_name_format(name, namebuf, sizeof(namebuf)); @@ -3346,7 +3411,7 @@ record_static_keys(isc_symtab_t *symtab, isc_mem_t *mctx, if (autovalidation && dns_name_equal(name, dns_rootname)) { cfg_obj_log(obj, logctx, ISC_LOG_ERROR, - "static key for root zone " + "static trust anchor for root zone " "cannot be used with " "'dnssec-validation auto'."); ret = ISC_R_FAILURE; @@ -3378,9 +3443,10 @@ check_initializing_keys(isc_symtab_t *symtab, const cfg_obj_t *keylist, const char *str; isc_symvalue_t symvalue; - init = cfg_tuple_get(obj, "init"); + init = cfg_tuple_get(obj, "anchortype"); if (cfg_obj_isvoid(init) || - strcasecmp(cfg_obj_asstring(init), "static-key") == 0) + strcasecmp(cfg_obj_asstring(init), "static-key") == 0 || + strcasecmp(cfg_obj_asstring(init), "static-ds") == 0) { /* static key, skip it */ continue; diff --git a/lib/isccfg/namedconf.c b/lib/isccfg/namedconf.c index e0e5217d55..8628cfdaa6 100644 --- a/lib/isccfg/namedconf.c +++ b/lib/isccfg/namedconf.c @@ -445,11 +445,11 @@ static cfg_type_t cfg_type_category = { */ static cfg_tuplefielddef_t dnsseckey_fields[] = { { "name", &cfg_type_astring, 0 }, - { "init", &cfg_type_void, 0 }, - { "flags", &cfg_type_uint32, 0 }, - { "protocol", &cfg_type_uint32, 0 }, - { "algorithm", &cfg_type_uint32, 0 }, - { "key", &cfg_type_qstring, 0 }, + { "anchortype", &cfg_type_void, 0 }, + { "n1", &cfg_type_uint32, 0 }, + { "n2", &cfg_type_uint32, 0 }, + { "n3", &cfg_type_uint32, 0 }, + { "data", &cfg_type_qstring, 0 }, { NULL, NULL, 0 } }; static cfg_type_t cfg_type_dnsseckey = { @@ -461,19 +461,20 @@ static cfg_type_t cfg_type_dnsseckey = { * A key initialization specifier, as used in the * "dnssec-keys" (or synonymous "managed-keys") statement. */ -static const char *init_enums[] = { "static-key", "initial-key", NULL }; -static cfg_type_t cfg_type_keyinit = { - "keyinit", cfg_parse_enum, cfg_print_ustring, cfg_doc_enum, - &cfg_rep_string, &init_enums +static const char *anchortype_enums[] = { + "static-key", "initial-key", "static-ds", "initial-ds", NULL +}; +static cfg_type_t cfg_type_anchortype = { + "anchortype", cfg_parse_enum, cfg_print_ustring, cfg_doc_enum, + &cfg_rep_string, anchortype_enums }; - static cfg_tuplefielddef_t managedkey_fields[] = { { "name", &cfg_type_astring, 0 }, - { "init", &cfg_type_keyinit, 0 }, - { "flags", &cfg_type_uint32, 0 }, - { "protocol", &cfg_type_uint32, 0 }, - { "algorithm", &cfg_type_uint32, 0 }, - { "key", &cfg_type_qstring, 0 }, + { "anchortype", &cfg_type_anchortype, 0 }, + { "n1", &cfg_type_uint32, 0 }, + { "n2", &cfg_type_uint32, 0 }, + { "n3", &cfg_type_uint32, 0 }, + { "data", &cfg_type_qstring, 0 }, { NULL, NULL, 0 } }; static cfg_type_t cfg_type_managedkey = { @@ -692,10 +693,9 @@ static cfg_type_t cfg_type_trustedkeys = { }; /*% - * A list of key entries, as in "trusted-keys". This has a format similar - * to dnssec keys, except the keyname is followed by keyword, either - * "initial-key" or "static-key". If "initial-key", then the key is - * RFC 5011 managed; if "static-key", then the key never changes. + * A list of managed trust anchors. Each entry contains a name, a keyword + * ("static-key", initial-key", "static-ds" or "initial-ds"), and the + * fields associated with either a DNSKEY or a DS record. */ static cfg_type_t cfg_type_dnsseckeys = { "dnsseckeys", cfg_parse_bracketed_list, cfg_print_bracketed_list, From feba480527202908299768244335d60d7983a69f Mon Sep 17 00:00:00 2001 From: Evan Hunt Date: Sun, 15 Sep 2019 23:14:51 -0700 Subject: [PATCH 03/13] read DS trust anchors in named.conf (but they aren't used for anything yet) --- bin/named/server.c | 154 ++++++++++++++---- .../checkconf/bad-static-initial-1.conf | 15 ++ .../checkconf/bad-static-initial-2.conf | 15 ++ .../checkconf/bad-static-initial-3.conf | 15 ++ .../checkconf/bad-static-initial-4.conf | 15 ++ .../checkconf/check-root-static-ds.conf | 14 ++ .../system/checkconf/good-initial-ds.conf | 14 ++ .../system/checkconf/good-static-ds.conf | 14 ++ bin/tests/system/checkconf/tests.sh | 10 +- lib/bind9/check.c | 127 +++++++++++---- 10 files changed, 322 insertions(+), 71 deletions(-) create mode 100644 bin/tests/system/checkconf/bad-static-initial-1.conf create mode 100644 bin/tests/system/checkconf/bad-static-initial-2.conf create mode 100644 bin/tests/system/checkconf/bad-static-initial-3.conf create mode 100644 bin/tests/system/checkconf/bad-static-initial-4.conf create mode 100644 bin/tests/system/checkconf/check-root-static-ds.conf create mode 100644 bin/tests/system/checkconf/good-initial-ds.conf create mode 100644 bin/tests/system/checkconf/good-static-ds.conf diff --git a/bin/named/server.c b/bin/named/server.c index d68d01ae7e..d6f912cbfb 100644 --- a/bin/named/server.c +++ b/bin/named/server.c @@ -698,19 +698,20 @@ configure_view_nametable(const cfg_obj_t *vconfig, const cfg_obj_t *config, } static isc_result_t -ta_fromconfig(const cfg_obj_t *key, bool *initialp, dst_key_t **target, - const char **keynamestrp, isc_mem_t *mctx) +ta_fromconfig(const cfg_obj_t *key, bool *initialp, dst_key_t **keyp, + dns_rdata_ds_t **dsp, const char **namestrp, isc_mem_t *mctx) { dns_rdata_dnskey_t keystruct; + dns_rdata_ds_t *ds = NULL; uint32_t n1, n2, n3; - const char *datastr = NULL, *keynamestr = NULL; + const char *datastr = NULL, *namestr = NULL; unsigned char data[4096]; isc_buffer_t databuf; unsigned char rrdata[4096]; isc_buffer_t rrdatabuf; isc_region_t r; - dns_fixedname_t fkeyname; - dns_name_t *keyname = NULL; + dns_fixedname_t fname; + dns_name_t *name = NULL; isc_buffer_t namebuf; isc_result_t result; dst_key_t *dstkey = NULL; @@ -723,8 +724,9 @@ ta_fromconfig(const cfg_obj_t *key, bool *initialp, dst_key_t **target, TRUSTED } anchortype; - REQUIRE(target != NULL && *target == NULL); - REQUIRE(keynamestrp != NULL && *keynamestrp == NULL); + REQUIRE(keyp != NULL && *keyp == NULL); + REQUIRE(dsp != NULL && *dsp == NULL); + REQUIRE(namestrp != NULL && *namestrp == NULL); /* if DNSKEY, flags; if DS, key tag */ n1 = cfg_obj_asuint32(cfg_tuple_get(key, "n1")); @@ -735,13 +737,13 @@ ta_fromconfig(const cfg_obj_t *key, bool *initialp, dst_key_t **target, /* if DNSKEY, algorithm; if DS, digest type */ n3 = cfg_obj_asuint32(cfg_tuple_get(key, "n3")); - keynamestr = cfg_obj_asstring(cfg_tuple_get(key, "name")); - *keynamestrp = keynamestr; + namestr = cfg_obj_asstring(cfg_tuple_get(key, "name")); + *namestrp = namestr; - keyname = dns_fixedname_initname(&fkeyname); - isc_buffer_constinit(&namebuf, keynamestr, strlen(keynamestr)); - isc_buffer_add(&namebuf, strlen(keynamestr)); - CHECK(dns_name_fromtext(keyname, &namebuf, dns_rootname, 0, NULL)); + name = dns_fixedname_initname(&fname); + isc_buffer_constinit(&namebuf, namestr, strlen(namestr)); + isc_buffer_add(&namebuf, strlen(namestr)); + CHECK(dns_name_fromtext(name, &namebuf, dns_rootname, 0, NULL)); if (*initialp) { atstr = cfg_obj_asstring(cfg_tuple_get(key, "anchortype")); @@ -760,7 +762,7 @@ ta_fromconfig(const cfg_obj_t *key, bool *initialp, dst_key_t **target, cfg_obj_log(key, named_g_lctx, ISC_LOG_ERROR, "key '%s': " "invalid initialization method '%s'", - keynamestr, atstr); + namestr, atstr); result = ISC_R_FAILURE; goto cleanup; } @@ -768,6 +770,9 @@ ta_fromconfig(const cfg_obj_t *key, bool *initialp, dst_key_t **target, anchortype = TRUSTED; } + isc_buffer_init(&databuf, data, sizeof(data)); + isc_buffer_init(&rrdatabuf, rrdata, sizeof(rrdata)); + switch(anchortype) { case INIT_DNSKEY: case STATIC_DNSKEY: @@ -803,9 +808,6 @@ ta_fromconfig(const cfg_obj_t *key, bool *initialp, dst_key_t **target, keystruct.protocol = (uint8_t)n2; keystruct.algorithm = (uint8_t)n3; - isc_buffer_init(&databuf, data, sizeof(data)); - isc_buffer_init(&rrdatabuf, rrdata, sizeof(rrdata)); - datastr = cfg_obj_asstring(cfg_tuple_get(key, "data")); CHECK(isc_base64_decodestring(datastr, &databuf)); isc_buffer_usedregion(&databuf, &r); @@ -815,20 +817,65 @@ ta_fromconfig(const cfg_obj_t *key, bool *initialp, dst_key_t **target, CHECK(dns_rdata_fromstruct(NULL, keystruct.common.rdclass, keystruct.common.rdtype, &keystruct, &rrdatabuf)); - CHECK(dst_key_fromdns(keyname, dns_rdataclass_in, + CHECK(dst_key_fromdns(name, dns_rdataclass_in, &rrdatabuf, mctx, &dstkey)); - *target = dstkey; + *keyp = dstkey; break; case INIT_DS: case STATIC_DS: - cfg_obj_log(key, named_g_lctx, ISC_LOG_ERROR, - "key '%s': " - "initialization method '%s' is " - "not yet supported", keynamestr, atstr); - result = ISC_R_FAILURE; - goto cleanup; + ds = isc_mem_get(mctx, sizeof(*ds)); + ds->common.rdclass = dns_rdataclass_in; + ds->common.rdtype = dns_rdatatype_ds; + ds->mctx = NULL; + + ISC_LINK_INIT(&ds->common, link); + + if (n1 > 0xffff) { + CHECKM(ISC_R_RANGE, "key tag"); + } + if (n2 > 0xff) { + CHECKM(ISC_R_RANGE, "key algorithm"); + } + if (n3 > 0xff) { + CHECKM(ISC_R_RANGE, "digest type"); + } + + ds->key_tag = (uint16_t)n1; + ds->algorithm = (uint8_t)n2; + ds->digest_type = (uint8_t)n3; + + datastr = cfg_obj_asstring(cfg_tuple_get(key, "data")); + CHECK(isc_hex_decodestring(datastr, &databuf)); + isc_buffer_usedregion(&databuf, &r); + + switch (ds->digest_type) { + case DNS_DSDIGEST_SHA1: + if (r.length != ISC_SHA1_DIGESTLENGTH) { + CHECK(ISC_R_UNEXPECTEDEND); + } + break; + case DNS_DSDIGEST_SHA256: + if (r.length != ISC_SHA256_DIGESTLENGTH) { + CHECK(ISC_R_UNEXPECTEDEND); + } + break; + case DNS_DSDIGEST_SHA384: + if (r.length != ISC_SHA384_DIGESTLENGTH) { + CHECK(ISC_R_UNEXPECTEDEND); + } + break; + } + + ds->mctx = mctx; + ds->length = r.length; + ds->digest = isc_mem_allocate(mctx, r.length); + memmove(ds->digest, r.base, r.length); + + *dsp = ds; + ds = NULL; + break; default: INSIST(0); @@ -842,6 +889,11 @@ ta_fromconfig(const cfg_obj_t *key, bool *initialp, dst_key_t **target, dst_key_free(&dstkey); } + if (ds != NULL) { + dns_rdata_freestruct(ds); + isc_mem_put(mctx, ds, sizeof(*ds)); + } + return (result); } @@ -861,23 +913,45 @@ process_key(const cfg_obj_t *key, dns_keytable_t *secroots, const dns_name_t *keyname_match, dns_resolver_t *resolver, bool managed, isc_mem_t *mctx) { - const dns_name_t *keyname = NULL; - const char *keynamestr = NULL; + dns_fixedname_t fkeyname; + dns_name_t *keyname = NULL; + const char *namestr = NULL; dst_key_t *dstkey = NULL; + dns_rdata_ds_t *ds = NULL; unsigned int keyalg; isc_result_t result; bool initializing = managed; - result = ta_fromconfig(key, &initializing, &dstkey, &keynamestr, mctx); + result = ta_fromconfig(key, &initializing, &dstkey, &ds, + &namestr, mctx); switch (result) { case ISC_R_SUCCESS: /* - * Key was parsed correctly, its algorithm is supported by the - * crypto library, and it is not revoked. + * Trust anchor was parsed correctly. If dstkey is + * not NULL, then it was a key anchor, its algorithm + * is supported by the crypto library, and it is not + * revoked. If dstkey is NULL, then it was a DS + * trust anchor instead. */ - keyname = dst_key_name(dstkey); - keyalg = dst_key_alg(dstkey); + if (dstkey != NULL) { + keyname = dst_key_name(dstkey); + keyalg = dst_key_alg(dstkey); + } else { + isc_buffer_t b; + + INSIST(ds != NULL); + + isc_buffer_constinit(&b, namestr, strlen(namestr)); + isc_buffer_add(&b, strlen(namestr)); + keyname = dns_fixedname_initname(&fkeyname); + result = dns_name_fromtext(keyname, &b, + dns_rootname, 0, NULL); + if (result != ISC_R_SUCCESS) { + return (result); + } + keyalg = ds->algorithm; + } break; case DST_R_UNSUPPORTEDALG: case DST_R_BADKEYTYPE: @@ -889,7 +963,7 @@ process_key(const cfg_obj_t *key, dns_keytable_t *secroots, cfg_obj_log(key, named_g_lctx, ISC_LOG_WARNING, "ignoring %s for '%s': %s", initializing ? "initial-key" : "static-key", - keynamestr, isc_result_totext(result)); + namestr, isc_result_totext(result)); return (ISC_R_SUCCESS); case DST_R_NOCRYPTO: /* @@ -898,7 +972,7 @@ process_key(const cfg_obj_t *key, dns_keytable_t *secroots, cfg_obj_log(key, named_g_lctx, ISC_LOG_ERROR, "ignoring %s for '%s': no crypto support", initializing ? "initial-key" : "static-key", - keynamestr); + namestr); return (result); default: /* @@ -909,7 +983,7 @@ process_key(const cfg_obj_t *key, dns_keytable_t *secroots, cfg_obj_log(key, named_g_lctx, ISC_LOG_ERROR, "configuring %s for '%s': %s", initializing ? "initial-key" : "static-key", - keynamestr, isc_result_totext(result)); + namestr, isc_result_totext(result)); return (ISC_R_FAILURE); } @@ -931,7 +1005,7 @@ process_key(const cfg_obj_t *key, dns_keytable_t *secroots, cfg_obj_log(key, named_g_lctx, ISC_LOG_WARNING, "ignoring %s for '%s': algorithm is disabled", initializing ? "initial-key" : "static-key", - keynamestr); + namestr); goto done; } @@ -955,6 +1029,14 @@ process_key(const cfg_obj_t *key, dns_keytable_t *secroots, dst_key_free(&dstkey); } + /* + * Ensure 'ds' does not leak. + */ + if (ds != NULL) { + dns_rdata_freestruct(ds); + isc_mem_put(mctx, ds, sizeof(*ds)); + } + return (result); } diff --git a/bin/tests/system/checkconf/bad-static-initial-1.conf b/bin/tests/system/checkconf/bad-static-initial-1.conf new file mode 100644 index 0000000000..406b12b8a1 --- /dev/null +++ b/bin/tests/system/checkconf/bad-static-initial-1.conf @@ -0,0 +1,15 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +dnssec-keys { + example. initial-ds 60724 5 1 "D74CF845955A0DFE604AF215E948E67D2EA94FF3"; + example. static-ds 60724 5 2 "29E79B9064EE1A11DF3BFF19581DDFED7952C22CC204ACE17B6007EB1437E9E6"; +}; diff --git a/bin/tests/system/checkconf/bad-static-initial-2.conf b/bin/tests/system/checkconf/bad-static-initial-2.conf new file mode 100644 index 0000000000..a8805b56a0 --- /dev/null +++ b/bin/tests/system/checkconf/bad-static-initial-2.conf @@ -0,0 +1,15 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +dnssec-keys { + example. initial-ds 60724 5 1 "D74CF845955A0DFE604AF215E948E67D2EA94FF3"; + example. static-key 257 3 5 "AwEAAZtP9+RAA+W33A97e+HnnH8WTXzCWiEICyWj1B6rvZ9hd50ysbody0NLx7b3vZ1bzMLxLSRAr/n3Wi0TDZ1fvCKZhennfW8Wlc7ulCvHntSQYfKHUP0YWEo84sQAqIi850N1aiddj6CidwFo9JNW/HQ+8yarfrnGMFhX2STtkE0hNJ/R6JYKmD2EH7k1nyqJd08ibrEt55DuV4BiUjyyERdVbsuwE60jVqAwCKyVBYXb2sI+zv1yPNDBIANd6KTgnq6YWzx5ZodQP3W4K7Z/Bk3EKmVCvrTKZK/ADLAKaL0/6DD07+1jXA4BiNyoZTLTapkudkGad+Rn6zqCkwuMmrU="; +}; diff --git a/bin/tests/system/checkconf/bad-static-initial-3.conf b/bin/tests/system/checkconf/bad-static-initial-3.conf new file mode 100644 index 0000000000..53bdc2cf8e --- /dev/null +++ b/bin/tests/system/checkconf/bad-static-initial-3.conf @@ -0,0 +1,15 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +dnssec-keys { + example. static-ds 60724 5 1 "D74CF845955A0DFE604AF215E948E67D2EA94FF3"; + example. initial-key 257 3 5 "AwEAAZtP9+RAA+W33A97e+HnnH8WTXzCWiEICyWj1B6rvZ9hd50ysbody0NLx7b3vZ1bzMLxLSRAr/n3Wi0TDZ1fvCKZhennfW8Wlc7ulCvHntSQYfKHUP0YWEo84sQAqIi850N1aiddj6CidwFo9JNW/HQ+8yarfrnGMFhX2STtkE0hNJ/R6JYKmD2EH7k1nyqJd08ibrEt55DuV4BiUjyyERdVbsuwE60jVqAwCKyVBYXb2sI+zv1yPNDBIANd6KTgnq6YWzx5ZodQP3W4K7Z/Bk3EKmVCvrTKZK/ADLAKaL0/6DD07+1jXA4BiNyoZTLTapkudkGad+Rn6zqCkwuMmrU="; +}; diff --git a/bin/tests/system/checkconf/bad-static-initial-4.conf b/bin/tests/system/checkconf/bad-static-initial-4.conf new file mode 100644 index 0000000000..bc2996236b --- /dev/null +++ b/bin/tests/system/checkconf/bad-static-initial-4.conf @@ -0,0 +1,15 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +dnssec-keys { + example. initial-key 257 3 5 "AwEAAawvFp8GlBx8Qt6yaIqXkDe+nMkSk2HkTAG7qlVBo++AQwZ1j3Xl25IN4jsw0VTMbKUbafw9DYsVzztIwx1sNkKRLo6qP9SSkBL8RicQaafGtURtsYI3oqte5qqLve1CUpRD8J06Pg1xkOxsDlz9sQAyiQrOyvMbykJYkYrFYGLzYAgl/JtMyVVYlBl9pqxQuAPKYPOuO1axaad/wLN3+wTy/hcJfpvJpqzXlDF9bI5RmpoX/7geZ06vpcYJEoT0xkkmPlEl0ZjEDrm/WIaSWG0/CEDpHcOXFz4OEczMVpY+lnuFfKybwF1WHFn2BwVEOS6cMM6ukIjINQyrszHhWUU="; + example. static-key 257 3 5 "AwEAAZtP9+RAA+W33A97e+HnnH8WTXzCWiEICyWj1B6rvZ9hd50ysbody0NLx7b3vZ1bzMLxLSRAr/n3Wi0TDZ1fvCKZhennfW8Wlc7ulCvHntSQYfKHUP0YWEo84sQAqIi850N1aiddj6CidwFo9JNW/HQ+8yarfrnGMFhX2STtkE0hNJ/R6JYKmD2EH7k1nyqJd08ibrEt55DuV4BiUjyyERdVbsuwE60jVqAwCKyVBYXb2sI+zv1yPNDBIANd6KTgnq6YWzx5ZodQP3W4K7Z/Bk3EKmVCvrTKZK/ADLAKaL0/6DD07+1jXA4BiNyoZTLTapkudkGad+Rn6zqCkwuMmrU="; +}; diff --git a/bin/tests/system/checkconf/check-root-static-ds.conf b/bin/tests/system/checkconf/check-root-static-ds.conf new file mode 100644 index 0000000000..42af9ba801 --- /dev/null +++ b/bin/tests/system/checkconf/check-root-static-ds.conf @@ -0,0 +1,14 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +dnssec-keys { + . static-ds 20326 8 2 "E06D44B80B8F1D39A95C0B0D7C65D08458E880409BBC683457104237C7F8EC8D"; +}; diff --git a/bin/tests/system/checkconf/good-initial-ds.conf b/bin/tests/system/checkconf/good-initial-ds.conf new file mode 100644 index 0000000000..31939c26dd --- /dev/null +++ b/bin/tests/system/checkconf/good-initial-ds.conf @@ -0,0 +1,14 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +dnssec-keys { + example. initial-ds 60724 5 2 "29E79B9064EE1A11DF3BFF19581DDFED7952C22CC204ACE17B6007EB1437E9E6"; +}; diff --git a/bin/tests/system/checkconf/good-static-ds.conf b/bin/tests/system/checkconf/good-static-ds.conf new file mode 100644 index 0000000000..fd5b393bfe --- /dev/null +++ b/bin/tests/system/checkconf/good-static-ds.conf @@ -0,0 +1,14 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +dnssec-keys { + example. static-ds 60724 5 2 "29E79B9064EE1A11DF3BFF19581DDFED7952C22CC204ACE17B6007EB1437E9E6"; +}; diff --git a/bin/tests/system/checkconf/tests.sh b/bin/tests/system/checkconf/tests.sh index 45e6cefb42..a25753e1bf 100644 --- a/bin/tests/system/checkconf/tests.sh +++ b/bin/tests/system/checkconf/tests.sh @@ -437,7 +437,15 @@ n=`expr $n + 1` echo_i "check that a static root key generates a warning ($n)" ret=0 $CHECKCONF check-root-static-key.conf > checkconf.out$n 2>/dev/null || ret=1 -grep "static-key entry for the root zone WILL FAIL" checkconf.out$n > /dev/null || ret=1 +grep "static entry for the root zone WILL FAIL" checkconf.out$n > /dev/null || ret=1 +if [ $ret != 0 ]; then echo_i "failed"; ret=1; fi +status=`expr $status + $ret` + +n=`expr $n + 1` +echo_i "check that a static root DS trust anchor generates a warning ($n)" +ret=0 +$CHECKCONF check-root-static-ds.conf > checkconf.out$n 2>/dev/null || ret=1 +grep "static entry for the root zone WILL FAIL" checkconf.out$n > /dev/null || ret=1 if [ $ret != 0 ]; then echo_i "failed"; ret=1; fi status=`expr $status + $ret` diff --git a/lib/bind9/check.c b/lib/bind9/check.c index 302c3dcc00..020d49982c 100644 --- a/lib/bind9/check.c +++ b/lib/bind9/check.c @@ -3119,10 +3119,10 @@ check_servers(const cfg_obj_t *config, const cfg_obj_t *voptions, #define ROOT_KSK_2017 0x08 static isc_result_t -check_trusted_key(const cfg_obj_t *key, bool managed, - unsigned int *keyflags, isc_log_t *logctx) +check_trust_anchor(const cfg_obj_t *key, bool managed, + unsigned int *flagsp, isc_log_t *logctx) { - const char *keystr = NULL, *keynamestr = NULL; + const char *str = NULL, *namestr = NULL; dns_fixedname_t fkeyname; dns_name_t *keyname = NULL; isc_buffer_t b; @@ -3130,7 +3130,7 @@ check_trusted_key(const cfg_obj_t *key, bool managed, isc_result_t result = ISC_R_SUCCESS; isc_result_t tresult; uint32_t n1, n2, n3; - unsigned char keydata[4096]; + unsigned char data[4096]; const char *atstr = NULL; enum { INIT_DNSKEY, @@ -3142,8 +3142,8 @@ check_trusted_key(const cfg_obj_t *key, bool managed, /* * The 2010 and 2017 IANA root keys - these are used below - * to check the contents of trusted-key, initial-key and - * static-key configurations. + * to check the contents of trusted, initial and + * static trust anchor configurations. */ static const unsigned char root_ksk_2010[] = { 0x03, 0x01, 0x00, 0x01, 0xa8, 0x00, 0x20, 0xa9, @@ -3215,7 +3215,17 @@ check_trusted_key(const cfg_obj_t *key, bool managed, 0x67, 0xe9, 0x4c, 0x0d, 0x47, 0x50, 0x24, 0x51, 0x35, 0x7b, 0xe1, 0xb5 }; - + static const unsigned char root_ds_1_2017[] = { + 0xae, 0x1e, 0xa5, 0xb9, 0x74, 0xd4, 0xc8, 0x58, + 0xb7, 0x40, 0xbd, 0x03, 0xe3, 0xce, 0xd7, 0xeb, + 0xfc, 0xbd, 0x17, 0x24 + }; + static const unsigned char root_ds_2_2017[] = { + 0xe0, 0x6d, 0x44, 0xb8, 0x0b, 0x8f, 0x1d, 0x39, + 0xa9, 0x5c, 0x0b, 0x0d, 0x7c, 0x65, 0xd0, 0x84, + 0x58, 0xe8, 0x80, 0x40, 0x9b, 0xbc, 0x68, 0x34, + 0x57, 0x10, 0x42, 0x37, 0xc7, 0xf8, 0xec, 0x8D + }; /* if DNSKEY, flags; if DS, key tag */ n1 = cfg_obj_asuint32(cfg_tuple_get(key, "n1")); @@ -3226,11 +3236,11 @@ check_trusted_key(const cfg_obj_t *key, bool managed, /* if DNSKEY, algorithm; if DS, digest type */ n3 = cfg_obj_asuint32(cfg_tuple_get(key, "n3")); - keynamestr = cfg_obj_asstring(cfg_tuple_get(key, "name")); + namestr = cfg_obj_asstring(cfg_tuple_get(key, "name")); keyname = dns_fixedname_initname(&fkeyname); - isc_buffer_constinit(&b, keynamestr, strlen(keynamestr)); - isc_buffer_add(&b, strlen(keynamestr)); + isc_buffer_constinit(&b, namestr, strlen(namestr)); + isc_buffer_add(&b, strlen(namestr)); result = dns_name_fromtext(keyname, &b, dns_rootname, 0, NULL); if (result != ISC_R_SUCCESS) { cfg_obj_log(key, logctx, ISC_LOG_WARNING, "bad key name: %s\n", @@ -3255,7 +3265,7 @@ check_trusted_key(const cfg_obj_t *key, bool managed, cfg_obj_log(key, logctx, ISC_LOG_ERROR, "key '%s': " "invalid initialization method '%s'", - keynamestr, atstr); + namestr, atstr); result = ISC_R_FAILURE; /* @@ -3282,7 +3292,7 @@ check_trusted_key(const cfg_obj_t *key, bool managed, cfg_obj_log(key, logctx, ISC_LOG_WARNING, "key flags revoke bit set"); } - if (n2 > 0xff) { + if (n2 > 0xff) { cfg_obj_log(key, logctx, ISC_LOG_ERROR, "protocol too big: %u", n2); result = ISC_R_RANGE; @@ -3293,10 +3303,10 @@ check_trusted_key(const cfg_obj_t *key, bool managed, result = ISC_R_RANGE; } - isc_buffer_init(&b, keydata, sizeof(keydata)); + isc_buffer_init(&b, data, sizeof(data)); - keystr = cfg_obj_asstring(cfg_tuple_get(key, "data")); - tresult = isc_base64_decodestring(keystr, &b); + str = cfg_obj_asstring(cfg_tuple_get(key, "data")); + tresult = isc_base64_decodestring(str, &b); if (tresult != ISC_R_SUCCESS) { cfg_obj_log(key, logctx, ISC_LOG_ERROR, @@ -3310,7 +3320,7 @@ check_trusted_key(const cfg_obj_t *key, bool managed, { cfg_obj_log(key, logctx, ISC_LOG_WARNING, "%s '%s' has a weak exponent", - atstr, keynamestr); + atstr, namestr); } } @@ -3319,36 +3329,85 @@ check_trusted_key(const cfg_obj_t *key, bool managed, /* * Flag any use of a root key, regardless of content. */ - *keyflags |= + *flagsp |= (managed ? ROOT_KSK_MANAGED : ROOT_KSK_STATIC); + if (n1 == 257 && n2 == 3 && n3 == 8 && (isc_buffer_usedlength(&b) == sizeof(root_ksk_2010)) && - memcmp(keydata, root_ksk_2010, + memcmp(data, root_ksk_2010, sizeof(root_ksk_2010)) == 0) { - *keyflags |= ROOT_KSK_2010; + *flagsp |= ROOT_KSK_2010; } if (n1 == 257 && n2 == 3 && n3 == 8 && (isc_buffer_usedlength(&b) == sizeof(root_ksk_2017)) && - memcmp(keydata, root_ksk_2017, + memcmp(data, root_ksk_2017, sizeof(root_ksk_2017)) == 0) { - *keyflags |= ROOT_KSK_2017; + *flagsp |= ROOT_KSK_2017; } } break; case INIT_DS: case STATIC_DS: - cfg_obj_log(key, logctx, ISC_LOG_ERROR, - "key '%s': " - "initialization method '%s' is " - "not yet supported", keynamestr, atstr); - result = ISC_R_FAILURE; + if (n1 > 0xffff) { + cfg_obj_log(key, logctx, ISC_LOG_ERROR, + "key tag too big: %u", n1); + result = ISC_R_RANGE; + } + if (n2 > 0xff) { + cfg_obj_log(key, logctx, ISC_LOG_ERROR, + "algorithm too big: %u\n", n2); + result = ISC_R_RANGE; + } + if (n3 > 0xff) { + cfg_obj_log(key, logctx, ISC_LOG_ERROR, + "digest type too big: %u", 32); + result = ISC_R_RANGE; + } + + isc_buffer_init(&b, data, sizeof(data)); + + str = cfg_obj_asstring(cfg_tuple_get(key, "data")); + tresult = isc_hex_decodestring(str, &b); + + if (tresult != ISC_R_SUCCESS) { + cfg_obj_log(key, logctx, ISC_LOG_ERROR, + "%s", isc_result_totext(tresult)); + result = ISC_R_FAILURE; + } + if (result == ISC_R_SUCCESS && + dns_name_equal(keyname, dns_rootname)) { + /* + * Flag any use of a root key, regardless of content. + */ + *flagsp |= + (managed ? ROOT_KSK_MANAGED : ROOT_KSK_STATIC); + + if (n1 == 20326 && n2 == 8 && n3 == 1 && + (isc_buffer_usedlength(&b) == + sizeof(root_ds_1_2017)) && + memcmp(data, root_ds_1_2017, + sizeof(root_ds_1_2017)) == 0) + { + *flagsp |= ROOT_KSK_2017; + } + + if (n1 == 20326 && n2 == 8 && n3 == 2 && + (isc_buffer_usedlength(&b) == + sizeof(root_ds_2_2017)) && + memcmp(data, root_ds_2_2017, + sizeof(root_ds_2_2017)) == 0) + { + *flagsp |= ROOT_KSK_2017; + } + } + break; } cleanup: @@ -3964,8 +4023,7 @@ check_viewconf(const cfg_obj_t *config, const cfg_obj_t *voptions, element2 = cfg_list_next(element2)) { obj = cfg_listelt_value(element2); - tresult = check_trusted_key(obj, - false, + tresult = check_trust_anchor(obj, false, &flags, logctx); if (tresult != ISC_R_SUCCESS) { @@ -3980,7 +4038,8 @@ check_viewconf(const cfg_obj_t *config, const cfg_obj_t *voptions, "trusted-keys entry for the root " "zone WILL FAIL after key " "rollover - use dnssec-keys " - "with initial-key instead."); + "with initial-key " + "or initial-ds instead."); } tflags |= flags; @@ -4024,8 +4083,7 @@ check_viewconf(const cfg_obj_t *config, const cfg_obj_t *voptions, element2 = cfg_list_next(element2)) { obj = cfg_listelt_value(element2); - tresult = check_trusted_key(obj, - true, + tresult = check_trust_anchor(obj, true, &flags, logctx); if (tresult != ISC_R_SUCCESS) { @@ -4037,10 +4095,11 @@ check_viewconf(const cfg_obj_t *config, const cfg_obj_t *voptions, if ((flags & ROOT_KSK_STATIC) != 0) { cfg_obj_log(check_keys[i], logctx, ISC_LOG_WARNING, - "static-key entry for the root " + "static entry for the root " "zone WILL FAIL after key " "rollover - use dnssec-keys " - "with initial-key instead."); + "with initial-key " + "or initial-ds instead."); } if ((flags & ROOT_KSK_2010) != 0 && @@ -4067,7 +4126,7 @@ check_viewconf(const cfg_obj_t *config, const cfg_obj_t *voptions, if ((dflags & ROOT_KSK_ANY) == ROOT_KSK_ANY) { keys = (view_dkeys != NULL) ? view_dkeys : global_dkeys; cfg_obj_log(keys, logctx, ISC_LOG_WARNING, - "both initial-key and static-key entries for the " + "both initial and static entries for the " "root zone are present"); } From 1a8348e2b45891e4297e7c1e6d2c7fe8b65cd508 Mon Sep 17 00:00:00 2001 From: Evan Hunt Date: Mon, 16 Sep 2019 13:56:31 -0700 Subject: [PATCH 04/13] disallow use of DS- and key-style trust anchors for the same name --- bin/tests/system/checkconf/bad-ds-key-1.conf | 15 ++ bin/tests/system/checkconf/bad-ds-key-2.conf | 15 ++ lib/bind9/check.c | 167 +++++++++++++++++-- 3 files changed, 184 insertions(+), 13 deletions(-) create mode 100644 bin/tests/system/checkconf/bad-ds-key-1.conf create mode 100644 bin/tests/system/checkconf/bad-ds-key-2.conf diff --git a/bin/tests/system/checkconf/bad-ds-key-1.conf b/bin/tests/system/checkconf/bad-ds-key-1.conf new file mode 100644 index 0000000000..ffc9c56a1f --- /dev/null +++ b/bin/tests/system/checkconf/bad-ds-key-1.conf @@ -0,0 +1,15 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +dnssec-keys { + example. initial-ds 60724 5 1 "D74CF845955A0DFE604AF215E948E67D2EA94FF3"; + example. initial-key 257 3 5 "AwEAAZtP9+RAA+W33A97e+HnnH8WTXzCWiEICyWj1B6rvZ9hd50ysbody0NLx7b3vZ1bzMLxLSRAr/n3Wi0TDZ1fvCKZhennfW8Wlc7ulCvHntSQYfKHUP0YWEo84sQAqIi850N1aiddj6CidwFo9JNW/HQ+8yarfrnGMFhX2STtkE0hNJ/R6JYKmD2EH7k1nyqJd08ibrEt55DuV4BiUjyyERdVbsuwE60jVqAwCKyVBYXb2sI+zv1yPNDBIANd6KTgnq6YWzx5ZodQP3W4K7Z/Bk3EKmVCvrTKZK/ADLAKaL0/6DD07+1jXA4BiNyoZTLTapkudkGad+Rn6zqCkwuMmrU="; +}; diff --git a/bin/tests/system/checkconf/bad-ds-key-2.conf b/bin/tests/system/checkconf/bad-ds-key-2.conf new file mode 100644 index 0000000000..bcaf4e1651 --- /dev/null +++ b/bin/tests/system/checkconf/bad-ds-key-2.conf @@ -0,0 +1,15 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +dnssec-keys { + example. static-ds 60724 5 1 "D74CF845955A0DFE604AF215E948E67D2EA94FF3"; + example. static-key 257 3 5 "AwEAAZtP9+RAA+W33A97e+HnnH8WTXzCWiEICyWj1B6rvZ9hd50ysbody0NLx7b3vZ1bzMLxLSRAr/n3Wi0TDZ1fvCKZhennfW8Wlc7ulCvHntSQYfKHUP0YWEo84sQAqIi850N1aiddj6CidwFo9JNW/HQ+8yarfrnGMFhX2STtkE0hNJ/R6JYKmD2EH7k1nyqJd08ibrEt55DuV4BiUjyyERdVbsuwE60jVqAwCKyVBYXb2sI+zv1yPNDBIANd6KTgnq6YWzx5ZodQP3W4K7Z/Bk3EKmVCvrTKZK/ADLAKaL0/6DD07+1jXA4BiNyoZTLTapkudkGad+Rn6zqCkwuMmrU="; +}; diff --git a/lib/bind9/check.c b/lib/bind9/check.c index 020d49982c..0dc28db325 100644 --- a/lib/bind9/check.c +++ b/lib/bind9/check.c @@ -3539,6 +3539,118 @@ check_initializing_keys(isc_symtab_t *symtab, const cfg_obj_t *keylist, return (ret); } +static isc_result_t +record_ds_keys(isc_symtab_t *symtab, isc_mem_t *mctx, const cfg_obj_t *keylist) +{ + isc_result_t result, ret = ISC_R_SUCCESS; + const cfg_listelt_t *elt; + dns_fixedname_t fixed; + dns_name_t *name; + char namebuf[DNS_NAME_FORMATSIZE], *p = NULL; + + name = dns_fixedname_initname(&fixed); + + for (elt = cfg_list_first(keylist); + elt != NULL; + elt = cfg_list_next(elt)) + { + const char *initmethod; + const cfg_obj_t *init = NULL; + const cfg_obj_t *obj = cfg_listelt_value(elt); + const char *str = cfg_obj_asstring(cfg_tuple_get(obj, "name")); + isc_symvalue_t symvalue; + + result = dns_name_fromstring(name, str, 0, NULL); + if (result != ISC_R_SUCCESS) { + continue; + } + + init = cfg_tuple_get(obj, "anchortype"); + if (!cfg_obj_isvoid(init)) { + initmethod = cfg_obj_asstring(init); + if (strcasecmp(initmethod, "initial-key") == 0 || + strcasecmp(initmethod, "static-key") == 0) + { + /* Key-style key, skip it */ + continue; + } + } + + dns_name_format(name, namebuf, sizeof(namebuf)); + symvalue.as_cpointer = obj; + p = isc_mem_strdup(mctx, namebuf); + result = isc_symtab_define(symtab, p, 1, symvalue, + isc_symexists_reject); + if (result == ISC_R_EXISTS) { + isc_mem_free(mctx, p); + } else if (result != ISC_R_SUCCESS) { + isc_mem_free(mctx, p); + ret = result; + continue; + } + } + + return (ret); +} + +static isc_result_t +check_non_ds_keys(isc_symtab_t *symtab, const cfg_obj_t *keylist, + isc_log_t *logctx) +{ + isc_result_t result, ret = ISC_R_SUCCESS; + const cfg_listelt_t *elt; + dns_fixedname_t fixed; + dns_name_t *name; + char namebuf[DNS_NAME_FORMATSIZE]; + + name = dns_fixedname_initname(&fixed); + + for (elt = cfg_list_first(keylist); + elt != NULL; + elt = cfg_list_next(elt)) + { + const cfg_obj_t *obj = cfg_listelt_value(elt); + const cfg_obj_t *init = NULL; + const char *str; + isc_symvalue_t symvalue; + + init = cfg_tuple_get(obj, "anchortype"); + if (cfg_obj_isvoid(init) || + strcasecmp(cfg_obj_asstring(init), "static-ds") == 0 || + strcasecmp(cfg_obj_asstring(init), "initial-ds") == 0) + { + /* DS-style entry, skip it */ + continue; + } + + str = cfg_obj_asstring(cfg_tuple_get(obj, "name")); + result = dns_name_fromstring(name, str, 0, NULL); + if (result != ISC_R_SUCCESS) { + continue; + } + + dns_name_format(name, namebuf, sizeof(namebuf)); + result = isc_symtab_lookup(symtab, namebuf, 1, &symvalue); + if (result == ISC_R_SUCCESS) { + const char *file = cfg_obj_file(symvalue.as_cpointer); + unsigned int line = cfg_obj_line(symvalue.as_cpointer); + if (file == NULL) { + file = ""; + } + cfg_obj_log(obj, logctx, ISC_LOG_ERROR, + "ds-style and key-style keys " + "cannot be used for the " + "same domain. " + "ds-style defined at " + "%s:%u", file, line); + + ret = ISC_R_FAILURE; + } + } + + return (ret); +} + /* * Check for conflicts between static and initialiizing keys. */ @@ -3550,27 +3662,38 @@ check_ta_conflicts(const cfg_obj_t *global_dkeys, const cfg_obj_t *view_dkeys, isc_result_t result, tresult; const cfg_listelt_t *elt = NULL; const cfg_obj_t *keylist = NULL; - isc_symtab_t *symtab = NULL; + isc_symtab_t *statictab = NULL, *dstab = NULL; - result = isc_symtab_create(mctx, 100, freekey, mctx, false, &symtab); + result = isc_symtab_create(mctx, 100, freekey, mctx, false, &statictab); + if (result != ISC_R_SUCCESS) { + goto cleanup; + } + + result = isc_symtab_create(mctx, 100, freekey, mctx, false, &dstab); if (result != ISC_R_SUCCESS) { goto cleanup; } /* * First we record all the static keys (i.e., old-style - * trusted-keys and dnssec-keys configured with "static-key") + * trusted-keys and dnssec-keys configured with "static-key"), + * and all the DS-style trust anchors. */ for (elt = cfg_list_first(global_dkeys); elt != NULL; elt = cfg_list_next(elt)) { keylist = cfg_listelt_value(elt); - tresult = record_static_keys(symtab, mctx, keylist, + tresult = record_static_keys(statictab, mctx, keylist, logctx, autovalidation); if (result == ISC_R_SUCCESS) { result = tresult; } + + tresult = record_ds_keys(dstab, mctx, keylist); + if (result == ISC_R_SUCCESS) { + result = tresult; + } } for (elt = cfg_list_first(view_dkeys); @@ -3578,11 +3701,16 @@ check_ta_conflicts(const cfg_obj_t *global_dkeys, const cfg_obj_t *view_dkeys, elt = cfg_list_next(elt)) { keylist = cfg_listelt_value(elt); - tresult = record_static_keys(symtab, mctx, keylist, + tresult = record_static_keys(statictab, mctx, keylist, logctx, autovalidation); if (result == ISC_R_SUCCESS) { result = tresult; } + + tresult = record_ds_keys(dstab, mctx, keylist); + if (result == ISC_R_SUCCESS) { + result = tresult; + } } for (elt = cfg_list_first(global_tkeys); @@ -3590,7 +3718,7 @@ check_ta_conflicts(const cfg_obj_t *global_dkeys, const cfg_obj_t *view_dkeys, elt = cfg_list_next(elt)) { keylist = cfg_listelt_value(elt); - tresult = record_static_keys(symtab, mctx, keylist, + tresult = record_static_keys(statictab, mctx, keylist, logctx, autovalidation); if (result == ISC_R_SUCCESS) { result = tresult; @@ -3602,24 +3730,29 @@ check_ta_conflicts(const cfg_obj_t *global_dkeys, const cfg_obj_t *view_dkeys, elt = cfg_list_next(elt)) { keylist = cfg_listelt_value(elt); - tresult = record_static_keys(symtab, mctx, keylist, + tresult = record_static_keys(statictab, mctx, keylist, logctx, autovalidation); if (result == ISC_R_SUCCESS) { result = tresult; } } - /* * Next, ensure that there's no conflict between the - * static keys and the dnssec-keys configured with "initial-key" + * static keys and the dnssec-keys configured with "initial-key", + * or between DS-style and DNSKEY-style dnssec-keys. */ for (elt = cfg_list_first(global_dkeys); elt != NULL; elt = cfg_list_next(elt)) { keylist = cfg_listelt_value(elt); - tresult = check_initializing_keys(symtab, keylist, logctx); + tresult = check_initializing_keys(statictab, keylist, logctx); + if (result == ISC_R_SUCCESS) { + result = tresult; + } + + tresult = check_non_ds_keys(dstab, keylist, logctx); if (result == ISC_R_SUCCESS) { result = tresult; } @@ -3630,15 +3763,23 @@ check_ta_conflicts(const cfg_obj_t *global_dkeys, const cfg_obj_t *view_dkeys, elt = cfg_list_next(elt)) { keylist = cfg_listelt_value(elt); - tresult = check_initializing_keys(symtab, keylist, logctx); + tresult = check_initializing_keys(statictab, keylist, logctx); + if (result == ISC_R_SUCCESS) { + result = tresult; + } + + tresult = check_non_ds_keys(dstab, keylist, logctx); if (result == ISC_R_SUCCESS) { result = tresult; } } cleanup: - if (symtab != NULL) { - isc_symtab_destroy(&symtab); + if (statictab != NULL) { + isc_symtab_destroy(&statictab); + } + if (dstab != NULL) { + isc_symtab_destroy(&dstab); } return (result); } From 854af5a353eb97d1a0ca840dea45ac1305a29dd2 Mon Sep 17 00:00:00 2001 From: Evan Hunt Date: Mon, 16 Sep 2019 21:06:23 -0700 Subject: [PATCH 05/13] allow DS trust anchors to be set in keytable note: this is a frankensteinian kluge which needs further refactoring. the keytable started as an RBT where the node->data points to a list of dns_keynode structures, each of which points to a single dst_key. later it was modified so that the list could instead point to a single "null" keynode structure, which does not reference a key; this means a trust anchor has been configured but the RFC 5011 refresh failed. in this branch it is further updated to allow the first keynode in the list to point to an rdatalist of DS-style trust anchors. these will be used by the validator to populate 'val->dsset' when validating a zone key. a DS style trust anchor can be updated as a result of RFC 5011 processing to contain DST keys instead; this results in the DS list being freed. the reverse is not possible; attempting to add a DS-style trust anchor if a key-style trust anchor is already in place results in an error. later, this should be refactored to use rdatalists for both DS-style and key-style trust anchors, but we're keeping the existing code for old-style trust anchors for now. --- bin/named/server.c | 6 +- lib/dns/client.c | 3 +- lib/dns/include/dns/keytable.h | 30 +++- lib/dns/keytable.c | 307 ++++++++++++++++++++++++++------- lib/dns/tests/keytable_test.c | 33 ++-- lib/dns/win32/libdns.def.in | 1 + lib/dns/zone.c | 3 +- 7 files changed, 304 insertions(+), 79 deletions(-) diff --git a/bin/named/server.c b/bin/named/server.c index d6f912cbfb..1b63900bd9 100644 --- a/bin/named/server.c +++ b/bin/named/server.c @@ -1017,7 +1017,9 @@ process_key(const cfg_obj_t *key, dns_keytable_t *secroots, * 'managed' and 'initializing' arguments to dns_keytable_add(). */ result = dns_keytable_add(secroots, initializing, - initializing, &dstkey); + initializing, keyname, + dstkey != NULL ? &dstkey : NULL, + ds); done: /* @@ -1030,7 +1032,7 @@ process_key(const cfg_obj_t *key, dns_keytable_t *secroots, } /* - * Ensure 'ds' does not leak. + * Free 'ds'. */ if (ds != NULL) { dns_rdata_freestruct(ds); diff --git a/lib/dns/client.c b/lib/dns/client.c index 48ed6aa88c..971c4e3af2 100644 --- a/lib/dns/client.c +++ b/lib/dns/client.c @@ -1507,7 +1507,8 @@ dns_client_addtrustedkey(dns_client_t *client, dns_rdataclass_t rdclass, if (result != ISC_R_SUCCESS) goto cleanup; - result = dns_keytable_add(secroots, false, false, &dstkey); + result = dns_keytable_add(secroots, false, false, + dst_key_name(dstkey), &dstkey, NULL); cleanup: if (dstkey != NULL) diff --git a/lib/dns/include/dns/keytable.h b/lib/dns/include/dns/keytable.h index 66fc5c1a84..0a68cb037c 100644 --- a/lib/dns/include/dns/keytable.h +++ b/lib/dns/include/dns/keytable.h @@ -42,6 +42,7 @@ #include #include +#include #include #include @@ -105,10 +106,14 @@ dns_keytable_detach(dns_keytable_t **keytablep); */ isc_result_t -dns_keytable_add(dns_keytable_t *keytable, bool managed, - bool initial, dst_key_t **keyp); +dns_keytable_add(dns_keytable_t *keytable, + bool managed, bool initial, + dns_name_t *name, dst_key_t **keyp, dns_rdata_ds_t *ds); /*%< - * Add '*keyp' to 'keytable' (using the name in '*keyp'). + * Add a key to 'keytable'. The keynode associated with 'name' + * is updated with either the key referenced in '*keyp' + * or with the DS specified in 'ds'. + * * The value of keynode->managed is set to 'managed', and the * value of keynode->initial is set to 'initial'. (Note: 'initial' * should only be used when adding managed-keys from configuration. @@ -119,6 +124,12 @@ dns_keytable_add(dns_keytable_t *keytable, bool managed, * Notes: * *\li Ownership of *keyp is transferred to the keytable. + *\li If 'keyp' is not NULL and DS-style keys already exist + * in the table for this name, they are freed before adding + * the new key. + *\li If 'ds' is not NULL and key-style keys already exist + * in the table for this name, return ISC_R_EXISTS. DS keys + * can be updated to key-style, but not vice versa. *\li If the key already exists in the table, ISC_R_EXISTS is * returned and the new key is freed. * @@ -407,6 +418,19 @@ dns_keynode_key(dns_keynode_t *keynode); * Get the DST key associated with keynode. */ +dns_rdataset_t * +dns_keynode_dsset(dns_keynode_t *keynode); +/*%< + * Return a pointer to the DS RRset associated with 'keynode'. + * + * Returns: + *\li ISC_R_SUCCESS + *\li ISC_R_FAILURE if the keynode does not contain a DS trust anchor. + * + * Requires: + *\li 'keynode' is valid. + */ + bool dns_keynode_managed(dns_keynode_t *keynode); /*%< diff --git a/lib/dns/keytable.c b/lib/dns/keytable.c index a154699d45..55aecc2892 100644 --- a/lib/dns/keytable.c +++ b/lib/dns/keytable.c @@ -24,6 +24,10 @@ #include #include #include +#include +#include +#include +#include #include #define KEYTABLE_MAGIC ISC_MAGIC('K', 'T', 'b', 'l') @@ -46,10 +50,12 @@ struct dns_keytable { struct dns_keynode { unsigned int magic; isc_refcount_t refcount; - dst_key_t * key; + dst_key_t *key; + dns_rdatalist_t *dslist; + dns_rdataset_t dsset; bool managed; bool initial; - struct dns_keynode * next; + struct dns_keynode *next; }; static void @@ -135,12 +141,32 @@ dns_keytable_detach(dns_keytable_t **keytablep) { } } +static void +free_dslist(isc_mem_t *mctx, dns_keynode_t *knode) { + dns_rdata_t *rdata = NULL; + + for (rdata = ISC_LIST_HEAD(knode->dslist->rdata); + rdata != NULL; + rdata = ISC_LIST_HEAD(knode->dslist->rdata)) + { + ISC_LIST_UNLINK(knode->dslist->rdata, rdata, link); + isc_mem_put(mctx, rdata->data, DNS_DS_BUFFERSIZE); + isc_mem_put(mctx, rdata, sizeof(*rdata)); + } + + isc_mem_put(mctx, knode->dslist, sizeof(*knode->dslist)); + knode->dslist = NULL; +} + /*% - * Search "node" for either a null key node or a key node for the exact same - * key as the one supplied in "keyp" and, if found, update it accordingly. + * Search "node" for an empty or DS-style keynode, or a keynode for the + * exact same key as the one supplied in "keyp" and, if found, update it + * accordingly. */ static isc_result_t -update_keynode(dst_key_t **keyp, dns_rbtnode_t *node, bool initial) { +update_keynode(dns_keytable_t *keytable, dns_rbtnode_t *node, + dst_key_t **keyp, bool initial) +{ dns_keynode_t *knode; REQUIRE(keyp != NULL && *keyp != NULL); @@ -149,13 +175,17 @@ update_keynode(dst_key_t **keyp, dns_rbtnode_t *node, bool initial) { for (knode = node->data; knode != NULL; knode = knode->next) { if (knode->key == NULL) { /* - * Null key node found. Attach the supplied key to it, - * making it a non-null key node and transferring key + * Null or DS-style keynode found. Detach + * the DS rdatalist if present. Attach the + * supplied key to it, transferring key * ownership to the keytable. */ + if (knode->dslist != NULL) { + free_dslist(keytable->mctx, knode); + } + knode->key = *keyp; *keyp = NULL; - return (ISC_R_SUCCESS); } else if (dst_key_compare(knode->key, *keyp)) { /* * Key node found for the supplied key. Free the @@ -163,11 +193,15 @@ update_keynode(dst_key_t **keyp, dns_rbtnode_t *node, bool initial) { * node's flags if necessary. */ dst_key_free(keyp); - if (!initial) { - dns_keynode_trust(knode); - } - return (ISC_R_SUCCESS); + } else { + continue; } + + if (!initial) { + dns_keynode_trust(knode); + } + + return (ISC_R_SUCCESS); } return (ISC_R_NOTFOUND); @@ -179,9 +213,9 @@ update_keynode(dst_key_t **keyp, dns_rbtnode_t *node, bool initial) { * one attached to "node" in "keytable". */ static isc_result_t -prepend_keynode(dst_key_t **keyp, dns_rbtnode_t *node, - dns_keytable_t *keytable, bool managed, - bool initial) +prepend_keynode(dst_key_t **keyp, dns_rdata_ds_t *ds, + dns_rbtnode_t *node, dns_keytable_t *keytable, + bool managed, bool initial) { dns_keynode_t *knode = NULL; isc_result_t result; @@ -196,15 +230,60 @@ prepend_keynode(dst_key_t **keyp, dns_rbtnode_t *node, } /* - * If a key was supplied, transfer its ownership to the keytable. + * If a dst_key was supplied, transfer its ownership to the keytable. + * Otherwise, if a DS was supplied, append it to the rdatalist + * (initializing if necessary). */ - if (keyp) { + if (keyp != NULL) { + if (knode->dslist != NULL) { + free_dslist(keytable->mctx, knode); + } knode->key = *keyp; *keyp = NULL; + } else if (ds != NULL) { + dns_rdata_t *rdata = NULL; + void *data = NULL; + isc_buffer_t b; + + if (knode->dslist == NULL) { + knode->dslist = isc_mem_get(keytable->mctx, + sizeof(*knode->dslist)); + dns_rdatalist_init(knode->dslist); + knode->dslist->rdclass = dns_rdataclass_in; + knode->dslist->type = dns_rdatatype_ds; + knode->dslist->ttl = 0; + } + + rdata = isc_mem_get(keytable->mctx, sizeof(*rdata)); + dns_rdata_init(rdata); + + data = isc_mem_get(keytable->mctx, DNS_DS_BUFFERSIZE); + isc_buffer_init(&b, data, DNS_DS_BUFFERSIZE); + + result = dns_rdata_fromstruct(rdata, dns_rdataclass_in, + dns_rdatatype_ds, ds, &b); + if (result != ISC_R_SUCCESS) { + return (result); + } + + ISC_LIST_APPEND(knode->dslist->rdata, rdata, link); + + if (dns_rdataset_isassociated(&knode->dsset)) { + dns_rdataset_disassociate(&knode->dsset); + } + + result = dns_rdatalist_tordataset(knode->dslist, + &knode->dsset); + if (result != ISC_R_SUCCESS) { + return (result); + } + + knode->dsset.trust = dns_trust_ultimate; } knode->managed = managed; knode->initial = initial; + knode->next = node->data; node->data = knode; @@ -219,7 +298,7 @@ prepend_keynode(dst_key_t **keyp, dns_rbtnode_t *node, */ static isc_result_t insert(dns_keytable_t *keytable, bool managed, bool initial, - const dns_name_t *keyname, dst_key_t **keyp) + const dns_name_t *keyname, dst_key_t **keyp, dns_rdata_ds_t *ds) { dns_rbtnode_t *node = NULL; isc_result_t result; @@ -232,17 +311,17 @@ insert(dns_keytable_t *keytable, bool managed, bool initial, if (result == ISC_R_SUCCESS) { /* * There was no node for "keyname" in "keytable" yet, so one - * was created. Create a new key node for the supplied key (or - * a null key node if "keyp" is NULL) and attach it to the - * created node. + * was created. Create a new key node for the supplied + * trust anchor (or a null key node if both "keyp" and + * "ds" are NULL) and attach it to the created node. */ - result = prepend_keynode(keyp, node, keytable, managed, - initial); + result = prepend_keynode(keyp, ds, node, keytable, + managed, initial); } else if (result == ISC_R_EXISTS) { /* * A node already exists for "keyname" in "keytable". */ - if (keyp == NULL) { + if (keyp == NULL && ds == NULL) { /* * We were told to add a null key at "keyname", which * means there is nothing left to do as there is either @@ -252,7 +331,7 @@ insert(dns_keytable_t *keytable, bool managed, bool initial, * "keyname" is already marked as secure. */ result = ISC_R_SUCCESS; - } else { + } else if (keyp != NULL) { /* * We were told to add the key supplied in "keyp" at * "keyname". Try to find an already existing key node @@ -260,7 +339,7 @@ insert(dns_keytable_t *keytable, bool managed, bool initial, * node or a key node for the exact same key) and, if * found, update it accordingly. */ - result = update_keynode(keyp, node, initial); + result = update_keynode(keytable, node, keyp, initial); if (result == ISC_R_NOTFOUND) { /* * The node for "keyname" only contains key @@ -268,9 +347,13 @@ insert(dns_keytable_t *keytable, bool managed, bool initial, * one. Create a new key node for the supplied * key and prepend it before the others. */ - result = prepend_keynode(keyp, node, keytable, + result = prepend_keynode(keyp, NULL, + node, keytable, managed, initial); } + } else if (ds != NULL) { + result = prepend_keynode(NULL, ds, node, keytable, + managed, initial); } } @@ -280,17 +363,19 @@ insert(dns_keytable_t *keytable, bool managed, bool initial, } isc_result_t -dns_keytable_add(dns_keytable_t *keytable, bool managed, - bool initial, dst_key_t **keyp) +dns_keytable_add(dns_keytable_t *keytable, + bool managed, bool initial, + dns_name_t *name, dst_key_t **keyp, dns_rdata_ds_t *ds) { - REQUIRE(keyp != NULL && *keyp != NULL); + REQUIRE(keyp == NULL || *keyp != NULL); + REQUIRE(keyp != NULL || ds != NULL); REQUIRE(!initial || managed); - return (insert(keytable, managed, initial, dst_key_name(*keyp), keyp)); + return (insert(keytable, managed, initial, name, keyp, ds)); } isc_result_t dns_keytable_marksecure(dns_keytable_t *keytable, const dns_name_t *name) { - return (insert(keytable, true, false, name, NULL)); + return (insert(keytable, true, false, name, NULL, NULL)); } isc_result_t @@ -420,8 +505,9 @@ dns_keytable_nextkeynode(dns_keytable_t *keytable, dns_keynode_t *keynode, REQUIRE(VALID_KEYNODE(keynode)); REQUIRE(nextnodep != NULL && *nextnodep == NULL); - if (keynode->next == NULL) + if (keynode->next == NULL) { return (ISC_R_NOTFOUND); + } dns_keynode_attach(keynode->next, nextnodep); isc_refcount_increment(&keytable->active_nodes); @@ -459,25 +545,29 @@ dns_keytable_findkeynode(dns_keytable_t *keytable, const dns_name_t *name, knode = NULL; data = NULL; result = dns_rbt_findname(keytable->table, name, 0, NULL, &data); - if (result == ISC_R_SUCCESS) { INSIST(data != NULL); + for (knode = data; knode != NULL; knode = knode->next) { if (knode->key == NULL) { knode = NULL; break; } - if (algorithm == dst_key_alg(knode->key) - && tag == dst_key_id(knode->key)) + if (algorithm == dst_key_alg(knode->key) && + tag == dst_key_id(knode->key)) + { break; + } } if (knode != NULL) { isc_refcount_increment0(&keytable->active_nodes); dns_keynode_attach(knode, keynodep); - } else + } else { result = DNS_R_PARTIALMATCH; - } else if (result == DNS_R_PARTIALMATCH) + } + } else if (result == DNS_R_PARTIALMATCH) { result = ISC_R_NOTFOUND; + } RWUNLOCK(&keytable->rwlock, isc_rwlocktype_read); @@ -507,14 +597,17 @@ dns_keytable_findnextkeynode(dns_keytable_t *keytable, dns_keynode_t *keynode, } if (dst_key_alg(keynode->key) == dst_key_alg(knode->key) && dst_key_id(keynode->key) == dst_key_id(knode->key)) + { break; + } } if (knode != NULL) { isc_refcount_increment(&keytable->active_nodes); result = ISC_R_SUCCESS; dns_keynode_attach(knode, nextnodep); - } else + } else { result = ISC_R_NOTFOUND; + } return (result); } @@ -539,8 +632,9 @@ dns_keytable_finddeepestmatch(dns_keytable_t *keytable, const dns_name_t *name, data = NULL; result = dns_rbt_findname(keytable->table, name, 0, foundname, &data); - if (result == ISC_R_SUCCESS || result == DNS_R_PARTIALMATCH) + if (result == ISC_R_SUCCESS || result == DNS_R_PARTIALMATCH) { result = ISC_R_SUCCESS; + } RWUNLOCK(&keytable->rwlock, isc_rwlocktype_read); @@ -616,8 +710,9 @@ putstr(isc_buffer_t **b, const char *str) { isc_result_t result; result = isc_buffer_reserve(b, strlen(str)); - if (result != ISC_R_SUCCESS) + if (result != ISC_R_SUCCESS) { return (result); + } isc_buffer_putstr(*b, str); return (ISC_R_SUCCESS); @@ -639,9 +734,9 @@ dns_keytable_dump(dns_keytable_t *keytable, FILE *fp) { if (isc_buffer_usedlength(text) != 0) { (void) putstr(&text, "\n"); - } else if (result == ISC_R_SUCCESS) + } else if (result == ISC_R_SUCCESS) { (void) putstr(&text, "none"); - else { + } else { (void) putstr(&text, "could not dump key table: "); (void) putstr(&text, isc_result_totext(result)); } @@ -653,44 +748,112 @@ dns_keytable_dump(dns_keytable_t *keytable, FILE *fp) { return (result); } +static isc_result_t +keynode_dslist_totext(dns_name_t *name, dns_keynode_t *keynode, + isc_buffer_t **text) +{ + isc_result_t result; + char namebuf[DNS_NAME_FORMATSIZE]; + char obuf[DNS_NAME_FORMATSIZE + 200]; + dns_rdataset_t *dsset = NULL; + + dns_name_format(name, namebuf, sizeof(namebuf)); + + dsset = dns_keynode_dsset(keynode); + + for (result = dns_rdataset_first(dsset); + result == ISC_R_SUCCESS; + result = dns_rdataset_next(dsset)) + { + char algbuf[DNS_SECALG_FORMATSIZE]; + dns_rdata_t rdata = DNS_RDATA_INIT; + dns_rdata_ds_t ds; + + dns_rdataset_current(dsset, &rdata); + result = dns_rdata_tostruct(&rdata, &ds, NULL); + RUNTIME_CHECK(result == ISC_R_SUCCESS); + + dns_secalg_format(ds.algorithm, algbuf, sizeof(algbuf)); + + snprintf(obuf, sizeof(obuf), "%s/%s/%d ; %s%s (DS)\n", + namebuf, algbuf, ds.key_tag, + keynode->initial ? "initializing " : "", + keynode->managed ? "managed" : "static"); + + result = putstr(text, obuf); + if (result != ISC_R_SUCCESS) { + return (result); + } + } + + return (ISC_R_SUCCESS); +} + isc_result_t dns_keytable_totext(dns_keytable_t *keytable, isc_buffer_t **text) { isc_result_t result; dns_keynode_t *knode; dns_rbtnode_t *node; dns_rbtnodechain_t chain; + dns_name_t *foundname, *origin, *fullname; + dns_fixedname_t fixedfoundname, fixedorigin, fixedfullname; REQUIRE(VALID_KEYTABLE(keytable)); REQUIRE(text != NULL && *text != NULL); + origin = dns_fixedname_initname(&fixedorigin); + fullname = dns_fixedname_initname(&fixedfullname); + foundname = dns_fixedname_initname(&fixedfoundname); + RWLOCK(&keytable->rwlock, isc_rwlocktype_read); dns_rbtnodechain_init(&chain); result = dns_rbtnodechain_first(&chain, keytable->table, NULL, NULL); if (result != ISC_R_SUCCESS && result != DNS_R_NEWORIGIN) { - if (result == ISC_R_NOTFOUND) + if (result == ISC_R_NOTFOUND) { result = ISC_R_SUCCESS; + } goto cleanup; } for (;;) { char pbuf[DST_KEY_FORMATSIZE]; - dns_rbtnodechain_current(&chain, NULL, NULL, &node); - for (knode = node->data; knode != NULL; knode = knode->next) { + dns_rbtnodechain_current(&chain, foundname, origin, &node); + + knode = node->data; + if (knode != NULL && knode->dslist != NULL) { + result = dns_name_concatenate(foundname, origin, + fullname, NULL); + if (result != ISC_R_SUCCESS) { + goto cleanup; + } + + result = keynode_dslist_totext(fullname, knode, text); + goto cleanup; + } + + for (; knode != NULL; knode = knode->next) { char obuf[DNS_NAME_FORMATSIZE + 200]; - if (knode->key == NULL) + + if (knode->key == NULL) { continue; + } + dst_key_format(knode->key, pbuf, sizeof(pbuf)); snprintf(obuf, sizeof(obuf), "%s ; %s%s\n", pbuf, knode->initial ? "initializing " : "", knode->managed ? "managed" : "static"); + result = putstr(text, obuf); - if (result != ISC_R_SUCCESS) + if (result != ISC_R_SUCCESS) { break; + } } + result = dns_rbtnodechain_next(&chain, NULL, NULL); if (result != ISC_R_SUCCESS && result != DNS_R_NEWORIGIN) { - if (result == ISC_R_NOMORE) + if (result == ISC_R_NOMORE) { result = ISC_R_SUCCESS; + } break; } } @@ -716,19 +879,22 @@ dns_keytable_forall(dns_keytable_t *keytable, dns_rbtnodechain_init(&chain); result = dns_rbtnodechain_first(&chain, keytable->table, NULL, NULL); if (result != ISC_R_SUCCESS && result != DNS_R_NEWORIGIN) { - if (result == ISC_R_NOTFOUND) + if (result == ISC_R_NOTFOUND) { result = ISC_R_SUCCESS; + } goto cleanup; } isc_refcount_increment0(&keytable->active_nodes); for (;;) { dns_rbtnodechain_current(&chain, NULL, NULL, &node); - if (node->data != NULL) + if (node->data != NULL) { (*func)(keytable, node->data, arg); + } result = dns_rbtnodechain_next(&chain, NULL, NULL); if (result != ISC_R_SUCCESS && result != DNS_R_NEWORIGIN) { - if (result == ISC_R_NOMORE) + if (result == ISC_R_NOMORE) { result = ISC_R_SUCCESS; + } break; } } @@ -747,6 +913,17 @@ dns_keynode_key(dns_keynode_t *keynode) { return (keynode->key); } +dns_rdataset_t * +dns_keynode_dsset(dns_keynode_t *keynode) { + REQUIRE(VALID_KEYNODE(keynode)); + + if (keynode->dslist != NULL) { + return (&keynode->dsset); + } + + return (NULL); +} + bool dns_keynode_managed(dns_keynode_t *keynode) { REQUIRE(VALID_KEYNODE(keynode)); @@ -780,6 +957,8 @@ dns_keynode_create(isc_mem_t *mctx, dns_keynode_t **target) { knode->managed = false; knode->initial = false; knode->key = NULL; + knode->dslist = NULL; + dns_rdataset_init(&knode->dsset); knode->next = NULL; isc_refcount_init(&knode->refcount, 1); @@ -796,17 +975,23 @@ dns_keynode_attach(dns_keynode_t *source, dns_keynode_t **target) { } void -dns_keynode_detach(isc_mem_t *mctx, dns_keynode_t **keynode) { - REQUIRE(keynode != NULL && VALID_KEYNODE(*keynode)); - dns_keynode_t *node = *keynode; - *keynode = NULL; +dns_keynode_detach(isc_mem_t *mctx, dns_keynode_t **keynodep) { + REQUIRE(keynodep != NULL && VALID_KEYNODE(*keynodep)); + dns_keynode_t *knode = *keynodep; + *keynodep = NULL; - if (isc_refcount_decrement(&node->refcount) == 1) { - isc_refcount_destroy(&node->refcount); - if (node->key != NULL) { - dst_key_free(&node->key); + if (isc_refcount_decrement(&knode->refcount) == 1) { + isc_refcount_destroy(&knode->refcount); + if (knode->key != NULL) { + dst_key_free(&knode->key); } - isc_mem_put(mctx, node, sizeof(dns_keynode_t)); + if (knode->dslist != NULL) { + if (dns_rdataset_isassociated(&knode->dsset)) { + dns_rdataset_disassociate(&knode->dsset); + } + free_dslist(mctx, knode); + } + isc_mem_put(mctx, knode, sizeof(dns_keynode_t)); } } diff --git a/lib/dns/tests/keytable_test.c b/lib/dns/tests/keytable_test.c index c6f777f03a..0ad5323ea7 100644 --- a/lib/dns/tests/keytable_test.c +++ b/lib/dns/tests/keytable_test.c @@ -155,12 +155,14 @@ create_tables() { /* Add a normal key */ create_key(257, 3, 5, "example.com", keystr1, &key); - assert_int_equal(dns_keytable_add(keytable, false, false, &key), + assert_int_equal(dns_keytable_add(keytable, false, false, + dst_key_name(key), &key, NULL), ISC_R_SUCCESS); /* Add an initializing managed key */ create_key(257, 3, 5, "managed.com", keystr1, &key); - assert_int_equal(dns_keytable_add(keytable, true, true, &key), + assert_int_equal(dns_keytable_add(keytable, true, true, + dst_key_name(key), &key, NULL), ISC_R_SUCCESS); /* Add a null key */ @@ -216,7 +218,8 @@ add_test(void **state) { * nextkeynode() should still return NOTFOUND. */ create_key(257, 3, 5, "example.com", keystr1, &key); - assert_int_equal(dns_keytable_add(keytable, false, false, &key), + assert_int_equal(dns_keytable_add(keytable, false, false, + dst_key_name(key), &key, NULL), ISC_R_SUCCESS); assert_int_equal(dns_keytable_nextkeynode(keytable, keynode, &next_keynode), @@ -225,7 +228,8 @@ add_test(void **state) { /* Add another key (different keydata) */ dns_keytable_detachkeynode(keytable, &keynode); create_key(257, 3, 5, "example.com", keystr2, &key); - assert_int_equal(dns_keytable_add(keytable, false, false, &key), + assert_int_equal(dns_keytable_add(keytable, false, false, + dst_key_name(key), &key, NULL), ISC_R_SUCCESS); assert_int_equal(dns_keytable_find(keytable, str2name("example.com"), &keynode), @@ -259,7 +263,8 @@ add_test(void **state) { * ISC_R_NOTFOUND and that the added key is an initializing key. */ create_key(257, 3, 5, "managed.com", keystr2, &key); - assert_int_equal(dns_keytable_add(keytable, true, true, &key), + assert_int_equal(dns_keytable_add(keytable, true, true, + dst_key_name(key), &key, NULL), ISC_R_SUCCESS); assert_int_equal(dns_keytable_find(keytable, str2name("managed.com"), &keynode), @@ -278,7 +283,8 @@ add_test(void **state) { * nodes for managed.com, both containing non-initializing keys. */ create_key(257, 3, 5, "managed.com", keystr2, &key); - assert_int_equal(dns_keytable_add(keytable, true, false, &key), + assert_int_equal(dns_keytable_add(keytable, true, false, + dst_key_name(key), &key, NULL), ISC_R_SUCCESS); assert_int_equal(dns_keytable_find(keytable, str2name("managed.com"), &keynode), @@ -302,7 +308,8 @@ add_test(void **state) { * that the added key is an initializing key. */ create_key(257, 3, 5, "two.com", keystr1, &key); - assert_int_equal(dns_keytable_add(keytable, true, true, &key), + assert_int_equal(dns_keytable_add(keytable, true, true, + dst_key_name(key), &key, NULL), ISC_R_SUCCESS); assert_int_equal(dns_keytable_find(keytable, str2name("two.com"), &keynode), @@ -319,7 +326,8 @@ add_test(void **state) { * ISC_R_NOTFOUND and that the added key is not an initializing key. */ create_key(257, 3, 5, "two.com", keystr2, &key); - assert_int_equal(dns_keytable_add(keytable, true, false, &key), + assert_int_equal(dns_keytable_add(keytable, true, false, + dst_key_name(key), &key, NULL), ISC_R_SUCCESS); assert_int_equal(dns_keytable_find(keytable, str2name("two.com"), &keynode), @@ -337,7 +345,8 @@ add_test(void **state) { * nodes for two.com, both containing non-initializing keys. */ create_key(257, 3, 5, "two.com", keystr1, &key); - assert_int_equal(dns_keytable_add(keytable, true, false, &key), + assert_int_equal(dns_keytable_add(keytable, true, false, + dst_key_name(key), &key, NULL), ISC_R_SUCCESS); assert_int_equal(dns_keytable_find(keytable, str2name("two.com"), &keynode), @@ -363,7 +372,8 @@ add_test(void **state) { &null_keynode), ISC_R_SUCCESS); create_key(257, 3, 5, "null.example", keystr2, &key); - assert_int_equal(dns_keytable_add(keytable, false, false, &key), + assert_int_equal(dns_keytable_add(keytable, false, false, + dst_key_name(key), &key, NULL), ISC_R_SUCCESS); assert_int_equal(dns_keytable_find(keytable, str2name("null.example"), &keynode), @@ -663,7 +673,8 @@ nta_test(void **state) { assert_int_equal(result, ISC_R_SUCCESS); create_key(257, 3, 5, "example", keystr1, &key); - result = dns_keytable_add(keytable, false, false, &key); + result = dns_keytable_add(keytable, false, false, + dst_key_name(key), &key, NULL), assert_int_equal(result, ISC_R_SUCCESS); isc_stdtime_get(&now); diff --git a/lib/dns/win32/libdns.def.in b/lib/dns/win32/libdns.def.in index 0988db5d27..b1a02a0f68 100644 --- a/lib/dns/win32/libdns.def.in +++ b/lib/dns/win32/libdns.def.in @@ -463,6 +463,7 @@ dns_keynode_attach dns_keynode_create dns_keynode_detach dns_keynode_detachall +dns_keynode_dsset dns_keynode_initial dns_keynode_key dns_keynode_managed diff --git a/lib/dns/zone.c b/lib/dns/zone.c index 41af9487ac..9e85916104 100644 --- a/lib/dns/zone.c +++ b/lib/dns/zone.c @@ -3985,7 +3985,8 @@ trust_key(dns_zone_t *zone, dns_name_t *keyname, goto failure; CHECK(dns_dnssec_keyfromrdata(keyname, &rdata, mctx, &dstkey)); - CHECK(dns_keytable_add(sr, true, initial, &dstkey)); + CHECK(dns_keytable_add(sr, true, initial, + dst_key_name(dstkey), &dstkey, NULL)); dns_keytable_detach(&sr); failure: From a8f89e9a9f117b7ec938972f9cfe915a3ae83de3 Mon Sep 17 00:00:00 2001 From: Evan Hunt Date: Tue, 17 Sep 2019 09:09:41 -0700 Subject: [PATCH 06/13] use DS-style trust anchor to verify 5011 key refresh query note: this also needs further refactoring. - when initializing RFC 5011 for a name, we populate the managed-keys zone with KEYDATA records derived from the initial-key trust anchors. however, with initial-ds trust anchors, there is no key. but the managed-keys zone still must have a KEYDATA record for the name, otherwise zone_refreshkeys() won't refresh that key. so, for initial-ds trust anchors, we now add an empty KEYDATA record and set the key refresh timer so that the real keys will be looked up as soon as possible. - when a key refresh query is done, we verify it against the trust anchor; this is done in two ways, one with the DS RRset set up during configuration if present, or with the keys linked from each keynode in the list if not. because there are two different verification methods, the loop structure is overly complex and should be simplified. - the keyfetch_done() and sync_keyzone() functions are both too long and should be broken into smaller functions. --- bin/named/server.c | 6 +- lib/dns/include/dns/keytable.h | 3 +- lib/dns/keytable.c | 16 +- lib/dns/zone.c | 354 +++++++++++++++++++++++---------- 4 files changed, 269 insertions(+), 110 deletions(-) diff --git a/bin/named/server.c b/bin/named/server.c index 1b63900bd9..6e96359fa7 100644 --- a/bin/named/server.c +++ b/bin/named/server.c @@ -6886,7 +6886,9 @@ get_tat_qname(dns_name_t *dst, const dns_name_t **origin, } static void -dotat(dns_keytable_t *keytable, dns_keynode_t *keynode, void *arg) { +dotat(dns_keytable_t *keytable, dns_keynode_t *keynode, + dns_name_t *name, void *arg) +{ struct dotat_arg *dotat_arg = arg; char namebuf[DNS_NAME_FORMATSIZE]; const dns_name_t *origin = NULL; @@ -6902,6 +6904,8 @@ dotat(dns_keytable_t *keytable, dns_keynode_t *keynode, void *arg) { REQUIRE(keynode != NULL); REQUIRE(dotat_arg != NULL); + UNUSED(name); + view = dotat_arg->view; task = dotat_arg->task; diff --git a/lib/dns/include/dns/keytable.h b/lib/dns/include/dns/keytable.h index 0a68cb037c..bcafdc3623 100644 --- a/lib/dns/include/dns/keytable.h +++ b/lib/dns/include/dns/keytable.h @@ -477,7 +477,8 @@ dns_keynode_detachall(isc_mem_t *mctx, dns_keynode_t **target); isc_result_t dns_keytable_forall(dns_keytable_t *keytable, - void (*func)(dns_keytable_t *, dns_keynode_t *, void *), + void (*func)(dns_keytable_t *, dns_keynode_t *, + dns_name_t *, void *), void *arg); ISC_LANG_ENDDECLS diff --git a/lib/dns/keytable.c b/lib/dns/keytable.c index 55aecc2892..cdc54071e7 100644 --- a/lib/dns/keytable.c +++ b/lib/dns/keytable.c @@ -866,15 +866,22 @@ dns_keytable_totext(dns_keytable_t *keytable, isc_buffer_t **text) { isc_result_t dns_keytable_forall(dns_keytable_t *keytable, - void (*func)(dns_keytable_t *, dns_keynode_t *, void *), + void (*func)(dns_keytable_t *, dns_keynode_t *, + dns_name_t *, void *), void *arg) { isc_result_t result; dns_rbtnode_t *node; dns_rbtnodechain_t chain; + dns_fixedname_t fixedfoundname, fixedorigin, fixedfullname; + dns_name_t *foundname, *origin, *fullname; REQUIRE(VALID_KEYTABLE(keytable)); + origin = dns_fixedname_initname(&fixedorigin); + fullname = dns_fixedname_initname(&fixedfullname); + foundname = dns_fixedname_initname(&fixedfoundname); + RWLOCK(&keytable->rwlock, isc_rwlocktype_read); dns_rbtnodechain_init(&chain); result = dns_rbtnodechain_first(&chain, keytable->table, NULL, NULL); @@ -886,9 +893,12 @@ dns_keytable_forall(dns_keytable_t *keytable, } isc_refcount_increment0(&keytable->active_nodes); for (;;) { - dns_rbtnodechain_current(&chain, NULL, NULL, &node); + dns_rbtnodechain_current(&chain, foundname, origin, &node); if (node->data != NULL) { - (*func)(keytable, node->data, arg); + result = dns_name_concatenate(foundname, origin, + fullname, NULL); + RUNTIME_CHECK(result == ISC_R_SUCCESS); + (*func)(keytable, node->data, fullname, arg); } result = dns_rbtnodechain_next(&chain, NULL, NULL); if (result != ISC_R_SUCCESS && result != DNS_R_NEWORIGIN) { diff --git a/lib/dns/zone.c b/lib/dns/zone.c index 9e85916104..cad1b8282b 100644 --- a/lib/dns/zone.c +++ b/lib/dns/zone.c @@ -3828,13 +3828,18 @@ set_refreshkeytimer(dns_zone_t *zone, dns_rdata_keydata_t *key, } /* - * Convert key(s) linked from 'keynode' to KEYDATA and add to the key zone. + * Convert key(s) linked from 'keynode' to KEYDATA and add it to the + * key zone. If `keynode` doesn't point to a key, then this is a + * DS-style trust anchor, so create an empty KEYDATA instead; it + * will be replaced with the correct key on refresh. + * * If the key zone is changed, set '*changed' to true. */ static isc_result_t create_keydata(dns_zone_t *zone, dns_db_t *db, dns_dbversion_t *ver, dns_diff_t *diff, dns_keytable_t *keytable, - dns_keynode_t **keynodep, bool *changed) + dns_keynode_t **keynodep, dns_name_t *keyname, + bool *changed) { const char me[] = "create_keydata"; isc_result_t result = ISC_R_SUCCESS; @@ -3843,10 +3848,10 @@ create_keydata(dns_zone_t *zone, dns_db_t *db, dns_dbversion_t *ver, dns_rdata_keydata_t keydata; dns_rdata_dnskey_t dnskey; dns_rdata_t rdata = DNS_RDATA_INIT; - dns_keynode_t *keynode; + dns_keynode_t *keynode = NULL; isc_stdtime_t now; isc_region_t r; - dst_key_t *key; + dst_key_t *key = NULL; REQUIRE(keynodep != NULL); keynode = *keynodep; @@ -3854,13 +3859,47 @@ create_keydata(dns_zone_t *zone, dns_db_t *db, dns_dbversion_t *ver, ENTER; isc_stdtime_get(&now); + if (dns_keynode_dsset(keynode) != NULL) { + dns_rdata_keydata_t kd = { 0 }; + unsigned char rrdata[4096]; + isc_buffer_t rrdatabuf; + + /* + * We're creating a placeholder KEYDATA record + * to be updated when the key zone is refreshed + * later. + */ + kd.common.rdclass = zone->rdclass; + kd.common.rdtype = dns_rdatatype_keydata; + ISC_LINK_INIT(&kd.common, link); + + isc_buffer_init(&rrdatabuf, rrdata, sizeof(rrdata)); + + CHECK(dns_rdata_fromstruct(&rdata, zone->rdclass, + dns_rdatatype_keydata, + &kd, &rrdatabuf)); + /* Add rdata to zone. */ + CHECK(update_one_rr(db, ver, diff, DNS_DIFFOP_ADD, + keyname, 0, &rdata)); + *changed = true; + + /* Refresh new keys from the zone apex as soon as possible. */ + set_refreshkeytimer(zone, &keydata, now, true); + + result = ISC_R_NOMORE; + } else { + /* Proceed to the while loop below */ + result = ISC_R_SUCCESS; + } + /* Loop in case there's more than one key. */ while (result == ISC_R_SUCCESS) { dns_keynode_t *nextnode = NULL; key = dns_keynode_key(keynode); - if (key == NULL) + if (key == NULL) { goto skip; + } isc_buffer_init(&dstb, dst_buf, sizeof(dst_buf)); CHECK(dst_key_todns(key, &dstb)); @@ -3885,7 +3924,7 @@ create_keydata(dns_zone_t *zone, dns_db_t *db, dns_dbversion_t *ver, /* Add rdata to zone. */ CHECK(update_one_rr(db, ver, diff, DNS_DIFFOP_ADD, - dst_key_name(key), 0, &rdata)); + keyname, 0, &rdata)); *changed = true; /* Refresh new keys from the zone apex as soon as possible. */ @@ -3899,8 +3938,9 @@ create_keydata(dns_zone_t *zone, dns_db_t *db, dns_dbversion_t *ver, } } - if (keynode != NULL) + if (keynode != NULL) { dns_keytable_detachkeynode(keytable, &keynode); + } *keynodep = NULL; return (ISC_R_SUCCESS); @@ -4263,7 +4303,9 @@ struct addifmissing_arg { }; static void -addifmissing(dns_keytable_t *keytable, dns_keynode_t *keynode, void *arg) { +addifmissing(dns_keytable_t *keytable, dns_keynode_t *keynode, + dns_name_t *keyname, void *arg) +{ dns_db_t *db = ((struct addifmissing_arg *)arg)->db; dns_dbversion_t *ver = ((struct addifmissing_arg *)arg)->ver; dns_diff_t *diff = ((struct addifmissing_arg *)arg)->diff; @@ -4271,43 +4313,61 @@ addifmissing(dns_keytable_t *keytable, dns_keynode_t *keynode, void *arg) { bool *changed = ((struct addifmissing_arg *)arg)->changed; isc_result_t result; dns_keynode_t *dummy = NULL; + dns_fixedname_t fname; - if (((struct addifmissing_arg *)arg)->result != ISC_R_SUCCESS) + + if (((struct addifmissing_arg *)arg)->result != ISC_R_SUCCESS) { return; - - if (dns_keynode_managed(keynode)) { - dns_fixedname_t fname; - dns_name_t *keyname; - dst_key_t *key; - - key = dns_keynode_key(keynode); - if (key == NULL) - return; - dns_fixedname_init(&fname); - - keyname = dst_key_name(key); - result = dns_db_find(db, keyname, ver, - dns_rdatatype_keydata, - DNS_DBFIND_NOWILD, 0, NULL, - dns_fixedname_name(&fname), - NULL, NULL); - if (result == ISC_R_SUCCESS) - return; - dns_keytable_attachkeynode(keytable, keynode, &dummy); - result = create_keydata(zone, db, ver, diff, keytable, - &dummy, changed); - if (result != ISC_R_SUCCESS && result != ISC_R_NOMORE) - ((struct addifmissing_arg *)arg)->result = result; } -}; + + if (!dns_keynode_managed(keynode)) { + return; + } + + /* + * If the keynode has neither a key-style nor a DS-style + * trust anchor, return. + */ + if (dns_keynode_dsset(keynode) == NULL && + dns_keynode_key(keynode) == NULL) + { + return; + } + + /* + * Check whether there's already a KEYDATA entry for this name; + * if so, we don't need to add another. + */ + dns_fixedname_init(&fname); + result = dns_db_find(db, keyname, ver, dns_rdatatype_keydata, + DNS_DBFIND_NOWILD, 0, NULL, + dns_fixedname_name(&fname), + NULL, NULL); + if (result == ISC_R_SUCCESS) { + return; + } + + /* + * Create the keydata. + */ + dns_keytable_attachkeynode(keytable, keynode, &dummy); + result = create_keydata(zone, db, ver, diff, keytable, &dummy, + keyname, changed); + if (result != ISC_R_SUCCESS && result != ISC_R_NOMORE) { + ((struct addifmissing_arg *)arg)->result = result; + } +} /* * Synchronize the set of initializing keys found in managed-keys {} * statements with the set of trust anchors found in the managed-keys.bind * zone. If a domain is no longer named in managed-keys, delete all keys - * from that domain from the key zone. If a domain is mentioned in in - * managed-keys but there are no references to it in the key zone, load - * the key zone with the initializing key(s) for that domain. + * from that domain from the key zone. If a domain is configured as an + * initial-key in dnssec-keys, but there are no references to it in the + * key zone, load the key zone with the initializing key(s) for that + * domain and schedule a key refresh. If a domain is configured as + * an initial-ds in dnssec-keys, fetch the DNSKEY RRset, load the key + * zone with the matching key, and schedule a key refresh. */ static isc_result_t sync_keyzone(dns_zone_t *zone, dns_db_t *db) { @@ -4338,9 +4398,9 @@ sync_keyzone(dns_zone_t *zone, dns_db_t *db) { /* * Walk the zone DB. If we find any keys whose names are no longer - * in managed-keys as initial-keys (or which are now configured as - * static keys, meaning they are permanent and not RFC5011-maintained), - * delete them from the zone. Otherwise call load_secroots(), which + * in dnssec-keys, or which have been changed from initial to static, + * (meaning they are permanent and not RFC5011-maintained), delete + * them from the zone. Otherwise call load_secroots(), which * loads keys into secroots as appropriate. */ dns_rriterator_init(&rrit, db, ver, 0); @@ -4363,12 +4423,8 @@ sync_keyzone(dns_zone_t *zone, dns_db_t *db) { } result = dns_keytable_find(sr, rrname, &keynode); - if ((result != ISC_R_SUCCESS && - result != DNS_R_PARTIALMATCH) || - dns_keynode_managed(keynode) == false) - { - CHECK(delete_keydata(db, ver, &diff, - rrname, rdataset)); + if (result != ISC_R_SUCCESS || !dns_keynode_managed(keynode)) { + CHECK(delete_keydata(db, ver, &diff, rrname, rdataset)); changed = true; } else { load_secroots(zone, rrname, rdataset); @@ -4381,8 +4437,10 @@ sync_keyzone(dns_zone_t *zone, dns_db_t *db) { dns_rriterator_destroy(&rrit); /* - * Now walk secroots to find any managed keys that aren't - * in the zone. If we find any, we add them to the zone. + * Walk secroots to find any initial keys that aren't in + * the zone. If we find any, add them to the zone directly. + * If any DS-style initial keys are found, refresh the key + * zone so that they'll be looked up. */ arg.db = db; arg.ver = ver; @@ -9741,7 +9799,7 @@ keyfetch_done(isc_task_t *task, isc_event_t *event) { dns_diff_t diff; bool alldone = false; bool commit = false; - dns_name_t *keyname; + dns_name_t *keyname = NULL; dns_rdata_t sigrr = DNS_RDATA_INIT; dns_rdata_t dnskeyrr = DNS_RDATA_INIT; dns_rdata_t keydatarr = DNS_RDATA_INIT; @@ -9752,11 +9810,14 @@ keyfetch_done(isc_task_t *task, isc_event_t *event) { char namebuf[DNS_NAME_FORMATSIZE]; unsigned char key_buf[4096]; isc_buffer_t keyb; - dst_key_t *dstkey; + dst_key_t *dstkey = NULL; isc_stdtime_t now; int pending = 0; bool secure = false, initial = false; bool free_needed; + dns_keynode_t *keynode = NULL; + dns_rdataset_t *dnskeys = NULL, *dnskeysigs = NULL; + dns_rdataset_t *keydataset = NULL, *dsset = NULL; UNUSED(task); INSIST(event != NULL && event->ev_type == DNS_EVENT_FETCHDONE); @@ -9766,6 +9827,9 @@ keyfetch_done(isc_task_t *task, isc_event_t *event) { zone = kfetch->zone; isc_mem_attach(zone->mctx, &mctx); keyname = dns_fixedname_name(&kfetch->name); + dnskeys = &kfetch->dnskeyset; + dnskeysigs = &kfetch->dnskeysigset; + keydataset = &kfetch->keydataset; devent = (dns_fetchevent_t *) event; eresult = devent->result; @@ -9807,9 +9871,7 @@ keyfetch_done(isc_task_t *task, isc_event_t *event) { namebuf, dns_result_totext(eresult)); /* Fetch failed */ - if (eresult != ISC_R_SUCCESS || - !dns_rdataset_isassociated(&kfetch->dnskeyset)) - { + if (eresult != ISC_R_SUCCESS || !dns_rdataset_isassociated(dnskeys)) { dnssec_log(zone, ISC_LOG_WARNING, "Unable to fetch DNSKEY set '%s': %s", namebuf, dns_result_totext(eresult)); @@ -9818,7 +9880,7 @@ keyfetch_done(isc_task_t *task, isc_event_t *event) { } /* No RRSIGs found */ - if (!dns_rdataset_isassociated(&kfetch->dnskeysigset)) { + if (!dns_rdataset_isassociated(dnskeysigs)) { dnssec_log(zone, ISC_LOG_WARNING, "No DNSKEY RRSIGs found for '%s': %s", namebuf, dns_result_totext(eresult)); @@ -9830,27 +9892,110 @@ keyfetch_done(isc_task_t *task, isc_event_t *event) { * Clear any cached trust level, as we need to run validation * over again; trusted keys might have changed. */ - kfetch->dnskeyset.trust = kfetch->dnskeysigset.trust = dns_trust_none; + dnskeys->trust = dnskeysigs->trust = dns_trust_none; + + /* Look up the trust anchor */ + result = dns_keytable_find(secroots, keyname, &keynode); + if (result != ISC_R_SUCCESS) { + goto anchors_done; + } /* - * Validate the dnskeyset against the current trusted keys. + * If the first keynode has a DS trust anchor, use that for + * verification. */ - for (result = dns_rdataset_first(&kfetch->dnskeysigset); - result == ISC_R_SUCCESS; - result = dns_rdataset_next(&kfetch->dnskeysigset)) - { - dns_keynode_t *keynode = NULL; + if ((dsset = dns_keynode_dsset(keynode)) != NULL) { + for (result = dns_rdataset_first(dnskeysigs); + result == ISC_R_SUCCESS; + result = dns_rdataset_next(dnskeysigs)) + { + isc_result_t tresult; + dns_rdata_t keyrdata = DNS_RDATA_INIT; + dns_rdata_reset(&sigrr); + dns_rdataset_current(dnskeysigs, &sigrr); + result = dns_rdata_tostruct(&sigrr, &sig, NULL); + RUNTIME_CHECK(result == ISC_R_SUCCESS); + + for (tresult = dns_rdataset_first(dsset); + tresult == ISC_R_SUCCESS; + tresult = dns_rdataset_next(dsset)) + { + dns_rdata_t dsrdata = DNS_RDATA_INIT; + dns_rdata_ds_t ds; + + dns_rdata_reset(&dsrdata); + dns_rdataset_current(dsset, &dsrdata); + tresult = dns_rdata_tostruct(&dsrdata, &ds, + NULL); + RUNTIME_CHECK(tresult == ISC_R_SUCCESS); + + if (ds.key_tag != sig.keyid || + ds.algorithm != sig.algorithm) + { + continue; + } + + result = dns_dnssec_matchdskey(keyname, + &dsrdata, + dnskeys, + &keyrdata); + if (result == ISC_R_SUCCESS) { + break; + } + } + + if (tresult == ISC_R_NOMORE) { + continue; + } + + result = dns_dnssec_keyfromrdata(keyname, &keyrdata, + mctx, &dstkey); + if (result != ISC_R_SUCCESS) { + continue; + } + + result = dns_dnssec_verify(keyname, dnskeys, + dstkey, false, 0, + mctx, &sigrr, NULL); + dst_key_free(&dstkey); + + dnssec_log(zone, ISC_LOG_DEBUG(3), + "Verifying DNSKEY set for zone " + "'%s' using DS %d/%d: %s", + namebuf, sig.keyid, sig.algorithm, + dns_result_totext(result)); + + if (result == ISC_R_SUCCESS) { + dnskeys->trust = dns_trust_secure; + dnskeysigs->trust = dns_trust_secure; + initial = dns_keynode_initial(keynode); + dns_keynode_trust(keynode); + secure = true; + break; + } + } + + dns_keytable_detachkeynode(secroots, &keynode); + goto anchors_done; + } + + /* + * Validate the DNSKEY set against using the key-style + * trust anchor(s). + */ + for (result = dns_rdataset_first(dnskeysigs); + result == ISC_R_SUCCESS; + result = dns_rdataset_next(dnskeysigs)) + { dns_rdata_reset(&sigrr); - dns_rdataset_current(&kfetch->dnskeysigset, &sigrr); + dns_rdataset_current(dnskeysigs, &sigrr); result = dns_rdata_tostruct(&sigrr, &sig, NULL); RUNTIME_CHECK(result == ISC_R_SUCCESS); - result = dns_keytable_find(secroots, keyname, &keynode); + result = ISC_R_SUCCESS; while (result == ISC_R_SUCCESS) { dns_keynode_t *nextnode = NULL; - dns_fixedname_t fixed; - dns_fixedname_init(&fixed); dstkey = dns_keynode_key(keynode); if (dstkey == NULL) { @@ -9861,26 +10006,21 @@ keyfetch_done(isc_task_t *task, isc_event_t *event) { if (dst_key_alg(dstkey) == sig.algorithm && dst_key_id(dstkey) == sig.keyid) { - result = dns_dnssec_verify(keyname, - &kfetch->dnskeyset, - dstkey, false, - 0, - zone->view->mctx, - &sigrr, - dns_fixedname_name( - &fixed)); + result = dns_dnssec_verify(keyname, dnskeys, + dstkey, false, 0, + mctx, &sigrr, NULL); dnssec_log(zone, ISC_LOG_DEBUG(3), - "Verifying DNSKEY set for zone " - "'%s' using key %d/%d: %s", - namebuf, sig.keyid, sig.algorithm, + "Verifying DNSKEY set " + "for zone '%s' " + "using key %d/%d: %s", + namebuf, sig.keyid, + sig.algorithm, dns_result_totext(result)); if (result == ISC_R_SUCCESS) { - kfetch->dnskeyset.trust = - dns_trust_secure; - kfetch->dnskeysigset.trust = - dns_trust_secure; + dnskeys->trust = dns_trust_secure; + dnskeysigs->trust = dns_trust_secure; secure = true; initial = dns_keynode_initial(keynode); dns_keynode_trust(keynode); @@ -9888,14 +10028,12 @@ keyfetch_done(isc_task_t *task, isc_event_t *event) { } } - result = dns_keytable_nextkeynode(secroots, - keynode, &nextnode); - dns_keytable_detachkeynode(secroots, &keynode); - keynode = nextnode; - } - - if (keynode != NULL) { - dns_keytable_detachkeynode(secroots, &keynode); + result = dns_keytable_nextkeynode(secroots, keynode, + &nextnode); + if (result == ISC_R_SUCCESS) { + dns_keytable_detachkeynode(secroots, &keynode); + keynode = nextnode; + } } if (secure) { @@ -9903,6 +10041,12 @@ keyfetch_done(isc_task_t *task, isc_event_t *event) { } } + anchors_done: + if (keynode != NULL) { + dns_keytable_detachkeynode(secroots, &keynode); + } + + /* * If we were not able to verify the answer using the current * trusted keys then all we can do is look at any revoked keys. @@ -9927,14 +10071,14 @@ keyfetch_done(isc_task_t *task, isc_event_t *event) { * updated */ initializing = true; - for (result = dns_rdataset_first(&kfetch->keydataset); + for (result = dns_rdataset_first(keydataset); result == ISC_R_SUCCESS; - result = dns_rdataset_next(&kfetch->keydataset)) + result = dns_rdataset_next(keydataset)) { dns_keytag_t keytag; dns_rdata_reset(&keydatarr); - dns_rdataset_current(&kfetch->keydataset, &keydatarr); + dns_rdataset_current(keydataset, &keydatarr); result = dns_rdata_tostruct(&keydatarr, &keydata, NULL); RUNTIME_CHECK(result == ISC_R_SUCCESS); @@ -9961,7 +10105,7 @@ keyfetch_done(isc_task_t *task, isc_event_t *event) { */ initializing = initializing && (keydata.addhd == 0); - if (! matchkey(&kfetch->dnskeyset, &keydatarr)) { + if (! matchkey(dnskeys, &keydatarr)) { bool deletekey = false; if (!secure) { @@ -10042,9 +10186,9 @@ keyfetch_done(isc_task_t *task, isc_event_t *event) { * - All keys not being removed have their refresh * timers updated */ - for (result = dns_rdataset_first(&kfetch->dnskeyset); + for (result = dns_rdataset_first(dnskeys); result == ISC_R_SUCCESS; - result = dns_rdataset_next(&kfetch->dnskeyset)) + result = dns_rdataset_next(dnskeys)) { bool revoked = false; bool newkey = false; @@ -10054,7 +10198,7 @@ keyfetch_done(isc_task_t *task, isc_event_t *event) { dns_keytag_t keytag; dns_rdata_reset(&dnskeyrr); - dns_rdataset_current(&kfetch->dnskeyset, &dnskeyrr); + dns_rdataset_current(dnskeys, &dnskeyrr); result = dns_rdata_tostruct(&dnskeyrr, &dnskey, NULL); RUNTIME_CHECK(result == ISC_R_SUCCESS); @@ -10079,9 +10223,9 @@ keyfetch_done(isc_task_t *task, isc_event_t *event) { revoked = ((dnskey.flags & DNS_KEYFLAG_REVOKE) != 0); - if (matchkey(&kfetch->keydataset, &dnskeyrr)) { + if (matchkey(keydataset, &dnskeyrr)) { dns_rdata_reset(&keydatarr); - dns_rdataset_current(&kfetch->keydataset, &keydatarr); + dns_rdataset_current(keydataset, &keydatarr); result = dns_rdata_tostruct(&keydatarr, &keydata, NULL); RUNTIME_CHECK(result == ISC_R_SUCCESS); @@ -10340,14 +10484,14 @@ keyfetch_done(isc_task_t *task, isc_event_t *event) { zone->irefs--; kfetch->zone = NULL; - if (dns_rdataset_isassociated(&kfetch->keydataset)) { - dns_rdataset_disassociate(&kfetch->keydataset); + if (dns_rdataset_isassociated(keydataset)) { + dns_rdataset_disassociate(keydataset); } - if (dns_rdataset_isassociated(&kfetch->dnskeyset)) { - dns_rdataset_disassociate(&kfetch->dnskeyset); + if (dns_rdataset_isassociated(dnskeys)) { + dns_rdataset_disassociate(dnskeys); } - if (dns_rdataset_isassociated(&kfetch->dnskeysigset)) { - dns_rdataset_disassociate(&kfetch->dnskeysigset); + if (dns_rdataset_isassociated(dnskeysigs)) { + dns_rdataset_disassociate(dnskeysigs); } dns_name_free(keyname, mctx); @@ -10367,8 +10511,8 @@ keyfetch_done(isc_task_t *task, isc_event_t *event) { } /* - * Refresh the data in the key zone. Initiate a fetch to get new DNSKEY - * records from the zone apex. + * Refresh the data in the key zone. Initiate a fetch to look up + * DNSKEY records at the trust anchor name. */ static void zone_refreshkeys(dns_zone_t *zone) { From 4d3ed3f4ea6a9437290a8f3f6820e80ed2736025 Mon Sep 17 00:00:00 2001 From: Evan Hunt Date: Tue, 17 Sep 2019 20:19:59 -0700 Subject: [PATCH 07/13] refactor create_keydata use empty placeholder KEYDATA records for all trust anchors, not just DS-style trust anchors. this revealed a pre-existing bug: keyfetch_done() skips keys without the SEP bit when populating the managed-keys zone. consequently, if a zone only has a single ZSK which is configured as trust anchor and no KSKs, then no KEYDATA record is ever written to the managed-keys zone when keys are refreshed. that was how the root server in the dnssec system test was configured. however, previously, the KEYDATA was created when the key was initialized; this prevented us from noticing the bug until now. configuring a ZSK as an RFC 5011 trust anchor is not forbidden by the spec, but it is highly unusual and not well defined. so for the time being, I have modified the system test to generate both a KSK and ZSK for the root zone, enabling the test to pass. we should consider adding code to detect this condition and allow keys without the SEP bit to be used as trust anchors if no key with the SEP bit is available, or at minimum, log a warning. --- bin/tests/system/dnssec/ns1/sign.sh | 11 +-- lib/dns/zone.c | 134 ++++++++-------------------- 2 files changed, 41 insertions(+), 104 deletions(-) diff --git a/bin/tests/system/dnssec/ns1/sign.sh b/bin/tests/system/dnssec/ns1/sign.sh index 385d3100a7..fe8a432eeb 100644 --- a/bin/tests/system/dnssec/ns1/sign.sh +++ b/bin/tests/system/dnssec/ns1/sign.sh @@ -30,14 +30,15 @@ cp "../ns2/dsset-in-addr.arpa$TP" . grep "$DEFAULT_ALGORITHM_NUMBER [12] " "../ns2/dsset-algroll$TP" > "dsset-algroll$TP" cp "../ns6/dsset-optout-tld$TP" . -keyname=$("$KEYGEN" -q -a "$DEFAULT_ALGORITHM" -b "$DEFAULT_BITS" -n zone "$zone") +ksk=$("$KEYGEN" -q -fk -a "$DEFAULT_ALGORITHM" -b "$DEFAULT_BITS" -n zone "$zone") +zsk=$("$KEYGEN" -q -a "$DEFAULT_ALGORITHM" -b "$DEFAULT_BITS" -n zone "$zone") -cat "$infile" "$keyname.key" > "$zonefile" +cat "$infile" "$ksk.key" "$zsk.key" > "$zonefile" "$SIGNER" -P -g -o "$zone" "$zonefile" > /dev/null 2>&1 # Configure the resolving server with a staitc key. -keyfile_to_static_keys "$keyname" > trusted.conf +keyfile_to_static_keys "$ksk" > trusted.conf cp trusted.conf ../ns2/trusted.conf cp trusted.conf ../ns3/trusted.conf cp trusted.conf ../ns4/trusted.conf @@ -46,11 +47,11 @@ cp trusted.conf ../ns7/trusted.conf cp trusted.conf ../ns9/trusted.conf # ...or with an initializing key. -keyfile_to_initial_keys "$keyname" > managed.conf +keyfile_to_initial_keys "$ksk" > managed.conf cp managed.conf ../ns4/managed.conf # # Save keyid for managed key id test. # -keyfile_to_key_id "$keyname" > managed.key.id +keyfile_to_key_id "$ksk" > managed.key.id diff --git a/lib/dns/zone.c b/lib/dns/zone.c index cad1b8282b..f70b460177 100644 --- a/lib/dns/zone.c +++ b/lib/dns/zone.c @@ -3828,121 +3828,59 @@ set_refreshkeytimer(dns_zone_t *zone, dns_rdata_keydata_t *key, } /* - * Convert key(s) linked from 'keynode' to KEYDATA and add it to the - * key zone. If `keynode` doesn't point to a key, then this is a - * DS-style trust anchor, so create an empty KEYDATA instead; it - * will be replaced with the correct key on refresh. + * If keynode references a key or a DS rdataset, and if the key + * zone does not contain a KEYDATA record for the corresponding name, + * then create an empty KEYDATA and push it into the zone as a placeholder, + * then schedule a key refresh immediately. This new KEYDATA record will be + * updated during the refresh. * * If the key zone is changed, set '*changed' to true. */ static isc_result_t create_keydata(dns_zone_t *zone, dns_db_t *db, dns_dbversion_t *ver, - dns_diff_t *diff, dns_keytable_t *keytable, - dns_keynode_t **keynodep, dns_name_t *keyname, - bool *changed) + dns_diff_t *diff, dns_keynode_t *keynode, + dns_name_t *keyname, bool *changed) { const char me[] = "create_keydata"; isc_result_t result = ISC_R_SUCCESS; - isc_buffer_t keyb, dstb; - unsigned char key_buf[4096], dst_buf[DST_KEY_MAXSIZE]; - dns_rdata_keydata_t keydata; - dns_rdata_dnskey_t dnskey; dns_rdata_t rdata = DNS_RDATA_INIT; - dns_keynode_t *keynode = NULL; + dns_rdata_keydata_t kd; + unsigned char rrdata[4096]; + isc_buffer_t rrdatabuf; isc_stdtime_t now; - isc_region_t r; - dst_key_t *key = NULL; - REQUIRE(keynodep != NULL); - keynode = *keynodep; + REQUIRE(keynode != NULL); ENTER; isc_stdtime_get(&now); - if (dns_keynode_dsset(keynode) != NULL) { - dns_rdata_keydata_t kd = { 0 }; - unsigned char rrdata[4096]; - isc_buffer_t rrdatabuf; - - /* - * We're creating a placeholder KEYDATA record - * to be updated when the key zone is refreshed - * later. - */ - kd.common.rdclass = zone->rdclass; - kd.common.rdtype = dns_rdatatype_keydata; - ISC_LINK_INIT(&kd.common, link); - - isc_buffer_init(&rrdatabuf, rrdata, sizeof(rrdata)); - - CHECK(dns_rdata_fromstruct(&rdata, zone->rdclass, - dns_rdatatype_keydata, - &kd, &rrdatabuf)); - /* Add rdata to zone. */ - CHECK(update_one_rr(db, ver, diff, DNS_DIFFOP_ADD, - keyname, 0, &rdata)); - *changed = true; - - /* Refresh new keys from the zone apex as soon as possible. */ - set_refreshkeytimer(zone, &keydata, now, true); - - result = ISC_R_NOMORE; - } else { - /* Proceed to the while loop below */ - result = ISC_R_SUCCESS; + /* + * If the keynode has neither a key nor a DS RRset, + * we shouldn't be here. + */ + if (dns_keynode_key(keynode) == NULL && + dns_keynode_dsset(keynode) == NULL) + { + return (ISC_R_FAILURE); } - /* Loop in case there's more than one key. */ - while (result == ISC_R_SUCCESS) { - dns_keynode_t *nextnode = NULL; + memset(&kd, 0, sizeof(kd)); + kd.common.rdclass = zone->rdclass; + kd.common.rdtype = dns_rdatatype_keydata; + ISC_LINK_INIT(&kd.common, link); - key = dns_keynode_key(keynode); - if (key == NULL) { - goto skip; - } + isc_buffer_init(&rrdatabuf, rrdata, sizeof(rrdata)); - isc_buffer_init(&dstb, dst_buf, sizeof(dst_buf)); - CHECK(dst_key_todns(key, &dstb)); - - /* Convert DST key to DNSKEY. */ - dns_rdata_reset(&rdata); - isc_buffer_usedregion(&dstb, &r); - dns_rdata_fromregion(&rdata, dst_key_class(key), - dns_rdatatype_dnskey, &r); - - /* DSTKEY to KEYDATA. */ - CHECK(dns_rdata_tostruct(&rdata, &dnskey, NULL)); - CHECK(dns_keydata_fromdnskey(&keydata, &dnskey, now, 0, 0, - NULL)); - - /* KEYDATA to rdata. */ - dns_rdata_reset(&rdata); - isc_buffer_init(&keyb, key_buf, sizeof(key_buf)); - CHECK(dns_rdata_fromstruct(&rdata, - zone->rdclass, dns_rdatatype_keydata, - &keydata, &keyb)); - - /* Add rdata to zone. */ - CHECK(update_one_rr(db, ver, diff, DNS_DIFFOP_ADD, - keyname, 0, &rdata)); - *changed = true; - - /* Refresh new keys from the zone apex as soon as possible. */ - set_refreshkeytimer(zone, &keydata, now, true); - - skip: - result = dns_keytable_nextkeynode(keytable, keynode, &nextnode); - if (result != ISC_R_NOTFOUND) { - dns_keytable_detachkeynode(keytable, &keynode); - keynode = nextnode; - } - } - - if (keynode != NULL) { - dns_keytable_detachkeynode(keytable, &keynode); - } - *keynodep = NULL; + CHECK(dns_rdata_fromstruct(&rdata, zone->rdclass, + dns_rdatatype_keydata, + &kd, &rrdatabuf)); + /* Add rdata to zone. */ + CHECK(update_one_rr(db, ver, diff, DNS_DIFFOP_ADD, + keyname, 0, &rdata)); + *changed = true; + /* Refresh new keys from the zone apex as soon as possible. */ + set_refreshkeytimer(zone, &kd, now, true); return (ISC_R_SUCCESS); failure: @@ -4312,9 +4250,9 @@ addifmissing(dns_keytable_t *keytable, dns_keynode_t *keynode, dns_zone_t *zone = ((struct addifmissing_arg *)arg)->zone; bool *changed = ((struct addifmissing_arg *)arg)->changed; isc_result_t result; - dns_keynode_t *dummy = NULL; dns_fixedname_t fname; + UNUSED(keytable); if (((struct addifmissing_arg *)arg)->result != ISC_R_SUCCESS) { return; @@ -4350,9 +4288,7 @@ addifmissing(dns_keytable_t *keytable, dns_keynode_t *keynode, /* * Create the keydata. */ - dns_keytable_attachkeynode(keytable, keynode, &dummy); - result = create_keydata(zone, db, ver, diff, keytable, &dummy, - keyname, changed); + result = create_keydata(zone, db, ver, diff, keynode, keyname, changed); if (result != ISC_R_SUCCESS && result != ISC_R_NOMORE) { ((struct addifmissing_arg *)arg)->result = result; } From 8aaee265489158caebd2a143aafd196776af7937 Mon Sep 17 00:00:00 2001 From: Evan Hunt Date: Wed, 18 Sep 2019 10:03:19 -0700 Subject: [PATCH 08/13] add validator support for static DS-style trust anchors --- lib/dns/validator.c | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/lib/dns/validator.c b/lib/dns/validator.c index 6f5306b515..545c8a2229 100644 --- a/lib/dns/validator.c +++ b/lib/dns/validator.c @@ -1883,6 +1883,8 @@ get_dsset(dns_validator_t *val, dns_name_t *tname, isc_result_t *resp) { * Attempts positive response validation of an RRset containing zone keys * (i.e. a DNSKEY rrset). * + * Caller must be holding the validator lock. + * * Returns: * \li ISC_R_SUCCESS Validation completed successfully * \li DNS_R_WAIT Validation has started but is waiting @@ -1894,14 +1896,31 @@ validate_dnskey(dns_validator_t *val) { isc_result_t result; dns_rdata_t dsrdata = DNS_RDATA_INIT; dns_rdata_t keyrdata = DNS_RDATA_INIT; + dns_keynode_t *keynode = NULL; dns_rdata_ds_t ds; bool supported_algorithm; char digest_types[256]; /* - * Caller must be holding the validator lock. + * If we don't already have a DS RRset, check to see if there's + * a DS style trust anchor configured for this key. */ + if (val->dsset == NULL) { + result = dns_keytable_find(val->keytable, + val->event->name, &keynode); + if (result == ISC_R_SUCCESS) { + val->dsset = dns_keynode_dsset(keynode); + if (val->dsset == NULL) { + dns_keytable_detachkeynode(val->keytable, + &keynode); + } + } + } + /* + * If that didn't work, see if there's a key-style trust anchor we + * can validate against. If not, look up the DS at the parent. + */ if (val->dsset == NULL) { isc_result_t tresult = ISC_R_SUCCESS; @@ -1945,6 +1964,7 @@ validate_dnskey(dns_validator_t *val) { INSIST(val->dsset != NULL); if (val->dsset->trust < dns_trust_secure) { + INSIST(keynode == NULL); return (markanswer(val, "validate_dnskey (2)", "insecure DS")); } @@ -2048,6 +2068,12 @@ validate_dnskey(dns_validator_t *val) { validator_log(val, ISC_LOG_DEBUG(3), "no RRSIG matching DS key"); } + + if (keynode != NULL) { + val->dsset = NULL; + dns_keytable_detachkeynode(val->keytable, &keynode); + } + if (result == ISC_R_SUCCESS) { marksecure(val->event); validator_log(val, ISC_LOG_DEBUG(3), "marking as secure (DS)"); From edafbf1c0f37149f61eeb465d84c9d998c680c37 Mon Sep 17 00:00:00 2001 From: Evan Hunt Date: Wed, 18 Sep 2019 13:04:07 -0700 Subject: [PATCH 09/13] fix root key sentinel code to send the correct key ID for DS trust anchors --- bin/named/server.c | 67 +++++++++++++++++++++++++++------------------- lib/ns/query.c | 37 +++++++++++++++++++++++-- 2 files changed, 75 insertions(+), 29 deletions(-) diff --git a/bin/named/server.c b/bin/named/server.c index 6e96359fa7..1e15368e3a 100644 --- a/bin/named/server.c +++ b/bin/named/server.c @@ -6824,35 +6824,55 @@ struct dotat_arg { * reported in the TAT query. */ static isc_result_t -get_tat_qname(dns_name_t *dst, const dns_name_t **origin, +get_tat_qname(dns_name_t *target, dns_name_t *keyname, dns_keytable_t *keytable, dns_keynode_t *keynode) { dns_keynode_t *firstnode = keynode; - dns_keynode_t *nextnode; + dns_keynode_t *nextnode = NULL; + dns_rdataset_t *dsset = NULL; unsigned int i, n = 0; uint16_t ids[12]; isc_textregion_t r; char label[64]; int m; - REQUIRE(origin != NULL && *origin == NULL); + if ((dsset = dns_keynode_dsset(keynode)) != NULL) { + isc_result_t result; - do { - dst_key_t *key = dns_keynode_key(keynode); - if (key != NULL) { - *origin = dst_key_name(key); + for (result = dns_rdataset_first(dsset); + result == ISC_R_SUCCESS; + result = dns_rdataset_next(dsset)) + { + dns_rdata_t rdata = DNS_RDATA_INIT; + dns_rdata_ds_t ds; + + dns_rdata_reset(&rdata); + dns_rdataset_current(dsset, &rdata); + result = dns_rdata_tostruct(&rdata, &ds, NULL); + RUNTIME_CHECK(result == ISC_R_SUCCESS); if (n < (sizeof(ids)/sizeof(ids[0]))) { - ids[n] = dst_key_id(key); + ids[n] = ds.key_tag; n++; } } - nextnode = NULL; - (void)dns_keytable_nextkeynode(keytable, keynode, &nextnode); - if (keynode != firstnode) { - dns_keytable_detachkeynode(keytable, &keynode); - } - keynode = nextnode; - } while (keynode != NULL); + } else { + do { + dst_key_t *key = dns_keynode_key(keynode); + if (key != NULL) { + if (n < (sizeof(ids)/sizeof(ids[0]))) { + ids[n] = dst_key_id(key); + n++; + } + } + nextnode = NULL; + (void)dns_keytable_nextkeynode(keytable, keynode, + &nextnode); + if (keynode != firstnode) { + dns_keytable_detachkeynode(keytable, &keynode); + } + keynode = nextnode; + } while (keynode != NULL); + } if (n == 0) { return (DNS_R_EMPTYNAME); @@ -6882,16 +6902,15 @@ get_tat_qname(dns_name_t *dst, const dns_name_t **origin, isc_textregion_consume(&r, m); } - return (dns_name_fromstring2(dst, label, *origin, 0, NULL)); + return (dns_name_fromstring2(target, label, keyname, 0, NULL)); } static void dotat(dns_keytable_t *keytable, dns_keynode_t *keynode, - dns_name_t *name, void *arg) + dns_name_t *keyname, void *arg) { struct dotat_arg *dotat_arg = arg; char namebuf[DNS_NAME_FORMATSIZE]; - const dns_name_t *origin = NULL; dns_fixedname_t fixed, fdomain; dns_name_t *tatname, *domain; dns_rdataset_t nameservers; @@ -6904,13 +6923,11 @@ dotat(dns_keytable_t *keytable, dns_keynode_t *keynode, REQUIRE(keynode != NULL); REQUIRE(dotat_arg != NULL); - UNUSED(name); - view = dotat_arg->view; task = dotat_arg->task; tatname = dns_fixedname_initname(&fixed); - result = get_tat_qname(tatname, &origin, keytable, keynode); + result = get_tat_qname(tatname, keyname, keytable, keynode); if (result != ISC_R_SUCCESS) { return; } @@ -6948,17 +6965,13 @@ dotat(dns_keytable_t *keytable, dns_keynode_t *keynode, * order to eventually find the destination host to send the TAT query * to. * - * 'origin' holds the domain name at 'keynode', i.e. the domain name - * for which the trust anchors to be reported by this TAT query are - * defined. - * * After the dns_view_findzonecut() call, 'domain' will hold the - * deepest zone cut we can find for 'origin' while 'nameservers' will + * deepest zone cut we can find for 'keyname' while 'nameservers' will * hold the NS RRset at that zone cut. */ domain = dns_fixedname_initname(&fdomain); dns_rdataset_init(&nameservers); - result = dns_view_findzonecut(view, origin, domain, NULL, 0, 0, + result = dns_view_findzonecut(view, keyname, domain, NULL, 0, 0, true, true, &nameservers, NULL); if (result == ISC_R_SUCCESS) { result = dns_resolver_createfetch(view->resolver, tatname, diff --git a/lib/ns/query.c b/lib/ns/query.c index 7adf04d96f..7bf8df4994 100644 --- a/lib/ns/query.c +++ b/lib/ns/query.c @@ -6596,6 +6596,8 @@ static bool has_ta(query_ctx_t *qctx) { dns_keytable_t *keytable = NULL; dns_keynode_t *keynode = NULL; + dns_rdataset_t *dsset = NULL; + dns_keytag_t sentinel = qctx->client->query.root_key_sentinel_keyid; isc_result_t result; result = dns_view_getsecroots(qctx->view, &keytable); @@ -6604,10 +6606,38 @@ has_ta(query_ctx_t *qctx) { } result = dns_keytable_find(keytable, dns_rootname, &keynode); + if (result != ISC_R_SUCCESS) { + if (keynode != NULL) { + dns_keytable_detachkeynode(keytable, &keynode); + } + dns_keytable_detach(&keytable); + return (false); + } + + if ((dsset = dns_keynode_dsset(keynode)) != NULL) { + for (result = dns_rdataset_first(dsset); + result == ISC_R_SUCCESS; + result = dns_rdataset_next(dsset)) + { + dns_rdata_t rdata = DNS_RDATA_INIT; + dns_rdata_ds_t ds; + + dns_rdata_reset(&rdata); + dns_rdataset_current(dsset, &rdata); + result = dns_rdata_tostruct(&rdata, &ds, NULL); + RUNTIME_CHECK(result == ISC_R_SUCCESS); + if (ds.key_tag == sentinel) { + dns_keytable_detachkeynode(keytable, &keynode); + dns_keytable_detach(&keytable); + return (true); + } + } + } + while (result == ISC_R_SUCCESS) { dns_keynode_t *nextnode = NULL; dns_keytag_t keyid = dst_key_id(dns_keynode_key(keynode)); - if (keyid == qctx->client->query.root_key_sentinel_keyid) { + if (keyid == sentinel) { dns_keytable_detachkeynode(keytable, &keynode); dns_keytable_detach(&keytable); return (true); @@ -6616,8 +6646,11 @@ has_ta(query_ctx_t *qctx) { dns_keytable_detachkeynode(keytable, &keynode); keynode = nextnode; } - dns_keytable_detach(&keytable); + if (keynode != NULL) { + dns_keytable_detachkeynode(keytable, &keynode); + } + dns_keytable_detach(&keytable); return (false); } From 342cc9b168a8c260e51916b0e6b33a7fb4411e09 Mon Sep 17 00:00:00 2001 From: Evan Hunt Date: Wed, 18 Sep 2019 19:45:20 -0700 Subject: [PATCH 10/13] add support for DS trust anchors in delv --- bin/delv/delv.c | 127 ++++++++++++++++++++++++++++------- lib/dns/client.c | 65 +++++++++++++----- lib/dns/include/dns/client.h | 8 ++- lib/irs/context.c | 1 + lib/samples/resolve.c | 1 + 5 files changed, 158 insertions(+), 44 deletions(-) diff --git a/bin/delv/delv.c b/bin/delv/delv.c index c02dfb9905..fb14aa05ba 100644 --- a/bin/delv/delv.c +++ b/bin/delv/delv.c @@ -33,8 +33,10 @@ #include #include #include +#include #include #include +#include #include #ifdef WIN32 #include @@ -608,11 +610,12 @@ convert_name(dns_fixedname_t *fn, dns_name_t **name, const char *text) { static isc_result_t key_fromconfig(const cfg_obj_t *key, dns_client_t *client) { - dns_rdata_dnskey_t keystruct; + dns_rdata_dnskey_t dnskey; + dns_rdata_ds_t ds; uint32_t n1, n2, n3; - const char *keystr, *keynamestr; - unsigned char keydata[4096]; - isc_buffer_t keydatabuf; + const char *datastr = NULL, *keynamestr = NULL, *atstr = NULL; + unsigned char data[4096]; + isc_buffer_t databuf; unsigned char rrdata[4096]; isc_buffer_t rrdatabuf; isc_region_t r; @@ -620,6 +623,13 @@ key_fromconfig(const cfg_obj_t *key, dns_client_t *client) { dns_name_t *keyname; isc_result_t result; bool match_root = false; + enum { + INITIAL_KEY, + STATIC_KEY, + INITIAL_DS, + STATIC_DS, + TRUSTED + } anchortype; keynamestr = cfg_obj_asstring(cfg_tuple_get(key, "name")); CHECK(convert_name(&fkeyname, &keyname, keynamestr)); @@ -651,14 +661,26 @@ key_fromconfig(const cfg_obj_t *key, dns_client_t *client) { /* if DNSKEY, algorithm; if DS, digest type */ n3 = cfg_obj_asuint32(cfg_tuple_get(key, "n3")); - keystruct.common.rdclass = dns_rdataclass_in; - keystruct.common.rdtype = dns_rdatatype_dnskey; - /* - * The key data in keystruct is not dynamically allocated. - */ - keystruct.mctx = NULL; + /* What type of trust anchor is this? */ + atstr = cfg_obj_asstring(cfg_tuple_get(key, "anchortype")); + if (strcasecmp(atstr, "static-key") == 0) { + anchortype = STATIC_KEY; + } else if (strcasecmp(atstr, "static-ds") == 0) { + anchortype = STATIC_DS; + } else if (strcasecmp(atstr, "initial-key") == 0) { + anchortype = INITIAL_KEY; + } else if (strcasecmp(atstr, "initial-ds") == 0) { + anchortype = INITIAL_DS; + } else { + delv_log(ISC_LOG_ERROR, + "key '%s': invalid initialization method '%s'", + keynamestr, atstr); + result = ISC_R_FAILURE; + goto cleanup; + } - ISC_LINK_INIT(&keystruct.common, link); + isc_buffer_init(&databuf, data, sizeof(data)); + isc_buffer_init(&rrdatabuf, rrdata, sizeof(rrdata)); if (n1 > 0xffff) { CHECK(ISC_R_RANGE); @@ -670,25 +692,78 @@ key_fromconfig(const cfg_obj_t *key, dns_client_t *client) { CHECK(ISC_R_RANGE); } - keystruct.flags = (uint16_t)n1; - keystruct.protocol = (uint8_t)n2; - keystruct.algorithm = (uint8_t)n3; + switch (anchortype) { + case STATIC_KEY: + case INITIAL_KEY: + case TRUSTED: + dnskey.common.rdclass = dns_rdataclass_in; + dnskey.common.rdtype = dns_rdatatype_dnskey; + dnskey.mctx = NULL; - isc_buffer_init(&keydatabuf, keydata, sizeof(keydata)); - isc_buffer_init(&rrdatabuf, rrdata, sizeof(rrdata)); + ISC_LINK_INIT(&dnskey.common, link); - keystr = cfg_obj_asstring(cfg_tuple_get(key, "data")); - CHECK(isc_base64_decodestring(keystr, &keydatabuf)); - isc_buffer_usedregion(&keydatabuf, &r); - keystruct.datalen = r.length; - keystruct.data = r.base; + dnskey.flags = (uint16_t)n1; + dnskey.protocol = (uint8_t)n2; + dnskey.algorithm = (uint8_t)n3; - CHECK(dns_rdata_fromstruct(NULL, keystruct.common.rdclass, - keystruct.common.rdtype, - &keystruct, &rrdatabuf)); + datastr = cfg_obj_asstring(cfg_tuple_get(key, "data")); + CHECK(isc_base64_decodestring(datastr, &databuf)); + isc_buffer_usedregion(&databuf, &r); + dnskey.datalen = r.length; + dnskey.data = r.base; + + CHECK(dns_rdata_fromstruct(NULL, dnskey.common.rdclass, + dnskey.common.rdtype, + &dnskey, &rrdatabuf)); + CHECK(dns_client_addtrustedkey(client, dns_rdataclass_in, + dns_rdatatype_dnskey, + keyname, &rrdatabuf)); + break; + case INITIAL_DS: + case STATIC_DS: + ds.common.rdclass = dns_rdataclass_in; + ds.common.rdtype = dns_rdatatype_ds; + ds.mctx = NULL; + + ISC_LINK_INIT(&ds.common, link); + + ds.key_tag = (uint16_t)n1; + ds.algorithm = (uint8_t)n2; + ds.digest_type = (uint8_t)n3; + + datastr = cfg_obj_asstring(cfg_tuple_get(key, "data")); + CHECK(isc_hex_decodestring(datastr, &databuf)); + isc_buffer_usedregion(&databuf, &r); + + switch (ds.digest_type) { + case DNS_DSDIGEST_SHA1: + if (r.length != ISC_SHA1_DIGESTLENGTH) { + CHECK(ISC_R_UNEXPECTEDEND); + } + break; + case DNS_DSDIGEST_SHA256: + if (r.length != ISC_SHA256_DIGESTLENGTH) { + CHECK(ISC_R_UNEXPECTEDEND); + } + break; + case DNS_DSDIGEST_SHA384: + if (r.length != ISC_SHA384_DIGESTLENGTH) { + CHECK(ISC_R_UNEXPECTEDEND); + } + break; + } + + ds.length = r.length; + ds.digest = r.base; + + CHECK(dns_rdata_fromstruct(NULL, ds.common.rdclass, + ds.common.rdtype, + &ds, &rrdatabuf)); + CHECK(dns_client_addtrustedkey(client, dns_rdataclass_in, + dns_rdatatype_ds, + keyname, &rrdatabuf)); + }; - CHECK(dns_client_addtrustedkey(client, dns_rdataclass_in, - keyname, &rrdatabuf)); num_keys++; cleanup: diff --git a/lib/dns/client.c b/lib/dns/client.c index 971c4e3af2..58da8506e4 100644 --- a/lib/dns/client.c +++ b/lib/dns/client.c @@ -69,6 +69,13 @@ #define RESOLVER_NTASKS 31 #endif /* TUNE_LARGE */ +#define CHECK(r) \ + do { \ + result = (r); \ + if (result != ISC_R_SUCCESS) \ + goto cleanup; \ + } while (0) + /*% * DNS client object */ @@ -1482,12 +1489,19 @@ dns_client_destroyrestrans(dns_clientrestrans_t **transp) { isc_result_t dns_client_addtrustedkey(dns_client_t *client, dns_rdataclass_t rdclass, - const dns_name_t *keyname, isc_buffer_t *keydatabuf) + dns_rdatatype_t rdtype, const dns_name_t *keyname, + isc_buffer_t *databuf) { isc_result_t result; dns_view_t *view = NULL; dst_key_t *dstkey = NULL; dns_keytable_t *secroots = NULL; + dns_name_t *name = NULL; + char dsbuf[DNS_DS_BUFFERSIZE]; + dns_rdata_ds_t ds; + dns_decompress_t dctx; + dns_rdata_t rdata; + isc_buffer_t b; REQUIRE(DNS_CLIENT_VALID(client)); @@ -1495,28 +1509,49 @@ dns_client_addtrustedkey(dns_client_t *client, dns_rdataclass_t rdclass, result = dns_viewlist_find(&client->viewlist, DNS_CLIENTVIEW_NAME, rdclass, &view); UNLOCK(&client->lock); - if (result != ISC_R_SUCCESS) - goto cleanup; + CHECK(result); - result = dns_view_getsecroots(view, &secroots); - if (result != ISC_R_SUCCESS) - goto cleanup; + CHECK(dns_view_getsecroots(view, &secroots)); - result = dst_key_fromdns(keyname, rdclass, keydatabuf, client->mctx, - &dstkey); - if (result != ISC_R_SUCCESS) - goto cleanup; + DE_CONST(keyname, name); - result = dns_keytable_add(secroots, false, false, - dst_key_name(dstkey), &dstkey, NULL); + switch (rdtype) { + case dns_rdatatype_dnskey: + result = dst_key_fromdns(keyname, rdclass, databuf, + client->mctx, &dstkey); + if (result != ISC_R_SUCCESS) { + goto cleanup; + } + CHECK(dns_keytable_add(secroots, false, false, + name, &dstkey, NULL)); + break; + case dns_rdatatype_ds: + isc_buffer_init(&b, dsbuf, sizeof(dsbuf)); + dns_decompress_init(&dctx, -1, DNS_DECOMPRESS_NONE); + dns_rdata_init(&rdata); + isc_buffer_setactive(databuf, isc_buffer_usedlength(databuf)); + CHECK(dns_rdata_fromwire(&rdata, rdclass, rdtype, + databuf, &dctx, 0, &b)); + dns_decompress_invalidate(&dctx); + CHECK(dns_rdata_tostruct(&rdata, &ds, NULL)); + CHECK(dns_keytable_add(secroots, false, false, + name, NULL, &ds)); + break; + + default: + result = ISC_R_NOTIMPLEMENTED; + } cleanup: - if (dstkey != NULL) + if (dstkey != NULL) { dst_key_free(&dstkey); - if (view != NULL) + } + if (view != NULL) { dns_view_detach(&view); - if (secroots != NULL) + } + if (secroots != NULL) { dns_keytable_detach(&secroots); + } return (result); } diff --git a/lib/dns/include/dns/client.h b/lib/dns/include/dns/client.h index d661e2deba..1740004892 100644 --- a/lib/dns/include/dns/client.h +++ b/lib/dns/include/dns/client.h @@ -385,11 +385,13 @@ dns_client_freeresanswer(dns_client_t *client, dns_namelist_t *namelist); isc_result_t dns_client_addtrustedkey(dns_client_t *client, dns_rdataclass_t rdclass, - const dns_name_t *keyname, isc_buffer_t *keydatabuf); + dns_rdatatype_t rdtype, const dns_name_t *keyname, + isc_buffer_t *keydatabuf); /*%< * Add a DNSSEC trusted key for the 'rdclass' class. A view for the 'rdclass' - * class must be created beforehand. 'keyname' is the DNS name of the key, - * and 'keydatabuf' stores the resource data of the key. + * class must be created beforehand. 'rdtype' is the type of the RR data + * for the key, either DNSKEY or DS. 'keyname' is the DNS name of the key, + * and 'keydatabuf' stores the RR data. * * Requires: * diff --git a/lib/irs/context.c b/lib/irs/context.c index a359c1ee89..d1b71c4204 100644 --- a/lib/irs/context.c +++ b/lib/irs/context.c @@ -255,6 +255,7 @@ irs_context_create(irs_context_t **contextp) { trustedkey != NULL; trustedkey = ISC_LIST_NEXT(trustedkey, link)) { result = dns_client_addtrustedkey(client, dns_rdataclass_in, + dns_rdatatype_dnskey, trustedkey->keyname, trustedkey->keydatabuf); if (result != ISC_R_SUCCESS) diff --git a/lib/samples/resolve.c b/lib/samples/resolve.c index 1715c5c89e..587d159f44 100644 --- a/lib/samples/resolve.c +++ b/lib/samples/resolve.c @@ -162,6 +162,7 @@ set_key(dns_client_t *client, char *keynamestr, char *keystr, exit(1); } result = dns_client_addtrustedkey(client, dns_rdataclass_in, + dns_rdatatype_dnskey, keyname, &rrdatabuf); if (result != ISC_R_SUCCESS) { fprintf(stderr, "failed to add key for %s\n", From 54a682ea502b81f6c4f77fbebf0c9f8b4945bf54 Mon Sep 17 00:00:00 2001 From: Evan Hunt Date: Wed, 18 Sep 2019 19:41:40 -0700 Subject: [PATCH 11/13] use DS style trust anchors in all system tests this adds functions in conf.sh.common to create DS-style trust anchor files. those functions are then used to create nearly all of the trust anchors in the system tests. there are a few exceptions: - some tests in dnssec and mkeys rely on detection of unsupported algorithms, which only works with key-style trust anchors, so those are used for those tests in particular. - the mirror test had a problem with the use of a CSK without a SEP bit, which still needs addressing in the future, some of these tests should be changed back to using traditional trust anchors, so that both types will be exercised going forward. --- bin/tests/system/autosign/ns1/keygen.sh | 4 +- bin/tests/system/autosign/ns2/keygen.sh | 2 +- bin/tests/system/conf.sh.common | 50 +++++++++++++++++--- bin/tests/system/dnssec/ns1/sign.sh | 4 +- bin/tests/system/dnssec/ns5/sign.sh | 4 +- bin/tests/system/dsdigest/ns1/sign.sh | 2 +- bin/tests/system/ecdsa/ns1/sign.sh | 2 +- bin/tests/system/eddsa/ns1/sign.sh | 2 +- bin/tests/system/filter-aaaa/ns1/sign.sh | 2 +- bin/tests/system/inline/ns1/sign.sh | 2 +- bin/tests/system/legacy/ns7/sign.sh | 2 +- bin/tests/system/mkeys/ns1/sign.sh | 4 +- bin/tests/system/mkeys/ns6/setup.sh | 2 + bin/tests/system/mkeys/tests.sh | 2 +- bin/tests/system/pending/ns1/sign.sh | 2 +- bin/tests/system/resolver/ns6/keygen.sh | 2 +- bin/tests/system/rootkeysentinel/ns1/sign.sh | 2 +- bin/tests/system/rsabigexponent/ns1/sign.sh | 2 +- bin/tests/system/sfcache/ns1/sign.sh | 4 +- bin/tests/system/sfcache/ns5/sign.sh | 2 +- bin/tests/system/staticstub/ns3/sign.sh | 4 +- bin/tests/system/synthfromdnssec/ns1/sign.sh | 2 +- bin/tests/system/wildcard/ns1/sign.sh | 6 +-- 23 files changed, 74 insertions(+), 36 deletions(-) diff --git a/bin/tests/system/autosign/ns1/keygen.sh b/bin/tests/system/autosign/ns1/keygen.sh index 6ba8f95df9..47d3eefe10 100644 --- a/bin/tests/system/autosign/ns1/keygen.sh +++ b/bin/tests/system/autosign/ns1/keygen.sh @@ -33,12 +33,12 @@ rm $zsknopriv.private ksksby=`$KEYGEN -3 -a RSASHA1 -q -P now -A now+15s -fk $zone` kskrev=`$KEYGEN -3 -a RSASHA1 -q -R now+15s -fk $zone` -keyfile_to_static_keys $ksksby > trusted.conf +keyfile_to_static_ds $ksksby > trusted.conf cp trusted.conf ../ns2/trusted.conf cp trusted.conf ../ns3/trusted.conf cp trusted.conf ../ns4/trusted.conf -keyfile_to_static_keys $kskrev > trusted.conf +keyfile_to_static_ds $kskrev > trusted.conf cp trusted.conf ../ns5/trusted.conf echo $zskact > ../active.key diff --git a/bin/tests/system/autosign/ns2/keygen.sh b/bin/tests/system/autosign/ns2/keygen.sh index de557d76e2..9d40b7fa34 100644 --- a/bin/tests/system/autosign/ns2/keygen.sh +++ b/bin/tests/system/autosign/ns2/keygen.sh @@ -37,7 +37,7 @@ zonefile="${zone}.db" infile="${zonefile}.in" ksk=`$KEYGEN -a RSASHA1 -3 -q -fk $zone` $KEYGEN -a RSASHA1 -3 -q $zone > /dev/null -keyfile_to_static_keys $ksk > private.conf +keyfile_to_static_ds $ksk > private.conf cp private.conf ../ns4/private.conf $SIGNER -S -3 beef -A -o $zone -f $zonefile $infile > /dev/null diff --git a/bin/tests/system/conf.sh.common b/bin/tests/system/conf.sh.common index 51c0f399f5..f2bafa76ce 100644 --- a/bin/tests/system/conf.sh.common +++ b/bin/tests/system/conf.sh.common @@ -221,9 +221,9 @@ assert_int_equal() { } # keyfile_to_keys_section: helper function for keyfile_to_*_keys() which -# converts keyfile data into a configuration section using the supplied -# parameters -keyfile_to_keys_section() { +# converts keyfile data into a key-style trust anchor configuration +# section using the supplied parameters +keyfile_to_keys() { section_name=$1 key_prefix=$2 shift @@ -241,18 +241,54 @@ keyfile_to_keys_section() { echo "};" } +# keyfile_to_dskeys_section: helper function for keyfile_to_*_dskeys() +# converts keyfile data into a DS-style trust anchor configuration +# section using the supplied parameters +keyfile_to_dskeys() { + section_name=$1 + key_prefix=$2 + shift + shift + echo "$section_name {" + for keyname in $*; do + $DSFROMKEY $keyname.key | \ + awk '!/^; /{ + printf "\t\""$1"\" " + printf "'"$key_prefix "'" + printf $4 " " $5 " " $6 " \"" + for (i=7; i<=NF; i++) printf $i + printf "\";\n" + }' + done + echo "};" +} + # keyfile_to_static_keys: convert key data contained in the keyfile(s) -# provided to a *static* "dnssec-keys" section suitable for including in a +# provided to a *static-key* "dnssec-keys" section suitable for including in a # resolver's configuration file keyfile_to_static_keys() { - keyfile_to_keys_section "dnssec-keys" "static-key" $* + keyfile_to_keys "dnssec-keys" "static-key" $* } # keyfile_to_initial_keys: convert key data contained in the keyfile(s) -# provided to an *initialzing* "dnssec-keys" section suitable for including +# provided to an *initial-key* "dnssec-keys" section suitable for including # in a resolver's configuration file keyfile_to_initial_keys() { - keyfile_to_keys_section "dnssec-keys" "initial-key" $* + keyfile_to_keys "dnssec-keys" "initial-key" $* +} + +# keyfile_to_static_ds_keys: convert key data contained in the keyfile(s) +# provided to a *static-ds* "dnssec-keys" section suitable for including in a +# resolver's configuration file +keyfile_to_static_ds() { + keyfile_to_dskeys "dnssec-keys" "static-ds" $* +} + +# keyfile_to_initial_ds_keys: convert key data contained in the keyfile(s) +# provided to an *initial-ds* "dnssec-keys" section suitable for including +# in a resolver's configuration file +keyfile_to_initial_ds() { + keyfile_to_dskeys "dnssec-keys" "initial-ds" $* } # keyfile_to_key_id: convert a key file name to a key ID diff --git a/bin/tests/system/dnssec/ns1/sign.sh b/bin/tests/system/dnssec/ns1/sign.sh index fe8a432eeb..66254b7cfe 100644 --- a/bin/tests/system/dnssec/ns1/sign.sh +++ b/bin/tests/system/dnssec/ns1/sign.sh @@ -38,7 +38,7 @@ cat "$infile" "$ksk.key" "$zsk.key" > "$zonefile" "$SIGNER" -P -g -o "$zone" "$zonefile" > /dev/null 2>&1 # Configure the resolving server with a staitc key. -keyfile_to_static_keys "$ksk" > trusted.conf +keyfile_to_static_ds "$ksk" > trusted.conf cp trusted.conf ../ns2/trusted.conf cp trusted.conf ../ns3/trusted.conf cp trusted.conf ../ns4/trusted.conf @@ -47,7 +47,7 @@ cp trusted.conf ../ns7/trusted.conf cp trusted.conf ../ns9/trusted.conf # ...or with an initializing key. -keyfile_to_initial_keys "$ksk" > managed.conf +keyfile_to_initial_ds "$ksk" > managed.conf cp managed.conf ../ns4/managed.conf # diff --git a/bin/tests/system/dnssec/ns5/sign.sh b/bin/tests/system/dnssec/ns5/sign.sh index 1c226d5f95..54ae148e0c 100644 --- a/bin/tests/system/dnssec/ns5/sign.sh +++ b/bin/tests/system/dnssec/ns5/sign.sh @@ -23,7 +23,7 @@ zonefile=root.db.signed keyname=$("$KEYGEN" -q -a "$DEFAULT_ALGORITHM" -b "$DEFAULT_BITS" -f KSK "$zone") # copy the KSK out first, then revoke it -keyfile_to_initial_keys "$keyname" > revoked.conf +keyfile_to_initial_ds "$keyname" > revoked.conf "$SETTIME" -R now "${keyname}.key" > /dev/null @@ -34,4 +34,4 @@ keyfile_to_initial_keys "$keyname" > revoked.conf keyname=$("$KEYGEN" -q -a "$DEFAULT_ALGORITHM" -b "$DEFAULT_BITS" -n zone ".") -keyfile_to_static_keys "$keyname" > trusted.conf +keyfile_to_static_ds "$keyname" > trusted.conf diff --git a/bin/tests/system/dsdigest/ns1/sign.sh b/bin/tests/system/dsdigest/ns1/sign.sh index dc893b1631..9f0ef6b036 100644 --- a/bin/tests/system/dsdigest/ns1/sign.sh +++ b/bin/tests/system/dsdigest/ns1/sign.sh @@ -29,7 +29,7 @@ cat $infile $key1.key $key2.key > $zonefile $SIGNER -P -g -o $zone $zonefile > /dev/null # Configure the resolving server with a static key. -keyfile_to_static_keys $key2 > trusted.conf +keyfile_to_static_ds $key2 > trusted.conf cp trusted.conf ../ns2/trusted.conf cp trusted.conf ../ns3/trusted.conf cp trusted.conf ../ns4/trusted.conf diff --git a/bin/tests/system/ecdsa/ns1/sign.sh b/bin/tests/system/ecdsa/ns1/sign.sh index 518e01d8d1..673aac8ac0 100644 --- a/bin/tests/system/ecdsa/ns1/sign.sh +++ b/bin/tests/system/ecdsa/ns1/sign.sh @@ -25,5 +25,5 @@ cat $infile $key1.key $key2.key > $zonefile $SIGNER -P -g -o $zone $zonefile > /dev/null 2> signer.err || cat signer.err # Configure the resolving server with a static key. -keyfile_to_static_keys $key1 > trusted.conf +keyfile_to_static_ds $key1 > trusted.conf cp trusted.conf ../ns2/trusted.conf diff --git a/bin/tests/system/eddsa/ns1/sign.sh b/bin/tests/system/eddsa/ns1/sign.sh index 6806db8c5c..761ee13428 100644 --- a/bin/tests/system/eddsa/ns1/sign.sh +++ b/bin/tests/system/eddsa/ns1/sign.sh @@ -26,7 +26,7 @@ cat $infile $key1.key $key2.key > $zonefile $SIGNER -P -g -o $zone $zonefile > /dev/null 2> signer.err || cat signer.err # Configure the resolving server with a static key. -keyfile_to_static_keys $key1 > trusted.conf +keyfile_to_static_ds $key1 > trusted.conf cp trusted.conf ../ns2/trusted.conf cd ../ns2 && $SHELL sign.sh diff --git a/bin/tests/system/filter-aaaa/ns1/sign.sh b/bin/tests/system/filter-aaaa/ns1/sign.sh index 44e27ed488..3223ffd4fa 100755 --- a/bin/tests/system/filter-aaaa/ns1/sign.sh +++ b/bin/tests/system/filter-aaaa/ns1/sign.sh @@ -24,7 +24,7 @@ $KEYGEN -f KSK -a $DEFAULT_ALGORITHM $zone 2>&1 > keygen.out | cat_i keyname=`cat keygen.out` rm -f keygen.out -keyfile_to_static_keys $keyname > trusted.conf +keyfile_to_static_ds $keyname > trusted.conf cp trusted.conf ../ns2/trusted.conf cp trusted.conf ../ns3/trusted.conf cp trusted.conf ../ns5/trusted.conf diff --git a/bin/tests/system/inline/ns1/sign.sh b/bin/tests/system/inline/ns1/sign.sh index c14a83837e..72fc52eb4b 100644 --- a/bin/tests/system/inline/ns1/sign.sh +++ b/bin/tests/system/inline/ns1/sign.sh @@ -20,5 +20,5 @@ keyname=`$KEYGEN -q -a RSASHA1 -b 1024 -n zone -f KSK $zone` $SIGNER -S -x -T 1200 -o ${zone} root.db > signer.out [ $? = 0 ] || cat signer.out -keyfile_to_static_keys $keyname > trusted.conf +keyfile_to_static_ds $keyname > trusted.conf cp trusted.conf ../ns6/trusted.conf diff --git a/bin/tests/system/legacy/ns7/sign.sh b/bin/tests/system/legacy/ns7/sign.sh index 21ab3d1e5a..51719c22c1 100755 --- a/bin/tests/system/legacy/ns7/sign.sh +++ b/bin/tests/system/legacy/ns7/sign.sh @@ -28,5 +28,5 @@ cat $infile $keyname1.key $keyname2.key >$zonefile $SIGNER -g -o $zone -f $outfile -e +30y $zonefile > /dev/null 2> signer.err || cat signer.err -keyfile_to_static_keys $keyname2 > trusted.conf +keyfile_to_static_ds $keyname2 > trusted.conf cp trusted.conf ../ns1 diff --git a/bin/tests/system/mkeys/ns1/sign.sh b/bin/tests/system/mkeys/ns1/sign.sh index 135080a467..483ed4ed55 100644 --- a/bin/tests/system/mkeys/ns1/sign.sh +++ b/bin/tests/system/mkeys/ns1/sign.sh @@ -21,13 +21,13 @@ zskkeyname=`$KEYGEN -a rsasha256 -q $zone` $SIGNER -Sg -o $zone $zonefile > /dev/null 2>/dev/null # Configure the resolving server with an initializing key. -keyfile_to_initial_keys $keyname > managed.conf +keyfile_to_initial_ds $keyname > managed.conf cp managed.conf ../ns2/managed.conf cp managed.conf ../ns4/managed.conf cp managed.conf ../ns5/managed.conf # Configure a static key to be used by delv. -keyfile_to_static_keys $keyname > trusted.conf +keyfile_to_static_ds $keyname > trusted.conf # Prepare an unsupported algorithm key. unsupportedkey=Kunknown.+255+00000 diff --git a/bin/tests/system/mkeys/ns6/setup.sh b/bin/tests/system/mkeys/ns6/setup.sh index 2e032e710a..3fead4bcf7 100644 --- a/bin/tests/system/mkeys/ns6/setup.sh +++ b/bin/tests/system/mkeys/ns6/setup.sh @@ -27,4 +27,6 @@ rootkey=`cat ../ns1/managed.key` cp "../ns1/${rootkey}.key" . # Configure the resolving server with an initializing key. +# (We use key-format trust anchors here because otherwise the +# unsupported algorithm test won't work.) keyfile_to_initial_keys $unsupportedkey $rsakey $rootkey > managed.conf diff --git a/bin/tests/system/mkeys/tests.sh b/bin/tests/system/mkeys/tests.sh index 80c19beb03..da19c20264 100644 --- a/bin/tests/system/mkeys/tests.sh +++ b/bin/tests/system/mkeys/tests.sh @@ -301,7 +301,7 @@ status=`expr $status + $ret` echo_i "reinitialize trust anchors, add second key to bind.keys" $PERL $SYSTEMTESTTOP/stop.pl --use-rndc --port ${CONTROLPORT} mkeys ns2 rm -f ns2/managed-keys.bind* -keyfile_to_initial_keys ns1/$original ns1/$standby1 > ns2/managed.conf +keyfile_to_initial_ds ns1/$original ns1/$standby1 > ns2/managed.conf nextpart ns2/named.run > /dev/null $PERL $SYSTEMTESTTOP/start.pl --noclean --restart --port ${PORT} mkeys ns2 diff --git a/bin/tests/system/pending/ns1/sign.sh b/bin/tests/system/pending/ns1/sign.sh index 284eb4f680..aa6bf6ee32 100644 --- a/bin/tests/system/pending/ns1/sign.sh +++ b/bin/tests/system/pending/ns1/sign.sh @@ -28,7 +28,7 @@ cat $infile $keyname1.key $keyname2.key > $zonefile $SIGNER -g -o $zone $zonefile > /dev/null # Configure the resolving server with a static key. -keyfile_to_static_keys $keyname2 > trusted.conf +keyfile_to_static_ds $keyname2 > trusted.conf cp trusted.conf ../ns2/trusted.conf cp trusted.conf ../ns3/trusted.conf cp trusted.conf ../ns4/trusted.conf diff --git a/bin/tests/system/resolver/ns6/keygen.sh b/bin/tests/system/resolver/ns6/keygen.sh index a6c5c5b176..34ca7dc01a 100644 --- a/bin/tests/system/resolver/ns6/keygen.sh +++ b/bin/tests/system/resolver/ns6/keygen.sh @@ -31,4 +31,4 @@ cat $ksk.key $zsk.key dsset-ds.example.net$TP >> $zonefile $SIGNER -P -o $zone $zonefile > /dev/null # Configure a static key to be used by delv -keyfile_to_static_keys $ksk > ../ns5/trusted.conf +keyfile_to_static_ds $ksk > ../ns5/trusted.conf diff --git a/bin/tests/system/rootkeysentinel/ns1/sign.sh b/bin/tests/system/rootkeysentinel/ns1/sign.sh index 50eb562763..cfbed026ba 100644 --- a/bin/tests/system/rootkeysentinel/ns1/sign.sh +++ b/bin/tests/system/rootkeysentinel/ns1/sign.sh @@ -28,7 +28,7 @@ cat $infile $keyname.key > $zonefile $SIGNER -P -g -o $zone $zonefile > /dev/null # Configure the resolving server with a static key. -keyfile_to_static_keys $keyname > trusted.conf +keyfile_to_static_ds $keyname > trusted.conf cp trusted.conf ../ns2/trusted.conf cp trusted.conf ../ns3/trusted.conf cp trusted.conf ../ns4/trusted.conf diff --git a/bin/tests/system/rsabigexponent/ns1/sign.sh b/bin/tests/system/rsabigexponent/ns1/sign.sh index 3b8d4adf69..cdc61327b8 100755 --- a/bin/tests/system/rsabigexponent/ns1/sign.sh +++ b/bin/tests/system/rsabigexponent/ns1/sign.sh @@ -25,7 +25,7 @@ cat $infile $keyname.key > $zonefile $SIGNER -P -g -o $zone $zonefile > /dev/null # Configure the resolving server with a static key. -keyfile_to_static_keys $keyname > trusted.conf +keyfile_to_static_ds $keyname > trusted.conf cp trusted.conf ../ns2/trusted.conf cp trusted.conf ../ns3/trusted.conf diff --git a/bin/tests/system/sfcache/ns1/sign.sh b/bin/tests/system/sfcache/ns1/sign.sh index c1acdce500..7e5b2b3bed 100644 --- a/bin/tests/system/sfcache/ns1/sign.sh +++ b/bin/tests/system/sfcache/ns1/sign.sh @@ -29,8 +29,8 @@ cat "$infile" "$keyname.key" > "$zonefile" $SIGNER -P -g -o $zone $zonefile > /dev/null # Configure the resolving server with a static key. -keyfile_to_static_keys "$keyname" > trusted.conf +keyfile_to_static_ds "$keyname" > trusted.conf cp trusted.conf ../ns2/trusted.conf # ...or with an initializing key. -keyfile_to_initial_keys "$keyname" > managed.conf +keyfile_to_initial_ds "$keyname" > managed.conf diff --git a/bin/tests/system/sfcache/ns5/sign.sh b/bin/tests/system/sfcache/ns5/sign.sh index c369e545eb..82b4301804 100644 --- a/bin/tests/system/sfcache/ns5/sign.sh +++ b/bin/tests/system/sfcache/ns5/sign.sh @@ -16,4 +16,4 @@ set -e keyname=$("$KEYGEN" -q -a "$DEFAULT_ALGORITHM" -b "$DEFAULT_BITS" -n zone ".") -keyfile_to_static_keys "$keyname" > trusted.conf +keyfile_to_static_ds "$keyname" > trusted.conf diff --git a/bin/tests/system/staticstub/ns3/sign.sh b/bin/tests/system/staticstub/ns3/sign.sh index ce7a0f7d13..0fe84ff66d 100755 --- a/bin/tests/system/staticstub/ns3/sign.sh +++ b/bin/tests/system/staticstub/ns3/sign.sh @@ -27,7 +27,7 @@ cat $infile $keyname1.key $keyname2.key > $zonefile $SIGNER -g -o $zone $zonefile > /dev/null # Configure the resolving server with a trusted key. -keyfile_to_static_keys $keyname2 > trusted.conf +keyfile_to_static_ds $keyname2 > trusted.conf zone=undelegated infile=undelegated.db.in @@ -38,5 +38,5 @@ cat $infile $keyname1.key $keyname2.key > $zonefile $SIGNER -g -o $zone $zonefile > /dev/null -keyfile_to_static_keys $keyname2 >> trusted.conf +keyfile_to_static_ds $keyname2 >> trusted.conf cp trusted.conf ../ns2/trusted.conf diff --git a/bin/tests/system/synthfromdnssec/ns1/sign.sh b/bin/tests/system/synthfromdnssec/ns1/sign.sh index 710d9f4633..b45c577fd4 100644 --- a/bin/tests/system/synthfromdnssec/ns1/sign.sh +++ b/bin/tests/system/synthfromdnssec/ns1/sign.sh @@ -40,4 +40,4 @@ cat "$infile" "$keyname.key" > "$zonefile" $SIGNER -P -g -o $zone $zonefile > /dev/null # Configure the resolving server with a static key. -keyfile_to_static_keys "$keyname" > trusted.conf +keyfile_to_static_ds "$keyname" > trusted.conf diff --git a/bin/tests/system/wildcard/ns1/sign.sh b/bin/tests/system/wildcard/ns1/sign.sh index b89331ce3e..497e2759a4 100755 --- a/bin/tests/system/wildcard/ns1/sign.sh +++ b/bin/tests/system/wildcard/ns1/sign.sh @@ -43,7 +43,7 @@ cat $infile $keyname1.key $keyname2.key > $zonefile $SIGNER -o $zone -f $outfile $zonefile > /dev/null 2> signer.err || cat signer.err echo_i "signed $zone" -keyfile_to_static_keys $keyname2 > private.nsec.conf +keyfile_to_static_ds $keyname2 > private.nsec.conf zone=nsec3 infile=nsec3.db.in @@ -72,7 +72,7 @@ cat $infile $keyname1.key $keyname2.key > $zonefile $SIGNER -3 - -H 10 -o $zone -f $outfile $zonefile > /dev/null 2> signer.err || cat signer.err echo_i "signed $zone" -keyfile_to_static_keys $keyname2 > private.nsec3.conf +keyfile_to_static_ds $keyname2 > private.nsec3.conf zone=. infile=root.db.in @@ -87,4 +87,4 @@ cat $infile $keyname1.key $keyname2.key $dssets >$zonefile $SIGNER -o $zone -f $outfile $zonefile > /dev/null 2> signer.err || cat signer.err echo_i "signed $zone" -keyfile_to_static_keys $keyname2 > trusted.conf +keyfile_to_static_ds $keyname2 > trusted.conf From c29ccae2a6f70237f96db2c3beb70a0f30899acd Mon Sep 17 00:00:00 2001 From: Evan Hunt Date: Thu, 19 Sep 2019 14:52:49 -0700 Subject: [PATCH 12/13] Document initial-ds and static-ds keywords --- bin/named/named.conf.docbook | 17 ++-- doc/arm/Bv9ARM-book.xml | 167 +++++++++++++++++-------------- doc/arm/dnssec-keys.grammar.xml | 3 +- doc/arm/libdns.xml | 3 +- doc/arm/managed-keys.grammar.xml | 6 +- doc/arm/managed-keys.xml | 4 +- doc/misc/options | 21 ++-- doc/misc/options.active | 21 ++-- 8 files changed, 135 insertions(+), 107 deletions(-) diff --git a/bin/named/named.conf.docbook b/bin/named/named.conf.docbook index 61016b6094..8bdfd30075 100644 --- a/bin/named/named.conf.docbook +++ b/bin/named/named.conf.docbook @@ -13,7 +13,7 @@ - 2019-08-07 + 2019-08-12 ISC @@ -113,7 +113,8 @@ dlz string { DNSSEC-KEYS dnssec-keys { string ( static-key | - initial-key ) integer integer integer + initial-key | static-ds | initial-ds ) + integer integer integer quoted_string; ... }; @@ -158,9 +159,9 @@ logging { Deprecated - see DNSSEC-KEYS. managed-keys { string ( static-key - | initial-key ) integer - integer integer - quoted_string; ... }; deprecated + | initial-key | static-ds | + initial-ds ) integer integer + integer quoted_string; ... }; deprecated @@ -607,8 +608,9 @@ view string [ class ] { dnssec-accept-expired boolean; dnssec-dnskey-kskonly boolean; dnssec-keys { string ( static-key | - initial-key ) integer integer - integer quoted_string; ... }; + initial-key | static-ds | initial-ds + ) integer integer integer + quoted_string; ... }; dnssec-loadkeys-interval integer; dnssec-must-be-secure string boolean; dnssec-secure-to-insecure boolean; @@ -646,6 +648,7 @@ view string [ class ] { lmdb-mapsize sizeval; managed-keys { string ( static-key | initial-key + | static-ds | initial-ds ) integer integer integer quoted_string; ... }; deprecated diff --git a/doc/arm/Bv9ARM-book.xml b/doc/arm/Bv9ARM-book.xml index a7092fb8c0..cb0de373a3 100644 --- a/doc/arm/Bv9ARM-book.xml +++ b/doc/arm/Bv9ARM-book.xml @@ -2230,13 +2230,14 @@ allow-update { !{ !localnets; any; }; key host1-host2. ;}; The keys specified in dnssec-keys copies of DNSKEY RRs for zones that are used to form the first link in the cryptographic chain of trust. Keys configured - with the keyword static-key are loaded directly + with the keyword static-key or + static-ds are loaded directly into the table of trust anchors, and can only be changed by altering the configuration. Keys configured with - initial-key are used to initialize - RFC 5011 trust anchor maintenance, and will be kept up to - date automatically after the first time named - runs. + initial-key or initial-ds + are used to initialize RFC 5011 trust anchor maintenance, and + will be kept up to date automatically after the first time + named runs. @@ -2276,17 +2277,7 @@ dnssec-keys { 97S+LKUTpQcq27R7AT3/V5hRQxScINqwcz4jYqZD2fQ dgxbcDTClU0CRBdiieyLMNzXG3"; /* Key for our organization's forward zone */ - example.com. static-key 257 3 5 "AwEAAaxPMcR2x0HbQV4WeZB6oEDX+r0QM6 - 5KbhTjrW1ZaARmPhEZZe3Y9ifgEuq7vZ/z - GZUdEGNWy+JZzus0lUptwgjGwhUS1558Hb - 4JKUbbOTcM8pwXlj0EiX3oDFVmjHO444gL - kBOUKUf/mC7HvfwYH/Be22GnClrinKJp1O - g4ywzO9WglMk7jbfW33gUKvirTHr25GL7S - TQUzBb5Usxt8lgnyTUHs1t3JwCY5hKZ6Cq - FxmAVZP20igTixin/1LcrgX/KMEGd/biuv - F4qJCyduieHukuY3H4XMAcR+xia2nIUPvm - /oyWR8BW/hWdzOvnSCThlHf3xiYleDbt/o - 1OTQ09A0="; + example.com. static-ds 54135 5 2 "8EF922C97F1D07B23134440F19682E7519ADDAE180E20B1B1EC52E7F58B2831D" /* Key for our reverse zone. */ 2.0.192.IN-ADDRPA.NET. static-key 257 3 5 "AQOnS4xn/IgOUpBPJ3bogzwc @@ -3215,11 +3206,14 @@ $ORIGIN 0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa. - defines DNSSEC keys: if used with the - initial-key keyword, - keys are kept up to date using RFC 5011 - trust anchor maintenance, and if used with - static-key, keys are permanent. + defines DNSSEC trust anchors: if used with + the initial-key or + initial-ds keyword, + trust anchors are kept up to date using RFC + 5011 trust anchor maintenance, and if used with + static-key or + static-ds, trust anchors + are permanent. @@ -4628,7 +4622,8 @@ badresp:1,adberr:0,findfail:0,valfail:0] Specifies the directory in which to store the files that track managed DNSSEC keys (i.e., those configured using - the initial-key keyword in a + the initial-key or + initial-ds keywords in a dnssec-keys statement). By default, this is the working directory. The directory must be writable by the effective @@ -10864,12 +10859,12 @@ example.com CNAME rpz-tcp-only. trust anchors. DNSSEC is described in . - A trust anchor is defined when the public key for - a non-authoritative zone is known, but cannot be securely - obtained through DNS, either because it is the DNS root zone - or because its parent zone is unsigned. Once a key has been - configured as a trust anchor, it is treated as if it had - been validated and proven secure. + A trust anchor is defined when the public key or public key + digest for a non-authoritative zone is known, but cannot be + securely obtained through DNS, either because it is the DNS + root zone or because its parent zone is unsigned. Once a key + or digest has been configured as a trust anchor, it is treated + as if it had been validated and proven secure. The resolver attempts DNSSEC validation on all DNS data @@ -10881,19 +10876,9 @@ example.com CNAME rpz-tcp-only. All keys listed in dnssec-keys, and their corresponding zones, are deemed to exist regardless - of what parent zones say. Only keys configured as trust anchors + of what parent zones say. Only keys configured as trust anchors are used to validate the DNSKEY RRset for the corresponding - name. The parent's DS RRset will not be used. - - - The dnssec-keys statement can contain - multiple key entries, each consisting of the key's - domain name, followed by the static-key or - initial-key keyword, then the key's flags, - protocol, algorithm, and the Base64 representation of the key - data. Spaces, tabs, newlines and carriage returns are ignored - in the key data, so the configuration may be split up into - multiple lines. + name. The parent's DS RRset will not be used. dnssec-keys may be set at the top level @@ -10903,11 +10888,33 @@ example.com CNAME rpz-tcp-only. defined in a view are only used within that view. - dnssec-keys entries can be configured with - two keywords: static-key or - initial-key. Keys configured with - static-key are immutable, - while keys configured with initial-key + The dnssec-keys statement can contain + multiple trust anchor entries, each consisting of a + domain name, followed by an "anchor type" keyword indicating + the trust anchor's format, followed by the key or digest data. + + + If the anchor type is static-key or + initial-key, then it is followed with the + key's flags, protocol, algorithm, and the Base64 representation + of the public key data. This is identical to the text + representation of a DNSKEY record. Spaces, tabs, newlines and + carriage returns are ignored in the key data, so the + configuration may be split up into multiple lines. + + + If the anchor type is static-ds or + initial-ds, then it is followed with the + key tag, algorithm, digest type, and the hexidecimal + representation of the key digest. This is identical to the + text representation of a DS record. Spaces, tabs, newlines + and carriage returns are ignored. + + + Trust anchors configured with the + static-key or static-ds + anchor types are immutable, while keys configured with + initial-key or initial-ds can be kept up to date automatically, without intervention from the resolver operator. (static-key keys are identical to keys configured using the deprecated @@ -10917,45 +10924,55 @@ example.com CNAME rpz-tcp-only. Suppose, for example, that a zone's key-signing key was compromised, and the zone owner had to revoke and replace the key. A resolver which had the original key - configured as a static-key would be - unable to validate this zone any longer; it would - reply with a SERVFAIL response code. This would - continue until the resolver operator had updated the - dnssec-keys statement with the new key. + configured using static-key or + static-ds would be unable to validate + this zone any longer; it would reply with a SERVFAIL response + code. This would continue until the resolver operator had + updated the dnssec-keys statement with + the new key. If, however, the trust anchor had been configured with - initial-key instead, then the - zone owner could add a "stand-by" key to their zone in advance. - named would store the stand-by key, and - when the original key was revoked, named - would be able to transition smoothly to the new key. It would - also recognize that the old key had been revoked, and cease - using that key to validate answers, minimizing the damage that - the compromised key could do. This is the process used to - keep the ICANN root DNSSEC key up to date. + initial-key or initial-ds + instead, then the zone owner could add a "stand-by" key to + their zone in advance. named would store + the stand-by key, and when the original key was revoked, + named would be able to transition smoothly + to the new key. It would also recognize that the old key had + been revoked, and cease using that key to validate answers, + minimizing the damage that the compromised key could do. + This is the process used to keep the ICANN root DNSSEC key + up to date. - Whereas static-key - keys continue to be trusted until they are removed from + Whereas static-key and + static-ds trust anchors continue + to be trusted until they are removed from named.conf, an - initial-key is only trusted - once: for as long as it + initial-key or initial-ds + is only trusted once: for as long as it takes to load the managed key database and start the RFC 5011 key maintenance process. + + It is not possible to mix static with initial trust anchors + for the same domain name. It is also not possible to mix + key with ds trust anchors. + The first time named runs with an - initial-key configured in - named.conf, it fetches the + initial-key or initial-ds + configured in named.conf, it fetches the DNSKEY RRset directly from the zone apex, and validates it - using the key specified in dnssec-keys. - If the DNSKEY RRset is validly signed, then it is - used as the basis for a new managed keys database. + using the trust anchor specified in dnssec-keys. + If the DNSKEY RRset is validly signed by a key matching + the trust anchor, then it is used as the basis for a new + managed keys database. From that point on, whenever named runs, it - sees the initial-key listed in + sees the initial-key or + initial-ds listed in dnssec-keys, checks to make sure RFC 5011 key maintenance has already been initialized for the specified domain, and if so, it simply moves on. The @@ -10966,13 +10983,13 @@ example.com CNAME rpz-tcp-only. The next time named runs after an - initial-key has been - removed from the + initial-key or initial-ds + trust anchor has been removed from the dnssec-keys statement (or changed to - a static-key), the corresponding - zone will be removed from the managed keys database, - and RFC 5011 key maintenance will no longer be used for that - domain. + a static-key or static-ds), + the corresponding keys will be removed from the managed keys + database, and RFC 5011 key maintenance will no longer be used + for that domain. In the current implementation, the managed keys database diff --git a/doc/arm/dnssec-keys.grammar.xml b/doc/arm/dnssec-keys.grammar.xml index 4f5d238a99..6c602292b5 100644 --- a/doc/arm/dnssec-keys.grammar.xml +++ b/doc/arm/dnssec-keys.grammar.xml @@ -13,6 +13,7 @@ dnssec-keys { string ( static-key | - initial-key ) integer integer integer + initial-key | static-ds | initial-ds ) + integer integer integer quoted_string; ... }; diff --git a/doc/arm/libdns.xml b/doc/arm/libdns.xml index 19230552fb..f4758429db 100644 --- a/doc/arm/libdns.xml +++ b/doc/arm/libdns.xml @@ -138,7 +138,8 @@ $ make named.conf, except that all managed-keys entries will be treated as if they were configured with the static-key - keyword, even if they are configured with initial-key. + or static-ds keywords, even if they are configured + with initial-key or iniital-ds. (See for syntax details.) diff --git a/doc/arm/managed-keys.grammar.xml b/doc/arm/managed-keys.grammar.xml index beb0f96725..2e1e7219f5 100644 --- a/doc/arm/managed-keys.grammar.xml +++ b/doc/arm/managed-keys.grammar.xml @@ -13,7 +13,7 @@ managed-keys { string ( static-key - | initial-key ) integer - integer integer - quoted_string; ... }; deprecated + | initial-key | static-ds | + initial-ds ) integer integer + integer quoted_string; ... }; deprecated diff --git a/doc/arm/managed-keys.xml b/doc/arm/managed-keys.xml index e4ba67ab6c..da6c170a35 100644 --- a/doc/arm/managed-keys.xml +++ b/doc/arm/managed-keys.xml @@ -25,8 +25,8 @@ To configure a validating resolver to use RFC 5011 to maintain a trust anchor, configure the trust anchor using a dnssec-keys statement and the - initial-key keyword. Information about - this can be found in + initial-key or initial-ds + keyword. Information about this can be found in .
Authoritative Server diff --git a/doc/misc/options b/doc/misc/options index 61dad9bbba..38881ac0c9 100644 --- a/doc/misc/options +++ b/doc/misc/options @@ -22,7 +22,8 @@ dlz { }; // may occur multiple times dnssec-keys { ( static-key | - initial-key ) + initial-key | static-ds | initial-ds ) + ; ... }; // may occur multiple times dnssec-policy { @@ -68,9 +69,9 @@ logging { lwres { }; // obsolete, may occur multiple times managed-keys { ( static-key - | initial-key ) - - ; ... }; // may occur multiple times, deprecated + | initial-key | static-ds | + initial-ds ) + ; ... }; // may occur multiple times, deprecated masters [ port ] [ dscp ] { ( | [ @@ -209,7 +210,7 @@ options { fstrm-set-output-queue-model ( mpsc | spsc ); // not configured fstrm-set-output-queue-size ; // not configured fstrm-set-reopen-interval ; // not configured - geoip-directory ( | none ); // not configured + geoip-directory ( | none ); geoip-use-ecs ; // obsolete glue-cache ; has-old-clients ; // ancient @@ -230,7 +231,7 @@ options { listen-on-v6 [ port ] [ dscp ] { ; ... }; // may occur multiple times - lmdb-mapsize ; // non-operational + lmdb-mapsize ; lock-file ( | none ); maintain-ixfr-base ; // ancient managed-keys-directory ; @@ -538,8 +539,9 @@ view [ ] { dnssec-dnskey-kskonly ; dnssec-enable ; // obsolete dnssec-keys { ( static-key | - initial-key ) - ; ... }; // may occur multiple times + initial-key | static-ds | initial-ds + ) + ; ... }; // may occur multiple times dnssec-loadkeys-interval ; dnssec-lookaside ( trust-anchor | @@ -581,10 +583,11 @@ view [ ] { }; // may occur multiple times key-directory ; lame-ttl ; - lmdb-mapsize ; // non-operational + lmdb-mapsize ; maintain-ixfr-base ; // ancient managed-keys { ( static-key | initial-key + | static-ds | initial-ds ) ; ... }; // may occur multiple times, deprecated diff --git a/doc/misc/options.active b/doc/misc/options.active index 21e47dc152..e4123c69c5 100644 --- a/doc/misc/options.active +++ b/doc/misc/options.active @@ -22,7 +22,8 @@ dlz { }; // may occur multiple times dnssec-keys { ( static-key | - initial-key ) + initial-key | static-ds | initial-ds ) + ; ... }; // may occur multiple times dyndb { @@ -50,9 +51,9 @@ logging { }; managed-keys { ( static-key - | initial-key ) - - ; ... }; // may occur multiple times, deprecated + | initial-key | static-ds | + initial-ds ) + ; ... }; // may occur multiple times, deprecated masters [ port ] [ dscp ] { ( | [ @@ -175,7 +176,7 @@ options { fstrm-set-output-queue-model ( mpsc | spsc ); // not configured fstrm-set-output-queue-size ; // not configured fstrm-set-reopen-interval ; // not configured - geoip-directory ( | none ); // not configured + geoip-directory ( | none ); glue-cache ; heartbeat-interval ; hostname ( | none ); @@ -192,7 +193,7 @@ options { listen-on-v6 [ port ] [ dscp ] { ; ... }; // may occur multiple times - lmdb-mapsize ; // non-operational + lmdb-mapsize ; lock-file ( | none ); managed-keys-directory ; masterfile-format ( map | raw | text ); @@ -470,8 +471,9 @@ view [ ] { dnssec-accept-expired ; dnssec-dnskey-kskonly ; dnssec-keys { ( static-key | - initial-key ) - ; ... }; // may occur multiple times + initial-key | static-ds | initial-ds + ) + ; ... }; // may occur multiple times dnssec-loadkeys-interval ; dnssec-must-be-secure ; // may occur multiple times dnssec-secure-to-insecure ; @@ -506,9 +508,10 @@ view [ ] { }; // may occur multiple times key-directory ; lame-ttl ; - lmdb-mapsize ; // non-operational + lmdb-mapsize ; managed-keys { ( static-key | initial-key + | static-ds | initial-ds ) ; ... }; // may occur multiple times, deprecated From d97e628f81751754540fca9fc97a229bd03e99fe Mon Sep 17 00:00:00 2001 From: Evan Hunt Date: Thu, 19 Sep 2019 15:02:42 -0700 Subject: [PATCH 13/13] CHANGES, release notes --- CHANGES | 9 +++++++++ doc/arm/notes-9.15.6.xml | 21 +++++++++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/CHANGES b/CHANGES index 61cdf9490c..ad8d294043 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,12 @@ +5319. [func] Trust anchors can now be configured using DS + format to represent a key digest, by using the + new "initial-ds" or "static-ds" keywords in + the "dnssec-keys" statement. + + Note: DNSKEY-format and DS-format trust anchors + cannot both be used for the same domain name. + [GL #622] + 5318. [cleanup] The DNSSEC validation code has been refactored for clarity and to reduce code duplication. [GL #622] diff --git a/doc/arm/notes-9.15.6.xml b/doc/arm/notes-9.15.6.xml index fca186f62a..b5c083af4b 100644 --- a/doc/arm/notes-9.15.6.xml +++ b/doc/arm/notes-9.15.6.xml @@ -33,6 +33,27 @@ policy used by dnssec-keymgr.) [GL #1134] + + + Two new keywords have been added to the + dnssec-keys statement: + initial-ds and static-ds. + These allow the use of trust anchors in DS format instead of + DNSKEY format. DS format allows trust anchors to be configured + for keys that have not yet been published; this is the format + used by IANA when announcing future root keys. + + + As with the initial-key and + static-key keywords, initial-ds + configures a dynamic trust anchor to be maintained via RFC 5011, and + static-ds configures a permanent trust anchor. + + + (Note: Currently, DNSKEY-format and DS-format trust anchors + cannot both be used for the same domain name.) [GL #6] [GL #622] + +