Skip to content

Commit

Permalink
Backport of Keytrap to rec-4.8.x
Browse files Browse the repository at this point in the history
  • Loading branch information
omoerbeek committed Feb 6, 2024
1 parent 60519a0 commit a4bc142
Show file tree
Hide file tree
Showing 15 changed files with 1,312 additions and 545 deletions.
23 changes: 23 additions & 0 deletions pdns/pdns_recursor.cc
Original file line number Diff line number Diff line change
Expand Up @@ -551,6 +551,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 " << dc->getRemote() << " during resolve of the custom filter policy '" << appliedPolicy.getName() << "' while resolving '" << dc->d_mdp.d_qname << "' because: " << e.what() << endl,
sr.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 " << dc->getRemote() << " during resolve of the custom filter policy '" << appliedPolicy.getName() << "' while resolving '" << dc->d_mdp.d_qname << "' because another RPZ policy was hit" << endl,
Expand Down Expand Up @@ -1197,6 +1206,13 @@ void startDoResolve(void* p)
}
res = RCode::ServFail;
}
catch (const pdns::validation::TooManySEC3IterationsException& e) {
if (g_logCommonErrors) {
SLOG(g_log << Logger::Notice << "Sending SERVFAIL to " << dc->getRemote() << " during resolve of '" << dc->d_mdp.d_qname << "' because: " << e.what() << endl,
sr.d_slog->error(Logr::Notice, e.what(), "Sending SERVFAIL during resolve"));
}
res = RCode::ServFail;
}
catch (const SendTruncatedAnswerException& e) {
ret.clear();
res = RCode::NoError;
Expand Down Expand Up @@ -1379,6 +1395,13 @@ void startDoResolve(void* p)
sr.d_slog->error(Logr::Notice, e.reason, "Sending SERVFAIL during validation", "exception", Logging::Loggable("ImmediateServFailException")));
goto sendit;
}
catch (const pdns::validation::TooManySEC3IterationsException& e) {
if (g_logCommonErrors) {
SLOG(g_log << Logger::Notice << "Sending SERVFAIL to " << dc->getRemote() << " during validation of '" << dc->d_mdp.d_qname << "|" << QType(dc->d_mdp.d_qtype) << "' because: " << e.what() << endl,
sr.d_slog->error(Logr::Notice, e.what(), "Sending SERVFAIL during validation", "exception", Logging::Loggable("TooManySEC3IterationsException")));
}
goto sendit; // NOLINT(cppcoreguidelines-avoid-goto)
}
}

if (ret.size()) {
Expand Down
25 changes: 19 additions & 6 deletions pdns/recursordist/aggressive_nsec.cc
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
#include "validate.hh"

std::unique_ptr<AggressiveNSECCache> g_aggressiveNSECCache{nullptr};
uint64_t AggressiveNSECCache::s_nsec3DenialProofMaxCost{0};

/* this is defined in syncres.hh and we are not importing that here */
extern std::unique_ptr<MemRecursorCache> g_recCache;
Expand Down Expand Up @@ -514,7 +515,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<RRSIGRecordContent>>& soaSignatures, const DNSName& name, const QType& type, std::vector<DNSRecord>& ret, int& res, bool doDNSSEC)
bool AggressiveNSECCache::getNSEC3Denial(time_t now, std::shared_ptr<LockGuarded<AggressiveNSECCache::ZoneEntry>>& zoneEntry, std::vector<DNSRecord>& soaSet, std::vector<std::shared_ptr<RRSIGRecordContent>>& soaSignatures, const DNSName& name, const QType& type, std::vector<DNSRecord>& ret, int& res, bool doDNSSEC, pdns::validation::ValidationContext& validationContext)
{
DNSName zone;
std::string salt;
Expand All @@ -530,7 +531,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"
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 @@ -577,8 +588,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)) {
LOG("Found closest encloser at " << closestEncloser << " (" << closestHash << ")" << endl);
Expand Down Expand Up @@ -739,7 +752,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)
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)
{
std::shared_ptr<LockGuarded<ZoneEntry>> zoneEntry;
if (type == QType::DS) {
Expand Down Expand Up @@ -779,7 +792,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);
return getNSEC3Denial(now, zoneEntry, soaSet, soaSignatures, name, type, ret, res, doDNSSEC, validationContext);
}

ZoneEntry::CacheEntry entry;
Expand Down
7 changes: 5 additions & 2 deletions pdns/recursordist/aggressive_nsec.hh
Original file line number Diff line number Diff line change
Expand Up @@ -35,17 +35,20 @@ using namespace ::boost::multi_index;
#include "dnsrecords.hh"
#include "lock.hh"
#include "stat_t.hh"
#include "validate.hh"

class AggressiveNSECCache
{
public:
static uint64_t s_nsec3DenialProofMaxCost;

AggressiveNSECCache(uint64_t entries) :
d_maxEntries(entries)
{
}

void insertNSEC(const DNSName& zone, const DNSName& owner, const DNSRecord& record, const std::vector<std::shared_ptr<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);
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);

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

Expand Down Expand Up @@ -132,7 +135,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<RRSIGRecordContent>>& soaSignatures, const DNSName& name, const QType& type, std::vector<DNSRecord>& ret, int& res, bool doDNSSEC);
bool getNSEC3Denial(time_t now, std::shared_ptr<LockGuarded<ZoneEntry>>& zoneEntry, std::vector<DNSRecord>& soaSet, std::vector<std::shared_ptr<RRSIGRecordContent>>& soaSignatures, const DNSName& name, const QType& type, std::vector<DNSRecord>& ret, int& res, bool doDNSSEC, 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);
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);

