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