]> git.ipfire.org Git - thirdparty/pdns.git/blame - pdns/validate.cc
Don't read potentially uninitalized memory if gethostname() failed
[thirdparty/pdns.git] / pdns / validate.cc
CommitLineData
243f4780 1#include "validate.hh"
2#include "misc.hh"
3#include "dnssecinfra.hh"
3d5ebf10 4#include "dnsseckeeper.hh"
2ac8ae89 5#include "rec-lua-conf.hh"
243f4780 6#include "base32.hh"
566ab902 7#include "logger.hh"
3e9c6c0a 8bool g_dnssecLOG{false};
9a3ab3e4 9time_t g_signatureInceptionSkew{0};
d377bb54 10uint16_t g_maxNSEC3Iterations{0};
243f4780 11
e6a9dde5 12#define LOG(x) if(g_dnssecLOG) { g_log <<Logger::Warning << x; }
243f4780 13
ff30823a 14const char *dStates[]={"nodata", "nxdomain", "nxqtype", "empty non-terminal", "insecure", "opt-out"};
895449a5 15const char *vStates[]={"Indeterminate", "Bogus", "Insecure", "Secure", "NTA", "TA"};
243f4780 16
4d2be65d 17static vector<shared_ptr<DNSKEYRecordContent > > getByTag(const skeyset_t& keys, uint16_t tag, uint8_t algorithm)
243f4780 18{
4d2be65d 19 vector<shared_ptr<DNSKEYRecordContent>> ret;
243f4780 20 for(const auto& key : keys)
5780cb46 21 if(key->d_protocol == 3 && key->getTag() == tag && key->d_algorithm == algorithm)
243f4780 22 ret.push_back(key);
23 return ret;
24}
25
812c3a69
RG
26static 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
eb3e3d99 31 (beginHash == nextHash && h != beginHash)); // "we have only 1 NSEC3 record, LOL!"
812c3a69
RG
32}
33
8119e5cb
RG
34static 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
eb3e3d99 39 (begin == next && name != begin)); // "we have only 1 NSEC record, LOL!"
8119e5cb
RG
40}
41
00e3fef4
RG
42static 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 */
00be1ff6 48 return begin.canonCompare(name) && next != name && next.isPartOf(name);
00e3fef4
RG
49}
50
bd9e6242 51static std::string getHashFromNSEC3(const DNSName& qname, const std::shared_ptr<NSEC3RecordContent>& nsec3)
5374b03b
RG
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
9b061cf5
RG
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*/
5374b03b
RG
68bool 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) {
27d4a65b 78 return !nsec->isSet(QType::NS);
5374b03b
RG
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) {
27d4a65b 98 return !nsec3->isSet(QType::NS);
5374b03b
RG
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
9b061cf5
RG
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*/
78cdf520
RG
115bool 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
9b061cf5 124static bool isWildcardExpanded(const DNSName& owner, const std::vector<std::shared_ptr<RRSIGRecordContent> >& signatures)
243f4780 125{
9b061cf5
RG
126 if (signatures.empty()) {
127 return false;
128 }
129
130 const auto& sign = signatures.at(0);
131 unsigned int labelsCount = owner.countLabels();
78cdf520
RG
132 return isWildcardExpanded(labelsCount, sign);
133}
134
135bool 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 */
9b061cf5
RG
139 return true;
140 }
9b061cf5
RG
141 return false;
142}
143
78cdf520
RG
144static 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
9b061cf5
RG
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. */
157static 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
bd9e6242 180static bool isNSECAncestorDelegation(const DNSName& signer, const DNSName& owner, const std::shared_ptr<NSECRecordContent>& nsec)
b580fad2 181{
27d4a65b
RG
182 return nsec->isSet(QType::NS) &&
183 !nsec->isSet(QType::SOA) &&
b580fad2
RG
184 signer.countLabels() < owner.countLabels();
185}
186
bd9e6242 187static bool isNSEC3AncestorDelegation(const DNSName& signer, const DNSName& owner, const std::shared_ptr<NSEC3RecordContent>& nsec3)
b580fad2 188{
27d4a65b
RG
189 return nsec3->isSet(QType::NS) &&
190 !nsec3->isSet(QType::SOA) &&
b580fad2
RG
191 signer.countLabels() < owner.countLabels();
192}
193
82566a96
RG
194static 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");
27d4a65b 219 if (qtype == 0 || !nsec->isSet(qtype)) {
82566a96
RG
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
9b061cf5 233/*
82566a96 234 This function checks whether the non-existence of a wildcard covering qname|qtype
9b061cf5 235 is proven by the NSEC records in validrrsets.
9b061cf5 236*/
82566a96 237static bool provesNoWildCard(const DNSName& qname, const uint16_t qtype, const cspmap_t & validrrsets)
9b061cf5
RG
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
82566a96 263 LOG("Comparing owner: "<<owner<<" with target: "<<target<<endl);
9b061cf5 264
82566a96 265 if (isCoveredByNSEC(target, owner, nsec->d_next)) {
9b061cf5
RG
266 LOG("\tWildcard is covered"<<endl);
267 return true;
268 }
269 }
270 }
271 }
272 }
273
274 return false;
275}
276
277/*
82566a96 278 This function checks whether the non-existence of a wildcard covering qname|qtype
9b061cf5
RG
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*/
283static 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 }
27d4a65b 315 if (qtype == 0 || !nsec3->isSet(qtype)) {
9b061cf5
RG
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.
f5a747bb 337 - If `referralToUnsigned` is true and qtype is QType::DS, this functions returns NODATA
9b061cf5
RG
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
ef2ea4bf 342 useful when we have a positive answer synthesized from a wildcard and we only need to prove that the exact
9b061cf5
RG
343 name does not exist.
344*/
e4894ce0 345dState getDenial(const cspmap_t &validrrsets, const DNSName& qname, const uint16_t qtype, bool referralToUnsigned, bool wantsNoDataProof, bool needWildcardProof, unsigned int wildcardLabelsCount)
9b061cf5
RG
346{
347 bool nsec3Seen = false;
e4894ce0 348 if (!needWildcardProof && wildcardLabelsCount == 0) {
ef2ea4bf 349 throw PDNSException("Invalid wildcard labels count for the validation of a positive answer synthesized from a wildcard");
e4894ce0 350 }
9b061cf5 351
da204325
PL
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
3143417d
RG
362 const DNSName signer = getSigner(v.second.signatures);
363 if (!v.first.first.isPartOf(signer))
364 continue;
365
82566a96 366 const DNSName owner = getNSECOwnerName(v.first.first, v.second.signatures);
95208ae3
RG
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 */
b580fad2 373 if (qtype != QType::DS && (qname == owner || qname.isPartOf(owner)) && isNSECAncestorDelegation(signer, owner, nsec)) {
27d4a65b 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);
95208ae3 375 /* this is an "ancestor delegation" NSEC RR */
b580fad2
RG
376 LOG("An ancestor delegation NSEC RR can only deny the existence of a DS"<<endl);
377 return NODATA;
95208ae3
RG
378 }
379
da204325 380 /* check if the type is denied */
82566a96 381 if(qname == owner) {
27d4a65b 382 if (nsec->isSet(qtype)) {
eb3e3d99 383 LOG("Does _not_ deny existence of type "<<QType(qtype).getName()<<endl);
b580fad2 384 return NODATA;
eb3e3d99
RG
385 }
386
da204325 387 LOG("Denies existence of type "<<QType(qtype).getName()<<endl);
95823c07 388
9b061cf5 389 /* RFC 6840 section 4.3 */
27d4a65b 390 if (nsec->isSet(QType::CNAME)) {
9b061cf5
RG
391 LOG("However a CNAME exists"<<endl);
392 return NODATA;
393 }
394
95823c07
RG
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 */
27d4a65b 401 if (referralToUnsigned && qtype == QType::DS && !nsec->isSet(QType::NS)) {
95823c07 402 LOG("However, no NS record exists at this level!"<<endl);
b7c40613 403 return NODATA;
95823c07
RG
404 }
405
9b061cf5
RG
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) */
78cdf520 409 if (!isWildcardExpanded(owner, v.second.signatures) || isWildcardExpandedOntoItself(owner, v.second.signatures)) {
9b061cf5
RG
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;
da204325 419 }
243f4780 420
da204325 421 /* check if the whole NAME is denied existing */
82566a96 422 if(isCoveredByNSEC(qname, owner, nsec->d_next)) {
78cdf520 423 LOG(qname<<" is covered ");
9b061cf5
RG
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. */
82566a96 426 if (wantsNoDataProof && nsecProvesENT(qname, owner, nsec->d_next)) {
00e3fef4
RG
427 LOG("Denies existence of type "<<qname<<"/"<<QType(qtype).getName()<<" by proving that "<<qname<<" is an ENT"<<endl);
428 return NXQTYPE;
429 }
430
82566a96 431 if (!needWildcardProof) {
78cdf520 432 LOG("and we did not need a wildcard proof"<<endl);
82566a96
RG
433 return NXDOMAIN;
434 }
435
78cdf520 436 LOG("but we do need a wildcard proof so ");
82566a96 437 if (wantsNoDataProof) {
78cdf520 438 LOG("looking for NODATA proof"<<endl);
82566a96 439 if (provesNoDataWildCard(qname, qtype, validrrsets)) {
9b061cf5
RG
440 return NXQTYPE;
441 }
9b061cf5 442 }
82566a96 443 else {
78cdf520 444 LOG("looking for NO wildcard proof"<<endl);
82566a96
RG
445 if (provesNoWildCard(qname, qtype, validrrsets)) {
446 return NXDOMAIN;
447 }
448 }
449
9b061cf5
RG
450 LOG("But the existence of a wildcard is not denied for "<<qname<<"/"<<QType(qtype).getName()<<endl);
451 return NODATA;
da204325 452 }
243f4780 453
27d4a65b 454 LOG("Did not deny existence of "<<QType(qtype).getName()<<", "<<owner<<"?="<<qname<<", "<<nsec->isSet(qtype)<<", next: "<<nsec->d_next<<endl);
da204325
PL
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
3143417d 463 const DNSName signer = getSigner(v.second.signatures);
9b061cf5
RG
464 if (!v.first.first.isPartOf(signer)) {
465 LOG("Owner "<<v.first.first<<" is not part of the signer "<<signer<<", ignoring"<<endl);
3143417d 466 continue;
9b061cf5 467 }
3143417d 468
5374b03b
RG
469 string h = getHashFromNSEC3(qname, nsec3);
470 if (h.empty()) {
9b061cf5 471 LOG("Unsupported hash, ignoring"<<endl);
d377bb54
RG
472 return INSECURE;
473 }
474
9b061cf5
RG
475 nsec3Seen = true;
476
812c3a69 477 // cerr<<"Salt length: "<<nsec3->d_salt.length()<<", iterations: "<<nsec3->d_iterations<<", hashed: "<<qname<<endl;
da204325
PL
478 LOG("\tquery hash: "<<toBase32Hex(h)<<endl);
479 string beginHash=fromBase32Hex(v.first.first.getRawLabels()[0]);
480
95823c07
RG
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 */
b580fad2 487 if (qtype != QType::DS && beginHash == h && isNSEC3AncestorDelegation(signer, v.first.first, nsec3)) {
27d4a65b 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);
95823c07 489 /* this is an "ancestor delegation" NSEC3 RR */
b580fad2
RG
490 LOG("An ancestor delegation NSEC3 RR can only deny the existence of a DS"<<endl);
491 return NODATA;
95823c07
RG
492 }
493
da204325 494 // If the name exists, check if the qtype is denied
eb3e3d99 495 if(beginHash == h) {
27d4a65b 496 if (nsec3->isSet(qtype)) {
95823c07 497 LOG("Does _not_ deny existence of type "<<QType(qtype).getName()<<" for name "<<qname<<" (not opt-out)."<<endl);
b580fad2 498 return NODATA;
eb3e3d99
RG
499 }
500
95823c07 501 LOG("Denies existence of type "<<QType(qtype).getName()<<" for name "<<qname<<" (not opt-out)."<<endl);
9b061cf5
RG
502
503 /* RFC 6840 section 4.3 */
27d4a65b 504 if (nsec3->isSet(QType::CNAME)) {
9b061cf5
RG
505 LOG("However a CNAME exists"<<endl);
506 return NODATA;
507 }
508
43c04e77
PL
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 */
27d4a65b 516 if (referralToUnsigned && qtype == QType::DS && !nsec3->isSet(QType::NS)) {
da204325 517 LOG("However, no NS record exists at this level!"<<endl);
b7c40613 518 return NODATA;
da204325 519 }
243f4780 520
9b061cf5 521 return NXQTYPE;
da204325 522 }
243f4780 523 }
da204325 524 }
243f4780 525 }
812c3a69 526
9b061cf5
RG
527 /* if we have no NSEC3 records, we are done */
528 if (!nsec3Seen) {
529 return NODATA;
530 }
531
532 DNSName closestEncloser(qname);
812c3a69
RG
533 bool found = false;
534
9b061cf5
RG
535 if (needWildcardProof) {
536 /* We now need to look for a NSEC3 covering the closest (provable) encloser
537 RFC 5155 section-7.2.1
c1797419 538 RFC 7129 section-5.5
9b061cf5
RG
539 */
540 LOG("Now looking for the closest encloser for "<<qname<<endl);
812c3a69 541
9b061cf5
RG
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;
d377bb54 550
b580fad2
RG
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
b7c40613
RG
557 string h = getHashFromNSEC3(closestEncloser, nsec3);
558 if (h.empty()) {
9b061cf5
RG
559 return INSECURE;
560 }
561
9b061cf5 562 string beginHash=fromBase32Hex(v.first.first.getRawLabels()[0]);
812c3a69 563
9b061cf5
RG
564 LOG("Comparing "<<toBase32Hex(h)<<" ("<<closestEncloser<<") against "<<toBase32Hex(beginHash)<<endl);
565 if(beginHash == h) {
b580fad2
RG
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
9b061cf5
RG
571 LOG("Closest encloser for "<<qname<<" is "<<closestEncloser<<endl);
572 found = true;
573 break;
574 }
812c3a69
RG
575 }
576 }
9b061cf5
RG
577 if (found == true) {
578 break;
579 }
812c3a69
RG
580 }
581 }
582 }
9b061cf5
RG
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;
e4894ce0
RG
590 unsigned int closestEncloserLabelsCount = closestEncloser.countLabels();
591 while (wildcardLabelsCount > 0 && closestEncloserLabelsCount > wildcardLabelsCount) {
592 closestEncloser.chopOff();
593 closestEncloserLabelsCount--;
594 }
9b061cf5
RG
595 }
596
597 bool nextCloserFound = false;
598 bool isOptOut = false;
812c3a69
RG
599
600 if (found == true) {
9b061cf5 601 /* now that we have found the closest (provable) encloser,
c1797419 602 we can construct the next closer (RFC7129 section-5.5) name
9b061cf5
RG
603 and look for a NSEC3 RR covering it */
604 unsigned int labelIdx = qname.countLabels() - closestEncloser.countLabels();
812c3a69 605 if (labelIdx >= 1) {
9b061cf5 606 DNSName nextCloser(closestEncloser);
812c3a69
RG
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;
b7c40613
RG
617
618 string h = getHashFromNSEC3(nextCloser, nsec3);
619 if (h.empty()) {
d377bb54
RG
620 return INSECURE;
621 }
812c3a69 622
812c3a69
RG
623 string beginHash=fromBase32Hex(v.first.first.getRawLabels()[0]);
624
9b061cf5 625 LOG("Comparing "<<toBase32Hex(h)<<" against "<<toBase32Hex(beginHash)<<" -> "<<toBase32Hex(nsec3->d_nexthash)<<endl);
812c3a69
RG
626 if(isCoveredByNSEC3Hash(h, beginHash, nsec3->d_nexthash)) {
627 LOG("Denies existence of name "<<qname<<"/"<<QType(qtype).getName());
9b061cf5
RG
628 nextCloserFound = true;
629
18c8faae 630 if ((qtype == QType::DS || qtype == 0) && nsec3->d_flags & 1) {
9b061cf5
RG
631 LOG(" but is opt-out!");
632 isOptOut = true;
812c3a69
RG
633 }
634 LOG(endl);
9b061cf5 635 break;
812c3a69 636 }
9b061cf5 637 LOG("Did not cover us ("<<qname<<"), start="<<v.first.first<<", us="<<toBase32Hex(h)<<", end="<<toBase32Hex(nsec3->d_nexthash)<<endl);
812c3a69
RG
638 }
639 }
9b061cf5
RG
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;
812c3a69 663 }
9b061cf5 664 return NXDOMAIN;
812c3a69
RG
665 }
666 }
667
da204325
PL
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;
243f4780 671}
672
de838038
PL
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 */
4d2be65d 677static const vector<DNSName> getZoneCuts(const DNSName& begin, const DNSName& end, DNSRecordOracle& dro)
de838038
PL
678{
679 vector<DNSName> ret;
680 if(!begin.isPartOf(end))
86f1af1c 681 throw PDNSException(end.toLogString() + "is not part of " + begin.toLogString());
de838038
PL
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) {
e2f91e70
RG
689 bool foundCut = false;
690 if (labelsToAdd.empty())
691 break;
692
754914f0 693 qname.prependRawLabel(labelsToAdd.back());
de838038 694 labelsToAdd.pop_back();
de838038 695 auto records = dro.get(qname, (uint16_t)QType::NS);
7af99dff 696 for (const auto& record : records) {
4d2be65d 697 if(record.d_type != QType::NS || record.d_name != qname)
de838038
PL
698 continue;
699 foundCut = true;
700 break;
701 }
702 if (foundCut)
703 ret.push_back(qname);
704 }
705 return ret;
706}
707
dbbef467
RG
708bool isRRSIGNotExpired(const time_t now, const shared_ptr<RRSIGRecordContent> sig)
709{
9a3ab3e4 710 return sig->d_siginception - g_signatureInceptionSkew <= now && sig->d_sigexpire >= now;
dbbef467
RG
711}
712
4d2be65d
RG
713static 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 {
179b340d
RG
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 */
dbbef467 721 if(isRRSIGNotExpired(now, sig)) {
4d2be65d
RG
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);
3d5ebf10 724 LOG("signature by key with tag "<<sig->d_tag<<" and algorithm "<<DNSSECKeeper::algorithm2name(sig->d_algorithm)<<" was " << (result ? "" : "NOT ")<<"valid"<<endl);
4d2be65d
RG
725 }
726 else {
9a3ab3e4 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);
4d2be65d
RG
728 }
729 }
5780cb46 730 catch(const std::exception& e) {
4d2be65d
RG
731 LOG("Could not make a validator for signature: "<<e.what()<<endl);
732 }
733 return result;
734}
735
c1e7b833 736bool validateWithKeySet(time_t now, const DNSName& name, const sortedRecords_t& toSign, const vector<shared_ptr<RRSIGRecordContent> >& signatures, const skeyset_t& keys, bool validateAllSigs)
4d2be65d
RG
737{
738 bool isValid = false;
739
740 for(const auto& signature : signatures) {
bb07ad8e
RG
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
4d2be65d
RG
746 auto r = getByTag(keys, signature->d_tag, signature->d_algorithm);
747
748 if(r.empty()) {
3d5ebf10 749 LOG("No key provided for "<<signature->d_tag<<" and algorithm "<<std::to_string(signature->d_algorithm)<<endl;);
4d2be65d
RG
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 }
4d2be65d
RG
765 if (signIsValid && !validateAllSigs) {
766 return true;
767 }
768 }
769 }
770
771 return isValid;
772}
773
774void validateWithKeySet(const cspmap_t& rrsets, cspmap_t& validated, const skeyset_t& keys)
243f4780 775{
776 validated.clear();
b3f0ed10 777 /* cerr<<"Validating an rrset with following keys: "<<endl;
243f4780 778 for(auto& key : keys) {
4d2be65d 779 cerr<<"\tTag: "<<key->getTag()<<" -> "<<key->getZoneRepresentation()<<endl;
243f4780 780 }
b3f0ed10 781 */
4d2be65d 782 time_t now = time(nullptr);
7a4f4632 783 for(auto i=rrsets.cbegin(); i!=rrsets.cend(); i++) {
a95d1b58 784 LOG("validating "<<(i->first.first)<<"/"<<DNSRecordContent::NumberToType(i->first.second)<<" with "<<i->second.signatures.size()<<" sigs"<<endl);
4d2be65d
RG
785 if (validateWithKeySet(now, i->first.first, i->second.records, i->second.signatures, keys, true)) {
786 validated[i->first] = i->second;
243f4780 787 }
788 }
789}
790
243f4780 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
243f4780 797cspmap_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);
ba3c54cb
RG
806 if (rrc) {
807 cspmap[{rec.d_name,rrc->d_type}].signatures.push_back(rrc);
808 }
243f4780 809 }
810 else {
c1e7b833 811 cspmap[{rec.d_name, rec.d_type}].records.insert(rec.d_content);
243f4780 812 }
813 }
814 return cspmap;
815}
816
4d2be65d
RG
817bool 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
829bool 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
c1e7b833 841void 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)
4d2be65d
RG
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);
3d5ebf10 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;
4d2be65d
RG
851
852 for(const auto& drc : r)
853 {
854 bool isValid = false;
855 bool dsCreated = false;
856 DSRecordContent dsrc2;
857 try {
5780cb46 858 dsrc2 = makeDSFromDNSKey(zone, *drc, dsrc.d_digesttype);
4d2be65d
RG
859 dsCreated = true;
860 isValid = dsrc == dsrc2;
861 }
5780cb46 862 catch(const std::exception &e) {
4d2be65d
RG
863 LOG("Unable to make DS from DNSKey: "<<e.what()<<endl);
864 }
865
866 if(isValid) {
3d5ebf10 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);
4d2be65d
RG
868
869 validkeys.insert(drc);
4d2be65d
RG
870 }
871 else {
872 if (dsCreated) {
873 LOG("DNSKEY did not match the DS, parent DS: "<<dsrc.getZoneRepresentation() << " ! = "<<dsrc2.getZoneRepresentation()<<endl);
874 }
875 }
4d2be65d
RG
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
5780cb46 902 string msg = getMessageForRRSET(zone, *sig, toSign);
4d2be65d
RG
903 for(const auto& key : bytag) {
904 // cerr<<"validating : ";
905 bool signIsValid = checkSignatureWithKey(now, sig, key, msg);
906
4d2be65d
RG
907 if(signIsValid)
908 {
909 LOG("validation succeeded - whole DNSKEY set is valid"<<endl);
5780cb46 910 validkeys = tkeys;
4d2be65d
RG
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
922vState getKeysFor(DNSRecordOracle& dro, const DNSName& zone, skeyset_t& keyset)
243f4780 923{
1bd49828 924 auto luaLocal = g_luaconfs.getLocal();
4d2be65d 925 const auto anchors = luaLocal->dsAnchors;
e9321921
PL
926 if (anchors.empty()) // Nothing to do here
927 return Insecure;
928
b2e3fc03
PL
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;
1bd49828
PL
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
4d2be65d 937 const auto negAnchors = luaLocal->negAnchors;
1bd49828
PL
938
939 if (!negAnchors.empty()) {
b2e3fc03 940 DNSName lowestNTA;
1bd49828
PL
941
942 for (auto const &negAnchor : negAnchors)
f418a272 943 if (zone.isPartOf(negAnchor.first) && lowestNTA.countLabels() <= negAnchor.first.countLabels())
1bd49828
PL
944 lowestNTA = negAnchor.first;
945
1bd49828 946 if(!lowestNTA.empty()) {
317f7521 947 LOG("Found a Negative Trust Anchor for "<<lowestNTA<<", which was added with reason '"<<negAnchors.at(lowestNTA)<<"', ");
1bd49828
PL
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 */
f418a272 954 if(lowestTA.countLabels() <= lowestNTA.countLabels()) {
1bd49828
PL
955 LOG("marking answer Insecure"<<endl);
956 return NTA; // Not Insecure, this way validateRecords() can shortcut
957 }
317f7521 958 LOG("but a Trust Anchor for "<<lowestTA<<" is configured, continuing validation."<<endl);
1bd49828
PL
959 }
960 }
961
4d2be65d 962 skeyset_t validkeys;
7226d53b 963 dsmap_t dsmap;
243f4780 964
4d2be65d 965 dsmap_t* tmp = (dsmap_t*) rplookup(anchors, lowestTA);
7226d53b
PL
966 if (tmp)
967 dsmap = *tmp;
b2e3fc03 968
7226d53b 969 auto zoneCuts = getZoneCuts(zone, lowestTA, dro);
52ad9eea 970
7226d53b
PL
971 LOG("Found the following zonecuts:")
972 for(const auto& zonecut : zoneCuts)
973 LOG(" => "<<zonecut);
974 LOG(endl);
975
7a4f4632 976 for(auto zoneCutIter = zoneCuts.cbegin(); zoneCutIter != zoneCuts.cend(); ++zoneCutIter)
7226d53b 977 {
4d2be65d 978 vector<shared_ptr<RRSIGRecordContent> > sigs;
c1e7b833 979 sortedRecords_t toSign;
243f4780 980
4d2be65d 981 skeyset_t tkeys; // tentative keys
243f4780 982 validkeys.clear();
52ad9eea 983
b3f0ed10 984 // cerr<<"got DS for ["<<qname<<"], grabbing DNSKEYs"<<endl;
7226d53b 985 auto records=dro.get(*zoneCutIter, (uint16_t)QType::DNSKEY);
243f4780 986 // this should use harvest perhaps
7226d53b
PL
987 for(const auto& rec : records) {
988 if(rec.d_name != *zoneCutIter)
243f4780 989 continue;
990
991 if(rec.d_type == QType::RRSIG)
992 {
993 auto rrc=getRR<RRSIGRecordContent> (rec);
7226d53b
PL
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;
4d2be65d 998 sigs.push_back(rrc);
7226d53b 999 }
243f4780 1000 }
1001 else if(rec.d_type == QType::DNSKEY)
1002 {
1003 auto drc=getRR<DNSKEYRecordContent> (rec);
ba3c54cb 1004 if(drc) {
4d2be65d 1005 tkeys.insert(drc);
3d5ebf10 1006 LOG("Inserting key with tag "<<drc->getTag()<<" and algorithm "<<DNSSECKeeper::algorithm2name(drc->d_algorithm)<<": "<<drc->getZoneRepresentation()<<endl);
243f4780 1007
c1e7b833 1008 toSign.insert(rec.d_content);
ba3c54cb 1009 }
243f4780 1010 }
1011 }
a95d1b58 1012 LOG("got "<<tkeys.size()<<" keys and "<<sigs.size()<<" sigs from server"<<endl);
243f4780 1013
7226d53b
PL
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 */
4d2be65d 1018 validateDNSKeysAgainstDS(time(nullptr), *zoneCutIter, dsmap, tkeys, toSign, sigs, validkeys);
243f4780 1019
1020 if(validkeys.empty())
1021 {
164de0a7 1022 LOG("ended up with zero valid DNSKEYs, going Bogus"<<endl);
7226d53b 1023 return Bogus;
243f4780 1024 }
7226d53b
PL
1025 LOG("situation: we have one or more valid DNSKEYs for ["<<*zoneCutIter<<"] (want ["<<zone<<"])"<<endl);
1026
7a4f4632 1027 if(zoneCutIter == zoneCuts.cend()-1) {
164de0a7 1028 LOG("requested keyset found! returning Secure for the keyset"<<endl);
7a4f4632 1029 keyset.insert(validkeys.cbegin(), validkeys.cend());
243f4780 1030 return Secure;
1031 }
243f4780 1032
7226d53b
PL
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);
243f4780 1035
7226d53b
PL
1036 dsmap_t tdsmap; // tentative DSes
1037 dsmap.clear();
1038 toSign.clear();
243f4780 1039
7226d53b 1040 auto recs=dro.get(*(zoneCutIter+1), QType::DS);
243f4780 1041
7226d53b 1042 cspmap_t cspmap=harvestCSPFromRecs(recs);
243f4780 1043
7226d53b
PL
1044 cspmap_t validrrsets;
1045 validateWithKeySet(cspmap, validrrsets, validkeys);
243f4780 1046
7226d53b 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);
3e9c6c0a 1048
7226d53b
PL
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);
00e3fef4 1052 dState res = getDenial(validrrsets, *(zoneCutIter+1), QType::DS, true, true);
ff30823a 1053 if (res == INSECURE || res == NXDOMAIN)
da204325 1054 return Bogus;
ff30823a 1055 if (res == NXQTYPE || res == OPTOUT)
da204325 1056 return Insecure;
7226d53b 1057 }
243f4780 1058
7226d53b
PL
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);
7226d53b
PL
1068 }
1069 }
1070 }
243f4780 1071 }
7226d53b
PL
1072 // There were no zone cuts (aka, we should never get here)
1073 return Bogus;
243f4780 1074}
1075
8455425c
RG
1076bool 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}
243f4780 1090
895449a5
RG
1091DNSName getSigner(const std::vector<std::shared_ptr<RRSIGRecordContent> >& signatures)
1092{
7af99dff 1093 for (const auto& sig : signatures) {
9b061cf5
RG
1094 if (sig) {
1095 return sig->d_signer;
1096 }
895449a5 1097 }
243f4780 1098
895449a5
RG
1099 return DNSName();
1100}