void AggressiveNSECCache::insertNSEC(const DNSName& zone, const DNSName& owner, const DNSRecord& record, const std::vector<std::shared_ptr<RRSIGRecordContent>>& signatures, bool nsec3)
{
+ if (signatures.empty()) {
+ return;
+ }
+
std::shared_ptr<AggressiveNSECCache::ZoneEntry> entry = getZone(zone);
{
std::lock_guard<std::mutex> lock(entry->d_lock);
throw std::runtime_error("Error getting the content from a NSEC record");
}
next = content->d_next;
+ if (next.canonCompare(owner) && next != zone) {
+ /* not accepting a NSEC whose next domain name is before the owner
+ unless the next domain name is the apex, sorry */
+ return;
+ }
}
else {
auto content = getRR<NSEC3RecordContent>(record);
if (!content) {
throw std::runtime_error("Error getting the content from a NSEC3 record");
}
- next = DNSName(toBase32Hex(content->d_nexthash)) + zone;
+
+ if (content->isOptOut()) {
+ /* doesn't prove anything, sorry */
+ return;
+ }
+
if (g_maxNSEC3Iterations && content->d_iterations > g_maxNSEC3Iterations) {
+ /* can't use that */
return;
}
+ next = DNSName(toBase32Hex(content->d_nexthash)) + zone;
entry->d_iterations = content->d_iterations;
entry->d_salt = content->d_salt;
}
/* the TTL is already a TTD by now */
- entry->d_entries.insert({record.d_content, signatures, owner, std::move(next), record.d_ttl});
+ if (!nsec3 && isWildcardExpanded(owner.countLabels(), signatures.at(0))) {
+ DNSName realOwner = getNSECOwnerName(owner, signatures);
+ entry->d_entries.insert({record.d_content, signatures, std::move(realOwner), std::move(next), record.d_ttl});
+ }
+ else {
+ entry->d_entries.insert({record.d_content, signatures, owner, std::move(next), record.d_ttl});
+ }
}
}
return false;
}
+ const DNSName signer = getSigner(exactNSEC3.d_signatures);
+ if (type != QType::DS && isNSEC3AncestorDelegation(signer, exactNSEC3.d_owner, nsec3)) {
+ /* RFC 6840 section 4.1 "Clarifications on Nonexistence Proofs":
+ Ancestor delegation NSEC or NSEC3 RRs MUST NOT be used to assume
+ nonexistence of any RRs below that zone cut, which include all RRs at
+ that (original) owner name other than DS RRs, and all RRs below that
+ owner name regardless of type.
+ */
+ return false;
+ }
+
cerr<<"Direct match, done!"<<endl;
+ res = RCode::NoError;
addToRRSet(now, soaSet, soaSignatures, zoneEntry->d_zone, doDNSSEC, ret);
- addRecordToRRSet(now, exactNSEC3.d_owner, QType::NSEC, exactNSEC3.d_ttd - now, exactNSEC3.d_record, exactNSEC3.d_signatures, doDNSSEC, ret);
+ addRecordToRRSet(now, exactNSEC3.d_owner, QType::NSEC3, exactNSEC3.d_ttd - now, exactNSEC3.d_record, exactNSEC3.d_signatures, doDNSSEC, ret);
return true;
}
-#warning FIXME opt-out / ENT
+
#warning FIXME ancestor delegation
cerr<<"no direct match, looking for closest encloser"<<endl;
DNSName closestEncloser(name);
if (nsec3->isSet(QType::CNAME)) {
return false;
}
+
+ res = RCode::NoError;
}
else {
if (!isCoveredByNSEC3Hash(DNSName(wcHash) + zone, wcEntry.d_owner, wcEntry.d_next)) {
cerr<<"no covering record found for the wildcard in aggressive cache"<<endl;
return false;
}
+ res = RCode::NXDomain;
}
addToRRSet(now, soaSet, soaSignatures, zoneEntry->d_zone, doDNSSEC, ret);
res = RCode::NXDomain;
}
else {
+ auto nsecContent = std::dynamic_pointer_cast<NSECRecordContent>(wcEntry.d_record);
+ denial = matchesNSEC(wc, type.getCode(), wcEntry.d_owner, nsecContent, wcEntry.d_signatures);
+ if (denial == dState::NODENIAL) {
+ /* too complicated for now */
+ return false;
+ }
+ else if (denial == dState::NXQTYPE) {
+ covered = true;
+ res = RCode::NoError;
+ }
+ else if (denial == dState::NXDOMAIN) {
+ covered = true;
+ res = RCode::NXDomain;
+ }
+
+ if (wcEntry.d_owner != wc) {
+ needWildcard = true;
+ }
+#if 0
if (wcEntry.d_owner == wc) {
- auto nsecContent = std::dynamic_pointer_cast<NSECRecordContent>(wcEntry.d_record);
if (!nsecContent) {
return false;
}
res = RCode::NXDomain;
needWildcard = true;
}
+#endif
}
}
pair.signatures = signatureContents;
denialMap[std::make_pair(records.at(0).d_name, records.at(0).d_type)] = pair;
- dState denialState = getDenial(denialMap, DNSName("b.powerdns.com."), QType::A, false, false);
+ dState denialState = getDenial(denialMap, DNSName("a.powerdns.com."), QType::A, false, false);
+ BOOST_CHECK_EQUAL(denialState, dState::NODENIAL);
+}
+
+BOOST_AUTO_TEST_CASE(test_nsec_wildcard_with_cname)
+{
+ initSR();
+
+ testkeysset_t keys;
+ generateKeyMaterial(DNSName("example.org."), DNSSECKeeper::ECDSA256, DNSSECKeeper::DIGEST_SHA256, keys);
+
+ vector<DNSRecord> records;
+
+ sortedRecords_t recordContents;
+ vector<shared_ptr<RRSIGRecordContent>> signatureContents;
+
+ /* proves that b.example.com does not exist */
+ addNSECRecordToLW(DNSName("a.example.org."), DNSName("d.example.org"), {QType::A, QType::TXT, QType::RRSIG, QType::NSEC}, 600, records);
+ recordContents.insert(records.at(0).d_content);
+ addRRSIG(keys, records, DNSName("example.org."), 300);
+ signatureContents.push_back(getRR<RRSIGRecordContent>(records.at(1)));
+ records.clear();
+
+ ContentSigPair pair;
+ pair.records = recordContents;
+ pair.signatures = signatureContents;
+ cspmap_t denialMap;
+ denialMap[std::make_pair(DNSName("a.example.org."), QType::NSEC)] = pair;
+
+ /* add a NSEC proving that a wildcard exists, without a CNAME type */
+ recordContents.clear();
+ signatureContents.clear();
+ addNSECRecordToLW(DNSName("*.example.org."), DNSName("+.example.org"), {QType::A, QType::TXT, QType::RRSIG, QType::NSEC }, 600, records);
+ recordContents.insert(records.at(0).d_content);
+ addRRSIG(keys, records, DNSName("example.org."), 300);
+ signatureContents.push_back(getRR<RRSIGRecordContent>(records.at(1)));
+ records.clear();
+
+ pair.records = recordContents;
+ pair.signatures = signatureContents;
+ denialMap[std::make_pair(DNSName("*.example.org."), QType::NSEC)] = pair;
+
+ /* A does exist at the wildcard, AAAA does not */
+ dState denialState = getDenial(denialMap, DNSName("b.example.org."), QType::A, false, true);
+ BOOST_CHECK_EQUAL(denialState, dState::NODENIAL);
+
+ denialState = getDenial(denialMap, DNSName("b.example.org."), QType::AAAA, false, true);
+ BOOST_CHECK_EQUAL(denialState, dState::NXQTYPE);
+
+ /* now we replace the wildcard by one with a CNAME */
+ recordContents.clear();
+ signatureContents.clear();
+ addNSECRecordToLW(DNSName("*.example.org."), DNSName("+.example.org"), {QType::CNAME, QType::RRSIG, QType::NSEC }, 600, records);
+ recordContents.insert(records.at(0).d_content);
+ addRRSIG(keys, records, DNSName("example.org."), 300);
+ signatureContents.push_back(getRR<RRSIGRecordContent>(records.at(1)));
+ records.clear();
+
+ pair.records = recordContents;
+ pair.signatures = signatureContents;
+ denialMap[std::make_pair(DNSName("*.example.org."), QType::NSEC)] = pair;
+
+ /* A and AAAA do not exist but we have a CNAME so at the wildcard */
+ denialState = getDenial(denialMap, DNSName("b.example.org."), QType::A, false, true);
+ BOOST_CHECK_EQUAL(denialState, dState::NODENIAL);
+
+ denialState = getDenial(denialMap, DNSName("b.example.org."), QType::AAAA, false, true);
+ BOOST_CHECK_EQUAL(denialState, dState::NODENIAL);
+}
+
+BOOST_AUTO_TEST_CASE(test_nsec3_wildcard_with_cname)
+{
+ initSR();
+
+ testkeysset_t keys;
+ generateKeyMaterial(DNSName("example.org."), DNSSECKeeper::ECDSA256, DNSSECKeeper::DIGEST_SHA256, keys);
+
+ vector<DNSRecord> records;
+
+ sortedRecords_t recordContents;
+ vector<shared_ptr<RRSIGRecordContent>> signatureContents;
+
+ /* proves that b.example.com does not exist */
+ addNSEC3NarrowRecordToLW(DNSName("b.example.org"), DNSName("example.org."), {QType::A, QType::TXT, QType::RRSIG, QType::NSEC3}, 600, records);
+ recordContents.insert(records.at(0).d_content);
+ addRRSIG(keys, records, DNSName("example.org."), 300);
+ signatureContents.push_back(getRR<RRSIGRecordContent>(records.at(1)));
+
+ ContentSigPair pair;
+ pair.records = recordContents;
+ pair.signatures = signatureContents;
+ cspmap_t denialMap;
+ denialMap[std::make_pair(records.at(0).d_name, records.at(0).d_type)] = pair;
+
+ /* Add NSEC3 for the closest encloser */
+ recordContents.clear();
+ signatureContents.clear();
+ records.clear();
+ addNSEC3UnhashedRecordToLW(DNSName("example.org."), DNSName("example.org."), "whatever", {QType::A, QType::TXT, QType::RRSIG, QType::NSEC}, 600, records);
+ recordContents.insert(records.at(0).d_content);
+ addRRSIG(keys, records, DNSName("example.org."), 300);
+ signatureContents.push_back(getRR<RRSIGRecordContent>(records.at(1)));
+
+ pair.records = recordContents;
+ pair.signatures = signatureContents;
+ denialMap[std::make_pair(records.at(0).d_name, records.at(0).d_type)] = pair;
+
+ /* add wildcard, without a CNAME type */
+ recordContents.clear();
+ signatureContents.clear();
+ records.clear();
+ addNSEC3UnhashedRecordToLW(DNSName("*.example.org."), DNSName("example.org"), "whatever", {QType::A, QType::TXT, QType::RRSIG, QType::NSEC3}, 600, records);
+ recordContents.insert(records.at(0).d_content);
+ addRRSIG(keys, records, DNSName("example.org."), 300);
+ signatureContents.push_back(getRR<RRSIGRecordContent>(records.at(1)));
+
+ pair.records = recordContents;
+ pair.signatures = signatureContents;
+ denialMap[std::make_pair(records.at(0).d_name, records.at(0).d_type)] = pair;
+
+ /* A does exist at the wildcard, AAAA does not */
+ dState denialState = getDenial(denialMap, DNSName("b.example.org."), QType::A, false, true);
+ BOOST_CHECK_EQUAL(denialState, dState::NODENIAL);
+
+ denialState = getDenial(denialMap, DNSName("b.example.org."), QType::AAAA, false, true);
+ BOOST_CHECK_EQUAL(denialState, dState::NXQTYPE);
+
+ /* now we replace the wildcard by one with a CNAME */
+ recordContents.clear();
+ signatureContents.clear();
+ records.clear();
+ addNSEC3UnhashedRecordToLW(DNSName("*.example.org."), DNSName("example.org"), "whatever", {QType::CNAME, QType::RRSIG, QType::NSEC3}, 600, records);
+ recordContents.insert(records.at(0).d_content);
+ addRRSIG(keys, records, DNSName("example.org."), 300);
+ signatureContents.push_back(getRR<RRSIGRecordContent>(records.at(1)));
+
+ pair.records = recordContents;
+ pair.signatures = signatureContents;
+ denialMap[std::make_pair(records.at(0).d_name, records.at(0).d_type)] = pair;
+
+ /* A and AAAA do not exist but we have a CNAME so at the wildcard */
+ denialState = getDenial(denialMap, DNSName("b.example.org."), QType::A, false, true);
+ BOOST_CHECK_EQUAL(denialState, dState::NODENIAL);
+
+ denialState = getDenial(denialMap, DNSName("b.example.org."), QType::AAAA, false, true);
BOOST_CHECK_EQUAL(denialState, dState::NODENIAL);
}
*/
dState denialState = getDenial(denialMap, DNSName("a."), QType::A, false, true);
- /* no data means the qname/qtype is not denied, because an ancestor
+ /* no denial means the qname/qtype is not denied, because an ancestor
delegation NSEC3 can only deny the DS */
BOOST_CHECK_EQUAL(denialState, dState::NODENIAL);
}
if (isCoveredByNSEC3Hash(h, beginHash, nsec3->d_nexthash)) {
- return !(nsec3->d_flags & 1);
+ return !(nsec3->isOptOut());
}
}
}
/* if this is a wildcard NSEC, the owner name has been modified
to match the name. Make sure we use the original '*' form. */
-static DNSName getNSECOwnerName(const DNSName& initialOwner, const std::vector<std::shared_ptr<RRSIGRecordContent> >& signatures)
+#warning we should not need to export this
+DNSName getNSECOwnerName(const DNSName& initialOwner, const std::vector<std::shared_ptr<RRSIGRecordContent> >& signatures)
{
DNSName result = initialOwner;
signer.countLabels() < owner.countLabels();
}
-static bool isNSEC3AncestorDelegation(const DNSName& signer, const DNSName& owner, const std::shared_ptr<NSEC3RecordContent>& nsec3)
+#warning FIXME: should not be exported
+bool isNSEC3AncestorDelegation(const DNSName& signer, const DNSName& owner, const std::shared_ptr<NSEC3RecordContent>& nsec3)
{
return nsec3->isSet(QType::NS) &&
!nsec3->isSet(QType::SOA) &&
if (qname.isPartOf(wildcard)) {
LOG("\tWildcard matches");
- if (qtype == 0 || !nsec->isSet(qtype)) {
+ if (qtype == 0 || (!nsec->isSet(qtype) && !nsec->isSet(QType::CNAME))) {
LOG(" and proves that the type did not exist"<<endl);
return true;
}
number of labels than the intersection of its owner name and next name.
*/
const DNSName commonLabels = owner.getCommonLabels(nsec->d_next);
- unsigned int commonLabelsCount = commonLabels.countLabels();
+ const unsigned int commonLabelsCount = commonLabels.countLabels();
DNSName wildcard(qname);
unsigned int wildcardLabelsCount = wildcard.countLabels();
while (wildcard.chopOff() && wildcardLabelsCount >= commonLabelsCount) {
DNSName target = g_wildcarddnsname + wildcard;
- #warning BUG?? should we decerement wildcardLabelsCount??
+ --wildcardLabelsCount;
LOG("Comparing owner: "<<owner<<" with target: "<<target<<endl);
if (wildcardExists) {
*wildcardExists = true;
}
- if (qtype == 0 || !nsec3->isSet(qtype)) {
+ if (qtype == 0 || (!nsec3->isSet(qtype) && !nsec3->isSet(QType::CNAME))) {
LOG(" and proves that the type did not exist"<<endl);
return true;
}
LOG("Denies existence of name "<<qname<<"/"<<QType(qtype).getName());
nextCloserFound = true;
- if ((qtype == QType::DS || qtype == 0) && nsec3->d_flags & 1) {
+ if ((qtype == QType::DS || qtype == 0) && nsec3->isOptOut()) {
LOG(" but is opt-out!");
isOptOut = true;
}