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