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