diff --git a/bin/dnssec/dnssec-cds.c b/bin/dnssec/dnssec-cds.c index 3c7db4d0ed..1d8c25185a 100644 --- a/bin/dnssec/dnssec-cds.c +++ b/bin/dnssec/dnssec-cds.c @@ -482,7 +482,7 @@ match_key_dsset(keyinfo_t *ki, dns_rdataset_t *dsset, strictness_t strictness) { } result = dns_ds_buildrdata(name, &ki->rdata, ds.digest_type, - dsbuf, &newdsrdata); + dsbuf, sizeof(dsbuf), &newdsrdata); if (result != ISC_R_SUCCESS) { vbprintf(3, "dns_ds_buildrdata(" @@ -769,7 +769,7 @@ ds_from_cdnskey(isc_buffer_t *buf, dns_rdata_t *ds, dns_dsdigest_t dt, return ISC_R_NOSPACE; } - result = dns_ds_buildrdata(name, cdnskey, dt, r.base, ds); + result = dns_ds_buildrdata(name, cdnskey, dt, r.base, r.length, ds); if (result == ISC_R_SUCCESS) { isc_buffer_add(buf, DNS_DS_BUFFERSIZE); } diff --git a/bin/dnssec/dnssec-dsfromkey.c b/bin/dnssec/dnssec-dsfromkey.c index 6fd0d35dcb..15acead642 100644 --- a/bin/dnssec/dnssec-dsfromkey.c +++ b/bin/dnssec/dnssec-dsfromkey.c @@ -270,7 +270,7 @@ emit(dns_dsdigest_t dt, bool showall, bool cds, dns_rdata_t *rdata) { return; } - result = dns_ds_buildrdata(name, rdata, dt, buf, &ds); + result = dns_ds_buildrdata(name, rdata, dt, buf, sizeof(buf), &ds); if (result != ISC_R_SUCCESS) { fatal("can't build record"); } diff --git a/bin/dnssec/dnssec-keyfromlabel.c b/bin/dnssec/dnssec-keyfromlabel.c index 6d57204034..49ddb8ad7a 100644 --- a/bin/dnssec/dnssec-keyfromlabel.c +++ b/bin/dnssec/dnssec-keyfromlabel.c @@ -113,7 +113,7 @@ main(int argc, char **argv) { dns_fixedname_t fname; dns_name_t *name; uint16_t flags = 0, kskflag = 0, revflag = 0; - dns_secalg_t alg; + dst_algorithm_t alg; bool oldstyle = false; isc_mem_t *mctx = NULL; int ch; @@ -382,7 +382,7 @@ main(int argc, char **argv) { r.base = algname; r.length = strlen(algname); - ret = dns_secalg_fromtext(&alg, &r); + ret = dst_algorithm_fromtext(&alg, &r); if (ret != ISC_R_SUCCESS) { fatal("unknown algorithm %s", algname); } diff --git a/bin/dnssec/dnssec-keygen.c b/bin/dnssec/dnssec-keygen.c index 12bbbe0d85..c3e24a39e7 100644 --- a/bin/dnssec/dnssec-keygen.c +++ b/bin/dnssec/dnssec-keygen.c @@ -89,7 +89,7 @@ struct keygen_ctx { bool wantzsk; bool wantksk; bool wantrev; - dns_secalg_t alg; + dst_algorithm_t alg; /* timing data */ int prepub; isc_stdtime_t now; @@ -245,7 +245,7 @@ keygen(keygen_ctx_t *ctx, isc_mem_t *mctx, int argc, char **argv) { UNUSED(argc); - dns_secalg_format(ctx->alg, algstr, sizeof(algstr)); + dst_algorithm_format(ctx->alg, algstr, sizeof(algstr)); if (ctx->predecessor == NULL) { if (ctx->prepub == -1) { @@ -286,6 +286,8 @@ keygen(keygen_ctx_t *ctx, isc_mem_t *mctx, int argc, char **argv) { case DST_ALG_NSEC3RSASHA1: case DST_ALG_RSASHA256: case DST_ALG_RSASHA512: + case DST_ALG_RSASHA256PRIVATEOID: + case DST_ALG_RSASHA512PRIVATEOID: case DST_ALG_ECDSA256: case DST_ALG_ECDSA384: case DST_ALG_ED25519: @@ -309,6 +311,8 @@ keygen(keygen_ctx_t *ctx, isc_mem_t *mctx, int argc, char **argv) { FALLTHROUGH; case DST_ALG_RSASHA256: case DST_ALG_RSASHA512: + case DST_ALG_RSASHA256PRIVATEOID: + case DST_ALG_RSASHA512PRIVATEOID: ctx->size = 2048; if (verbose > 0) { fprintf(stderr, @@ -454,14 +458,16 @@ keygen(keygen_ctx_t *ctx, isc_mem_t *mctx, int argc, char **argv) { } switch (ctx->alg) { - case DNS_KEYALG_RSASHA1: - case DNS_KEYALG_NSEC3RSASHA1: + case DST_ALG_RSASHA1: + case DST_ALG_NSEC3RSASHA1: if (isc_crypto_fips_mode()) { fatal("SHA1 based keys not supported in FIPS mode"); } FALLTHROUGH; - case DNS_KEYALG_RSASHA256: - case DNS_KEYALG_RSASHA512: + case DST_ALG_RSASHA256: + case DST_ALG_RSASHA512: + case DST_ALG_RSASHA256PRIVATEOID: + case DST_ALG_RSASHA512PRIVATEOID: if (ctx->size != 0 && (ctx->size < min_rsa || ctx->size > MAX_RSA)) { @@ -480,6 +486,8 @@ keygen(keygen_ctx_t *ctx, isc_mem_t *mctx, int argc, char **argv) { case DST_ALG_ED448: ctx->size = 456; break; + default: + fatal("not a dnskey algorithm %u\n", ctx->alg); } if ((ctx->options & DST_TYPE_KEY) == 0) { @@ -502,10 +510,10 @@ keygen(keygen_ctx_t *ctx, isc_mem_t *mctx, int argc, char **argv) { } switch (ctx->alg) { - case DNS_KEYALG_RSASHA1: - case DNS_KEYALG_NSEC3RSASHA1: - case DNS_KEYALG_RSASHA256: - case DNS_KEYALG_RSASHA512: + case DST_ALG_RSASHA1: + case DST_ALG_NSEC3RSASHA1: + case DST_ALG_RSASHA256: + case DST_ALG_RSASHA512: show_progress = true; break; @@ -515,6 +523,8 @@ keygen(keygen_ctx_t *ctx, isc_mem_t *mctx, int argc, char **argv) { case DST_ALG_ED448: show_progress = true; break; + default: + break; } if ((flags & DNS_KEYFLAG_TYPEMASK) == DNS_KEYTYPE_NOKEY && @@ -1072,7 +1082,7 @@ main(int argc, char **argv) { } r.base = algname; r.length = strlen(algname); - ret = dns_secalg_fromtext(&ctx.alg, &r); + ret = dst_algorithm_fromtext(&ctx.alg, &r); if (ret != ISC_R_SUCCESS) { fatal("unknown algorithm %s", algname); } diff --git a/bin/dnssec/dnssec-ksr.c b/bin/dnssec/dnssec-ksr.c index 189f8994fa..ea96d41c11 100644 --- a/bin/dnssec/dnssec-ksr.c +++ b/bin/dnssec/dnssec-ksr.c @@ -64,7 +64,7 @@ struct ksr_ctx { /* keygen */ bool ksk; dns_ttl_t ttl; - dns_secalg_t alg; + dst_algorithm_t alg; int size; time_t lifetime; time_t parentpropagation; @@ -341,7 +341,7 @@ create_key(ksr_ctx_t *ksr, dns_kasp_t *kasp, dns_kasp_key_t *kaspkey, } /* Check algorithm and size. */ - dns_secalg_format(ksr->alg, algstr, sizeof(algstr)); + dst_algorithm_format(ksr->alg, algstr, sizeof(algstr)); if (!dst_algorithm_supported(ksr->alg)) { fatal("unsupported algorithm: %s", algstr); } @@ -356,6 +356,8 @@ create_key(ksr_ctx_t *ksr, dns_kasp_t *kasp, dns_kasp_key_t *kaspkey, FALLTHROUGH; case DST_ALG_RSASHA256: case DST_ALG_RSASHA512: + case DST_ALG_RSASHA256PRIVATEOID: + case DST_ALG_RSASHA512PRIVATEOID: if (ksr->size != 0 && (ksr->size < min_rsa || ksr->size > MAX_RSA)) { @@ -851,7 +853,7 @@ get_keymaterial(ksr_ctx_t *ksr, dns_kasp_t *kasp, isc_stdtime_t inception, dns_rdata_init(rdata2); CHECK(dns_ds_buildrdata(name, rdata, alg->digest, - cdsbuf, &cds)); + cdsbuf, sizeof(cdsbuf), &cds)); cds.type = dns_rdatatype_cds; dns_rdata_toregion(&cds, &rcds); isc_buffer_allocate(mctx, &newbuf2, rcds.length); diff --git a/bin/dnssec/dnssec-signzone.c b/bin/dnssec/dnssec-signzone.c index 68a6cc5c2c..4ceb483bde 100644 --- a/bin/dnssec/dnssec-signzone.c +++ b/bin/dnssec/dnssec-signzone.c @@ -348,9 +348,12 @@ iszsk(dns_dnsseckey_t *key) { */ static dns_dnsseckey_t * keythatsigned_unlocked(dns_rdata_rrsig_t *rrsig) { + dst_algorithm_t algorithm = dst_algorithm_fromdata( + rrsig->algorithm, rrsig->signature, rrsig->siglen); + ISC_LIST_FOREACH (keylist, key, link) { if (rrsig->keyid == dst_key_id(key->key) && - rrsig->algorithm == dst_key_alg(key->key) && + algorithm == dst_key_alg(key->key) && dns_name_equal(&rrsig->signer, dst_key_name(key->key))) { return key; @@ -1070,7 +1073,7 @@ loadds(dns_name_t *name, uint32_t ttl, dns_rdataset_t *dsset) { dns_rdata_t ds = DNS_RDATA_INIT; dns_rdataset_current(&keyset, &key); result = dns_ds_buildrdata(name, &key, DNS_DSDIGEST_SHA256, - dsbuf, &ds); + dsbuf, sizeof(dsbuf), &ds); check_result(result, "dns_ds_buildrdata"); dns_difftuple_create(mctx, DNS_DIFFOP_ADDRESIGN, name, ttl, &ds, @@ -3052,7 +3055,7 @@ writeset(const char *prefix, dns_rdatatype_t type) { if (type != dns_rdatatype_dnskey) { result = dns_ds_buildrdata(gorigin, &rdata, DNS_DSDIGEST_SHA256, dsbuf, - &ds); + sizeof(dsbuf), &ds); check_result(result, "dns_ds_buildrdata"); dns_difftuple_create(mctx, DNS_DIFFOP_ADDRESIGN, name, 0, &ds, &tuple); diff --git a/bin/dnssec/dnssectool.c b/bin/dnssec/dnssectool.c index cb42bb710a..88c398400b 100644 --- a/bin/dnssec/dnssectool.c +++ b/bin/dnssec/dnssectool.c @@ -120,9 +120,11 @@ void sig_format(dns_rdata_rrsig_t *sig, char *cp, unsigned int size) { char namestr[DNS_NAME_FORMATSIZE]; char algstr[DNS_NAME_FORMATSIZE]; + dst_algorithm_t algorithm = dst_algorithm_fromdata( + sig->algorithm, sig->signature, sig->siglen); dns_name_format(&sig->signer, namestr, sizeof(namestr)); - dns_secalg_format(sig->algorithm, algstr, sizeof(algstr)); + dst_algorithm_format(algorithm, algstr, sizeof(algstr)); snprintf(cp, size, "%s/%s/%d", namestr, algstr, sig->keyid); } @@ -361,7 +363,10 @@ strtodsdigest(const char *str) { r.length = strlen(str); result = dns_dsdigest_fromtext(&alg, &r); if (result != ISC_R_SUCCESS) { - fatal("unknown DS algorithm %s", str); + fatal("unknown DS digest %s", str); + } + if (!dst_ds_digest_supported(alg)) { + fatal("unsupported DS digest %s", str); } return alg; } diff --git a/bin/named/main.c b/bin/named/main.c index fb8062588d..903598e707 100644 --- a/bin/named/main.c +++ b/bin/named/main.c @@ -424,7 +424,7 @@ list_dnssec_algorithms(isc_buffer_t *b) { } if (dst_algorithm_supported(i)) { isc_buffer_putstr(b, " "); - (void)dns_secalg_totext(i, b); + dst_algorithm_totext(i, b); } } } diff --git a/bin/named/server.c b/bin/named/server.c index db395e20ef..6cbfc245c8 100644 --- a/bin/named/server.c +++ b/bin/named/server.c @@ -154,11 +154,11 @@ #endif /* HAVE_LMDB */ #ifndef SIZE_MAX -#define SIZE_MAX ((size_t)-1) +#define SIZE_MAX ((size_t)(-1)) #endif /* ifndef SIZE_MAX */ #ifndef SIZE_AS_PERCENT -#define SIZE_AS_PERCENT ((size_t)-2) +#define SIZE_AS_PERCENT ((size_t)(-2)) #endif /* ifndef SIZE_AS_PERCENT */ /* RFC7828 defines timeout as 16-bit value specified in units of 100 @@ -677,7 +677,7 @@ cleanup: static isc_result_t ta_fromconfig(const cfg_obj_t *key, bool *initialp, const char **namestrp, - unsigned char *digest, dns_rdata_ds_t *ds) { + unsigned char *digest, size_t digest_len, dns_rdata_ds_t *ds) { isc_result_t result; dns_rdata_dnskey_t keystruct; dns_rdata_t rdata = DNS_RDATA_INIT; @@ -699,6 +699,7 @@ ta_fromconfig(const cfg_obj_t *key, bool *initialp, const char **namestrp, STATIC_DS, TRUSTED } anchortype; + dst_algorithm_t algorithm; REQUIRE(namestrp != NULL && *namestrp == NULL); REQUIRE(ds != NULL); @@ -787,22 +788,24 @@ ta_fromconfig(const cfg_obj_t *key, bool *initialp, const char **namestrp, keystruct.flags = (uint16_t)rdata1; keystruct.protocol = (uint8_t)rdata2; keystruct.algorithm = (uint8_t)rdata3; - - if (!dst_algorithm_supported(keystruct.algorithm)) { - CHECK(DST_R_UNSUPPORTEDALG); - } - 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; + algorithm = dst_algorithm_fromdata( + keystruct.algorithm, keystruct.data, keystruct.datalen); + + if (!dst_algorithm_supported(algorithm)) { + CHECK(DST_R_UNSUPPORTEDALG); + } + CHECK(dns_rdata_fromstruct(&rdata, keystruct.common.rdclass, keystruct.common.rdtype, &keystruct, &rrdatabuf)); CHECK(dns_ds_fromkeyrdata(name, &rdata, DNS_DSDIGEST_SHA256, - digest, ds)); + digest, digest_len, ds)); break; case INIT_DS: @@ -841,6 +844,20 @@ ta_fromconfig(const cfg_obj_t *key, bool *initialp, const char **namestrp, CHECK(ISC_R_UNEXPECTEDEND); } break; +#if defined(DNS_DSDIGEST_SHA256PRIVATE) + case DNS_DSDIGEST_SHA256PRIVATE: + if (r.length < ISC_SHA256_DIGESTLENGTH) { + CHECK(ISC_R_UNEXPECTEDEND); + } + break; +#endif +#if defined(DNS_DSDIGEST_SHA384PRIVATE) + case DNS_DSDIGEST_SHA384PRIVATE: + if (r.length < ISC_SHA384_DIGESTLENGTH) { + CHECK(ISC_R_UNEXPECTEDEND); + } + break; +#endif default: cfg_obj_log(key, ISC_LOG_ERROR, "key '%s': " @@ -851,10 +868,49 @@ ta_fromconfig(const cfg_obj_t *key, bool *initialp, const char **namestrp, break; } + if (r.length > digest_len) { + CHECK(ISC_R_NOSPACE); + } ds->length = r.length; ds->digest = digest; memmove(ds->digest, r.base, r.length); + algorithm = ds->algorithm; + +#if defined(DNS_DSDIGEST_SHA256PRIVATE) && defined(DNS_DSDIGEST_SHA384PRIVATE) + /* + * Extract the private algorithm from the start + * of the hash field. + */ + switch (ds->digest_type) { + /* + * Digest types that do not encode the private DNSSEC algorithm + * at the start of the digest field. + */ + case DNS_DSDIGEST_SHA1: + case DNS_DSDIGEST_SHA256: + case DNS_DSDIGEST_SHA384: + break; + /* + * Digest types that encode the private DNSSEC algorithm + * at the start of the digest field. + */ + case DNS_DSDIGEST_SHA256PRIVATE: + case DNS_DSDIGEST_SHA384PRIVATE: + algorithm = dst_algorithm_fromdata( + ds->algorithm, ds->digest, ds->length); + break; + /* + * Unknown digest types. + */ + default: + break; + } +#endif + if (!dst_algorithm_supported(algorithm)) { + CHECK(DST_R_UNSUPPORTEDALG); + } + break; default: @@ -894,10 +950,11 @@ process_key(const cfg_obj_t *key, dns_keytable_t *secroots, dns_rdata_ds_t ds; isc_result_t result; bool initializing = managed; - unsigned char digest[ISC_MAX_MD_SIZE]; + unsigned char digest[DNS_DS_BUFFERSIZE]; isc_buffer_t b; - result = ta_fromconfig(key, &initializing, &namestr, digest, &ds); + result = ta_fromconfig(key, &initializing, &namestr, digest, + sizeof(digest), &ds); switch (result) { case ISC_R_SUCCESS: @@ -950,7 +1007,7 @@ process_key(const cfg_obj_t *key, dns_keytable_t *secroots, * warning, but do not prevent further keys from being processed. */ if (!dns_resolver_algorithm_supported(view->resolver, keyname, - ds.algorithm)) + ds.algorithm, NULL, 0)) { cfg_obj_log(key, ISC_LOG_WARNING, "ignoring %s for '%s': algorithm is disabled", @@ -1592,17 +1649,11 @@ disable_algorithms(const cfg_obj_t *disabled, dns_resolver_t *resolver) { algorithms = cfg_tuple_get(disabled, "algorithms"); CFG_LIST_FOREACH (algorithms, element) { isc_textregion_t r; - dns_secalg_t alg; + dst_algorithm_t alg; r.base = UNCONST(cfg_obj_asstring(cfg_listelt_value(element))); r.length = strlen(r.base); - - result = dns_secalg_fromtext(&alg, &r); - if (result != ISC_R_SUCCESS) { - uint8_t ui; - result = isc_parse_uint8(&ui, r.base, 10); - alg = ui; - } + result = dst_algorithm_fromtext(&alg, &r); if (result != ISC_R_SUCCESS) { cfg_obj_log(cfg_listelt_value(element), ISC_LOG_ERROR, "invalid algorithm"); @@ -14358,7 +14409,7 @@ named_server_dnssec(named_server_t *server, isc_lex_t *lex, /* variables for -key */ bool use_keyid = false; dns_keytag_t keyid = 0; - uint8_t algorithm = 0; + dst_algorithm_t algorithm = 0; /* variables for -status */ bool status = false; char output[4096]; @@ -14414,7 +14465,7 @@ named_server_dnssec(named_server_t *server, isc_lex_t *lex, } alg.base = ptr; alg.length = strlen(alg.base); - result = dns_secalg_fromtext( + result = dst_algorithm_fromtext( &algorithm, (isc_textregion_t *)&alg); if (result != ISC_R_SUCCESS) { msg = "Bad algorithm"; diff --git a/bin/tests/system/autosign/tests.sh b/bin/tests/system/autosign/tests.sh index 14064affe2..298de9196c 100755 --- a/bin/tests/system/autosign/tests.sh +++ b/bin/tests/system/autosign/tests.sh @@ -31,12 +31,13 @@ showprivate() { $DIG $DIGOPTS +nodnssec +short @$2 -t ${4:-type65534} $1 | cut -f3 -d' ' \ | while read record; do $PERL -e 'my $rdata = pack("H*", @ARGV[0]); - die "invalid record" unless length($rdata) == 5; - my ($alg, $key, $remove, $complete) = unpack("CnCC", $rdata); + die "invalid record" unless length($rdata) == 5 || length($rdata) == 7; + my ($dns, $key, $remove, $complete, $alg) = unpack("CnCCn", $rdata); my $action = "signing"; $action = "removing" if $remove; my $state = " (incomplete)"; $state = " (complete)" if $complete; + $alg = $dns if ! defined($alg); print ("$action: alg: $alg, key: $key$state\n");' $record done } diff --git a/bin/tests/system/conf.sh b/bin/tests/system/conf.sh index fa5b17b703..dddf5dac20 100644 --- a/bin/tests/system/conf.sh +++ b/bin/tests/system/conf.sh @@ -211,10 +211,19 @@ private_type_record() { _zone=$1 _algorithm=$2 _keyfile=$3 + _secalg=$2 + case $_secalg in + 256) _secalg=254 ;; # RSASHA256OID + 257) _secalg=254 ;; # RSASHA512OID + esac _id=$(keyfile_to_key_id "$_keyfile") - printf "%s. 0 IN TYPE65534 %s 5 %02x%04x0000\n" "$_zone" "\\#" "$_algorithm" "$_id" + if test "$_algorithm" -lt 256; then + printf "%s. 0 IN TYPE65534 %s 5 %02x%04x0000\n" "$_zone" "\\#" "$_secalg" "$_id" + else + printf "%s. 0 IN TYPE65534 %s 7 %02x%04x0000%04x\n" "$_zone" "\\#" "$_secalg" "$_id" "$_algorithm" + fi } # nextpart*() - functions for reading files incrementally @@ -518,13 +527,16 @@ copy_setports() { -e "s/@CONTROLPORT@/${CONTROLPORT}/g" \ -e "s/@DEFAULT_ALGORITHM@/${DEFAULT_ALGORITHM}/g" \ -e "s/@DEFAULT_ALGORITHM_NUMBER@/${DEFAULT_ALGORITHM_NUMBER}/g" \ + -e "s/@DEFAULT_ALGORITHM_DST_NUMBER@/${DEFAULT_ALGORITHM_DST_NUMBER}/g" \ -e "s/@DEFAULT_BITS@/${DEFAULT_BITS}/g" \ -e "s/@ALTERNATIVE_ALGORITHM@/${ALTERNATIVE_ALGORITHM}/g" \ -e "s/@ALTERNATIVE_ALGORITHM_NUMBER@/${ALTERNATIVE_ALGORITHM_NUMBER}/g" \ + -e "s/@ALTERNATIVE_ALGORITHM_DST_NUMBER@/${ALTERNATIVE_ALGORITHM_DST_NUMBER}/g" \ -e "s/@ALTERNATIVE_BITS@/${ALTERNATIVE_BITS}/g" \ -e "s/@DEFAULT_HMAC@/${DEFAULT_HMAC}/g" \ -e "s/@DISABLED_ALGORITHM@/${DISABLED_ALGORITHM}/g" \ -e "s/@DISABLED_ALGORITHM_NUMBER@/${DISABLED_ALGORITHM_NUMBER}/g" \ + -e "s/@DISABLED_ALGORITHM_NUMBER@/${DISABLED_ALGORITHM_DST_NUMBER}/g" \ -e "s/@DISABLED_BITS@/${DISABLED_BITS}/g" \ $1 >$2 } diff --git a/bin/tests/system/dnssec/ns2/example.db.in b/bin/tests/system/dnssec/ns2/example.db.in index a7ec0e471c..1efb1755b4 100644 --- a/bin/tests/system/dnssec/ns2/example.db.in +++ b/bin/tests/system/dnssec/ns2/example.db.in @@ -181,3 +181,21 @@ rsasha1-1024 NS ns.rsasha1-1024 ns.rsasha1-1024 A 10.53.0.3 dname-at-apex-nsec3 NS ns3 + +rsasha256oid NS ns.rsasha256oid +ns.rsasha256oid A 10.53.0.3 + +rsasha512oid NS ns.rsasha512oid +ns.rsasha512oid A 10.53.0.3 + +unknownoid NS ns.unknownoid +ns.unknownoid A 10.53.0.3 + +extradsoid NS ns.extradsoid +ns.extradsoid A 10.53.0.3 + +extradsunknownoid NS ns.extradsunknownoid +ns.extradsunknownoid A 10.53.0.3 + +extended-ds-unknown-oid NS ns.extended-ds-unknown-oid +ns.extended-ds-unknown-oid A 10.53.0.3 diff --git a/bin/tests/system/dnssec/ns2/sign.sh b/bin/tests/system/dnssec/ns2/sign.sh index 08f4d89e27..2658fd10b3 100644 --- a/bin/tests/system/dnssec/ns2/sign.sh +++ b/bin/tests/system/dnssec/ns2/sign.sh @@ -65,7 +65,9 @@ for subdomain in digest-alg-unsupported ds-unsupported secure badds \ ttlpatch split-dnssec split-smart expired expiring upper lower \ dnskey-unknown dnskey-unsupported dnskey-unsupported-2 \ dnskey-nsec3-unknown managed-future future revkey \ - dname-at-apex-nsec3 occluded rsasha1 rsasha1-1024; do + dname-at-apex-nsec3 occluded rsasha1 rsasha1-1024 \ + rsasha256oid rsasha512oid unknownoid extradsoid extradsunknownoid \ + extended-ds-unknown-oid; do cp "../ns3/dsset-$subdomain.example." . done @@ -87,7 +89,7 @@ zonefiletmp=$(mktemp "$zonefile.XXXXXX") || exit 1 | awk ' tolower($1) == "bad-cname.example." && $4 == "RRSIG" && $5 == "CNAME" { for (i = 1; i <= NF; i++ ) { - if (i <= 12) { + if (i <= 13) { printf("%s ", $i); continue; } @@ -106,7 +108,7 @@ tolower($1) == "bad-cname.example." && $4 == "RRSIG" && $5 == "CNAME" { tolower($1) == "bad-dname.example." && $4 == "RRSIG" && $5 == "DNAME" { for (i = 1; i <= NF; i++ ) { - if (i <= 12) { + if (i <= 13) { printf("%s ", $i); continue; } diff --git a/bin/tests/system/dnssec/ns3/extended-ds-unknown-oid.example.db.in b/bin/tests/system/dnssec/ns3/extended-ds-unknown-oid.example.db.in new file mode 100644 index 0000000000..f6c4fabdd3 --- /dev/null +++ b/bin/tests/system/dnssec/ns3/extended-ds-unknown-oid.example.db.in @@ -0,0 +1,28 @@ +; Copyright (C) Internet Systems Consortium, Inc. ("ISC") +; +; SPDX-License-Identifier: MPL-2.0 +; +; 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 https://mozilla.org/MPL/2.0/. +; +; See the COPYRIGHT file distributed with this work for additional +; information regarding copyright ownership. + +$TTL 300 ; 5 minutes +@ IN SOA mname1. . ( + 2009102722 ; serial + 20 ; refresh (20 seconds) + 20 ; retry (20 seconds) + 1814400 ; expire (3 weeks) + 3600 ; minimum (1 hour) + ) + NS ns +ns A 10.53.0.3 + +a A 10.0.0.1 +b A 10.0.0.2 +d A 10.0.0.4 +z A 10.0.0.26 +a.a.a.a.a.a.a.a.a.a.e A 10.0.0.27 +x CNAME a diff --git a/bin/tests/system/dnssec/ns3/extradsoid.example.db.in b/bin/tests/system/dnssec/ns3/extradsoid.example.db.in new file mode 100644 index 0000000000..f6c4fabdd3 --- /dev/null +++ b/bin/tests/system/dnssec/ns3/extradsoid.example.db.in @@ -0,0 +1,28 @@ +; Copyright (C) Internet Systems Consortium, Inc. ("ISC") +; +; SPDX-License-Identifier: MPL-2.0 +; +; 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 https://mozilla.org/MPL/2.0/. +; +; See the COPYRIGHT file distributed with this work for additional +; information regarding copyright ownership. + +$TTL 300 ; 5 minutes +@ IN SOA mname1. . ( + 2009102722 ; serial + 20 ; refresh (20 seconds) + 20 ; retry (20 seconds) + 1814400 ; expire (3 weeks) + 3600 ; minimum (1 hour) + ) + NS ns +ns A 10.53.0.3 + +a A 10.0.0.1 +b A 10.0.0.2 +d A 10.0.0.4 +z A 10.0.0.26 +a.a.a.a.a.a.a.a.a.a.e A 10.0.0.27 +x CNAME a diff --git a/bin/tests/system/dnssec/ns3/extradsunknownoid.example.db.in b/bin/tests/system/dnssec/ns3/extradsunknownoid.example.db.in new file mode 100644 index 0000000000..f6c4fabdd3 --- /dev/null +++ b/bin/tests/system/dnssec/ns3/extradsunknownoid.example.db.in @@ -0,0 +1,28 @@ +; Copyright (C) Internet Systems Consortium, Inc. ("ISC") +; +; SPDX-License-Identifier: MPL-2.0 +; +; 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 https://mozilla.org/MPL/2.0/. +; +; See the COPYRIGHT file distributed with this work for additional +; information regarding copyright ownership. + +$TTL 300 ; 5 minutes +@ IN SOA mname1. . ( + 2009102722 ; serial + 20 ; refresh (20 seconds) + 20 ; retry (20 seconds) + 1814400 ; expire (3 weeks) + 3600 ; minimum (1 hour) + ) + NS ns +ns A 10.53.0.3 + +a A 10.0.0.1 +b A 10.0.0.2 +d A 10.0.0.4 +z A 10.0.0.26 +a.a.a.a.a.a.a.a.a.a.e A 10.0.0.27 +x CNAME a diff --git a/bin/tests/system/dnssec/ns3/named.conf.in b/bin/tests/system/dnssec/ns3/named.conf.in index c7f76b4638..a9a1b207bf 100644 --- a/bin/tests/system/dnssec/ns3/named.conf.in +++ b/bin/tests/system/dnssec/ns3/named.conf.in @@ -429,11 +429,41 @@ zone "rsasha1-1024.example" { file "rsasha1-1024.example.db"; }; +zone "rsasha256oid.example" { + type primary; + file "rsasha256oid.example.db.signed"; +}; + +zone "rsasha512oid.example" { + type primary; + file "rsasha512oid.example.db.signed"; +}; + +zone "unknownoid.example" { + type primary; + file "unknownoid.example.db.signed"; +}; + zone "target.peer-ns-spoof" { type primary; file "target.peer-ns-spoof.db.signed"; }; +zone "extradsoid.example" { + type primary; + file "extradsoid.example.db.signed"; +}; + +zone "extradsunknownoid.example" { + type primary; + file "extradsunknownoid.example.db.signed"; +}; + +zone "extended-ds-unknown-oid.example" { + type primary; + file "extended-ds-unknown-oid.example.db.signed"; +}; + dnssec-policy "siginterval1" { keys { ksk key-directory lifetime unlimited algorithm @DEFAULT_ALGORITHM@; diff --git a/bin/tests/system/dnssec/ns3/rsasha256oid.example.db.in b/bin/tests/system/dnssec/ns3/rsasha256oid.example.db.in new file mode 100644 index 0000000000..f6c4fabdd3 --- /dev/null +++ b/bin/tests/system/dnssec/ns3/rsasha256oid.example.db.in @@ -0,0 +1,28 @@ +; Copyright (C) Internet Systems Consortium, Inc. ("ISC") +; +; SPDX-License-Identifier: MPL-2.0 +; +; 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 https://mozilla.org/MPL/2.0/. +; +; See the COPYRIGHT file distributed with this work for additional +; information regarding copyright ownership. + +$TTL 300 ; 5 minutes +@ IN SOA mname1. . ( + 2009102722 ; serial + 20 ; refresh (20 seconds) + 20 ; retry (20 seconds) + 1814400 ; expire (3 weeks) + 3600 ; minimum (1 hour) + ) + NS ns +ns A 10.53.0.3 + +a A 10.0.0.1 +b A 10.0.0.2 +d A 10.0.0.4 +z A 10.0.0.26 +a.a.a.a.a.a.a.a.a.a.e A 10.0.0.27 +x CNAME a diff --git a/bin/tests/system/dnssec/ns3/rsasha512oid.example.db.in b/bin/tests/system/dnssec/ns3/rsasha512oid.example.db.in new file mode 100644 index 0000000000..f6c4fabdd3 --- /dev/null +++ b/bin/tests/system/dnssec/ns3/rsasha512oid.example.db.in @@ -0,0 +1,28 @@ +; Copyright (C) Internet Systems Consortium, Inc. ("ISC") +; +; SPDX-License-Identifier: MPL-2.0 +; +; 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 https://mozilla.org/MPL/2.0/. +; +; See the COPYRIGHT file distributed with this work for additional +; information regarding copyright ownership. + +$TTL 300 ; 5 minutes +@ IN SOA mname1. . ( + 2009102722 ; serial + 20 ; refresh (20 seconds) + 20 ; retry (20 seconds) + 1814400 ; expire (3 weeks) + 3600 ; minimum (1 hour) + ) + NS ns +ns A 10.53.0.3 + +a A 10.0.0.1 +b A 10.0.0.2 +d A 10.0.0.4 +z A 10.0.0.26 +a.a.a.a.a.a.a.a.a.a.e A 10.0.0.27 +x CNAME a diff --git a/bin/tests/system/dnssec/ns3/sign.sh b/bin/tests/system/dnssec/ns3/sign.sh index 8f52e1f514..8cf46a2f06 100644 --- a/bin/tests/system/dnssec/ns3/sign.sh +++ b/bin/tests/system/dnssec/ns3/sign.sh @@ -424,6 +424,146 @@ cat "$infile" "$keyname.key" >"$zonefile" "$SIGNER" -P -o "$zone" "$zonefile" >/dev/null +# +# A RSASHA256OID zone. +# +zone=rsasha256oid.example. +infile=rsasha256oid.example.db.in +zonefile=rsasha256oid.example.db + +keyname=$("$KEYGEN" -q -a RSASHA256OID "$zone") + +cat "$infile" "$keyname.key" >"$zonefile" + +"$SIGNER" -z -o "$zone" "$zonefile" >/dev/null + +# +# A RSASHA512OID zone. +# +zone=rsasha512oid.example. +infile=rsasha512oid.example.db.in +zonefile=rsasha512oid.example.db + +keyname=$("$KEYGEN" -q -a RSASHA512OID "$zone") + +cat "$infile" "$keyname.key" >"$zonefile" + +"$SIGNER" -z -o "$zone" "$zonefile" >/dev/null + +# +# A UNKNOWNOID zone. Sign the zone using RSASHA512OID then +# update the OID in the DNSKEY and RRSIGS to the unknown OID +# 1.2.840.113549.1.1.14 +# +zone=unknownoid.example +infile=unknownoid.example.db.in +zonefile=unknownoid.example.db + +keyname=$("$KEYGEN" -q -a RSASHA512OID "$zone") + +cat "$infile" "$keyname.key" >"$zonefile" + +# Sign with known OID RSASHA512OID +"$SIGNER" -z -o "$zone" -f "${zonefile}.stage1" "$zonefile" >/dev/null + +# Change OID from 1.2.840.113549.1.1.13 to 1.2.840.113549.1.1.14 +sed 's/CwYJKoZIhvcN/CwYJKoZIhvcO/' <"${zonefile}.stage1" >"${zonefile}.stage2" + +"$DSFROMKEY" -2A -f "${zonefile}.stage2" "$zone" >"dsset-${zone}." + +# extract the updated DNSKEY's tag +tag=$(awk '{print $4}' "dsset-${zone}.") + +# Update RRSIG tags +sed "s/\(2[0-9]* 2[0-9]*\) [1-9][0-9]* unknownoid.example./\1 ${tag} unknownoid.example./" <"${zonefile}.stage2" >"${zonefile}.signed" + +# +# A PRIVATEOID zone with a extra DS record for a non-existent DNSKEY. +# +zone=extradsoid.example. +infile=extradsoid.example.db.in +zonefile=extradsoid.example.db + +keyname=$("$KEYGEN" -q -a RSASHA512OID "$zone") + +cat "$infile" "$keyname.key" >"$zonefile" + +"$SIGNER" -z -o "$zone" "$zonefile" >/dev/null + +# add a DS for a second key with the same algorithm +keyname=$("$KEYGEN" -q -a RSASHA512OID "$zone") + +"$DSFROMKEY" -2A "$keyname.key" >>"dsset-$zone" + +# +# A UNKNOWNOID with an extra DS zone. Sign the zone using RSASHA512OID +# then update the OID in the DNSKEY and RRSIGS to the unknown OID +# 1.2.840.113549.1.1.14. Add an additional DS which does not match +# the DNSKEY RRset with using this unknown OID. +# +zone=extradsunknownoid.example +infile=extradsunknownoid.example.db.in +zonefile=extradsunknownoid.example.db + +keyname=$("$KEYGEN" -q -a RSASHA512OID "$zone") + +cat "$infile" "$keyname.key" >"$zonefile" + +# Sign with known OID RSASHA512OID +"$SIGNER" -z -o "$zone" -f "${zonefile}.stage1" "$zonefile" >/dev/null + +# Change OID from 1.2.840.113549.1.1.13 to 1.2.840.113549.1.1.14 +sed 's/CwYJKoZIhvcN/CwYJKoZIhvcO/' <"${zonefile}.stage1" >"${zonefile}.stage2" + +"$DSFROMKEY" -2A -f "${zonefile}.stage2" "$zone" >"dsset-${zone}." +tag=$(awk '{print $4}' "dsset-${zone}.") + +# Update RRSIG tags +sed "s/\(2[0-9]* 2[0-9]*\) [1-9][0-9]* extradsunknownoid.example./\1 ${tag} extradsunknownoid.example./" <"${zonefile}.stage2" >"${zonefile}.signed" + +# add a DS for a second key with the same algorithm +keyname=$("$KEYGEN" -L 300 -q -a RSASHA512OID "$zone") + +# Change OID from 1.2.840.113549.1.1.13 to 1.2.840.113549.1.1.14 and +# add the resulting DS to the dsset. +sed 's/CwYJKoZIhvcN/CwYJKoZIhvcO/' <"$keyname.key" | "$DSFROMKEY" -2A -f - "$zone" >>"dsset-${zone}." + +# +# A UNKNOWNOID with an extra DS zone. Sign the zone using RSASHA512OID +# then update the OID in the DNSKEY and RRSIGS to the unknown OID +# 1.2.840.113549.1.1.14. Add an additional DS with an extended digest +# type that encoded the DNSKEY's private type identifier which does not +# match the DNSKEY RRset with using this unknown OID. +# +zone=extended-ds-unknown-oid.example +infile=extended-ds-unknown-oid.example.db.in +zonefile=extended-ds-unknown-oid.example.db + +keyname=$("$KEYGEN" -q -a RSASHA512OID "$zone") + +cat "$infile" "$keyname.key" >"$zonefile" + +# Sign with known OID RSASHA512OID +"$SIGNER" -z -o "$zone" -f "${zonefile}.stage1" "$zonefile" >/dev/null + +# Change OID from 1.2.840.113549.1.1.13 to 1.2.840.113549.1.1.14 +sed 's/CwYJKoZIhvcN/CwYJKoZIhvcO/' <"${zonefile}.stage1" >"${zonefile}.stage2" + +"$DSFROMKEY" -2A -f "${zonefile}.stage2" "$zone" >"dsset-${zone}." +tag=$(awk '{print $4}' "dsset-${zone}.") + +# Update RRSIG tags +sed "s/\(2[0-9]* 2[0-9]*\) [1-9][0-9]* ${zone}./\1 ${tag} ${zone}./" <"${zonefile}.stage2" >"${zonefile}.signed" + +if $FEATURETEST --extended-ds-digest; then + # add a DS for a second key with the same algorithm + keyname=$("$KEYGEN" -L 300 -q -a RSASHA512OID "$zone") + + # Change OID from 1.2.840.113549.1.1.13 to 1.2.840.113549.1.1.14 and + # add the resulting DS using digest type SHA-256-PRIVATE to the dsset. + sed 's/CwYJKoZIhvcN/CwYJKoZIhvcO/' <"$keyname.key" | "$DSFROMKEY" -a SHA-256-PRIVATE -A -f - "$zone" >>"dsset-${zone}." +fi + # # A zone with the DNSKEY set only signed by the KSK # diff --git a/bin/tests/system/dnssec/ns3/unknownoid.example.db.in b/bin/tests/system/dnssec/ns3/unknownoid.example.db.in new file mode 100644 index 0000000000..f6c4fabdd3 --- /dev/null +++ b/bin/tests/system/dnssec/ns3/unknownoid.example.db.in @@ -0,0 +1,28 @@ +; Copyright (C) Internet Systems Consortium, Inc. ("ISC") +; +; SPDX-License-Identifier: MPL-2.0 +; +; 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 https://mozilla.org/MPL/2.0/. +; +; See the COPYRIGHT file distributed with this work for additional +; information regarding copyright ownership. + +$TTL 300 ; 5 minutes +@ IN SOA mname1. . ( + 2009102722 ; serial + 20 ; refresh (20 seconds) + 20 ; retry (20 seconds) + 1814400 ; expire (3 weeks) + 3600 ; minimum (1 hour) + ) + NS ns +ns A 10.53.0.3 + +a A 10.0.0.1 +b A 10.0.0.2 +d A 10.0.0.4 +z A 10.0.0.26 +a.a.a.a.a.a.a.a.a.a.e A 10.0.0.27 +x CNAME a diff --git a/bin/tests/system/dnssec/tests.sh b/bin/tests/system/dnssec/tests.sh index 845adc306d..86441c6f94 100644 --- a/bin/tests/system/dnssec/tests.sh +++ b/bin/tests/system/dnssec/tests.sh @@ -53,18 +53,20 @@ dnssec_loadkeys_on() { # convert private-type records to readable form showprivate() { echo "-- $* --" - dig_with_opts +nodnssec +short "@$2" -t type65534 "$1" | cut -f3 -d' ' \ - | while read -r record; do - # shellcheck disable=SC2016 - $PERL -e 'my $rdata = pack("H*", @ARGV[0]); - die "invalid record" unless length($rdata) == 5; - my ($alg, $key, $remove, $complete) = unpack("CnCC", $rdata); - my $action = "signing"; - $action = "removing" if $remove; - my $state = " (incomplete)"; - $state = " (complete)" if $complete; - print ("$action: alg: $alg, key: $key$state\n");' "$record" - done + dig_with_opts +nodnssec +short "@$2" -t type65534 "$1" >dig.out.$1.test$n + cut -f3 -d' ' dig.out.ns3.test$n || ret=1 +dig_with_opts +noauth a.rsasha256oid.example. \ + @10.53.0.4 a >dig.out.ns4.test$n || ret=1 +digcomp dig.out.ns3.test$n dig.out.ns4.test$n || ret=1 +grep "flags:.*ad.*QUERY" dig.out.ns4.test$n >/dev/null || ret=1 +n=$((n + 1)) +test "$ret" -eq 0 || echo_i "failed" +status=$((status + ret)) + +if [ -x "${DELV}" ]; then + ret=0 + echo_i "checking positive validation NSEC3 using dns_client ($n)" + delv_with_opts @10.53.0.4 a a.nsec3.example >delv.out$n || ret=1 + grep "a.nsec3.example..*10.0.0.1" delv.out$n >/dev/null || ret=1 + grep "a.nsec3.example..*RRSIG.A [0-9][0-9]* 3 300.*" delv.out$n >/dev/null || ret=1 + n=$((n + 1)) + test "$ret" -eq 0 || echo_i "failed" + status=$((status + ret)) +fi + +echo_i "checking positive validation with unknown private algorithm works ($n)" +ret=0 +dig_with_opts +noauth a.unknownoid.example. \ + @10.53.0.3 a >dig.out.ns3.test$n || ret=1 +dig_with_opts +noauth a.unknownoid.example. \ + @10.53.0.4 a >dig.out.ns4.test$n || ret=1 +digcomp dig.out.ns3.test$n dig.out.ns4.test$n || ret=1 +grep "flags:.*ad.*QUERY" dig.out.ns4.test$n >/dev/null && ret=1 +n=$((n + 1)) +test "$ret" -eq 0 || echo_i "failed" +status=$((status + ret)) + +echo_i "checking positive validation with extra ds for private algorithm ($n)" +ret=0 +dig_with_opts +noauth a.extradsoid.example. \ + @10.53.0.3 a >dig.out.ns3.test$n || ret=1 +dig_with_opts +noauth a.extradsoid.example. \ + @10.53.0.4 a >dig.out.ns4.test$n || ret=1 +digcomp dig.out.ns3.test$n dig.out.ns4.test$n || ret=1 +grep "flags:.*ad.*QUERY" dig.out.ns4.test$n >/dev/null || ret=1 +n=$((n + 1)) +test "$ret" -eq 0 || echo_i "failed" +status=$((status + ret)) + +echo_i "checking positive validation with extra ds for unknown private algorithm fails ($n)" +ret=0 +dig_with_opts +noauth a.extradsunknownoid.example. \ + @10.53.0.3 a >dig.out.ns3.test$n || ret=1 +dig_with_opts +noauth a.extradsunknownoid.example. \ + @10.53.0.4 a >dig.out.ns4.test$n || ret=1 +grep "status: NOERROR" dig.out.ns3.test$n >/dev/null || ret=1 +grep "status: SERVFAIL" dig.out.ns4.test$n >/dev/null || ret=1 +grep 'No DNSKEY for extradsunknownoid.example/DS with PRIVATEOID algorithm, tag [1-9][0-9]*$' ns4/named.run >/dev/null || ret=1 +n=$((n + 1)) +test "$ret" -eq 0 || echo_i "failed" +status=$((status + ret)) + +if $FEATURETEST --extended-ds-digest; then + echo_i "checking positive validation with extra ds using extended digest type for unknown private algorithm succeeds ($n)" + ret=0 + dig_with_opts +noauth a.extended-ds-unknown-oid.example. \ + @10.53.0.3 a >dig.out.ns3.test$n || ret=1 + dig_with_opts +noauth a.extended-ds-unknown-oid.example. \ + @10.53.0.4 a >dig.out.ns4.test$n || ret=1 + digcomp dig.out.ns3.test$n dig.out.ns4.test$n || ret=1 + grep "flags:.*ad.*QUERY" dig.out.ns4.test$n >/dev/null && ret=1 + n=$((n + 1)) + test "$ret" -eq 0 || echo_i "failed" + status=$((status + ret)) +fi + # Check the bogus domain echo_i "checking failed validation ($n)" @@ -3479,7 +3555,7 @@ status=$((status + ret)) echo_i "check that 'dnssec-keygen -S' works for all supported algorithms ($n)" ret=0 alg=1 -until test $alg -eq 256; do +until test $alg -eq 258; do zone="keygen-$alg." case $alg in 2) # Diffie Helman @@ -3496,10 +3572,20 @@ until test $alg -eq 256; do 15 | 16) key1=$($KEYGEN -a "$alg" "$zone" 2>"keygen-$alg.err" || true) ;; + 256) + key1=$($KEYGEN -a "RSASHA256OID" "$zone" 2>"keygen-$alg.err" || true) + ;; + 257) + key1=$($KEYGEN -a "RSASHA512OID" "$zone" 2>"keygen-$alg.err" || true) + ;; *) key1=$($KEYGEN -a "$alg" "$zone" 2>"keygen-$alg.err" || true) ;; esac + if grep "unknown algorithm" "keygen-$alg.err" >/dev/null; then + alg=$((alg + 1)) + continue + fi if grep "unsupported algorithm" "keygen-$alg.err" >/dev/null; then alg=$((alg + 1)) continue diff --git a/bin/tests/system/dnssec/tests_sh_dnssec.py b/bin/tests/system/dnssec/tests_sh_dnssec.py index ac95282452..e4e0a085f5 100644 --- a/bin/tests/system/dnssec/tests_sh_dnssec.py +++ b/bin/tests/system/dnssec/tests_sh_dnssec.py @@ -84,6 +84,7 @@ pytestmark = pytest.mark.extra_artifacts( "ns3/auto-nsec3.example.db", "ns3/badds.example.db", "ns3/bogus.example.db", + "ns3/digest-alg-unsupported.example.db", "ns3/disabled.managed.db", "ns3/disabled.trusted.db", "ns3/dname-at-apex-nsec3.example.db", @@ -94,13 +95,20 @@ pytestmark = pytest.mark.extra_artifacts( "ns3/dnskey-unsupported-2.example.db", "ns3/dnskey-unsupported.example.db", "ns3/dnskey-unsupported.example.db.tmp", + "ns3/ds-unsupported.example.db", "ns3/dynamic.example.db", - "ns3/digest-alg-unsupported.example.db", "ns3/enabled.managed.db", "ns3/enabled.trusted.db", "ns3/example.bk", "ns3/expired.example.db", "ns3/expiring.example.db", + "ns3/extended-ds-unknown-oid.example.db", + "ns3/extended-ds-unknown-oid.example.db.stage1", + "ns3/extended-ds-unknown-oid.example.db.stage2", + "ns3/extradsoid.example.db", + "ns3/extradsunknownoid.example.db", + "ns3/extradsunknownoid.example.db.stage1", + "ns3/extradsunknownoid.example.db.stage2", "ns3/future.example.db", "ns3/keyless.example.db", "ns3/kskonly.example.db", @@ -123,7 +131,9 @@ pytestmark = pytest.mark.extra_artifacts( "ns3/revoked.trusted.db", "ns3/rfc2335.example.bk", "ns3/rsasha256.example.db", + "ns3/rsasha256oid.example.db", "ns3/rsasha512.example.db", + "ns3/rsasha512oid.example.db", "ns3/secure.below-cname.example.db", "ns3/secure.example.db", "ns3/secure.managed.db", @@ -138,6 +148,9 @@ pytestmark = pytest.mark.extra_artifacts( "ns3/trusted-future.key", "ns3/ttlpatch.example.db", "ns3/ttlpatch.example.db.patched", + "ns3/unknownoid.example.db", + "ns3/unknownoid.example.db.stage1", + "ns3/unknownoid.example.db.stage2", "ns3/unsupported.managed.db", "ns3/unsupported.managed.db.tmp", "ns3/unsupported.trusted.db", @@ -146,7 +159,6 @@ pytestmark = pytest.mark.extra_artifacts( "ns3/update-nsec3.example.db.signed", "ns3/upper.example.db", "ns3/upper.example.db.lower", - "ns3/ds-unsupported.example.db", "ns4/broken.conf", "ns4/managed.conf", "ns4/managed-keys.bind", diff --git a/bin/tests/system/feature-test.c b/bin/tests/system/feature-test.c index 8f58e133f1..73d0e73e66 100644 --- a/bin/tests/system/feature-test.c +++ b/bin/tests/system/feature-test.c @@ -30,6 +30,7 @@ #include #include +#include #include #include @@ -42,6 +43,7 @@ usage(void) { fprintf(stderr, "\t--edns-version\n"); fprintf(stderr, "\t--enable-dnstap\n"); fprintf(stderr, "\t--enable-querytrace\n"); + fprintf(stderr, "\t--extended-ds-digest\n"); fprintf(stderr, "\t--fips-provider\n"); fprintf(stderr, "\t--gethostname\n"); fprintf(stderr, "\t--gssapi\n"); @@ -93,6 +95,14 @@ main(int argc, char **argv) { #endif /* ifdef WANT_QUERYTRACE */ } + if (strcasecmp(argv[1], "--extended-ds-digest") == 0) { +#if defined(DNS_DSDIGEST_SHA256PRIVATE) && defined(DNS_DSDIGEST_SHA384PRIVATE) + return 0; +#else + return 1; +#endif + } + if (strcasecmp(argv[1], "--fips-provider") == 0) { #if OPENSSL_VERSION_NUMBER >= 0x30000000L OSSL_PROVIDER *fips = OSSL_PROVIDER_load(NULL, "fips"); diff --git a/bin/tests/system/isctest/kasp.py b/bin/tests/system/isctest/kasp.py index 1aec27c3f5..86e05ebac9 100644 --- a/bin/tests/system/isctest/kasp.py +++ b/bin/tests/system/isctest/kasp.py @@ -390,6 +390,14 @@ class Key: return ksigning, zsigning + def get_dnsalg(self) -> int: + alg = int(self.get_metadata("Algorithm")) + if alg == isctest.vars.algorithms.RSASHA256OID.dst: + return isctest.vars.algorithms.RSASHA256OID.number + if alg == isctest.vars.algorithms.RSASHA512OID.dst: + return isctest.vars.algorithms.RSASHA512OID.number + return alg + def ttl(self) -> int: with open(self.keyfile, "r", encoding="utf-8") as file: for line in file: @@ -810,7 +818,7 @@ def _check_signatures( offline_ksk=offline_ksk, zsk_missing=zsk_missing, smooth=smooth ) - alg = key.get_metadata("Algorithm") + alg = key.get_dnsalg() rtype = dns.rdatatype.to_text(covers) expect = rf"IN RRSIG {rtype} {alg} (\d) (\d+) (\d+) (\d+) {key.tag} {fqdn}" diff --git a/bin/tests/system/isctest/vars/algorithms.py b/bin/tests/system/isctest/vars/algorithms.py index 446ab09d69..58b5a9a652 100644 --- a/bin/tests/system/isctest/vars/algorithms.py +++ b/bin/tests/system/isctest/vars/algorithms.py @@ -30,17 +30,20 @@ ALG_VARS = { "ALGORITHM_SET": "none", "DEFAULT_ALGORITHM": "", "DEFAULT_ALGORITHM_NUMBER": "", + "DEFAULT_ALGORITHM_DST_NUMBER": "", "DEFAULT_BITS": "", # Alternative algorithm for test cases that require more than one algorithm # (for example algorithm rollover). Must be different from # DEFAULT_ALGORITHM. "ALTERNATIVE_ALGORITHM": "", "ALTERNATIVE_ALGORITHM_NUMBER": "", + "ALTERNATIVE_ALGORITHM_DST_NUMBER": "", "ALTERNATIVE_BITS": "", # Algorithm that is used for tests against the "disable-algorithms" # configuration option. Must be different from above algorithms. "DISABLED_ALGORITHM": "", "DISABLED_ALGORITHM_NUMBER": "", + "DISABLED_ALGORITHM_DST_NUMBER": "", "DISABLED_BITS": "", # Default HMAC algorithm. Must match the rndc configuration in # bin/tests/system/_common (rndc.conf, rndc.key) @@ -54,6 +57,7 @@ STABLE_PERIOD = 3600 * 3 class Algorithm(NamedTuple): name: str number: int + dst: int bits: int @@ -72,13 +76,15 @@ class AlgorithmSet(NamedTuple): "disable-algorithms" configuration option.""" -RSASHA1 = Algorithm("RSASHA1", 5, 2048) -RSASHA256 = Algorithm("RSASHA256", 8, 2048) -RSASHA512 = Algorithm("RSASHA512", 10, 2048) -ECDSAP256SHA256 = Algorithm("ECDSAP256SHA256", 13, 256) -ECDSAP384SHA384 = Algorithm("ECDSAP384SHA384", 14, 384) -ED25519 = Algorithm("ED25519", 15, 256) -ED448 = Algorithm("ED448", 16, 456) +RSASHA1 = Algorithm("RSASHA1", 5, 5, 2048) +RSASHA256 = Algorithm("RSASHA256", 8, 8, 2048) +RSASHA512 = Algorithm("RSASHA512", 10, 10, 2048) +ECDSAP256SHA256 = Algorithm("ECDSAP256SHA256", 13, 13, 256) +ECDSAP384SHA384 = Algorithm("ECDSAP384SHA384", 14, 14, 384) +ED25519 = Algorithm("ED25519", 15, 15, 256) +ED448 = Algorithm("ED448", 16, 16, 456) +RSASHA256OID = Algorithm("RSASHA256OID", 254, 256, 2048) +RSASHA512OID = Algorithm("RSASHA512OID", 254, 257, 2048) ALL_ALGORITHMS = [ RSASHA1, @@ -88,6 +94,8 @@ ALL_ALGORITHMS = [ ECDSAP384SHA384, ED25519, ED448, + RSASHA256OID, + RSASHA512OID, ] ALL_ALGORITHMS_BY_NUM = {alg.number: alg for alg in ALL_ALGORITHMS} @@ -149,6 +157,8 @@ CRYPTO_SUPPORTED_VARS = { "ECDSAP384SHA384_SUPPORTED": "0", "ED25519_SUPPORTED": "0", "ED448_SUPPORTED": "0", + "RSASHA256OID_SUPPORTED": "0", + "RSASHA512OID_SUPPORTED": "0", } SUPPORTED_ALGORITHMS: List[Algorithm] = [] @@ -250,6 +260,7 @@ def _algorithms_env(algs: AlgorithmSet, name: str) -> Dict[str, str]: def set_alg_env(alg: Algorithm, prefix): algs_env[f"{prefix}_ALGORITHM"] = alg.name algs_env[f"{prefix}_ALGORITHM_NUMBER"] = str(alg.number) + algs_env[f"{prefix}_ALGORITHM_DST_NUMBER"] = str(alg.dst) algs_env[f"{prefix}_BITS"] = str(alg.bits) assert isinstance(algs.default, Algorithm) diff --git a/bin/tests/system/ksr/tests_ksr.py b/bin/tests/system/ksr/tests_ksr.py index a5aefa0b21..fa09e85d84 100644 --- a/bin/tests/system/ksr/tests_ksr.py +++ b/bin/tests/system/ksr/tests_ksr.py @@ -105,7 +105,7 @@ def ksr(zone, policy, action, options="", raise_on_exception=True): def check_keys( keys, lifetime, - alg=os.environ["DEFAULT_ALGORITHM_NUMBER"], + alg=os.environ["DEFAULT_ALGORITHM_DST_NUMBER"], size=os.environ["DEFAULT_BITS"], offset=0, with_state=False, @@ -246,7 +246,7 @@ def check_rrsig_bundle(bundle_keys, bundle_lines, zone, rrtype, sigend, sigstart count = 0 for key in bundle_keys: found = False - alg = key.get_metadata("Algorithm") + alg = key.get_dnsalg() expect = f"{zone}. 3600 IN RRSIG {rrtype} {alg} 2 3600 {sigend} {sigstart} {key.tag} {zone}." # there must be a signature of this ksk for line in bundle_lines: @@ -1125,9 +1125,9 @@ def test_ksr_twotone(servers): ksks_altalg = [] for ksk in ksks: alg = ksk.get_metadata("Algorithm") - if alg == os.environ.get("DEFAULT_ALGORITHM_NUMBER"): + if alg == os.environ.get("DEFAULT_ALGORITHM_DST_NUMBER"): ksks_defalg.append(ksk) - elif alg == os.environ.get("ALTERNATIVE_ALGORITHM_NUMBER"): + elif alg == os.environ.get("ALTERNATIVE_ALGORITHM_DST_NUMBER"): ksks_altalg.append(ksk) assert len(ksks_defalg) == 1 @@ -1135,7 +1135,7 @@ def test_ksr_twotone(servers): check_keys(ksks_defalg, None) - alg = os.environ.get("ALTERNATIVE_ALGORITHM_NUMBER") + alg = os.environ.get("ALTERNATIVE_ALGORITHM_DST_NUMBER") size = os.environ.get("ALTERNATIVE_BITS") check_keys(ksks_altalg, None, alg, size) @@ -1154,9 +1154,9 @@ def test_ksr_twotone(servers): zsks_altalg = [] for zsk in zsks: alg = zsk.get_metadata("Algorithm") - if alg == os.environ.get("DEFAULT_ALGORITHM_NUMBER"): + if alg == os.environ.get("DEFAULT_ALGORITHM_DST_NUMBER"): zsks_defalg.append(zsk) - elif alg == os.environ.get("ALTERNATIVE_ALGORITHM_NUMBER"): + elif alg == os.environ.get("ALTERNATIVE_ALGORITHM_DST_NUMBER"): zsks_altalg.append(zsk) assert len(zsks_defalg) == 4 @@ -1165,7 +1165,7 @@ def test_ksr_twotone(servers): lifetime = timedelta(days=31 * 3) check_keys(zsks_defalg, lifetime) - alg = os.environ.get("ALTERNATIVE_ALGORITHM_NUMBER") + alg = os.environ.get("ALTERNATIVE_ALGORITHM_DST_NUMBER") size = os.environ.get("ALTERNATIVE_BITS") lifetime = timedelta(days=31 * 5) check_keys(zsks_altalg, lifetime, alg, size) @@ -1216,7 +1216,7 @@ def test_ksr_twotone(servers): lifetime = timedelta(days=31 * 3) check_keys(zsks_defalg, lifetime, with_state=True) - alg = os.environ.get("ALTERNATIVE_ALGORITHM_NUMBER") + alg = os.environ.get("ALTERNATIVE_ALGORITHM_DST_NUMBER") size = os.environ.get("ALTERNATIVE_BITS") lifetime = timedelta(days=31 * 5) check_keys(zsks_altalg, lifetime, alg, size, with_state=True) diff --git a/lib/dns/dnssec.c b/lib/dns/dnssec.c index 3632f86178..993168e3dc 100644 --- a/lib/dns/dnssec.c +++ b/lib/dns/dnssec.c @@ -224,7 +224,7 @@ dns_dnssec_sign(const dns_name_t *name, dns_rdataset_t *set, dst_key_t *key, dns_name_clone(dns_fixedname_name(&fsigner), &sig.signer); sig.covered = set->type; - sig.algorithm = dst_key_alg(key); + sig.algorithm = dst_algorithm_tosecalg(dst_key_alg(key)); sig.labels = dns_name_countlabels(name) - 1; if (dns_name_iswildcard(name)) { sig.labels--; @@ -762,7 +762,7 @@ dns_dnssec_signmessage(dns_message_t *msg, dst_key_t *key) { ISC_LINK_INIT(&sig.common, link); sig.covered = 0; - sig.algorithm = dst_key_alg(key); + sig.algorithm = dst_algorithm_tosecalg(dst_key_alg(key)); sig.labels = 0; /* the root name */ sig.originalttl = 0; @@ -1469,7 +1469,7 @@ mark_active_keys(dns_dnsseckeylist_t *keylist, dns_rdataset_t *rrsigs) { dns_rdataset_clone(rrsigs, &sigs); ISC_LIST_FOREACH (*keylist, key, link) { uint16_t keyid, sigid; - dns_secalg_t keyalg, sigalg; + dst_algorithm_t keyalg, sigalg; keyid = dst_key_id(key->key); keyalg = dst_key_alg(key->key); @@ -1480,7 +1480,8 @@ mark_active_keys(dns_dnsseckeylist_t *keylist, dns_rdataset_t *rrsigs) { dns_rdataset_current(&sigs, &rdata); result = dns_rdata_tostruct(&rdata, &sig, NULL); RUNTIME_CHECK(result == ISC_R_SUCCESS); - sigalg = sig.algorithm; + sigalg = dst_algorithm_fromdata( + sig.algorithm, sig.signature, sig.siglen); sigid = sig.keyid; if (keyid == sigid && keyalg == sigalg) { key->is_active = true; @@ -1544,14 +1545,23 @@ dns_dnssec_keylistfromrdataset(const dns_name_t *origin, dns_kasp_t *kasp, dns_rdataset_clone(keyset, &keys); DNS_RDATASET_FOREACH (&keys) { dns_rdata_t rdata = DNS_RDATA_INIT; + dst_algorithm_t algorithm; + dns_rdata_dnskey_t keystruct; + dns_rdataset_current(&keys, &rdata); REQUIRE(rdata.type == dns_rdatatype_key || rdata.type == dns_rdatatype_dnskey); REQUIRE(rdata.length > 3); + result = dns_rdata_tostruct(&rdata, &keystruct, NULL); + RUNTIME_CHECK(result == ISC_R_SUCCESS); + + algorithm = dst_algorithm_fromdata( + keystruct.algorithm, keystruct.data, keystruct.datalen); + /* Skip unsupported algorithms */ - if (!dst_algorithm_supported(rdata.data[3])) { + if (!dst_algorithm_supported(algorithm)) { goto skip; } @@ -1831,7 +1841,8 @@ add_cds(dns_dnsseckey_t *key, dns_rdata_t *keyrdata, const char *keystr, dns_rdata_t cdsrdata = DNS_RDATA_INIT; dns_name_t *origin = dst_key_name(key->key); - r = dns_ds_buildrdata(origin, keyrdata, digesttype, dsbuf, &cdsrdata); + r = dns_ds_buildrdata(origin, keyrdata, digesttype, dsbuf, + sizeof(dsbuf), &cdsrdata); if (r != ISC_R_SUCCESS) { char algbuf[DNS_DSDIGEST_FORMATSIZE]; dns_dsdigest_format(digesttype, algbuf, @@ -1866,7 +1877,8 @@ delete_cds(dns_dnsseckey_t *key, dns_rdata_t *keyrdata, const char *keystr, dns_rdata_t cdsrdata = DNS_RDATA_INIT; dns_name_t *origin = dst_key_name(key->key); - r = dns_ds_buildrdata(origin, keyrdata, digesttype, dsbuf, &cdsrdata); + r = dns_ds_buildrdata(origin, keyrdata, digesttype, dsbuf, + sizeof(dsbuf), &cdsrdata); if (r != ISC_R_SUCCESS) { return r; } @@ -2354,7 +2366,7 @@ dns_dnssec_matchdskey(dns_name_t *name, dns_rdata_t *dsrdata, } result = dns_ds_buildrdata(name, keyrdata, ds.digest_type, buf, - &newdsrdata); + sizeof(buf), &newdsrdata); if (result != ISC_R_SUCCESS) { continue; } diff --git a/lib/dns/ds.c b/lib/dns/ds.c index c56691bf19..773a880127 100644 --- a/lib/dns/ds.c +++ b/lib/dns/ds.c @@ -23,6 +23,7 @@ #include #include +#include #include #include #include @@ -32,11 +33,12 @@ isc_result_t dns_ds_fromkeyrdata(const dns_name_t *owner, dns_rdata_t *key, dns_dsdigest_t digest_type, unsigned char *digest, - dns_rdata_ds_t *dsrdata) { + size_t len, dns_rdata_ds_t *dsrdata) { isc_result_t result; dns_fixedname_t fname; dns_name_t *name; - unsigned int digestlen; + unsigned int digestlen = 0; + unsigned int privatelen = 0; isc_region_t r; isc_md_t *md; const isc_md_type_t *md_type = NULL; @@ -44,6 +46,7 @@ dns_ds_fromkeyrdata(const dns_name_t *owner, dns_rdata_t *key, REQUIRE(key != NULL); REQUIRE(key->type == dns_rdatatype_dnskey || key->type == dns_rdatatype_cdnskey); + REQUIRE(digest != NULL); if (!dst_ds_digest_supported(digest_type)) { return ISC_R_NOTIMPLEMENTED; @@ -55,10 +58,16 @@ dns_ds_fromkeyrdata(const dns_name_t *owner, dns_rdata_t *key, break; case DNS_DSDIGEST_SHA384: +#ifdef DNS_DSDIGEST_SHA384PRIVATE + case DNS_DSDIGEST_SHA384PRIVATE: +#endif md_type = ISC_MD_SHA384; break; case DNS_DSDIGEST_SHA256: +#ifdef DNS_DSDIGEST_SHA256PRIVATE + case DNS_DSDIGEST_SHA256PRIVATE: +#endif md_type = ISC_MD_SHA256; break; @@ -91,6 +100,64 @@ dns_ds_fromkeyrdata(const dns_name_t *owner, dns_rdata_t *key, goto end; } +#if defined(DNS_DSDIGEST_SHA256PRIVATE) && defined(DNS_DSDIGEST_SHA384PRIVATE) + /* + * Insert PRIVATE algorithm identify at start of digest. + */ + switch (digest_type) { + case DNS_DSDIGEST_SHA1: + case DNS_DSDIGEST_SHA256: + case DNS_DSDIGEST_SHA384: + break; + case DNS_DSDIGEST_SHA256PRIVATE: + case DNS_DSDIGEST_SHA384PRIVATE: + switch (r.base[3]) { + case DNS_KEYALG_PRIVATEDNS: { + isc_region_t r2 = r; + INSIST(r2.length >= 5); + isc_region_consume(&r2, 4); + dns_name_fromregion(name, &r2); + dns_name_toregion(name, &r2); + privatelen = r2.length; + if (r2.length > len) { + result = ISC_R_NOSPACE; + goto end; + } + memmove(digest, r2.base, privatelen); + digest += privatelen; + len -= privatelen; + break; + } + case DNS_KEYALG_PRIVATEOID: { + isc_region_t r2 = r; + INSIST(r2.length >= 5); + isc_region_consume(&r2, 4); + privatelen = r2.base[0] + 1; + if (r2.base[0] > len) { + result = ISC_R_NOSPACE; + goto end; + } + INSIST(r2.length >= privatelen); + memmove(digest, r2.base, privatelen); + digest += privatelen; + len -= privatelen; + break; + } + default: + break; + } + break; + default: + break; + } +#endif + + size_t mdsize = isc_md_get_size(md); + if (mdsize > len) { + result = ISC_R_NOSPACE; + goto end; + } + result = isc_md_final(md, digest, &digestlen); if (result != ISC_R_SUCCESS) { goto end; @@ -102,8 +169,8 @@ dns_ds_fromkeyrdata(const dns_name_t *owner, dns_rdata_t *key, dsrdata->algorithm = r.base[3]; dsrdata->key_tag = dst_region_computeid(&r); dsrdata->digest_type = digest_type; - dsrdata->digest = digest; - dsrdata->length = digestlen; + dsrdata->digest = digest - privatelen; + dsrdata->length = digestlen + privatelen; end: isc_md_free(md); @@ -112,14 +179,14 @@ end: isc_result_t dns_ds_buildrdata(dns_name_t *owner, dns_rdata_t *key, - dns_dsdigest_t digest_type, unsigned char *buffer, + dns_dsdigest_t digest_type, unsigned char *buffer, size_t len, dns_rdata_t *rdata) { isc_result_t result; unsigned char digest[ISC_MAX_MD_SIZE]; dns_rdata_ds_t ds; isc_buffer_t b; - result = dns_ds_fromkeyrdata(owner, key, digest_type, digest, &ds); + result = dns_ds_fromkeyrdata(owner, key, digest_type, digest, len, &ds); if (result != ISC_R_SUCCESS) { return result; } diff --git a/lib/dns/dst_api.c b/lib/dns/dst_api.c index 2851e3d0a7..5e52548d29 100644 --- a/lib/dns/dst_api.c +++ b/lib/dns/dst_api.c @@ -140,6 +140,14 @@ static const char *keystates[KEYSTATES_NVALUES] = { static dst_func_t *dst_t_func[DST_MAX_ALGS] = { 0 }; +/* length byte + 1.2.840.113549.1.1.11 BER encoded RFC 4055 */ +static unsigned char oid_rsasha256[] = { 0x0b, 0x06, 0x09, 0x2a, 0x86, 0x48, + 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b }; + +/* length byte + 1.2.840.113549.1.1.13 BER encoded RFC 4055 */ +static unsigned char oid_rsasha512[] = { 0x0b, 0x06, 0x09, 0x2a, 0x86, 0x48, + 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0d }; + void gss_log(int level, const char *fmt, ...) ISC_FORMAT_PRINTF(2, 3); @@ -215,6 +223,18 @@ dst__lib_initialize(void) { #if HAVE_GSSAPI dst__gssapi_init(&dst_t_func[DST_ALG_GSSAPI]); #endif /* HAVE_GSSAPI */ + /* + * RSASHA256 using assigned OID 1.2.840.113549.1.1.11 as + * a private OID example. + */ + dst__opensslrsa_init(&dst_t_func[DST_ALG_RSASHA256PRIVATEOID], + DST_ALG_RSASHA256PRIVATEOID); + /* + * RSASHA512 using assigned OID 1.2.840.113549.1.1.13 as + * a private OID example. + */ + dst__opensslrsa_init(&dst_t_func[DST_ALG_RSASHA512PRIVATEOID], + DST_ALG_RSASHA512PRIVATEOID); } void @@ -233,7 +253,13 @@ dst_algorithm_supported(unsigned int alg) { bool dst_ds_digest_supported(unsigned int digest_type) { return digest_type == DNS_DSDIGEST_SHA1 || +#if defined(DNS_DSDIGEST_SHA256PRIVATE) + digest_type == DNS_DSDIGEST_SHA256PRIVATE || +#endif digest_type == DNS_DSDIGEST_SHA256 || +#if defined(DNS_DSDIGEST_SHA256PRIVATE) + digest_type == DNS_DSDIGEST_SHA384PRIVATE || +#endif digest_type == DNS_DSDIGEST_SHA384; } @@ -645,7 +671,8 @@ dst_key_todns(const dst_key_t *key, isc_buffer_t *target) { } isc_buffer_putuint16(target, (uint16_t)(key->key_flags & 0xffff)); isc_buffer_putuint8(target, (uint8_t)key->key_proto); - isc_buffer_putuint8(target, (uint8_t)key->key_alg); + isc_buffer_putuint8(target, + (uint8_t)dst_algorithm_tosecalg(key->key_alg)); if ((key->key_flags & DNS_KEYFLAG_EXTENDED) != 0) { if (isc_buffer_availablelength(target) < 2) { @@ -1304,6 +1331,12 @@ dst_key_sigsize(const dst_key_t *key, unsigned int *n) { case DST_ALG_RSASHA512: *n = (key->key_size + 7) / 8; break; + case DST_ALG_RSASHA256PRIVATEOID: + *n = (key->key_size + 7) / 8 + sizeof(oid_rsasha256); + break; + case DST_ALG_RSASHA512PRIVATEOID: + *n = (key->key_size + 7) / 8 + sizeof(oid_rsasha512); + break; case DST_ALG_ECDSA256: *n = DNS_SIG_ECDSA256SIZE; break; @@ -1357,10 +1390,10 @@ void dst_key_format(const dst_key_t *key, char *cp, unsigned int size) { char namestr[DNS_NAME_FORMATSIZE]; char algstr[DNS_NAME_FORMATSIZE]; + dst_algorithm_t algorithm = dst_key_alg(key); dns_name_format(dst_key_name(key), namestr, sizeof(namestr)); - dns_secalg_format((dns_secalg_t)dst_key_alg(key), algstr, - sizeof(algstr)); + dst_algorithm_format(algorithm, algstr, sizeof(algstr)); snprintf(cp, size, "%s/%s/%d", namestr, algstr, dst_key_id(key)); } @@ -2107,6 +2140,8 @@ buildfilename(dns_name_t *name, dns_keytag_t id, unsigned int alg, isc_result_t result; REQUIRE(out != NULL); + REQUIRE(alg != 0 && alg != DST_ALG_PRIVATEOID && + alg != DST_ALG_PRIVATEDNS); if ((type & DST_TYPE_PRIVATE) != 0) { suffix = ".private"; @@ -2172,6 +2207,22 @@ frombuffer(const dns_name_t *name, unsigned int alg, unsigned int flags, REQUIRE(mctx != NULL); REQUIRE(keyp != NULL && *keyp == NULL); + if (alg == DNS_KEYALG_PRIVATEDNS) { + isc_buffer_t b = *source; + alg = dst_algorithm_fromprivatedns(&b); + if (alg == 0) { + return DST_R_UNSUPPORTEDALG; + } + } + + if (alg == DNS_KEYALG_PRIVATEOID) { + isc_buffer_t b = *source; + alg = dst_algorithm_fromprivateoid(&b); + if (alg == 0) { + return DST_R_UNSUPPORTEDALG; + } + } + key = get_key_struct(name, alg, flags, protocol, 0, rdclass, 0, mctx); if (isc_buffer_remaininglength(source) > 0) { @@ -2636,3 +2687,115 @@ dst_hmac_algorithm_totext(dst_algorithm_t alg) { return "unknown"; } } + +dns_secalg_t +dst_algorithm_tosecalg(dst_algorithm_t dst_alg) { + static dns_secalg_t dns_alg[DST_MAX_ALGS] = { + [DST_ALG_RSASHA256PRIVATEOID] = DNS_KEYALG_PRIVATEOID, + [DST_ALG_RSASHA512PRIVATEOID] = DNS_KEYALG_PRIVATEOID, + }; + + if (dst_alg < 256) { + return dst_alg; + } + if (dst_alg < DST_MAX_ALGS) { + return dns_alg[dst_alg]; + } + return 0; +} + +#if TEST_PRIVATEDNS +/* + * These are examples of specifying an algorithm using + * PRIVATEDNS. When creating such an algorithm, use your + * organisation's domain name instead of "example.org" + * so the identifier will be globally unique. + */ +static unsigned char rsasha256dns_data[] = "\011rsasha256\007example\003org"; +static dns_name_t const rsasha256dns = DNS_NAME_INITABSOLUTE(rsasha256dns_data); +static unsigned char rsasha512dns_data[] = "\011rsasha512\007example\003org"; +static dns_name_t const rsasha512dns = DNS_NAME_INITABSOLUTE(rsasha512dns_data); +#endif + +dst_algorithm_t +dst_algorithm_fromprivatedns(isc_buffer_t *buffer) { + dns_fixedname_t fixed; + dns_name_t *name = dns_fixedname_initname(&fixed); + isc_result_t result; + + result = dns_name_fromwire(name, buffer, DNS_DECOMPRESS_DEFAULT, NULL); + if (result != ISC_R_SUCCESS) { + return 0; + } + + /* + * Do name to dst_algorithm number mapping here. + */ + switch (name->length) { +#if TEST_PRIVATEDNS + case 23: + switch (name->ndata[7]) { + case '2': + if (dns_name_equal(name, &rsasha256dns)) { + return DST_ALG_RSASHA256PRIVATEDNS; + } + break; + case '5': + if (dns_name_equal(name, &rsasha512dns)) { + return DST_ALG_RSASHA512PRIVATEDNS; + } + break; + } + break; +#endif + default: + break; + } + + return 0; +} + +dst_algorithm_t +dst_algorithm_fromprivateoid(isc_buffer_t *buffer) { + isc_region_t r; + + isc_buffer_remainingregion(buffer, &r); + + /* + * Do OID to dst_algorithm number mapping here. There is a + * length byte followed by the OID of that length. + */ + if (r.length > 0 && ((unsigned int)r.base[0] + 1) <= r.length) { + if (r.base[0] + 1 == sizeof(oid_rsasha256) && + memcmp(oid_rsasha256, r.base, sizeof(oid_rsasha256)) == 0) + { + return DST_ALG_RSASHA256PRIVATEOID; + } + + if (r.base[0] + 1 == sizeof(oid_rsasha512) && + memcmp(oid_rsasha512, r.base, sizeof(oid_rsasha512)) == 0) + { + return DST_ALG_RSASHA512PRIVATEOID; + } + } + + return 0; +} + +dst_algorithm_t +dst_algorithm_fromdata(dns_secalg_t algorithm, unsigned char *data, + unsigned int length) { + isc_buffer_t b; + switch (algorithm) { + case DNS_KEYALG_PRIVATEDNS: + isc_buffer_init(&b, data, length); + isc_buffer_add(&b, length); + return dst_algorithm_fromprivatedns(&b); + case DNS_KEYALG_PRIVATEOID: + isc_buffer_init(&b, data, length); + isc_buffer_add(&b, length); + return dst_algorithm_fromprivateoid(&b); + default: + return algorithm; + } +} diff --git a/lib/dns/dst_internal.h b/lib/dns/dst_internal.h index 049d634987..21dc8cb06a 100644 --- a/lib/dns/dst_internal.h +++ b/lib/dns/dst_internal.h @@ -193,7 +193,7 @@ dst__hmacsha384_init(struct dst_func **funcp); void dst__hmacsha512_init(struct dst_func **funcp); void -dst__opensslrsa_init(struct dst_func **funcp, unsigned char algorithm); +dst__opensslrsa_init(struct dst_func **funcp, unsigned short algorithm); void dst__opensslecdsa_init(struct dst_func **funcp); void diff --git a/lib/dns/dst_parse.c b/lib/dns/dst_parse.c index b897407c41..5e53a19e49 100644 --- a/lib/dns/dst_parse.c +++ b/lib/dns/dst_parse.c @@ -332,6 +332,8 @@ check_data(const dst_private_t *priv, const unsigned int alg, bool old, case DST_ALG_NSEC3RSASHA1: case DST_ALG_RSASHA256: case DST_ALG_RSASHA512: + case DST_ALG_RSASHA256PRIVATEOID: + case DST_ALG_RSASHA512PRIVATEOID: return check_rsa(priv, external); case DST_ALG_ECDSA256: case DST_ALG_ECDSA384: @@ -694,6 +696,12 @@ dst__privstruct_writefile(const dst_key_t *key, const dst_private_t *priv, case DST_ALG_HMACSHA512: fprintf(fp, "(HMAC_SHA512)\n"); break; + case DST_ALG_RSASHA256PRIVATEOID: + fprintf(fp, "(OID:RSASHA256)\n"); + break; + case DST_ALG_RSASHA512PRIVATEOID: + fprintf(fp, "(OID:RSASHA512)\n"); + break; default: fprintf(fp, "(?)\n"); break; diff --git a/lib/dns/include/dns/ds.h b/lib/dns/include/dns/ds.h index a10d9355c1..734d93e647 100644 --- a/lib/dns/include/dns/ds.h +++ b/lib/dns/include/dns/ds.h @@ -16,20 +16,32 @@ #include #include -#define DNS_DSDIGEST_SHA1 (1) -#define DNS_DSDIGEST_SHA256 (2) -#define DNS_DSDIGEST_GOST (3) -#define DNS_DSDIGEST_SHA384 (4) +#define DNS_DSDIGEST_SHA1 (1) +#define DNS_DSDIGEST_SHA256 (2) +#define DNS_DSDIGEST_GOST (3) +#define DNS_DSDIGEST_SHA384 (4) +#define DNS_DSDIGEST_GOST2012 (5) +#define DNS_DSDIGEST_SM3 (6) + +#if TEST_PRIVATE_DSDIGEST +/* + * Possible future digest types that encode the PRIVATEDNS and + * PRIVATEOID identifiers before the cryptographic digest value. + */ +#define DNS_DSDIGEST_SHA256PRIVATE (7) +#define DNS_DSDIGEST_SHA384PRIVATE (8) +#define DNS_DSDIGEST_SM3PRIVATE (9) +#endif /* - * Assuming SHA-384 digest type. + * Assuming SHA-384 digest type + maximal PRIVATEDNS name. */ -#define DNS_DS_BUFFERSIZE (52) +#define DNS_DS_BUFFERSIZE (52 + 255) isc_result_t dns_ds_fromkeyrdata(const dns_name_t *owner, dns_rdata_t *key, dns_dsdigest_t digest_type, unsigned char *digest, - dns_rdata_ds_t *dsrdata); + size_t len, dns_rdata_ds_t *dsrdata); /*%< * Build a DS rdata structure from a key. * @@ -41,7 +53,7 @@ dns_ds_fromkeyrdata(const dns_name_t *owner, dns_rdata_t *key, isc_result_t dns_ds_buildrdata(dns_name_t *owner, dns_rdata_t *key, - dns_dsdigest_t digest_type, unsigned char *buffer, + dns_dsdigest_t digest_type, unsigned char *buffer, size_t len, dns_rdata_t *rdata); /*%< * Similar to dns_ds_fromkeyrdata(), but copies the DS into a diff --git a/lib/dns/include/dns/kasp.h b/lib/dns/include/dns/kasp.h index 609edbe949..e2b0ea94df 100644 --- a/lib/dns/include/dns/kasp.h +++ b/lib/dns/include/dns/kasp.h @@ -52,7 +52,7 @@ struct dns_kasp_key { /* Configuration */ dns_keystore_t *keystore; uint32_t lifetime; - uint8_t algorithm; + dst_algorithm_t algorithm; int length; uint8_t role; uint16_t tag_min; diff --git a/lib/dns/include/dns/resolver.h b/lib/dns/include/dns/resolver.h index 459e451731..a16d730619 100644 --- a/lib/dns/include/dns/resolver.h +++ b/lib/dns/include/dns/resolver.h @@ -434,12 +434,18 @@ dns_resolver_disable_ds_digest(dns_resolver_t *resolver, const dns_name_t *name, bool dns_resolver_algorithm_supported(dns_resolver_t *resolver, - const dns_name_t *name, unsigned int alg); + const dns_name_t *name, unsigned int alg, + unsigned char *private, size_t len); /*%< * Check if the given algorithm is supported by this resolver. * This checks whether the algorithm has been disabled via * dns_resolver_disable_algorithm(), then checks the underlying * crypto libraries if it was not specifically disabled. + * + * The algorithm is specified with the value 'alg' or, if + * 'alg' is PRIVATEOID or PRIVATEDNS, then the algorithm is + * encoded as a DNS name or OID in the first 'len' bytes of + * 'private'. */ bool diff --git a/lib/dns/include/dns/validator.h b/lib/dns/include/dns/validator.h index 00d56d6866..6132ad88ec 100644 --- a/lib/dns/include/dns/validator.h +++ b/lib/dns/include/dns/validator.h @@ -137,6 +137,7 @@ struct dns_validator { dns_rdataset_t fdsset; dns_rdataset_t frdataset; dns_rdataset_t fsigrdataset; + dns_rdataset_t dsrdataset; dns_fixedname_t fname; dns_fixedname_t wild; dns_fixedname_t closest; diff --git a/lib/dns/include/dns/zone.h b/lib/dns/include/dns/zone.h index dcf796f4c1..20b5f885b1 100644 --- a/lib/dns/include/dns/zone.h +++ b/lib/dns/include/dns/zone.h @@ -2187,8 +2187,8 @@ dns_zone_getsignatures(dns_zone_t *zone); */ isc_result_t -dns_zone_signwithkey(dns_zone_t *zone, dns_secalg_t algorithm, uint16_t keyid, - bool deleteit); +dns_zone_signwithkey(dns_zone_t *zone, dst_algorithm_t algorithm, + uint16_t keyid, bool deleteit); /*%< * Initiate/resume signing of the entire zone with the zone DNSKEY(s) * that match the given algorithm and keyid. diff --git a/lib/dns/include/dst/dst.h b/lib/dns/include/dst/dst.h index 0a9472b284..8ba5fe1360 100644 --- a/lib/dns/include/dst/dst.h +++ b/lib/dns/include/dst/dst.h @@ -110,8 +110,15 @@ typedef enum dst_algorithm { DST_ALG_HMAC_LAST = DST_ALG_HMACSHA512, DST_ALG_INDIRECT = 252, - DST_ALG_PRIVATE = 254, - DST_MAX_ALGS = 256, + DST_ALG_PRIVATEDNS = 253, + DST_ALG_PRIVATEOID = 254, + DST_ALG_RESERVED = 255, + /* + * Put PRIVATE DNS and PRIVATE OID identifiers here. + */ + DST_ALG_RSASHA256PRIVATEOID = 256, /* 1.2.840.113549.1.1.11 */ + DST_ALG_RSASHA512PRIVATEOID = 257, /* 1.2.840.113549.1.1.13 */ + DST_MAX_ALGS = 258, } dst_algorithm_t; /*% A buffer of this size is large enough to hold any key */ @@ -206,6 +213,20 @@ dst_algorithm_supported(unsigned int alg); * \li false */ +dst_algorithm_t +dst_algorithm_fromprivateoid(isc_buffer_t *buffer); +/* + * Extract the dst algorithm identifier that matches + * the OID value found at the start of 'buffer'. + */ + +dst_algorithm_t +dst_algorithm_fromprivatedns(isc_buffer_t *buf); +/* + * Extract the dst algorithm identifier that matches + * the DNS name found at the start of 'buffer'. + */ + bool dst_ds_digest_supported(unsigned int digest_type); /*%< @@ -1160,3 +1181,89 @@ dst_hmac_algorithm_totext(dst_algorithm_t alg); * Return the name associtated with the HMAC algorithm 'alg' * or return "unknown". */ + +isc_result_t +dst_algorithm_fromtext(dst_algorithm_t *algp, isc_textregion_t *source); +/*%< + * Convert the text 'source' refers to into a DST security algorithm value. + * The text may contain either a mnemonic algorithm name or a decimal algorithm + * number. This supports more algorithms than 'dns_secalg_fromtext' as it + * supports private algorithms used with PRIVATEDNS and PRIVATEOID. + * + * Requires: + *\li 'algp' is a valid pointer. + * + *\li 'source' is a valid text region. + * + * Returns: + *\li ISC_R_SUCCESS on success + *\li ISC_R_RANGE numeric type is out of range + *\li DNS_R_UNKNOWN mnemonic type is unknown + */ + +isc_result_t +dst_algorithm_totext(dst_algorithm_t alg, isc_buffer_t *target); +/*%< + * Put a textual representation of DST security algorithm 'alg' + * into 'target'. This supports a superset of dns_secalg_totext. + * + * Requires: + *\li 'alg' is a valid dst_algorithm_t. + * + *\li 'target' is a valid text buffer. + * + * Ensures, + * if the result is success: + *\li The used space in 'target' is updated. + * + * Returns: + *\li ISC_R_SUCCESS on success + *\li ISC_R_NOSPACE target buffer is too small + */ + +#define DST_ALGORITHM_FORMATSIZE 20 +void +dst_algorithm_format(dst_algorithm_t dst_alg, char *data, unsigned int length); +/*%< + * Wrapper for dst_algorithm_totext(), writing text into 'cp' + */ + +dns_secalg_t +dst_algorithm_tosecalg(dst_algorithm_t dst_alg); +/*%< + * Return the DNSSEC algorithm identifier that applies for the DST + * algorithm. For PRIVATEDNS and PRIVATEOID based algorithms, this + * is PRIVATEDNS and PRIVATEOID respectively. + * + * Zero is returned when there is no mapping. + */ + +isc_result_t +dst_privatedns_fromtext(dst_algorithm_t *algp, isc_textregion_t *source); + +isc_result_t +dns_privatedns_totext(dst_algorithm_t alg, isc_buffer_t *b); + +void +dns_privatedns_format(dst_algorithm_t alg, char *buf, unsigned int size); + +isc_result_t +dst_privateoid_fromtext(dst_algorithm_t *algp, isc_textregion_t *source); + +isc_result_t +dns_privateoid_totext(dst_algorithm_t alg, isc_buffer_t *b); + +void +dns_privateoid_format(dst_algorithm_t alg, char *buf, unsigned int size); + +dst_algorithm_t +dst_algorithm_fromdata(dns_secalg_t algorithm, unsigned char *data, + unsigned int length); +/*%< + * If 'algorithm' is PRIVATEOID or PRIVATEDNS, extract the DNSSEC private + * algorithm encoded at the begining of data and return the DST algorithm + * number that corresponds to it; if the algorithm is unknown to DST, + * return 0. + * + * If 'algorithm' is any other value, return it directly. + */ diff --git a/lib/dns/kasp.c b/lib/dns/kasp.c index 3cd44ea74c..8fac378ce6 100644 --- a/lib/dns/kasp.c +++ b/lib/dns/kasp.c @@ -426,11 +426,13 @@ dns_kasp_key_size(dns_kasp_key_t *key) { REQUIRE(key != NULL); switch (key->algorithm) { - case DNS_KEYALG_RSASHA1: - case DNS_KEYALG_NSEC3RSASHA1: - case DNS_KEYALG_RSASHA256: - case DNS_KEYALG_RSASHA512: - min = (key->algorithm == DNS_KEYALG_RSASHA512) ? 1024 : 512; + case DST_ALG_RSASHA1: + case DST_ALG_NSEC3RSASHA1: + case DST_ALG_RSASHA256: + case DST_ALG_RSASHA512: + case DST_ALG_RSASHA256PRIVATEOID: + case DST_ALG_RSASHA512PRIVATEOID: + min = (key->algorithm == DST_ALG_RSASHA512) ? 1024 : 512; if (key->length > -1) { size = (unsigned int)key->length; if (size < min) { @@ -443,16 +445,16 @@ dns_kasp_key_size(dns_kasp_key_t *key) { size = 2048; } break; - case DNS_KEYALG_ECDSA256: + case DST_ALG_ECDSA256: size = 256; break; - case DNS_KEYALG_ECDSA384: + case DST_ALG_ECDSA384: size = 384; break; - case DNS_KEYALG_ED25519: + case DST_ALG_ED25519: size = 256; break; - case DNS_KEYALG_ED448: + case DST_ALG_ED448: size = 456; break; default: diff --git a/lib/dns/keytable.c b/lib/dns/keytable.c index 2caae9612c..280740b991 100644 --- a/lib/dns/keytable.c +++ b/lib/dns/keytable.c @@ -453,7 +453,7 @@ dns_keytable_deletekey(dns_keytable_t *keytable, const dns_name_t *keyname, } result = dns_ds_fromkeyrdata(keyname, &rdata, DNS_DSDIGEST_SHA256, - digest, &ds); + digest, sizeof(digest), &ds); if (result != ISC_R_SUCCESS) { goto finish; } diff --git a/lib/dns/opensslrsa_link.c b/lib/dns/opensslrsa_link.c index cc945a20ea..e31ba6b15d 100644 --- a/lib/dns/opensslrsa_link.c +++ b/lib/dns/opensslrsa_link.c @@ -50,6 +50,14 @@ typedef struct rsa_components { const BIGNUM *e, *n, *d, *p, *q, *dmp1, *dmq1, *iqmp; } rsa_components_t; +/* length byte + 1.2.840.113549.1.1.11 BER encoded RFC 4055 */ +static unsigned char oid_rsasha256[] = { 0x0b, 0x06, 0x09, 0x2a, 0x86, 0x48, + 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b }; + +/* length byte + 1.2.840.113549.1.1.13 BER encoded RFC 4055 */ +static unsigned char oid_rsasha512[] = { 0x0b, 0x06, 0x09, 0x2a, 0x86, 0x48, + 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0d }; + static isc_result_t opensslrsa_components_get(const dst_key_t *key, rsa_components_t *c, bool private) { @@ -154,6 +162,8 @@ opensslrsa_valid_key_alg(unsigned int key_alg) { case DST_ALG_NSEC3RSASHA1: case DST_ALG_RSASHA256: case DST_ALG_RSASHA512: + case DST_ALG_RSASHA256PRIVATEOID: + case DST_ALG_RSASHA512PRIVATEOID: return true; default: return false; @@ -181,12 +191,14 @@ opensslrsa_createctx(dst_key_t *key, dst_context_t *dctx) { } break; case DST_ALG_RSASHA256: + case DST_ALG_RSASHA256PRIVATEOID: /* From RFC 5702 */ if (dctx->key->key_size < 512 || dctx->key->key_size > 4096) { return ISC_R_FAILURE; } break; case DST_ALG_RSASHA512: + case DST_ALG_RSASHA512PRIVATEOID: /* From RFC 5702 */ if (dctx->key->key_size < 1024 || dctx->key->key_size > 4096) { return ISC_R_FAILURE; @@ -207,9 +219,11 @@ opensslrsa_createctx(dst_key_t *key, dst_context_t *dctx) { type = isc__crypto_sha1; /* SHA1 + RSA */ break; case DST_ALG_RSASHA256: + case DST_ALG_RSASHA256PRIVATEOID: type = isc__crypto_sha256; /* SHA256 + RSA */ break; case DST_ALG_RSASHA512: + case DST_ALG_RSASHA512PRIVATEOID: type = isc__crypto_sha512; break; default: @@ -264,6 +278,7 @@ opensslrsa_sign(dst_context_t *dctx, isc_buffer_t *sig) { unsigned int siglen = 0; EVP_MD_CTX *evp_md_ctx = NULL; EVP_PKEY *pkey = NULL; + unsigned int len = 0; REQUIRE(dctx != NULL && dctx->key != NULL); REQUIRE(opensslrsa_valid_key_alg(dctx->key->key_alg)); @@ -272,17 +287,42 @@ opensslrsa_sign(dst_context_t *dctx, isc_buffer_t *sig) { evp_md_ctx = dctx->ctxdata.evp_md_ctx; pkey = key->keydata.pkeypair.priv; + /* + * Account to the space the OIDs and DNS names consume. + */ + switch (key->key_alg) { + case DST_ALG_RSASHA256PRIVATEOID: + len = sizeof(oid_rsasha256); + break; + case DST_ALG_RSASHA512PRIVATEOID: + len = sizeof(oid_rsasha512); + break; + } + isc_buffer_availableregion(sig, &r); - if (r.length < (unsigned int)EVP_PKEY_size(pkey)) { + if (r.length < (unsigned int)EVP_PKEY_size(pkey) + len) { return ISC_R_NOSPACE; } + /* + * Add OID and DNS names to start of signature. + */ + switch (key->key_alg) { + case DST_ALG_RSASHA256PRIVATEOID: + isc_buffer_putmem(sig, oid_rsasha256, sizeof(oid_rsasha256)); + isc_region_consume(&r, sizeof(oid_rsasha256)); + break; + case DST_ALG_RSASHA512PRIVATEOID: + isc_buffer_putmem(sig, oid_rsasha512, sizeof(oid_rsasha512)); + isc_region_consume(&r, sizeof(oid_rsasha512)); + break; + } + if (!EVP_SignFinal(evp_md_ctx, r.base, &siglen, pkey)) { return dst__openssl_toresult3(dctx->category, "EVP_SignFinal", ISC_R_FAILURE); } - isc_buffer_add(sig, siglen); return ISC_R_SUCCESS; @@ -317,6 +357,8 @@ opensslrsa_verify(dst_context_t *dctx, const isc_region_t *sig) { int status = 0; EVP_MD_CTX *evp_md_ctx = NULL; EVP_PKEY *pkey = NULL; + const unsigned char *base = sig->base; + unsigned int length = sig->length; REQUIRE(dctx != NULL && dctx->key != NULL); REQUIRE(opensslrsa_valid_key_alg(dctx->key->key_alg)); @@ -330,7 +372,31 @@ opensslrsa_verify(dst_context_t *dctx, const isc_region_t *sig) { return DST_R_VERIFYFAILURE; } - status = EVP_VerifyFinal(evp_md_ctx, sig->base, sig->length, pkey); + /* + * Check identifying OID in front of public key material. + */ + switch (key->key_alg) { + case DST_ALG_RSASHA256PRIVATEOID: + if (length < sizeof(oid_rsasha256) || + memcmp(base, oid_rsasha256, sizeof(oid_rsasha256)) != 0) + { + return DST_R_VERIFYFAILURE; + } + base += sizeof(oid_rsasha256); + length -= sizeof(oid_rsasha256); + break; + case DST_ALG_RSASHA512PRIVATEOID: + if (length < sizeof(oid_rsasha512) || + memcmp(base, oid_rsasha512, sizeof(oid_rsasha512)) != 0) + { + return DST_R_VERIFYFAILURE; + } + base += sizeof(oid_rsasha512); + length -= sizeof(oid_rsasha512); + break; + } + + status = EVP_VerifyFinal(evp_md_ctx, base, length, pkey); switch (status) { case 1: return ISC_R_SUCCESS; @@ -692,12 +758,14 @@ opensslrsa_generate(dst_key_t *key, int unused, void (*callback)(int)) { } break; case DST_ALG_RSASHA256: + case DST_ALG_RSASHA256PRIVATEOID: /* From RFC 5702 */ if (key->key_size < 512 || key->key_size > 4096) { DST_RET(DST_R_INVALIDPARAM); } break; case DST_ALG_RSASHA512: + case DST_ALG_RSASHA512PRIVATEOID: /* From RFC 5702 */ if (key->key_size < 1024 || key->key_size > 4096) { DST_RET(DST_R_INVALIDPARAM); @@ -740,6 +808,26 @@ opensslrsa_todns(const dst_key_t *key, isc_buffer_t *data) { isc_buffer_availableregion(data, &r); + /* + * Add identifying OID and DNS names to front of public key material. + */ + switch (key->key_alg) { + case DST_ALG_RSASHA256PRIVATEOID: + if (r.length < sizeof(oid_rsasha256)) { + DST_RET(ISC_R_NOSPACE); + } + isc_buffer_putmem(data, oid_rsasha256, sizeof(oid_rsasha256)); + isc_region_consume(&r, sizeof(oid_rsasha256)); + break; + case DST_ALG_RSASHA512PRIVATEOID: + if (r.length < sizeof(oid_rsasha512)) { + DST_RET(ISC_R_NOSPACE); + } + isc_buffer_putmem(data, oid_rsasha512, sizeof(oid_rsasha512)); + isc_region_consume(&r, sizeof(oid_rsasha512)); + break; + } + ret = opensslrsa_components_get(key, &c, false); if (ret != ISC_R_SUCCESS) { goto err; @@ -794,6 +882,31 @@ opensslrsa_fromdns(dst_key_t *key, isc_buffer_t *data) { if (r.length == 0) { DST_RET(ISC_R_SUCCESS); } + + /* + * Check identifying OID in front of public key material. + */ + switch (key->key_alg) { + case DST_ALG_RSASHA256PRIVATEOID: + if (r.length < sizeof(oid_rsasha256) || + memcmp(r.base, oid_rsasha256, sizeof(oid_rsasha256)) != 0) + { + DST_RET(DST_R_INVALIDPUBLICKEY); + } + isc_region_consume(&r, sizeof(oid_rsasha256)); + isc_buffer_forward(data, sizeof(oid_rsasha256)); + break; + case DST_ALG_RSASHA512PRIVATEOID: + if (r.length < sizeof(oid_rsasha512) || + memcmp(r.base, oid_rsasha512, sizeof(oid_rsasha512)) != 0) + { + DST_RET(DST_R_INVALIDPUBLICKEY); + } + isc_region_consume(&r, sizeof(oid_rsasha512)); + isc_buffer_forward(data, sizeof(oid_rsasha512)); + break; + } + length = r.length; if (r.length < 1) { DST_RET(DST_R_INVALIDPUBLICKEY); @@ -1214,7 +1327,7 @@ static const unsigned char sha512_sig[] = "\xf1"; static isc_result_t -check_algorithm(unsigned char algorithm) { +check_algorithm(unsigned short algorithm) { rsa_components_t c = { .bnfree = true }; EVP_MD_CTX *evp_md_ctx = EVP_MD_CTX_create(); EVP_PKEY *pkey = NULL; @@ -1231,11 +1344,13 @@ check_algorithm(unsigned char algorithm) { len = sizeof(sha1_sig) - 1; break; case DST_ALG_RSASHA256: + case DST_ALG_RSASHA256PRIVATEOID: type = isc__crypto_sha256; /* SHA256 + RSA */ sig = sha256_sig; len = sizeof(sha256_sig) - 1; break; case DST_ALG_RSASHA512: + case DST_ALG_RSASHA512PRIVATEOID: type = isc__crypto_sha512; sig = sha512_sig; len = sizeof(sha512_sig) - 1; @@ -1272,7 +1387,7 @@ err: } void -dst__opensslrsa_init(dst_func_t **funcp, unsigned char algorithm) { +dst__opensslrsa_init(dst_func_t **funcp, unsigned short algorithm) { REQUIRE(funcp != NULL); if (*funcp == NULL) { diff --git a/lib/dns/private.c b/lib/dns/private.c index 57f724c82e..e16514cdeb 100644 --- a/lib/dns/private.c +++ b/lib/dns/private.c @@ -354,6 +354,7 @@ dns_private_totext(dns_rdata_t *private, isc_buffer_t *buf) { isc_buffer_putstr(buf, " / creating NSEC chain"); } } else if (private->length == 5) { + /* Old Form */ unsigned char alg = private->data[0]; dns_keytag_t keyid = (private->data[2] | private->data[1] << 8); char keybuf[DNS_SECALG_FORMATSIZE + BUFSIZ], @@ -371,6 +372,33 @@ dns_private_totext(dns_rdata_t *private, isc_buffer_t *buf) { isc_buffer_putstr(buf, "Signing with "); } + dns_secalg_format(alg, algbuf, sizeof(algbuf)); + snprintf(keybuf, sizeof(keybuf), "key %d/%s", keyid, algbuf); + isc_buffer_putstr(buf, keybuf); + } else if (private->length == 7) { + /* New Form - supports private types */ + dns_keytag_t keyid = private->data[2] | (private->data[1] << 8); + char keybuf[DNS_SECALG_FORMATSIZE + BUFSIZ], + algbuf[DNS_SECALG_FORMATSIZE]; + bool del = private->data[3]; + bool complete = private->data[4]; + dst_algorithm_t alg = private->data[6] | + (private->data[5] << 8); + + if (dst_algorithm_tosecalg(alg) != private->data[0]) { + return ISC_R_NOTFOUND; + } + + if (del && complete) { + isc_buffer_putstr(buf, "Done removing signatures for "); + } else if (del) { + isc_buffer_putstr(buf, "Removing signatures for "); + } else if (complete) { + isc_buffer_putstr(buf, "Done signing with "); + } else { + isc_buffer_putstr(buf, "Signing with "); + } + dns_secalg_format(alg, algbuf, sizeof(algbuf)); snprintf(keybuf, sizeof(keybuf), "key %d/%s", keyid, algbuf); isc_buffer_putstr(buf, keybuf); diff --git a/lib/dns/rcode.c b/lib/dns/rcode.c index 6c957e870c..133f49a99e 100644 --- a/lib/dns/rcode.c +++ b/lib/dns/rcode.c @@ -36,6 +36,8 @@ #include #include +#include + #define RETERR(x) \ do { \ isc_result_t _r = (x); \ @@ -47,6 +49,10 @@ #define TOTEXTONLY 0x01 +/* clang-format off */ +#define SENTINEL { 0, NULL, 0 } +/* clang-format on */ + #define RCODENAMES \ /* standard rcodes */ \ { dns_rcode_noerror, "NOERROR", 0 }, \ @@ -69,7 +75,7 @@ #define ERCODENAMES \ /* extended rcodes */ \ { dns_rcode_badvers, "BADVERS", 0 }, \ - { dns_rcode_badcookie, "BADCOOKIE", 0 }, { 0, NULL, 0 } + { dns_rcode_badcookie, "BADCOOKIE", 0 }, SENTINEL #define TSIGRCODENAMES \ /* extended rcodes */ \ @@ -79,7 +85,7 @@ { dns_tsigerror_badmode, "BADMODE", 0 }, \ { dns_tsigerror_badname, "BADNAME", 0 }, \ { dns_tsigerror_badalg, "BADALG", 0 }, \ - { dns_tsigerror_badtrunc, "BADTRUNC", 0 }, { 0, NULL, 0 } + { dns_tsigerror_badtrunc, "BADTRUNC", 0 }, SENTINEL /* RFC4398 section 2.1 */ @@ -87,7 +93,7 @@ { 1, "PKIX", 0 }, { 2, "SPKI", 0 }, { 3, "PGP", 0 }, \ { 4, "IPKIX", 0 }, { 5, "ISPKI", 0 }, { 6, "IPGP", 0 }, \ { 7, "ACPKIX", 0 }, { 8, "IACPKIX", 0 }, { 253, "URI", 0 }, \ - { 254, "OID", 0 }, { 0, NULL, 0 } + { 254, "OID", 0 }, SENTINEL /* RFC2535 section 7, RFC3110 */ @@ -109,26 +115,56 @@ { DNS_KEYALG_ED448, "ED448", 0 }, \ { DNS_KEYALG_INDIRECT, "INDIRECT", 0 }, \ { DNS_KEYALG_PRIVATEDNS, "PRIVATEDNS", 0 }, \ - { DNS_KEYALG_PRIVATEOID, "PRIVATEOID", 0 }, { 0, NULL, 0 } + { DNS_KEYALG_PRIVATEOID, "PRIVATEOID", 0 }, SENTINEL + +/* + * PRIVATEDNS subtypes we support. + */ +#define PRIVATEDNSS /* currently empty */ + +/* + * PRIVATEOID subtypes we support. + */ +#define PRIVATEOIDS \ + { DST_ALG_RSASHA256PRIVATEOID, "RSASHA256OID", 0 }, \ + { DST_ALG_RSASHA512PRIVATEOID, "RSASHA512OID", 0 }, /* RFC2535 section 7.1 */ #define SECPROTONAMES \ { 0, "NONE", 0 }, { 1, "TLS", 0 }, { 2, "EMAIL", 0 }, \ { 3, "DNSSEC", 0 }, { 4, "IPSEC", 0 }, { 255, "ALL", 0 }, \ - { 0, NULL, 0 } + SENTINEL -#define HASHALGNAMES { 1, "SHA-1", 0 }, { 0, NULL, 0 } +#define HASHALGNAMES { 1, "SHA-1", 0 }, SENTINEL -/* RFC3658, RFC4509, RFC5933, RFC6605 */ +/* RFC3658, RFC4509, RFC5933, RFC6605, RFC9558, RFC9563 */ + +#if defined(DNS_DSDIGEST_SHA256PRIVATE) && \ + defined(DNS_DSDIGEST_SHA384PRIVATE) && \ + defined(DNS_DSDIGEST_SM3PRIVATE) +#define DSDIGESTPRIVATENAMES \ + { DNS_DSDIGEST_SHA256PRIVATE, "SHA-256-PRIVATE", 0 }, \ + { DNS_DSDIGEST_SHA256PRIVATE, "SHA256PRIVATE", 0 }, \ + { DNS_DSDIGEST_SHA384PRIVATE, "SHA-384-PRIVATE", 0 }, \ + { DNS_DSDIGEST_SHA384PRIVATE, "SHA384PRIVATE", 0 }, \ + { DNS_DSDIGEST_SM3PRIVATE, "SM3-PRIVATE", 0 }, \ + { DNS_DSDIGEST_SM3PRIVATE, "SM3PRIVATE", 0 }, +#else +#define DSDIGESTPRIVATENAMES +#endif #define DSDIGESTNAMES \ { DNS_DSDIGEST_SHA1, "SHA-1", 0 }, { DNS_DSDIGEST_SHA1, "SHA1", 0 }, \ { DNS_DSDIGEST_SHA256, "SHA-256", 0 }, \ { DNS_DSDIGEST_SHA256, "SHA256", 0 }, \ { DNS_DSDIGEST_GOST, "GOST", 0 }, \ + { DNS_DSDIGEST_SM3, "SM3", 0 }, \ { DNS_DSDIGEST_SHA384, "SHA-384", 0 }, \ - { DNS_DSDIGEST_SHA384, "SHA384", 0 }, { 0, NULL, 0 } + { DNS_DSDIGEST_SHA384, "SHA384", 0 }, \ + { DNS_DSDIGEST_GOST2012, "GOST-2012", 0 }, \ + { DNS_DSDIGEST_GOST2012, "GOST2012", 0 }, \ + DSDIGESTPRIVATENAMES SENTINEL struct tbl { unsigned int value; @@ -143,6 +179,9 @@ static struct tbl secalgs[] = { SECALGNAMES }; static struct tbl secprotos[] = { SECPROTONAMES }; static struct tbl hashalgs[] = { HASHALGNAMES }; static struct tbl dsdigests[] = { DSDIGESTNAMES }; +static struct tbl privatednss[] = { PRIVATEDNSS SENTINEL }; +static struct tbl privateoids[] = { PRIVATEOIDS SENTINEL }; +static struct tbl dstalgorithms[] = { PRIVATEDNSS PRIVATEOIDS SECALGNAMES }; static struct keyflag { const char *name; @@ -346,6 +385,64 @@ dns_secalg_format(dns_secalg_t alg, char *cp, unsigned int size) { } } +isc_result_t +dst_privatedns_fromtext(dst_algorithm_t *dstalgp, isc_textregion_t *source) { + unsigned int value; + RETERR(dns_mnemonic_fromtext(&value, source, privatednss, 0)); + *dstalgp = value; + return ISC_R_SUCCESS; +} + +isc_result_t +dns_privatedns_totext(dst_algorithm_t alg, isc_buffer_t *target) { + return dns_mnemonic_totext(alg, target, privatednss); +} + +void +dns_privatedns_format(dst_algorithm_t alg, char *cp, unsigned int size) { + isc_buffer_t b; + isc_region_t r; + isc_result_t result; + + REQUIRE(cp != NULL && size > 0); + isc_buffer_init(&b, cp, size - 1); + result = dns_privatedns_totext(alg, &b); + isc_buffer_usedregion(&b, &r); + r.base[r.length] = 0; + if (result != ISC_R_SUCCESS) { + r.base[0] = 0; + } +} + +isc_result_t +dst_privateoid_fromtext(dst_algorithm_t *dstalgp, isc_textregion_t *source) { + unsigned int value; + RETERR(dns_mnemonic_fromtext(&value, source, privateoids, 0)); + *dstalgp = value; + return ISC_R_SUCCESS; +} + +isc_result_t +dns_privateoid_totext(dst_algorithm_t alg, isc_buffer_t *target) { + return dns_mnemonic_totext(alg, target, privateoids); +} + +void +dns_privateoid_format(dst_algorithm_t alg, char *cp, unsigned int size) { + isc_buffer_t b; + isc_region_t r; + isc_result_t result; + + REQUIRE(cp != NULL && size > 0); + isc_buffer_init(&b, cp, size - 1); + result = dns_privateoid_totext(alg, &b); + isc_buffer_usedregion(&b, &r); + r.base[r.length] = 0; + if (result != ISC_R_SUCCESS) { + r.base[0] = 0; + } +} + isc_result_t dns_secproto_fromtext(dns_secproto_t *secprotop, isc_textregion_t *source) { unsigned int value; @@ -572,3 +669,32 @@ dns_rdataclass_format(dns_rdataclass_t rdclass, char *array, strlcpy(array, "", size); } } + +isc_result_t +dst_algorithm_fromtext(dst_algorithm_t *dstalgp, isc_textregion_t *source) { + unsigned int value; + RETERR(dns_mnemonic_fromtext(&value, source, dstalgorithms, 255)); + *dstalgp = value; + return ISC_R_SUCCESS; +} + +isc_result_t +dst_algorithm_totext(dst_algorithm_t alg, isc_buffer_t *target) { + return dns_mnemonic_totext(alg, target, dstalgorithms); +} + +void +dst_algorithm_format(dst_algorithm_t alg, char *cp, unsigned int size) { + isc_buffer_t b; + isc_region_t r; + isc_result_t result; + + REQUIRE(cp != NULL && size > 0); + isc_buffer_init(&b, cp, size - 1); + result = dst_algorithm_totext(alg, &b); + isc_buffer_usedregion(&b, &r); + r.base[r.length] = 0; + if (result != ISC_R_SUCCESS) { + r.base[0] = 0; + } +} diff --git a/lib/dns/resolver.c b/lib/dns/resolver.c index 08f0533fe2..8947973236 100644 --- a/lib/dns/resolver.c +++ b/lib/dns/resolver.c @@ -10593,7 +10593,7 @@ dns_resolver_disable_algorithm(dns_resolver_t *resolver, const dns_name_t *name, unsigned int alg) { REQUIRE(VALID_RESOLVER(resolver)); - if (alg > 255) { + if (alg >= DST_MAX_ALGS) { return ISC_R_RANGE; } @@ -10614,13 +10614,37 @@ dns_resolver_disable_ds_digest(dns_resolver_t *resolver, const dns_name_t *name, bool dns_resolver_algorithm_supported(dns_resolver_t *resolver, - const dns_name_t *name, unsigned int alg) { + const dns_name_t *name, unsigned int alg, + unsigned char *private, size_t len) { REQUIRE(VALID_RESOLVER(resolver)); if ((alg == DST_ALG_DH) || (alg == DST_ALG_INDIRECT)) { return false; } + /* + * Look up the DST algorithm identifier for private-OID + * and private-DNS keys. + */ + if (alg == DST_ALG_PRIVATEDNS && private != NULL) { + isc_buffer_t b; + isc_buffer_init(&b, private, len); + isc_buffer_add(&b, len); + alg = dst_algorithm_fromprivatedns(&b); + if (alg == 0) { + return false; + } + } + + if (alg == DST_ALG_PRIVATEOID && private != NULL) { + isc_buffer_t b; + isc_buffer_init(&b, private, len); + isc_buffer_add(&b, len); + alg = dst_algorithm_fromprivateoid(&b); + if (alg == 0) { + return false; + } + } if (dns_nametree_covered(resolver->algorithms, name, NULL, alg)) { return false; } diff --git a/lib/dns/validator.c b/lib/dns/validator.c index f2c8fb11be..82a839977a 100644 --- a/lib/dns/validator.c +++ b/lib/dns/validator.c @@ -146,7 +146,8 @@ static isc_result_t validate_nx(dns_validator_t *val, bool resume); static isc_result_t -proveunsecure(dns_validator_t *val, bool have_ds, bool resume); +proveunsecure(dns_validator_t *val, bool have_ds, bool have_dnskey, + bool resume); static void validator_logv(dns_validator_t *val, isc_logcategory_t category, @@ -397,6 +398,13 @@ fetch_callback_dnskey(void *arg) { dns_rdataset_t *rdataset = &val->frdataset; isc_result_t eresult = resp->result; isc_result_t result; + bool trustchain; + + /* + * Set 'trustchain' to true if we're walking a chain of + * trust; false if we're attempting to prove insecurity. + */ + trustchain = ((val->attributes & VALATTR_INSECURITY) == 0); /* Free resources which are not of interest. */ if (resp->node != NULL) { @@ -418,34 +426,55 @@ fetch_callback_dnskey(void *arg) { goto cleanup; } - switch (eresult) { - case ISC_R_SUCCESS: - case DNS_R_NCACHENXRRSET: - /* - * We have an answer to our DNSKEY query. Either the DNSKEY - * RRset or a NODATA response. - */ - validator_log(val, ISC_LOG_DEBUG(3), "%s with trust %s", - eresult == ISC_R_SUCCESS ? "keyset" - : "NCACHENXRRSET", - dns_trust_totext(rdataset->trust)); - /* - * Only extract the dst key if the keyset exists and is secure. - */ - if (eresult == ISC_R_SUCCESS && - rdataset->trust >= dns_trust_secure) - { - result = validate_helper_run(val, - resume_answer_with_key); - } else { - result = validate_async_run(val, resume_answer); + if (trustchain) { + switch (eresult) { + case ISC_R_SUCCESS: + case DNS_R_NCACHENXRRSET: + /* + * We have an answer to our DNSKEY query. Either the + * DNSKEY RRset or a NODATA response. + */ + validator_log(val, ISC_LOG_DEBUG(3), "%s with trust %s", + eresult == ISC_R_SUCCESS + ? "keyset" + : "NCACHENXRRSET", + dns_trust_totext(rdataset->trust)); + /* + * Only extract the dst key if the keyset exists and is + * secure. + */ + if (eresult == ISC_R_SUCCESS && + rdataset->trust >= dns_trust_secure) + { + result = validate_helper_run( + val, resume_answer_with_key); + } else { + result = validate_async_run(val, resume_answer); + } + break; + default: + validator_log(val, ISC_LOG_DEBUG(3), + "fetch_callback_dnskey: got %s", + isc_result_totext(eresult)); + result = DNS_R_BROKENCHAIN; + break; + } + } else { + switch (eresult) { + case ISC_R_SUCCESS: + /* + * We have a DS (val->dsrdataset) and + * DNSKEY (val->fdataset). + */ + result = proveunsecure(val, false, true, true); + break; + default: + validator_log(val, ISC_LOG_DEBUG(3), + "fetch_callback_dnskey: got %s", + isc_result_totext(eresult)); + result = DNS_R_BROKENCHAIN; + break; } - break; - default: - validator_log(val, ISC_LOG_DEBUG(3), - "fetch_callback_dnskey: got %s", - isc_result_totext(eresult)); - result = DNS_R_BROKENCHAIN; } cleanup: @@ -518,7 +547,7 @@ fetch_callback_ds(void *arg) { validator_log(val, ISC_LOG_DEBUG(3), "falling back to insecurity proof (%s)", isc_result_totext(eresult)); - result = proveunsecure(val, false, false); + result = proveunsecure(val, false, false, false); break; default: validator_log(val, ISC_LOG_DEBUG(3), @@ -537,7 +566,7 @@ fetch_callback_ds(void *arg) { * trust. */ - result = proveunsecure(val, false, true); + result = proveunsecure(val, false, false, true); break; case ISC_R_SUCCESS: /* @@ -546,7 +575,7 @@ fetch_callback_ds(void *arg) { * so keep looking for the break in the chain * of trust. */ - result = proveunsecure(val, true, true); + result = proveunsecure(val, true, false, true); break; case DNS_R_NXRRSET: case DNS_R_NCACHENXRRSET: @@ -567,13 +596,14 @@ fetch_callback_ds(void *arg) { * Not a zone cut, so we have to keep looking for * the break point in the chain of trust. */ - result = proveunsecure(val, false, true); + result = proveunsecure(val, false, false, true); break; default: validator_log(val, ISC_LOG_DEBUG(3), "fetch_callback_ds: got %s", isc_result_totext(eresult)); result = DNS_R_BROKENCHAIN; + break; } } @@ -673,7 +703,7 @@ validator_callback_ds(void *arg) { { result = markanswer(val, "validator_callback_ds"); } else if ((val->attributes & VALATTR_INSECURITY) != 0) { - result = proveunsecure(val, have_dsset, true); + result = proveunsecure(val, have_dsset, false, true); } else { result = validate_async_run(val, validate_dnskey); } @@ -724,7 +754,7 @@ validator_callback_cname(void *arg) { if (eresult == ISC_R_SUCCESS) { validator_log(val, ISC_LOG_DEBUG(3), "cname with trust %s", dns_trust_totext(val->frdataset.trust)); - result = proveunsecure(val, false, true); + result = proveunsecure(val, false, false, true); } else { if (eresult != DNS_R_BROKENCHAIN) { expire_rdatasets(val); @@ -832,6 +862,7 @@ validator_callback_nsec(void *arg) { FALLTHROUGH; default: result = validate_nx(val, true); + break; } } @@ -1364,6 +1395,7 @@ selfsigned_dnskey(dns_validator_t *val) { return ISC_R_QUOTA; } consume_validation_fail(val); + break; } } else if (rdataset->trust >= dns_trust_secure) { /* @@ -1469,6 +1501,7 @@ again: break; } consume_validation_fail(val); + break; } return result; } @@ -1641,11 +1674,15 @@ validate_answer_process(void *arg) { * At this point we could check that the signature algorithm * was known and "sufficiently good". */ - if (!dns_resolver_algorithm_supported(val->view->resolver, val->name, - val->siginfo->algorithm)) + if (!dns_resolver_algorithm_supported( + val->view->resolver, val->name, val->siginfo->algorithm, + val->siginfo->signature, val->siginfo->siglen)) { if (val->unsupported_algorithm == 0) { val->unsupported_algorithm = val->siginfo->algorithm; + /* + * XXXMPA save PRIVATEOID/PRIVATEDNS identifier here + */ } goto next_key; } @@ -1814,7 +1851,7 @@ validate_async_done(dns_validator_t *val, isc_result_t result) { isc_result_t saved_result = result; validator_log(val, ISC_LOG_DEBUG(3), "falling back to insecurity proof"); - result = proveunsecure(val, false, false); + result = proveunsecure(val, false, false, false); if (result == DNS_R_NOTINSECURE) { result = saved_result; } @@ -1975,6 +2012,7 @@ validate_dnskey_dsset_done(dns_validator_t *val, isc_result_t result) { validator_log(val, ISC_LOG_INFO, "no valid signature found (DS)"); result = DNS_R_NOVALIDSIG; + break; } if (val->dsset == &val->fdsset) { @@ -1991,6 +2029,9 @@ validate_dnskey_dsset(dns_validator_t *val) { dns_rdata_t keyrdata = DNS_RDATA_INIT; isc_result_t result; dns_rdata_ds_t ds; + dns_rdata_dnskey_t key; + unsigned char *data = 0; + unsigned int datalen = 0; dns_rdata_reset(&dsrdata); dns_rdataset_current(val->dsset, &dsrdata); @@ -2010,13 +2051,37 @@ validate_dnskey_dsset(dns_validator_t *val) { return DNS_R_BADALG; } - if (!dns_resolver_algorithm_supported(val->view->resolver, val->name, - ds.algorithm)) - { - if (val->unsupported_algorithm == 0) { - val->unsupported_algorithm = ds.algorithm; + switch (ds.algorithm) { + case DNS_KEYALG_PRIVATEDNS: + case DNS_KEYALG_PRIVATEOID: + switch (ds.digest_type) { +#if defined(DNS_DSDIGEST_SHA256PRIVATE) && defined(DNS_DSDIGEST_SHA384PRIVATE) + case DNS_DSDIGEST_SHA256PRIVATE: + case DNS_DSDIGEST_SHA384PRIVATE: + data = ds.digest; + datalen = ds.length; + break; +#endif + default: + break; + } + break; + default: + break; + } + + if (data != NULL || (ds.algorithm != DNS_KEYALG_PRIVATEDNS && + ds.algorithm != DNS_KEYALG_PRIVATEOID)) + { + if (!dns_resolver_algorithm_supported(val->view->resolver, + val->name, ds.algorithm, + data, datalen)) + { + if (val->unsupported_algorithm == 0) { + val->unsupported_algorithm = ds.algorithm; + } + return DNS_R_BADALG; } - return DNS_R_BADALG; } /* @@ -2029,6 +2094,28 @@ validate_dnskey_dsset(dns_validator_t *val) { return DNS_R_NOKEYMATCH; } + /* + * Figure out if the private algorithm is supported now that we have + * found a matching dnskey. + */ + dns_rdata_tostruct(&keyrdata, &key, NULL); + if (data == NULL && (ds.algorithm == DNS_KEYALG_PRIVATEDNS || + ds.algorithm == DNS_KEYALG_PRIVATEOID)) + { + if (!dns_resolver_algorithm_supported(val->view->resolver, + val->name, key.algorithm, + key.data, key.datalen)) + { + if (val->unsupported_algorithm == 0) { + val->unsupported_algorithm = key.algorithm; + /* + * XXXMPA Save PRIVATEOID / PRIVATEDNS here. + */ + } + return DNS_R_BADALG; + } + } + /* * ... and check that it signed the DNSKEY RRset. */ @@ -2213,7 +2300,8 @@ validate_dnskey(void *arg) { } if (!dns_resolver_algorithm_supported(val->view->resolver, - val->name, ds.algorithm)) + val->name, ds.algorithm, + NULL, 0)) { continue; } @@ -2894,7 +2982,31 @@ validate_nx(dns_validator_t *val, bool resume) { return DNS_R_BROKENCHAIN; } - return proveunsecure(val, false, false); + return proveunsecure(val, false, false, false); +} + +/* + * Check if any of the DS records has a private DNSSEC algorithm. + */ +static bool +check_ds_private(dns_rdataset_t *rdataset) { + dns_rdata_ds_t ds; + isc_result_t result; + + for (result = dns_rdataset_first(rdataset); result == ISC_R_SUCCESS; + result = dns_rdataset_next(rdataset)) + { + dns_rdata_t rdata = DNS_RDATA_INIT; + dns_rdataset_current(rdataset, &rdata); + result = dns_rdata_tostruct(&rdata, &ds, NULL); + RUNTIME_CHECK(result == ISC_R_SUCCESS); + if (ds.algorithm == DNS_KEYALG_PRIVATEDNS || + ds.algorithm == DNS_KEYALG_PRIVATEOID) + { + return true; + } + } + return false; } /*% @@ -2903,21 +3015,72 @@ validate_nx(dns_validator_t *val, bool resume) { */ static bool check_ds_algs(dns_validator_t *val, dns_name_t *name, - dns_rdataset_t *rdataset) { + dns_rdataset_t *dsrdataset, dns_rdataset_t *dnskeyset) { dns_rdata_ds_t ds; + dns_rdata_dnskey_t key; + bool seen_private = false; + uint16_t key_tag = 0; + uint8_t algorithm = 0; - DNS_RDATASET_FOREACH (rdataset) { + DNS_RDATASET_FOREACH (dsrdataset) { isc_result_t result; dns_rdata_t dsrdata = DNS_RDATA_INIT; - dns_rdataset_current(rdataset, &dsrdata); + dns_rdata_t keyrdata = DNS_RDATA_INIT; + unsigned char *data = NULL; + size_t datalen = 0; + dns_rdataset_current(dsrdataset, &dsrdata); result = dns_rdata_tostruct(&dsrdata, &ds, NULL); RUNTIME_CHECK(result == ISC_R_SUCCESS); + /* + * Look for a matching DNSKEY to find the PRIVATE + * DNSSEC algorithm. + */ + if (ds.algorithm == DNS_KEYALG_PRIVATEOID || + ds.algorithm == DNS_KEYALG_PRIVATEDNS) + { + switch (ds.digest_type) { +#if defined(DNS_DSDIGEST_SHA256PRIVATE) && defined(DNS_DSDIGEST_SHA384PRIVATE) + case DNS_DSDIGEST_SHA256PRIVATE: + case DNS_DSDIGEST_SHA384PRIVATE: + data = ds.digest; + datalen = ds.length; + break; +#endif + case DNS_DSDIGEST_SHA1: + case DNS_DSDIGEST_SHA256: + case DNS_DSDIGEST_SHA384: + if (dnskeyset == NULL) { + algorithm = ds.algorithm; + key_tag = ds.key_tag; + seen_private = true; + continue; + } + result = dns_dnssec_matchdskey( + name, &dsrdata, dnskeyset, &keyrdata); + if (result != ISC_R_SUCCESS) { + algorithm = ds.algorithm; + key_tag = ds.key_tag; + seen_private = true; + continue; + } + result = dns_rdata_tostruct(&keyrdata, &key, + NULL); + RUNTIME_CHECK(result == ISC_R_SUCCESS); + data = key.data; + datalen = key.datalen; + break; + default: + break; + } + } + if (dns_resolver_ds_digest_supported(val->view->resolver, name, ds.digest_type) && dns_resolver_algorithm_supported(val->view->resolver, name, - ds.algorithm)) + ds.algorithm, data, + datalen)) { return true; } @@ -2928,8 +3091,26 @@ check_ds_algs(dns_validator_t *val, dns_name_t *name, * unsecure flow always runs after a validate/validatenx flow. So if an * unsupported alg/digest was found while building the chain of trust, * it would be raised already. + * + * If we have seen a private algorithm for which we couldn't find a + * DNSKEY nor extract it from the digest field we must assume the child + * zone is secure. With PRIVATEDNS and PRIVATEOID we can make that + * determination if we match a DNSKEY for every DS with these algorithms + * or extract the algorithm from the digest field. Since we don't know + * whether the private algorithm is unsupported or not, we are required + * to treat it as supported. */ - return false; + if (seen_private) { + char namebuf[DNS_NAME_FORMATSIZE]; + dns_name_format(name, namebuf, sizeof(namebuf)); + validator_log(val, ISC_LOG_INFO, + "No DNSKEY for %s/DS with %s algorithm, tag %u", + namebuf, + algorithm == DNS_KEYALG_PRIVATEDNS ? "PRIVATEDNS" + : "PRIVATEOID", + key_tag); + } + return seen_private; } /*% @@ -2972,7 +3153,49 @@ seek_ds(dns_validator_t *val, isc_result_t *resp) { * validated, continue walking down labels. */ if (val->frdataset.trust >= dns_trust_secure) { - if (!check_ds_algs(val, tname, &val->frdataset)) { + dns_rdataset_t *dssetp = &val->frdataset, + *keysetp = NULL; + + if (check_ds_private(&val->frdataset)) { + if (dns_rdataset_isassociated(&val->dsrdataset)) + { + dns_rdataset_disassociate( + &val->dsrdataset); + } + dns_rdataset_clone(&val->frdataset, + &val->dsrdataset); + dssetp = &val->dsrdataset; + dns_rdataset_disassociate(&val->frdataset); + result = view_find(val, tname, + dns_rdatatype_dnskey); + switch (result) { + case ISC_R_SUCCESS: + keysetp = &val->frdataset; + break; + case ISC_R_NOTFOUND: + /* + * We don't know anything about the + * DNSKEY. Find it. + */ + *resp = DNS_R_WAIT; + result = create_fetch( + val, tname, + dns_rdatatype_dnskey, + fetch_callback_dnskey, + "seek_ds"); + if (result != ISC_R_SUCCESS) { + *resp = result; + } + return ISC_R_COMPLETE; + break; + default: + validator_log(val, ISC_LOG_DEBUG(3), + "no DNSKEY found (%s/DS)", + namebuf); + break; + } + } + if (!check_ds_algs(val, tname, dssetp, keysetp)) { validator_log( val, ISC_LOG_DEBUG(3), "no supported algorithm/digest (%s/DS)", @@ -3161,13 +3384,16 @@ seek_ds(dns_validator_t *val, isc_result_t *resp) { * \li DNS_R_BROKENCHAIN */ static isc_result_t -proveunsecure(dns_validator_t *val, bool have_ds, bool resume) { +proveunsecure(dns_validator_t *val, bool have_ds, bool have_dnskey, + bool resume) { isc_result_t result; char namebuf[DNS_NAME_FORMATSIZE]; dns_fixedname_t fixedsecroot; dns_name_t *secroot = dns_fixedname_initname(&fixedsecroot); unsigned int labels; + INSIST(!(have_ds && have_dnskey)); + /* * We're attempting to prove insecurity. */ @@ -3207,17 +3433,65 @@ proveunsecure(dns_validator_t *val, bool have_ds, bool resume) { * it has a supported algorithm combination. If not, this is * an insecure delegation as far as this resolver is concerned. */ - if (have_ds && val->frdataset.trust >= dns_trust_secure && - !check_ds_algs(val, dns_fixedname_name(&val->fname), - &val->frdataset)) + if (have_dnskey || + (have_ds && val->frdataset.trust >= dns_trust_secure)) { - dns_name_format(dns_fixedname_name(&val->fname), - namebuf, sizeof(namebuf)); - validator_log(val, ISC_LOG_DEBUG(3), - "no supported algorithm/digest (%s/DS)", - namebuf); - result = markanswer(val, "proveunsecure (2)"); - goto out; + dns_rdataset_t *dssetp = NULL, *keysetp = NULL; + dns_name_t *fname = dns_fixedname_name(&val->fname); + if (have_dnskey) { + dssetp = &val->dsrdataset; + keysetp = &val->frdataset; + } else { + dssetp = &val->frdataset; + } + + if (!have_dnskey && check_ds_private(&val->frdataset)) { + if (dns_rdataset_isassociated(&val->dsrdataset)) + { + dns_rdataset_disassociate( + &val->dsrdataset); + } + dns_rdataset_clone(&val->frdataset, + &val->dsrdataset); + dssetp = &val->dsrdataset; + dns_rdataset_disassociate(&val->frdataset); + result = view_find(val, fname, + dns_rdatatype_dnskey); + switch (result) { + case ISC_R_SUCCESS: + keysetp = &val->frdataset; + break; + case ISC_R_NOTFOUND: + /* + * We don't know anything about the + * DNSKEY. Find it. + */ + result = create_fetch( + val, fname, + dns_rdatatype_dnskey, + fetch_callback_dnskey, + "seek_ds"); + if (result == ISC_R_SUCCESS) { + result = DNS_R_WAIT; + } + goto out; + default: + validator_log(val, ISC_LOG_DEBUG(3), + "no DNSKEY found (%s/DS)", + namebuf); + break; + } + } + if (!check_ds_algs(val, fname, dssetp, keysetp)) { + dns_name_format(fname, namebuf, + sizeof(namebuf)); + validator_log( + val, ISC_LOG_DEBUG(3), + "no supported algorithm/digest (%s/DS)", + namebuf); + result = markanswer(val, "proveunsecure (2)"); + goto out; + } } val->labels++; } @@ -3308,7 +3582,7 @@ validator_start(void *arg) { validator_log(val, ISC_LOG_DEBUG(3), "attempting insecurity proof"); - result = proveunsecure(val, false, false); + result = proveunsecure(val, false, false, false); if (result == DNS_R_NOTINSECURE) { validator_log(val, ISC_LOG_INFO, "got insecure response; " @@ -3421,6 +3695,7 @@ dns_validator_create(dns_view_t *view, dns_name_t *name, dns_rdatatype_t type, dns_rdataset_init(&val->fdsset); dns_rdataset_init(&val->frdataset); dns_rdataset_init(&val->fsigrdataset); + dns_rdataset_init(&val->dsrdataset); dns_fixedname_init(&val->wild); dns_fixedname_init(&val->closest); val->start = isc_stdtime_now(); @@ -3496,6 +3771,9 @@ destroy_validator(dns_validator_t *val) { dns_keytable_detach(&val->keytable); } disassociate_rdatasets(val); + if (dns_rdataset_isassociated(&val->dsrdataset)) { + dns_rdataset_disassociate(&val->dsrdataset); + } mctx = val->view->mctx; if (val->siginfo != NULL) { isc_mem_put(mctx, val->siginfo, sizeof(*val->siginfo)); diff --git a/lib/dns/view.c b/lib/dns/view.c index 12b02669e5..36619b2989 100644 --- a/lib/dns/view.c +++ b/lib/dns/view.c @@ -1648,9 +1648,9 @@ dns_view_istrusted(dns_view_t *view, const dns_name_t *keyname, goto finish; } - result = dns_ds_fromkeyrdata(keyname, &rdata, - DNS_DSDIGEST_SHA256, - digest, &ds); + result = dns_ds_fromkeyrdata( + keyname, &rdata, DNS_DSDIGEST_SHA256, digest, + sizeof(digest), &ds); if (result != ISC_R_SUCCESS) { goto finish; } @@ -2311,7 +2311,7 @@ dns_view_addtrustedkey(dns_view_t *view, dns_rdatatype_t rdtype, isc_result_t result; dns_name_t *name = UNCONST(keyname); char rdatabuf[DST_KEY_MAXSIZE]; - unsigned char digest[ISC_MAX_MD_SIZE]; + unsigned char digest[DNS_DS_BUFFERSIZE]; dns_rdata_ds_t ds; dns_rdata_t rdata; isc_buffer_t b; @@ -2334,7 +2334,7 @@ dns_view_addtrustedkey(dns_view_t *view, dns_rdatatype_t rdtype, CHECK(dns_rdata_tostruct(&rdata, &ds, NULL)); } else { CHECK(dns_ds_fromkeyrdata(name, &rdata, DNS_DSDIGEST_SHA256, - digest, &ds)); + digest, sizeof(digest), &ds)); } CHECK(dns_keytable_add(view->secroots_priv, false, false, name, &ds, diff --git a/lib/dns/zone.c b/lib/dns/zone.c index edb2a9d12d..ffe1b65887 100644 --- a/lib/dns/zone.c +++ b/lib/dns/zone.c @@ -728,7 +728,7 @@ struct dns_signing { unsigned int magic; dns_db_t *db; dns_dbiterator_t *dbiterator; - dns_secalg_t algorithm; + dst_algorithm_t algorithm; uint16_t keyid; bool deleteit; bool done; @@ -972,7 +972,7 @@ zone_notify(dns_zone_t *zone, isc_time_t *now); static void dump_done(void *arg, isc_result_t result); static isc_result_t -zone_signwithkey(dns_zone_t *zone, dns_secalg_t algorithm, uint16_t keyid, +zone_signwithkey(dns_zone_t *zone, dst_algorithm_t algorithm, uint16_t keyid, bool deleteit); static isc_result_t delete_nsec(dns_db_t *db, dns_dbversion_t *ver, dns_dbnode_t *node, @@ -3755,6 +3755,9 @@ cleanup: } } +#define OLD_SIGNING_RECORD_SIZE 5 +#define SIGNING_RECORD_SIZE 7 + static void resume_signingwithkey(dns_zone_t *zone) { dns_dbnode_t *node = NULL; @@ -3788,14 +3791,22 @@ resume_signingwithkey(dns_zone_t *zone) { DNS_RDATASET_FOREACH (&rdataset) { dns_rdata_t rdata = DNS_RDATA_INIT; + dst_algorithm_t alg; + dns_rdataset_current(&rdataset, &rdata); - if (rdata.length != 5 || rdata.data[0] == 0 || - rdata.data[4] != 0) + /* + * Old or New Forms + */ + if ((rdata.length != OLD_SIGNING_RECORD_SIZE && + rdata.length != SIGNING_RECORD_SIZE) || + rdata.data[0] == 0 || rdata.data[4] != 0) { continue; } - - result = zone_signwithkey(zone, rdata.data[0], + alg = (rdata.length == OLD_SIGNING_RECORD_SIZE) + ? rdata.data[0] + : ((rdata.data[5] << 8) | rdata.data[6]); + result = zone_signwithkey(zone, alg, (rdata.data[1] << 8) | rdata.data[2], rdata.data[3]); if (result != ISC_R_SUCCESS) { @@ -4422,7 +4433,7 @@ trust_key(dns_zone_t *zone, dns_name_t *keyname, dns_rdata_dnskey_t *dnskey, bool initial) { isc_result_t result; dns_rdata_t rdata = DNS_RDATA_INIT; - unsigned char data[4096], digest[ISC_MAX_MD_SIZE]; + unsigned char data[4096], digest[DNS_DS_BUFFERSIZE]; isc_buffer_t buffer; dns_keytable_t *sr = NULL; dns_rdata_ds_t ds; @@ -4437,7 +4448,7 @@ trust_key(dns_zone_t *zone, dns_name_t *keyname, dns_rdata_dnskey_t *dnskey, dns_rdata_fromstruct(&rdata, dnskey->common.rdclass, dns_rdatatype_dnskey, dnskey, &buffer); CHECK(dns_ds_fromkeyrdata(keyname, &rdata, DNS_DSDIGEST_SHA256, digest, - &ds)); + sizeof(digest), &ds)); CHECK(dns_keytable_add(sr, true, initial, keyname, &ds, sfd_add, zone->view)); @@ -6806,6 +6817,10 @@ delsig_ok(dns_rdata_rrsig_t *rrsig_ptr, dst_key_t **keys, unsigned int nkeys, isc_result_t ret; bool have_ksk = false, have_zsk = false; bool have_pksk = false, have_pzsk = false; + dst_algorithm_t algorithm; + + algorithm = dst_algorithm_fromdata( + rrsig_ptr->algorithm, rrsig_ptr->signature, rrsig_ptr->siglen); for (i = 0; i < nkeys; i++) { bool ksk, zsk; @@ -6814,7 +6829,7 @@ delsig_ok(dns_rdata_rrsig_t *rrsig_ptr, dst_key_t **keys, unsigned int nkeys, break; } - if (rrsig_ptr->algorithm != dst_key_alg(keys[i])) { + if (algorithm != dst_key_alg(keys[i])) { continue; } @@ -6873,7 +6888,7 @@ delsig_ok(dns_rdata_rrsig_t *rrsig_ptr, dst_key_t **keys, unsigned int nkeys, * if the associated public key is still in the DNSKEY RRset */ for (i = 0; i < nkeys; i++) { - if ((rrsig_ptr->algorithm == dst_key_alg(keys[i])) && + if ((algorithm == dst_key_alg(keys[i])) && (rrsig_ptr->keyid == dst_key_id(keys[i]))) { return false; @@ -6936,10 +6951,13 @@ del_sigs(dns_zone_t *zone, dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name, DNS_RDATASET_FOREACH (&rdataset) { dns_rdata_t rdata = DNS_RDATA_INIT; + dst_algorithm_t algorithm; dns_rdataset_current(&rdataset, &rdata); result = dns_rdata_tostruct(&rdata, &rrsig, NULL); RUNTIME_CHECK(result == ISC_R_SUCCESS); + algorithm = dst_algorithm_fromdata( + rrsig.algorithm, rrsig.signature, rrsig.siglen); if (!dns_rdatatype_iskeymaterial(type)) { bool warn = false, deleted = false; @@ -6983,9 +7001,8 @@ del_sigs(dns_zone_t *zone, dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name, char algbuf[DNS_NAME_FORMATSIZE]; dns_name_format(&zone->origin, origin, sizeof(origin)); - dns_secalg_format(rrsig.algorithm, - algbuf, - sizeof(algbuf)); + dst_algorithm_format(algorithm, algbuf, + sizeof(algbuf)); dns_zone_log(zone, ISC_LOG_WARNING, "Key %s/%s/%d " "missing or inactive " @@ -7005,7 +7022,7 @@ del_sigs(dns_zone_t *zone, dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name, */ found = false; for (i = 0; i < nkeys; i++) { - if (rrsig.algorithm == dst_key_alg(keys[i]) && + if (algorithm == dst_key_alg(keys[i]) && rrsig.keyid == dst_key_id(keys[i])) { found = true; @@ -7618,13 +7635,16 @@ signed_with_good_key(dns_zone_t *zone, dns_db_t *db, dns_dbnode_t *node, dns_rdataset_current(&rdataset, &rdata); result = dns_rdata_tostruct(&rdata, &rrsig, NULL); INSIST(result == ISC_R_SUCCESS); - if (rrsig.algorithm == dst_key_alg(key) && + dst_algorithm_t algorithm; + algorithm = dst_algorithm_fromdata( + rrsig.algorithm, rrsig.signature, rrsig.siglen); + if (algorithm == dst_key_alg(key) && rrsig.keyid == dst_key_id(key)) { dns_rdataset_disassociate(&rdataset); return true; } - if (rrsig.algorithm == dst_key_alg(key)) { + if (algorithm == dst_key_alg(key)) { count++; } } @@ -7958,18 +7978,25 @@ updatesignwithkey(dns_zone_t *zone, dns_signing_t *signing, INSIST(!dns_rdataset_isassociated(&rdataset)); goto failure; } + DNS_RDATASET_FOREACH (&rdataset) { dns_rdata_t rdata = DNS_RDATA_INIT; + unsigned char alg = dst_algorithm_tosecalg(signing->algorithm); + dns_rdataset_current(&rdataset, &rdata); /* * If we don't match the algorithm or keyid skip the record. */ - if (rdata.length != 5 || rdata.data[0] != signing->algorithm || + if ((rdata.length != SIGNING_RECORD_SIZE && + rdata.length != OLD_SIGNING_RECORD_SIZE) || + rdata.data[0] == 0 || rdata.data[0] != alg || rdata.data[1] != ((signing->keyid >> 8) & 0xff) || - rdata.data[2] != (signing->keyid & 0xff)) + rdata.data[2] != (signing->keyid & 0xff) || + (rdata.length == SIGNING_RECORD_SIZE && + (rdata.data[5] != (signing->algorithm >> 8 & 0xff) || + rdata.data[6] != (signing->algorithm & 0xff)))) { have_rr = true; - dns_rdata_reset(&rdata); continue; } /* @@ -7999,20 +8026,32 @@ updatesignwithkey(dns_zone_t *zone, dns_signing_t *signing, * finished signing the zone with this key. If it is already * there we don't need to add it a second time. */ - unsigned char data[5] = { - signing->algorithm, + unsigned char data[SIGNING_RECORD_SIZE] = { + dst_algorithm_tosecalg(signing->algorithm), (signing->keyid >> 8) & 0xff, signing->keyid & 0xff, 0, 1, + (signing->algorithm >> 8) & 0xff, + signing->algorithm & 0xff, }; dns_rdata_t rdata = (dns_rdata_t){ - .length = sizeof(data), + .length = signing->algorithm < 256 + ? OLD_SIGNING_RECORD_SIZE + : sizeof(data), .data = data, .type = zone->privatetype, .rdclass = dns_db_class(signing->db), .link = ISC_LINK_INITIALIZER, }; + /* + * data[0] can't be 0 as that is used to signal that the + * record is being used to for NSEC/NSEC3 chains generation. + * Set it to 255 instead. + */ + if (data[0] == 0) { + data[0] = 255; + } CHECK(update_one_rr(signing->db, version, diff, DNS_DIFFOP_ADD, &zone->origin, rdataset.ttl, &rdata)); } else if (!have_rr) { @@ -9341,7 +9380,7 @@ failure: */ static isc_result_t del_sig(dns_db_t *db, dns_dbversion_t *version, dns_name_t *name, - dns_dbnode_t *node, unsigned int nkeys, dns_secalg_t algorithm, + dns_dbnode_t *node, unsigned int nkeys, dst_algorithm_t algorithm, uint16_t keyid, bool *has_algp, dns_diff_t *diff) { dns_rdata_rrsig_t rrsig; dns_rdataset_t rdataset; @@ -9382,12 +9421,17 @@ del_sig(dns_db_t *db, dns_dbversion_t *version, dns_name_t *name, } DNS_RDATASET_FOREACH (&rdataset) { dns_rdata_t rdata = DNS_RDATA_INIT; + dst_algorithm_t sigalg; + dns_rdataset_current(&rdataset, &rdata); CHECK(dns_rdata_tostruct(&rdata, &rrsig, NULL)); - if (nkeys != 0 && (rrsig.algorithm != algorithm || - rrsig.keyid != keyid)) + + sigalg = dst_algorithm_fromdata( + rrsig.algorithm, rrsig.signature, rrsig.siglen); + if (nkeys != 0 && + (sigalg != algorithm || rrsig.keyid != keyid)) { - if (rrsig.algorithm == algorithm) { + if (sigalg == algorithm) { has_alg = true; } continue; @@ -9752,7 +9796,7 @@ zone_sign(dns_zone_t *zone) { * When adding look for the specific key. */ if (!signing->deleteit && - (dst_key_alg(zone_keys[i]) != signing->algorithm || + (ALG(zone_keys[i]) != signing->algorithm || dst_key_id(zone_keys[i]) != signing->keyid)) { continue; @@ -10288,6 +10332,7 @@ revocable(dns_keyfetch_t *kfetch, dns_rdata_keydata_t *keydata) { unsigned char key_buf[4096]; isc_buffer_t keyb; bool answer = false; + dst_algorithm_t algorithm; REQUIRE(kfetch != NULL && keydata != NULL); REQUIRE(dns_rdataset_isassociated(&kfetch->dnskeysigset)); @@ -10315,7 +10360,9 @@ revocable(dns_keyfetch_t *kfetch, dns_rdata_keydata_t *keydata) { result = dns_rdata_tostruct(&sigrr, &sig, NULL); RUNTIME_CHECK(result == ISC_R_SUCCESS); - if (dst_key_alg(dstkey) == sig.algorithm && + algorithm = dst_algorithm_fromdata(sig.algorithm, sig.signature, + sig.siglen); + if (dst_key_alg(dstkey) == algorithm && dst_key_rid(dstkey) == sig.keyid) { result = dns_dnssec_verify(keyname, &kfetch->dnskeyset, @@ -16440,7 +16487,8 @@ cds_inuse(dns_zone_t *zone, dns_rdata_t *rdata, dns_dnsseckeylist_t *keylist, unsigned char cdsbuf[DNS_DS_BUFFERSIZE]; if (dst_key_id(k->key) != cds.key_tag || - dst_key_alg(k->key) != cds.algorithm) + dst_algorithm_tosecalg(dst_key_alg(k->key)) != + cds.algorithm) { continue; } @@ -16453,7 +16501,8 @@ cds_inuse(dns_zone_t *zone, dns_rdata_t *rdata, dns_dnsseckeylist_t *keylist, return result; } result = dns_ds_buildrdata(dns_zone_getorigin(zone), &dnskey, - cds.digest_type, cdsbuf, &cdsrdata); + cds.digest_type, cdsbuf, + sizeof(cdsbuf), &cdsrdata); if (result != ISC_R_SUCCESS) { dns_zone_log(zone, ISC_LOG_ERROR, "dns_ds_buildrdata(keytag=%d, algo=%d, " @@ -20093,8 +20142,8 @@ dns_zone_setnotifydelay(dns_zone_t *zone, uint32_t delay) { } isc_result_t -dns_zone_signwithkey(dns_zone_t *zone, dns_secalg_t algorithm, uint16_t keyid, - bool deleteit) { +dns_zone_signwithkey(dns_zone_t *zone, dst_algorithm_t algorithm, + uint16_t keyid, bool deleteit) { isc_result_t result; REQUIRE(DNS_ZONE_VALID(zone)); @@ -20178,7 +20227,7 @@ dns_zone_getprivatetype(dns_zone_t *zone) { } static isc_result_t -zone_signwithkey(dns_zone_t *zone, dns_secalg_t algorithm, uint16_t keyid, +zone_signwithkey(dns_zone_t *zone, dst_algorithm_t algorithm, uint16_t keyid, bool deleteit) { dns_signing_t *signing = NULL; isc_result_t result = ISC_R_SUCCESS; @@ -20335,7 +20384,7 @@ add_signing_records(dns_db_t *db, dns_rdatatype_t privatetype, isc_region_t r; isc_result_t result = ISC_R_SUCCESS; uint16_t keyid; - unsigned char buf[5]; + unsigned char data[SIGNING_RECORD_SIZE]; dns_name_t *name = dns_db_origin(db); dns_difftuplelist_t add = ISC_LIST_INITIALIZER; dns_difftuplelist_t del = ISC_LIST_INITIALIZER; @@ -20410,17 +20459,23 @@ add_signing_records(dns_db_t *db, dns_rdatatype_t privatetype, * or added. */ ISC_LIST_FOREACH (tuples, tuple, link) { + dst_algorithm_t algorithm; dns_rdata_toregion(&tuple->rdata, &r); keyid = dst_region_computeid(&r); - buf[0] = dnskey.algorithm; - buf[1] = (keyid & 0xff00) >> 8; - buf[2] = (keyid & 0xff); - buf[3] = (tuple->op == DNS_DIFFOP_ADD) ? 0 : 1; - buf[4] = 0; - rdata.data = buf; - rdata.length = sizeof(buf); + algorithm = dst_algorithm_fromdata(dnskey.algorithm, + dnskey.data, dnskey.datalen); + data[0] = dnskey.algorithm; + data[1] = (keyid & 0xff00) >> 8; + data[2] = (keyid & 0xff); + data[3] = (tuple->op == DNS_DIFFOP_ADD) ? 0 : 1; + data[4] = 0; + data[5] = (algorithm & 0xff00) >> 8; + data[6] = (algorithm & 0xff); + rdata.data = data; + rdata.length = algorithm < 256 ? OLD_SIGNING_RECORD_SIZE + : sizeof(data); rdata.type = privatetype; rdata.rdclass = tuple->rdata.rdclass; @@ -20440,7 +20495,7 @@ add_signing_records(dns_db_t *db, dns_rdatatype_t privatetype, * Remove any record which says this operation has already * completed. */ - buf[4] = 1; + data[4] = 1; CHECK(rr_exists(db, ver, name, &rdata, &flag)); if (flag) { dns_difftuple_create(diff->mctx, DNS_DIFFOP_DEL, name, @@ -20609,7 +20664,9 @@ failure: * are any signatures using that algorithm. */ static bool -signed_with_alg(dns_rdataset_t *rdataset, dns_secalg_t alg) { +signed_with_alg(dns_rdataset_t *rdataset, dst_algorithm_t alg) { + dst_algorithm_t sigalg; + REQUIRE(rdataset == NULL || rdataset->type == dns_rdatatype_rrsig); if (rdataset == NULL || !dns_rdataset_isassociated(rdataset)) { return false; @@ -20621,7 +20678,9 @@ signed_with_alg(dns_rdataset_t *rdataset, dns_secalg_t alg) { dns_rdataset_current(rdataset, &rdata); dns_rdata_tostruct(&rdata, &rrsig, NULL); - if (rrsig.algorithm == alg) { + sigalg = dst_algorithm_fromdata(rrsig.algorithm, + rrsig.signature, rrsig.siglen); + if (sigalg == alg) { return true; } } @@ -20967,13 +21026,16 @@ checkds_done(void *arg) { if (dst_key_id(key->key) != ds.key_tag) { continue; } - if (dst_key_alg(key->key) != ds.algorithm) { + if (dst_algorithm_tosecalg(dst_key_alg(key->key)) != + ds.algorithm) + { continue; } /* Derive DS from DNSKEY, see if the rdata is equal. */ make_dnskey(key->key, keybuf, sizeof(keybuf), &dnskey); r = dns_ds_buildrdata(&zone->origin, &dnskey, - ds.digest_type, dsbuf, &dsrdata); + ds.digest_type, dsbuf, + sizeof(dsbuf), &dsrdata); if (r != ISC_R_SUCCESS) { continue; } @@ -22804,7 +22866,7 @@ dns_zone_cdscheck(dns_zone_t *zone, dns_db_t *db, dns_dbversion_t *version) { isc_result_t result; dns_dbnode_t *node = NULL; dns_rdataset_t dnskey, cds, cdnskey; - unsigned char algorithms[256]; + unsigned char algorithms[DST_MAX_ALGS]; unsigned int i; bool empty = false; @@ -22879,21 +22941,49 @@ dns_zone_cdscheck(dns_zone_t *zone, dns_db_t *db, dns_dbversion_t *version) { } CHECK(dns_rdata_tostruct(&crdata, &structcds, NULL)); - if (algorithms[structcds.algorithm] == 0) { - algorithms[structcds.algorithm] = expected; - } - DNS_RDATASET_FOREACH (&dnskey) { + if (structcds.algorithm != DNS_KEYALG_PRIVATEDNS && + structcds.algorithm != DNS_KEYALG_PRIVATEOID) + { + if (algorithms[structcds.algorithm] == 0) { + algorithms[structcds.algorithm] = + expected; + } + DNS_RDATASET_FOREACH (&dnskey) { + dns_rdata_t rdata = DNS_RDATA_INIT; + dns_rdata_dnskey_t structdnskey; + + dns_rdataset_current(&dnskey, &rdata); + dns_rdata_tostruct(&rdata, + &structdnskey, NULL); + + if (structdnskey.algorithm == + structcds.algorithm) + { + algorithms[structcds.algorithm] = + found; + } + } + } else { dns_rdata_t rdata = DNS_RDATA_INIT; dns_rdata_dnskey_t structdnskey; + dst_algorithm_t dnskeyalg; - dns_rdataset_current(&dnskey, &rdata); - dns_rdata_tostruct(&rdata, &structdnskey, NULL); - - if (structdnskey.algorithm == - structcds.algorithm) - { - algorithms[structcds.algorithm] = found; + /* Convert CDS to DS */ + crdata.type = dns_rdatatype_ds; + result = dns_dnssec_matchdskey(&zone->origin, + &crdata, &dnskey, + &rdata); + if (result != ISC_R_SUCCESS) { + result = DNS_R_BADCDS; + goto failure; } + CHECK(dns_rdata_tostruct(&rdata, &structdnskey, + NULL)); + dnskeyalg = dst_algorithm_fromdata( + structdnskey.algorithm, + structdnskey.data, + structdnskey.datalen); + algorithms[dnskeyalg] = found; } } for (i = 0; i < sizeof(algorithms); i++) { @@ -22920,6 +23010,7 @@ dns_zone_cdscheck(dns_zone_t *zone, dns_db_t *db, dns_dbversion_t *version) { DNS_RDATASET_FOREACH (&cdnskey) { dns_rdata_t crdata = DNS_RDATA_INIT; dns_rdata_cdnskey_t structcdnskey; + dst_algorithm_t cdnskeyalg; dns_rdataset_current(&cdnskey, &crdata); /* @@ -22942,22 +23033,27 @@ dns_zone_cdscheck(dns_zone_t *zone, dns_db_t *db, dns_dbversion_t *version) { CHECK(dns_rdata_tostruct(&crdata, &structcdnskey, NULL)); - if (algorithms[structcdnskey.algorithm] == 0) { - algorithms[structcdnskey.algorithm] = expected; + cdnskeyalg = dst_algorithm_fromdata( + structcdnskey.algorithm, structcdnskey.data, + structcdnskey.datalen); + if (algorithms[cdnskeyalg] == 0) { + algorithms[cdnskeyalg] = expected; } DNS_RDATASET_FOREACH (&dnskey) { dns_rdata_t rdata = DNS_RDATA_INIT; dns_rdata_dnskey_t structdnskey; + dst_algorithm_t dnskeyalg; dns_rdataset_current(&dnskey, &rdata); CHECK(dns_rdata_tostruct(&rdata, &structdnskey, NULL)); + dnskeyalg = dst_algorithm_fromdata( + structdnskey.algorithm, + structdnskey.data, + structdnskey.datalen); - if (structdnskey.algorithm == - structcdnskey.algorithm) - { - algorithms[structcdnskey.algorithm] = - found; + if (dnskeyalg == cdnskeyalg) { + algorithms[cdnskeyalg] = found; } } } @@ -23211,7 +23307,7 @@ dns_zone_issecure(dns_zone_t *zone) { struct keydone { bool all; - unsigned char data[5]; + unsigned char data[SIGNING_RECORD_SIZE]; dns_zone_t *zone; }; @@ -23279,8 +23375,11 @@ keydone(void *arg) { dns_rdataset_current(&rdataset, &rdata); if (kd->all) { - if (rdata.length == 5 && rdata.data[0] != 0 && - rdata.data[3] == 0 && rdata.data[4] == 1) + /* Old (5) and new (7) forms */ + if ((rdata.length == OLD_SIGNING_RECORD_SIZE || + rdata.length == SIGNING_RECORD_SIZE) && + rdata.data[0] != 0 && rdata.data[3] == 0 && + rdata.data[4] == 1) { found = true; } else if (rdata.data[0] == 0 && @@ -23289,8 +23388,14 @@ keydone(void *arg) { found = true; clear_pending = true; } - } else if (rdata.length == 5 && - memcmp(rdata.data, kd->data, 5) == 0) + } else if (rdata.length == OLD_SIGNING_RECORD_SIZE && + memcmp(rdata.data, kd->data, + OLD_SIGNING_RECORD_SIZE) == 0) + { + found = true; + } else if (rdata.length == SIGNING_RECORD_SIZE && + memcmp(rdata.data, kd->data, SIGNING_RECORD_SIZE) == + 0) { found = true; } @@ -23367,7 +23472,7 @@ dns_zone_keydone(dns_zone_t *zone, const char *keystr) { isc_textregion_t r; const char *algstr = NULL; dns_keytag_t keyid; - dns_secalg_t alg; + dst_algorithm_t alg; size_t n; n = sscanf(keystr, "%hu/", &keyid); @@ -23382,20 +23487,20 @@ dns_zone_keydone(dns_zone_t *zone, const char *keystr) { CHECK(ISC_R_FAILURE); } - n = sscanf(algstr, "%hhu", &alg); + n = sscanf(algstr, "%u", &alg); if (n == 0U) { r.base = UNCONST(algstr); r.length = strlen(algstr); - CHECK(dns_secalg_fromtext(&alg, &r)); + CHECK(dst_algorithm_fromtext(&alg, &r)); } /* construct a private-type rdata */ isc_buffer_init(&b, kd->data, sizeof(kd->data)); - isc_buffer_putuint8(&b, alg); - isc_buffer_putuint8(&b, (keyid & 0xff00) >> 8); - isc_buffer_putuint8(&b, (keyid & 0xff)); + isc_buffer_putuint8(&b, dst_algorithm_tosecalg(alg)); + isc_buffer_putuint16(&b, keyid); isc_buffer_putuint8(&b, 0); isc_buffer_putuint8(&b, 1); + isc_buffer_putuint16(&b, alg); } zone_iattach(zone, &kd->zone); diff --git a/lib/dns/zoneverify.c b/lib/dns/zoneverify.c index aeb010746a..9acab371e7 100644 --- a/lib/dns/zoneverify.c +++ b/lib/dns/zoneverify.c @@ -68,14 +68,14 @@ typedef struct vctx { dns_rdataset_t nsecsigs; dns_rdataset_t nsec3paramset; dns_rdataset_t nsec3paramsigs; - unsigned char revoked_ksk[256]; - unsigned char revoked_zsk[256]; - unsigned char standby_ksk[256]; - unsigned char standby_zsk[256]; - unsigned char ksk_algorithms[256]; - unsigned char zsk_algorithms[256]; - unsigned char bad_algorithms[256]; - unsigned char act_algorithms[256]; + unsigned char revoked_ksk[DST_MAX_ALGS]; + unsigned char revoked_zsk[DST_MAX_ALGS]; + unsigned char standby_ksk[DST_MAX_ALGS]; + unsigned char standby_zsk[DST_MAX_ALGS]; + unsigned char ksk_algorithms[DST_MAX_ALGS]; + unsigned char zsk_algorithms[DST_MAX_ALGS]; + unsigned char bad_algorithms[DST_MAX_ALGS]; + unsigned char act_algorithms[DST_MAX_ALGS]; isc_heap_t *expected_chains; isc_heap_t *found_chains; } vctx_t; @@ -173,12 +173,16 @@ goodsig(const vctx_t *vctx, dns_rdata_t *sigrdata, const dns_name_t *name, dst_key_t **dstkeys, size_t nkeys, dns_rdataset_t *rdataset) { dns_rdata_rrsig_t sig; isc_result_t result; + dst_algorithm_t algorithm; result = dns_rdata_tostruct(sigrdata, &sig, NULL); RUNTIME_CHECK(result == ISC_R_SUCCESS); + algorithm = dst_algorithm_fromdata(sig.algorithm, sig.signature, + sig.siglen); + for (size_t key = 0; key < nkeys; key++) { - if (sig.algorithm != dst_key_alg(dstkeys[key]) || + if (algorithm != dst_key_alg(dstkeys[key]) || sig.keyid != dst_key_id(dstkeys[key]) || !dns_name_equal(&sig.signer, vctx->origin)) { @@ -792,7 +796,7 @@ verifynsec3s(const vctx_t *vctx, const dns_name_t *name, static isc_result_t verifyset(vctx_t *vctx, dns_rdataset_t *rdataset, const dns_name_t *name, dns_dbnode_t *node, dst_key_t **dstkeys, size_t nkeys) { - unsigned char set_algorithms[256] = { 0 }; + unsigned char set_algorithms[DST_MAX_ALGS] = { 0 }; char namebuf[DNS_NAME_FORMATSIZE]; char algbuf[DNS_SECALG_FORMATSIZE]; char typebuf[DNS_RDATATYPE_FORMATSIZE]; @@ -835,6 +839,7 @@ verifyset(vctx_t *vctx, dns_rdataset_t *rdataset, const dns_name_t *name, DNS_RDATASET_FOREACH (&sigrdataset) { dns_rdata_t rdata = DNS_RDATA_INIT; dns_rdata_rrsig_t sig; + dst_algorithm_t algorithm; dns_rdataset_current(&sigrdataset, &rdata); result = dns_rdata_tostruct(&rdata, &sig, NULL); @@ -849,15 +854,17 @@ verifyset(vctx_t *vctx, dns_rdataset_t *rdataset, const dns_name_t *name, namebuf, typebuf, sig.keyid); continue; } - if ((set_algorithms[sig.algorithm] != 0) || - (vctx->act_algorithms[sig.algorithm] == 0)) + algorithm = dst_algorithm_fromdata(sig.algorithm, sig.signature, + sig.siglen); + if ((set_algorithms[algorithm] != 0) || + (vctx->act_algorithms[algorithm] == 0)) { continue; } if (goodsig(vctx, &rdata, name, dstkeys, nkeys, rdataset)) { dns_rdataset_settrust(rdataset, dns_trust_secure); dns_rdataset_settrust(&sigrdataset, dns_trust_secure); - set_algorithms[sig.algorithm] = 1; + set_algorithms[algorithm] = 1; } } result = ISC_R_SUCCESS; @@ -871,7 +878,7 @@ verifyset(vctx_t *vctx, dns_rdataset_t *rdataset, const dns_name_t *name, if ((vctx->act_algorithms[i] != 0) && (set_algorithms[i] == 0)) { - dns_secalg_format(i, algbuf, sizeof(algbuf)); + dst_algorithm_format(i, algbuf, sizeof(algbuf)); zoneverify_log_error(vctx, "No correct %s signature " "for %s %s", @@ -1425,10 +1432,13 @@ check_dnskey_sigs(vctx_t *vctx, const dns_rdata_dnskey_t *dnskey, dst_key_t *key = NULL; isc_result_t result; dns_rdataset_t dsset; + dst_algorithm_t algorithm; active_keys = (is_ksk ? vctx->ksk_algorithms : vctx->zsk_algorithms); standby_keys = (is_ksk ? vctx->standby_ksk : vctx->standby_zsk); goodkey = (is_ksk ? &vctx->goodksk : &vctx->goodzsk); + algorithm = dst_algorithm_fromdata(dnskey->algorithm, dnskey->data, + dnskey->datalen); /* * First, does this key sign the DNSKEY rrset? @@ -1440,19 +1450,19 @@ check_dnskey_sigs(vctx_t *vctx, const dns_rdata_dnskey_t *dnskey, dns_dnssec_signs(keyrdata, vctx->origin, &vctx->soaset, &vctx->soasigs, false, vctx->mctx)) { - if (active_keys[dnskey->algorithm] != DNS_KEYALG_MAX) { - active_keys[dnskey->algorithm]++; + if (active_keys[algorithm] != DNS_KEYALG_MAX) { + active_keys[algorithm]++; } } else { - if (standby_keys[dnskey->algorithm] != DNS_KEYALG_MAX) { - standby_keys[dnskey->algorithm]++; + if (standby_keys[algorithm] != DNS_KEYALG_MAX) { + standby_keys[algorithm]++; } } return; } - if (active_keys[dnskey->algorithm] != DNS_KEYALG_MAX) { - active_keys[dnskey->algorithm]++; + if (active_keys[algorithm] != DNS_KEYALG_MAX) { + active_keys[algorithm]++; } /* @@ -1503,14 +1513,15 @@ check_dnskey_sigs(vctx_t *vctx, const dns_rdata_dnskey_t *dnskey, RUNTIME_CHECK(result == ISC_R_SUCCESS); if (ds.key_tag != dst_key_id(key) || - ds.algorithm != dst_key_alg(key)) + ds.algorithm != + dst_algorithm_tosecalg(dst_key_alg(key))) { continue; } result = dns_ds_buildrdata(vctx->origin, keyrdata, ds.digest_type, buf, - &newdsrdata); + sizeof(buf), &newdsrdata); if (result != ISC_R_SUCCESS) { continue; } @@ -1558,6 +1569,7 @@ check_dnskey(vctx_t *vctx) { if ((dnskey.flags & DNS_KEYOWNER_ZONE) != 0 && (dnskey.flags & DNS_KEYFLAG_REVOKE) != 0) { + dst_algorithm_t algorithm; if ((dnskey.flags & DNS_KEYFLAG_KSK) != 0 && !dns_dnssec_selfsigns(&rdata, vctx->origin, &vctx->keyset, &vctx->keysigs, @@ -1586,16 +1598,17 @@ check_dnskey(vctx_t *vctx) { buffer); return ISC_R_FAILURE; } + algorithm = dst_algorithm_fromdata( + dnskey.algorithm, dnskey.data, dnskey.datalen); if ((dnskey.flags & DNS_KEYFLAG_KSK) != 0 && - vctx->revoked_ksk[dnskey.algorithm] != - DNS_KEYALG_MAX) + vctx->revoked_ksk[algorithm] != DNS_KEYALG_MAX) { - vctx->revoked_ksk[dnskey.algorithm]++; + vctx->revoked_ksk[algorithm]++; } else if ((dnskey.flags & DNS_KEYFLAG_KSK) == 0 && - vctx->revoked_zsk[dnskey.algorithm] != + vctx->revoked_zsk[algorithm] != DNS_KEYALG_MAX) { - vctx->revoked_zsk[dnskey.algorithm]++; + vctx->revoked_zsk[algorithm]++; } } else { check_dnskey_sigs(vctx, &dnskey, &rdata, is_ksk); @@ -1627,7 +1640,7 @@ determine_active_algorithms(vctx_t *vctx, bool ignore_kskflag, : 0; } if (vctx->act_algorithms[i] != 0) { - dns_secalg_format(i, algbuf, sizeof(algbuf)); + dst_algorithm_format(i, algbuf, sizeof(algbuf)); report("- %s", algbuf); } } @@ -1646,7 +1659,7 @@ determine_active_algorithms(vctx_t *vctx, bool ignore_kskflag, { continue; } - dns_secalg_format(i, algbuf, sizeof(algbuf)); + dst_algorithm_format(i, algbuf, sizeof(algbuf)); zoneverify_log_error(vctx, "Missing %s for algorithm %s", (vctx->ksk_algorithms[i] != 0) ? "ZSK" : "self-" @@ -1889,7 +1902,7 @@ check_bad_algorithms(const vctx_t *vctx, void (*report)(const char *, ...)) { report("The zone is not fully signed " "for the following algorithms:"); } - dns_secalg_format(i, algbuf, sizeof(algbuf)); + dst_algorithm_format(i, algbuf, sizeof(algbuf)); report(" %s", algbuf); first = false; } @@ -1916,7 +1929,7 @@ print_summary(const vctx_t *vctx, bool keyset_kskonly, { continue; } - dns_secalg_format(i, algbuf, sizeof(algbuf)); + dst_algorithm_format(i, algbuf, sizeof(algbuf)); report("Algorithm: %s: KSKs: " "%u active, %u stand-by, %u revoked", algbuf, vctx->ksk_algorithms[i], vctx->standby_ksk[i], diff --git a/lib/isccfg/check.c b/lib/isccfg/check.c index 1e82acf9d3..af78c88c42 100644 --- a/lib/isccfg/check.c +++ b/lib/isccfg/check.c @@ -365,12 +365,12 @@ disabled_algorithms(const cfg_obj_t *disabled) { CFG_LIST_FOREACH (obj, element) { isc_textregion_t r; - dns_secalg_t alg; + dst_algorithm_t alg; r.base = UNCONST(cfg_obj_asstring(cfg_listelt_value(element))); r.length = strlen(r.base); - tresult = dns_secalg_fromtext(&alg, &r); + tresult = dst_algorithm_fromtext(&alg, &r); if (tresult != ISC_R_SUCCESS) { cfg_obj_log(cfg_listelt_value(element), ISC_LOG_ERROR, "invalid algorithm '%s'", r.base); diff --git a/lib/isccfg/kaspconf.c b/lib/isccfg/kaspconf.c index 8b5bd53f6d..4b424d4bfb 100644 --- a/lib/isccfg/kaspconf.c +++ b/lib/isccfg/kaspconf.c @@ -31,6 +31,8 @@ #include #include +#include + #include #include #include @@ -127,7 +129,7 @@ cfg_kaspkey_fromconfig(const cfg_obj_t *config, dns_kasp_t *kasp, INSIST(!offline_ksk); key->role |= DNS_KASP_KEY_ROLE_KSK | DNS_KASP_KEY_ROLE_ZSK; key->lifetime = 0; /* unlimited */ - key->algorithm = DNS_KEYALG_ECDSA256; + key->algorithm = DST_ALG_ECDSA256; key->length = -1; result = dns_keystorelist_find(keystorelist, DNS_KEYSTORE_KEYDIRECTORY, @@ -217,8 +219,8 @@ cfg_kaspkey_fromconfig(const cfg_obj_t *config, dns_kasp_t *kasp, obj = cfg_tuple_get(config, "algorithm"); alg.base = cfg_obj_asstring(obj); alg.length = strlen(alg.base); - result = dns_secalg_fromtext(&key->algorithm, - (isc_textregion_t *)&alg); + result = dst_algorithm_fromtext(&key->algorithm, + (isc_textregion_t *)&alg); if (result != ISC_R_SUCCESS) { cfg_obj_log(obj, ISC_LOG_ERROR, "dnssec-policy: bad algorithm %s", @@ -226,10 +228,9 @@ cfg_kaspkey_fromconfig(const cfg_obj_t *config, dns_kasp_t *kasp, result = DNS_R_BADALG; goto cleanup; } - if (check_algorithms && isc_crypto_fips_mode() && - (key->algorithm == DNS_KEYALG_RSASHA1 || - key->algorithm == DNS_KEYALG_NSEC3RSASHA1)) + (key->algorithm == DST_ALG_RSASHA1 || + key->algorithm == DST_ALG_NSEC3RSASHA1)) { cfg_obj_log(obj, ISC_LOG_ERROR, "dnssec-policy: algorithm %s not supported " @@ -255,14 +256,16 @@ cfg_kaspkey_fromconfig(const cfg_obj_t *config, dns_kasp_t *kasp, size = cfg_obj_asuint32(obj); switch (key->algorithm) { - case DNS_KEYALG_RSASHA1: - case DNS_KEYALG_NSEC3RSASHA1: - case DNS_KEYALG_RSASHA256: - case DNS_KEYALG_RSASHA512: + case DST_ALG_RSASHA1: + case DST_ALG_NSEC3RSASHA1: + case DST_ALG_RSASHA256: + case DST_ALG_RSASHA512: + case DST_ALG_RSASHA256PRIVATEOID: + case DST_ALG_RSASHA512PRIVATEOID: if (isc_crypto_fips_mode()) { min = 2048; } else { - min = DNS_KEYALG_RSASHA512 ? 1024 : 512; + min = DST_ALG_RSASHA512 ? 1024 : 512; } if (size < min || size > 4096) { cfg_obj_log(obj, ISC_LOG_ERROR, @@ -274,10 +277,10 @@ cfg_kaspkey_fromconfig(const cfg_obj_t *config, dns_kasp_t *kasp, goto cleanup; } break; - case DNS_KEYALG_ECDSA256: - case DNS_KEYALG_ECDSA384: - case DNS_KEYALG_ED25519: - case DNS_KEYALG_ED448: + case DST_ALG_ECDSA256: + case DST_ALG_ECDSA384: + case DST_ALG_ED25519: + case DST_ALG_ED448: cfg_obj_log(obj, ISC_LOG_WARNING, "dnssec-policy: key algorithm %s " "has predefined length; ignoring " @@ -356,8 +359,8 @@ cfg_nsec3param_fromconfig(const cfg_obj_t *config, dns_kasp_t *kasp) { } /* NSEC3 cannot be used with certain key algorithms. */ - if (keyalg == DNS_KEYALG_RSAMD5 || keyalg == DNS_KEYALG_DSA || - keyalg == DNS_KEYALG_RSASHA1) + if (keyalg == DST_ALG_RSAMD5 || keyalg == DST_ALG_DSA || + keyalg == DST_ALG_RSASHA1) { badalg = keyalg; } @@ -624,8 +627,8 @@ cfg_kasp_fromconfig(const cfg_obj_t *config, dns_kasp_t *default_kasp, (void)confget(maps, "keys", &keys); if (keys != NULL) { - char role[256] = { 0 }; - bool warn[256][2] = { { false } }; + char role[DST_MAX_ALGS] = { 0 }; + bool warn[DST_MAX_ALGS][2] = { { false } }; CFG_LIST_FOREACH (keys, element) { cfg_obj_t *kobj = cfg_listelt_value(element); diff --git a/lib/ns/query.c b/lib/ns/query.c index 9ccda552f8..e4e769d85b 100644 --- a/lib/ns/query.c +++ b/lib/ns/query.c @@ -2375,6 +2375,7 @@ get_key(ns_client_t *client, dns_db_t *db, dns_rdata_rrsig_t *rrsig, bool secure = false; dns_clientinfomethods_t cm; dns_clientinfo_t ci; + dst_algorithm_t sigalg, keyalg; dns_clientinfomethods_init(&cm, ns_client_sourceip); dns_clientinfo_init(&ci, client, NULL); @@ -2403,6 +2404,9 @@ get_key(ns_client_t *client, dns_db_t *db, dns_rdata_rrsig_t *rrsig, result = dns_rdataset_next(keyrdataset); } + sigalg = dst_algorithm_fromdata(rrsig->algorithm, rrsig->signature, + rrsig->siglen); + for (; result == ISC_R_SUCCESS; result = dns_rdataset_next(keyrdataset)) { dns_rdata_t rdata = DNS_RDATA_INIT; @@ -2411,10 +2415,10 @@ get_key(ns_client_t *client, dns_db_t *db, dns_rdata_rrsig_t *rrsig, dns_rdataset_current(keyrdataset, &rdata); dns_rdata_tostruct(&rdata, &key, NULL); /* can't fail */ + keyalg = dst_algorithm_fromdata(key.algorithm, key.data, + key.datalen); - if (rrsig->algorithm != key.algorithm || - !dns_dnssec_iszonekey(&key)) - { + if (sigalg != keyalg || !dns_dnssec_iszonekey(&key)) { continue; } @@ -2476,14 +2480,18 @@ validate(ns_client_t *client, dns_db_t *db, dns_name_t *name, dns_rdataset_current(sigrdataset, &rdata); result = dns_rdata_tostruct(&rdata, &rrsig, NULL); RUNTIME_CHECK(result == ISC_R_SUCCESS); - if (!dns_resolver_algorithm_supported(client->view->resolver, - name, rrsig.algorithm)) + if (!dns_resolver_algorithm_supported( + client->view->resolver, name, rrsig.algorithm, + rrsig.signature, rrsig.siglen)) { char txt[DNS_NAME_FORMATSIZE + 32]; isc_buffer_t buffer; + dst_algorithm_t alg; + alg = dst_algorithm_fromdata( + rrsig.algorithm, rrsig.signature, rrsig.siglen); isc_buffer_init(&buffer, txt, sizeof(txt)); - dns_secalg_totext(rrsig.algorithm, &buffer); + dst_algorithm_totext(alg, &buffer); isc_buffer_putstr(&buffer, " "); dns_name_totext(name, DNS_NAME_OMITFINALDOT, &buffer); isc_buffer_putstr(&buffer, " (cached)"); diff --git a/tests/dns/keytable_test.c b/tests/dns/keytable_test.c index 538a98bfeb..c125c18c65 100644 --- a/tests/dns/keytable_test.c +++ b/tests/dns/keytable_test.c @@ -130,7 +130,7 @@ create_keystruct(uint16_t flags, uint8_t proto, uint8_t alg, const char *keystr, static void create_dsstruct(dns_name_t *name, uint16_t flags, uint8_t proto, uint8_t alg, - const char *keystr, unsigned char *digest, + const char *keystr, unsigned char *digest, size_t digest_len, dns_rdata_ds_t *dsstruct) { isc_result_t result; unsigned char rrdata[4096]; @@ -156,7 +156,7 @@ create_dsstruct(dns_name_t *name, uint16_t flags, uint8_t proto, uint8_t alg, * Build DS rdata struct. */ result = dns_ds_fromkeyrdata(name, &rdata, DNS_DSDIGEST_SHA256, digest, - dsstruct); + digest_len, dsstruct); assert_int_equal(result, ISC_R_SUCCESS); dns_rdata_freestruct(&dnskey); @@ -165,7 +165,7 @@ create_dsstruct(dns_name_t *name, uint16_t flags, uint8_t proto, uint8_t alg, /* Common setup: create a keytable and ntatable to test with a few keys */ static void create_tables(void) { - unsigned char digest[ISC_MAX_MD_SIZE]; + unsigned char digest[DNS_DS_BUFFERSIZE]; dns_rdata_ds_t ds; dns_fixedname_t fn; dns_name_t *keyname = dns_fixedname_name(&fn); @@ -179,14 +179,16 @@ create_tables(void) { /* Add a normal key */ dns_test_namefromstring("example.com.", &fn); - create_dsstruct(keyname, 257, 3, 5, keystr1, digest, &ds); + create_dsstruct(keyname, 257, 3, 5, keystr1, digest, sizeof(digest), + &ds); assert_int_equal(dns_keytable_add(keytable, false, false, keyname, &ds, NULL, NULL), ISC_R_SUCCESS); /* Add an initializing managed key */ dns_test_namefromstring("managed.com.", &fn); - create_dsstruct(keyname, 257, 3, 5, keystr1, digest, &ds); + create_dsstruct(keyname, 257, 3, 5, keystr1, digest, sizeof(digest), + &ds); assert_int_equal(dns_keytable_add(keytable, true, true, keyname, &ds, NULL, NULL), ISC_R_SUCCESS); @@ -220,7 +222,7 @@ destroy_tables(void) { ISC_LOOP_TEST_IMPL(add) { dns_keynode_t *keynode = NULL; dns_keynode_t *null_keynode = NULL; - unsigned char digest[ISC_MAX_MD_SIZE]; + unsigned char digest[DNS_DS_BUFFERSIZE]; dns_rdata_ds_t ds; dns_fixedname_t fn; dns_name_t *keyname = dns_fixedname_name(&fn); @@ -241,7 +243,8 @@ ISC_LOOP_TEST_IMPL(add) { * report success. */ dns_test_namefromstring("example.com.", &fn); - create_dsstruct(keyname, 257, 3, 5, keystr1, digest, &ds); + create_dsstruct(keyname, 257, 3, 5, keystr1, digest, sizeof(digest), + &ds); assert_int_equal(dns_keytable_add(keytable, false, false, keyname, &ds, NULL, NULL), ISC_R_SUCCESS); @@ -252,7 +255,8 @@ ISC_LOOP_TEST_IMPL(add) { /* Add another key (different keydata) */ dns_keynode_detach(&keynode); - create_dsstruct(keyname, 257, 3, 5, keystr2, digest, &ds); + create_dsstruct(keyname, 257, 3, 5, keystr2, digest, sizeof(digest), + &ds); assert_int_equal(dns_keytable_add(keytable, false, false, keyname, &ds, NULL, NULL), ISC_R_SUCCESS); @@ -280,7 +284,8 @@ ISC_LOOP_TEST_IMPL(add) { * node, the node should *not* be marked as initializing. */ dns_test_namefromstring("managed.com.", &fn); - create_dsstruct(keyname, 257, 3, 5, keystr2, digest, &ds); + create_dsstruct(keyname, 257, 3, 5, keystr2, digest, sizeof(digest), + &ds); assert_int_equal(dns_keytable_add(keytable, true, true, keyname, &ds, NULL, NULL), ISC_R_SUCCESS); @@ -310,7 +315,8 @@ ISC_LOOP_TEST_IMPL(add) { * initializing key. */ dns_test_namefromstring("two.com.", &fn); - create_dsstruct(keyname, 257, 3, 5, keystr1, digest, &ds); + create_dsstruct(keyname, 257, 3, 5, keystr1, digest, sizeof(digest), + &ds); assert_int_equal(dns_keytable_add(keytable, true, true, keyname, &ds, NULL, NULL), ISC_R_SUCCESS); @@ -326,7 +332,8 @@ ISC_LOOP_TEST_IMPL(add) { * trust anchor for two.com and we haven't run dns_keynode_trust(), * the initialization status should not change. */ - create_dsstruct(keyname, 257, 3, 5, keystr2, digest, &ds); + create_dsstruct(keyname, 257, 3, 5, keystr2, digest, sizeof(digest), + &ds); assert_int_equal(dns_keytable_add(keytable, true, false, keyname, &ds, NULL, NULL), ISC_R_SUCCESS); @@ -344,7 +351,8 @@ ISC_LOOP_TEST_IMPL(add) { &null_keynode), ISC_R_SUCCESS); dns_test_namefromstring("null.example.", &fn); - create_dsstruct(keyname, 257, 3, 5, keystr2, digest, &ds); + create_dsstruct(keyname, 257, 3, 5, keystr2, digest, sizeof(digest), + &ds); assert_int_equal(dns_keytable_add(keytable, false, false, keyname, &ds, NULL, NULL), ISC_R_SUCCESS); @@ -598,7 +606,7 @@ ISC_LOOP_TEST_IMPL(nta) { bool issecure, covered; dns_fixedname_t fn; dns_name_t *keyname = dns_fixedname_name(&fn); - unsigned char digest[ISC_MAX_MD_SIZE]; + unsigned char digest[DNS_DS_BUFFERSIZE]; dns_rdata_ds_t ds; dns_view_t *myview = NULL; isc_stdtime_t now = isc_stdtime_now(); @@ -616,7 +624,8 @@ ISC_LOOP_TEST_IMPL(nta) { assert_int_equal(result, ISC_R_SUCCESS); dns_test_namefromstring("example.", &fn); - create_dsstruct(keyname, 257, 3, 5, keystr1, digest, &ds); + create_dsstruct(keyname, 257, 3, 5, keystr1, digest, sizeof(digest), + &ds); result = dns_keytable_add(keytable, false, false, keyname, &ds, NULL, NULL), assert_int_equal(result, ISC_R_SUCCESS);