]> git.ipfire.org Git - thirdparty/pdns.git/blob - pdns/validate.cc
Avoid throwing an exception in Logger::log().
[thirdparty/pdns.git] / pdns / validate.cc
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
14 const char *dStates[]={"nodata", "nxdomain", "nxqtype", "empty non-terminal", "insecure", "opt-out"};
15 const char *vStates[]={"Indeterminate", "Bogus", "Insecure", "Secure", "NTA", "TA"};
16
17 static vector<shared_ptr<DNSKEYRecordContent > > getByTag(const skeyset_t& keys, uint16_t tag, uint8_t algorithm)
18 {
19 vector<shared_ptr<DNSKEYRecordContent>> ret;
20 for(const auto& key : keys)
21 if(key->d_protocol == 3 && key->getTag() == tag && key->d_algorithm == algorithm)
22 ret.push_back(key);
23 return ret;
24 }
25
26 static bool isCoveredByNSEC3Hash(const std::string& h, const std::string& beginHash, const std::string& nextHash)
27 {
28 return ((beginHash < h && h < nextHash) || // no wrap BEGINNING --- HASH -- END
29 (nextHash > h && beginHash > nextHash) || // wrap HASH --- END --- BEGINNING
30 (nextHash < beginHash && beginHash < h) || // wrap other case END --- BEGINNING --- HASH
31 (beginHash == nextHash && h != beginHash)); // "we have only 1 NSEC3 record, LOL!"
32 }
33
34 static bool isCoveredByNSEC(const DNSName& name, const DNSName& begin, const DNSName& next)
35 {
36 return ((begin.canonCompare(name) && name.canonCompare(next)) || // no wrap BEGINNING --- NAME --- NEXT
37 (name.canonCompare(next) && next.canonCompare(begin)) || // wrap NAME --- NEXT --- BEGINNING
38 (next.canonCompare(begin) && begin.canonCompare(name)) || // wrap other case NEXT --- BEGINNING --- NAME
39 (begin == next && name != begin)); // "we have only 1 NSEC record, LOL!"
40 }
41
42 static bool nsecProvesENT(const DNSName& name, const DNSName& begin, const DNSName& next)
43 {
44 /* if name is an ENT:
45 - begin < name
46 - next is a child of name
47 */
48 return begin.canonCompare(name) && next != name && next.isPartOf(name);
49 }
50
51 static std::string getHashFromNSEC3(const DNSName& qname, const std::shared_ptr<NSEC3RecordContent>& nsec3)
52 {
53 std::string result;
54
55 if (g_maxNSEC3Iterations && nsec3->d_iterations > g_maxNSEC3Iterations) {
56 return result;
57 }
58
59 return hashQNameWithSalt(nsec3->d_salt, nsec3->d_iterations, qname);
60 }
61
62 /* There is no delegation at this exact point if:
63 - the name exists but the NS type is not set
64 - the name does not exist
65 One exception, if the name is covered by an opt-out NSEC3
66 it doesn't prove that an insecure delegation doesn't exist.
67 */
68 bool denialProvesNoDelegation(const DNSName& zone, const std::vector<DNSRecord>& dsrecords)
69 {
70 for (const auto& record : dsrecords) {
71 if (record.d_type == QType::NSEC) {
72 const auto nsec = getRR<NSECRecordContent>(record);
73 if (!nsec) {
74 continue;
75 }
76
77 if (record.d_name == zone) {
78 return !nsec->isSet(QType::NS);
79 }
80
81 if (isCoveredByNSEC(zone, record.d_name, nsec->d_next)) {
82 return true;
83 }
84 }
85 else if (record.d_type == QType::NSEC3) {
86 const auto nsec3 = getRR<NSEC3RecordContent>(record);
87 if (!nsec3) {
88 continue;
89 }
90
91 const string h = getHashFromNSEC3(zone, nsec3);
92 if (h.empty()) {
93 return false;
94 }
95
96 const string beginHash = fromBase32Hex(record.d_name.getRawLabels()[0]);
97 if (beginHash == h) {
98 return !nsec3->isSet(QType::NS);
99 }
100
101 if (isCoveredByNSEC3Hash(h, beginHash, nsec3->d_nexthash)) {
102 return !(nsec3->d_flags & 1);
103 }
104 }
105 }
106
107 return false;
108 }
109
110 /* RFC 4035 section-5.3.4:
111 "If the number of labels in an RRset's owner name is greater than the
112 Labels field of the covering RRSIG RR, then the RRset and its
113 covering RRSIG RR were created as a result of wildcard expansion."
114 */
115 bool isWildcardExpanded(unsigned int labelCount, const std::shared_ptr<RRSIGRecordContent>& sign)
116 {
117 if (sign && sign->d_labels < labelCount) {
118 return true;
119 }
120
121 return false;
122 }
123
124 static bool isWildcardExpanded(const DNSName& owner, const std::vector<std::shared_ptr<RRSIGRecordContent> >& signatures)
125 {
126 if (signatures.empty()) {
127 return false;
128 }
129
130 const auto& sign = signatures.at(0);
131 unsigned int labelsCount = owner.countLabels();
132 return isWildcardExpanded(labelsCount, sign);
133 }
134
135 bool isWildcardExpandedOntoItself(const DNSName& owner, unsigned int labelCount, const std::shared_ptr<RRSIGRecordContent>& sign)
136 {
137 if (owner.isWildcard() && (labelCount - 1) == sign->d_labels) {
138 /* this is a wildcard alright, but it has not been expanded */
139 return true;
140 }
141 return false;
142 }
143
144 static bool isWildcardExpandedOntoItself(const DNSName& owner, const std::vector<std::shared_ptr<RRSIGRecordContent> >& signatures)
145 {
146 if (signatures.empty()) {
147 return false;
148 }
149
150 const auto& sign = signatures.at(0);
151 unsigned int labelsCount = owner.countLabels();
152 return isWildcardExpandedOntoItself(owner, labelsCount, sign);
153 }
154
155 /* if this is a wildcard NSEC, the owner name has been modified
156 to match the name. Make sure we use the original '*' form. */
157 static DNSName getNSECOwnerName(const DNSName& initialOwner, const std::vector<std::shared_ptr<RRSIGRecordContent> >& signatures)
158 {
159 DNSName result = initialOwner;
160
161 if (signatures.empty()) {
162 return result;
163 }
164
165 const auto& sign = signatures.at(0);
166 unsigned int labelsCount = initialOwner.countLabels();
167 if (sign && sign->d_labels < labelsCount) {
168 do {
169 result.chopOff();
170 labelsCount--;
171 }
172 while (sign->d_labels < labelsCount);
173
174 result = g_wildcarddnsname + result;
175 }
176
177 return result;
178 }
179
180 static bool isNSECAncestorDelegation(const DNSName& signer, const DNSName& owner, const std::shared_ptr<NSECRecordContent>& nsec)
181 {
182 return nsec->isSet(QType::NS) &&
183 !nsec->isSet(QType::SOA) &&
184 signer.countLabels() < owner.countLabels();
185 }
186
187 static bool isNSEC3AncestorDelegation(const DNSName& signer, const DNSName& owner, const std::shared_ptr<NSEC3RecordContent>& nsec3)
188 {
189 return nsec3->isSet(QType::NS) &&
190 !nsec3->isSet(QType::SOA) &&
191 signer.countLabels() < owner.countLabels();
192 }
193
194 static bool provesNoDataWildCard(const DNSName& qname, const uint16_t qtype, const cspmap_t& validrrsets)
195 {
196 LOG("Trying to prove that there is no data in wildcard for "<<qname<<"/"<<QType(qtype).getName()<<endl);
197 for (const auto& v : validrrsets) {
198 LOG("Do have: "<<v.first.first<<"/"<<DNSRecordContent::NumberToType(v.first.second)<<endl);
199 if (v.first.second == QType::NSEC) {
200 for (const auto& r : v.second.records) {
201 LOG("\t"<<r->getZoneRepresentation()<<endl);
202 auto nsec = std::dynamic_pointer_cast<NSECRecordContent>(r);
203 if (!nsec) {
204 continue;
205 }
206
207 if (!v.first.first.isWildcard()) {
208 continue;
209 }
210 DNSName wildcard = getNSECOwnerName(v.first.first, v.second.signatures);
211 if (qname.countLabels() < wildcard.countLabels()) {
212 continue;
213 }
214
215 wildcard.chopOff();
216
217 if (qname.isPartOf(wildcard)) {
218 LOG("\tWildcard matches");
219 if (qtype == 0 || !nsec->isSet(qtype)) {
220 LOG(" and proves that the type did not exist"<<endl);
221 return true;
222 }
223 LOG(" BUT the type did exist!"<<endl);
224 return false;
225 }
226 }
227 }
228 }
229
230 return false;
231 }
232
233 /*
234 This function checks whether the non-existence of a wildcard covering qname|qtype
235 is proven by the NSEC records in validrrsets.
236 */
237 static bool provesNoWildCard(const DNSName& qname, const uint16_t qtype, const cspmap_t & validrrsets)
238 {
239 LOG("Trying to prove that there is no wildcard for "<<qname<<"/"<<QType(qtype).getName()<<endl);
240 for (const auto& v : validrrsets) {
241 LOG("Do have: "<<v.first.first<<"/"<<DNSRecordContent::NumberToType(v.first.second)<<endl);
242 if (v.first.second == QType::NSEC) {
243 for (const auto& r : v.second.records) {
244 LOG("\t"<<r->getZoneRepresentation()<<endl);
245 auto nsec = std::dynamic_pointer_cast<NSECRecordContent>(r);
246 if (!nsec) {
247 continue;
248 }
249
250 const DNSName owner = getNSECOwnerName(v.first.first, v.second.signatures);
251 /*
252 A NSEC can only prove the non-existence of a wildcard with at least the same
253 number of labels than the intersection of its owner name and next name.
254 */
255 const DNSName commonLabels = owner.getCommonLabels(nsec->d_next);
256 unsigned int commonLabelsCount = commonLabels.countLabels();
257
258 DNSName wildcard(qname);
259 unsigned int wildcardLabelsCount = wildcard.countLabels();
260 while (wildcard.chopOff() && wildcardLabelsCount >= commonLabelsCount) {
261 DNSName target = g_wildcarddnsname + wildcard;
262
263 LOG("Comparing owner: "<<owner<<" with target: "<<target<<endl);
264
265 if (isCoveredByNSEC(target, owner, nsec->d_next)) {
266 LOG("\tWildcard is covered"<<endl);
267 return true;
268 }
269 }
270 }
271 }
272 }
273
274 return false;
275 }
276
277 /*
278 This function checks whether the non-existence of a wildcard covering qname|qtype
279 is proven by the NSEC3 records in validrrsets.
280 If `wildcardExists` is not NULL, if will be set to true if a wildcard exists
281 for this qname but doesn't have this qtype.
282 */
283 static bool provesNSEC3NoWildCard(DNSName wildcard, uint16_t const qtype, const cspmap_t & validrrsets, bool * wildcardExists=nullptr)
284 {
285 wildcard = g_wildcarddnsname + wildcard;
286 LOG("Trying to prove that there is no wildcard for "<<wildcard<<"/"<<QType(qtype).getName()<<endl);
287
288 for (const auto& v : validrrsets) {
289 LOG("Do have: "<<v.first.first<<"/"<<DNSRecordContent::NumberToType(v.first.second)<<endl);
290 if (v.first.second == QType::NSEC3) {
291 for (const auto& r : v.second.records) {
292 LOG("\t"<<r->getZoneRepresentation()<<endl);
293 auto nsec3 = std::dynamic_pointer_cast<NSEC3RecordContent>(r);
294 if (!nsec3) {
295 continue;
296 }
297
298 const DNSName signer = getSigner(v.second.signatures);
299 if (!v.first.first.isPartOf(signer))
300 continue;
301
302 string h = getHashFromNSEC3(wildcard, nsec3);
303 if (h.empty()) {
304 return false;
305 }
306 LOG("\tWildcard hash: "<<toBase32Hex(h)<<endl);
307 string beginHash=fromBase32Hex(v.first.first.getRawLabels()[0]);
308 LOG("\tNSEC3 hash: "<<toBase32Hex(beginHash)<<" -> "<<toBase32Hex(nsec3->d_nexthash)<<endl);
309
310 if (beginHash == h) {
311 LOG("\tWildcard hash matches");
312 if (wildcardExists) {
313 *wildcardExists = true;
314 }
315 if (qtype == 0 || !nsec3->isSet(qtype)) {
316 LOG(" and proves that the type did not exist"<<endl);
317 return true;
318 }
319 LOG(" BUT the type did exist!"<<endl);
320 return false;
321 }
322
323 if (isCoveredByNSEC3Hash(h, beginHash, nsec3->d_nexthash)) {
324 LOG("\tWildcard hash is covered"<<endl);
325 return true;
326 }
327 }
328 }
329 }
330
331 return false;
332 }
333
334 /*
335 This function checks whether the existence of qname|qtype is denied by the NSEC and NSEC3
336 in validrrsets.
337 - If `referralToUnsigned` is true and qtype is QType::DS, this functions returns NODATA
338 if a NSEC or NSEC3 proves that the name exists but no NS type exists, as specified in RFC 5155 section 8.9.
339 - If `wantsNoDataProof` is set but a NSEC proves that the whole name does not exist, the function will return
340 NXQTYPE is the name is proven to be ENT and NXDOMAIN otherwise.
341 - If `needWildcardProof` is false, the proof that a wildcard covering this qname|qtype is not checked. It is
342 useful when we have a positive answer synthesized from a wildcard and we only need to prove that the exact
343 name does not exist.
344 */
345 dState getDenial(const cspmap_t &validrrsets, const DNSName& qname, const uint16_t qtype, bool referralToUnsigned, bool wantsNoDataProof, bool needWildcardProof, unsigned int wildcardLabelsCount)
346 {
347 bool nsec3Seen = false;
348 if (!needWildcardProof && wildcardLabelsCount == 0) {
349 throw PDNSException("Invalid wildcard labels count for the validation of a positive answer synthesized from a wildcard");
350 }
351
352 for(const auto& v : validrrsets) {
353 LOG("Do have: "<<v.first.first<<"/"<<DNSRecordContent::NumberToType(v.first.second)<<endl);
354
355 if(v.first.second==QType::NSEC) {
356 for(const auto& r : v.second.records) {
357 LOG("\t"<<r->getZoneRepresentation()<<endl);
358 auto nsec = std::dynamic_pointer_cast<NSECRecordContent>(r);
359 if(!nsec)
360 continue;
361
362 const DNSName signer = getSigner(v.second.signatures);
363 if (!v.first.first.isPartOf(signer))
364 continue;
365
366 const DNSName owner = getNSECOwnerName(v.first.first, v.second.signatures);
367 /* RFC 6840 section 4.1 "Clarifications on Nonexistence Proofs":
368 Ancestor delegation NSEC or NSEC3 RRs MUST NOT be used to assume
369 nonexistence of any RRs below that zone cut, which include all RRs at
370 that (original) owner name other than DS RRs, and all RRs below that
371 owner name regardless of type.
372 */
373 if (qtype != QType::DS && (qname == owner || qname.isPartOf(owner)) && isNSECAncestorDelegation(signer, owner, nsec)) {
374 LOG("type is "<<QType(qtype).getName()<<", NS is "<<std::to_string(nsec->isSet(QType::NS))<<", SOA is "<<std::to_string(nsec->isSet(QType::SOA))<<", signer is "<<signer<<", owner name is "<<owner<<endl);
375 /* this is an "ancestor delegation" NSEC RR */
376 LOG("An ancestor delegation NSEC RR can only deny the existence of a DS"<<endl);
377 return NODATA;
378 }
379
380 /* check if the type is denied */
381 if(qname == owner) {
382 if (nsec->isSet(qtype)) {
383 LOG("Does _not_ deny existence of type "<<QType(qtype).getName()<<endl);
384 return NODATA;
385 }
386
387 LOG("Denies existence of type "<<QType(qtype).getName()<<endl);
388
389 /* RFC 6840 section 4.3 */
390 if (nsec->isSet(QType::CNAME)) {
391 LOG("However a CNAME exists"<<endl);
392 return NODATA;
393 }
394
395 /*
396 * RFC 4035 Section 2.3:
397 * The bitmap for the NSEC RR at a delegation point requires special
398 * attention. Bits corresponding to the delegation NS RRset and any
399 * RRsets for which the parent zone has authoritative data MUST be set
400 */
401 if (referralToUnsigned && qtype == QType::DS && !nsec->isSet(QType::NS)) {
402 LOG("However, no NS record exists at this level!"<<endl);
403 return NODATA;
404 }
405
406 /* we know that the name exists (but this qtype doesn't) so except
407 if the answer was generated by a wildcard expansion, no wildcard
408 could have matched (rfc4035 section 5.4 bullet 1) */
409 if (!isWildcardExpanded(owner, v.second.signatures) || isWildcardExpandedOntoItself(owner, v.second.signatures)) {
410 needWildcardProof = false;
411 }
412
413 if (!needWildcardProof || provesNoWildCard(qname, qtype, validrrsets)) {
414 return NXQTYPE;
415 }
416
417 LOG("But the existence of a wildcard is not denied for "<<qname<<"/"<<endl);
418 return NODATA;
419 }
420
421 /* check if the whole NAME is denied existing */
422 if(isCoveredByNSEC(qname, owner, nsec->d_next)) {
423 LOG(qname<<" is covered ");
424 /* if the name is an ENT and we received a NODATA answer,
425 we are fine with a NSEC proving that the name does not exist. */
426 if (wantsNoDataProof && nsecProvesENT(qname, owner, nsec->d_next)) {
427 LOG("Denies existence of type "<<qname<<"/"<<QType(qtype).getName()<<" by proving that "<<qname<<" is an ENT"<<endl);
428 return NXQTYPE;
429 }
430
431 if (!needWildcardProof) {
432 LOG("and we did not need a wildcard proof"<<endl);
433 return NXDOMAIN;
434 }
435
436 LOG("but we do need a wildcard proof so ");
437 if (wantsNoDataProof) {
438 LOG("looking for NODATA proof"<<endl);
439 if (provesNoDataWildCard(qname, qtype, validrrsets)) {
440 return NXQTYPE;
441 }
442 }
443 else {
444 LOG("looking for NO wildcard proof"<<endl);
445 if (provesNoWildCard(qname, qtype, validrrsets)) {
446 return NXDOMAIN;
447 }
448 }
449
450 LOG("But the existence of a wildcard is not denied for "<<qname<<"/"<<QType(qtype).getName()<<endl);
451 return NODATA;
452 }
453
454 LOG("Did not deny existence of "<<QType(qtype).getName()<<", "<<owner<<"?="<<qname<<", "<<nsec->isSet(qtype)<<", next: "<<nsec->d_next<<endl);
455 }
456 } else if(v.first.second==QType::NSEC3) {
457 for(const auto& r : v.second.records) {
458 LOG("\t"<<r->getZoneRepresentation()<<endl);
459 auto nsec3 = std::dynamic_pointer_cast<NSEC3RecordContent>(r);
460 if(!nsec3)
461 continue;
462
463 const DNSName signer = getSigner(v.second.signatures);
464 if (!v.first.first.isPartOf(signer)) {
465 LOG("Owner "<<v.first.first<<" is not part of the signer "<<signer<<", ignoring"<<endl);
466 continue;
467 }
468
469 string h = getHashFromNSEC3(qname, nsec3);
470 if (h.empty()) {
471 LOG("Unsupported hash, ignoring"<<endl);
472 return INSECURE;
473 }
474
475 nsec3Seen = true;
476
477 // cerr<<"Salt length: "<<nsec3->d_salt.length()<<", iterations: "<<nsec3->d_iterations<<", hashed: "<<qname<<endl;
478 LOG("\tquery hash: "<<toBase32Hex(h)<<endl);
479 string beginHash=fromBase32Hex(v.first.first.getRawLabels()[0]);
480
481 /* RFC 6840 section 4.1 "Clarifications on Nonexistence Proofs":
482 Ancestor delegation NSEC or NSEC3 RRs MUST NOT be used to assume
483 nonexistence of any RRs below that zone cut, which include all RRs at
484 that (original) owner name other than DS RRs, and all RRs below that
485 owner name regardless of type.
486 */
487 if (qtype != QType::DS && beginHash == h && isNSEC3AncestorDelegation(signer, v.first.first, nsec3)) {
488 LOG("type is "<<QType(qtype).getName()<<", NS is "<<std::to_string(nsec3->isSet(QType::NS))<<", SOA is "<<std::to_string(nsec3->isSet(QType::SOA))<<", signer is "<<signer<<", owner name is "<<v.first.first<<endl);
489 /* this is an "ancestor delegation" NSEC3 RR */
490 LOG("An ancestor delegation NSEC3 RR can only deny the existence of a DS"<<endl);
491 return NODATA;
492 }
493
494 // If the name exists, check if the qtype is denied
495 if(beginHash == h) {
496 if (nsec3->isSet(qtype)) {
497 LOG("Does _not_ deny existence of type "<<QType(qtype).getName()<<" for name "<<qname<<" (not opt-out)."<<endl);
498 return NODATA;
499 }
500
501 LOG("Denies existence of type "<<QType(qtype).getName()<<" for name "<<qname<<" (not opt-out)."<<endl);
502
503 /* RFC 6840 section 4.3 */
504 if (nsec3->isSet(QType::CNAME)) {
505 LOG("However a CNAME exists"<<endl);
506 return NODATA;
507 }
508
509 /*
510 * RFC 5155 section 8.9:
511 * If there is an NSEC3 RR present in the response that matches the
512 * delegation name, then the validator MUST ensure that the NS bit is
513 * set and that the DS bit is not set in the Type Bit Maps field of the
514 * NSEC3 RR.
515 */
516 if (referralToUnsigned && qtype == QType::DS && !nsec3->isSet(QType::NS)) {
517 LOG("However, no NS record exists at this level!"<<endl);
518 return NODATA;
519 }
520
521 return NXQTYPE;
522 }
523 }
524 }
525 }
526
527 /* if we have no NSEC3 records, we are done */
528 if (!nsec3Seen) {
529 return NODATA;
530 }
531
532 DNSName closestEncloser(qname);
533 bool found = false;
534
535 if (needWildcardProof) {
536 /* We now need to look for a NSEC3 covering the closest (provable) encloser
537 RFC 5155 section-7.2.1
538 RFC 7129 section-5.5
539 */
540 LOG("Now looking for the closest encloser for "<<qname<<endl);
541
542 while (found == false && closestEncloser.chopOff()) {
543 for(const auto& v : validrrsets) {
544 if(v.first.second==QType::NSEC3) {
545 for(const auto& r : v.second.records) {
546 LOG("\t"<<r->getZoneRepresentation()<<endl);
547 auto nsec3 = std::dynamic_pointer_cast<NSEC3RecordContent>(r);
548 if(!nsec3)
549 continue;
550
551 const DNSName signer = getSigner(v.second.signatures);
552 if (!v.first.first.isPartOf(signer)) {
553 LOG("Owner "<<v.first.first<<" is not part of the signer "<<signer<<", ignoring"<<endl);
554 continue;
555 }
556
557 string h = getHashFromNSEC3(closestEncloser, nsec3);
558 if (h.empty()) {
559 return INSECURE;
560 }
561
562 string beginHash=fromBase32Hex(v.first.first.getRawLabels()[0]);
563
564 LOG("Comparing "<<toBase32Hex(h)<<" ("<<closestEncloser<<") against "<<toBase32Hex(beginHash)<<endl);
565 if(beginHash == h) {
566 if (qtype != QType::DS && isNSEC3AncestorDelegation(signer, v.first.first, nsec3)) {
567 LOG("An ancestor delegation NSEC3 RR can only deny the existence of a DS"<<endl);
568 continue;
569 }
570
571 LOG("Closest encloser for "<<qname<<" is "<<closestEncloser<<endl);
572 found = true;
573 break;
574 }
575 }
576 }
577 if (found == true) {
578 break;
579 }
580 }
581 }
582 }
583 else {
584 /* RFC 5155 section-7.2.6:
585 "It is not necessary to return an NSEC3 RR that matches the closest encloser,
586 as the existence of this closest encloser is proven by the presence of the
587 expanded wildcard in the response.
588 */
589 found = true;
590 unsigned int closestEncloserLabelsCount = closestEncloser.countLabels();
591 while (wildcardLabelsCount > 0 && closestEncloserLabelsCount > wildcardLabelsCount) {
592 closestEncloser.chopOff();
593 closestEncloserLabelsCount--;
594 }
595 }
596
597 bool nextCloserFound = false;
598 bool isOptOut = false;
599
600 if (found == true) {
601 /* now that we have found the closest (provable) encloser,
602 we can construct the next closer (RFC7129 section-5.5) name
603 and look for a NSEC3 RR covering it */
604 unsigned int labelIdx = qname.countLabels() - closestEncloser.countLabels();
605 if (labelIdx >= 1) {
606 DNSName nextCloser(closestEncloser);
607 nextCloser.prependRawLabel(qname.getRawLabel(labelIdx - 1));
608 LOG("Looking for a NSEC3 covering the next closer name "<<nextCloser<<endl);
609
610 for(const auto& v : validrrsets) {
611 if(v.first.second==QType::NSEC3) {
612 for(const auto& r : v.second.records) {
613 LOG("\t"<<r->getZoneRepresentation()<<endl);
614 auto nsec3 = std::dynamic_pointer_cast<NSEC3RecordContent>(r);
615 if(!nsec3)
616 continue;
617
618 string h = getHashFromNSEC3(nextCloser, nsec3);
619 if (h.empty()) {
620 return INSECURE;
621 }
622
623 string beginHash=fromBase32Hex(v.first.first.getRawLabels()[0]);
624
625 LOG("Comparing "<<toBase32Hex(h)<<" against "<<toBase32Hex(beginHash)<<" -> "<<toBase32Hex(nsec3->d_nexthash)<<endl);
626 if(isCoveredByNSEC3Hash(h, beginHash, nsec3->d_nexthash)) {
627 LOG("Denies existence of name "<<qname<<"/"<<QType(qtype).getName());
628 nextCloserFound = true;
629
630 if ((qtype == QType::DS || qtype == 0) && nsec3->d_flags & 1) {
631 LOG(" but is opt-out!");
632 isOptOut = true;
633 }
634 LOG(endl);
635 break;
636 }
637 LOG("Did not cover us ("<<qname<<"), start="<<v.first.first<<", us="<<toBase32Hex(h)<<", end="<<toBase32Hex(nsec3->d_nexthash)<<endl);
638 }
639 }
640 if (nextCloserFound) {
641 break;
642 }
643 }
644 }
645 }
646
647 if (nextCloserFound) {
648 bool wildcardExists = false;
649 /* RFC 7129 section-5.6 */
650 if (needWildcardProof && !provesNSEC3NoWildCard(closestEncloser, qtype, validrrsets, &wildcardExists)) {
651 if (!isOptOut) {
652 LOG("But the existence of a wildcard is not denied for "<<qname<<"/"<<QType(qtype).getName()<<endl);
653 return NODATA;
654 }
655 }
656
657 if (isOptOut) {
658 return OPTOUT;
659 }
660 else {
661 if (wildcardExists) {
662 return NXQTYPE;
663 }
664 return NXDOMAIN;
665 }
666 }
667
668 // There were no valid NSEC(3) records
669 // XXX maybe this should be INSECURE... it depends on the semantics of this function
670 return NODATA;
671 }
672
673 /*
674 * Finds all the zone-cuts between begin (longest name) and end (shortest name),
675 * returns them all zone cuts, including end, but (possibly) not begin
676 */
677 static const vector<DNSName> getZoneCuts(const DNSName& begin, const DNSName& end, DNSRecordOracle& dro)
678 {
679 vector<DNSName> ret;
680 if(!begin.isPartOf(end))
681 throw PDNSException(end.toLogString() + "is not part of " + begin.toLogString());
682
683 DNSName qname(end);
684 vector<string> labelsToAdd = begin.makeRelative(end).getRawLabels();
685
686 // The shortest name is assumed to a zone cut
687 ret.push_back(qname);
688 while(qname != begin) {
689 bool foundCut = false;
690 if (labelsToAdd.empty())
691 break;
692
693 qname.prependRawLabel(labelsToAdd.back());
694 labelsToAdd.pop_back();
695 auto records = dro.get(qname, (uint16_t)QType::NS);
696 for (const auto& record : records) {
697 if(record.d_type != QType::NS || record.d_name != qname)
698 continue;
699 foundCut = true;
700 break;
701 }
702 if (foundCut)
703 ret.push_back(qname);
704 }
705 return ret;
706 }
707
708 bool isRRSIGNotExpired(const time_t now, const shared_ptr<RRSIGRecordContent> sig)
709 {
710 return sig->d_siginception - g_signatureInceptionSkew <= now && sig->d_sigexpire >= now;
711 }
712
713 static bool checkSignatureWithKey(time_t now, const shared_ptr<RRSIGRecordContent> sig, const shared_ptr<DNSKEYRecordContent> key, const std::string& msg)
714 {
715 bool result = false;
716 try {
717 /* rfc4035:
718 - 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.
719 - 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.
720 */
721 if(isRRSIGNotExpired(now, sig)) {
722 std::shared_ptr<DNSCryptoKeyEngine> dke = shared_ptr<DNSCryptoKeyEngine>(DNSCryptoKeyEngine::makeFromPublicKeyString(key->d_algorithm, key->d_key));
723 result = dke->verify(msg, sig->d_signature);
724 LOG("signature by key with tag "<<sig->d_tag<<" and algorithm "<<DNSSECKeeper::algorithm2name(sig->d_algorithm)<<" was " << (result ? "" : "NOT ")<<"valid"<<endl);
725 }
726 else {
727 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);
728 }
729 }
730 catch(const std::exception& e) {
731 LOG("Could not make a validator for signature: "<<e.what()<<endl);
732 }
733 return result;
734 }
735
736 bool validateWithKeySet(time_t now, const DNSName& name, const sortedRecords_t& toSign, const vector<shared_ptr<RRSIGRecordContent> >& signatures, const skeyset_t& keys, bool validateAllSigs)
737 {
738 bool isValid = false;
739
740 for(const auto& signature : signatures) {
741 unsigned int labelCount = name.countLabels();
742 if (signature->d_labels > labelCount) {
743 LOG(name<<": Discarding invalid RRSIG whose label count is "<<signature->d_labels<<" while the RRset owner name has only "<<labelCount<<endl);
744 }
745
746 auto r = getByTag(keys, signature->d_tag, signature->d_algorithm);
747
748 if(r.empty()) {
749 LOG("No key provided for "<<signature->d_tag<<" and algorithm "<<std::to_string(signature->d_algorithm)<<endl;);
750 continue;
751 }
752
753 string msg=getMessageForRRSET(name, *signature, toSign, true);
754 for(const auto& l : r) {
755 bool signIsValid = checkSignatureWithKey(now, signature, l, msg);
756 if(signIsValid) {
757 isValid = true;
758 LOG("Validated "<<name<<"/"<<DNSRecordContent::NumberToType(signature->d_type)<<endl);
759 // cerr<<"valid"<<endl;
760 // cerr<<"! validated "<<i->first.first<<"/"<<)<<endl;
761 }
762 else {
763 LOG("signature invalid"<<endl);
764 }
765 if (signIsValid && !validateAllSigs) {
766 return true;
767 }
768 }
769 }
770
771 return isValid;
772 }
773
774 void validateWithKeySet(const cspmap_t& rrsets, cspmap_t& validated, const skeyset_t& keys)
775 {
776 validated.clear();
777 /* cerr<<"Validating an rrset with following keys: "<<endl;
778 for(auto& key : keys) {
779 cerr<<"\tTag: "<<key->getTag()<<" -> "<<key->getZoneRepresentation()<<endl;
780 }
781 */
782 time_t now = time(nullptr);
783 for(auto i=rrsets.cbegin(); i!=rrsets.cend(); i++) {
784 LOG("validating "<<(i->first.first)<<"/"<<DNSRecordContent::NumberToType(i->first.second)<<" with "<<i->second.signatures.size()<<" sigs"<<endl);
785 if (validateWithKeySet(now, i->first.first, i->second.records, i->second.signatures, keys, true)) {
786 validated[i->first] = i->second;
787 }
788 }
789 }
790
791 // returns vState
792 // should return vState, zone cut and validated keyset
793 // i.e. www.7bits.nl -> insecure/7bits.nl/[]
794 // www.powerdnssec.org -> secure/powerdnssec.org/[keys]
795 // www.dnssec-failed.org -> bogus/dnssec-failed.org/[]
796
797 cspmap_t harvestCSPFromRecs(const vector<DNSRecord>& recs)
798 {
799 cspmap_t cspmap;
800 for(const auto& rec : recs) {
801 // cerr<<"res "<<rec.d_name<<"/"<<rec.d_type<<endl;
802 if(rec.d_type == QType::OPT) continue;
803
804 if(rec.d_type == QType::RRSIG) {
805 auto rrc = getRR<RRSIGRecordContent>(rec);
806 if (rrc) {
807 cspmap[{rec.d_name,rrc->d_type}].signatures.push_back(rrc);
808 }
809 }
810 else {
811 cspmap[{rec.d_name, rec.d_type}].records.insert(rec.d_content);
812 }
813 }
814 return cspmap;
815 }
816
817 bool getTrustAnchor(const map<DNSName,dsmap_t>& anchors, const DNSName& zone, dsmap_t &res)
818 {
819 const auto& it = anchors.find(zone);
820
821 if (it == anchors.cend()) {
822 return false;
823 }
824
825 res = it->second;
826 return true;
827 }
828
829 bool haveNegativeTrustAnchor(const map<DNSName,std::string>& negAnchors, const DNSName& zone, std::string& reason)
830 {
831 const auto& it = negAnchors.find(zone);
832
833 if (it == negAnchors.cend()) {
834 return false;
835 }
836
837 reason = it->second;
838 return true;
839 }
840
841 void 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)
842 {
843 /*
844 * Check all DNSKEY records against all DS records and place all DNSKEY records
845 * that have DS records (that we support the algo for) in the tentative key storage
846 */
847 for(auto const& dsrc : dsmap)
848 {
849 auto r = getByTag(tkeys, dsrc.d_tag, dsrc.d_algorithm);
850 // 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;
851
852 for(const auto& drc : r)
853 {
854 bool isValid = false;
855 bool dsCreated = false;
856 DSRecordContent dsrc2;
857 try {
858 dsrc2 = makeDSFromDNSKey(zone, *drc, dsrc.d_digesttype);
859 dsCreated = true;
860 isValid = dsrc == dsrc2;
861 }
862 catch(const std::exception &e) {
863 LOG("Unable to make DS from DNSKey: "<<e.what()<<endl);
864 }
865
866 if(isValid) {
867 LOG("got valid DNSKEY (it matches the DS) with tag "<<dsrc.d_tag<<" and algorithm "<<std::to_string(dsrc.d_algorithm)<<" for "<<zone<<endl);
868
869 validkeys.insert(drc);
870 }
871 else {
872 if (dsCreated) {
873 LOG("DNSKEY did not match the DS, parent DS: "<<dsrc.getZoneRepresentation() << " ! = "<<dsrc2.getZoneRepresentation()<<endl);
874 }
875 }
876 }
877 }
878
879 vector<uint16_t> toSignTags;
880 for (const auto& key : tkeys) {
881 toSignTags.push_back(key->getTag());
882 }
883
884 // cerr<<"got "<<validkeys.size()<<"/"<<tkeys.size()<<" valid/tentative keys"<<endl;
885 // these counts could be off if we somehow ended up with
886 // duplicate keys. Should switch to a type that prevents that.
887 if(validkeys.size() < tkeys.size())
888 {
889 // this should mean that we have one or more DS-validated DNSKEYs
890 // but not a fully validated DNSKEY set, yet
891 // one of these valid DNSKEYs should be able to validate the
892 // whole set
893 for(const auto& sig : sigs)
894 {
895 // 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;
896 auto bytag = getByTag(validkeys, sig->d_tag, sig->d_algorithm);
897
898 if (bytag.empty()) {
899 continue;
900 }
901
902 string msg = getMessageForRRSET(zone, *sig, toSign);
903 for(const auto& key : bytag) {
904 // cerr<<"validating : ";
905 bool signIsValid = checkSignatureWithKey(now, sig, key, msg);
906
907 if(signIsValid)
908 {
909 LOG("validation succeeded - whole DNSKEY set is valid"<<endl);
910 validkeys = tkeys;
911 break;
912 }
913 else {
914 LOG("Validation did not succeed!"<<endl);
915 }
916 }
917 // if(validkeys.empty()) cerr<<"did not manage to validate DNSKEY set based on DS-validated KSK, only passing KSK on"<<endl;
918 }
919 }
920 }
921
922 vState getKeysFor(DNSRecordOracle& dro, const DNSName& zone, skeyset_t& keyset)
923 {
924 auto luaLocal = g_luaconfs.getLocal();
925 const auto anchors = luaLocal->dsAnchors;
926 if (anchors.empty()) // Nothing to do here
927 return Insecure;
928
929 // Determine the lowest (i.e. with the most labels) Trust Anchor for zone
930 DNSName lowestTA(".");
931 for (auto const &anchor : anchors)
932 if (zone.isPartOf(anchor.first) && lowestTA.countLabels() < anchor.first.countLabels())
933 lowestTA = anchor.first;
934
935 // Before searching for the keys, see if we have a Negative Trust Anchor. If
936 // so, test if the NTA is valid and return an NTA state
937 const auto negAnchors = luaLocal->negAnchors;
938
939 if (!negAnchors.empty()) {
940 DNSName lowestNTA;
941
942 for (auto const &negAnchor : negAnchors)
943 if (zone.isPartOf(negAnchor.first) && lowestNTA.countLabels() <= negAnchor.first.countLabels())
944 lowestNTA = negAnchor.first;
945
946 if(!lowestNTA.empty()) {
947 LOG("Found a Negative Trust Anchor for "<<lowestNTA<<", which was added with reason '"<<negAnchors.at(lowestNTA)<<"', ");
948
949 /* RFC 7646 section 2.1 tells us that we SHOULD still validate if there
950 * is a Trust Anchor below the Negative Trust Anchor for the name we
951 * attempt validation for. However, section 3 tells us this positive
952 * Trust Anchor MUST be *below* the name and not the name itself
953 */
954 if(lowestTA.countLabels() <= lowestNTA.countLabels()) {
955 LOG("marking answer Insecure"<<endl);
956 return NTA; // Not Insecure, this way validateRecords() can shortcut
957 }
958 LOG("but a Trust Anchor for "<<lowestTA<<" is configured, continuing validation."<<endl);
959 }
960 }
961
962 skeyset_t validkeys;
963 dsmap_t dsmap;
964
965 dsmap_t* tmp = (dsmap_t*) rplookup(anchors, lowestTA);
966 if (tmp)
967 dsmap = *tmp;
968
969 auto zoneCuts = getZoneCuts(zone, lowestTA, dro);
970
971 LOG("Found the following zonecuts:")
972 for(const auto& zonecut : zoneCuts)
973 LOG(" => "<<zonecut);
974 LOG(endl);
975
976 for(auto zoneCutIter = zoneCuts.cbegin(); zoneCutIter != zoneCuts.cend(); ++zoneCutIter)
977 {
978 vector<shared_ptr<RRSIGRecordContent> > sigs;
979 sortedRecords_t toSign;
980
981 skeyset_t tkeys; // tentative keys
982 validkeys.clear();
983
984 // cerr<<"got DS for ["<<qname<<"], grabbing DNSKEYs"<<endl;
985 auto records=dro.get(*zoneCutIter, (uint16_t)QType::DNSKEY);
986 // this should use harvest perhaps
987 for(const auto& rec : records) {
988 if(rec.d_name != *zoneCutIter)
989 continue;
990
991 if(rec.d_type == QType::RRSIG)
992 {
993 auto rrc=getRR<RRSIGRecordContent> (rec);
994 if(rrc) {
995 LOG("Got signature: "<<rrc->getZoneRepresentation()<<" with tag "<<rrc->d_tag<<", for type "<<DNSRecordContent::NumberToType(rrc->d_type)<<endl);
996 if(rrc->d_type != QType::DNSKEY)
997 continue;
998 sigs.push_back(rrc);
999 }
1000 }
1001 else if(rec.d_type == QType::DNSKEY)
1002 {
1003 auto drc=getRR<DNSKEYRecordContent> (rec);
1004 if(drc) {
1005 tkeys.insert(drc);
1006 LOG("Inserting key with tag "<<drc->getTag()<<" and algorithm "<<DNSSECKeeper::algorithm2name(drc->d_algorithm)<<": "<<drc->getZoneRepresentation()<<endl);
1007
1008 toSign.insert(rec.d_content);
1009 }
1010 }
1011 }
1012 LOG("got "<<tkeys.size()<<" keys and "<<sigs.size()<<" sigs from server"<<endl);
1013
1014 /*
1015 * Check all DNSKEY records against all DS records and place all DNSKEY records
1016 * that have DS records (that we support the algo for) in the tentative key storage
1017 */
1018 validateDNSKeysAgainstDS(time(nullptr), *zoneCutIter, dsmap, tkeys, toSign, sigs, validkeys);
1019
1020 if(validkeys.empty())
1021 {
1022 LOG("ended up with zero valid DNSKEYs, going Bogus"<<endl);
1023 return Bogus;
1024 }
1025 LOG("situation: we have one or more valid DNSKEYs for ["<<*zoneCutIter<<"] (want ["<<zone<<"])"<<endl);
1026
1027 if(zoneCutIter == zoneCuts.cend()-1) {
1028 LOG("requested keyset found! returning Secure for the keyset"<<endl);
1029 keyset.insert(validkeys.cbegin(), validkeys.cend());
1030 return Secure;
1031 }
1032
1033 // We now have the DNSKEYs, use them to validate the DS records at the next zonecut
1034 LOG("next name ["<<*(zoneCutIter+1)<<"], trying to get DS"<<endl);
1035
1036 dsmap_t tdsmap; // tentative DSes
1037 dsmap.clear();
1038 toSign.clear();
1039
1040 auto recs=dro.get(*(zoneCutIter+1), QType::DS);
1041
1042 cspmap_t cspmap=harvestCSPFromRecs(recs);
1043
1044 cspmap_t validrrsets;
1045 validateWithKeySet(cspmap, validrrsets, validkeys);
1046
1047 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);
1048
1049 auto r = validrrsets.equal_range(make_pair(*(zoneCutIter+1), QType::DS));
1050 if(r.first == r.second) {
1051 LOG("No DS for "<<*(zoneCutIter+1)<<", now look for a secure denial"<<endl);
1052 dState res = getDenial(validrrsets, *(zoneCutIter+1), QType::DS, true, true);
1053 if (res == INSECURE || res == NXDOMAIN)
1054 return Bogus;
1055 if (res == NXQTYPE || res == OPTOUT)
1056 return Insecure;
1057 }
1058
1059 /*
1060 * Collect all DS records and add them to the dsmap for the next iteration
1061 */
1062 for(auto cspiter =r.first; cspiter!=r.second; cspiter++) {
1063 for(auto j=cspiter->second.records.cbegin(); j!=cspiter->second.records.cend(); j++)
1064 {
1065 const auto dsrc=std::dynamic_pointer_cast<DSRecordContent>(*j);
1066 if(dsrc) {
1067 dsmap.insert(*dsrc);
1068 }
1069 }
1070 }
1071 }
1072 // There were no zone cuts (aka, we should never get here)
1073 return Bogus;
1074 }
1075
1076 bool isSupportedDS(const DSRecordContent& ds)
1077 {
1078 if (!DNSCryptoKeyEngine::isAlgorithmSupported(ds.d_algorithm)) {
1079 LOG("Discarding DS "<<ds.d_tag<<" because we don't support algorithm number "<<std::to_string(ds.d_algorithm)<<endl);
1080 return false;
1081 }
1082
1083 if (!DNSCryptoKeyEngine::isDigestSupported(ds.d_digesttype)) {
1084 LOG("Discarding DS "<<ds.d_tag<<" because we don't support digest number "<<std::to_string(ds.d_digesttype)<<endl);
1085 return false;
1086 }
1087
1088 return true;
1089 }
1090
1091 DNSName getSigner(const std::vector<std::shared_ptr<RRSIGRecordContent> >& signatures)
1092 {
1093 for (const auto& sig : signatures) {
1094 if (sig) {
1095 return sig->d_signer;
1096 }
1097 }
1098
1099 return DNSName();
1100 }