From: Pieter Lexis Date: Sun, 27 Dec 2015 11:01:50 +0000 (+0100) Subject: Send out CDNSKEY/CDS only for entrypoints X-Git-Tag: dnsdist-1.0.0-alpha2~3^2~3 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=f889ab99099dc624341ea36a236d9bed869aed3d;p=thirdparty%2Fpdns.git Send out CDNSKEY/CDS only for entrypoints Closes #3098 --- diff --git a/pdns/dbdnsseckeeper.cc b/pdns/dbdnsseckeeper.cc index 7df4afc4f5..65d9dab407 100644 --- a/pdns/dbdnsseckeeper.cc +++ b/pdns/dbdnsseckeeper.cc @@ -367,6 +367,38 @@ bool DNSSECKeeper::unsetPublishCDNSKEY(const DNSName& zname) return d_keymetadb->setDomainMetadata(zname, "PUBLISH_CDNSKEY", vector()); } +/** + * Returns all keys that are used to sign the DNSKEY RRSet in a zone + * + * @param zone DNSName of the zone + * @return a keyset_t with all keys that are used to sign the DNSKEY + * RRSet (these are the entrypoint(s) to the zone) + */ +DNSSECKeeper::keyset_t DNSSECKeeper::getEntryPoints(const DNSName& zone) +{ + DNSSECKeeper::keyset_t ret; + DNSSECKeeper::keyset_t keys = getKeys(zone); + + set algoHasKSK; + + for(DNSSECKeeper::keyset_t::value_type& keymeta : keys) { + if(keymeta.second.active && keymeta.second.keyOrZone) { + // KSKs should always be returned + ret.push_back(keymeta); + algoHasKSK.insert(keymeta.first.d_algorithm); + } + } + + // Now add ZSK EntryPoints + for(auto const &keymeta : keys) { + // Skip inactive keys, KSKs and algos that have a KSK + if (!keymeta.second.active || keymeta.second.keyOrZone || algoHasKSK.count(keymeta.first.d_algorithm)) + continue; + ret.push_back(keymeta); + } + return ret; +} + DNSSECKeeper::keyset_t DNSSECKeeper::getKeys(const DNSName& zone, boost::tribool allOrKeyOrZone, bool useCache) { unsigned int now = time(0); diff --git a/pdns/dnsseckeeper.hh b/pdns/dnsseckeeper.hh index 4cdbd81b77..aff52b99c6 100644 --- a/pdns/dnsseckeeper.hh +++ b/pdns/dnsseckeeper.hh @@ -71,6 +71,7 @@ public: } bool isSecuredZone(const DNSName& zone); static uint64_t dbdnssecCacheSizes(const std::string& str); + keyset_t getEntryPoints(const DNSName& zone); keyset_t getKeys(const DNSName& zone, boost::tribool allOrKeyOrZone = boost::indeterminate, bool useCache = true); DNSSECPrivateKey getKeyById(const DNSName& zone, unsigned int id); bool addKey(const DNSName& zname, bool keyOrZone, int algorithm=5, int bits=0, bool active=true); diff --git a/pdns/packethandler.cc b/pdns/packethandler.cc index d6217e412d..ae50e3ec19 100644 --- a/pdns/packethandler.cc +++ b/pdns/packethandler.cc @@ -133,34 +133,64 @@ void PacketHandler::addRootReferral(DNSPacket* r) } /** - * This adds DNSKEY records to the answer packet. Returns true if one was added. - * The optional doCDNSKEY parameter signifies that we need to add a CDNSKEY (RFC 7344) - * instead of DNSKEY. + * This adds CDNSKEY records to the answer packet. Returns true if one was added. * * @param p Pointer to the DNSPacket containing the original question * @param r Pointer to the DNSPacket where the records should be inserted into - * @param sd SOAData of the zone for which DNSKEY records sets should be added - * @param doCDNSKEY When set to true, add CDNSKEYs instead of DNSKEYs + * @param sd SOAData of the zone for which CDNSKEY records sets should be added * @return bool that shows if any records were added **/ -bool PacketHandler::addDNSKEY(DNSPacket *p, DNSPacket *r, const SOAData& sd, bool doCDNSKEY=false) +bool PacketHandler::addCDNSKEY(DNSPacket *p, DNSPacket *r, const SOAData& sd) { string publishCDNSKEY; d_dk.getFromMeta(p->qdomain, "PUBLISH_CDNSKEY", publishCDNSKEY); - if (doCDNSKEY && publishCDNSKEY != "1") + if (publishCDNSKEY != "1") return false; DNSResourceRecord rr; bool haveOne=false; DNSSECPrivateKey dpk; + DNSSECKeeper::keyset_t entryPoints = d_dk.getEntryPoints(p->qdomain); + for(const auto& value: entryPoints) { + rr.qtype=QType::CDNSKEY; + rr.ttl=sd.default_ttl; + rr.qname=p->qdomain; + rr.content=value.first.getDNSKEY().getZoneRepresentation(); + rr.auth=true; + r->addRecord(rr); + haveOne=true; + } + + if(::arg().mustDo("direct-dnskey")) { + B.lookup(QType(QType::CDNSKEY), p->qdomain, p, sd.domain_id); + + while(B.get(rr)) { + rr.ttl=sd.default_ttl; + r->addRecord(rr); + haveOne=true; + } + } + return haveOne; +} + +/** + * This adds DNSKEY records to the answer packet. Returns true if one was added. + * + * @param p Pointer to the DNSPacket containing the original question + * @param r Pointer to the DNSPacket where the records should be inserted into + * @param sd SOAData of the zone for which DNSKEY records sets should be added + * @return bool that shows if any records were added +**/ +bool PacketHandler::addDNSKEY(DNSPacket *p, DNSPacket *r, const SOAData& sd) +{ + DNSResourceRecord rr; + bool haveOne=false; + DNSSECPrivateKey dpk; + DNSSECKeeper::keyset_t keyset = d_dk.getKeys(p->qdomain); for(const auto& value: keyset) { - if (doCDNSKEY && !value.second.keyOrZone) { - // Don't send out CDNSKEY records for ZSKs - continue; - } - rr.qtype=doCDNSKEY ? QType::CDNSKEY : QType::DNSKEY; + rr.qtype=QType::DNSKEY; rr.ttl=sd.default_ttl; rr.qname=p->qdomain; rr.content=value.first.getDNSKEY().getZoneRepresentation(); @@ -170,10 +200,7 @@ bool PacketHandler::addDNSKEY(DNSPacket *p, DNSPacket *r, const SOAData& sd, boo } if(::arg().mustDo("direct-dnskey")) { - if(doCDNSKEY) - B.lookup(QType(QType::CDNSKEY), p->qdomain, p, sd.domain_id); - else - B.lookup(QType(QType::DNSKEY), p->qdomain, p, sd.domain_id); + B.lookup(QType(QType::DNSKEY), p->qdomain, p, sd.domain_id); while(B.get(rr)) { rr.ttl=sd.default_ttl; @@ -213,13 +240,9 @@ bool PacketHandler::addCDS(DNSPacket *p, DNSPacket *r, const SOAData& sd) bool haveOne=false; DNSSECPrivateKey dpk; - DNSSECKeeper::keyset_t keyset = d_dk.getKeys(p->qdomain); + DNSSECKeeper::keyset_t keyset = d_dk.getEntryPoints(p->qdomain); for(auto const &value : keyset) { - if (!value.second.keyOrZone) { - // Don't send out CDS records for ZSKs - continue; - } for(auto const &digestAlgo : digestAlgos){ rr.content=makeDSFromDNSKey(p->qdomain, value.first.getDNSKEY(), std::stoi(digestAlgo)).getZoneRepresentation(); r->addRecord(rr); @@ -1024,11 +1047,11 @@ void PacketHandler::completeANYRecords(DNSPacket *p, DNSPacket*r, SOAData& sd, c if(!d_dk.isSecuredZone(sd.qname)) return; - + addNSECX(p, r, target, DNSName(), sd.qname, 5); if(sd.qname == p->qdomain) { addDNSKEY(p, r, sd); - addDNSKEY(p, r, sd, true); + addCDNSKEY(p, r, sd); addCDS(p, r, sd); addNSEC3PARAM(p, r, sd); } @@ -1283,7 +1306,7 @@ DNSPacket *PacketHandler::questionOrRecurse(DNSPacket *p, bool *shouldRecurse) } else if(p->qtype.getCode() == QType::CDNSKEY) { - if(addDNSKEY(p,r, sd, true)) + if(addCDNSKEY(p,r, sd)) goto sendit; } else if(p->qtype.getCode() == QType::CDS) diff --git a/pdns/packethandler.hh b/pdns/packethandler.hh index 550358465a..24f9d243d6 100644 --- a/pdns/packethandler.hh +++ b/pdns/packethandler.hh @@ -71,7 +71,8 @@ private: int processNotify(DNSPacket *); void addRootReferral(DNSPacket *r); int doChaosRequest(DNSPacket *p, DNSPacket *r, DNSName &target); - bool addDNSKEY(DNSPacket *p, DNSPacket *r, const SOAData& sd, bool doCDNSKEY); + bool addDNSKEY(DNSPacket *p, DNSPacket *r, const SOAData& sd); + bool addCDNSKEY(DNSPacket *p, DNSPacket *r, const SOAData& sd); bool addCDS(DNSPacket *p, DNSPacket *r, const SOAData& sd); bool addNSEC3PARAM(DNSPacket *p, DNSPacket *r, const SOAData& sd); int doAdditionalProcessingAndDropAA(DNSPacket *p, DNSPacket *r, const SOAData& sd, bool retargeted); diff --git a/pdns/tcpreceiver.cc b/pdns/tcpreceiver.cc index 9920f51603..09de4f6565 100644 --- a/pdns/tcpreceiver.cc +++ b/pdns/tcpreceiver.cc @@ -664,6 +664,10 @@ int TCPNameserver::doAXFR(const DNSName &target, shared_ptr q, int ou dk.getFromMeta(q->qdomain, "PUBLISH_CDNSKEY", publishCDNSKEY); dk.getFromMeta(q->qdomain, "PUBLISH_CDS", publishCDS); vector cds, cdnskey; + DNSSECKeeper::keyset_t entryPoints = dk.getEntryPoints(q->qdomain); + set entryPointIds; + for (auto const& value : entryPoints) + entryPointIds.insert(value.second.id); for(const DNSSECKeeper::keyset_t::value_type& value : keys) { rr.qtype = QType(QType::DNSKEY); @@ -676,7 +680,7 @@ int TCPNameserver::doAXFR(const DNSName &target, shared_ptr q, int ou csp.submit(rr); // generate CDS and CDNSKEY records - if(value.second.keyOrZone){ + if(entryPointIds.count(value.second.id) > 0){ if(publishCDNSKEY == "1") { rr.qtype=QType(QType::CDNSKEY); rr.content = value.first.getDNSKEY().getZoneRepresentation(); diff --git a/regression-tests.nobackend/tinydns-data-check/expected_result b/regression-tests.nobackend/tinydns-data-check/expected_result index 71e330d340..36553b1404 100644 --- a/regression-tests.nobackend/tinydns-data-check/expected_result +++ b/regression-tests.nobackend/tinydns-data-check/expected_result @@ -9,4 +9,5 @@ a63dc120391d9df0003f2ec4f461a6af ../regression-tests/zones/secure-delegated.dns 24514dc104b22206daeb973ff9303545 ../regression-tests/zones/minimal.com 0b20d7a0250576451135483b863750bf ../regression-tests/zones/tsig.com b1f775045fa2cf0a3b91aa834af06e49 ../regression-tests/zones/stest.com +a98864b315f16bcf49ce577426063c42 ../regression-tests/zones/cdnskey-cds-test.com 2d30ff6855f531368c72e2195a76eabb ../modules/tinydnsbackend/data.cdb diff --git a/regression-tests/named.conf b/regression-tests/named.conf index c1ca084901..ce6483cb60 100644 --- a/regression-tests/named.conf +++ b/regression-tests/named.conf @@ -64,3 +64,8 @@ zone "stest.com"{ type master; file "stest.com"; }; + +zone "cdnskey-cds-test.com"{ + type master; + file "cdnskey-cds-test.com"; +}; diff --git a/regression-tests/start-test-stop b/regression-tests/start-test-stop index 6bca98904c..231f165461 100755 --- a/regression-tests/start-test-stop +++ b/regression-tests/start-test-stop @@ -92,6 +92,10 @@ securezone () $PDNSUTIL --config-dir=. $configname hsm create-key $zone $kid else $PDNSUTIL --config-dir=. $configname secure-zone $zone 2>&1 + if [ "${zone: 0:20}" = "cdnskey-cds-test.com" ]; then + $PDNSUTIL --config-dir=. $configname set-publish-cds $zone 2>&1 + $PDNSUTIL --config-dir=. $configname set-publish-cdnskey $zone 2>&1 + fi fi fi } diff --git a/regression-tests/tests/00dnssec-grabkeys/expected_result.dnssec b/regression-tests/tests/00dnssec-grabkeys/expected_result.dnssec index f599e28b8a..b4de394767 100644 --- a/regression-tests/tests/00dnssec-grabkeys/expected_result.dnssec +++ b/regression-tests/tests/00dnssec-grabkeys/expected_result.dnssec @@ -1 +1 @@ -10 +11 diff --git a/regression-tests/tests/publishing-cds-cdnskey/command b/regression-tests/tests/publishing-cds-cdnskey/command index 2cf7d9c205..b019335fcf 100755 --- a/regression-tests/tests/publishing-cds-cdnskey/command +++ b/regression-tests/tests/publishing-cds-cdnskey/command @@ -3,3 +3,9 @@ cleandig secure-delegated.dnssec-parent.com CDS dnssec cleandig secure-delegated.dnssec-parent.com CDNSKEY dnssec cleandig secure-delegated.dnssec-parent.com ANY dnssec tcp | grep 'CDS\|CDNSKEY' | grep -v 'NSEC' + +# We only want to know if we get these records when the default secure-zone is use +# This means a single ZSK, we don't care about the conents +cleandig cdnskey-cds-test.com CDS dnssec | perl -pe 's!86400.*!86400!g' +cleandig cdnskey-cds-test.com CDNSKEY dnssec | perl -pe 's!86400.*!86400!g' +cleandig cdnskey-cds-test.com ANY dnssec tcp | grep 'CDS\|CDNSKEY' | grep -v 'NSEC' | perl -pe 's!86400.*!86400!g' diff --git a/regression-tests/tests/publishing-cds-cdnskey/expected_result b/regression-tests/tests/publishing-cds-cdnskey/expected_result index d07af16d8d..cc03f2d9a0 100644 --- a/regression-tests/tests/publishing-cds-cdnskey/expected_result +++ b/regression-tests/tests/publishing-cds-cdnskey/expected_result @@ -14,3 +14,19 @@ Reply to question for qname='secure-delegated.dnssec-parent.com.', qtype=CDNSKEY 0 secure-delegated.dnssec-parent.com. IN CDS 86400 54319 8 2 a0b9c38cd324182af0ef66830d0a0e85a1d58979c9834e18c871779e040857b7 0 secure-delegated.dnssec-parent.com. IN RRSIG 86400 CDNSKEY 8 3 86400 [expiry] [inception] [keytag] secure-delegated.dnssec-parent.com. ... 0 secure-delegated.dnssec-parent.com. IN RRSIG 86400 CDS 8 3 86400 [expiry] [inception] [keytag] secure-delegated.dnssec-parent.com. ... +0 cdnskey-cds-test.com. IN CDS 86400 +0 cdnskey-cds-test.com. IN CDS 86400 +0 cdnskey-cds-test.com. IN RRSIG 86400 +2 . IN OPT 32768 +Rcode: 0 (No Error), RD: 0, QR: 1, TC: 0, AA: 1, opcode: 0 +Reply to question for qname='cdnskey-cds-test.com.', qtype=CDS +0 cdnskey-cds-test.com. IN CDNSKEY 86400 +0 cdnskey-cds-test.com. IN RRSIG 86400 +2 . IN OPT 32768 +Rcode: 0 (No Error), RD: 0, QR: 1, TC: 0, AA: 1, opcode: 0 +Reply to question for qname='cdnskey-cds-test.com.', qtype=CDNSKEY +0 cdnskey-cds-test.com. IN CDNSKEY 86400 +0 cdnskey-cds-test.com. IN CDS 86400 +0 cdnskey-cds-test.com. IN CDS 86400 +0 cdnskey-cds-test.com. IN RRSIG 86400 +0 cdnskey-cds-test.com. IN RRSIG 86400 diff --git a/regression-tests/tests/verify-dnssec-zone/expected_result b/regression-tests/tests/verify-dnssec-zone/expected_result index a116ca02b2..074ec83493 100644 --- a/regression-tests/tests/verify-dnssec-zone/expected_result +++ b/regression-tests/tests/verify-dnssec-zone/expected_result @@ -73,3 +73,11 @@ zone stest.com/IN: loaded serial 2000081501 (DNSSEC signed) OK RETVAL: 0 +--- ldns-verify-zone -V2 cdnskey-cds-test.com +RETVAL: 0 + +--- named-checkzone cdnskey-cds-test.com +zone cdnskey-cds-test.com/IN: loaded serial 2005092501 (DNSSEC signed) +OK +RETVAL: 0 + diff --git a/regression-tests/tests/verify-dnssec-zone/expected_result.nsec3 b/regression-tests/tests/verify-dnssec-zone/expected_result.nsec3 index a116ca02b2..074ec83493 100644 --- a/regression-tests/tests/verify-dnssec-zone/expected_result.nsec3 +++ b/regression-tests/tests/verify-dnssec-zone/expected_result.nsec3 @@ -73,3 +73,11 @@ zone stest.com/IN: loaded serial 2000081501 (DNSSEC signed) OK RETVAL: 0 +--- ldns-verify-zone -V2 cdnskey-cds-test.com +RETVAL: 0 + +--- named-checkzone cdnskey-cds-test.com +zone cdnskey-cds-test.com/IN: loaded serial 2005092501 (DNSSEC signed) +OK +RETVAL: 0 + diff --git a/regression-tests/tests/verify-dnssec-zone/expected_result.nsec3-optout b/regression-tests/tests/verify-dnssec-zone/expected_result.nsec3-optout index b8d3184a98..8f41144cf9 100644 --- a/regression-tests/tests/verify-dnssec-zone/expected_result.nsec3-optout +++ b/regression-tests/tests/verify-dnssec-zone/expected_result.nsec3-optout @@ -76,3 +76,11 @@ zone stest.com/IN: loaded serial 2000081501 (DNSSEC signed) OK RETVAL: 0 +--- ldns-verify-zone -V2 cdnskey-cds-test.com +RETVAL: 0 + +--- named-checkzone cdnskey-cds-test.com +zone cdnskey-cds-test.com/IN: loaded serial 2005092501 (DNSSEC signed) +OK +RETVAL: 0 + diff --git a/regression-tests/zones/cdnskey-cds-test.com b/regression-tests/zones/cdnskey-cds-test.com new file mode 100644 index 0000000000..7fb64fa031 --- /dev/null +++ b/regression-tests/zones/cdnskey-cds-test.com @@ -0,0 +1,14 @@ +$TTL 3600 +$ORIGIN cdnskey-cds-test.com. +@ IN SOA ns1.cdnskey-cds-test. ahu.example.com. ( 2005092501 + 8H ; refresh + 2H ; retry + 1W ; expire + 1D ; default_ttl + ) + +@ IN NS ns1 +@ IN NS ns2 +@ IN A 127.0.0.1 +ns1 IN A 1.1.1.1 +ns2 IN A 2.2.2.2