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