From: Otto Moerbeek Date: Fri, 14 Jan 2022 13:46:29 +0000 (+0100) Subject: Implement ZONEMD when processing zone-to-cache via AXFR X-Git-Tag: auth-4.7.0-alpha1~42^2~19 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=5ecf9d92ace0ad390e54bc17ed62f2ca4e0acafe;p=thirdparty%2Fpdns.git Implement ZONEMD when processing zone-to-cache via AXFR --- diff --git a/pdns/recursordist/Makefile.am b/pdns/recursordist/Makefile.am index 776bf3ffe6..f35c9374fd 100644 --- a/pdns/recursordist/Makefile.am +++ b/pdns/recursordist/Makefile.am @@ -206,6 +206,7 @@ pdns_recursor_SOURCES = \ ws-api.cc ws-api.hh \ ws-recursor.cc ws-recursor.hh \ xpf.cc xpf.hh \ + zonemd.cc zonemd.hh \ zoneparser-tng.cc zoneparser-tng.hh if !HAVE_LUA_HPP @@ -346,6 +347,7 @@ testrunner_SOURCES = \ validate-recursor.cc validate-recursor.hh \ validate.cc validate.hh \ xpf.cc xpf.hh \ + zonemd.cc zonemd.hh \ zoneparser-tng.cc zoneparser-tng.hh testrunner_LDFLAGS = \ diff --git a/pdns/recursordist/rec-zonetocache.cc b/pdns/recursordist/rec-zonetocache.cc index fa3894bb9c..b85a1d0e47 100644 --- a/pdns/recursordist/rec-zonetocache.cc +++ b/pdns/recursordist/rec-zonetocache.cc @@ -30,6 +30,7 @@ #include "logging.hh" #include "threadname.hh" #include "rec-lua-conf.hh" +#include "zonemd.hh" #ifdef HAVE_LIBCURL #include "minicurl.hh" @@ -58,7 +59,7 @@ struct ZoneData bool isRRSetAuth(const DNSName& qname, QType qtype) const; void parseDRForCache(DNSRecord& dr); - void getByAXFR(const RecZoneToCache::Config&); + pdns::ZoneMD::Result getByAXFR(const RecZoneToCache::Config&); void ZoneToCache(const RecZoneToCache::Config& config, uint64_t gen); }; @@ -125,7 +126,7 @@ void ZoneData::parseDRForCache(DNSRecord& dr) } } -void ZoneData::getByAXFR(const RecZoneToCache::Config& config) +pdns::ZoneMD::Result ZoneData::getByAXFR(const RecZoneToCache::Config& config) { ComboAddress primary = ComboAddress(config.d_sources.at(0), 53); uint16_t axfrTimeout = config.d_timeout; @@ -142,8 +143,12 @@ void ZoneData::getByAXFR(const RecZoneToCache::Config& config) time_t axfrStart = time(nullptr); time_t axfrNow = time(nullptr); + vector v; while (axfr.getChunk(nop, &chunk, (axfrStart + axfrTimeout - axfrNow))) { for (auto& dr : chunk) { + if (config.d_zonemd != pdns::ZoneMD::Config::Ignore) { + v.push_back(dr); + } parseDRForCache(dr); } axfrNow = time(nullptr); @@ -151,6 +156,19 @@ void ZoneData::getByAXFR(const RecZoneToCache::Config& config) throw std::runtime_error("Total AXFR time for zoneToCache exceeded!"); } } + if (config.d_zonemd != pdns::ZoneMD::Config::Ignore) { + auto zonemd = pdns::ZoneMD(d_zone); + zonemd.readRecords(v); + bool validationDone, validationSuccess; + zonemd.verify(validationDone, validationSuccess); + if (!validationDone) { + return pdns::ZoneMD::Result::NoValidationDone; + } + if (!validationSuccess) { + return pdns::ZoneMD::Result::ValidationFailure; + } + } + return pdns::ZoneMD::Result::OK; } static std::vector getLinesFromFile(const std::string& file) @@ -201,9 +219,10 @@ void ZoneData::ZoneToCache(const RecZoneToCache::Config& config, uint64_t config // A this moment, we ignore NSEC and NSEC3 records. It is not clear to me yet under which conditions // they could be entered in into the (neg)cache. + pdns::ZoneMD::Result result = pdns::ZoneMD::Result::OK; if (config.d_method == "axfr") { d_log->info("Getting zone by AXFR"); - getByAXFR(config); + result = getByAXFR(config); } else { vector lines; @@ -224,6 +243,30 @@ void ZoneData::ZoneToCache(const RecZoneToCache::Config& config, uint64_t config DNSRecord dr(drr); parseDRForCache(dr); } + // XXX ZONEMD processing + } + + if (config.d_zonemd == pdns::ZoneMD::Config::Required && result != pdns::ZoneMD::Result::OK) { + // We do not accept NoValidationDone in this case + throw PDNSException("ZoneMD validation failure"); + return; + } + if (config.d_zonemd == pdns::ZoneMD::Config::Process && result == pdns::ZoneMD::Result::ValidationFailure) { + throw PDNSException("ZoneMD validation failure"); + return; + } + if (config.d_zonemd == pdns::ZoneMD::Config::LogOnly) { + switch (result) { + case pdns::ZoneMD::Result::ValidationFailure: + d_log->info("ZoneMD failure (ignored)"); + break; + case pdns::ZoneMD::Result::NoValidationDone: + d_log->info("No ZoneMD validation done"); + break; + case pdns::ZoneMD::Result::OK: + d_log->info("ZoneMD validation succeeded"); + break; + } } // Extra check before we are touching the cache diff --git a/pdns/recursordist/rec-zonetocache.hh b/pdns/recursordist/rec-zonetocache.hh index 8eb6425af2..11064cad44 100644 --- a/pdns/recursordist/rec-zonetocache.hh +++ b/pdns/recursordist/rec-zonetocache.hh @@ -25,6 +25,7 @@ #include "namespaces.hh" #include "dns.hh" #include "iputils.hh" +#include "zonemd.hh" class RecZoneToCache { @@ -40,6 +41,7 @@ public: time_t d_retryOnError{60}; // Retry on error time_t d_refreshPeriod{24 * 3600}; // Time between refetch uint32_t d_timeout{20}; // timeout in seconds + pdns::ZoneMD::Config d_zonemd{pdns::ZoneMD::Config::LogOnly}; }; static void ZoneToCache(Config config, uint64_t gen); }; diff --git a/pdns/zonemd.cc b/pdns/zonemd.cc index 823c316c86..1cce94b2ed 100644 --- a/pdns/zonemd.cc +++ b/pdns/zonemd.cc @@ -45,6 +45,33 @@ void pdns::ZoneMD::readRecords(ZoneParserTNG& zpt) } } +void pdns::ZoneMD::readRecords(const vector& records) +{ + for (auto& record : records) { + if (!record.d_name.isPartOf(d_zone) && record.d_name != d_zone) { + continue; + } + if (record.d_type == QType::SOA && d_soaRecordContent) { + continue; + } + + if (record.d_type == QType::SOA && record.d_name == d_zone) { + d_soaRecordContent = std::dynamic_pointer_cast(record.d_content); + } + if (record.d_type == QType::ZONEMD && record.d_name == d_zone) { + auto zonemd = std::dynamic_pointer_cast(record.d_content); + auto inserted = d_zonemdRecords.insert({pair(zonemd->d_scheme, zonemd->d_hashalgo), {zonemd, false}}); + if (!inserted.second) { + // Mark as duplicate + inserted.first->second.duplicate = true; + } + } + RRSetKey_t key = std::pair(record.d_name, record.d_type); + d_resourceRecordSets[key].push_back(record.d_content); + d_resourceRecordSetTTLs[key] = record.d_ttl; + } +} + void pdns::ZoneMD::verify(bool& validationDone, bool& validationOK) { validationDone = false; diff --git a/pdns/zonemd.hh b/pdns/zonemd.hh index 9a5a243b38..2f4a932d88 100644 --- a/pdns/zonemd.hh +++ b/pdns/zonemd.hh @@ -36,10 +36,14 @@ namespace pdns class ZoneMD { public: + enum class Config: uint8_t { Ignore, Process, LogOnly, Required }; + enum class Result: uint8_t { OK, NoValidationDone, ValidationFailure }; + ZoneMD(const DNSName& zone) : d_zone(zone) {} void readRecords(ZoneParserTNG& zpt); + void readRecords(const std::vector& records); void verify(bool& validationDone, bool& validationOK); private: