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/bin/delv/delv.c b/bin/delv/delv.c index f3127ab9e8..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; - uint32_t flags, proto, alg; - const char *keystr, *keynamestr; - unsigned char keydata[4096]; - isc_buffer_t keydatabuf; + dns_rdata_dnskey_t dnskey; + dns_rdata_ds_t ds; + uint32_t n1, n2, n3; + 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)); @@ -642,46 +652,118 @@ 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")); - keystruct.common.rdclass = dns_rdataclass_in; - keystruct.common.rdtype = dns_rdatatype_dnskey; - /* - * The key data in keystruct is not dynamically allocated. - */ - keystruct.mctx = NULL; + /* if DNSKEY, protocol; if DS, algorithm */ + n2 = cfg_obj_asuint32(cfg_tuple_get(key, "n2")); - ISC_LINK_INIT(&keystruct.common, link); + /* if DNSKEY, algorithm; if DS, digest type */ + n3 = cfg_obj_asuint32(cfg_tuple_get(key, "n3")); - if (flags > 0xffff) - CHECK(ISC_R_RANGE); - if (proto > 0xff) - CHECK(ISC_R_RANGE); - if (alg > 0xff) - CHECK(ISC_R_RANGE); + /* 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; + } - 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(&databuf, data, sizeof(data)); 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 (n1 > 0xffff) { + CHECK(ISC_R_RANGE); + } + if (n2 > 0xff) { + CHECK(ISC_R_RANGE); + } + if (n3 > 0xff) { + CHECK(ISC_R_RANGE); + } - CHECK(dns_rdata_fromstruct(NULL, - keystruct.common.rdclass, - keystruct.common.rdtype, - &keystruct, &rrdatabuf)); + 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_LINK_INIT(&dnskey.common, link); + + dnskey.flags = (uint16_t)n1; + dnskey.protocol = (uint8_t)n2; + dnskey.algorithm = (uint8_t)n3; + + 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/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/bin/named/server.c b/bin/named/server.c index 337471dd9d..1e15368e3a 100644 --- a/bin/named/server.c +++ b/bin/named/server.c @@ -698,104 +698,190 @@ 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 **keyp, + dns_rdata_ds_t **dsp, const char **namestrp, 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; + dns_rdata_ds_t *ds = NULL; + uint32_t n1, n2, n3; + 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; + dns_fixedname_t fname; + dns_name_t *name = 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(keyp != NULL && *keyp == NULL); + REQUIRE(dsp != NULL && *dsp == NULL); + REQUIRE(namestrp != NULL && *namestrp == NULL); - 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 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")); + + namestr = cfg_obj_asstring(cfg_tuple_get(key, "name")); + *namestrp = namestr; + + 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) { - 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) { *initialp = false; - } else if (strcasecmp(initmethod, "initial-key") != 0) { + 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, initmethod); + namestr, atstr); result = ISC_R_FAILURE; goto cleanup; } + } else { + anchortype = TRUSTED; } - /* - * 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(&databuf, data, sizeof(data)); 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; + 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; - 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); + /* + * 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; + + 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(name, dns_rdataclass_in, + &rrdatabuf, mctx, &dstkey)); + + *keyp = dstkey; + break; + + case INIT_DS: + case STATIC_DS: + 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); + ISC_UNREACHABLE(); } - CHECK(dns_rdata_fromstruct(NULL, - keystruct.common.rdclass, - keystruct.common.rdtype, - &keystruct, &rrdatabuf)); - dns_fixedname_init(&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; return (ISC_R_SUCCESS); cleanup: @@ -803,6 +889,11 @@ dstkey_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); } @@ -822,24 +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 = dstkey_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: @@ -851,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: /* @@ -860,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: /* @@ -871,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); } @@ -893,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; } @@ -905,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: /* @@ -917,6 +1031,14 @@ process_key(const cfg_obj_t *key, dns_keytable_t *secroots, dst_key_free(&dstkey); } + /* + * Free 'ds'. + */ + if (ds != NULL) { + dns_rdata_freestruct(ds); + isc_mem_put(mctx, ds, sizeof(*ds)); + } + return (result); } @@ -1117,16 +1239,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)); } /* @@ -6700,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); @@ -6758,14 +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, void *arg) { +dotat(dns_keytable_t *keytable, dns_keynode_t *keynode, + 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; @@ -6782,7 +6927,7 @@ dotat(dns_keytable_t *keytable, dns_keynode_t *keynode, void *arg) { 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; } @@ -6820,17 +6965,13 @@ dotat(dns_keytable_t *keytable, dns_keynode_t *keynode, void *arg) { * 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/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/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/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/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 385d3100a7..66254b7cfe 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_ds "$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_ds "$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/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/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/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 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/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] + +
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 diff --git a/lib/bind9/check.c b/lib/bind9/check.c index 402a679053..0dc28db325 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 @@ -3117,28 +3119,128 @@ 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, *keynamestr; + const char *str = NULL, *namestr = 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; - unsigned char keydata[4096]; + uint32_t n1, n2, n3; + unsigned char data[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, initial and + * static trust anchor 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 + }; + 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")); + + /* 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")); + + namestr = cfg_obj_asstring(cfg_tuple_get(key, "name")); keyname = dns_fixedname_initname(&fkeyname); - keynamestr = cfg_obj_asstring(cfg_tuple_get(key, "name")); - - 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", @@ -3146,151 +3248,169 @@ 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); + namestr, atstr); + result = ISC_R_FAILURE; + + /* + * We can't interpret the trust anchor, so + * we skip all other checks. + */ + goto cleanup; + } + } else { + atstr = "trusted-key"; + anchortype = TRUSTED; + } + + 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, + "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, data, sizeof(data)); + + 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, + "%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, namestr); + } + } + + 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 == 257 && n2 == 3 && n3 == 8 && + (isc_buffer_usedlength(&b) == + sizeof(root_ksk_2010)) && + memcmp(data, root_ksk_2010, + sizeof(root_ksk_2010)) == 0) + { + *flagsp |= ROOT_KSK_2010; + } + + if (n1 == 257 && n2 == 3 && n3 == 8 && + (isc_buffer_usedlength(&b) == + sizeof(root_ksk_2017)) && + memcmp(data, root_ksk_2017, + sizeof(root_ksk_2017)) == 0) + { + *flagsp |= ROOT_KSK_2017; + } + } + break; + + case INIT_DS: + case STATIC_DS: + 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; } - 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); - - if ((alg == DST_ALG_RSASHA1) && - r.length > 1 && r.base[0] == 1 && r.base[1] == 3) - { - 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; - } - } - + cleanup: return (result); } @@ -3322,13 +3442,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 +3470,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 +3502,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; @@ -3414,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. */ @@ -3425,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); @@ -3453,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); @@ -3465,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; @@ -3477,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; } @@ -3505,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); } @@ -3898,8 +4164,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) { @@ -3914,7 +4179,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; @@ -3958,8 +4224,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) { @@ -3971,10 +4236,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 && @@ -4001,7 +4267,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"); } diff --git a/lib/dns/client.c b/lib/dns/client.c index 48ed6aa88c..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,27 +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, &dstkey); + 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/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/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/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/include/dns/keytable.h b/lib/dns/include/dns/keytable.h index 66fc5c1a84..bcafdc3623 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); /*%< @@ -453,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 a154699d45..cdc54071e7 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; } } @@ -703,32 +866,45 @@ 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); 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) - (*func)(keytable, node->data, arg); + dns_rbtnodechain_current(&chain, foundname, origin, &node); + if (node->data != NULL) { + 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) { - if (result == ISC_R_NOMORE) + if (result == ISC_R_NOMORE) { result = ISC_R_SUCCESS; + } break; } } @@ -747,6 +923,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 +967,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 +985,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/validator.c b/lib/dns/validator.c index 11bafb567e..545c8a2229 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; @@ -1926,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 @@ -1935,17 +1894,33 @@ 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_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; @@ -1989,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")); } @@ -2069,16 +2045,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,13 +2062,18 @@ 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; } 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)"); diff --git a/lib/dns/win32/libdns.def.in b/lib/dns/win32/libdns.def.in index 31af4b1c1e..b1a02a0f68 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 @@ -462,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..f70b460177 100644 --- a/lib/dns/zone.c +++ b/lib/dns/zone.c @@ -3828,81 +3828,59 @@ 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. + * 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, 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; + 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; - REQUIRE(keynodep != NULL); - keynode = *keynodep; + REQUIRE(keynode != NULL); ENTER; isc_stdtime_get(&now); - /* 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) - goto skip; - - 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, - dst_key_name(key), 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 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); } - if (keynode != NULL) - dns_keytable_detachkeynode(keytable, &keynode); - *keynodep = NULL; + memset(&kd, 0, sizeof(kd)); + 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, &kd, now, true); return (ISC_R_SUCCESS); failure: @@ -3985,7 +3963,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: @@ -4262,51 +4241,69 @@ 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; 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; - if (((struct addifmissing_arg *)arg)->result != ISC_R_SUCCESS) + UNUSED(keytable); + + 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. + */ + 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; + } +} /* * 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) { @@ -4337,9 +4334,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); @@ -4362,12 +4359,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); @@ -4380,8 +4373,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; @@ -9740,7 +9735,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; @@ -9751,11 +9746,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); @@ -9765,6 +9763,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; @@ -9806,9 +9807,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)); @@ -9817,7 +9816,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)); @@ -9829,27 +9828,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) { @@ -9860,26 +9942,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); @@ -9887,14 +9964,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) { @@ -9902,6 +9977,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. @@ -9926,14 +10007,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); @@ -9960,7 +10041,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) { @@ -10041,9 +10122,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; @@ -10053,7 +10134,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); @@ -10078,9 +10159,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); @@ -10339,14 +10420,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); @@ -10366,8 +10447,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) { 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/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, 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); } 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",