Skip to content

Commit

Permalink
Merge 8cb2740 into cf1d041
Browse files Browse the repository at this point in the history
  • Loading branch information
omoerbeek committed Feb 13, 2024
2 parents cf1d041 + 8cb2740 commit 5dc6c75
Show file tree
Hide file tree
Showing 16 changed files with 1,213 additions and 190 deletions.
29 changes: 21 additions & 8 deletions pdns/recursordist/aggressive_nsec.cc
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
#include "validate.hh"

std::unique_ptr<AggressiveNSECCache> g_aggressiveNSECCache{nullptr};
uint64_t AggressiveNSECCache::s_nsec3DenialProofMaxCost{0};
uint8_t AggressiveNSECCache::s_maxNSEC3CommonPrefix = AggressiveNSECCache::s_default_maxNSEC3CommonPrefix;

/* this is defined in syncres.hh and we are not importing that here */
Expand Down Expand Up @@ -539,7 +540,7 @@ bool AggressiveNSECCache::synthesizeFromNSECWildcard(time_t now, const DNSName&
return true;
}

bool AggressiveNSECCache::getNSEC3Denial(time_t now, std::shared_ptr<LockGuarded<AggressiveNSECCache::ZoneEntry>>& zoneEntry, std::vector<DNSRecord>& soaSet, std::vector<std::shared_ptr<const RRSIGRecordContent>>& soaSignatures, const DNSName& name, const QType& type, std::vector<DNSRecord>& ret, int& res, bool doDNSSEC, const OptLog& log)
bool AggressiveNSECCache::getNSEC3Denial(time_t now, std::shared_ptr<LockGuarded<AggressiveNSECCache::ZoneEntry>>& zoneEntry, std::vector<DNSRecord>& soaSet, std::vector<std::shared_ptr<const RRSIGRecordContent>>& soaSignatures, const DNSName& name, const QType& type, std::vector<DNSRecord>& ret, int& res, bool doDNSSEC, const OptLog& log, pdns::validation::ValidationContext& validationContext)
{
DNSName zone;
std::string salt;
Expand All @@ -555,7 +556,17 @@ bool AggressiveNSECCache::getNSEC3Denial(time_t now, std::shared_ptr<LockGuarded
iterations = entry->d_iterations;
}

auto nameHash = DNSName(toBase32Hex(hashQNameWithSalt(salt, iterations, name))) + zone;
const auto zoneLabelsCount = zone.countLabels();
if (s_nsec3DenialProofMaxCost != 0) {
const auto worstCaseIterations = getNSEC3DenialProofWorstCaseIterationsCount(name.countLabels() - zoneLabelsCount, iterations, salt.length());
if (worstCaseIterations > s_nsec3DenialProofMaxCost) {
// skip NSEC3 aggressive cache for expensive NSEC3 parameters: "if you want us to take the pain of PRSD away from you, you need to make it cheap for us to do so"
VLOG(log, name << ": Skipping aggressive use of the NSEC3 cache since the zone parameters are too expensive" << endl);
return false;
}
}

auto nameHash = DNSName(toBase32Hex(getHashFromNSEC3(name, iterations, salt, validationContext))) + zone;

ZoneEntry::CacheEntry exactNSEC3;
if (getNSEC3(now, zoneEntry, nameHash, exactNSEC3)) {
Expand Down Expand Up @@ -602,8 +613,10 @@ bool AggressiveNSECCache::getNSEC3Denial(time_t now, std::shared_ptr<LockGuarded
DNSName closestEncloser(name);
bool found = false;
ZoneEntry::CacheEntry closestNSEC3;
while (!found && closestEncloser.chopOff()) {
auto closestHash = DNSName(toBase32Hex(hashQNameWithSalt(salt, iterations, closestEncloser))) + zone;
auto remainingLabels = closestEncloser.countLabels() - 1;
while (!found && closestEncloser.chopOff() && remainingLabels >= zoneLabelsCount) {
auto closestHash = DNSName(toBase32Hex(getHashFromNSEC3(closestEncloser, iterations, salt, validationContext))) + zone;
remainingLabels--;

if (getNSEC3(now, zoneEntry, closestHash, closestNSEC3)) {
VLOG(log, name << ": Found closest encloser at " << closestEncloser << " (" << closestHash << ")" << endl);
Expand Down Expand Up @@ -651,7 +664,7 @@ bool AggressiveNSECCache::getNSEC3Denial(time_t now, std::shared_ptr<LockGuarded
DNSName nsecFound;
DNSName nextCloser(closestEncloser);
nextCloser.prependRawLabel(name.getRawLabel(labelIdx - 1));
auto nextCloserHash = toBase32Hex(hashQNameWithSalt(salt, iterations, nextCloser));
auto nextCloserHash = toBase32Hex(getHashFromNSEC3(nextCloser, iterations, salt, validationContext));
VLOG(log, name << ": Looking for a NSEC3 covering the next closer " << nextCloser << " (" << nextCloserHash << ")" << endl);

ZoneEntry::CacheEntry nextCloserEntry;
Expand Down Expand Up @@ -680,7 +693,7 @@ bool AggressiveNSECCache::getNSEC3Denial(time_t now, std::shared_ptr<LockGuarded
/* An ancestor NSEC3 would be fine here, since it does prove that there is no delegation at the next closer
name (we don't insert opt-out NSEC3s into the cache). */
DNSName wildcard(g_wildcarddnsname + closestEncloser);
auto wcHash = toBase32Hex(hashQNameWithSalt(salt, iterations, wildcard));
auto wcHash = toBase32Hex(getHashFromNSEC3(wildcard, iterations, salt, validationContext));
VLOG(log, name << ": Looking for a NSEC3 covering the wildcard " << wildcard << " (" << wcHash << ")" << endl);

ZoneEntry::CacheEntry wcEntry;
Expand Down Expand Up @@ -764,7 +777,7 @@ bool AggressiveNSECCache::getNSEC3Denial(time_t now, std::shared_ptr<LockGuarded
return true;
}

bool AggressiveNSECCache::getDenial(time_t now, const DNSName& name, const QType& type, std::vector<DNSRecord>& ret, int& res, const ComboAddress& who, const boost::optional<std::string>& routingTag, bool doDNSSEC, const OptLog& log)
bool AggressiveNSECCache::getDenial(time_t now, const DNSName& name, const QType& type, std::vector<DNSRecord>& ret, int& res, const ComboAddress& who, const boost::optional<std::string>& routingTag, bool doDNSSEC, pdns::validation::ValidationContext& validationContext, const OptLog& log)
{
std::shared_ptr<LockGuarded<ZoneEntry>> zoneEntry;
if (type == QType::DS) {
Expand Down Expand Up @@ -804,7 +817,7 @@ bool AggressiveNSECCache::getDenial(time_t now, const DNSName& name, const QType
}

if (nsec3) {
return getNSEC3Denial(now, zoneEntry, soaSet, soaSignatures, name, type, ret, res, doDNSSEC, log);
return getNSEC3Denial(now, zoneEntry, soaSet, soaSignatures, name, type, ret, res, doDNSSEC, log, validationContext);
}

ZoneEntry::CacheEntry entry;
Expand Down
8 changes: 5 additions & 3 deletions pdns/recursordist/aggressive_nsec.hh
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,13 @@ using namespace ::boost::multi_index;
#include "lock.hh"
#include "stat_t.hh"
#include "logger.hh"
#include "validate.hh"

class AggressiveNSECCache
{
public:
static const uint8_t s_default_maxNSEC3CommonPrefix = 10;
static constexpr uint8_t s_default_maxNSEC3CommonPrefix = 10;
static uint64_t s_nsec3DenialProofMaxCost;
static uint8_t s_maxNSEC3CommonPrefix;

AggressiveNSECCache(uint64_t entries) :
Expand All @@ -54,7 +56,7 @@ public:
}

void insertNSEC(const DNSName& zone, const DNSName& owner, const DNSRecord& record, const std::vector<std::shared_ptr<const RRSIGRecordContent>>& signatures, bool nsec3);
bool getDenial(time_t, const DNSName& name, const QType& type, std::vector<DNSRecord>& ret, int& res, const ComboAddress& who, const boost::optional<std::string>& routingTag, bool doDNSSEC, const OptLog& log = std::nullopt);
bool getDenial(time_t, const DNSName& name, const QType& type, std::vector<DNSRecord>& ret, int& res, const ComboAddress& who, const boost::optional<std::string>& routingTag, bool doDNSSEC, pdns::validation::ValidationContext& validationContext, const OptLog& log = std::nullopt);

void removeZoneInfo(const DNSName& zone, bool subzones);

Expand Down Expand Up @@ -144,7 +146,7 @@ private:
std::shared_ptr<LockGuarded<ZoneEntry>> getBestZone(const DNSName& zone);
bool getNSECBefore(time_t now, std::shared_ptr<LockGuarded<ZoneEntry>>& zoneEntry, const DNSName& name, ZoneEntry::CacheEntry& entry);
bool getNSEC3(time_t now, std::shared_ptr<LockGuarded<ZoneEntry>>& zoneEntry, const DNSName& name, ZoneEntry::CacheEntry& entry);
bool getNSEC3Denial(time_t now, std::shared_ptr<LockGuarded<ZoneEntry>>& zoneEntry, std::vector<DNSRecord>& soaSet, std::vector<std::shared_ptr<const RRSIGRecordContent>>& soaSignatures, const DNSName& name, const QType& type, std::vector<DNSRecord>& ret, int& res, bool doDNSSEC, const OptLog&);
bool getNSEC3Denial(time_t now, std::shared_ptr<LockGuarded<ZoneEntry>>& zoneEntry, std::vector<DNSRecord>& soaSet, std::vector<std::shared_ptr<const RRSIGRecordContent>>& soaSignatures, const DNSName& name, const QType& type, std::vector<DNSRecord>& ret, int& res, bool doDNSSEC, const OptLog&, pdns::validation::ValidationContext& validationContext);
bool synthesizeFromNSEC3Wildcard(time_t now, const DNSName& name, const QType& type, std::vector<DNSRecord>& ret, int& res, bool doDNSSEC, ZoneEntry::CacheEntry& nextCloser, const DNSName& wildcardName, const OptLog&);
bool synthesizeFromNSECWildcard(time_t now, const DNSName& name, const QType& type, std::vector<DNSRecord>& ret, int& res, bool doDNSSEC, ZoneEntry::CacheEntry& nsec, const DNSName& wildcardName, const OptLog&);

Expand Down
23 changes: 23 additions & 0 deletions pdns/recursordist/pdns_recursor.cc
Original file line number Diff line number Diff line change
Expand Up @@ -568,6 +568,15 @@ static PolicyResult handlePolicyHit(const DNSFilterEngine::Policy& appliedPolicy
res = RCode::ServFail;
break;
}
catch (const pdns::validation::TooManySEC3IterationsException& e) {
if (g_logCommonErrors) {
SLOG(g_log << Logger::Notice << "Sending SERVFAIL to " << comboWriter->getRemote() << " during resolve of the custom filter policy '" << appliedPolicy.getName() << "' while resolving '" << comboWriter->d_mdp.d_qname << "' because: " << e.what() << endl,
resolver.d_slog->error(Logr::Notice, e.what(), "Sending SERVFAIL during resolve of the custom filter policy",
"policyName", Logging::Loggable(appliedPolicy.getName()), "exception", Logging::Loggable("TooManySEC3IterationsException")));
}
res = RCode::ServFail;
break;
}
catch (const PolicyHitException& e) {
if (g_logCommonErrors) {
SLOG(g_log << Logger::Notice << "Sending SERVFAIL to " << comboWriter->getRemote() << " during resolve of the custom filter policy '" << appliedPolicy.getName() << "' while resolving '" << comboWriter->d_mdp.d_qname << "' because another RPZ policy was hit" << endl,
Expand Down Expand Up @@ -1270,6 +1279,13 @@ void startDoResolve(void* arg) // NOLINT(readability-function-cognitive-complexi
}
res = RCode::ServFail;
}
catch (const pdns::validation::TooManySEC3IterationsException& e) {
if (g_logCommonErrors) {
SLOG(g_log << Logger::Notice << "Sending SERVFAIL to " << comboWriter->getRemote() << " during resolve of '" << comboWriter->d_mdp.d_qname << "' because: " << e.what() << endl,
resolver.d_slog->error(Logr::Notice, e.what(), "Sending SERVFAIL during resolve"));
}
res = RCode::ServFail;
}
catch (const SendTruncatedAnswerException& e) {
ret.clear();
resolver.d_appliedPolicy.addSOAtoRPZResult(ret);
Expand Down Expand Up @@ -1449,6 +1465,13 @@ void startDoResolve(void* arg) // NOLINT(readability-function-cognitive-complexi
}
goto sendit; // NOLINT(cppcoreguidelines-avoid-goto)
}
catch (const pdns::validation::TooManySEC3IterationsException& e) {
if (g_logCommonErrors) {
SLOG(g_log << Logger::Notice << "Sending SERVFAIL to " << comboWriter->getRemote() << " during validation of '" << comboWriter->d_mdp.d_qname << "|" << QType(comboWriter->d_mdp.d_qtype) << "' because: " << e.what() << endl,
resolver.d_slog->error(Logr::Notice, e.what(), "Sending SERVFAIL during validation", "exception", Logging::Loggable("TooManySEC3IterationsException")));
}
goto sendit; // NOLINT(cppcoreguidelines-avoid-goto)
}
}

if (!ret.empty()) {
Expand Down
7 changes: 7 additions & 0 deletions pdns/recursordist/rec-main.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1563,6 +1563,10 @@ static int initDNSSEC(Logr::log_t log)

g_dnssecLogBogus = ::arg().mustDo("dnssec-log-bogus");
g_maxNSEC3Iterations = ::arg().asNum("nsec3-max-iterations");
g_maxRRSIGsPerRecordToConsider = ::arg().asNum("max-rrsigs-per-record");
g_maxNSEC3sPerRecordToConsider = ::arg().asNum("max-nsec3s-per-record");
g_maxDNSKEYsToConsider = ::arg().asNum("max-dnskeys");
g_maxDSsToConsider = ::arg().asNum("max-ds-per-zone");

vector<string> nums;
bool automatic = true;
Expand Down Expand Up @@ -1652,6 +1656,8 @@ static int initSyncRes(Logr::log_t log)
SyncRes::s_maxnsaddressqperq = ::arg().asNum("max-ns-address-qperq");
SyncRes::s_maxtotusec = 1000 * ::arg().asNum("max-total-msec");
SyncRes::s_maxdepth = ::arg().asNum("max-recursion-depth");
SyncRes::s_maxvalidationsperq = ::arg().asNum("max-signature-validations-per-query");
SyncRes::s_maxnsec3iterationsperq = ::arg().asNum("max-nsec3-hash-computations-per-query");
SyncRes::s_rootNXTrust = ::arg().mustDo("root-nx-trust");
SyncRes::s_refresh_ttlperc = ::arg().asNum("refresh-on-ttl-perc");
SyncRes::s_locked_ttlperc = ::arg().asNum("record-cache-locked-ttl-perc");
Expand Down Expand Up @@ -2168,6 +2174,7 @@ static int serviceMain(Logr::log_t log)
}
}

AggressiveNSECCache::s_nsec3DenialProofMaxCost = ::arg().asNum("aggressive-cache-max-nsec3-hash-cost");
AggressiveNSECCache::s_maxNSEC3CommonPrefix = static_cast<uint8_t>(std::round(std::log2(::arg().asNum("aggressive-cache-min-nsec3-hit-ratio"))));
SLOG(g_log << Logger::Debug << "NSEC3 aggressive cache tuning: aggressive-cache-min-nsec3-hit-ratio: " << ::arg().asNum("aggressive-cache-min-nsec3-hit-ratio") << " max common prefix bits: " << std::to_string(AggressiveNSECCache::s_maxNSEC3CommonPrefix) << endl,
log->info(Logr::Debug, "NSEC3 aggressive cache tuning", "aggressive-cache-min-nsec3-hit-ratio", Logging::Loggable(::arg().asNum("aggressive-cache-min-nsec3-hit-ratio")), "maxCommonPrefixBits", Logging::Loggable(AggressiveNSECCache::s_maxNSEC3CommonPrefix)));
Expand Down
14 changes: 8 additions & 6 deletions pdns/recursordist/rec-zonetocache.cc
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,8 @@ pdns::ZoneMD::Result ZoneData::processLines(const vector<string>& lines, const R

vState ZoneData::dnssecValidate(pdns::ZoneMD& zonemd, size_t& zonemdCount) const
{
pdns::validation::ValidationContext validationContext;
validationContext.d_nsec3IterationsRemainingQuota = std::numeric_limits<decltype(validationContext.d_nsec3IterationsRemainingQuota)>::max();
zonemdCount = 0;

SyncRes resolver({d_now, 0});
Expand All @@ -272,7 +274,7 @@ vState ZoneData::dnssecValidate(pdns::ZoneMD& zonemd, size_t& zonemdCount) const
}

skeyset_t validKeys;
vState dnsKeyState = validateDNSKeysAgainstDS(d_now, d_zone, dsmap, dnsKeys, records, zonemd.getRRSIGs(), validKeys, std::nullopt);
vState dnsKeyState = validateDNSKeysAgainstDS(d_now, d_zone, dsmap, dnsKeys, records, zonemd.getRRSIGs(), validKeys, std::nullopt, validationContext);
if (dnsKeyState != vState::Secure) {
return dnsKeyState;
}
Expand All @@ -294,7 +296,7 @@ vState ZoneData::dnssecValidate(pdns::ZoneMD& zonemd, size_t& zonemdCount) const

if (!nsecs.records.empty() && !nsecs.signatures.empty()) {
// Valdidate the NSEC
nsecValidationStatus = validateWithKeySet(d_now, d_zone, nsecs.records, nsecs.signatures, validKeys, std::nullopt);
nsecValidationStatus = validateWithKeySet(d_now, d_zone, nsecs.records, nsecs.signatures, validKeys, std::nullopt, validationContext);
csp.emplace(std::pair(d_zone, QType::NSEC), nsecs);
}
else if (!nsec3s.records.empty() && !nsec3s.signatures.empty()) {
Expand All @@ -303,13 +305,13 @@ vState ZoneData::dnssecValidate(pdns::ZoneMD& zonemd, size_t& zonemdCount) const
for (const auto& rec : zonemd.getNSEC3Params()) {
records.emplace(rec);
}
nsecValidationStatus = validateWithKeySet(d_now, d_zone, records, zonemd.getRRSIGs(), validKeys, std::nullopt);
nsecValidationStatus = validateWithKeySet(d_now, d_zone, records, zonemd.getRRSIGs(), validKeys, std::nullopt, validationContext);
if (nsecValidationStatus != vState::Secure) {
d_log->info(Logr::Warning, "NSEC3PARAMS records did not validate");
return nsecValidationStatus;
}
// Valdidate the NSEC3
nsecValidationStatus = validateWithKeySet(d_now, zonemd.getNSEC3Label(), nsec3s.records, nsec3s.signatures, validKeys, std::nullopt);
nsecValidationStatus = validateWithKeySet(d_now, zonemd.getNSEC3Label(), nsec3s.records, nsec3s.signatures, validKeys, std::nullopt, validationContext);
csp.emplace(std::pair(zonemd.getNSEC3Label(), QType::NSEC3), nsec3s);
}
else {
Expand All @@ -322,7 +324,7 @@ vState ZoneData::dnssecValidate(pdns::ZoneMD& zonemd, size_t& zonemdCount) const
return nsecValidationStatus;
}

auto denial = getDenial(csp, d_zone, QType::ZONEMD, false, false, std::nullopt, true);
auto denial = getDenial(csp, d_zone, QType::ZONEMD, false, false, validationContext, std::nullopt, true);
if (denial == dState::NXQTYPE) {
d_log->info(Logr::Info, "Validated denial of existence of ZONEMD record");
return vState::Secure;
Expand All @@ -336,7 +338,7 @@ vState ZoneData::dnssecValidate(pdns::ZoneMD& zonemd, size_t& zonemdCount) const
for (const auto& rec : zonemdRecords) {
records.emplace(rec);
}
return validateWithKeySet(d_now, d_zone, records, zonemd.getRRSIGs(), validKeys, std::nullopt);
return validateWithKeySet(d_now, d_zone, records, zonemd.getRRSIGs(), validKeys, std::nullopt, validationContext);
}

void ZoneData::ZoneToCache(const RecZoneToCache::Config& config)
Expand Down
Loading

0 comments on commit 5dc6c75

Please sign in to comment.