]> granicus.if.org Git - pdns/commitdiff
recursor: Prevent DNSSEC downgrade attacks
authorPieter Lexis <pieter.lexis@powerdns.com>
Tue, 31 Oct 2017 21:57:46 +0000 (22:57 +0100)
committerPieter Lexis <pieter.lexis@powerdns.com>
Thu, 2 Nov 2017 15:24:51 +0000 (16:24 +0100)
RFC 4509 section 3: "Validator implementations SHOULD ignore DS RR
containing SHA-1 digests if DS RRs with SHA-256 digests are present in the
DS RRset."

As SHA348 is specified as well, the spirit of the this line is "use the
best algorithm".

This also means that if a delegation has DS records for multiple keys
(and algos) and only a subset have stronger digests, we will discard the
DS records for the weaker digests.

pdns/syncres.cc
pdns/syncres.hh

index 504aa61cee718b2efec7803a1dafd7606315a323..79db0a646a398c61ac45484fcf1b0650fa1bdaa6 100644 (file)
@@ -33,6 +33,7 @@
 #include "lua-recursor4.hh"
 #include "rec-lua-conf.hh"
 #include "syncres.hh"
+#include "dnsseckeeper.hh"
 #include "validate-recursor.hh"
 
 thread_local SyncRes::ThreadLocalStorage SyncRes::t_sstorage;
@@ -1542,12 +1543,21 @@ vState SyncRes::getDSRecords(const DNSName& zone, dsmap_t& ds, bool taOnly, unsi
 
   if (rcode == RCode::NoError || (rcode == RCode::NXDomain && !bogusOnNXD)) {
 
+    uint8_t bestDigestType = 0;
+
     if (state == Secure) {
       bool gotCNAME = false;
       for (const auto& record : dsrecords) {
         if (record.d_type == QType::DS) {
           const auto dscontent = getRR<DSRecordContent>(record);
           if (dscontent && isSupportedDS(*dscontent)) {
+            // Make GOST a lower prio than SHA256
+            if (dscontent->d_digesttype == DNSSECKeeper::GOST && bestDigestType == DNSSECKeeper::SHA256) {
+              continue;
+            }
+            if (dscontent->d_digesttype > bestDigestType || (bestDigestType == DNSSECKeeper::GOST && dscontent->d_digesttype == DNSSECKeeper::SHA256)) {
+              bestDigestType = dscontent->d_digesttype;
+            }
             ds.insert(*dscontent);
           }
         }
@@ -1556,6 +1566,16 @@ vState SyncRes::getDSRecords(const DNSName& zone, dsmap_t& ds, bool taOnly, unsi
         }
       }
 
+      /* RFC 4509 section 3: "Validator implementations SHOULD ignore DS RRs containing SHA-1
+       * digests if DS RRs with SHA-256 digests are present in the DS RRset."
+       * As SHA348 is specified as well, the spirit of the this line is "use the best algorithm".
+       */
+      for (const auto& dsrec : ds) {
+        if (dsrec.d_digesttype != bestDigestType) {
+          ds.erase(dsrec);
+        }
+      }
+
       if (rcode == RCode::NoError && ds.empty()) {
         if (foundCut) {
           if (gotCNAME || denialProvesNoDelegation(zone, dsrecords)) {
index 59bd8e253aa501100b329200fda554ff13b68aa7..d9313804389f359d89c5afcbf8d1586319e4d7f6 100644 (file)
@@ -329,6 +329,8 @@ public:
   typedef map<DNSName, DecayingEwmaCollection> nsspeeds_t;
   typedef map<ComboAddress, EDNSStatus> ednsstatus_t;
 
+  vState getDSRecords(const DNSName& zone, dsmap_t& ds, bool onlyTA, unsigned int depth, bool bogusOnNXD=true, bool* foundCut=nullptr);
+
   class AuthDomain
   {
   public:
@@ -754,7 +756,6 @@ private:
   void updateValidationState(vState& state, const vState stateUpdate);
   vState validateRecordsWithSigs(unsigned int depth, const DNSName& qname, const QType& qtype, const DNSName& name, const std::vector<DNSRecord>& records, const std::vector<std::shared_ptr<RRSIGRecordContent> >& signatures);
   vState validateDNSKeys(const DNSName& zone, const std::vector<DNSRecord>& dnskeys, const std::vector<std::shared_ptr<RRSIGRecordContent> >& 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);
   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);