Example #1
0
// Returns success if it adds the nsecs and the rrsigs to the cache record. Otherwise, it returns
// failure (mDNSfalse)
mDNSexport mDNSBool AddNSECSForCacheRecord(mDNS *const m, CacheRecord *crlist, CacheRecord *negcr, mDNSu8 rcode)
{
    CacheRecord *cr;
    mDNSBool nsecs_seen = mDNSfalse;
    mDNSBool nsec3s_seen = mDNSfalse;

    if (rcode != kDNSFlag1_RC_NoErr && rcode != kDNSFlag1_RC_NXDomain)
    {
        LogMsg("AddNSECSForCacheRecord: Addings nsecs for rcode %d", rcode);
        return mDNSfalse;
    }

    // Sanity check the list to see if we have anything else other than
    // NSECs and its RRSIGs
    for (cr = crlist; cr; cr = cr->next)
    {
        if (cr->resrec.rrtype != kDNSType_NSEC && cr->resrec.rrtype != kDNSType_NSEC3 &&
            cr->resrec.rrtype != kDNSType_SOA && cr->resrec.rrtype != kDNSType_RRSIG)
        {
            LogMsg("AddNSECSForCacheRecord: ERROR!! Adding Wrong record %s", CRDisplayString(m, cr));
            return mDNSfalse;
        }
        if (cr->resrec.rrtype == kDNSType_RRSIG)
        {
            RDataBody2 *const rdb = (RDataBody2 *)cr->smallrdatastorage.data;
            rdataRRSig *rrsig = &rdb->rrsig;
            mDNSu16 tc = swap16(rrsig->typeCovered);
            if (tc != kDNSType_NSEC && tc != kDNSType_NSEC3 && tc != kDNSType_SOA)
            {
                LogMsg("AddNSECSForCacheRecord:ERROR!! Adding RRSIG with Wrong type %s", CRDisplayString(m, cr));
                return mDNSfalse;
            }
        }
        else if (cr->resrec.rrtype == kDNSType_NSEC)
        {
            nsecs_seen = mDNStrue;
        }
        else if (cr->resrec.rrtype == kDNSType_NSEC3)
        {
            nsec3s_seen = mDNStrue;
        }
        LogDNSSEC("AddNSECSForCacheRecord: Found a valid record %s", CRDisplayString(m, cr));
    }
    if ((nsecs_seen && nsec3s_seen) || (!nsecs_seen && !nsec3s_seen))
    {
        LogDNSSEC("AddNSECSForCacheRecord:ERROR  nsecs_seen %d, nsec3s_seen %d", nsecs_seen, nsec3s_seen);
        return mDNSfalse;
    }
    DeleteCachedNSECS(m, negcr);
    LogDNSSEC("AddNSECSForCacheRecord: Adding NSEC Records for %s", CRDisplayString(m, negcr));
    negcr->nsec = crlist;
    return mDNStrue;
}
Example #2
0
mDNSexport void NSEC3NameErrorProof(mDNS *const m, DNSSECVerifier *dv, CacheRecord *ncr)
{
    CacheRecord *closerEncloser;
    CacheRecord *closestEncloser;
    CacheRecord *wildcard;
    const domainname *ce = mDNSNULL;
    domainname wild;

    if (!NSEC3ClosestEncloserProof(m, ncr, &dv->q.qname, &closestEncloser, &closerEncloser, &ce, dv->q.qtype))
    {
        goto error;
    }
    LogDNSSEC("NSEC3NameErrorProof: ClosestEncloser %s, ce %##s", CRDisplayString(m, closestEncloser), ce->c);
    LogDNSSEC("NSEC3NameErrorProof: CloserEncloser %s", CRDisplayString(m, closerEncloser));

    // *.closestEncloser should be covered by some nsec3 which would then prove
    // that the wildcard does not exist
    wild.c[0] = 1;
    wild.c[1] = '*';
    wild.c[2] = 0;
    if (!AppendDomainName(&wild, ce))
    {
        LogMsg("NSEC3NameErrorProof: Can't append domainname to closest encloser name %##s", ce->c);
        goto error;
    }
    if (!NSEC3Find(m, NSEC3Covers, ncr, &wild, mDNSNULL, &wildcard, mDNSNULL, dv->q.qtype))
    {
        LogMsg("NSEC3NameErrorProof: Cannot find encloser for wildcard");
        goto error;
    }
    else
    {
        LogDNSSEC("NSEC3NameErrorProof: Wildcard %##s covered by %s", wild.c, CRDisplayString(m, wildcard));
        if (wildcard == closestEncloser)
        {
            LogDNSSEC("NSEC3NameErrorProof: ClosestEncloser matching Wildcard %s", CRDisplayString(m, wildcard));
        }
    }
    if (NSEC3OptOut(closerEncloser))
    {
        dv->flags |= NSEC3_OPT_OUT;
    }
    if (!VerifyNSEC3(m, dv, ncr, closestEncloser, closerEncloser, wildcard, NameErrorNSECCallback))
        goto error;
    else
        return;

error:
    dv->DVCallback(m, dv, DNSSEC_Bogus);
}
Example #3
0
// Section 8.5, 8.6 of RFC 5155 first paragraph
mDNSlocal mDNSBool NSEC3NoDataError(mDNS *const m, CacheRecord *ncr, domainname *name, mDNSu16 qtype, CacheRecord **closestEncloser)
{
    const domainname *ce = mDNSNULL;

    *closestEncloser = mDNSNULL;
    // Note: This also covers ENT in which case the bitmap is empty
    if (NSEC3Find(m, NSEC3ClosestEncloser, ncr, name, closestEncloser, mDNSNULL, &ce, qtype))
    {
        int bmaplen;
        mDNSu8 *bmap;
        mDNSBool ns, soa;

        NSEC3Parse(&(*closestEncloser)->resrec, mDNSNULL, mDNSNULL, mDNSNULL, &bmaplen, &bmap);
        if (BitmapTypeCheck(bmap, bmaplen, qtype) || BitmapTypeCheck(bmap, bmaplen, kDNSType_CNAME))
        {
            LogMsg("NSEC3NoDataError: qtype %s exists in %s", DNSTypeName(qtype), CRDisplayString(m, *closestEncloser));
            return mDNSfalse;
        }
        ns = BitmapTypeCheck(bmap, bmaplen, kDNSType_NS);
        soa = BitmapTypeCheck(bmap, bmaplen, kDNSType_SOA);
        if (qtype != kDNSType_DS)
        {
            // For non-DS type questions, we don't want to use the parent side records to
            // answer it
            if (ns && !soa)
            {
                LogDNSSEC("NSEC3NoDataError: Parent side NSEC %s, can't use for child qname %##s (%s)",
                    CRDisplayString(m, *closestEncloser), name->c, DNSTypeName(qtype));
                return mDNSfalse;
            }
        }
        else
        {
            if (soa)
            {
                LogDNSSEC("NSEC3NoDataError: Child side NSEC %s, can't use for parent qname %##s (%s)",
                    CRDisplayString(m, *closestEncloser), name->c, DNSTypeName(qtype));
                return mDNSfalse;
            }
        }
        LogDNSSEC("NSEC3NoDataError: Name -%##s- exists, but qtype %s does not exist in %s", name->c, DNSTypeName(qtype), CRDisplayString(m, *closestEncloser));
        return mDNStrue;
    }
    return mDNSfalse;
}
// Find a matching NSEC3 record for the name. We parse the questions and the records in the packet in order.
// Similarly we also parse the NSEC3 records in order and this mapping to the questions and records
// respectively.
mDNSlocal CacheRecord *FindMatchingNSEC3ForName(mDNS *const m, CacheRecord **nsec3, const domainname *name)
{
    CacheRecord *cr;
    CacheRecord **prev = nsec3;
    
    (void) m;

    for (cr = *nsec3; cr; cr = cr->next)
    {
        if (SameDomainName(cr->resrec.name, name))
        {
            debugf("FindMatchingNSEC3ForName: NSEC3 record %s matched %##s", CRDisplayString(m, cr), name->c);
            *prev = cr->next;
            cr->next = mDNSNULL;
            return cr;
        }
        prev = &cr->next;
    }
    return mDNSNULL;
}
Example #5
0
// Does the NSEC3 in "ncr" covers the "name" ?
// hashName is hash of the "name" and b32Name is the base32 encoded equivalent.
mDNSlocal mDNSBool NSEC3CoversName(mDNS *const m, CacheRecord *ncr, const mDNSu8 *hashName, int hashLen, const mDNSu8 *b32Name,
	int b32len)
{
    mDNSu8 *nxtName;
    int nxtLength;
    int ret, ret1, ret2;
    const mDNSu8 b32nxtname[NSEC3_MAX_B32_LEN+1];
    int b32nxtlen;

    NSEC3Parse(&ncr->resrec, mDNSNULL, &nxtLength, &nxtName, mDNSNULL, mDNSNULL);

    if (nxtLength != hashLen || ncr->resrec.name->c[0] != b32len)
        return mDNSfalse;

    // Compare the owner names and the "nxt" names.
    //
    // Owner name is base32 encoded and hence use the base32 encoded name b32name.
    // nxt name is binary and hence use the binary value in hashName. 
    ret1 = NSEC3SameName(&ncr->resrec.name->c[1], ncr->resrec.name->c[0], b32Name, b32len);
    ret2 = DNSMemCmp(nxtName, hashName, hashLen);

#if NSEC3_DEBUG
    {
        char nxtbuf1[50];
        char nxtbuf2[50];

        PrintHash(nxtName, nxtLength, nxtbuf1, sizeof(nxtbuf1));
        PrintHash((mDNSu8 *)hashName, hashLen, nxtbuf2, sizeof(nxtbuf2));
        LogMsg("NSEC3CoversName: Owner name %s, name %s", &ncr->resrec.name->c[1], b32Name);
        LogMsg("NSEC3CoversName: Nxt hash name %s, name %s", nxtbuf1, nxtbuf2);
    }
#endif

    // "name" is greater than the owner name and smaller than nxtName. This also implies
	// that nxtName > owner name implying that it is normal NSEC3.
    if (ret1 < 0 && ret2 > 0)
    {
        LogDNSSEC("NSEC3CoversName: NSEC3 %s covers %s (Normal)", CRDisplayString(m, ncr), b32Name);
        return mDNStrue;
    }
    // Need to compare the owner name and "nxt" to see if this is the last
    // NSEC3 in the zone. Only the owner name is in base32 and hence we need to
    // convert the nxtName to base32.
    b32nxtlen = baseEncode((char *)b32nxtname, sizeof(b32nxtname), nxtName, nxtLength, ENC_BASE32);
    if (!b32nxtlen)
    {
        LogDNSSEC("NSEC3CoversName: baseEncode of nxtName of %s failed", CRDisplayString(m, ncr));
        return mDNSfalse;
    }
    if (b32len != b32nxtlen)
    {
        LogDNSSEC("NSEC3CoversName: baseEncode of nxtName for %s resulted in wrong length b32nxtlen %d, b32len %d",
            CRDisplayString(m, ncr), b32len, b32nxtlen);
        return mDNSfalse;
    }
    LogDNSSEC("NSEC3CoversName: Owner name %s, b32nxtname %s, ret1 %d, ret2 %d", &ncr->resrec.name->c[1], b32nxtname, ret1, ret2);

    // If it is the last NSEC3 in the zone nxt < "name" and NSEC3SameName returns -1.
    //
    // - ret1 < 0 means "name > owner"
    // - ret2 > 0 means "name < nxt"
    // 
    // Note: We also handle the case of only NSEC3 in the zone where NSEC3SameName returns zero.
    ret = NSEC3SameName(b32nxtname, b32nxtlen, &ncr->resrec.name->c[1], ncr->resrec.name->c[0]);
    if (ret <= 0 &&
        (ret1 < 0 || ret2 > 0))
    {
        LogDNSSEC("NSEC3CoversName: NSEC3 %s covers %s (Last), ret1 %d, ret2 %d", CRDisplayString(m, ncr), b32Name, ret1, ret2);
        return mDNStrue;
    }

    return mDNSfalse;
}
Example #6
0
mDNSexport CacheRecord *NSEC3RecordIsDelegation(mDNS *const m, domainname *name, mDNSu16 qtype)
{
    CacheGroup *cg;
    CacheRecord *cr;
    CacheRecord *ncr;
    mDNSu32 slot, namehash;

    slot = HashSlot(name);
    namehash = DomainNameHashValue(name);

    cg = CacheGroupForName(m, (const mDNSu32)slot, namehash, name);
    if (!cg)
    {
        LogDNSSEC("NSEC3RecordForName: cg NULL for %##s", name);
        return mDNSNULL;
    }
    for (ncr = cg->members; ncr; ncr = ncr->next)
    {
        if (ncr->resrec.RecordType != kDNSRecordTypePacketNegative ||
            ncr->resrec.rrtype != qtype)
        {
            continue;
        }
        for (cr = ncr->nsec; cr; cr = cr->next)
        {
            int hlen, b32len;
            const mDNSu8 hashName[NSEC3_MAX_HASH_LEN];
            const mDNSu8 b32Name[NSEC3_MAX_B32_LEN+1];
            const RDataBody2 *const rdb = (RDataBody2 *)cr->resrec.rdata->u.data;
            rdataNSEC3 *nsec3;

            if (cr->resrec.rrtype != kDNSType_NSEC3)
                continue;

            nsec3 = (rdataNSEC3 *)rdb->data;

            if (!NSEC3HashName(name, nsec3, mDNSNULL, 0, hashName, &hlen))
            {
                LogMsg("NSEC3RecordIsDelegation: NSEC3HashName failed for ##s", name->c);
                return mDNSNULL;
            }

            b32len = baseEncode((char *)b32Name, sizeof(b32Name), (mDNSu8 *)hashName, hlen, ENC_BASE32);
            if (!b32len)
            {
                LogMsg("NSEC3RecordIsDelegation: baseEncode of name %##s failed", name->c);
                return mDNSNULL;
            }
            // Section 2.3 of RFC 4035 states that:
            //
            // Each owner name in the zone that has authoritative data or a delegation point NS RRset MUST
            // have an NSEC resource record. 
            //
            // This applies to NSEC3 record. So, if we have an NSEC3 record matching the question name with the
            // NS bit set, then this is a delegation.
            //
            if (!NSEC3SameName(&cr->resrec.name->c[1], cr->resrec.name->c[0], (const mDNSu8 *)b32Name, b32len))
            {
                int bmaplen;
                mDNSu8 *bmap;
                
                LogDNSSEC("NSEC3RecordIsDelegation: CacheRecord %s matches name %##s, b32name %s", CRDisplayString(m, cr), name->c, b32Name);
                NSEC3Parse(&cr->resrec, mDNSNULL, mDNSNULL, mDNSNULL, &bmaplen, &bmap);

                // See the Insecure Delegation Proof section in dnssec-bis: DS bit and SOA bit
                // should be absent
                if (BitmapTypeCheck(bmap, bmaplen, kDNSType_SOA) ||
                    BitmapTypeCheck(bmap, bmaplen, kDNSType_DS))
                {
                    LogDNSSEC("NSEC3RecordIsDelegation: CacheRecord %s has DS or SOA bit set, ignoring", CRDisplayString(m, cr));
                    return mDNSNULL;
                }
                if (BitmapTypeCheck(bmap, bmaplen, kDNSType_NS))
                    return cr;
                else
                    return mDNSNULL;
            }
            // If opt-out is not set, then it does not cover any delegations
            if (!(nsec3->flags & NSEC3_FLAGS_OPTOUT))
                continue;
            // Opt-out allows insecure delegations to exist without the NSEC3 RR at the
            // hashed owner name (see RFC 5155 section 6.0).
            if (NSEC3CoversName(m, cr, hashName, hlen, b32Name, b32len))
            {
                LogDNSSEC("NSEC3RecordIsDelegation: CacheRecord %s covers name %##s with optout", CRDisplayString(m, cr), name->c);
                return cr;
            }
        }
    }
    return mDNSNULL;
}
Example #7
0
mDNSexport void NSEC3NoDataProof(mDNS *const m, DNSSECVerifier *dv, CacheRecord *ncr)
{
    CacheRecord *closerEncloser = mDNSNULL;
    CacheRecord *closestEncloser = mDNSNULL;
    CacheRecord *wildcard = mDNSNULL;
    const domainname *ce = mDNSNULL;
    domainname wild;

    // Section 8.5, 8.6 of RFC 5155
    if (NSEC3NoDataError(m, ncr, &dv->q.qname, dv->q.qtype, &closestEncloser))
    {
        goto verify;
    }
    // Section 8.6, 8.7: if we can't find the NSEC3 RR, verify the closest encloser proof
    // for QNAME and the "next closer" should have the opt out
    if (!NSEC3ClosestEncloserProof(m, ncr, &dv->q.qname, &closestEncloser, &closerEncloser, &ce, dv->q.qtype))
    {
        goto error;
    }

    // Section 8.7: find a matching NSEC3 for *.closestEncloser
    wild.c[0] = 1;
    wild.c[1] = '*';
    wild.c[2] = 0;
    if (!AppendDomainName(&wild, ce))
    {
        LogMsg("NSEC3NameErrorProof: Can't append domainname to closest encloser name %##s", ce->c);
        goto error;
    }
    if (!NSEC3Find(m, NSEC3ClosestEncloser, ncr, &wild, &wildcard, mDNSNULL, &ce, dv->q.qtype))
    {
        // Not a wild card case. Section 8.6 second para applies.
        LogDNSSEC("NSEC3NoDataProof: Cannot find encloser for wildcard, perhaps not a wildcard case");
        if (!NSEC3OptOut(closerEncloser))
        {
            LogDNSSEC("NSEC3DataProof: opt out not set for %##s (%s), bogus", dv->q.qname.c, DNSTypeName(dv->q.qtype));
            goto error;
        }
        LogDNSSEC("NSEC3DataProof: opt out set, proof complete for %##s (%s)", dv->q.qname.c, DNSTypeName(dv->q.qtype));
        dv->flags |= NSEC3_OPT_OUT;
    }
    else
    {
        int bmaplen;
        mDNSu8 *bmap;
        NSEC3Parse(&wildcard->resrec, mDNSNULL, mDNSNULL, mDNSNULL, &bmaplen, &bmap);
        if (BitmapTypeCheck(bmap, bmaplen, dv->q.qtype) || BitmapTypeCheck(bmap, bmaplen, kDNSType_CNAME))
        {
            LogDNSSEC("NSEC3NoDataProof: qtype %s exists in %s", DNSTypeName(dv->q.qtype), CRDisplayString(m, wildcard));
            goto error;
        }
        if (dv->q.qtype == kDNSType_DS && BitmapTypeCheck(bmap, bmaplen, kDNSType_SOA))
        {
            LogDNSSEC("NSEC3NoDataProof: Child side wildcard NSEC3 %s, can't use for parent qname %##s (%s)",
                CRDisplayString(m, wildcard), dv->q.qname.c, DNSTypeName(dv->q.qtype));
            goto error;
        }
        else if (dv->q.qtype != kDNSType_DS && !BitmapTypeCheck(bmap, bmaplen, kDNSType_SOA) &&
            BitmapTypeCheck(bmap, bmaplen, kDNSType_NS))
        {
            // Don't use the parent side record for this
            LogDNSSEC("NSEC3NoDataProof: Parent side wildcard NSEC3 %s, can't use for child qname %##s (%s)",
                CRDisplayString(m, wildcard), dv->q.qname.c, DNSTypeName(dv->q.qtype));
            goto error;
        }
        LogDNSSEC("NSEC3NoDataProof: Wildcard %##s matched by %s", wild.c, CRDisplayString(m, wildcard));
    }
verify:

    if (!VerifyNSEC3(m, dv, ncr, closestEncloser, closerEncloser, wildcard, NoDataNSECCallback))
        goto error;
    else
        return;
error:
    dv->DVCallback(m, dv, DNSSEC_Bogus);
}
Example #8
0
mDNSlocal mDNSBool NSEC3ClosestEncloserProof(mDNS *const m, CacheRecord *ncr, domainname *name, CacheRecord **closestEncloser, CacheRecord **closerEncloser,
	const domainname **ce, mDNSu16 qtype)
{
    if (!NSEC3Find(m, NSEC3CEProof, ncr, name, closestEncloser, closerEncloser, ce, qtype))
    {
        LogDNSSEC("NSEC3ClosestEncloserProof: ERROR!! Cannot do closest encloser proof");
        return mDNSfalse;
    }

    // Note: It is possible that closestEncloser and closerEncloser are the same.
    if (!closestEncloser || !closerEncloser || !ce)
    {
        LogMsg("NSEC3ClosestEncloserProof: ClosestEncloser %p or CloserEncloser %p ce %p, something is NULL", *closestEncloser,
            *closerEncloser, *ce);
        return mDNSfalse;
    }

    // If the name exists, we should not have gotten the name error
    if (SameDomainName((*ce), name))
    {
        LogMsg("NSEC3ClosestEncloserProof: ClosestEncloser %s same as origName %##s", CRDisplayString(m, *closestEncloser),
            (*ce)->c);
        return mDNSfalse;
    }
    return mDNStrue;
}
Example #9
0
// This function can be called with NSEC3ClosestEncloser, NSEC3Covers and NSEC3CEProof
//
// Passing in NSEC3ClosestEncloser means "find an exact match for the origName".
// Passing in NSEC3Covers means "find an NSEC3 that covers the origName".
//
// i.e., in both cases the nsec3 records are iterated to find the best match and returned.
// With NSEC3ClosestEncloser, as we are just looking for a name match, extra checks for
// the types being present or absent will not be checked.
//
// If NSEC3CEProof is passed, the name is tried as such first by iterating through all NSEC3s
// finding a ClosestEncloser or CloserEncloser and then one label skipped from the left and
// retried again till both the closest and closer encloser is found.
//
// ncr is the negative cache record that has the NSEC3 chain
// origName is the name for which we are trying to find the ClosestEncloser etc.
// closestEncloser and closerEncloser are the return values of the function
// ce is the closest encloser that will be returned if we find one
mDNSlocal mDNSBool NSEC3Find(mDNS *const m, NSEC3FindValues val, CacheRecord *ncr, domainname *origName, CacheRecord **closestEncloser,
	CacheRecord **closerEncloser, const domainname **ce, mDNSu16 qtype)
{
    int i;
    int labelCount = CountLabels(origName);
    CacheRecord *cr;
    rdataNSEC3 *nsec3;

    (void) qtype; // unused
    // Pick the first NSEC for the iterations, salt etc.
    for (cr = ncr->nsec; cr; cr = cr->next)
    {
        if (cr->resrec.rrtype == kDNSType_NSEC3)
        {
            const RDataBody2 *const rdb = (RDataBody2 *)cr->resrec.rdata->u.data;
            nsec3 = (rdataNSEC3 *)rdb->data;
            break;
        }
    }
    if (!cr)
    {
        LogMsg("NSEC3Find: cr NULL");
        return mDNSfalse;
    }

    // Note: The steps defined in this function are for "NSEC3CEProof". As part of NSEC3CEProof,
    // we need to find both the closestEncloser and closerEncloser which can also be found
    // by passing NSEC3ClosestEncloser and NSEC3Covers respectively.
    //
    // Section 8.3 of RFC 5155.
    // 1.  Set SNAME=QNAME.  Clear the flag.
    //
    // closerEncloser is the "flag". "name" below is SNAME.

    if (closestEncloser)
    {
        *ce = mDNSNULL;
        *closestEncloser = mDNSNULL;
    }
    if (closerEncloser)
        *closerEncloser = mDNSNULL;

    // If we are looking for a closestEncloser or a covering NSEC3, we don't have
    // to truncate the name. For the give name, try to find the closest or closer
    // encloser.
    if (val != NSEC3CEProof)
    {
        labelCount = 0;
    }

    for (i = 0; i < labelCount + 1; i++)
    { 
        int hlen;
        const mDNSu8 hashName[NSEC3_MAX_HASH_LEN];
        const domainname *name;
        const mDNSu8 b32Name[NSEC3_MAX_B32_LEN+1];
        int b32len;

        name = SkipLeadingLabels(origName, i);
        if (!NSEC3HashName(name, nsec3, mDNSNULL, 0, hashName, &hlen))
        {
            LogMsg("NSEC3Find: NSEC3HashName failed for ##s", name->c);
            continue;
        }

        b32len = baseEncode((char *)b32Name, sizeof(b32Name), (mDNSu8 *)hashName, hlen, ENC_BASE32);
        if (!b32len)
        {
            LogMsg("NSEC3Find: baseEncode of name %##s failed", name->c);
            continue;
        }


        for (cr = ncr->nsec; cr; cr = cr->next)
        {
            const domainname *nsecZone;
            int result, subdomain;

            if (cr->resrec.rrtype != kDNSType_NSEC3)
                continue;

            nsecZone = SkipLeadingLabels(cr->resrec.name, 1);
            if (!nsecZone)
            {
                LogMsg("NSEC3Find: SkipLeadingLabel failed for %s, current name %##s",
                    CRDisplayString(m, cr), name->c);
                continue;
            }

            // NSEC3 owner names are formed by hashing the owner name and then appending
            // the zone name to it. If we skip the first label, the rest should be
            // the zone name. See whether it is the subdomain of the name we are looking
            // for. 
            result = DNSSECCanonicalOrder(origName, nsecZone, &subdomain);
            
            // The check can't be a strict subdomain check. When NSEC3ClosestEncloser is
            // passed in, there can be an exact match. If it is a subdomain or an exact
            // match, we should continue with the proof.
            if (!(subdomain || !result))
            {
                LogMsg("NSEC3Find: NSEC3 %s not a subdomain of %##s, result %d", CRDisplayString(m, cr),
                    origName->c, result);
                continue;
            }

            // 2.1) If there is no NSEC3 RR in the response that matches SNAME
            // (i.e., an NSEC3 RR whose owner name is the same as the hash of
            // SNAME, prepended as a single label to the zone name), clear
            // the flag.
            //
            // Note: We don't try to determine the actual zone name. We know that
            // the labels following the hash (nsecZone) is the ancestor and we don't
            // know where the zone cut is. Hence, we verify just the hash to be
            // the same.

            if (val == NSEC3ClosestEncloser || val == NSEC3CEProof)
            {
                if (!NSEC3SameName(&cr->resrec.name->c[1], cr->resrec.name->c[0], (const mDNSu8 *)b32Name, b32len))
                {
                    int bmaplen;
                    mDNSu8 *bmap;

                    // For NSEC3ClosestEncloser, we are finding an exact match and
                    // "type" specific checks should be done by the caller.
                    if (val != NSEC3ClosestEncloser)
                    {
                        // DNAME bit must not be set and NS bit may be set only if SOA bit is set
                        NSEC3Parse(&cr->resrec, mDNSNULL, mDNSNULL, mDNSNULL, &bmaplen, &bmap);
                        if (BitmapTypeCheck(bmap, bmaplen, kDNSType_DNAME))
                        {
                            LogDNSSEC("NSEC3Find: DNAME bit set in %s, ignoring", CRDisplayString(m, cr));
                            return mDNSfalse;
                        }
                        // This is the closest encloser and should come from the right zone.
                        if (BitmapTypeCheck(bmap, bmaplen, kDNSType_NS) &&
                            !BitmapTypeCheck(bmap, bmaplen, kDNSType_SOA))
                        {
                            LogDNSSEC("NSEC3Find: NS bit set without SOA bit in %s, ignoring", CRDisplayString(m, cr));
                            return mDNSfalse;
                        }
                    }

                    LogDNSSEC("NSEC3Find: ClosestEncloser %s found for name %##s", CRDisplayString(m, cr), name->c);
                    if (closestEncloser)
                    {
                        *ce = name;
                        *closestEncloser = cr;
                    }
                    if (val == NSEC3ClosestEncloser)
                        return mDNStrue;
                    else
                        break;
                }
            }

            if ((val == NSEC3Covers || val == NSEC3CEProof) && !(*closerEncloser))
            {
                if (NSEC3CoversName(m, cr, hashName, hlen, b32Name, b32len))
                {
                    // 2.2) If there is an NSEC3 RR in the response that covers SNAME, set the flag.
                    if (closerEncloser)
                        *closerEncloser = cr;
                    if (val == NSEC3Covers)
                        return mDNStrue;
                    else
                        break;
                }
            }
        }
        // 2.3) If there is a matching NSEC3 RR in the response and the flag
        // was set, then the proof is complete, and SNAME is the closest
        // encloser.
        if (val == NSEC3CEProof)
        {
            if (*closestEncloser && *closerEncloser)
            {
                LogDNSSEC("NSEC3Find: Found closest and closer encloser");
                return mDNStrue;
            }

            // 2.4) If there is a matching NSEC3 RR in the response, but the flag
            // is not set, then the response is bogus.
            //
            // Note: We don't have to wait till we finish trying all the names. If the matchName
            // happens, we found the closest encloser which means we should have found the closer
            // encloser before.

            if (*closestEncloser && !(*closerEncloser))
            {
                LogDNSSEC("NSEC3Find: Found closest, but not closer encloser");
                return mDNSfalse;
            }
        }
        // 3.  Truncate SNAME by one label from the left, go to step 2.
    }
    LogDNSSEC("NSEC3Find: Cannot find name %##s (%s)", origName->c, DNSTypeName(qtype));
    return mDNSfalse;
}
Example #10
0
// If the answer was result of a wildcard match, then this function proves
// that a proper wildcard was used to answer the question and that the
// original name does not exist
mDNSexport void WildcardAnswerProof(mDNS *const m, DNSSECVerifier *dv)
{
    CacheRecord *ncr;
    CacheRecord **rp;
    const domainname *ce;
    DNSQuestion q;
    CacheRecord **nsec3 = mDNSNULL;

    LogDNSSEC("WildcardAnswerProof: Question %##s (%s)", dv->origName.c, DNSTypeName(dv->origType));
    //
    // RFC 4035: Section 3.1.3.3
    //
    // 1) We used a wildcard because the qname does not exist, so verify
    //    that the qname does not exist
    //
    // 2) Is the wildcard the right one ?
    //
    // Unfortunately, this is not well explained in that section. Refer to
    // RFC 5155 section 7.2.6.

    // Walk the list of nsecs we received and see if they prove that
    // the name does not exist

    mDNSPlatformMemZero(&q, sizeof(DNSQuestion));
    q.ThisQInterval = -1;
    InitializeQuestion(m, &q, dv->InterfaceID, &dv->origName, dv->origType, mDNSNULL, mDNSNULL);

    ncr = NSECParentForQuestion(m, &q);
    if (!ncr)
    {
        LogMsg("WildcardAnswerProof: Can't find NSEC Parent for %##s (%s)", q.qname.c, DNSTypeName(q.qtype));
        goto error;
    }
    else
    {
        LogDNSSEC("WildcardAnswerProof: found %s", CRDisplayString(m, ncr));
    }
    rp = &(ncr->nsec);
    while (*rp)
    {
        if ((*rp)->resrec.rrtype == kDNSType_NSEC)
        {
            CacheRecord *cr = *rp;
            if (!NSECNameExists(m, &cr->resrec, &dv->origName, dv->origType))
                break;
        }
        else if ((*rp)->resrec.rrtype == kDNSType_NSEC3)
        {
            nsec3 = rp;
        }
        rp=&(*rp)->next;
    }
    if (!(*rp))
    {
        mDNSBool ret = mDNSfalse;
        if (nsec3)
        {
            ret = NSEC3WildcardAnswerProof(m, ncr, dv);
        }
        if (!ret)
        {
            LogDNSSEC("WildcardAnswerProof: NSEC3 wildcard proof failed for %##s (%s)", q.qname.c, DNSTypeName(q.qtype));
            goto error;
        }
        rp = nsec3;
    }
    else
    {
        ce = NSECClosestEncloser(&((*rp)->resrec), &dv->origName);
        if (!ce)
        {
            LogMsg("WildcardAnswerProof: ERROR!! Closest Encloser NULL for %##s (%s)", q.qname.c, DNSTypeName(q.qtype));
            goto error;
        }
        if (!SameDomainName(ce, dv->wildcardName))
        {
            LogMsg("WildcardAnswerProof: ERROR!! Closest Encloser %##s does not match wildcard name %##s", q.qname.c, dv->wildcardName->c);
            goto error;
        }
    }

    VerifyNSEC(m, &((*rp)->resrec), mDNSNULL, dv, ncr, mDNSNULL);
    return;
error:
    dv->DVCallback(m, dv, DNSSEC_Bogus);
}
Example #11
0
mDNSexport void ValidateWithNSECS(mDNS *const m, DNSSECVerifier *dv, CacheRecord *cr)
{
    LogDNSSEC("ValidateWithNSECS: called for %s", CRDisplayString(m, cr));

    // If we are encountering a break in the chain of trust i.e., NSEC/NSEC3s for
    // DS query, then do the insecure proof. This is important because if we
    // validate these NSECs normally and prove that they are "secure", we will
    // end up delivering the secure result to the original question where as
    // these NSEC/NSEC3s actually prove that DS does not exist and hence insecure.
    //
    // This break in the chain can happen after we have partially validated the
    // path (dv->ac is non-NULL) or the first time (dv->ac is NULL) after we
    // fetched the DNSKEY (dv->key is non-NULL). We don't want to do this
    // if we have just started the non-existence proof (dv->key is NULL) as
    // it does not indicate a break in the chain of trust.
    //
    // If we are already doing a insecurity proof, don't start another one. In
    // the case of NSECs, it is possible that insecurity proof starts and it
    // gets NSECs and as part of validating that we receive more NSECS in which
    // case we don't want to start another insecurity proof.
    if (dv->ValidationRequired != DNSSEC_VALIDATION_INSECURE &&
        (!dv->parent || dv->parent->ValidationRequired != DNSSEC_VALIDATION_INSECURE))
    {
         if ((dv->ac && dv->q.qtype == kDNSType_DS) ||
             (!dv->ac && dv->key && dv->q.qtype == kDNSType_DS))
        {
            LogDNSSEC("ValidateWithNSECS: Starting insecure proof: name %##s ac %p, key %p, parent %p", dv->q.qname.c,
                dv->ac, dv->key, dv->parent);
            StartInsecureProof(m, dv);
            return;
        }
    }
    // "parent" is set when we are validating a NSEC and we should not be here in
    // the normal case when parent is set. For example, we are looking up the A
    // record for www.example.com and following can happen.
    //
    // a) Record does not exist and we get a NSEC
    // b) While validating (a), we get an NSEC for the first DS record that we look up
    // c) Record exists but we get NSECs for the first DS record
    // d) We are able to partially validate (a) or (b), but we get NSECs somewhere in
    //    the chain
    //
    // For (a), parent is not set as we are not validating the NSEC yet. Hence we would
    // start the validation now.
    //
    // For (b), the parent is set, but should be caught by the above "if" block because we 
    // should have gotten the DNSKEY at least. In the case of nested insecurity proof,
    // we would end up here and fail with bogus.
    //
    // For (c), the parent is not set and should be caught by the above "if" block because we 
    // should have gotten the DNSKEY at least.
    //
    // For (d), the above "if" block would catch it as "dv->ac" is non-NULL.
    // 
    // Hence, we should not come here in the normal case. Possible pathological cases are:
    // Insecure proof getting NSECs while validating NSECs, getting NSECs for DNSKEY for (c)
    // above etc.
    if (dv->parent)
    {
        LogDNSSEC("ValidateWithNSECS: dv parent set for %##s (%s)", dv->q.qname.c, DNSTypeName(dv->q.qtype));
        dv->DVCallback(m, dv, DNSSEC_Bogus);
        return;
    }
    if (cr->resrec.RecordType == kDNSRecordTypePacketNegative)
    {
        mDNSu8 rcode;
        CacheRecord *neg = cr->nsec;
        mDNSBool nsecs_seen = mDNSfalse;

        while (neg)
        {
            // The list can only have NSEC or NSEC3s. This was checked when we added the
            // NSECs to the cache record.
            if (neg->resrec.rrtype == kDNSType_NSEC)
                nsecs_seen = mDNStrue;
            LogDNSSEC("ValidateWithNSECS: NSECCached Record %s", CRDisplayString(m, neg));
            neg = neg->next;
        }

        rcode = (mDNSu8)(cr->responseFlags.b[1] & kDNSFlag1_RC_Mask);
        if (rcode == kDNSFlag1_RC_NoErr)
        {
            if (nsecs_seen)
                NoDataProof(m, dv, cr);
            else
                NSEC3NoDataProof(m, dv, cr);
        }
        else if (rcode == kDNSFlag1_RC_NXDomain)
        {
            if (nsecs_seen)
                NameErrorProof(m, dv, cr);
            else
                NSEC3NameErrorProof(m, dv, cr);
        }
        else
        {
            LogDNSSEC("ValidateWithNSECS: Rcode %d invalid", rcode);
            dv->DVCallback(m, dv, DNSSEC_Bogus);
        }
    }
    else
    {
        LogMsg("ValidateWithNSECS: Not a valid cache record %s for NSEC proofs", CRDisplayString(m, cr));
        dv->DVCallback(m, dv, DNSSEC_Bogus);
        return;
    }
}
Example #12
0
mDNSexport CacheRecord *NSECRecordIsDelegation(mDNS *const m, domainname *name, mDNSu16 qtype)
{
    CacheGroup *cg;
    CacheRecord *cr;
    mDNSu32 slot, namehash;

    slot = HashSlot(name);
    namehash = DomainNameHashValue(name);

    cg = CacheGroupForName(m, (const mDNSu32)slot, namehash, name);
    if (!cg)
    {
        LogDNSSEC("NSECRecordForName: cg NULL for %##s", name);
        return mDNSNULL;
    }
    for (cr = cg->members; cr; cr = cr->next)
    {
        if (cr->resrec.RecordType == kDNSRecordTypePacketNegative && cr->resrec.rrtype == qtype)
        {
            CacheRecord *ncr;
            for (ncr = cr->nsec; ncr; ncr = ncr->next)
            {
                if (ncr->resrec.rrtype == kDNSType_NSEC &&
                    SameDomainName(ncr->resrec.name, name))
                {
                    // See the Insecure Delegation Proof section in dnssec-bis: DS bit and SOA bit
                    // should be absent
                    if (RRAssertsExistence(&ncr->resrec, kDNSType_SOA) ||
                        RRAssertsExistence(&ncr->resrec, kDNSType_DS))
                    {
                        LogDNSSEC("NSECRecordForName: found record %s for %##s (%s), but DS or SOA bit set", CRDisplayString(m, ncr), name,
                            DNSTypeName(qtype));
                        return mDNSNULL;
                    }
                    // Section 2.3 of RFC 4035 states that:
                    //
                    // Each owner name in the zone that has authoritative data or a delegation point NS RRset MUST
                    // have an NSEC resource record. 
                    //
                    // So, if we have an NSEC record matching the question name with the NS bit set,
                    // then this is a delegation.
                    //
                    if (RRAssertsExistence(&ncr->resrec, kDNSType_NS))
                    {
                        LogDNSSEC("NSECRecordForName: found record %s for %##s (%s)", CRDisplayString(m, ncr), name, DNSTypeName(qtype));
                        return ncr;
                    }
                    else
                    {
                        LogDNSSEC("NSECRecordForName: found record %s for %##s (%s), but NS bit is not set", CRDisplayString(m, ncr), name,
                            DNSTypeName(qtype));
                        return mDNSNULL;
                    }
                }
            }
        }
    }
    return mDNSNULL;
}