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