From: Remi Gacogne Date: Fri, 28 Apr 2017 11:50:13 +0000 (+0200) Subject: rec: Add DNSSEC tests in the SyncRes unit tests suite X-Git-Tag: rec-4.1.0-alpha1~50^2~29 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=8455425c1bc742b329065191b6c1bf5b7f7b84b0;p=thirdparty%2Fpdns.git rec: Add DNSSEC tests in the SyncRes unit tests suite --- diff --git a/pdns/dnssecinfra.cc b/pdns/dnssecinfra.cc index 474a597d40..95cbc3bdae 100644 --- a/pdns/dnssecinfra.cc +++ b/pdns/dnssecinfra.cc @@ -141,9 +141,9 @@ std::string DNSCryptoKeyEngine::convertToISC() const shared_ptr DNSCryptoKeyEngine::make(unsigned int algo) { - makers_t& makers = getMakers(); + const makers_t& makers = getMakers(); makers_t::const_iterator iter = makers.find(algo); - if(iter != makers.end()) + if(iter != makers.cend()) return (iter->second)(algo); else { throw runtime_error("Request to create key object for unknown algorithm number "+std::to_string(algo)); @@ -320,7 +320,7 @@ shared_ptr DNSCryptoKeyEngine::makeFromPublicKeyString(unsig shared_ptr DNSCryptoKeyEngine::makeFromPEMString(DNSKEYRecordContent& drc, const std::string& raw) { - for(makers_t::value_type& val : getMakers()) + for(const makers_t::value_type& val : getMakers()) { shared_ptr ret=nullptr; try { @@ -336,7 +336,7 @@ shared_ptr DNSCryptoKeyEngine::makeFromPEMString(DNSKEYRecor } -bool sharedDNSSECCompare(const shared_ptr& a, const shared_ptr& b) +static bool sharedDNSSECCompare(const shared_ptr& a, const shared_ptr& b) { return a->serialize(g_rootdnsname, true, true) < b->serialize(g_rootdnsname, true, true); } @@ -398,35 +398,60 @@ string getMessageForRRSET(const DNSName& qname, const RRSIGRecordContent& rrc, v return toHash; } -DSRecordContent makeDSFromDNSKey(const DNSName& qname, const DNSKEYRecordContent& drc, int digest) +bool DNSCryptoKeyEngine::isAlgorithmSupported(unsigned int algo) +{ + const makers_t& makers = getMakers(); + makers_t::const_iterator iter = makers.find(algo); + return iter != makers.cend(); +} + +static unsigned int digestToAlgorithmNumber(uint8_t digest) +{ + switch(digest) { + case DNSSECKeeper::SHA1: + return DNSSECKeeper::RSASHA1; + case DNSSECKeeper::SHA256: + return DNSSECKeeper::RSASHA256; + case DNSSECKeeper::GOST: + return DNSSECKeeper::ECCGOST; + case DNSSECKeeper::SHA384: + return DNSSECKeeper::ECDSA384; + default: + throw std::runtime_error("Unknown digest type " + std::to_string(digest)); + } + return 0; +} + +bool DNSCryptoKeyEngine::isDigestSupported(uint8_t digest) +{ + try { + unsigned int algo = digestToAlgorithmNumber(digest); + return isAlgorithmSupported(algo); + } + catch(const std::exception& e) { + return false; + } +} + +DSRecordContent makeDSFromDNSKey(const DNSName& qname, const DNSKEYRecordContent& drc, uint8_t digest) { string toHash; toHash.assign(qname.toDNSStringLC()); toHash.append(const_cast(drc).serialize(DNSName(), true, true)); DSRecordContent dsrc; - if(digest==1) { - shared_ptr dpk(DNSCryptoKeyEngine::make(5)); // gives us SHA1 - dsrc.d_digest = dpk->hash(toHash); - } - else if(digest == 2) { - shared_ptr dpk(DNSCryptoKeyEngine::make(8)); // gives us SHA256 - dsrc.d_digest = dpk->hash(toHash); - } - else if(digest == 3) { - shared_ptr dpk(DNSCryptoKeyEngine::make(12)); // gives us GOST - dsrc.d_digest = dpk->hash(toHash); - } - else if(digest == 4) { - shared_ptr dpk(DNSCryptoKeyEngine::make(14)); // gives us ECDSAP384 + try { + unsigned int algo = digestToAlgorithmNumber(digest); + shared_ptr dpk(DNSCryptoKeyEngine::make(algo)); dsrc.d_digest = dpk->hash(toHash); } - else + catch(const std::exception& e) { throw std::runtime_error("Asked to a DS of unknown digest type " + std::to_string(digest)+"\n"); + } - dsrc.d_algorithm= drc.d_algorithm; - dsrc.d_digesttype=digest; - dsrc.d_tag=const_cast(drc).getTag(); + dsrc.d_algorithm = drc.d_algorithm; + dsrc.d_digesttype = digest; + dsrc.d_tag = const_cast(drc).getTag(); return dsrc; } @@ -445,21 +470,6 @@ DNSKEYRecordContent makeDNSKEYFromDNSCryptoKeyEngine(const std::shared_ptr& output) -{ - output.clear(); - DEREater de(input); - if(de.getByte() != 0x30) - throw runtime_error("Not a DER sequence"); - - unsigned int seqlen=de.getLength(); - unsigned int startseq=de.getOffset(); - unsigned int len; - string ret; - try { - for(;;) { - uint8_t kind = de.getByte(); - if(kind != 0x02) - throw runtime_error("DER Sequence contained non-INTEGER component: "+std::to_string(static_cast(kind)) ); - len = de.getLength(); - ret = de.getBytes(len); - output.push_back(ret); - } - } - catch(DEREater::eof& eof) - { - if(de.getOffset() - startseq != seqlen) - throw runtime_error("DER Sequence ended before end of data"); - } -} - static string calculateHMAC(const std::string& key, const std::string& text, TSIGHashEnum hasher) { const EVP_MD* md_type; diff --git a/pdns/dnssecinfra.hh b/pdns/dnssecinfra.hh index 58d35df09c..d094fdb552 100644 --- a/pdns/dnssecinfra.hh +++ b/pdns/dnssecinfra.hh @@ -56,6 +56,10 @@ class DNSCryptoKeyEngine virtual std::string getPubKeyHash()const =0; virtual std::string getPublicKeyString()const =0; virtual int getBits() const =0; + virtual unsigned int getAlgorithm() const + { + return d_algorithm; + } virtual void fromISCMap(DNSKEYRecordContent& drc, stormap_t& stormap)=0; virtual void fromPEMString(DNSKEYRecordContent& drc, const std::string& raw) @@ -72,6 +76,8 @@ class DNSCryptoKeyEngine static shared_ptr makeFromPEMString(DNSKEYRecordContent& drc, const std::string& raw); static shared_ptr makeFromPublicKeyString(unsigned int algorithm, const std::string& raw); static shared_ptr make(unsigned int algorithm); + static bool isAlgorithmSupported(unsigned int algo); + static bool isDigestSupported(uint8_t digest); typedef shared_ptr maker_t(unsigned int algorithm); @@ -110,6 +116,7 @@ struct DNSSECPrivateKey void setKey(const shared_ptr key) { d_key = key; + d_algorithm = key->getAlgorithm(); } DNSKEYRecordContent getDNSKEY() const; @@ -137,27 +144,17 @@ struct CanonicalCompare: public std::binary_function } }; -bool sharedDNSSECCompare(const std::shared_ptr& a, const shared_ptr& b); string getMessageForRRSET(const DNSName& qname, const RRSIGRecordContent& rrc, std::vector >& signRecords, bool processRRSIGLabels = false); -DSRecordContent makeDSFromDNSKey(const DNSName& qname, const DNSKEYRecordContent& drc, int digest=1); +DSRecordContent makeDSFromDNSKey(const DNSName& qname, const DNSKEYRecordContent& drc, uint8_t digest); - -class RSAContext; class DNSSECKeeper; -struct DNSSECPrivateKey; -void fillOutRRSIG(DNSSECPrivateKey& dpk, const DNSName& signQName, RRSIGRecordContent& rrc, vector >& toSign); uint32_t getStartOfWeek(); -void addSignature(DNSSECKeeper& dk, UeberBackend& db, const DNSName& signer, const DNSName signQName, const DNSName& wildcardname, uint16_t signQType, uint32_t signTTL, DNSResourceRecord::Place signPlace, - vector >& toSign, vector& outsigned, uint32_t origTTL); -int getRRSIGsForRRSET(DNSSECKeeper& dk, const DNSName& signer, const DNSName signQName, uint16_t signQType, uint32_t signTTL, - vector >& toSign, vector &rrc); string hashQNameWithSalt(const NSEC3PARAMRecordContent& ns3prc, const DNSName& qname); string hashQNameWithSalt(const std::string& salt, unsigned int iterations, const DNSName& qname); -void decodeDERIntegerSequence(const std::string& input, vector& output); -class DNSPacket; + void addRRSigs(DNSSECKeeper& dk, UeberBackend& db, const std::set& authMap, vector& rrs); void addTSIG(DNSPacketWriter& pw, TSIGRecordContent& trc, const DNSName& tsigkeyname, const string& tsigsecret, const string& tsigprevious, bool timersonly); diff --git a/pdns/dnsseckeeper.hh b/pdns/dnsseckeeper.hh index b24d555380..7f111ecbf7 100644 --- a/pdns/dnsseckeeper.hh +++ b/pdns/dnsseckeeper.hh @@ -55,6 +55,13 @@ public: ED448=16 }; + enum dsdigestalgorithm_t : uint8_t { + SHA1=1, + SHA256=2, + GOST=3, + SHA384=4 + }; + struct KeyMetaData { string fname; diff --git a/pdns/dnssecsigner.cc b/pdns/dnssecsigner.cc index 031b870891..864d473742 100644 --- a/pdns/dnssecsigner.cc +++ b/pdns/dnssecsigner.cc @@ -33,10 +33,59 @@ #include "statbag.hh" extern StatBag S; +static pthread_rwlock_t g_signatures_lock = PTHREAD_RWLOCK_INITIALIZER; +typedef map, string> signaturecache_t; +static signaturecache_t g_signatures; +static int g_cacheweekno; + +AtomicCounter* g_signatureCount; + +static void fillOutRRSIG(DNSSECPrivateKey& dpk, const DNSName& signQName, RRSIGRecordContent& rrc, vector >& toSign) +{ + if(!g_signatureCount) + g_signatureCount = S.getPointer("signatures"); + + DNSKEYRecordContent drc = dpk.getDNSKEY(); + const std::shared_ptr rc = dpk.getKey(); + rrc.d_tag = drc.getTag(); + rrc.d_algorithm = drc.d_algorithm; + + string msg=getMessageForRRSET(signQName, rrc, toSign); // this is what we will hash & sign + pair lookup(rc->getPubKeyHash(), pdns_md5sum(msg)); // this hash is a memory saving exercise + + bool doCache=1; + if(doCache) + { + ReadLock l(&g_signatures_lock); + signaturecache_t::const_iterator iter = g_signatures.find(lookup); + if(iter != g_signatures.end()) { + rrc.d_signature=iter->second; + return; + } + // else cerr<<"Miss!"<sign(msg); + (*g_signatureCount)++; + if(doCache) { + /* we add some jitter here so not all your slaves start pruning their caches at the very same millisecond */ + int weekno = (time(0) - dns_random(3600)) / (86400*7); // we just spent milliseconds doing a signature, microsecond more won't kill us + const static int maxcachesize=::arg().asNum("max-signature-cache-entries", INT_MAX); + + WriteLock l(&g_signatures_lock); + if(g_cacheweekno < weekno || g_signatures.size() >= (uint) maxcachesize) { // blunt but effective (C) Habbie, mind04 + L< >& toSign, vector& rrcs) +static int getRRSIGsForRRSET(DNSSECKeeper& dk, const DNSName& signer, const DNSName signQName, uint16_t signQType, uint32_t signTTL, + vector >& toSign, vector& rrcs) { if(toSign.empty()) return -1; @@ -69,9 +118,9 @@ int getRRSIGsForRRSET(DNSSECKeeper& dk, const DNSName& signer, const DNSName sig } // this is the entrypoint from DNSPacket -void addSignature(DNSSECKeeper& dk, UeberBackend& db, const DNSName& signer, const DNSName signQName, const DNSName& wildcardname, uint16_t signQType, - uint32_t signTTL, DNSResourceRecord::Place signPlace, - vector >& toSign, vector& outsigned, uint32_t origTTL) +static void addSignature(DNSSECKeeper& dk, UeberBackend& db, const DNSName& signer, const DNSName signQName, const DNSName& wildcardname, uint16_t signQType, + uint32_t signTTL, DNSResourceRecord::Place signPlace, + vector >& toSign, vector& outsigned, uint32_t origTTL) { //cerr<<"Asked to sign '"<, string> signaturecache_t; -static signaturecache_t g_signatures; -static int g_cacheweekno; - -AtomicCounter* g_signatureCount; - uint64_t signatureCacheSize(const std::string& str) { ReadLock l(&g_signatures_lock); return g_signatures.size(); } -void fillOutRRSIG(DNSSECPrivateKey& dpk, const DNSName& signQName, RRSIGRecordContent& rrc, vector >& toSign) -{ - if(!g_signatureCount) - g_signatureCount = S.getPointer("signatures"); - - DNSKEYRecordContent drc = dpk.getDNSKEY(); - const std::shared_ptr rc = dpk.getKey(); - rrc.d_tag = drc.getTag(); - rrc.d_algorithm = drc.d_algorithm; - - string msg=getMessageForRRSET(signQName, rrc, toSign); // this is what we will hash & sign - pair lookup(rc->getPubKeyHash(), pdns_md5sum(msg)); // this hash is a memory saving exercise - - bool doCache=1; - if(doCache) - { - ReadLock l(&g_signatures_lock); - signaturecache_t::const_iterator iter = g_signatures.find(lookup); - if(iter != g_signatures.end()) { - rrc.d_signature=iter->second; - return; - } - // else cerr<<"Miss!"<sign(msg); - (*g_signatureCount)++; - if(doCache) { - /* we add some jitter here so not all your slaves start pruning their caches at the very same millisecond */ - int weekno = (time(0) - dns_random(3600)) / (86400*7); // we just spent milliseconds doing a signature, microsecond more won't kill us - const static int maxcachesize=::arg().asNum("max-signature-cache-entries", INT_MAX); - - WriteLock l(&g_signatures_lock); - if(g_cacheweekno < weekno || g_signatures.size() >= (uint) maxcachesize) { // blunt but effective (C) Habbie, mind04 - L<qdomain); for(const auto& value: entryPoints) { @@ -149,7 +148,6 @@ bool PacketHandler::addDNSKEY(DNSPacket *p, DNSPacket *r, const SOAData& sd) { DNSZoneRecord rr; bool haveOne=false; - DNSSECPrivateKey dpk; DNSSECKeeper::keyset_t keyset = d_dk.getKeys(p->qdomain); for(const auto& value: keyset) { @@ -201,13 +199,12 @@ bool PacketHandler::addCDS(DNSPacket *p, DNSPacket *r, const SOAData& sd) rr.auth=true; bool haveOne=false; - DNSSECPrivateKey dpk; DNSSECKeeper::keyset_t keyset = d_dk.getEntryPoints(p->qdomain); for(auto const &value : keyset) { for(auto const &digestAlgo : digestAlgos){ - rr.dr.d_content=std::make_shared(makeDSFromDNSKey(p->qdomain, value.first.getDNSKEY(), std::stoi(digestAlgo))); + rr.dr.d_content=std::make_shared(makeDSFromDNSKey(p->qdomain, value.first.getDNSKEY(), pdns_stou(digestAlgo))); r->addRecord(rr); haveOne=true; } diff --git a/pdns/pdnsutil.cc b/pdns/pdnsutil.cc index ae464f7683..079125bed4 100644 --- a/pdns/pdnsutil.cc +++ b/pdns/pdnsutil.cc @@ -1704,16 +1704,16 @@ bool showZone(DNSSECKeeper& dk, const DNSName& zone, bool exportDS = false) shown=true; const std::string prefix(exportDS ? "" : "DS = "); - cout< #include "arguments.hh" +#include "dnssecinfra.hh" +#include "dnsseckeeper.hh" #include "lua-recursor4.hh" #include "namespaces.hh" #include "rec-lua-conf.hh" @@ -139,8 +141,16 @@ static void init(bool debug=false) auto luaconfsCopy = g_luaconfs.getCopy(); luaconfsCopy.dfe.clear(); + luaconfsCopy.dsAnchors.clear(); + for (const auto &dsRecord : rootDSs) { + auto ds=unique_ptr(dynamic_cast(DSRecordContent::make(dsRecord))); + luaconfsCopy.dsAnchors[g_rootdnsname].insert(*ds); + } + luaconfsCopy.negAnchors.clear(); g_luaconfs.setState(luaconfsCopy); + g_dnssecmode = DNSSECMode::Off; + ::arg().set("version-string", "string reported on version.pdns or version.bind")="PowerDNS Unit Tests"; } @@ -171,33 +181,42 @@ static void setLWResult(LWResult* res, int rcode, bool aa=false, bool tc=false, res->d_haveEDNS = edns; } -static void addRecordToList(std::vector& records, const DNSName& name, uint16_t type, const std::string& content, DNSResourceRecord::Place place, uint32_t ttl) +static std::shared_ptr getRecordContent(uint16_t type, const std::string& content) { - DNSRecord rec; - rec.d_place = place; - rec.d_name = name; - rec.d_type = type; - rec.d_ttl = ttl; + std::shared_ptr result = nullptr; if (type == QType::NS) { - rec.d_content = std::make_shared(DNSName(content)); + result = std::make_shared(DNSName(content)); } else if (type == QType::A) { - rec.d_content = std::make_shared(ComboAddress(content)); + result = std::make_shared(ComboAddress(content)); } else if (type == QType::AAAA) { - rec.d_content = std::make_shared(ComboAddress(content)); + result = std::make_shared(ComboAddress(content)); } else if (type == QType::CNAME) { - rec.d_content = std::make_shared(DNSName(content)); + result = std::make_shared(DNSName(content)); } else if (type == QType::OPT) { - rec.d_content = std::make_shared(); + result = std::make_shared(); } else { - rec.d_content = DNSRecordContent::mastermake(type, QClass::IN, content); + result = DNSRecordContent::mastermake(type, QClass::IN, content); } + return result; +} + +static void addRecordToList(std::vector& records, const DNSName& name, uint16_t type, const std::string& content, DNSResourceRecord::Place place, uint32_t ttl) +{ + DNSRecord rec; + rec.d_place = place; + rec.d_name = name; + rec.d_type = type; + rec.d_ttl = ttl; + + rec.d_content = getRecordContent(type, content); + records.push_back(rec); } @@ -218,20 +237,145 @@ static void addRecordToLW(LWResult* res, const std::string& name, uint16_t type, static bool isRootServer(const ComboAddress& ip) { - for (size_t idx = 0; idx < rootIps4Count; idx++) { - if (ip.toString() == rootIps4[idx]) { - return true; + if (ip.isIPv4()) { + for (size_t idx = 0; idx < rootIps4Count; idx++) { + if (ip.toString() == rootIps4[idx]) { + return true; + } } } - - for (size_t idx = 0; idx < rootIps6Count; idx++) { - if (ip.toString() == rootIps6[idx]) { - return true; + else { + for (size_t idx = 0; idx < rootIps6Count; idx++) { + if (ip.toString() == rootIps6[idx]) { + return true; + } } } + return false; } +static void computeRRSIG(const DNSSECPrivateKey& dpk, const DNSName& signer, const DNSName& signQName, uint16_t signQType, uint32_t signTTL, uint32_t sigValidity, RRSIGRecordContent& rrc, vector >& toSign) +{ + time_t now = time(nullptr); + DNSKEYRecordContent drc = dpk.getDNSKEY(); + const std::shared_ptr rc = dpk.getKey(); + + rrc.d_type = signQType; + rrc.d_labels = signQName.countLabels() - signQName.isWildcard(); + rrc.d_originalttl = signTTL; + rrc.d_siginception = now - 1; + rrc.d_sigexpire = now + sigValidity; + rrc.d_signer = signer; + rrc.d_tag = 0; + rrc.d_tag = drc.getTag(); + rrc.d_algorithm = drc.d_algorithm; + + std::string msg = getMessageForRRSET(signQName, rrc, toSign); + + rrc.d_signature = rc->sign(msg); +} + +static void addRRSIG(const std::unordered_map& keys, std::vector& records, const DNSName& signer, uint32_t sigValidity) +{ + if (records.empty()) { + return; + } + + const auto it = keys.find(signer); + if (it == keys.cend()) { + throw std::runtime_error("No DNSKEY found for " + signer.toString() + ", unable to compute the requested RRSIG"); + } + + size_t recordsCount = records.size(); + const DNSName& name = records[recordsCount-1].d_name; + const uint16_t type = records[recordsCount-1].d_type; + + std::vector > recordcontents; + for (const auto record : records) { + if (record.d_name == name && record.d_type == type) { + recordcontents.push_back(record.d_content); + } + } + + RRSIGRecordContent rrc; + computeRRSIG(it->second, signer, records[recordsCount-1].d_name, records[recordsCount-1].d_type, records[recordsCount-1].d_ttl, sigValidity, rrc, recordcontents); + + DNSRecord rec; + rec.d_place = records[recordsCount-1].d_place; + rec.d_name = records[recordsCount-1].d_name; + rec.d_type = QType::RRSIG; + rec.d_ttl = sigValidity; + + rec.d_content = std::make_shared(rrc); + records.push_back(rec); +} + +static void addDNSKEY(const std::unordered_map& keys, const DNSName& signer, uint32_t ttl, std::vector& records) +{ + const auto it = keys.find(signer); + if (it == keys.cend()) { + throw std::runtime_error("No DNSKEY found for " + signer.toString()); + } + + DNSRecord rec; + rec.d_place = DNSResourceRecord::ANSWER; + rec.d_name = signer; + rec.d_type = QType::DNSKEY; + rec.d_ttl = ttl; + + rec.d_content = std::make_shared(it->second.getDNSKEY()); + records.push_back(rec); +} + +static void addDS(const DNSName& domain, uint32_t ttl, std::vector& records) +{ + auto luaconfsLocal = g_luaconfs.getLocal(); + const auto it = luaconfsLocal->dsAnchors.find(domain); + if (it == luaconfsLocal->dsAnchors.cend()) { + return; + } + + for (const auto& ds : it->second) { + DNSRecord rec; + rec.d_name = domain; + rec.d_type = QType::DS; + rec.d_place = DNSResourceRecord::AUTHORITY; + rec.d_ttl = ttl; + rec.d_content = std::make_shared(ds); + + records.push_back(rec); + } +} + +static void addNSECRecordToLW(const DNSName& domain, const DNSName& next, const std::set& types, uint32_t ttl, std::vector& records) +{ + NSECRecordContent nrc; + nrc.d_next = next; + nrc.d_set = types; + + DNSRecord rec; + rec.d_name = domain; + rec.d_ttl = ttl; + rec.d_type = QType::NSEC; + rec.d_content = std::make_shared(nrc); + rec.d_place = DNSResourceRecord::AUTHORITY; + + records.push_back(rec); +} + +static void generateKeyMaterial(const DNSName& name, unsigned int algo, uint8_t digest, std::unordered_map& keys, map& dsAnchors) +{ + auto dcke = std::shared_ptr(DNSCryptoKeyEngine::make(algo)); + dcke->create(dcke->getBits()); + DNSSECPrivateKey dpk; + dpk.d_flags = 256; + dpk.setKey(dcke); + keys[name] = dpk; + DSRecordContent ds = makeDSFromDNSKey(name, dpk.getDNSKEY(), digest); + dsAnchors[name].insert(ds); +} + /* Real tests */ BOOST_AUTO_TEST_SUITE(syncres_cc) @@ -258,7 +402,7 @@ BOOST_AUTO_TEST_CASE(test_root_primed) { BOOST_REQUIRE_EQUAL(ret.size(), 1); BOOST_CHECK(ret[0].d_type == QType::AAAA); BOOST_CHECK_EQUAL(ret[0].d_name, target); - + BOOST_CHECK_EQUAL(sr->getValidationState(), Indeterminate); } BOOST_AUTO_TEST_CASE(test_root_primed_ns) { @@ -280,8 +424,10 @@ BOOST_AUTO_TEST_CASE(test_root_primed_ns) { if (domain == target && type == QType::NS) { setLWResult(res, 0, true, false, true); + char addr[] = "a.root-servers.net."; for (char idx = 'a'; idx <= 'm'; idx++) { - addRecordToLW(res, g_rootdnsname, QType::NS, std::to_string(idx) + ".root-servers.net.", DNSResourceRecord::ANSWER, 3600); + addr[0] = idx; + addRecordToLW(res, g_rootdnsname, QType::NS, std::string(addr), DNSResourceRecord::ANSWER, 3600); } addRecordToLW(res, "a.root-servers.net.", QType::A, "198.41.0.4", DNSResourceRecord::ADDITIONAL, 3600); @@ -509,14 +655,14 @@ BOOST_AUTO_TEST_CASE(test_all_nss_down) { sr->setAsyncCallback([&downServers](const ComboAddress& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boost::optional& srcmask, boost::optional context, std::shared_ptr outgoingLogger, LWResult* res) { if (isRootServer(ip)) { - setLWResult(res, 0, true, false, true); + setLWResult(res, 0, false, false, true); addRecordToLW(res, "com.", QType::NS, "a.gtld-servers.net.", DNSResourceRecord::AUTHORITY, 172800); addRecordToLW(res, "a.gtld-servers.net.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600); addRecordToLW(res, "a.gtld-servers.net.", QType::AAAA, "2001:DB8::1", DNSResourceRecord::ADDITIONAL, 3600); return 1; } else if (ip == ComboAddress("192.0.2.1:53") || ip == ComboAddress("[2001:DB8::1]:53")) { - setLWResult(res, 0, true, false, true); + setLWResult(res, 0, false, false, true); addRecordToLW(res, "powerdns.com.", QType::NS, "pdns-public-ns1.powerdns.com.", DNSResourceRecord::AUTHORITY, 172800); addRecordToLW(res, "powerdns.com.", QType::NS, "pdns-public-ns2.powerdns.com.", DNSResourceRecord::AUTHORITY, 172800); addRecordToLW(res, "pdns-public-ns1.powerdns.com.", QType::A, "192.0.2.2", DNSResourceRecord::ADDITIONAL, 172800); @@ -556,14 +702,14 @@ BOOST_AUTO_TEST_CASE(test_all_nss_network_error) { sr->setAsyncCallback([&downServers](const ComboAddress& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boost::optional& srcmask, boost::optional context, std::shared_ptr outgoingLogger, LWResult* res) { if (isRootServer(ip)) { - setLWResult(res, 0, true, false, true); + setLWResult(res, 0, false, false, true); addRecordToLW(res, "com.", QType::NS, "a.gtld-servers.net.", DNSResourceRecord::AUTHORITY, 172800); addRecordToLW(res, "a.gtld-servers.net.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600); addRecordToLW(res, "a.gtld-servers.net.", QType::AAAA, "2001:DB8::1", DNSResourceRecord::ADDITIONAL, 3600); return 1; } else if (ip == ComboAddress("192.0.2.1:53") || ip == ComboAddress("[2001:DB8::1]:53")) { - setLWResult(res, 0, true, false, true); + setLWResult(res, 0, false, false, true); addRecordToLW(res, "powerdns.com.", QType::NS, "pdns-public-ns1.powerdns.com.", DNSResourceRecord::AUTHORITY, 172800); addRecordToLW(res, "powerdns.com.", QType::NS, "pdns-public-ns2.powerdns.com.", DNSResourceRecord::AUTHORITY, 172800); addRecordToLW(res, "pdns-public-ns1.powerdns.com.", QType::A, "192.0.2.2", DNSResourceRecord::ADDITIONAL, 172800); @@ -605,14 +751,14 @@ BOOST_AUTO_TEST_CASE(test_os_limit_errors) { sr->setAsyncCallback([&downServers](const ComboAddress& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boost::optional& srcmask, boost::optional context, std::shared_ptr outgoingLogger, LWResult* res) { if (isRootServer(ip)) { - setLWResult(res, 0, true, false, true); + setLWResult(res, 0, false, false, true); addRecordToLW(res, "com.", QType::NS, "a.gtld-servers.net.", DNSResourceRecord::AUTHORITY, 172800); addRecordToLW(res, "a.gtld-servers.net.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600); addRecordToLW(res, "a.gtld-servers.net.", QType::AAAA, "2001:DB8::1", DNSResourceRecord::ADDITIONAL, 3600); return 1; } else if (ip == ComboAddress("192.0.2.1:53") || ip == ComboAddress("[2001:DB8::1]:53")) { - setLWResult(res, 0, true, false, true); + setLWResult(res, 0, false, false, true); addRecordToLW(res, "powerdns.com.", QType::NS, "pdns-public-ns1.powerdns.com.", DNSResourceRecord::AUTHORITY, 172800); addRecordToLW(res, "powerdns.com.", QType::NS, "pdns-public-ns2.powerdns.com.", DNSResourceRecord::AUTHORITY, 172800); addRecordToLW(res, "pdns-public-ns1.powerdns.com.", QType::A, "192.0.2.2", DNSResourceRecord::ADDITIONAL, 172800); @@ -666,14 +812,14 @@ BOOST_AUTO_TEST_CASE(test_glued_referral) { } if (isRootServer(ip)) { - setLWResult(res, 0, true, false, true); + setLWResult(res, 0, false, false, true); addRecordToLW(res, "com.", QType::NS, "a.gtld-servers.net.", DNSResourceRecord::AUTHORITY, 172800); addRecordToLW(res, "a.gtld-servers.net.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600); addRecordToLW(res, "a.gtld-servers.net.", QType::AAAA, "2001:DB8::1", DNSResourceRecord::ADDITIONAL, 3600); return 1; } else if (ip == ComboAddress("192.0.2.1:53") || ip == ComboAddress("[2001:DB8::1]:53")) { - setLWResult(res, 0, true, false, true); + setLWResult(res, 0, false, false, true); addRecordToLW(res, "powerdns.com.", QType::NS, "pdns-public-ns1.powerdns.com.", DNSResourceRecord::AUTHORITY, 172800); addRecordToLW(res, "powerdns.com.", QType::NS, "pdns-public-ns2.powerdns.com.", DNSResourceRecord::AUTHORITY, 172800); addRecordToLW(res, "pdns-public-ns1.powerdns.com.", QType::A, "192.0.2.2", DNSResourceRecord::ADDITIONAL, 172800); @@ -712,7 +858,7 @@ BOOST_AUTO_TEST_CASE(test_glueless_referral) { sr->setAsyncCallback([target](const ComboAddress& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boost::optional& srcmask, boost::optional context, std::shared_ptr outgoingLogger, LWResult* res) { if (isRootServer(ip)) { - setLWResult(res, 0, true, false, true); + setLWResult(res, 0, false, false, true); if (domain.isPartOf(DNSName("com."))) { addRecordToLW(res, "com.", QType::NS, "a.gtld-servers.net.", DNSResourceRecord::AUTHORITY, 172800); @@ -730,7 +876,7 @@ BOOST_AUTO_TEST_CASE(test_glueless_referral) { } else if (ip == ComboAddress("192.0.2.1:53") || ip == ComboAddress("[2001:DB8::1]:53")) { if (domain == target) { - setLWResult(res, 0, true, false, true); + setLWResult(res, 0, false, false, true); addRecordToLW(res, "powerdns.com.", QType::NS, "pdns-public-ns1.powerdns.org.", DNSResourceRecord::AUTHORITY, 172800); addRecordToLW(res, "powerdns.com.", QType::NS, "pdns-public-ns2.powerdns.org.", DNSResourceRecord::AUTHORITY, 172800); return 1; @@ -816,7 +962,7 @@ BOOST_AUTO_TEST_CASE(test_edns_submask_by_addr) { if (isRootServer(ip)) { BOOST_REQUIRE(!srcmask); - setLWResult(res, 0, true, false, true); + setLWResult(res, 0, false, false, true); addRecordToLW(res, domain, QType::NS, "a.gtld-servers.net.", DNSResourceRecord::AUTHORITY, 172800); addRecordToLW(res, "a.gtld-servers.net.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600); return 1; @@ -854,7 +1000,7 @@ BOOST_AUTO_TEST_CASE(test_following_cname) { sr->setAsyncCallback([target, cnameTarget](const ComboAddress& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boost::optional& srcmask, boost::optional context, std::shared_ptr outgoingLogger, LWResult* res) { if (isRootServer(ip)) { - setLWResult(res, 0, true, false, true); + setLWResult(res, 0, false, false, true); addRecordToLW(res, domain, QType::NS, "a.gtld-servers.net.", DNSResourceRecord::AUTHORITY, 172800); addRecordToLW(res, "a.gtld-servers.net.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600); return 1; @@ -904,7 +1050,7 @@ BOOST_AUTO_TEST_CASE(test_included_poisonous_cname) { if (isRootServer(ip)) { - setLWResult(res, 0, true, false, true); + setLWResult(res, 0, false, false, true); addRecordToLW(res, domain, QType::NS, "a.gtld-servers.net.", DNSResourceRecord::AUTHORITY, 172800); addRecordToLW(res, "a.gtld-servers.net.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600); @@ -956,7 +1102,7 @@ BOOST_AUTO_TEST_CASE(test_cname_loop) { if (isRootServer(ip)) { - setLWResult(res, 0, true, false, true); + setLWResult(res, 0, false, false, true); addRecordToLW(res, domain, QType::NS, "a.gtld-servers.net.", DNSResourceRecord::AUTHORITY, 172800); addRecordToLW(res, "a.gtld-servers.net.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600); return 1; @@ -995,7 +1141,7 @@ BOOST_AUTO_TEST_CASE(test_cname_depth) { if (isRootServer(ip)) { - setLWResult(res, 0, true, false, true); + setLWResult(res, 0, false, false, true); addRecordToLW(res, domain, QType::NS, "a.gtld-servers.net.", DNSResourceRecord::AUTHORITY, 172800); addRecordToLW(res, "a.gtld-servers.net.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600); return 1; @@ -1033,7 +1179,7 @@ BOOST_AUTO_TEST_CASE(test_time_limit) { queries++; if (isRootServer(ip)) { - setLWResult(res, 0, true, false, true); + setLWResult(res, 0, false, false, true); /* Pretend that this query took 2000 ms */ res->d_usec = 2000; @@ -1078,7 +1224,7 @@ BOOST_AUTO_TEST_CASE(test_referral_depth) { queries++; if (isRootServer(ip)) { - setLWResult(res, 0, true, false, true); + setLWResult(res, 0, false, false, true); if (domain == DNSName("www.powerdns.com.")) { addRecordToLW(res, domain, QType::NS, "ns.powerdns.com.", DNSResourceRecord::AUTHORITY, 172800); @@ -1139,7 +1285,7 @@ BOOST_AUTO_TEST_CASE(test_cname_qperq) { if (isRootServer(ip)) { - setLWResult(res, 0, true, false, true); + setLWResult(res, 0, false, false, true); addRecordToLW(res, domain, QType::NS, "a.gtld-servers.net.", DNSResourceRecord::AUTHORITY, 172800); addRecordToLW(res, "a.gtld-servers.net.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600); return 1; @@ -1181,7 +1327,7 @@ BOOST_AUTO_TEST_CASE(test_throttled_server) { if (isRootServer(ip)) { - setLWResult(res, 0, true, false, true); + setLWResult(res, 0, false, false, true); addRecordToLW(res, domain, QType::NS, "a.gtld-servers.net.", DNSResourceRecord::AUTHORITY, 172800); addRecordToLW(res, "a.gtld-servers.net.", QType::A, ns.toString(), DNSResourceRecord::ADDITIONAL, 3600); return 1; @@ -1266,7 +1412,7 @@ BOOST_AUTO_TEST_CASE(test_dont_query_server) { if (isRootServer(ip)) { - setLWResult(res, 0, true, false, true); + setLWResult(res, 0, false, false, true); addRecordToLW(res, domain, QType::NS, "a.gtld-servers.net.", DNSResourceRecord::AUTHORITY, 172800); addRecordToLW(res, "a.gtld-servers.net.", QType::A, ns.toString(), DNSResourceRecord::ADDITIONAL, 3600); return 1; @@ -1500,7 +1646,7 @@ BOOST_AUTO_TEST_CASE(test_skip_negcache_for_variable_response) { BOOST_CHECK_EQUAL(srcmask->toString(), "192.0.2.0/24"); if (isRootServer(ip)) { - setLWResult(res, 0, true, false, true); + setLWResult(res, 0, false, false, true); addRecordToLW(res, "powerdns.com.", QType::NS, "pdns-public-ns1.powerdns.com.", DNSResourceRecord::AUTHORITY, 172800); addRecordToLW(res, "pdns-public-ns1.powerdns.com.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600); @@ -1547,7 +1693,7 @@ BOOST_AUTO_TEST_CASE(test_ns_speed) { sr->setAsyncCallback([target,&nsCounts](const ComboAddress& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boost::optional& srcmask, boost::optional context, std::shared_ptr outgoingLogger, LWResult* res) { if (isRootServer(ip)) { - setLWResult(res, 0, true, false, true); + setLWResult(res, 0, false, false, true); addRecordToLW(res, domain, QType::NS, "pdns-public-ns1.powerdns.com.", DNSResourceRecord::AUTHORITY, 172800); addRecordToLW(res, domain, QType::NS, "pdns-public-ns2.powerdns.com.", DNSResourceRecord::AUTHORITY, 172800); addRecordToLW(res, domain, QType::NS, "pdns-public-ns3.powerdns.com.", DNSResourceRecord::AUTHORITY, 172800); @@ -1617,7 +1763,7 @@ BOOST_AUTO_TEST_CASE(test_flawed_nsset) { sr->setAsyncCallback([target](const ComboAddress& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boost::optional& srcmask, boost::optional context, std::shared_ptr outgoingLogger, LWResult* res) { if (isRootServer(ip)) { - setLWResult(res, 0, true, false, true); + setLWResult(res, 0, false, false, true); addRecordToLW(res, domain, QType::NS, "pdns-public-ns1.powerdns.com.", DNSResourceRecord::AUTHORITY, 172800); addRecordToLW(res, "pdns-public-ns1.powerdns.com.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600); @@ -1661,7 +1807,7 @@ BOOST_AUTO_TEST_CASE(test_completely_flawed_nsset) { queriesCount++; if (isRootServer(ip) && domain == target) { - setLWResult(res, 0, true, false, true); + setLWResult(res, 0, false, false, true); addRecordToLW(res, domain, QType::NS, "pdns-public-ns2.powerdns.com.", DNSResourceRecord::AUTHORITY, 172800); addRecordToLW(res, domain, QType::NS, "pdns-public-ns3.powerdns.com.", DNSResourceRecord::AUTHORITY, 172800); return 1; @@ -1749,7 +1895,7 @@ BOOST_AUTO_TEST_CASE(test_cache_min_max_ttl) { if (isRootServer(ip)) { - setLWResult(res, 0, true, false, true); + setLWResult(res, 0, false, false, true); addRecordToLW(res, domain, QType::NS, "a.gtld-servers.net.", DNSResourceRecord::AUTHORITY, 172800); addRecordToLW(res, "a.gtld-servers.net.", QType::A, ns.toString(), DNSResourceRecord::ADDITIONAL, 7200); return 1; @@ -1800,7 +1946,7 @@ BOOST_AUTO_TEST_CASE(test_cache_expired_ttl) { sr->setAsyncCallback([target](const ComboAddress& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boost::optional& srcmask, boost::optional context, std::shared_ptr outgoingLogger, LWResult* res) { if (isRootServer(ip)) { - setLWResult(res, 0, true, false, true); + setLWResult(res, 0, false, false, true); addRecordToLW(res, domain, QType::NS, "pdns-public-ns1.powerdns.com.", DNSResourceRecord::AUTHORITY, 172800); addRecordToLW(res, "pdns-public-ns1.powerdns.com.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600); @@ -1847,7 +1993,7 @@ BOOST_AUTO_TEST_CASE(test_delegation_only) { sr->setAsyncCallback([target](const ComboAddress& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boost::optional& srcmask, boost::optional context, std::shared_ptr outgoingLogger, LWResult* res) { if (isRootServer(ip)) { - setLWResult(res, 0, true, false, true); + setLWResult(res, 0, false, false, true); addRecordToLW(res, "com.", QType::NS, "a.gtld-servers.net.", DNSResourceRecord::AUTHORITY, 172800); addRecordToLW(res, "a.gtld-servers.net.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600); return 1; @@ -1879,7 +2025,7 @@ BOOST_AUTO_TEST_CASE(test_unauth_any) { sr->setAsyncCallback([target](const ComboAddress& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boost::optional& srcmask, boost::optional context, std::shared_ptr outgoingLogger, LWResult* res) { if (isRootServer(ip)) { - setLWResult(res, 0, true, false, true); + setLWResult(res, 0, false, false, true); addRecordToLW(res, "com.", QType::NS, "a.gtld-servers.net.", DNSResourceRecord::AUTHORITY, 172800); addRecordToLW(res, "a.gtld-servers.net.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600); return 1; @@ -2051,7 +2197,7 @@ BOOST_AUTO_TEST_CASE(test_nx_nsec_dnssec) { BOOST_AUTO_TEST_CASE(test_qclass_none) { std::unique_ptr sr; init(); - initSR(sr, true, true); + initSR(sr, true, false); primeHints(); @@ -2075,7 +2221,7 @@ BOOST_AUTO_TEST_CASE(test_qclass_none) { BOOST_AUTO_TEST_CASE(test_xfr) { std::unique_ptr sr; init(); - initSR(sr, true, true); + initSR(sr, true, false); primeHints(); @@ -2105,7 +2251,7 @@ BOOST_AUTO_TEST_CASE(test_xfr) { BOOST_AUTO_TEST_CASE(test_special_names) { std::unique_ptr sr; init(); - initSR(sr, true, true); + initSR(sr, true, false); primeHints(); @@ -2240,7 +2386,7 @@ BOOST_AUTO_TEST_CASE(test_nameserver_ipv4_rpz) { sr->setAsyncCallback([target,ns](const ComboAddress& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boost::optional& srcmask, boost::optional context, std::shared_ptr outgoingLogger, LWResult* res) { if (isRootServer(ip)) { - setLWResult(res, 0, true, false, true); + setLWResult(res, false, true, false, true); addRecordToLW(res, "com.", QType::NS, "a.gtld-servers.net.", DNSResourceRecord::AUTHORITY, 172800); addRecordToLW(res, "a.gtld-servers.net.", QType::A, ns.toString(), DNSResourceRecord::ADDITIONAL, 3600); return 1; @@ -2282,7 +2428,7 @@ BOOST_AUTO_TEST_CASE(test_nameserver_ipv6_rpz) { sr->setAsyncCallback([target,ns](const ComboAddress& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boost::optional& srcmask, boost::optional context, std::shared_ptr outgoingLogger, LWResult* res) { if (isRootServer(ip)) { - setLWResult(res, 0, true, false, true); + setLWResult(res, 0, false, false, true); addRecordToLW(res, "com.", QType::NS, "a.gtld-servers.net.", DNSResourceRecord::AUTHORITY, 172800); addRecordToLW(res, "a.gtld-servers.net.", QType::AAAA, ns.toString(), DNSResourceRecord::ADDITIONAL, 3600); return 1; @@ -2325,7 +2471,7 @@ BOOST_AUTO_TEST_CASE(test_nameserver_name_rpz) { sr->setAsyncCallback([target,ns,nsName](const ComboAddress& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boost::optional& srcmask, boost::optional context, std::shared_ptr outgoingLogger, LWResult* res) { if (isRootServer(ip)) { - setLWResult(res, 0, true, false, true); + setLWResult(res, 0, false, false, true); addRecordToLW(res, domain, QType::NS, nsName.toString(), DNSResourceRecord::AUTHORITY, 172800); addRecordToLW(res, nsName, QType::A, ns.toString(), DNSResourceRecord::ADDITIONAL, 3600); return 1; @@ -2368,7 +2514,7 @@ BOOST_AUTO_TEST_CASE(test_nameserver_name_rpz_disabled) { sr->setAsyncCallback([target,ns,nsName](const ComboAddress& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boost::optional& srcmask, boost::optional context, std::shared_ptr outgoingLogger, LWResult* res) { if (isRootServer(ip)) { - setLWResult(res, 0, true, false, true); + setLWResult(res, 0, false, false, true); addRecordToLW(res, domain, QType::NS, nsName.toString(), DNSResourceRecord::AUTHORITY, 172800); addRecordToLW(res, nsName, QType::A, ns.toString(), DNSResourceRecord::ADDITIONAL, 3600); return 1; @@ -3125,6 +3271,773 @@ BOOST_AUTO_TEST_CASE(test_auth_zone_cache_only) { BOOST_CHECK_EQUAL(queriesCount, 0); } +BOOST_AUTO_TEST_CASE(test_dnssec_rrsig) { +// g_dnssecLOG = true; + init(); + + auto dcke = std::shared_ptr(DNSCryptoKeyEngine::make(DNSSECKeeper::ECDSA256)); + dcke->create(dcke->getBits()); + // cerr<convertToISC()< > recordcontents; + recordcontents.push_back(getRecordContent(QType::A, "192.0.2.1")); + + DNSName qname("powerdns.com."); + + RRSIGRecordContent rrc; + computeRRSIG(dpk, qname, qname, QType::A, 600, 300, rrc, recordcontents); + + skeyset_t keyset; + keyset.insert(std::make_shared(dpk.getDNSKEY())); + + std::vector > sigs; + sigs.push_back(std::make_shared(rrc)); + + BOOST_CHECK(validateWithKeySet(time(nullptr), qname, recordcontents, sigs, keyset)); +} + +BOOST_AUTO_TEST_CASE(test_dnssec_root_validation_csk) { + std::unique_ptr sr; + init(); + initSR(sr, true, true); + + g_dnssecmode = DNSSECMode::ValidateAll; + + primeHints(); + const DNSName target("."); + std::unordered_map keys; + + auto luaconfsCopy = g_luaconfs.getCopy(); + luaconfsCopy.dsAnchors.clear(); + generateKeyMaterial(target, DNSSECKeeper::ECDSA256, DNSSECKeeper::SHA256, keys, luaconfsCopy.dsAnchors); + g_luaconfs.setState(luaconfsCopy); + + size_t queriesCount = 0; + + sr->setAsyncCallback([target,&queriesCount,keys](const ComboAddress& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boost::optional& srcmask, boost::optional context, std::shared_ptr outgoingLogger, LWResult* res) { + queriesCount++; + + if (domain == target && type == QType::NS) { + + setLWResult(res, 0, true, false, true); + char addr[] = "a.root-servers.net."; + for (char idx = 'a'; idx <= 'm'; idx++) { + addr[0] = idx; + addRecordToLW(res, domain, QType::NS, std::string(addr), DNSResourceRecord::ANSWER, 3600); + } + + addRRSIG(keys, res->d_records, domain, 300); + + addRecordToLW(res, "a.root-servers.net.", QType::A, "198.41.0.4", DNSResourceRecord::ADDITIONAL, 3600); + addRecordToLW(res, "a.root-servers.net.", QType::AAAA, "2001:503:ba3e::2:30", DNSResourceRecord::ADDITIONAL, 3600); + + return 1; + } else if (domain == target && type == QType::DNSKEY) { + + setLWResult(res, 0, true, false, true); + + addDNSKEY(keys, domain, 300, res->d_records); + addRRSIG(keys, res->d_records, domain, 300); + + return 1; + } + + return 0; + }); + + vector ret; + int res = sr->beginResolve(target, QType(QType::NS), QClass::IN, ret); + BOOST_CHECK_EQUAL(res, 0); + /* 13 NS + 1 RRSIG */ + BOOST_REQUIRE_EQUAL(ret.size(), 14); + BOOST_CHECK_EQUAL(queriesCount, 2); + BOOST_CHECK_EQUAL(sr->getValidationState(), Secure); +} + +BOOST_AUTO_TEST_CASE(test_dnssec_root_validation_ksk_zsk) { + std::unique_ptr sr; + init(); + initSR(sr, true, true); + + g_dnssecmode = DNSSECMode::ValidateAll; + + primeHints(); + const DNSName target("."); + std::unordered_map zskeys; + std::unordered_map kskeys; + + /* Generate key material for "." */ + auto dckeZ = std::shared_ptr(DNSCryptoKeyEngine::make(DNSSECKeeper::ECDSA256)); + dckeZ->create(dckeZ->getBits()); + DNSSECPrivateKey ksk; + ksk.d_flags = 257; + ksk.setKey(dckeZ); + auto dckeK = std::shared_ptr(DNSCryptoKeyEngine::make(DNSSECKeeper::ECDSA256)); + dckeK->create(dckeK->getBits()); + DNSSECPrivateKey zsk; + zsk.d_flags = 256; + zsk.setKey(dckeK); + + kskeys[target] = ksk; + zskeys[target] = zsk; + + /* Set the root DS */ + DSRecordContent drc = makeDSFromDNSKey(target, ksk.getDNSKEY(), DNSSECKeeper::SHA256); + + auto luaconfsCopy = g_luaconfs.getCopy(); + luaconfsCopy.dsAnchors.clear(); + luaconfsCopy.dsAnchors[g_rootdnsname].insert(drc); + g_luaconfs.setState(luaconfsCopy); + + size_t queriesCount = 0; + + sr->setAsyncCallback([target,&queriesCount,zskeys,kskeys](const ComboAddress& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boost::optional& srcmask, boost::optional context, std::shared_ptr outgoingLogger, LWResult* res) { + queriesCount++; + + if (domain == target && type == QType::NS) { + + setLWResult(res, 0, true, false, true); + char addr[] = "a.root-servers.net."; + for (char idx = 'a'; idx <= 'm'; idx++) { + addr[0] = idx; + addRecordToLW(res, domain, QType::NS, std::string(addr), DNSResourceRecord::ANSWER, 3600); + } + + addRRSIG(zskeys, res->d_records, domain, 300); + + addRecordToLW(res, "a.root-servers.net.", QType::A, "198.41.0.4", DNSResourceRecord::ADDITIONAL, 3600); + addRecordToLW(res, "a.root-servers.net.", QType::AAAA, "2001:503:ba3e::2:30", DNSResourceRecord::ADDITIONAL, 3600); + + return 1; + } else if (domain == target && type == QType::DNSKEY) { + + setLWResult(res, 0, true, false, true); + + addDNSKEY(kskeys, domain, 300, res->d_records); + addDNSKEY(zskeys, domain, 300, res->d_records); + addRRSIG(kskeys, res->d_records, domain, 300); + + return 1; + } + + return 0; + }); + + vector ret; + int res = sr->beginResolve(target, QType(QType::NS), QClass::IN, ret); + BOOST_CHECK_EQUAL(res, 0); + /* 13 NS + 1 RRSIG */ + BOOST_REQUIRE_EQUAL(ret.size(), 14); + BOOST_CHECK_EQUAL(queriesCount, 2); + BOOST_CHECK_EQUAL(sr->getValidationState(), Secure); +} + +BOOST_AUTO_TEST_CASE(test_dnssec_bogus_no_dnskey) { + std::unique_ptr sr; + init(); + initSR(sr, true, true); + + g_dnssecmode = DNSSECMode::ValidateAll; + + primeHints(); + const DNSName target("."); + std::unordered_map keys; + + auto luaconfsCopy = g_luaconfs.getCopy(); + luaconfsCopy.dsAnchors.clear(); + generateKeyMaterial(target, DNSSECKeeper::ECDSA256, DNSSECKeeper::SHA256, keys, luaconfsCopy.dsAnchors); + g_luaconfs.setState(luaconfsCopy); + + size_t queriesCount = 0; + + sr->setAsyncCallback([target,&queriesCount,keys](const ComboAddress& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boost::optional& srcmask, boost::optional context, std::shared_ptr outgoingLogger, LWResult* res) { + queriesCount++; + + if (domain == target && type == QType::NS) { + + setLWResult(res, 0, true, false, true); + char addr[] = "a.root-servers.net."; + for (char idx = 'a'; idx <= 'm'; idx++) { + addr[0] = idx; + addRecordToLW(res, domain, QType::NS, std::string(addr), DNSResourceRecord::ANSWER, 3600); + } + + addRRSIG(keys, res->d_records, domain, 300); + + addRecordToLW(res, "a.root-servers.net.", QType::A, "198.41.0.4", DNSResourceRecord::ADDITIONAL, 3600); + addRecordToLW(res, "a.root-servers.net.", QType::AAAA, "2001:503:ba3e::2:30", DNSResourceRecord::ADDITIONAL, 3600); + + return 1; + } else if (domain == target && type == QType::DNSKEY) { + + setLWResult(res, 0, true, false, true); + + /* No DNSKEY */ + + return 1; + } + + return 0; + }); + + vector ret; + int res = sr->beginResolve(target, QType(QType::NS), QClass::IN, ret); + BOOST_CHECK_EQUAL(res, 0); + /* 13 NS + 1 RRSIG */ + BOOST_REQUIRE_EQUAL(ret.size(), 14); + BOOST_CHECK_EQUAL(queriesCount, 2); + BOOST_CHECK_EQUAL(sr->getValidationState(), Bogus); +} + +BOOST_AUTO_TEST_CASE(test_dnssec_bogus_dnskey_doesnt_match_ds) { + std::unique_ptr sr; + init(); + initSR(sr, true, true); + + g_dnssecmode = DNSSECMode::ValidateAll; + + primeHints(); + const DNSName target("."); + std::unordered_map dskeys; + std::unordered_map keys; + + /* Generate key material for "." */ + auto dckeDS = std::shared_ptr(DNSCryptoKeyEngine::make(DNSSECKeeper::ECDSA256)); + dckeDS->create(dckeDS->getBits()); + DNSSECPrivateKey dskey; + dskey.d_flags = 257; + dskey.setKey(dckeDS); + auto dcke = std::shared_ptr(DNSCryptoKeyEngine::make(DNSSECKeeper::ECDSA256)); + dcke->create(dcke->getBits()); + DNSSECPrivateKey dpk; + dpk.d_flags = 256; + dpk.setKey(dcke); + + dskeys[target] = dskey; + keys[target] = dpk; + + /* Set the root DS */ + DSRecordContent drc = makeDSFromDNSKey(target, dskey.getDNSKEY(), DNSSECKeeper::SHA256); + + auto luaconfsCopy = g_luaconfs.getCopy(); + luaconfsCopy.dsAnchors.clear(); + luaconfsCopy.dsAnchors[g_rootdnsname].insert(drc); + g_luaconfs.setState(luaconfsCopy); + + size_t queriesCount = 0; + + sr->setAsyncCallback([target,&queriesCount,keys](const ComboAddress& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boost::optional& srcmask, boost::optional context, std::shared_ptr outgoingLogger, LWResult* res) { + queriesCount++; + + if (domain == target && type == QType::NS) { + + setLWResult(res, 0, true, false, true); + char addr[] = "a.root-servers.net."; + for (char idx = 'a'; idx <= 'm'; idx++) { + addr[0] = idx; + addRecordToLW(res, domain, QType::NS, std::string(addr), DNSResourceRecord::ANSWER, 3600); + } + + addRRSIG(keys, res->d_records, domain, 300); + + addRecordToLW(res, "a.root-servers.net.", QType::A, "198.41.0.4", DNSResourceRecord::ADDITIONAL, 3600); + addRecordToLW(res, "a.root-servers.net.", QType::AAAA, "2001:503:ba3e::2:30", DNSResourceRecord::ADDITIONAL, 3600); + + return 1; + } else if (domain == target && type == QType::DNSKEY) { + + setLWResult(res, 0, true, false, true); + + addDNSKEY(keys, domain, 300, res->d_records); + addRRSIG(keys, res->d_records, domain, 300); + + return 1; + } + + return 0; + }); + + vector ret; + int res = sr->beginResolve(target, QType(QType::NS), QClass::IN, ret); + BOOST_CHECK_EQUAL(res, 0); + /* 13 NS + 1 RRSIG */ + BOOST_REQUIRE_EQUAL(ret.size(), 14); + BOOST_CHECK_EQUAL(queriesCount, 2); + BOOST_CHECK_EQUAL(sr->getValidationState(), Bogus); +} + +BOOST_AUTO_TEST_CASE(test_dnssec_bogus_rrsig_signed_with_unknown_dnskey) { + std::unique_ptr sr; + init(); + initSR(sr, true, true); + + g_dnssecmode = DNSSECMode::ValidateAll; + + primeHints(); + const DNSName target("."); + std::unordered_map keys; + std::unordered_map rrsigkeys; + + auto luaconfsCopy = g_luaconfs.getCopy(); + luaconfsCopy.dsAnchors.clear(); + generateKeyMaterial(target, DNSSECKeeper::ECDSA256, DNSSECKeeper::SHA256, keys, luaconfsCopy.dsAnchors); + g_luaconfs.setState(luaconfsCopy); + + auto dckeRRSIG = std::shared_ptr(DNSCryptoKeyEngine::make(DNSSECKeeper::ECDSA256)); + dckeRRSIG->create(dckeRRSIG->getBits()); + DNSSECPrivateKey rrsigkey; + rrsigkey.d_flags = 257; + rrsigkey.setKey(dckeRRSIG); + rrsigkeys[target] = rrsigkey; + + size_t queriesCount = 0; + + sr->setAsyncCallback([target,&queriesCount,keys,rrsigkeys](const ComboAddress& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boost::optional& srcmask, boost::optional context, std::shared_ptr outgoingLogger, LWResult* res) { + queriesCount++; + + if (domain == target && type == QType::NS) { + + setLWResult(res, 0, true, false, true); + char addr[] = "a.root-servers.net."; + for (char idx = 'a'; idx <= 'm'; idx++) { + addr[0] = idx; + addRecordToLW(res, domain, QType::NS, std::string(addr), DNSResourceRecord::ANSWER, 3600); + } + + addRRSIG(rrsigkeys, res->d_records, domain, 300); + + addRecordToLW(res, "a.root-servers.net.", QType::A, "198.41.0.4", DNSResourceRecord::ADDITIONAL, 3600); + addRecordToLW(res, "a.root-servers.net.", QType::AAAA, "2001:503:ba3e::2:30", DNSResourceRecord::ADDITIONAL, 3600); + + return 1; + } else if (domain == target && type == QType::DNSKEY) { + + setLWResult(res, 0, true, false, true); + + addDNSKEY(keys, domain, 300, res->d_records); + addRRSIG(rrsigkeys, res->d_records, domain, 300); + + return 1; + } + + return 0; + }); + + vector ret; + int res = sr->beginResolve(target, QType(QType::NS), QClass::IN, ret); + BOOST_CHECK_EQUAL(res, 0); + /* 13 NS + 1 RRSIG */ + BOOST_REQUIRE_EQUAL(ret.size(), 14); + BOOST_CHECK_EQUAL(queriesCount, 2); + BOOST_CHECK_EQUAL(sr->getValidationState(), Bogus); +} + +BOOST_AUTO_TEST_CASE(test_dnssec_bogus_no_rrsig) { + std::unique_ptr sr; + init(); + initSR(sr, true, true); + + g_dnssecmode = DNSSECMode::ValidateAll; + + primeHints(); + const DNSName target("."); + std::unordered_map keys; + + auto luaconfsCopy = g_luaconfs.getCopy(); + luaconfsCopy.dsAnchors.clear(); + generateKeyMaterial(target, DNSSECKeeper::ECDSA256, DNSSECKeeper::SHA256, keys, luaconfsCopy.dsAnchors); + g_luaconfs.setState(luaconfsCopy); + + size_t queriesCount = 0; + + sr->setAsyncCallback([target,&queriesCount,keys](const ComboAddress& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boost::optional& srcmask, boost::optional context, std::shared_ptr outgoingLogger, LWResult* res) { + queriesCount++; + + if (domain == target && type == QType::NS) { + + setLWResult(res, 0, true, false, true); + char addr[] = "a.root-servers.net."; + for (char idx = 'a'; idx <= 'm'; idx++) { + addr[0] = idx; + addRecordToLW(res, domain, QType::NS, std::string(addr), DNSResourceRecord::ANSWER, 3600); + } + + /* No RRSIG */ + + addRecordToLW(res, "a.root-servers.net.", QType::A, "198.41.0.4", DNSResourceRecord::ADDITIONAL, 3600); + addRecordToLW(res, "a.root-servers.net.", QType::AAAA, "2001:503:ba3e::2:30", DNSResourceRecord::ADDITIONAL, 3600); + + return 1; + } else if (domain == target && type == QType::DNSKEY) { + + setLWResult(res, 0, true, false, true); + + addDNSKEY(keys, domain, 300, res->d_records); + addRRSIG(keys, res->d_records, domain, 300); + + return 1; + } + + return 0; + }); + + vector ret; + int res = sr->beginResolve(target, QType(QType::NS), QClass::IN, ret); + BOOST_CHECK_EQUAL(res, 0); + /* 13 NS + 0 RRSIG */ + BOOST_REQUIRE_EQUAL(ret.size(), 13); + /* no RRSIG so no query for DNSKEYs */ + BOOST_CHECK_EQUAL(queriesCount, 1); + BOOST_CHECK_EQUAL(sr->getValidationState(), Bogus); +} + +BOOST_AUTO_TEST_CASE(test_dnssec_insecure_unknown_ds_algorithm) { + std::unique_ptr sr; + init(); + initSR(sr, true, true); + + g_dnssecmode = DNSSECMode::ValidateAll; + + primeHints(); + const DNSName target("."); + std::unordered_map keys; + + /* Generate key material for "." */ + auto dcke = std::shared_ptr(DNSCryptoKeyEngine::make(DNSSECKeeper::ECDSA256)); + dcke->create(dcke->getBits()); + DNSSECPrivateKey dpk; + dpk.d_flags = 256; + dpk.setKey(dcke); + /* Fake algorithm number (private) */ + dpk.d_algorithm = 253; + + keys[target] = dpk; + + /* Set the root DS */ + DSRecordContent drc = makeDSFromDNSKey(target, dpk.getDNSKEY(), DNSSECKeeper::SHA256); + /* Fake algorithm number (private) */ + drc.d_algorithm = 253; + + auto luaconfsCopy = g_luaconfs.getCopy(); + luaconfsCopy.dsAnchors.clear(); + luaconfsCopy.dsAnchors[g_rootdnsname].insert(drc); + g_luaconfs.setState(luaconfsCopy); + + size_t queriesCount = 0; + + sr->setAsyncCallback([target,&queriesCount,keys](const ComboAddress& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boost::optional& srcmask, boost::optional context, std::shared_ptr outgoingLogger, LWResult* res) { + queriesCount++; + + if (domain == target && type == QType::NS) { + + setLWResult(res, 0, true, false, true); + char addr[] = "a.root-servers.net."; + for (char idx = 'a'; idx <= 'm'; idx++) { + addr[0] = idx; + addRecordToLW(res, domain, QType::NS, std::string(addr), DNSResourceRecord::ANSWER, 3600); + } + + addRRSIG(keys, res->d_records, domain, 300); + + addRecordToLW(res, "a.root-servers.net.", QType::A, "198.41.0.4", DNSResourceRecord::ADDITIONAL, 3600); + addRecordToLW(res, "a.root-servers.net.", QType::AAAA, "2001:503:ba3e::2:30", DNSResourceRecord::ADDITIONAL, 3600); + + return 1; + } else if (domain == target && type == QType::DNSKEY) { + + setLWResult(res, 0, true, false, true); + + addDNSKEY(keys, domain, 300, res->d_records); + addRRSIG(keys, res->d_records, domain, 300); + + return 1; + } + + return 0; + }); + + vector ret; + int res = sr->beginResolve(target, QType(QType::NS), QClass::IN, ret); + BOOST_CHECK_EQUAL(res, 0); + /* 13 NS + 1 RRSIG */ + BOOST_REQUIRE_EQUAL(ret.size(), 14); + /* no supported DS so no query for DNSKEYs */ + BOOST_CHECK_EQUAL(queriesCount, 1); + BOOST_CHECK_EQUAL(sr->getValidationState(), Insecure); +} + +BOOST_AUTO_TEST_CASE(test_dnssec_insecure_unknown_ds_digest) { + std::unique_ptr sr; + init(); + initSR(sr, true, true); + + g_dnssecmode = DNSSECMode::ValidateAll; + + primeHints(); + const DNSName target("."); + std::unordered_map keys; + + /* Generate key material for "." */ + auto dcke = std::shared_ptr(DNSCryptoKeyEngine::make(DNSSECKeeper::ECDSA256)); + dcke->create(dcke->getBits()); + DNSSECPrivateKey dpk; + dpk.d_flags = 256; + dpk.setKey(dcke); + + keys[target] = dpk; + + /* Set the root DS */ + DSRecordContent drc = makeDSFromDNSKey(target, dpk.getDNSKEY(), DNSSECKeeper::SHA256); + /* Fake digest number (reserved) */ + drc.d_digesttype = 0; + + auto luaconfsCopy = g_luaconfs.getCopy(); + luaconfsCopy.dsAnchors.clear(); + luaconfsCopy.dsAnchors[g_rootdnsname].insert(drc); + g_luaconfs.setState(luaconfsCopy); + + size_t queriesCount = 0; + + sr->setAsyncCallback([target,&queriesCount,keys](const ComboAddress& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boost::optional& srcmask, boost::optional context, std::shared_ptr outgoingLogger, LWResult* res) { + queriesCount++; + + if (domain == target && type == QType::NS) { + + setLWResult(res, 0, true, false, true); + char addr[] = "a.root-servers.net."; + for (char idx = 'a'; idx <= 'm'; idx++) { + addr[0] = idx; + addRecordToLW(res, domain, QType::NS, std::string(addr), DNSResourceRecord::ANSWER, 3600); + } + + addRRSIG(keys, res->d_records, domain, 300); + + addRecordToLW(res, "a.root-servers.net.", QType::A, "198.41.0.4", DNSResourceRecord::ADDITIONAL, 3600); + addRecordToLW(res, "a.root-servers.net.", QType::AAAA, "2001:503:ba3e::2:30", DNSResourceRecord::ADDITIONAL, 3600); + + return 1; + } else if (domain == target && type == QType::DNSKEY) { + + setLWResult(res, 0, true, false, true); + + addDNSKEY(keys, domain, 300, res->d_records); + addRRSIG(keys, res->d_records, domain, 300); + + return 1; + } + + return 0; + }); + + vector ret; + int res = sr->beginResolve(target, QType(QType::NS), QClass::IN, ret); + BOOST_CHECK_EQUAL(res, 0); + /* 13 NS + 1 RRSIG */ + BOOST_REQUIRE_EQUAL(ret.size(), 14); + /* no supported DS so no query for DNSKEYs */ + BOOST_CHECK_EQUAL(queriesCount, 1); + BOOST_CHECK_EQUAL(sr->getValidationState(), Insecure); +} + +BOOST_AUTO_TEST_CASE(test_dnssec_root_validation_nsec) { + std::unique_ptr sr; + init(); + initSR(sr, true, true); + + g_dnssecmode = DNSSECMode::ValidateAll; + + primeHints(); + const DNSName target("powerdns.com."); + std::unordered_map keys; + + auto luaconfsCopy = g_luaconfs.getCopy(); + luaconfsCopy.dsAnchors.clear(); + generateKeyMaterial(g_rootdnsname, DNSSECKeeper::ECDSA256, DNSSECKeeper::SHA256, keys, luaconfsCopy.dsAnchors); + generateKeyMaterial(DNSName("com."), DNSSECKeeper::ECDSA256, DNSSECKeeper::SHA256, keys, luaconfsCopy.dsAnchors); + generateKeyMaterial(DNSName("powerdns.com."), DNSSECKeeper::ECDSA256, DNSSECKeeper::SHA256, keys, luaconfsCopy.dsAnchors); + + g_luaconfs.setState(luaconfsCopy); + + size_t queriesCount = 0; + + sr->setAsyncCallback([target,&queriesCount,keys](const ComboAddress& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boost::optional& srcmask, boost::optional context, std::shared_ptr outgoingLogger, LWResult* res) { + queriesCount++; + + if (type == QType::DS) { + setLWResult(res, 0, false, false, true); + addDS(domain, 300, res->d_records); + addRRSIG(keys, res->d_records, domain, 300); + return 1; + } + else if (type == QType::DNSKEY) { + setLWResult(res, 0, true, false, true); + addDNSKEY(keys, domain, 300, res->d_records); + addRRSIG(keys, res->d_records, domain, 300); + return 1; + } + else if (domain == target) { + if (isRootServer(ip)) { + setLWResult(res, 0, false, false, true); + addRecordToLW(res, "com.", QType::NS, "a.gtld-servers.com.", DNSResourceRecord::AUTHORITY, 3600); + addRRSIG(keys, res->d_records, DNSName("."), 300); + addDS(DNSName("com."), 300, res->d_records); + addRRSIG(keys, res->d_records, DNSName("."), 300); + addRecordToLW(res, "a.gtld-servers.com.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600); + return 1; + } + else if (ip == ComboAddress("192.0.2.1:53")) { + setLWResult(res, 0, false, false, true); + addRecordToLW(res, domain, QType::NS, "ns1.powerdns.com.", DNSResourceRecord::AUTHORITY, 3600); + addRRSIG(keys, res->d_records, DNSName("com."), 300); + addDS(domain, 300, res->d_records); + addRRSIG(keys, res->d_records, DNSName("com."), 300); + addRecordToLW(res, "ns1.powerdns.com.", QType::A, "192.0.2.2", DNSResourceRecord::ADDITIONAL, 3600); + return 1; + } + else if (ip == ComboAddress("192.0.2.2:53")) { + setLWResult(res, 0, true, false, true); + addRecordToLW(res, domain, QType::SOA, "pdns-public-ns1.powerdns.com. pieter\\.lexis.powerdns.com. 2017032301 10800 3600 604800 3600", DNSResourceRecord::AUTHORITY, 3600); + addRRSIG(keys, res->d_records, domain, 300); + addNSECRecordToLW(domain, DNSName("z.powerdns.com."), { QType::NS, QType::DNSKEY }, 600, res->d_records); + addRRSIG(keys, res->d_records, domain, 300); + return 1; + } + } + + return 0; + }); + + vector ret; + int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret); + BOOST_CHECK_EQUAL(res, 0); + BOOST_REQUIRE_EQUAL(ret.size(), 4); + BOOST_CHECK_EQUAL(queriesCount, 6); + BOOST_CHECK_EQUAL(sr->getValidationState(), Secure); +} + +BOOST_AUTO_TEST_CASE(test_dnssec_nta) { + std::unique_ptr sr; + init(); + initSR(sr, true, true); + + g_dnssecmode = DNSSECMode::ValidateAll; + + primeHints(); + const DNSName target("."); + std::unordered_map keys; + + /* Generate key material for "." */ + auto dcke = std::shared_ptr(DNSCryptoKeyEngine::make(DNSSECKeeper::ECDSA256)); + dcke->create(dcke->getBits()); + DNSSECPrivateKey dpk; + dpk.d_flags = 256; + dpk.setKey(dcke); + + keys[target] = dpk; + + /* Set the root DS */ + DSRecordContent drc = makeDSFromDNSKey(target, dpk.getDNSKEY(), DNSSECKeeper::SHA256); + + auto luaconfsCopy = g_luaconfs.getCopy(); + luaconfsCopy.dsAnchors.clear(); + luaconfsCopy.dsAnchors[g_rootdnsname].insert(drc); + + /* Add a NTA for "." */ + luaconfsCopy.negAnchors[g_rootdnsname] = "NTA for Root"; + g_luaconfs.setState(luaconfsCopy); + + size_t queriesCount = 0; + + sr->setAsyncCallback([target,&queriesCount,keys](const ComboAddress& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boost::optional& srcmask, boost::optional context, std::shared_ptr outgoingLogger, LWResult* res) { + queriesCount++; + + if (domain == target && type == QType::NS) { + + setLWResult(res, 0, true, false, true); + char addr[] = "a.root-servers.net."; + for (char idx = 'a'; idx <= 'm'; idx++) { + addr[0] = idx; + addRecordToLW(res, domain, QType::NS, std::string(addr), DNSResourceRecord::ANSWER, 3600); + } + + addRRSIG(keys, res->d_records, domain, 300); + + addRecordToLW(res, "a.root-servers.net.", QType::A, "198.41.0.4", DNSResourceRecord::ADDITIONAL, 3600); + addRecordToLW(res, "a.root-servers.net.", QType::AAAA, "2001:503:ba3e::2:30", DNSResourceRecord::ADDITIONAL, 3600); + + return 1; + } else if (domain == target && type == QType::DNSKEY) { + + setLWResult(res, 0, true, false, true); + + /* No DNSKEY */ + + return 1; + } + + return 0; + }); + + vector ret; + int res = sr->beginResolve(target, QType(QType::NS), QClass::IN, ret); + BOOST_CHECK_EQUAL(res, 0); + /* 13 NS + 1 RRSIG */ + BOOST_REQUIRE_EQUAL(ret.size(), 14); + BOOST_CHECK_EQUAL(queriesCount, 1); + BOOST_CHECK_EQUAL(sr->getValidationState(), Insecure); +} + +BOOST_AUTO_TEST_CASE(test_dnssec_no_ta) { + std::unique_ptr sr; + init(); + initSR(sr, true, true); + + g_dnssecmode = DNSSECMode::ValidateAll; + + primeHints(); + const DNSName target("."); + std::unordered_map keys; + + /* Remove the root DS */ + auto luaconfsCopy = g_luaconfs.getCopy(); + luaconfsCopy.dsAnchors.clear(); + g_luaconfs.setState(luaconfsCopy); + + size_t queriesCount = 0; + + sr->setAsyncCallback([target,&queriesCount,keys](const ComboAddress& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boost::optional& srcmask, boost::optional context, std::shared_ptr outgoingLogger, LWResult* res) { + queriesCount++; + + if (domain == target && type == QType::NS) { + + setLWResult(res, 0, true, false, true); + char addr[] = "a.root-servers.net."; + for (char idx = 'a'; idx <= 'm'; idx++) { + addr[0] = idx; + addRecordToLW(res, domain, QType::NS, std::string(addr), DNSResourceRecord::ANSWER, 3600); + } + + addRecordToLW(res, "a.root-servers.net.", QType::A, "198.41.0.4", DNSResourceRecord::ADDITIONAL, 3600); + addRecordToLW(res, "a.root-servers.net.", QType::AAAA, "2001:503:ba3e::2:30", DNSResourceRecord::ADDITIONAL, 3600); + + return 1; + } + + return 0; + }); + + vector ret; + int res = sr->beginResolve(target, QType(QType::NS), QClass::IN, ret); + BOOST_CHECK_EQUAL(res, 0); + /* 13 NS + 0 RRSIG */ + BOOST_REQUIRE_EQUAL(ret.size(), 13); + BOOST_CHECK_EQUAL(queriesCount, 1); + BOOST_CHECK_EQUAL(sr->getValidationState(), Insecure); +} + /* // cerr<<"asyncresolve called to ask "<& records, const std::vector >& signatures, uint32_t signaturesTTL) const @@ -1267,19 +1262,35 @@ vState SyncRes::getTA(const DNSName& zone, dsmap_t& ds) return Indeterminate; } +static size_t countSupportedDS(const dsmap_t& dsmap) +{ + size_t count = 0; + + for (const auto& ds : dsmap) { + if (isSupportedDS(ds)) { + count++; + } + } + + return count; +} + vState SyncRes::getDSRecords(const DNSName& zone, dsmap_t& ds, bool taOnly, unsigned int depth) { vState result = getTA(zone, ds); if (result != Indeterminate || taOnly) { + if (result == Secure && countSupportedDS(ds) == 0) { + ds.clear(); + result = Insecure; + } + return result; } bool oldSkipCNAME = d_skipCNAMECheck; - bool oldValidationRequested = d_validationRequested; bool oldRequireAuthData = d_requireAuthData; d_skipCNAMECheck = true; - d_validationRequested = true; d_requireAuthData = false; std::set beenthere; @@ -1288,7 +1299,6 @@ vState SyncRes::getDSRecords(const DNSName& zone, dsmap_t& ds, bool taOnly, unsi vState state = Indeterminate; int rcode = doResolve(zone, QType(QType::DS), dsrecords, depth, beenthere, state); d_skipCNAMECheck = oldSkipCNAME; - d_validationRequested = oldValidationRequested; d_requireAuthData = oldRequireAuthData; if (rcode == RCode::NoError) { @@ -1296,7 +1306,7 @@ vState SyncRes::getDSRecords(const DNSName& zone, dsmap_t& ds, bool taOnly, unsi for (const auto& record : dsrecords) { if (record.d_type == QType::DS) { const auto dscontent = getRR(record); - if (dscontent) { + if (dscontent && isSupportedDS(*dscontent)) { ds.insert(*dscontent); } } @@ -1322,6 +1332,14 @@ vState SyncRes::getValidationStatus(const DNSName& subdomain, unsigned int depth dsmap_t ds; vState result = getTA(subdomain, ds); if (result != Indeterminate) { + if (result == NTA) { + result = Insecure; + } + else if (result == Secure && countSupportedDS(ds) == 0) { + ds.clear(); + result = Insecure; + } + return result; } @@ -1351,6 +1369,9 @@ vState SyncRes::getValidationStatus(const DNSName& subdomain, unsigned int depth result = getDSRecords(subdomain, ds, false, depth); if (result != Secure) { + if (result == NTA) { + result = Insecure; + } return result; } @@ -1408,6 +1429,9 @@ vState SyncRes::validateDNSKeys(const DNSName& zone, const std::vector s_queries; @@ -737,7 +732,6 @@ private: boost::optional getEDNSSubnetMask(const ComboAddress& local, const DNSName&dn, const ComboAddress& rem); bool validationEnabled() const; - bool validationRequested() const; uint32_t computeLowestTTD(const std::vector& records, const std::vector >& signatures, uint32_t signaturesTTL) const; void updateValidationState(vState& state, const vState stateUpdate); void updateValidationStatusAfterReferral(const DNSName& newauth, vState& state, unsigned int depth); @@ -774,9 +768,7 @@ private: bool d_incomingECSFound{false}; bool d_requireAuthData{true}; bool d_skipCNAMECheck{false}; - bool d_skipValidation{false}; bool d_updatingRootNS{false}; - bool d_validationRequested{false}; bool d_wantsRPZ{true}; bool d_wasOutOfBand{false}; bool d_wasVariable{false}; diff --git a/pdns/validate.cc b/pdns/validate.cc index 1b097e2f8e..f4362f64c2 100644 --- a/pdns/validate.cc +++ b/pdns/validate.cc @@ -152,7 +152,7 @@ static bool checkSignatureWithKey(time_t now, const shared_ptrd_tag<<" was " << (result ? "" : "NOT ")<<"valid"<d_siginception >= now) ? "not yet valid" : "expired")<& anchors, const DNSName& zone, ds bool haveNegativeTrustAnchor(const map& negAnchors, const DNSName& zone, std::string& reason); void validateDNSKeysAgainstDS(time_t now, const DNSName& zone, const dsmap_t& dsmap, const skeyset_t& tkeys, vector >& toSign, const vector >& sigs, skeyset_t& validkeys); dState getDenial(const cspmap_t &validrrsets, const DNSName& qname, const uint16_t qtype); +bool isSupportedDS(const DSRecordContent& ds); diff --git a/pdns/ws-auth.cc b/pdns/ws-auth.cc index 78ddccd3e9..5142fd9b04 100644 --- a/pdns/ws-auth.cc +++ b/pdns/ws-auth.cc @@ -744,7 +744,7 @@ static void apiZoneCryptokeysGET(DNSName zonename, int inquireKeyId, HttpRespons if (value.second.keyType == DNSSECKeeper::KSK || value.second.keyType == DNSSECKeeper::CSK) { Json::array dses; - for(const int keyid : { 1, 2, 3, 4 }) + for(const uint8_t keyid : { DNSSECKeeper::SHA1, DNSSECKeeper::SHA256, DNSSECKeeper::GOST, DNSSECKeeper::SHA384 }) try { dses.push_back(makeDSFromDNSKey(zonename, value.first.getDNSKEY(), keyid).getZoneRepresentation()); } catch (...) {}