Expand Down
15 changes: 15 additions & 0 deletions pdns/recursordist/rec-main.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1436,6 +1436,10 @@ static int serviceMain(int argc, char* argv[], 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");

g_maxCacheEntries = ::arg().asNum("max-cache-entries");
g_maxPacketCacheEntries = ::arg().asNum("max-packetcache-entries");
Expand Down Expand Up @@ -1522,6 +1526,8 @@ static int serviceMain(int argc, char* argv[], 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 @@ -1701,6 +1707,7 @@ static int serviceMain(int argc, char* argv[], Logr::log_t log)

g_addExtendedResolutionDNSErrors = ::arg().mustDo("extended-resolution-errors");

AggressiveNSECCache::s_nsec3DenialProofMaxCost = ::arg().asNum("aggressive-cache-max-nsec3-hash-cost");
if (::arg().asNum("aggressive-nsec-cache-size") > 0) {
if (g_dnssecmode == DNSSECMode::ValidateAll || g_dnssecmode == DNSSECMode::ValidateForLog || g_dnssecmode == DNSSECMode::Process) {
g_aggressiveNSECCache = make_unique<AggressiveNSECCache>(::arg().asNum("aggressive-nsec-cache-size"));
Expand Down Expand Up @@ -2773,6 +2780,14 @@ int main(int argc, char** argv)
::arg().set("tcp-fast-open-connect", "Enable TCP Fast Open support on outgoing sockets") = "no";
::arg().set("nsec3-max-iterations", "Maximum number of iterations allowed for an NSEC3 record") = "150";

::arg().set("max-rrsigs-per-record", "Maximum number of RRSIGs to consider when validating a given record") = "2";
::arg().set("max-nsec3s-per-record", "Maximum number of NSEC3s to consider when validating a given denial of existence") = "10";
::arg().set("max-signature-validations-per-query", "Maximum number of RRSIG signatures we are willing to validate per incoming query") = "30";
::arg().set("max-nsec3-hash-computations-per-query", "Maximum number of NSEC3 hashes that we are willing to compute during DNSSEC validation, per incoming query") = "600";
::arg().set("aggressive-cache-max-nsec3-hash-cost", "Maximum estimated NSEC3 cost for a given query to consider aggressive use of the NSEC3 cache") = "150";
::arg().set("max-dnskeys", "Maximum number of DNSKEYs with the same algorithm and tag to consider when validating a given record") = "2";
::arg().set("max-ds-per-zone", "Maximum number of DS records to consider per zone") = "8";

::arg().set("cpu-map", "Thread to CPU mapping, space separated thread-id=cpu1,cpu2..cpuN pairs") = "";

::arg().setSwitch("log-rpz-changes", "Log additions and removals to RPZ zones at Info level") = "no";
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 @@ -243,6 +243,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 sr({d_now, 0});
Expand All @@ -266,7 +268,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);
vState dnsKeyState = validateDNSKeysAgainstDS(d_now, d_zone, dsmap, dnsKeys, records, zonemd.getRRSIGs(), validKeys, validationContext);
if (dnsKeyState != vState::Secure) {
return dnsKeyState;
}
Expand All @@ -288,7 +290,7 @@ vState ZoneData::dnssecValidate(pdns::ZoneMD& zonemd, size_t& zonemdCount) const

if (nsecs.records.size() > 0 && nsecs.signatures.size() > 0) {
// Valdidate the NSEC
nsecValidationStatus = validateWithKeySet(d_now, d_zone, nsecs.records, nsecs.signatures, validKeys);
nsecValidationStatus = validateWithKeySet(d_now, d_zone, nsecs.records, nsecs.signatures, validKeys, validationContext);
csp.emplace(std::make_pair(d_zone, QType::NSEC), nsecs);
}
else if (nsec3s.records.size() > 0 && nsec3s.signatures.size() > 0) {
Expand All @@ -297,13 +299,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);
nsecValidationStatus = validateWithKeySet(d_now, d_zone, records, zonemd.getRRSIGs(), validKeys, validationContext);
if (nsecValidationStatus != vState::Secure) {
d_log->info("NSEC3PARAMS records did not validate");
return nsecValidationStatus;
}
// Valdidate the NSEC3
nsecValidationStatus = validateWithKeySet(d_now, zonemd.getNSEC3Label(), nsec3s.records, nsec3s.signatures, validKeys);
nsecValidationStatus = validateWithKeySet(d_now, zonemd.getNSEC3Label(), nsec3s.records, nsec3s.signatures, validKeys, validationContext);
csp.emplace(std::make_pair(zonemd.getNSEC3Label(), QType::NSEC3), nsec3s);
}
else {
Expand All @@ -316,7 +318,7 @@ vState ZoneData::dnssecValidate(pdns::ZoneMD& zonemd, size_t& zonemdCount) const
return nsecValidationStatus;
}

auto denial = getDenial(csp, d_zone, QType::ZONEMD, false, false, true);
auto denial = getDenial(csp, d_zone, QType::ZONEMD, false, false, validationContext, true);
if (denial == dState::NXQTYPE) {
d_log->info("Validated denial of absence of ZONEMD record");
return vState::Secure;
Expand All @@ -330,7 +332,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);
return validateWithKeySet(d_now, d_zone, records, zonemd.getRRSIGs(), validKeys, validationContext);
}

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

0 comments on commit a4bc142

Please sign in to comment.