1 #include "validate.hh"
2 #include "misc.hh"
3 #include "dnssecinfra.hh"
4 #include "dnsseckeeper.hh"
5 #include "rec-lua-conf.hh"
6 #include "base32.hh"
7 #include "logger.hh"
8 bool g_dnssecLOG{false};
9 time_t g_signatureInceptionSkew{0};
10 uint16_t g_maxNSEC3Iterations{0};
11 
12 #define LOG(x) if(g_dnssecLOG) { g_log <<Logger::Warning << x; }
13 
isAZoneKey(const DNSKEYRecordContent & key)14 static bool isAZoneKey(const DNSKEYRecordContent& key)
15 {
16   /* rfc4034 Section 2.1.1:
17      "Bit 7 of the Flags field is the Zone Key flag.  If bit 7 has value 1,
18      then the DNSKEY record holds a DNS zone key, and the DNSKEY RR's
19      owner name MUST be the name of a zone.  If bit 7 has value 0, then
20      the DNSKEY record holds some other type of DNS public key and MUST
21      NOT be used to verify RRSIGs that cover RRsets."
22 
23      Let's check that this is a ZONE key, even though there is no other
24      types of DNSKEYs at the moment.
25   */
26   return (key.d_flags & 256) != 0;
27 }
28 
isRevokedKey(const DNSKEYRecordContent & key)29 static bool isRevokedKey(const DNSKEYRecordContent& key)
30 {
31   /* rfc5011 Section 3 */
32   return (key.d_flags & 128) != 0;
33 }
34 
getByTag(const skeyset_t & keys,uint16_t tag,uint8_t algorithm)35 static vector<shared_ptr<DNSKEYRecordContent > > getByTag(const skeyset_t& keys, uint16_t tag, uint8_t algorithm)
36 {
37   vector<shared_ptr<DNSKEYRecordContent>> ret;
38 
39   for (const auto& key : keys) {
40     if (!isAZoneKey(*key)) {
41       LOG("Key for tag "<<std::to_string(tag)<<" and algorithm "<<std::to_string(algorithm)<<" is not a zone key, skipping"<<endl;);
42       continue;
43     }
44 
45     if (isRevokedKey(*key)) {
46       LOG("Key for tag "<<std::to_string(tag)<<" and algorithm "<<std::to_string(algorithm)<<" has been revoked, skipping"<<endl;);
47       continue;
48     }
49 
50     if (key->d_protocol == 3 && key->getTag() == tag && key->d_algorithm == algorithm) {
51       ret.push_back(key);
52     }
53   }
54 
55   return ret;
56 }
57 
isCoveredByNSEC3Hash(const std::string & h,const std::string & beginHash,const std::string & nextHash)58 bool isCoveredByNSEC3Hash(const std::string& h, const std::string& beginHash, const std::string& nextHash)
59 {
60   return ((beginHash < h && h < nextHash) ||          // no wrap          BEGINNING --- HASH -- END
61           (nextHash > h  && beginHash > nextHash) ||  // wrap             HASH --- END --- BEGINNING
62           (nextHash < beginHash  && beginHash < h) || // wrap other case  END --- BEGINNING --- HASH
63           (beginHash == nextHash && h != beginHash));   // "we have only 1 NSEC3 record, LOL!"
64 }
65 
isCoveredByNSEC3Hash(const DNSName & h,const DNSName & beginHash,const DNSName & nextHash)66 bool isCoveredByNSEC3Hash(const DNSName& h, const DNSName& beginHash, const DNSName& nextHash)
67 {
68   return ((beginHash.canonCompare(h) && h.canonCompare(nextHash)) ||          // no wrap          BEGINNING --- HASH -- END
69           (h.canonCompare(nextHash) && nextHash.canonCompare(beginHash)) ||  // wrap             HASH --- END --- BEGINNING
70           (nextHash.canonCompare(beginHash) && beginHash.canonCompare(h)) || // wrap other case  END --- BEGINNING --- HASH
71           (beginHash == nextHash && h != beginHash));   // "we have only 1 NSEC3 record, LOL!"
72 }
73 
isCoveredByNSEC(const DNSName & name,const DNSName & begin,const DNSName & next)74 bool isCoveredByNSEC(const DNSName& name, const DNSName& begin, const DNSName& next)
75 {
76   return ((begin.canonCompare(name) && name.canonCompare(next)) ||  // no wrap          BEGINNING --- NAME --- NEXT
77           (name.canonCompare(next) && next.canonCompare(begin)) ||  // wrap             NAME --- NEXT --- BEGINNING
78           (next.canonCompare(begin) && begin.canonCompare(name)) || // wrap other case  NEXT --- BEGINNING --- NAME
79           (begin == next && name != begin));                        // "we have only 1 NSEC record, LOL!"
80 }
81 
nsecProvesENT(const DNSName & name,const DNSName & begin,const DNSName & next)82 static bool nsecProvesENT(const DNSName& name, const DNSName& begin, const DNSName& next)
83 {
84   /* if name is an ENT:
85      - begin < name
86      - next is a child of name
87   */
88   return begin.canonCompare(name) && next != name && next.isPartOf(name);
89 }
90 
91 using nsec3HashesCache = std::map<std::tuple<DNSName, std::string, uint16_t>, std::string>;
92 
getHashFromNSEC3(const DNSName & qname,const std::shared_ptr<NSEC3RecordContent> & nsec3,nsec3HashesCache & cache)93 static std::string getHashFromNSEC3(const DNSName& qname, const std::shared_ptr<NSEC3RecordContent>& nsec3, nsec3HashesCache& cache)
94 {
95   std::string result;
96 
97   if (g_maxNSEC3Iterations && nsec3->d_iterations > g_maxNSEC3Iterations) {
98     return result;
99   }
100 
101   auto key = std::make_tuple(qname, nsec3->d_salt, nsec3->d_iterations);
102   auto it = cache.find(key);
103   if (it != cache.end())
104   {
105     return it->second;
106   }
107 
108   result = hashQNameWithSalt(nsec3->d_salt, nsec3->d_iterations, qname);
109   cache[key] = result;
110   return result;
111 }
112 
113 /* There is no delegation at this exact point if:
114    - the name exists but the NS type is not set
115    - the name does not exist
116    One exception, if the name is covered by an opt-out NSEC3
117    it doesn't prove that an insecure delegation doesn't exist.
118 */
denialProvesNoDelegation(const DNSName & zone,const std::vector<DNSRecord> & dsrecords)119 bool denialProvesNoDelegation(const DNSName& zone, const std::vector<DNSRecord>& dsrecords)
120 {
121   nsec3HashesCache cache;
122 
123   for (const auto& record : dsrecords) {
124     if (record.d_type == QType::NSEC) {
125       const auto nsec = getRR<NSECRecordContent>(record);
126       if (!nsec) {
127         continue;
128       }
129 
130       if (record.d_name == zone) {
131         return !nsec->isSet(QType::NS);
132       }
133 
134       if (isCoveredByNSEC(zone, record.d_name, nsec->d_next)) {
135         return true;
136       }
137     }
138     else if (record.d_type == QType::NSEC3) {
139       const auto nsec3 = getRR<NSEC3RecordContent>(record);
140       if (!nsec3) {
141         continue;
142       }
143 
144       const string h = getHashFromNSEC3(zone, nsec3, cache);
145       if (h.empty()) {
146         return false;
147       }
148 
149       const string beginHash = fromBase32Hex(record.d_name.getRawLabels()[0]);
150       if (beginHash == h) {
151         return !nsec3->isSet(QType::NS);
152       }
153 
154       if (isCoveredByNSEC3Hash(h, beginHash, nsec3->d_nexthash)) {
155         return !(nsec3->isOptOut());
156       }
157     }
158   }
159 
160   return false;
161 }
162 
163 /* RFC 4035 section-5.3.4:
164    "If the number of labels in an RRset's owner name is greater than the
165    Labels field of the covering RRSIG RR, then the RRset and its
166    covering RRSIG RR were created as a result of wildcard expansion."
167 */
isWildcardExpanded(unsigned int labelCount,const std::shared_ptr<RRSIGRecordContent> & sign)168 bool isWildcardExpanded(unsigned int labelCount, const std::shared_ptr<RRSIGRecordContent>& sign)
169 {
170   if (sign && sign->d_labels < labelCount) {
171     return true;
172   }
173 
174   return false;
175 }
176 
isWildcardExpanded(const DNSName & owner,const std::vector<std::shared_ptr<RRSIGRecordContent>> & signatures)177 static bool isWildcardExpanded(const DNSName& owner, const std::vector<std::shared_ptr<RRSIGRecordContent> >& signatures)
178 {
179   if (signatures.empty()) {
180     return false;
181   }
182 
183   const auto& sign = signatures.at(0);
184   unsigned int labelsCount = owner.countLabels();
185   return isWildcardExpanded(labelsCount, sign);
186 }
187 
isWildcardExpandedOntoItself(const DNSName & owner,unsigned int labelCount,const std::shared_ptr<RRSIGRecordContent> & sign)188 bool isWildcardExpandedOntoItself(const DNSName& owner, unsigned int labelCount, const std::shared_ptr<RRSIGRecordContent>& sign)
189 {
190   if (owner.isWildcard() && (labelCount - 1) == sign->d_labels) {
191     /* this is a wildcard alright, but it has not been expanded */
192     return true;
193   }
194   return false;
195 }
196 
isWildcardExpandedOntoItself(const DNSName & owner,const std::vector<std::shared_ptr<RRSIGRecordContent>> & signatures)197 static bool isWildcardExpandedOntoItself(const DNSName& owner, const std::vector<std::shared_ptr<RRSIGRecordContent> >& signatures)
198 {
199   if (signatures.empty()) {
200     return false;
201   }
202 
203   const auto& sign = signatures.at(0);
204   unsigned int labelsCount = owner.countLabels();
205   return isWildcardExpandedOntoItself(owner, labelsCount, sign);
206 }
207 
208 /* if this is a wildcard NSEC, the owner name has been modified
209    to match the name. Make sure we use the original '*' form. */
getNSECOwnerName(const DNSName & initialOwner,const std::vector<std::shared_ptr<RRSIGRecordContent>> & signatures)210 DNSName getNSECOwnerName(const DNSName& initialOwner, const std::vector<std::shared_ptr<RRSIGRecordContent> >& signatures)
211 {
212   DNSName result = initialOwner;
213 
214   if (signatures.empty()) {
215     return result;
216   }
217 
218   const auto& sign = signatures.at(0);
219   unsigned int labelsCount = initialOwner.countLabels();
220   if (sign && sign->d_labels < labelsCount) {
221     do {
222       result.chopOff();
223       labelsCount--;
224     }
225     while (sign->d_labels < labelsCount);
226 
227     result = g_wildcarddnsname + result;
228   }
229 
230   return result;
231 }
232 
isNSECAncestorDelegation(const DNSName & signer,const DNSName & owner,const std::shared_ptr<NSECRecordContent> & nsec)233 static bool isNSECAncestorDelegation(const DNSName& signer, const DNSName& owner, const std::shared_ptr<NSECRecordContent>& nsec)
234 {
235   return nsec->isSet(QType::NS) &&
236     !nsec->isSet(QType::SOA) &&
237     signer.countLabels() < owner.countLabels();
238 }
239 
isNSEC3AncestorDelegation(const DNSName & signer,const DNSName & owner,const std::shared_ptr<NSEC3RecordContent> & nsec3)240 bool isNSEC3AncestorDelegation(const DNSName& signer, const DNSName& owner, const std::shared_ptr<NSEC3RecordContent>& nsec3)
241 {
242   return nsec3->isSet(QType::NS) &&
243     !nsec3->isSet(QType::SOA) &&
244     signer.countLabels() < owner.countLabels();
245 }
246 
provesNoDataWildCard(const DNSName & qname,const uint16_t qtype,const DNSName & closestEncloser,const cspmap_t & validrrsets)247 static bool provesNoDataWildCard(const DNSName& qname, const uint16_t qtype, const DNSName& closestEncloser, const cspmap_t& validrrsets)
248 {
249   const DNSName wildcard = g_wildcarddnsname + closestEncloser;
250   LOG("Trying to prove that there is no data in wildcard for "<<qname<<"/"<<QType(qtype).toString()<<endl);
251   for (const auto& v : validrrsets) {
252     LOG("Do have: "<<v.first.first<<"/"<<DNSRecordContent::NumberToType(v.first.second)<<endl);
253     if (v.first.second == QType::NSEC) {
254       for (const auto& r : v.second.records) {
255         LOG("\t"<<r->getZoneRepresentation()<<endl);
256         auto nsec = std::dynamic_pointer_cast<NSECRecordContent>(r);
257         if (!nsec) {
258           continue;
259         }
260 
261         DNSName owner = getNSECOwnerName(v.first.first, v.second.signatures);
262         if (owner != wildcard) {
263           continue;
264         }
265 
266         LOG("\tWildcard matches");
267         if (qtype == 0 || isTypeDenied(nsec, QType(qtype))) {
268           LOG(" and proves that the type did not exist"<<endl);
269           return true;
270         }
271         LOG(" BUT the type did exist!"<<endl);
272         return false;
273       }
274     }
275   }
276 
277   return false;
278 }
279 
getClosestEncloserFromNSEC(const DNSName & name,const DNSName & owner,const DNSName & next)280 DNSName getClosestEncloserFromNSEC(const DNSName& name, const DNSName& owner, const DNSName& next)
281 {
282   DNSName commonWithOwner(name.getCommonLabels(owner));
283   DNSName commonWithNext(name.getCommonLabels(next));
284   if (commonWithOwner.countLabels() >= commonWithNext.countLabels()) {
285     return commonWithOwner;
286   }
287   return commonWithNext;
288 }
289 
290 /*
291   This function checks whether the non-existence of a wildcard covering qname|qtype
292   is proven by the NSEC records in validrrsets.
293 */
provesNoWildCard(const DNSName & qname,const uint16_t qtype,const DNSName & closestEncloser,const cspmap_t & validrrsets)294 static bool provesNoWildCard(const DNSName& qname, const uint16_t qtype, const DNSName& closestEncloser, const cspmap_t & validrrsets)
295 {
296   LOG("Trying to prove that there is no wildcard for "<<qname<<"/"<<QType(qtype).toString()<<endl);
297   const DNSName wildcard = g_wildcarddnsname + closestEncloser;
298   for (const auto& v : validrrsets) {
299     LOG("Do have: "<<v.first.first<<"/"<<DNSRecordContent::NumberToType(v.first.second)<<endl);
300     if (v.first.second == QType::NSEC) {
301       for (const auto& r : v.second.records) {
302         LOG("\t"<<r->getZoneRepresentation()<<endl);
303         auto nsec = std::dynamic_pointer_cast<NSECRecordContent>(r);
304         if (!nsec) {
305           continue;
306         }
307 
308         const DNSName owner = getNSECOwnerName(v.first.first, v.second.signatures);
309         LOG("Comparing owner: "<<owner<<" with target: "<<wildcard<<endl);
310 
311         if (qname != owner && qname.isPartOf(owner) && nsec->isSet(QType::DNAME)) {
312           /* rfc6672 section 5.3.2: DNAME Bit in NSEC Type Map
313 
314              In any negative response, the NSEC or NSEC3 [RFC5155] record type
315              bitmap SHOULD be checked to see that there was no DNAME that could
316              have been applied.  If the DNAME bit in the type bitmap is set and
317              the query name is a subdomain of the closest encloser that is
318              asserted, then DNAME substitution should have been done, but the
319              substitution has not been done as specified.
320           */
321           LOG("\tThe qname is a subdomain of the NSEC and the DNAME bit is set"<<endl);
322           return false;
323         }
324 
325         if (wildcard != owner && isCoveredByNSEC(wildcard, owner, nsec->d_next)) {
326           LOG("\tWildcard is covered"<<endl);
327           return true;
328         }
329       }
330     }
331   }
332 
333   return false;
334 }
335 
336 /*
337   This function checks whether the non-existence of a wildcard covering qname|qtype
338   is proven by the NSEC3 records in validrrsets.
339   If `wildcardExists` is not NULL, if will be set to true if a wildcard exists
340   for this qname but doesn't have this qtype.
341 */
provesNSEC3NoWildCard(const DNSName & closestEncloser,uint16_t const qtype,const cspmap_t & validrrsets,bool * wildcardExists,nsec3HashesCache & cache)342 static bool provesNSEC3NoWildCard(const DNSName& closestEncloser, uint16_t const qtype, const cspmap_t& validrrsets, bool* wildcardExists, nsec3HashesCache& cache)
343 {
344   auto wildcard = g_wildcarddnsname + closestEncloser;
345   LOG("Trying to prove that there is no wildcard for "<<wildcard<<"/"<<QType(qtype).toString()<<endl);
346 
347   for (const auto& v : validrrsets) {
348     LOG("Do have: "<<v.first.first<<"/"<<DNSRecordContent::NumberToType(v.first.second)<<endl);
349     if (v.first.second == QType::NSEC3) {
350       for (const auto& r : v.second.records) {
351         LOG("\t"<<r->getZoneRepresentation()<<endl);
352         auto nsec3 = std::dynamic_pointer_cast<NSEC3RecordContent>(r);
353         if (!nsec3) {
354           continue;
355         }
356 
357         const DNSName signer = getSigner(v.second.signatures);
358         if (!v.first.first.isPartOf(signer)) {
359           continue;
360         }
361 
362         string h = getHashFromNSEC3(wildcard, nsec3, cache);
363         if (h.empty()) {
364           return false;
365         }
366         LOG("\tWildcard hash: "<<toBase32Hex(h)<<endl);
367         string beginHash=fromBase32Hex(v.first.first.getRawLabels()[0]);
368         LOG("\tNSEC3 hash: "<<toBase32Hex(beginHash)<<" -> "<<toBase32Hex(nsec3->d_nexthash)<<endl);
369 
370         if (beginHash == h) {
371           LOG("\tWildcard hash matches");
372           if (wildcardExists) {
373             *wildcardExists = true;
374           }
375 
376           /* RFC 6840 section 4.1 "Clarifications on Nonexistence Proofs":
377              Ancestor delegation NSEC or NSEC3 RRs MUST NOT be used to assume
378              nonexistence of any RRs below that zone cut, which include all RRs at
379              that (original) owner name other than DS RRs, and all RRs below that
380              owner name regardless of type.
381           */
382           if (qtype != QType::DS && isNSEC3AncestorDelegation(signer, v.first.first, nsec3)) {
383             /* this is an "ancestor delegation" NSEC3 RR */
384             LOG(" BUT an ancestor delegation NSEC3 RR can only deny the existence of a DS"<<endl);
385             return false;
386           }
387 
388           if (qtype == 0 || isTypeDenied(nsec3, QType(qtype))) {
389             LOG(" and proves that the type did not exist"<<endl);
390             return true;
391           }
392           LOG(" BUT the type did exist!"<<endl);
393           return false;
394         }
395 
396         if (isCoveredByNSEC3Hash(h, beginHash, nsec3->d_nexthash)) {
397           LOG("\tWildcard hash is covered"<<endl);
398           return true;
399         }
400       }
401     }
402   }
403 
404   return false;
405 }
406 
matchesNSEC(const DNSName & name,uint16_t qtype,const DNSName & nsecOwner,const std::shared_ptr<NSECRecordContent> & nsec,const std::vector<std::shared_ptr<RRSIGRecordContent>> & signatures)407 dState matchesNSEC(const DNSName& name, uint16_t qtype, const DNSName& nsecOwner, const std::shared_ptr<NSECRecordContent>& nsec, const std::vector<std::shared_ptr<RRSIGRecordContent>>& signatures)
408 {
409   const DNSName signer = getSigner(signatures);
410   if (!name.isPartOf(signer) || !nsecOwner.isPartOf(signer)) {
411     return dState::INCONCLUSIVE;
412   }
413 
414   const DNSName owner = getNSECOwnerName(nsecOwner, signatures);
415   /* RFC 6840 section 4.1 "Clarifications on Nonexistence Proofs":
416      Ancestor delegation NSEC or NSEC3 RRs MUST NOT be used to assume
417      nonexistence of any RRs below that zone cut, which include all RRs at
418      that (original) owner name other than DS RRs, and all RRs below that
419      owner name regardless of type.
420   */
421   if (name.isPartOf(owner) && isNSECAncestorDelegation(signer, owner, nsec)) {
422     /* this is an "ancestor delegation" NSEC RR */
423     if (!(qtype == QType::DS && name == owner)) {
424       LOG("An ancestor delegation NSEC RR can only deny the existence of a DS"<<endl);
425       return dState::NODENIAL;
426     }
427   }
428 
429   /* check if the type is denied */
430   if (name == owner) {
431     if (!isTypeDenied(nsec, QType(qtype))) {
432       LOG("Does _not_ deny existence of type "<<QType(qtype).toString()<<endl);
433       return dState::NODENIAL;
434     }
435 
436     LOG("Denies existence of type "<<QType(qtype).toString()<<endl);
437     return dState::NXQTYPE;
438   }
439 
440   if (name.isPartOf(owner) && nsec->isSet(QType::DNAME)) {
441     /* rfc6672 section 5.3.2: DNAME Bit in NSEC Type Map
442 
443        In any negative response, the NSEC or NSEC3 [RFC5155] record type
444        bitmap SHOULD be checked to see that there was no DNAME that could
445        have been applied.  If the DNAME bit in the type bitmap is set and
446        the query name is a subdomain of the closest encloser that is
447        asserted, then DNAME substitution should have been done, but the
448        substitution has not been done as specified.
449     */
450     LOG("The DNAME bit is set and the query name is a subdomain of that NSEC");
451     return dState::NODENIAL;
452   }
453 
454   if (isCoveredByNSEC(name, owner, nsec->d_next)) {
455     LOG(name<<" is covered by ("<<owner<<" to "<<nsec->d_next<<") ");
456 
457     if (nsecProvesENT(name, owner, nsec->d_next)) {
458       LOG("Denies existence of type "<<name<<"/"<<QType(qtype).toString()<<" by proving that "<<name<<" is an ENT"<<endl);
459       return dState::NXQTYPE;
460     }
461 
462     return dState::NXDOMAIN;
463   }
464 
465   return dState::INCONCLUSIVE;
466 }
467 
468 /*
469   This function checks whether the existence of qname|qtype is denied by the NSEC and NSEC3
470   in validrrsets.
471   - If `referralToUnsigned` is true and qtype is QType::DS, this functions returns NODENIAL
472   if a NSEC or NSEC3 proves that the name exists but no NS type exists, as specified in RFC 5155 section 8.9.
473   - If `wantsNoDataProof` is set but a NSEC proves that the whole name does not exist, the function will return
474   NXQTYPE is the name is proven to be ENT and NXDOMAIN otherwise.
475   - If `needWildcardProof` is false, the proof that a wildcard covering this qname|qtype is not checked. It is
476   useful when we have a positive answer synthesized from a wildcard and we only need to prove that the exact
477   name does not exist.
478 */
479 
getDenial(const cspmap_t & validrrsets,const DNSName & qname,const uint16_t qtype,bool referralToUnsigned,bool wantsNoDataProof,bool needWildcardProof,unsigned int wildcardLabelsCount)480 dState getDenial(const cspmap_t &validrrsets, const DNSName& qname, const uint16_t qtype, bool referralToUnsigned, bool wantsNoDataProof, bool needWildcardProof, unsigned int wildcardLabelsCount)
481 {
482   nsec3HashesCache cache;
483   bool nsec3Seen = false;
484   if (!needWildcardProof && wildcardLabelsCount == 0) {
485     throw PDNSException("Invalid wildcard labels count for the validation of a positive answer synthesized from a wildcard");
486   }
487 
488   for (const auto& v : validrrsets) {
489     LOG("Do have: "<<v.first.first<<"/"<<DNSRecordContent::NumberToType(v.first.second)<<endl);
490 
491     if (v.first.second==QType::NSEC) {
492       for (const auto& r : v.second.records) {
493         LOG("\t"<<r->getZoneRepresentation()<<endl);
494 
495         if (v.second.signatures.empty()) {
496           continue;
497         }
498 
499         auto nsec = std::dynamic_pointer_cast<NSECRecordContent>(r);
500         if (!nsec) {
501           continue;
502         }
503 
504         const DNSName owner = getNSECOwnerName(v.first.first, v.second.signatures);
505         const DNSName signer = getSigner(v.second.signatures);
506         if (!v.first.first.isPartOf(signer) || !owner.isPartOf(signer) ) {
507            continue;
508         }
509 
510         /* RFC 6840 section 4.1 "Clarifications on Nonexistence Proofs":
511            Ancestor delegation NSEC or NSEC3 RRs MUST NOT be used to assume
512            nonexistence of any RRs below that zone cut, which include all RRs at
513            that (original) owner name other than DS RRs, and all RRs below that
514            owner name regardless of type.
515         */
516         if (qname.isPartOf(owner) && isNSECAncestorDelegation(signer, owner, nsec)) {
517           /* this is an "ancestor delegation" NSEC RR */
518           if (!(qtype == QType::DS && qname == owner)) {
519             LOG("An ancestor delegation NSEC RR can only deny the existence of a DS"<<endl);
520             return dState::NODENIAL;
521           }
522         }
523 
524         /* check if the type is denied */
525         if (qname == owner) {
526           if (!isTypeDenied(nsec, QType(qtype))) {
527             LOG("Does _not_ deny existence of type "<<QType(qtype).toString()<<endl);
528             return dState::NODENIAL;
529           }
530 
531           LOG("Denies existence of type "<<QType(qtype).toString()<<endl);
532 
533           /*
534            * RFC 4035 Section 2.3:
535            * The bitmap for the NSEC RR at a delegation point requires special
536            * attention.  Bits corresponding to the delegation NS RRset and any
537            * RRsets for which the parent zone has authoritative data MUST be set
538            */
539           if (referralToUnsigned && qtype == QType::DS && !nsec->isSet(QType::NS)) {
540             LOG("However, no NS record exists at this level!"<<endl);
541             return dState::NODENIAL;
542           }
543 
544           /* we know that the name exists (but this qtype doesn't) so except
545              if the answer was generated by a wildcard expansion, no wildcard
546              could have matched (rfc4035 section 5.4 bullet 1) */
547           if (needWildcardProof && (!isWildcardExpanded(owner, v.second.signatures) || isWildcardExpandedOntoItself(owner, v.second.signatures))) {
548             needWildcardProof = false;
549           }
550 
551           if (!needWildcardProof) {
552             return dState::NXQTYPE;
553           }
554 
555           DNSName closestEncloser = getClosestEncloserFromNSEC(qname, owner, nsec->d_next);
556           if (provesNoWildCard(qname, qtype, closestEncloser, validrrsets)) {
557             return dState::NXQTYPE;
558           }
559 
560           LOG("But the existence of a wildcard is not denied for "<<qname<<"/"<<endl);
561           return dState::NODENIAL;
562         }
563 
564         if (qname.isPartOf(owner) && nsec->isSet(QType::DNAME)) {
565           /* rfc6672 section 5.3.2: DNAME Bit in NSEC Type Map
566 
567              In any negative response, the NSEC or NSEC3 [RFC5155] record type
568              bitmap SHOULD be checked to see that there was no DNAME that could
569              have been applied.  If the DNAME bit in the type bitmap is set and
570              the query name is a subdomain of the closest encloser that is
571              asserted, then DNAME substitution should have been done, but the
572              substitution has not been done as specified.
573           */
574           LOG("The DNAME bit is set and the query name is a subdomain of that NSEC");
575           return dState::NODENIAL;
576         }
577 
578         /* check if the whole NAME is denied existing */
579         if (isCoveredByNSEC(qname, owner, nsec->d_next)) {
580           LOG(qname<<" is covered by ("<<owner<<" to "<<nsec->d_next<<") ");
581 
582           if (nsecProvesENT(qname, owner, nsec->d_next)) {
583             if (wantsNoDataProof) {
584               /* if the name is an ENT and we received a NODATA answer,
585                  we are fine with a NSEC proving that the name does not exist. */
586               LOG("Denies existence of type "<<qname<<"/"<<QType(qtype).toString()<<" by proving that "<<qname<<" is an ENT"<<endl);
587               return dState::NXQTYPE;
588             }
589             else {
590               /* but for a NXDOMAIN proof, this doesn't make sense! */
591               LOG("but it tries to deny the existence of "<<qname<<" by proving that "<<qname<<" is an ENT, this does not make sense!"<<endl);
592               return dState::NODENIAL;
593             }
594           }
595 
596           if (!needWildcardProof) {
597             LOG("and we did not need a wildcard proof"<<endl);
598             return dState::NXDOMAIN;
599           }
600 
601           LOG("but we do need a wildcard proof so ");
602           DNSName closestEncloser = getClosestEncloserFromNSEC(qname, owner, nsec->d_next);
603           if (wantsNoDataProof) {
604             LOG("looking for NODATA proof"<<endl);
605             if (provesNoDataWildCard(qname, qtype, closestEncloser, validrrsets)) {
606               return dState::NXQTYPE;
607             }
608           }
609           else {
610             LOG("looking for NO wildcard proof"<<endl);
611             if (provesNoWildCard(qname, qtype, closestEncloser, validrrsets)) {
612               return dState::NXDOMAIN;
613             }
614           }
615 
616           LOG("But the existence of a wildcard is not denied for "<<qname<<"/"<<endl);
617           return dState::NODENIAL;
618         }
619 
620         LOG("Did not deny existence of "<<QType(qtype).toString()<<", "<<v.first.first<<"?="<<qname<<", "<<nsec->isSet(qtype)<<", next: "<<nsec->d_next<<endl);
621       }
622     } else if(v.first.second==QType::NSEC3) {
623       for (const auto& r : v.second.records) {
624         LOG("\t"<<r->getZoneRepresentation()<<endl);
625         auto nsec3 = std::dynamic_pointer_cast<NSEC3RecordContent>(r);
626         if (!nsec3) {
627           continue;
628         }
629 
630         if (v.second.signatures.empty()) {
631           continue;
632         }
633 
634         const DNSName signer = getSigner(v.second.signatures);
635         if (!v.first.first.isPartOf(signer)) {
636           LOG("Owner "<<v.first.first<<" is not part of the signer "<<signer<<", ignoring"<<endl);
637           continue;
638         }
639 
640         string h = getHashFromNSEC3(qname, nsec3, cache);
641         if (h.empty()) {
642           LOG("Unsupported hash, ignoring"<<endl);
643           return dState::INSECURE;
644         }
645 
646         nsec3Seen = true;
647 
648         LOG("\tquery hash: "<<toBase32Hex(h)<<endl);
649         string beginHash=fromBase32Hex(v.first.first.getRawLabels()[0]);
650 
651         // If the name exists, check if the qtype is denied
652         if (beginHash == h) {
653 
654           /* RFC 6840 section 4.1 "Clarifications on Nonexistence Proofs":
655              Ancestor delegation NSEC or NSEC3 RRs MUST NOT be used to assume
656              nonexistence of any RRs below that zone cut, which include all RRs at
657              that (original) owner name other than DS RRs, and all RRs below that
658              owner name regardless of type.
659           */
660           if (qtype != QType::DS && isNSEC3AncestorDelegation(signer, v.first.first, nsec3)) {
661             /* this is an "ancestor delegation" NSEC3 RR */
662             LOG("An ancestor delegation NSEC3 RR can only deny the existence of a DS"<<endl);
663             return dState::NODENIAL;
664           }
665 
666           if (!isTypeDenied(nsec3, QType(qtype))) {
667             LOG("Does _not_ deny existence of type "<<QType(qtype).toString()<<" for name "<<qname<<" (not opt-out)."<<endl);
668             return dState::NODENIAL;
669           }
670 
671           LOG("Denies existence of type "<<QType(qtype).toString()<<" for name "<<qname<<" (not opt-out)."<<endl);
672 
673           /*
674            * RFC 5155 section 8.9:
675            * If there is an NSEC3 RR present in the response that matches the
676            * delegation name, then the validator MUST ensure that the NS bit is
677            * set and that the DS bit is not set in the Type Bit Maps field of the
678            * NSEC3 RR.
679            */
680           if (referralToUnsigned && qtype == QType::DS && !nsec3->isSet(QType::NS)) {
681             LOG("However, no NS record exists at this level!"<<endl);
682             return dState::NODENIAL;
683           }
684 
685           return dState::NXQTYPE;
686         }
687       }
688     }
689   }
690 
691   /* if we have no NSEC3 records, we are done */
692   if (!nsec3Seen) {
693     return dState::NODENIAL;
694   }
695 
696   DNSName closestEncloser(qname);
697   bool found = false;
698 
699   if (needWildcardProof) {
700     /* We now need to look for a NSEC3 covering the closest (provable) encloser
701        RFC 5155 section-7.2.1
702        RFC 7129 section-5.5
703     */
704     LOG("Now looking for the closest encloser for "<<qname<<endl);
705 
706     while (found == false && closestEncloser.chopOff()) {
707 
708       for(const auto& v : validrrsets) {
709         if(v.first.second==QType::NSEC3) {
710           for(const auto& r : v.second.records) {
711             LOG("\t"<<r->getZoneRepresentation()<<endl);
712             auto nsec3 = std::dynamic_pointer_cast<NSEC3RecordContent>(r);
713             if (!nsec3) {
714               continue;
715             }
716 
717             const DNSName signer = getSigner(v.second.signatures);
718             if (!v.first.first.isPartOf(signer)) {
719               LOG("Owner "<<v.first.first<<" is not part of the signer "<<signer<<", ignoring"<<endl);
720               continue;
721             }
722 
723             string h = getHashFromNSEC3(closestEncloser, nsec3, cache);
724             if (h.empty()) {
725               return dState::INSECURE;
726             }
727 
728             string beginHash=fromBase32Hex(v.first.first.getRawLabels()[0]);
729 
730             LOG("Comparing "<<toBase32Hex(h)<<" ("<<closestEncloser<<") against "<<toBase32Hex(beginHash)<<endl);
731             if (beginHash == h) {
732               if (qtype != QType::DS && isNSEC3AncestorDelegation(signer, v.first.first, nsec3)) {
733                 LOG("An ancestor delegation NSEC3 RR can only deny the existence of a DS"<<endl);
734                 continue;
735               }
736 
737               LOG("Closest encloser for "<<qname<<" is "<<closestEncloser<<endl);
738               found = true;
739 
740               if (nsec3->isSet(QType::DNAME)) {
741                 /* rfc6672 section 5.3.2: DNAME Bit in NSEC Type Map
742 
743                    In any negative response, the NSEC or NSEC3 [RFC5155] record type
744                    bitmap SHOULD be checked to see that there was no DNAME that could
745                    have been applied.  If the DNAME bit in the type bitmap is set and
746                    the query name is a subdomain of the closest encloser that is
747                    asserted, then DNAME substitution should have been done, but the
748                    substitution has not been done as specified.
749                 */
750                 LOG("\tThe closest encloser NSEC3 has the DNAME bit is set"<<endl);
751                 return dState::NODENIAL;
752               }
753 
754               break;
755             }
756           }
757         }
758         if (found == true) {
759           break;
760         }
761       }
762     }
763   }
764   else {
765     /* RFC 5155 section-7.2.6:
766        "It is not necessary to return an NSEC3 RR that matches the closest encloser,
767        as the existence of this closest encloser is proven by the presence of the
768        expanded wildcard in the response.
769     */
770     found = true;
771     unsigned int closestEncloserLabelsCount = closestEncloser.countLabels();
772     while (wildcardLabelsCount > 0 && closestEncloserLabelsCount > wildcardLabelsCount) {
773       closestEncloser.chopOff();
774       closestEncloserLabelsCount--;
775     }
776   }
777 
778   bool nextCloserFound = false;
779   bool isOptOut = false;
780 
781   if (found == true) {
782     /* now that we have found the closest (provable) encloser,
783        we can construct the next closer (RFC7129 section-5.5) name
784        and look for a NSEC3 RR covering it */
785     unsigned int labelIdx = qname.countLabels() - closestEncloser.countLabels();
786     if (labelIdx >= 1) {
787       DNSName nextCloser(closestEncloser);
788       nextCloser.prependRawLabel(qname.getRawLabel(labelIdx - 1));
789       LOG("Looking for a NSEC3 covering the next closer name "<<nextCloser<<endl);
790 
791       for(const auto& v : validrrsets) {
792         if(v.first.second==QType::NSEC3) {
793           for(const auto& r : v.second.records) {
794             LOG("\t"<<r->getZoneRepresentation()<<endl);
795             auto nsec3 = std::dynamic_pointer_cast<NSEC3RecordContent>(r);
796             if(!nsec3)
797               continue;
798 
799             string h = getHashFromNSEC3(nextCloser, nsec3, cache);
800             if (h.empty()) {
801               return dState::INSECURE;
802             }
803 
804             const DNSName signer = getSigner(v.second.signatures);
805             if (!v.first.first.isPartOf(signer)) {
806               LOG("Owner "<<v.first.first<<" is not part of the signer "<<signer<<", ignoring"<<endl);
807               continue;
808             }
809 
810             string beginHash=fromBase32Hex(v.first.first.getRawLabels()[0]);
811 
812             LOG("Comparing "<<toBase32Hex(h)<<" against "<<toBase32Hex(beginHash)<<" -> "<<toBase32Hex(nsec3->d_nexthash)<<endl);
813             if (isCoveredByNSEC3Hash(h, beginHash, nsec3->d_nexthash)) {
814               LOG("Denies existence of name "<<qname<<"/"<<QType(qtype).toString());
815               nextCloserFound = true;
816 
817               if (nsec3->isOptOut()) {
818                 LOG(" but is opt-out!");
819                 isOptOut = true;
820               }
821 
822               LOG(endl);
823               break;
824             }
825             LOG("Did not cover us ("<<qname<<"), start="<<v.first.first<<", us="<<toBase32Hex(h)<<", end="<<toBase32Hex(nsec3->d_nexthash)<<endl);
826           }
827         }
828         if (nextCloserFound) {
829           break;
830         }
831       }
832     }
833   }
834 
835   if (nextCloserFound) {
836     bool wildcardExists = false;
837     /* RFC 7129 section-5.6 */
838     if (needWildcardProof && !provesNSEC3NoWildCard(closestEncloser, qtype, validrrsets, &wildcardExists, cache)) {
839       if (!isOptOut) {
840         LOG("But the existence of a wildcard is not denied for "<<qname<<"/"<<QType(qtype).toString()<<endl);
841         return dState::NODENIAL;
842       }
843     }
844 
845     if (isOptOut) {
846       return dState::OPTOUT;
847     }
848     else {
849       if (wildcardExists) {
850         return dState::NXQTYPE;
851       }
852       return dState::NXDOMAIN;
853     }
854   }
855 
856   // There were no valid NSEC(3) records
857   return dState::NODENIAL;
858 }
859 
860 /*
861  * Finds all the zone-cuts between begin (longest name) and end (shortest name),
862  * returns them all zone cuts, including end, but (possibly) not begin
863  */
getZoneCuts(const DNSName & begin,const DNSName & end,DNSRecordOracle & dro)864 static const vector<DNSName> getZoneCuts(const DNSName& begin, const DNSName& end, DNSRecordOracle& dro)
865 {
866   vector<DNSName> ret;
867   if(!begin.isPartOf(end))
868     throw PDNSException(end.toLogString() + "is not part of " + begin.toLogString());
869 
870   DNSName qname(end);
871   vector<string> labelsToAdd = begin.makeRelative(end).getRawLabels();
872 
873   // The shortest name is assumed to a zone cut
874   ret.push_back(qname);
875   while(qname != begin) {
876     bool foundCut = false;
877     if (labelsToAdd.empty())
878       break;
879 
880     qname.prependRawLabel(labelsToAdd.back());
881     labelsToAdd.pop_back();
882     auto records = dro.get(qname, (uint16_t)QType::NS);
883     for (const auto& record : records) {
884       if(record.d_type != QType::NS || record.d_name != qname)
885         continue;
886       foundCut = true;
887       break;
888     }
889     if (foundCut)
890       ret.push_back(qname);
891   }
892   return ret;
893 }
894 
isRRSIGNotExpired(const time_t now,const shared_ptr<RRSIGRecordContent> & sig)895 bool isRRSIGNotExpired(const time_t now, const shared_ptr<RRSIGRecordContent>& sig)
896 {
897   // Should use https://www.rfc-editor.org/rfc/rfc4034.txt section 3.1.5
898   return sig->d_sigexpire >= now;
899 }
900 
isRRSIGIncepted(const time_t now,const shared_ptr<RRSIGRecordContent> & sig)901 bool isRRSIGIncepted(const time_t now, const shared_ptr<RRSIGRecordContent>& sig)
902 {
903   // Should use https://www.rfc-editor.org/rfc/rfc4034.txt section 3.1.5
904   return sig->d_siginception - g_signatureInceptionSkew <= now;
905 }
906 
checkSignatureWithKey(time_t now,const shared_ptr<RRSIGRecordContent> sig,const shared_ptr<DNSKEYRecordContent> key,const std::string & msg)907 static bool checkSignatureWithKey(time_t now, const shared_ptr<RRSIGRecordContent> sig, const shared_ptr<DNSKEYRecordContent> key, const std::string& msg)
908 {
909   bool result = false;
910   try {
911     /* rfc4035:
912        - The validator's notion of the current time MUST be less than or equal to the time listed in the RRSIG RR's Expiration field.
913        - The validator's notion of the current time MUST be greater than or equal to the time listed in the RRSIG RR's Inception field.
914     */
915     if (isRRSIGIncepted(now, sig) && isRRSIGNotExpired(now, sig)) {
916       auto dke = DNSCryptoKeyEngine::makeFromPublicKeyString(key->d_algorithm, key->d_key);
917       result = dke->verify(msg, sig->d_signature);
918       LOG("signature by key with tag "<<sig->d_tag<<" and algorithm "<<DNSSECKeeper::algorithm2name(sig->d_algorithm)<<" was " << (result ? "" : "NOT ")<<"valid"<<endl);
919     }
920     else {
921       LOG("Signature is "<<((sig->d_siginception - g_signatureInceptionSkew > now) ? "not yet valid" : "expired")<<" (inception: "<<sig->d_siginception<<", inception skew: "<<g_signatureInceptionSkew<<", expiration: "<<sig->d_sigexpire<<", now: "<<now<<")"<<endl);
922     }
923   }
924   catch (const std::exception& e) {
925     LOG("Could not make a validator for signature: "<<e.what()<<endl);
926   }
927   return result;
928 }
929 
validateWithKeySet(time_t now,const DNSName & name,const sortedRecords_t & toSign,const vector<shared_ptr<RRSIGRecordContent>> & signatures,const skeyset_t & keys,bool validateAllSigs)930 vState validateWithKeySet(time_t now, const DNSName& name, const sortedRecords_t& toSign, const vector<shared_ptr<RRSIGRecordContent> >& signatures, const skeyset_t& keys, bool validateAllSigs)
931 {
932   bool foundKey = false;
933   bool isValid = false;
934   bool allExpired = true;
935   bool noneIncepted = true;
936 
937   for(const auto& signature : signatures) {
938     unsigned int labelCount = name.countLabels();
939     if (signature->d_labels > labelCount) {
940       LOG(name<<": Discarding invalid RRSIG whose label count is "<<signature->d_labels<<" while the RRset owner name has only "<<labelCount<<endl);
941       continue;
942     }
943 
944     auto keysMatchingTag = getByTag(keys, signature->d_tag, signature->d_algorithm);
945 
946     if (keysMatchingTag.empty()) {
947       LOG("No key provided for "<<signature->d_tag<<" and algorithm "<<std::to_string(signature->d_algorithm)<<endl;);
948       continue;
949     }
950 
951     string msg = getMessageForRRSET(name, *signature, toSign, true);
952     for (const auto& key : keysMatchingTag) {
953       bool signIsValid = checkSignatureWithKey(now, signature, key, msg);
954       foundKey = true;
955 
956       if (signIsValid) {
957         isValid = true;
958         LOG("Validated "<<name<<"/"<<DNSRecordContent::NumberToType(signature->d_type)<<endl);
959         //	  cerr<<"valid"<<endl;
960         //	  cerr<<"! validated "<<i->first.first<<"/"<<)<<endl;
961       }
962       else {
963         LOG("signature invalid"<<endl);
964         if (isRRSIGIncepted(now, signature)) {
965           noneIncepted = false;
966         }
967         if (isRRSIGNotExpired(now, signature)) {
968           allExpired = false;
969         }
970       }
971 
972       if (signIsValid && !validateAllSigs) {
973         return vState::Secure;
974       }
975     }
976   }
977 
978   if (isValid) {
979     return vState::Secure;
980   }
981   if (!foundKey) {
982     return vState::BogusNoValidRRSIG;
983   }
984   if (noneIncepted) {
985     return vState::BogusSignatureNotYetValid;
986   }
987   if (allExpired) {
988     return vState::BogusSignatureExpired;
989   }
990 
991   return vState::BogusNoValidRRSIG;
992 }
993 
validateWithKeySet(const cspmap_t & rrsets,cspmap_t & validated,const skeyset_t & keys)994 void validateWithKeySet(const cspmap_t& rrsets, cspmap_t& validated, const skeyset_t& keys)
995 {
996   validated.clear();
997   /*  cerr<<"Validating an rrset with following keys: "<<endl;
998   for(auto& key : keys) {
999     cerr<<"\tTag: "<<key->getTag()<<" -> "<<key->getZoneRepresentation()<<endl;
1000   }
1001   */
1002   time_t now = time(nullptr);
1003   for(auto i=rrsets.cbegin(); i!=rrsets.cend(); i++) {
1004     LOG("validating "<<(i->first.first)<<"/"<<DNSRecordContent::NumberToType(i->first.second)<<" with "<<i->second.signatures.size()<<" sigs"<<endl);
1005     if (validateWithKeySet(now, i->first.first, i->second.records, i->second.signatures, keys, true) == vState::Secure) {
1006       validated[i->first] = i->second;
1007     }
1008   }
1009 }
1010 
1011 // returns vState
1012 // should return vState, zone cut and validated keyset
1013 // i.e. www.7bits.nl -> insecure/7bits.nl/[]
1014 //      www.powerdnssec.org -> secure/powerdnssec.org/[keys]
1015 //      www.dnssec-failed.org -> bogus/dnssec-failed.org/[]
1016 
harvestCSPFromRecs(const vector<DNSRecord> & recs)1017 cspmap_t harvestCSPFromRecs(const vector<DNSRecord>& recs)
1018 {
1019   cspmap_t cspmap;
1020   for(const auto& rec : recs) {
1021     //        cerr<<"res "<<rec.d_name<<"/"<<rec.d_type<<endl;
1022     if(rec.d_type == QType::OPT) continue;
1023 
1024     if(rec.d_type == QType::RRSIG) {
1025       auto rrc = getRR<RRSIGRecordContent>(rec);
1026       if (rrc) {
1027         cspmap[{rec.d_name,rrc->d_type}].signatures.push_back(rrc);
1028       }
1029     }
1030     else {
1031       cspmap[{rec.d_name, rec.d_type}].records.insert(rec.d_content);
1032     }
1033   }
1034   return cspmap;
1035 }
1036 
getTrustAnchor(const map<DNSName,dsmap_t> & anchors,const DNSName & zone,dsmap_t & res)1037 bool getTrustAnchor(const map<DNSName,dsmap_t>& anchors, const DNSName& zone, dsmap_t &res)
1038 {
1039   const auto& it = anchors.find(zone);
1040 
1041   if (it == anchors.cend()) {
1042     return false;
1043   }
1044 
1045   res = it->second;
1046   return true;
1047 }
1048 
haveNegativeTrustAnchor(const map<DNSName,std::string> & negAnchors,const DNSName & zone,std::string & reason)1049 bool haveNegativeTrustAnchor(const map<DNSName,std::string>& negAnchors, const DNSName& zone, std::string& reason)
1050 {
1051   const auto& it = negAnchors.find(zone);
1052 
1053   if (it == negAnchors.cend()) {
1054     return false;
1055   }
1056 
1057   reason = it->second;
1058   return true;
1059 }
1060 
validateDNSKeysAgainstDS(time_t now,const DNSName & zone,const dsmap_t & dsmap,const skeyset_t & tkeys,const sortedRecords_t & toSign,const vector<shared_ptr<RRSIGRecordContent>> & sigs,skeyset_t & validkeys)1061 vState validateDNSKeysAgainstDS(time_t now, const DNSName& zone, const dsmap_t& dsmap, const skeyset_t& tkeys, const sortedRecords_t& toSign, const vector<shared_ptr<RRSIGRecordContent> >& sigs, skeyset_t& validkeys)
1062 {
1063   /*
1064    * Check all DNSKEY records against all DS records and place all DNSKEY records
1065    * that have DS records (that we support the algo for) in the tentative key storage
1066    */
1067   for (const auto& dsrc : dsmap)
1068   {
1069     auto r = getByTag(tkeys, dsrc.d_tag, dsrc.d_algorithm);
1070     // cerr<<"looking at DS with tag "<<dsrc.d_tag<<", algo "<<DNSSECKeeper::algorithm2name(dsrc.d_algorithm)<<", digest "<<std::to_string(dsrc.d_digesttype)<<" for "<<zone<<", got "<<r.size()<<" DNSKEYs for tag"<<endl;
1071 
1072     for (const auto& drc : r)
1073     {
1074       bool isValid = false;
1075       bool dsCreated = false;
1076       DSRecordContent dsrc2;
1077       try {
1078         dsrc2 = makeDSFromDNSKey(zone, *drc, dsrc.d_digesttype);
1079         dsCreated = true;
1080         isValid = dsrc == dsrc2;
1081       }
1082       catch (const std::exception &e) {
1083         LOG("Unable to make DS from DNSKey: "<<e.what()<<endl);
1084       }
1085 
1086       if (isValid) {
1087         LOG("got valid DNSKEY (it matches the DS) with tag "<<dsrc.d_tag<<" and algorithm "<<std::to_string(dsrc.d_algorithm)<<" for "<<zone<<endl);
1088 
1089         validkeys.insert(drc);
1090       }
1091       else {
1092         if (dsCreated) {
1093           LOG("DNSKEY did not match the DS, parent DS: "<<dsrc.getZoneRepresentation() << " ! = "<<dsrc2.getZoneRepresentation()<<endl);
1094         }
1095       }
1096     }
1097   }
1098 
1099   //    cerr<<"got "<<validkeys.size()<<"/"<<tkeys.size()<<" valid/tentative keys"<<endl;
1100   // these counts could be off if we somehow ended up with
1101   // duplicate keys. Should switch to a type that prevents that.
1102   if (validkeys.size() < tkeys.size())
1103   {
1104     // this should mean that we have one or more DS-validated DNSKEYs
1105     // but not a fully validated DNSKEY set, yet
1106     // one of these valid DNSKEYs should be able to validate the
1107     // whole set
1108     for (const auto& sig : sigs)
1109     {
1110       //        cerr<<"got sig for keytag "<<i->d_tag<<" matching "<<getByTag(tkeys, i->d_tag).size()<<" keys of which "<<getByTag(validkeys, i->d_tag).size()<<" valid"<<endl;
1111       auto bytag = getByTag(validkeys, sig->d_tag, sig->d_algorithm);
1112 
1113       if (bytag.empty()) {
1114         continue;
1115       }
1116 
1117       string msg = getMessageForRRSET(zone, *sig, toSign);
1118       for (const auto& key : bytag) {
1119         //          cerr<<"validating : ";
1120         bool signIsValid = checkSignatureWithKey(now, sig, key, msg);
1121 
1122         if (signIsValid)
1123         {
1124           LOG("validation succeeded - whole DNSKEY set is valid"<<endl);
1125           validkeys = tkeys;
1126           break;
1127         }
1128         else {
1129           LOG("Validation did not succeed!"<<endl);
1130         }
1131       }
1132       //        if(validkeys.empty()) cerr<<"did not manage to validate DNSKEY set based on DS-validated KSK, only passing KSK on"<<endl;
1133     }
1134   }
1135 
1136   if (validkeys.size() < tkeys.size()) {
1137     /* so we failed to validate the whole set, let's try to find out why exactly */
1138     bool dnskeyAlgoSupported = false;
1139     bool dsDigestSupported = false;
1140 
1141     for (const auto& dsrc : dsmap)
1142     {
1143       if (DNSCryptoKeyEngine::isAlgorithmSupported(dsrc.d_algorithm)) {
1144         dnskeyAlgoSupported = true;
1145         if (DNSCryptoKeyEngine::isDigestSupported(dsrc.d_digesttype)) {
1146           dsDigestSupported = true;
1147         }
1148       }
1149     }
1150 
1151     if (!dnskeyAlgoSupported) {
1152       return vState::BogusUnsupportedDNSKEYAlgo;
1153     }
1154     if (!dsDigestSupported) {
1155       return vState::BogusUnsupportedDSDigestType;
1156     }
1157 
1158     bool zoneKey = false;
1159     bool notRevoked = false;
1160     bool validProtocol = false;
1161 
1162     for (const auto& key : tkeys) {
1163       if (!isAZoneKey(*key)) {
1164         continue;
1165       }
1166       zoneKey = true;
1167 
1168       if (isRevokedKey(*key)) {
1169         continue;
1170       }
1171       notRevoked = true;
1172 
1173       if (key->d_protocol != 3) {
1174         continue;
1175       }
1176       validProtocol = true;
1177     }
1178 
1179     if (!zoneKey) {
1180       return vState::BogusNoZoneKeyBitSet;
1181     }
1182     if (!notRevoked) {
1183       return vState::BogusRevokedDNSKEY;
1184     }
1185     if (!validProtocol) {
1186       return vState::BogusInvalidDNSKEYProtocol;
1187     }
1188 
1189     return vState::BogusNoValidDNSKEY;
1190   }
1191 
1192   return vState::Secure;
1193 }
1194 
getKeysFor(DNSRecordOracle & dro,const DNSName & zone,skeyset_t & keyset)1195 vState getKeysFor(DNSRecordOracle& dro, const DNSName& zone, skeyset_t& keyset)
1196 {
1197   auto luaLocal = g_luaconfs.getLocal();
1198   const auto anchors = luaLocal->dsAnchors;
1199   if (anchors.empty()) // Nothing to do here
1200     return vState::Insecure;
1201 
1202   // Determine the lowest (i.e. with the most labels) Trust Anchor for zone
1203   DNSName lowestTA(".");
1204   for (auto const &anchor : anchors)
1205     if (zone.isPartOf(anchor.first) && lowestTA.countLabels() < anchor.first.countLabels())
1206       lowestTA = anchor.first;
1207 
1208   // Before searching for the keys, see if we have a Negative Trust Anchor. If
1209   // so, test if the NTA is valid and return an NTA state
1210   const auto negAnchors = luaLocal->negAnchors;
1211 
1212   if (!negAnchors.empty()) {
1213     DNSName lowestNTA;
1214 
1215     for (auto const &negAnchor : negAnchors)
1216       if (zone.isPartOf(negAnchor.first) && lowestNTA.countLabels() <= negAnchor.first.countLabels())
1217         lowestNTA = negAnchor.first;
1218 
1219     if(!lowestNTA.empty()) {
1220       LOG("Found a Negative Trust Anchor for "<<lowestNTA<<", which was added with reason '"<<negAnchors.at(lowestNTA)<<"', ");
1221 
1222       /* RFC 7646 section 2.1 tells us that we SHOULD still validate if there
1223        * is a Trust Anchor below the Negative Trust Anchor for the name we
1224        * attempt validation for. However, section 3 tells us this positive
1225        * Trust Anchor MUST be *below* the name and not the name itself
1226        */
1227       if(lowestTA.countLabels() <= lowestNTA.countLabels()) {
1228         LOG("marking answer Insecure"<<endl);
1229         return vState::NTA; // Not Insecure, this way validateRecords() can shortcut
1230       }
1231       LOG("but a Trust Anchor for "<<lowestTA<<" is configured, continuing validation."<<endl);
1232     }
1233   }
1234 
1235   skeyset_t validkeys;
1236   dsmap_t dsmap;
1237 
1238   dsmap_t* tmp = (dsmap_t*) rplookup(anchors, lowestTA);
1239   if (tmp)
1240     dsmap = *tmp;
1241 
1242   auto zoneCuts = getZoneCuts(zone, lowestTA, dro);
1243 
1244   LOG("Found the following zonecuts:")
1245   for(const auto& zonecut : zoneCuts)
1246     LOG(" => "<<zonecut);
1247   LOG(endl);
1248 
1249   for(auto zoneCutIter = zoneCuts.cbegin(); zoneCutIter != zoneCuts.cend(); ++zoneCutIter)
1250   {
1251     vector<shared_ptr<RRSIGRecordContent> > sigs;
1252     sortedRecords_t toSign;
1253 
1254     skeyset_t tkeys; // tentative keys
1255     validkeys.clear();
1256 
1257     //    cerr<<"got DS for ["<<qname<<"], grabbing DNSKEYs"<<endl;
1258     auto records=dro.get(*zoneCutIter, (uint16_t)QType::DNSKEY);
1259     // this should use harvest perhaps
1260     for(const auto& rec : records) {
1261       if(rec.d_name != *zoneCutIter)
1262         continue;
1263 
1264       if(rec.d_type == QType::RRSIG)
1265       {
1266         auto rrc=getRR<RRSIGRecordContent> (rec);
1267         if(rrc) {
1268           LOG("Got signature: "<<rrc->getZoneRepresentation()<<" with tag "<<rrc->d_tag<<", for type "<<DNSRecordContent::NumberToType(rrc->d_type)<<endl);
1269           if(rrc->d_type != QType::DNSKEY)
1270             continue;
1271           sigs.push_back(rrc);
1272         }
1273       }
1274       else if(rec.d_type == QType::DNSKEY)
1275       {
1276         auto drc=getRR<DNSKEYRecordContent> (rec);
1277         if(drc) {
1278           tkeys.insert(drc);
1279           LOG("Inserting key with tag "<<drc->getTag()<<" and algorithm "<<DNSSECKeeper::algorithm2name(drc->d_algorithm)<<": "<<drc->getZoneRepresentation()<<endl);
1280 
1281           toSign.insert(rec.d_content);
1282         }
1283       }
1284     }
1285     LOG("got "<<tkeys.size()<<" keys and "<<sigs.size()<<" sigs from server"<<endl);
1286 
1287     /*
1288      * Check all DNSKEY records against all DS records and place all DNSKEY records
1289      * that have DS records (that we support the algo for) in the tentative key storage
1290      */
1291     auto state = validateDNSKeysAgainstDS(time(nullptr), *zoneCutIter, dsmap, tkeys, toSign, sigs, validkeys);
1292 
1293     if (validkeys.empty())
1294     {
1295       LOG("ended up with zero valid DNSKEYs, going Bogus"<<endl);
1296       return state;
1297     }
1298     LOG("situation: we have one or more valid DNSKEYs for ["<<*zoneCutIter<<"] (want ["<<zone<<"])"<<endl);
1299 
1300     if (zoneCutIter == zoneCuts.cend()-1) {
1301       LOG("requested keyset found! returning Secure for the keyset"<<endl);
1302       keyset.insert(validkeys.cbegin(), validkeys.cend());
1303       return state;
1304     }
1305 
1306     // We now have the DNSKEYs, use them to validate the DS records at the next zonecut
1307     LOG("next name ["<<*(zoneCutIter+1)<<"], trying to get DS"<<endl);
1308 
1309     dsmap_t tdsmap; // tentative DSes
1310     dsmap.clear();
1311     toSign.clear();
1312 
1313     auto recs=dro.get(*(zoneCutIter+1), QType::DS);
1314 
1315     cspmap_t cspmap=harvestCSPFromRecs(recs);
1316 
1317     cspmap_t validrrsets;
1318     validateWithKeySet(cspmap, validrrsets, validkeys);
1319 
1320     LOG("got "<<cspmap.count(make_pair(*(zoneCutIter+1),QType::DS))<<" records for DS query of which "<<validrrsets.count(make_pair(*(zoneCutIter+1),QType::DS))<<" valid "<<endl);
1321 
1322     auto r = validrrsets.equal_range(make_pair(*(zoneCutIter+1), QType::DS));
1323     if(r.first == r.second) {
1324       LOG("No DS for "<<*(zoneCutIter+1)<<", now look for a secure denial"<<endl);
1325       dState res = getDenial(validrrsets, *(zoneCutIter+1), QType::DS, true, true);
1326       if (res == dState::INSECURE || res == dState::NXDOMAIN)
1327         return vState::BogusInvalidDenial;
1328       if (res == dState::NXQTYPE || res == dState::OPTOUT)
1329         return vState::Insecure;
1330     }
1331 
1332     /*
1333      * Collect all DS records and add them to the dsmap for the next iteration
1334      */
1335     for(auto cspiter =r.first;  cspiter!=r.second; cspiter++) {
1336       for(auto j=cspiter->second.records.cbegin(); j!=cspiter->second.records.cend(); j++)
1337       {
1338         const auto dsrc=std::dynamic_pointer_cast<DSRecordContent>(*j);
1339         if(dsrc) {
1340           dsmap.insert(*dsrc);
1341         }
1342       }
1343     }
1344   }
1345   // There were no zone cuts (aka, we should never get here)
1346   return vState::BogusUnableToGetDNSKEYs;
1347 }
1348 
isSupportedDS(const DSRecordContent & ds)1349 bool isSupportedDS(const DSRecordContent& ds)
1350 {
1351   if (!DNSCryptoKeyEngine::isAlgorithmSupported(ds.d_algorithm)) {
1352     LOG("Discarding DS "<<ds.d_tag<<" because we don't support algorithm number "<<std::to_string(ds.d_algorithm)<<endl);
1353     return false;
1354   }
1355 
1356   if (!DNSCryptoKeyEngine::isDigestSupported(ds.d_digesttype)) {
1357     LOG("Discarding DS "<<ds.d_tag<<" because we don't support digest number "<<std::to_string(ds.d_digesttype)<<endl);
1358     return false;
1359   }
1360 
1361   return true;
1362 }
1363 
getSigner(const std::vector<std::shared_ptr<RRSIGRecordContent>> & signatures)1364 DNSName getSigner(const std::vector<std::shared_ptr<RRSIGRecordContent> >& signatures)
1365 {
1366   for (const auto& sig : signatures) {
1367     if (sig) {
1368       return sig->d_signer;
1369     }
1370   }
1371 
1372   return DNSName();
1373 }
1374 
vStateToString(vState state)1375 const std::string& vStateToString(vState state)
1376 {
1377   static const std::vector<std::string> vStates = {"Indeterminate", "Insecure", "Secure", "NTA", "TA", "Bogus - No valid DNSKEY", "Bogus - Invalid denial", "Bogus - Unable to get DSs", "Bogus - Unable to get DNSKEYs", "Bogus - Self Signed DS", "Bogus - No RRSIG", "Bogus - No valid RRSIG", "Bogus - Missing negative indication", "Bogus - Signature not yet valid", "Bogus - Signature expired", "Bogus - Unsupported DNSKEY algorithm", "Bogus - Unsupported DS digest type", "Bogus - No zone key bit set", "Bogus - Revoked DNSKEY", "Bogus - Invalid DNSKEY Protocol" };
1378   return vStates.at(static_cast<size_t>(state));
1379 }
1380 
operator <<(std::ostream & os,const vState d)1381 std::ostream& operator<<(std::ostream &os, const vState d)
1382 {
1383   os<<vStateToString(d);
1384   return os;
1385 }
1386 
operator <<(std::ostream & os,const dState d)1387 std::ostream& operator<<(std::ostream &os, const dState d)
1388 {
1389   static const std::vector<std::string> dStates = {"no denial", "inconclusive", "nxdomain", "nxqtype", "empty non-terminal", "insecure", "opt-out"};
1390   os<<dStates.at(static_cast<size_t>(d));
1391   return os;
1392 }
1393 
updateDNSSECValidationState(vState & state,const vState stateUpdate)1394 void updateDNSSECValidationState(vState& state, const vState stateUpdate)
1395 {
1396   if (stateUpdate == vState::TA) {
1397     state = vState::Secure;
1398   }
1399   else if (stateUpdate == vState::NTA) {
1400     state = vState::Insecure;
1401   }
1402   else if (vStateIsBogus(stateUpdate)) {
1403     state = stateUpdate;
1404   }
1405   else if (state == vState::Indeterminate) {
1406     state = stateUpdate;
1407   }
1408   else if (stateUpdate == vState::Insecure) {
1409     if (!vStateIsBogus(state)) {
1410       state = vState::Insecure;
1411     }
1412   }
1413 }
1414