From: Remi Gacogne Date: Fri, 20 Oct 2017 13:42:07 +0000 (+0200) Subject: rec: Validate entries retrieved from the negcache if needed X-Git-Tag: rec-4.1.0-rc2~23^2~1 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=142ab370d6bc0bea5502bebf4f13a538a715dc68;p=pdns rec: Validate entries retrieved from the negcache if needed This happens if validation was not requested during the first query but is requested when we retrieve a negatively cached entry. This is useful when running with dnssec=process, and also especially so now that we don't validate infra queries anymore. --- diff --git a/pdns/recursordist/negcache.cc b/pdns/recursordist/negcache.cc index a9f306b7c..9df2ece3c 100644 --- a/pdns/recursordist/negcache.cc +++ b/pdns/recursordist/negcache.cc @@ -100,6 +100,21 @@ void NegCache::add(const NegCacheEntry& ne) { replacing_insert(d_negcache, ne); } +/*! + * Update the validation state of an existing entry with the provided state. + * + * \param qname The name of the entry to replace + * \param qtype The type of the entry to replace + * \param newState The new validation state + */ +void NegCache::updateValidationStatus(const DNSName& qname, const QType& qtype, const vState newState) { + auto range = d_negcache.equal_range(tie(qname, qtype)); + + if (range.first != range.second) { + range.first->d_validationState = newState; + } +} + /*! * Returns the amount of entries in the cache * diff --git a/pdns/recursordist/negcache.hh b/pdns/recursordist/negcache.hh index 336dc853c..d1a742da3 100644 --- a/pdns/recursordist/negcache.hh +++ b/pdns/recursordist/negcache.hh @@ -51,13 +51,14 @@ class NegCache : public boost::noncopyable { uint32_t d_ttd; // Timestamp when this entry should die recordsAndSignatures authoritySOA; // The upstream SOA record and RRSIGs recordsAndSignatures DNSSECRecords; // The upstream NSEC(3) and RRSIGs - vState d_validationState{Indeterminate}; + mutable vState d_validationState{Indeterminate}; uint32_t getTTD() const { return d_ttd; }; }; void add(const NegCacheEntry& ne); + void updateValidationStatus(const DNSName& qname, const QType& qtype, const vState newState); bool get(const DNSName& qname, const QType& qtype, const struct timeval& now, NegCacheEntry& ne, bool typeMustMatch=false); bool getRootNXTrust(const DNSName& qname, const struct timeval& now, NegCacheEntry& ne); uint64_t count(const DNSName& qname) const; diff --git a/pdns/syncres.cc b/pdns/syncres.cc index 6f7f919e9..a1c1f3c4d 100644 --- a/pdns/syncres.cc +++ b/pdns/syncres.cc @@ -845,7 +845,7 @@ bool SyncRes::doCNAMECacheCheck(const DNSName &qname, const QType &qtype, vector /* make sure they are computed before validating */ computeZoneCuts(qname, g_rootdnsname, depth); - vState recordState = getValidationStatus(qname); + vState recordState = getValidationStatus(qname, false); if (recordState == Secure) { LOG(prefix< records; + vector> signatures; + uint32_t signaturesTTL{std::numeric_limits::max()}; +}; +struct CacheKey +{ + DNSName name; + uint16_t type; + DNSResourceRecord::Place place; + bool operator<(const CacheKey& rhs) const { + return tie(name, type) < tie(rhs.name, rhs.type); + } +}; +typedef map tcache_t; + +static void reapRecordsFromNegCacheEntryForValidation(tcache_t& tcache, const vector& records) +{ + for (const auto& rec : records) { + if (rec.d_type == QType::RRSIG) { + auto rrsig = getRR(rec); + if (rrsig) { + tcache[{rec.d_name, rrsig->d_type, rec.d_place}].signatures.push_back(rrsig); + } + } else { + tcache[{rec.d_name,rec.d_type,rec.d_place}].records.push_back(rec); + } + } +} + /*! * Convience function to push the records from records into ret with a new TTL * @@ -913,6 +944,51 @@ static void addTTLModifiedRecords(const vector& records, const uint32 } } +void SyncRes::computeNegCacheValidationStatus(NegCache::NegCacheEntry& ne, const DNSName& qname, const QType& qtype, const int res, vState& state, unsigned int depth) +{ + computeZoneCuts(qname, g_rootdnsname, depth); + + tcache_t tcache; + reapRecordsFromNegCacheEntryForValidation(tcache, ne.authoritySOA.records); + reapRecordsFromNegCacheEntryForValidation(tcache, ne.authoritySOA.signatures); + reapRecordsFromNegCacheEntryForValidation(tcache, ne.DNSSECRecords.records); + reapRecordsFromNegCacheEntryForValidation(tcache, ne.DNSSECRecords.signatures); + + for (const auto& entry : tcache) { + // this happens when we did store signatures, but passed on the records themselves + if (entry.second.records.empty()) { + continue; + } + + const DNSName& owner = entry.first.name; + + vState recordState = getValidationStatus(owner, false); + if (state == Indeterminate) { + state = recordState; + } + + if (recordState == Secure) { + vState cachedState = SyncRes::validateRecordsWithSigs(depth, qname, qtype, owner, entry.second.records, entry.second.signatures); + + if (state == Secure && cachedState != recordState) { + updateValidationState(state, cachedState); + if (state != Secure) { + break; + } + } + } + } + + if (state == Secure) { + dState expectedState = res == RCode::NXDomain ? NXDOMAIN : NXQTYPE; + dState denialState = getDenialValidationState(ne, state, expectedState, qtype == QType::DS); + updateDenialValidationState(ne, state, denialState, expectedState, qtype == QType::DS); + } + if (state != Indeterminate) { + /* validation succeeded, let's update the cache entry so we don't have to validate again */ + t_sstorage.negcache.updateValidationStatus(ne.d_name, ne.d_qtype, state); + } +} bool SyncRes::doCacheCheck(const DNSName &qname, const QType &qtype, vector&ret, unsigned int depth, int &res, vState& state) { @@ -978,6 +1054,14 @@ bool SyncRes::doCacheCheck(const DNSName &qname, const QType &qtype, vector beenthere; std::vector dsrecords; @@ -1423,7 +1504,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 + 1, beenthere, state); d_skipCNAMECheck = oldSkipCNAME; - d_requireAuthData = oldRequireAuthData; if (rcode == RCode::NoError || (rcode == RCode::NXDomain && !bogusOnNXD)) { @@ -1719,22 +1799,6 @@ vState SyncRes::validateRecordsWithSigs(unsigned int depth, const DNSName& qname RCode::rcodes_ SyncRes::updateCacheFromRecords(unsigned int depth, LWResult& lwr, const DNSName& qname, const QType& qtype, const DNSName& auth, bool wasForwarded, const boost::optional ednsmask, vState& state, bool& needWildcardProof) { - struct CacheEntry - { - vector records; - vector> signatures; - uint32_t signaturesTTL{std::numeric_limits::max()}; - }; - struct CacheKey - { - DNSName name; - uint16_t type; - DNSResourceRecord::Place place; - bool operator<(const CacheKey& rhs) const { - return tie(name, type) < tie(rhs.name, rhs.type); - } - }; - typedef map tcache_t; tcache_t tcache; string prefix; @@ -1744,6 +1808,7 @@ RCode::rcodes_ SyncRes::updateCacheFromRecords(unsigned int depth, LWResult& lwr } std::vector> authorityRecs; + const unsigned int labelCount = qname.countLabels(); bool isCNAMEAnswer = false; for(const auto& rec : lwr.d_records) { if(!isCNAMEAnswer && rec.d_place == DNSResourceRecord::ANSWER && rec.d_type == QType::CNAME && (!(qtype==QType(QType::CNAME))) && rec.d_name == qname) { @@ -1764,7 +1829,6 @@ RCode::rcodes_ SyncRes::updateCacheFromRecords(unsigned int depth, LWResult& lwr if(rec.d_type == QType::RRSIG) { auto rrsig = getRR(rec); if (rrsig) { - unsigned int labelCount = rec.d_name.countLabels(); /* As illustrated in rfc4035's Appendix B.6, the RRSIG label count can be lower than the name's label count if it was synthesized from the wildcard. Note that the difference might @@ -1924,7 +1988,7 @@ RCode::rcodes_ SyncRes::updateCacheFromRecords(unsigned int depth, LWResult& lwr - denial of existence proofs for negative responses are stored in the negative cache */ if (i->first.type != QType::NSEC3) { - t_RC->replace(d_now.tv_sec, i->first.name, QType(i->first.type), i->second.records, i->second.signatures, authorityRecs, isAA, i->first.place == DNSResourceRecord::ANSWER ? ednsmask : boost::none, recordState); + t_RC->replace(d_now.tv_sec, i->first.name, QType(i->first.type), i->second.records, i->second.signatures, authorityRecs, i->first.type == QType::DS ? true : isAA, i->first.place == DNSResourceRecord::ANSWER ? ednsmask : boost::none, recordState); } if(i->first.place == DNSResourceRecord::ANSWER && ednsmask) @@ -1934,32 +1998,32 @@ RCode::rcodes_ SyncRes::updateCacheFromRecords(unsigned int depth, LWResult& lwr return RCode::NoError; } -void SyncRes::getDenialValidationState(NegCache::NegCacheEntry& ne, vState& state, const dState expectedState, bool allowOptOut, bool referralToUnsigned) +void SyncRes::updateDenialValidationState(NegCache::NegCacheEntry& ne, vState& state, const dState denialState, const dState expectedState, bool allowOptOut) { - ne.d_validationState = state; - - if (state == Secure) { - cspmap_t csp = harvestCSPFromNE(ne); - dState res = getDenial(csp, ne.d_name, ne.d_qtype.getCode(), referralToUnsigned, expectedState == NXQTYPE); - if (res != expectedState) { - if (res == OPTOUT && allowOptOut) { - LOG(d_prefix<<"OPT-out denial found for "<second == Indeterminate) { - cut->second = Insecure; - } - } - else { - LOG(prefix<& dnskeys, const std::vector >& signatures, unsigned int depth); vState getDSRecords(const DNSName& zone, dsmap_t& ds, bool onlyTA, unsigned int depth, bool bogusOnNXD=true, bool* foundCut=nullptr); vState getDNSKeys(const DNSName& signer, skeyset_t& keys, unsigned int depth); - void getDenialValidationState(NegCache::NegCacheEntry& ne, vState& state, const dState expectedState, bool allowOptOut, bool referralToUnsigned); + dState getDenialValidationState(NegCache::NegCacheEntry& ne, const vState state, const dState expectedState, bool referralToUnsigned); + void updateDenialValidationState(NegCache::NegCacheEntry& ne, vState& state, const dState denialState, const dState expectedState, bool allowOptOut); + void computeNegCacheValidationStatus(NegCache::NegCacheEntry& ne, const DNSName& qname, const QType& qtype, const int res, vState& state, unsigned int depth); vState getTA(const DNSName& zone, dsmap_t& ds); bool haveExactValidationStatus(const DNSName& domain); vState getValidationStatus(const DNSName& subdomain, bool allowIndeterminate=true);