mDNSlocal mDNSBool VerifyNSEC3(mDNS *const m, DNSSECVerifier *dv, CacheRecord *ncr, CacheRecord *closestEncloser, CacheRecord *closerEncloser, CacheRecord *wildcard, DNSSECVerifierCallback callback) { mStatus status; RRVerifier *r; // We have three NSEC3s. If any of two are same, we should just prove one of them. // This is just not an optimization; DNSSECNegativeValidationCB does not handle // identical NSEC3s very well. if (closestEncloser == closerEncloser) closestEncloser = mDNSNULL; if (closerEncloser == wildcard) closerEncloser = mDNSNULL; if (closestEncloser == wildcard) closestEncloser = mDNSNULL; dv->pendingNSEC = mDNSNULL; if (closestEncloser) { r = AllocateRRVerifier(&closestEncloser->resrec, &status); if (!r) return mDNSfalse; r->next = dv->pendingNSEC; dv->pendingNSEC = r; } if (closerEncloser) { r = AllocateRRVerifier(&closerEncloser->resrec, &status); if (!r) return mDNSfalse; r->next = dv->pendingNSEC; dv->pendingNSEC = r; } if (wildcard) { r = AllocateRRVerifier(&wildcard->resrec, &status); if (!r) return mDNSfalse; r->next = dv->pendingNSEC; dv->pendingNSEC = r; } if (!dv->pendingNSEC) { LogMsg("VerifyNSEC3: ERROR!! pending NSEC null"); return mDNSfalse; } r = dv->pendingNSEC; dv->pendingNSEC = r->next; r->next = mDNSNULL; LogDNSSEC("VerifyNSEC3: Verifying %##s (%s)", r->name.c, DNSTypeName(r->rrtype)); if (!dv->pendingNSEC) VerifyNSEC(m, mDNSNULL, r, dv, ncr, mDNSNULL); else VerifyNSEC(m, mDNSNULL, r, dv, ncr, callback); return mDNStrue; }
// We get a NXDOMAIN error with no records in answer section. This proves // that qname does not exist. mDNSlocal void NameErrorProof(mDNS *const m, DNSSECVerifier *dv, CacheRecord *ncr) { CacheRecord **rp; ResourceRecord *nsec_wild = mDNSNULL; ResourceRecord *nsec_noname = mDNSNULL; mStatus status; // NXDOMAIN Error. We need to prove that the qname does not exist and there // is no wildcard that can be used to answer the question. rp = &(ncr->nsec); while (*rp) { if ((*rp)->resrec.rrtype == kDNSType_NSEC) { CacheRecord *cr = *rp; if (!NSECNameExists(m, &cr->resrec, &dv->q.qname, dv->q.qtype)) { LogDNSSEC("NameErrorProof: NSEC %s proves name does not exist for %##s (%s)", RRDisplayString(m, &(*rp)->resrec), dv->q.qname.c, DNSTypeName(dv->q.qtype)); // If we have a wildcard, then we should check to see if the closest // encloser is the same as the wildcard. dv->flags |= NSEC_PROVES_NONAME_EXISTS; nsec_noname = &cr->resrec; } if (NSECNoWildcard(m, &cr->resrec, &dv->q.qname, dv->q.qtype)) { dv->flags |= WILDCARD_PROVES_NONAME_EXISTS; nsec_wild = &cr->resrec; LogDNSSEC("NameErrorProof: NSEC %s proves wildcard cannot answer question for %##s (%s)", RRDisplayString(m, &(*rp)->resrec), dv->q.qname.c, DNSTypeName(dv->q.qtype)); } } rp=&(*rp)->next; } if (!nsec_noname || !nsec_wild) { LogMsg("NameErrorProof: Proof failed for %##s (%s) noname %p, wild %p", dv->q.qname.c, DNSTypeName(dv->q.qtype), nsec_noname, nsec_wild); goto error; } // First verify wildcard NSEC and then when we are done, we will verify the noname nsec. // Sometimes a single NSEC can prove both that the "qname" does not exist and a wildcard // could not have produced qname. These are a few examples where this can happen. // // 1. If the zone is example.com and you look up *.example.com and if there are no wildcards, // you will get a NSEC back "example.com NSEC a.example.com". This proves that both the // name does not exist and *.example.com also does not exist // // 2. If the zone is example.com and it has a record like this: // // example.com NSEC d.example.com // // any name you lookup in between like a.example.com,b.example.com etc. you will get a single // NSEC back. In that case we just have to verify only once. // if (nsec_wild != nsec_noname) { RRVerifier *r = AllocateRRVerifier(nsec_noname, &status); if (!r) goto error; dv->pendingNSEC = r; LogDNSSEC("NoDataProof: Verifying wild %s", RRDisplayString(m, nsec_wild)); VerifyNSEC(m, nsec_wild, mDNSNULL, dv, ncr, NameErrorNSECCallback); } else { LogDNSSEC("NoDataProof: Verifying only one %s", RRDisplayString(m, nsec_wild)); VerifyNSEC(m, nsec_wild, mDNSNULL, dv, ncr, mDNSNULL); } return; error: dv->DVCallback(m, dv, DNSSEC_Bogus); }
// We get a NODATA error with no records in answer section. This proves // that qname does not exist. mDNSlocal void NoDataProof(mDNS *const m, DNSSECVerifier *dv, CacheRecord *ncr) { CacheRecord **rp; domainname *wildcard = mDNSNULL; const domainname *ce = mDNSNULL; ResourceRecord *nsec_wild = mDNSNULL; ResourceRecord *nsec_noname = mDNSNULL; // NODATA Error could mean two things. The name exists with no type or there is a // wildcard that matches the name but no type. This is done by NSECNoDataError. // // If it is the case of wildcard, there are two NSECs. One is the wildcard NSEC and // the other NSEC to prove that there is no other closer match. wildcard = mDNSNULL; rp = &(ncr->nsec); while (*rp) { if ((*rp)->resrec.rrtype == kDNSType_NSEC) { CacheRecord *cr = *rp; if (NSECNoDataError(m, &cr->resrec, &dv->q.qname, dv->q.qtype, &wildcard)) { if (wildcard) { dv->flags |= WILDCARD_PROVES_NONAME_EXISTS; LogDNSSEC("NoDataProof: NSEC %s proves NODATA error for %##s (%s)", RRDisplayString(m, &(*rp)->resrec), dv->q.qname.c, DNSTypeName(dv->q.qtype)); } else { dv->flags |= NSEC_PROVES_NOTYPE_EXISTS; LogDNSSEC("NoDataProof: NSEC %s proves NOTYPE error for %##s (%s)", RRDisplayString(m, &(*rp)->resrec), dv->q.qname.c, DNSTypeName(dv->q.qtype)); } nsec_wild = &cr->resrec; } if (!NSECNameExists(m, &cr->resrec, &dv->q.qname, dv->q.qtype)) { LogDNSSEC("NoDataProof: NSEC %s proves that name %##s (%s) does not exist", RRDisplayString(m, &(*rp)->resrec), dv->q.qname.c, DNSTypeName(dv->q.qtype)); // If we have a wildcard, then we should check to see if the closest // encloser is the same as the wildcard. ce = NSECClosestEncloser(&cr->resrec, &dv->q.qname); dv->flags |= NSEC_PROVES_NONAME_EXISTS; nsec_noname = &cr->resrec; } } rp=&(*rp)->next; } if (!nsec_noname && !nsec_wild) { LogDNSSEC("NoDataProof: No valid NSECs for %##s (%s)", dv->q.qname.c, DNSTypeName(dv->q.qtype)); goto error; } // If the type exists, then we have to verify just that NSEC if (!(dv->flags & NSEC_PROVES_NOTYPE_EXISTS)) { // If we have a wildcard, then we should have a "ce" which matches the wildcard // If we don't have a wildcard, then we should have proven that the name does not // exist which means we would have set the "ce". if (wildcard && !ce) { LogMsg("NoDataProof: Cannot prove that the name %##s (%s) does not exist", dv->q.qname.c, DNSTypeName(dv->q.qtype)); goto error; } if (wildcard && !SameDomainName(wildcard, ce)) { LogMsg("NoDataProof: wildcard %##s does not match closest encloser %##s", wildcard->c, ce->c); goto error; } // If a single NSEC can prove both, then we just have validate that one NSEC. if (nsec_wild == nsec_noname) { nsec_noname = mDNSNULL; dv->flags &= ~NSEC_PROVES_NONAME_EXISTS; } } if ((dv->flags & (WILDCARD_PROVES_NONAME_EXISTS|NSEC_PROVES_NONAME_EXISTS)) == (WILDCARD_PROVES_NONAME_EXISTS|NSEC_PROVES_NONAME_EXISTS)) { mStatus status; RRVerifier *r = AllocateRRVerifier(nsec_noname, &status); if (!r) goto error; // First verify wildcard NSEC and then when we are done, we // will verify the noname nsec dv->pendingNSEC = r; LogDNSSEC("NoDataProof: Verifying wild and noname %s", RRDisplayString(m, nsec_wild)); VerifyNSEC(m, nsec_wild, mDNSNULL, dv, ncr, NoDataNSECCallback); } else if ((dv->flags & WILDCARD_PROVES_NONAME_EXISTS) || (dv->flags & NSEC_PROVES_NOTYPE_EXISTS)) { LogDNSSEC("NoDataProof: Verifying wild %s", RRDisplayString(m, nsec_wild)); VerifyNSEC(m, nsec_wild, mDNSNULL, dv, ncr, mDNSNULL); } else if (dv->flags & NSEC_PROVES_NONAME_EXISTS) { LogDNSSEC("NoDataProof: Verifying noname %s", RRDisplayString(m, nsec_noname)); VerifyNSEC(m, nsec_noname, mDNSNULL, dv, ncr, mDNSNULL); } return; error: LogDNSSEC("NoDataProof: Error return"); dv->DVCallback(m, dv, DNSSEC_Bogus); }