1/* -*- Mode: C; tab-width: 4 -*-
2 *
3 * Copyright (c) 2011 Apple Computer, Inc. All rights reserved.
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 *     http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18// ***************************************************************************
19// nsec3.c: This file contains support functions to validate NSEC3 records for
20// NODATA and NXDOMAIN error.
21// ***************************************************************************
22
23#include "mDNSEmbeddedAPI.h"
24#include "DNSCommon.h"
25#include "CryptoAlg.h"
26#include "nsec3.h"
27#include "nsec.h"
28
29// Define DNSSEC_DISABLED to remove all the DNSSEC functionality
30// and use the stub functions implemented later in this file.
31
32#ifndef DNSSEC_DISABLED
33
34typedef enum
35{
36    NSEC3ClosestEncloser,
37    NSEC3Covers,
38    NSEC3CEProof
39} NSEC3FindValues;
40
41//#define NSEC3_DEBUG 1
42
43#if NSEC3_DEBUG
44mDNSlocal void PrintHash(mDNSu8 *digest, int digestlen, char *buffer, int buflen)
45{
46    int length = 0;
47    for (int j = 0; j < digestlen; j++)
48    {
49        length += mDNS_snprintf(buffer+length, buflen-length-1, "%x", digest[j]);
50    }
51}
52#endif
53
54mDNSlocal mDNSBool NSEC3OptOut(CacheRecord *cr)
55{
56    const RDataBody2 *const rdb = (RDataBody2 *)cr->resrec.rdata->u.data;
57    rdataNSEC3 *nsec3 = (rdataNSEC3 *)rdb->data;
58    return (nsec3->flags & NSEC3_FLAGS_OPTOUT);
59}
60
61mDNSlocal int NSEC3SameName(const mDNSu8 *name, int namelen, const mDNSu8 *nsecName, int nsecLen)
62{
63    int i;
64
65    // Note: With NSEC3, the lengths should always be same.
66    if (namelen != nsecLen)
67    {
68        LogMsg("NSEC3SameName: ERROR!! namelen %d, nsecLen %d", namelen, nsecLen);
69        return ((namelen < nsecLen) ? -1 : 1);
70    }
71
72    for (i = 0; i < namelen; i++)
73    {
74        mDNSu8 ac = *name++;
75        mDNSu8 bc = *nsecName++;
76        if (mDNSIsUpperCase(ac)) ac += 'a' - 'A';
77        if (mDNSIsUpperCase(bc)) bc += 'a' - 'A';
78        if (ac != bc)
79        {
80            verbosedebugf("NSEC3SameName: returning ac %c, bc %c", ac, bc);
81            return ((ac < bc) ? -1 : 1);
82        }
83    }
84    return 0;
85}
86
87// Does the NSEC3 in "ncr" covers the "name" ?
88// hashName is hash of the "name" and b32Name is the base32 encoded equivalent.
89mDNSlocal mDNSBool NSEC3CoversName(mDNS *const m, CacheRecord *ncr, const mDNSu8 *hashName, int hashLen, const mDNSu8 *b32Name,
90	int b32len)
91{
92    mDNSu8 *nxtName;
93    int nxtLength;
94    int ret, ret1, ret2;
95    const mDNSu8 b32nxtname[NSEC3_MAX_B32_LEN+1];
96    int b32nxtlen;
97
98    NSEC3Parse(&ncr->resrec, mDNSNULL, &nxtLength, &nxtName, mDNSNULL, mDNSNULL);
99
100    if (nxtLength != hashLen || ncr->resrec.name->c[0] != b32len)
101        return mDNSfalse;
102
103    // Compare the owner names and the "nxt" names.
104    //
105    // Owner name is base32 encoded and hence use the base32 encoded name b32name.
106    // nxt name is binary and hence use the binary value in hashName.
107    ret1 = NSEC3SameName(&ncr->resrec.name->c[1], ncr->resrec.name->c[0], b32Name, b32len);
108    ret2 = DNSMemCmp(nxtName, hashName, hashLen);
109
110#if NSEC3_DEBUG
111    {
112        char nxtbuf1[50];
113        char nxtbuf2[50];
114
115        PrintHash(nxtName, nxtLength, nxtbuf1, sizeof(nxtbuf1));
116        PrintHash((mDNSu8 *)hashName, hashLen, nxtbuf2, sizeof(nxtbuf2));
117        LogMsg("NSEC3CoversName: Owner name %s, name %s", &ncr->resrec.name->c[1], b32Name);
118        LogMsg("NSEC3CoversName: Nxt hash name %s, name %s", nxtbuf1, nxtbuf2);
119    }
120#endif
121
122    // "name" is greater than the owner name and smaller than nxtName. This also implies
123	// that nxtName > owner name implying that it is normal NSEC3.
124    if (ret1 < 0 && ret2 > 0)
125    {
126        LogDNSSEC("NSEC3CoversName: NSEC3 %s covers %s (Normal)", CRDisplayString(m, ncr), b32Name);
127        return mDNStrue;
128    }
129    // Need to compare the owner name and "nxt" to see if this is the last
130    // NSEC3 in the zone. Only the owner name is in base32 and hence we need to
131    // convert the nxtName to base32.
132    b32nxtlen = baseEncode((char *)b32nxtname, sizeof(b32nxtname), nxtName, nxtLength, ENC_BASE32);
133    if (!b32nxtlen)
134    {
135        LogDNSSEC("NSEC3CoversName: baseEncode of nxtName of %s failed", CRDisplayString(m, ncr));
136        return mDNSfalse;
137    }
138    if (b32len != b32nxtlen)
139    {
140        LogDNSSEC("NSEC3CoversName: baseEncode of nxtName for %s resulted in wrong length b32nxtlen %d, b32len %d",
141            CRDisplayString(m, ncr), b32len, b32nxtlen);
142        return mDNSfalse;
143    }
144    LogDNSSEC("NSEC3CoversName: Owner name %s, b32nxtname %s, ret1 %d, ret2 %d", &ncr->resrec.name->c[1], b32nxtname, ret1, ret2);
145
146    // If it is the last NSEC3 in the zone nxt < "name" and NSEC3SameName returns -1.
147    //
148    // - ret1 < 0 means "name > owner"
149    // - ret2 > 0 means "name < nxt"
150    //
151    // Note: We also handle the case of only NSEC3 in the zone where NSEC3SameName returns zero.
152    ret = NSEC3SameName(b32nxtname, b32nxtlen, &ncr->resrec.name->c[1], ncr->resrec.name->c[0]);
153    if (ret <= 0 &&
154        (ret1 < 0 || ret2 > 0))
155    {
156        LogDNSSEC("NSEC3CoversName: NSEC3 %s covers %s (Last), ret1 %d, ret2 %d", CRDisplayString(m, ncr), b32Name, ret1, ret2);
157        return mDNStrue;
158    }
159
160    return mDNSfalse;
161}
162
163// This function can be called with NSEC3ClosestEncloser, NSEC3Covers and NSEC3CEProof
164//
165// Passing in NSEC3ClosestEncloser means "find an exact match for the origName".
166// Passing in NSEC3Covers means "find an NSEC3 that covers the origName".
167//
168// i.e., in both cases the nsec3 records are iterated to find the best match and returned.
169// With NSEC3ClosestEncloser, as we are just looking for a name match, extra checks for
170// the types being present or absent will not be checked.
171//
172// If NSEC3CEProof is passed, the name is tried as such first by iterating through all NSEC3s
173// finding a ClosestEncloser or CloserEncloser and then one label skipped from the left and
174// retried again till both the closest and closer encloser is found.
175//
176// ncr is the negative cache record that has the NSEC3 chain
177// origName is the name for which we are trying to find the ClosestEncloser etc.
178// closestEncloser and closerEncloser are the return values of the function
179// ce is the closest encloser that will be returned if we find one
180mDNSlocal mDNSBool NSEC3Find(mDNS *const m, NSEC3FindValues val, CacheRecord *ncr, domainname *origName, CacheRecord **closestEncloser,
181	CacheRecord **closerEncloser, const domainname **ce, mDNSu16 qtype)
182{
183    int i;
184    int labelCount = CountLabels(origName);
185    CacheRecord *cr;
186    rdataNSEC3 *nsec3;
187
188    (void) qtype; // unused
189    // Pick the first NSEC for the iterations, salt etc.
190    for (cr = ncr->nsec; cr; cr = cr->next)
191    {
192        if (cr->resrec.rrtype == kDNSType_NSEC3)
193        {
194            const RDataBody2 *const rdb = (RDataBody2 *)cr->resrec.rdata->u.data;
195            nsec3 = (rdataNSEC3 *)rdb->data;
196            break;
197        }
198    }
199    if (!cr)
200    {
201        LogMsg("NSEC3Find: cr NULL");
202        return mDNSfalse;
203    }
204
205    // Note: The steps defined in this function are for "NSEC3CEProof". As part of NSEC3CEProof,
206    // we need to find both the closestEncloser and closerEncloser which can also be found
207    // by passing NSEC3ClosestEncloser and NSEC3Covers respectively.
208    //
209    // Section 8.3 of RFC 5155.
210    // 1.  Set SNAME=QNAME.  Clear the flag.
211    //
212    // closerEncloser is the "flag". "name" below is SNAME.
213
214    if (closestEncloser)
215    {
216        *ce = mDNSNULL;
217        *closestEncloser = mDNSNULL;
218    }
219    if (closerEncloser)
220        *closerEncloser = mDNSNULL;
221
222    // If we are looking for a closestEncloser or a covering NSEC3, we don't have
223    // to truncate the name. For the give name, try to find the closest or closer
224    // encloser.
225    if (val != NSEC3CEProof)
226    {
227        labelCount = 0;
228    }
229
230    for (i = 0; i < labelCount + 1; i++)
231    {
232        int hlen;
233        const mDNSu8 hashName[NSEC3_MAX_HASH_LEN];
234        const domainname *name;
235        const mDNSu8 b32Name[NSEC3_MAX_B32_LEN+1];
236        int b32len;
237
238        name = SkipLeadingLabels(origName, i);
239        if (!NSEC3HashName(name, nsec3, mDNSNULL, 0, hashName, &hlen))
240        {
241            LogMsg("NSEC3Find: NSEC3HashName failed for ##s", name->c);
242            continue;
243        }
244
245        b32len = baseEncode((char *)b32Name, sizeof(b32Name), (mDNSu8 *)hashName, hlen, ENC_BASE32);
246        if (!b32len)
247        {
248            LogMsg("NSEC3Find: baseEncode of name %##s failed", name->c);
249            continue;
250        }
251
252
253        for (cr = ncr->nsec; cr; cr = cr->next)
254        {
255            const domainname *nsecZone;
256            int result, subdomain;
257
258            if (cr->resrec.rrtype != kDNSType_NSEC3)
259                continue;
260
261            nsecZone = SkipLeadingLabels(cr->resrec.name, 1);
262            if (!nsecZone)
263            {
264                LogMsg("NSEC3Find: SkipLeadingLabel failed for %s, current name %##s",
265                    CRDisplayString(m, cr), name->c);
266                continue;
267            }
268
269            // NSEC3 owner names are formed by hashing the owner name and then appending
270            // the zone name to it. If we skip the first label, the rest should be
271            // the zone name. See whether it is the subdomain of the name we are looking
272            // for.
273            result = DNSSECCanonicalOrder(origName, nsecZone, &subdomain);
274
275            // The check can't be a strict subdomain check. When NSEC3ClosestEncloser is
276            // passed in, there can be an exact match. If it is a subdomain or an exact
277            // match, we should continue with the proof.
278            if (!(subdomain || !result))
279            {
280                LogMsg("NSEC3Find: NSEC3 %s not a subdomain of %##s, result %d", CRDisplayString(m, cr),
281                    origName->c, result);
282                continue;
283            }
284
285            // 2.1) If there is no NSEC3 RR in the response that matches SNAME
286            // (i.e., an NSEC3 RR whose owner name is the same as the hash of
287            // SNAME, prepended as a single label to the zone name), clear
288            // the flag.
289            //
290            // Note: We don't try to determine the actual zone name. We know that
291            // the labels following the hash (nsecZone) is the ancestor and we don't
292            // know where the zone cut is. Hence, we verify just the hash to be
293            // the same.
294
295            if (val == NSEC3ClosestEncloser || val == NSEC3CEProof)
296            {
297                if (!NSEC3SameName(&cr->resrec.name->c[1], cr->resrec.name->c[0], (const mDNSu8 *)b32Name, b32len))
298                {
299                    int bmaplen;
300                    mDNSu8 *bmap;
301
302                    // For NSEC3ClosestEncloser, we are finding an exact match and
303                    // "type" specific checks should be done by the caller.
304                    if (val != NSEC3ClosestEncloser)
305                    {
306                        // DNAME bit must not be set and NS bit may be set only if SOA bit is set
307                        NSEC3Parse(&cr->resrec, mDNSNULL, mDNSNULL, mDNSNULL, &bmaplen, &bmap);
308                        if (BitmapTypeCheck(bmap, bmaplen, kDNSType_DNAME))
309                        {
310                            LogDNSSEC("NSEC3Find: DNAME bit set in %s, ignoring", CRDisplayString(m, cr));
311                            return mDNSfalse;
312                        }
313                        // This is the closest encloser and should come from the right zone.
314                        if (BitmapTypeCheck(bmap, bmaplen, kDNSType_NS) &&
315                            !BitmapTypeCheck(bmap, bmaplen, kDNSType_SOA))
316                        {
317                            LogDNSSEC("NSEC3Find: NS bit set without SOA bit in %s, ignoring", CRDisplayString(m, cr));
318                            return mDNSfalse;
319                        }
320                    }
321
322                    LogDNSSEC("NSEC3Find: ClosestEncloser %s found for name %##s", CRDisplayString(m, cr), name->c);
323                    if (closestEncloser)
324                    {
325                        *ce = name;
326                        *closestEncloser = cr;
327                    }
328                    if (val == NSEC3ClosestEncloser)
329                        return mDNStrue;
330                    else
331                        break;
332                }
333            }
334
335            if ((val == NSEC3Covers || val == NSEC3CEProof) && !(*closerEncloser))
336            {
337                if (NSEC3CoversName(m, cr, hashName, hlen, b32Name, b32len))
338                {
339                    // 2.2) If there is an NSEC3 RR in the response that covers SNAME, set the flag.
340                    if (closerEncloser)
341                        *closerEncloser = cr;
342                    if (val == NSEC3Covers)
343                        return mDNStrue;
344                    else
345                        break;
346                }
347            }
348        }
349        // 2.3) If there is a matching NSEC3 RR in the response and the flag
350        // was set, then the proof is complete, and SNAME is the closest
351        // encloser.
352        if (val == NSEC3CEProof)
353        {
354            if (*closestEncloser && *closerEncloser)
355            {
356                LogDNSSEC("NSEC3Find: Found closest and closer encloser");
357                return mDNStrue;
358            }
359
360            // 2.4) If there is a matching NSEC3 RR in the response, but the flag
361            // is not set, then the response is bogus.
362            //
363            // Note: We don't have to wait till we finish trying all the names. If the matchName
364            // happens, we found the closest encloser which means we should have found the closer
365            // encloser before.
366
367            if (*closestEncloser && !(*closerEncloser))
368            {
369                LogDNSSEC("NSEC3Find: Found closest, but not closer encloser");
370                return mDNSfalse;
371            }
372        }
373        // 3.  Truncate SNAME by one label from the left, go to step 2.
374    }
375    LogDNSSEC("NSEC3Find: Cannot find name %##s (%s)", origName->c, DNSTypeName(qtype));
376    return mDNSfalse;
377}
378
379mDNSlocal mDNSBool NSEC3ClosestEncloserProof(mDNS *const m, CacheRecord *ncr, domainname *name, CacheRecord **closestEncloser, CacheRecord **closerEncloser,
380	const domainname **ce, mDNSu16 qtype)
381{
382    if (!NSEC3Find(m, NSEC3CEProof, ncr, name, closestEncloser, closerEncloser, ce, qtype))
383    {
384        LogDNSSEC("NSEC3ClosestEncloserProof: ERROR!! Cannot do closest encloser proof");
385        return mDNSfalse;
386    }
387
388    // Note: It is possible that closestEncloser and closerEncloser are the same.
389    if (!closestEncloser || !closerEncloser || !ce)
390    {
391        LogMsg("NSEC3ClosestEncloserProof: ClosestEncloser %p or CloserEncloser %p ce %p, something is NULL", *closestEncloser,
392            *closerEncloser, *ce);
393        return mDNSfalse;
394    }
395
396    // If the name exists, we should not have gotten the name error
397    if (SameDomainName((*ce), name))
398    {
399        LogMsg("NSEC3ClosestEncloserProof: ClosestEncloser %s same as origName %##s", CRDisplayString(m, *closestEncloser),
400            (*ce)->c);
401        return mDNSfalse;
402    }
403    return mDNStrue;
404}
405
406mDNSlocal mDNSBool VerifyNSEC3(mDNS *const m, DNSSECVerifier *dv, CacheRecord *ncr, CacheRecord *closestEncloser,
407    CacheRecord *closerEncloser, CacheRecord *wildcard, DNSSECVerifierCallback callback)
408{
409    mStatus status;
410    RRVerifier *r;
411
412    // We have three NSEC3s. If any of two are same, we should just prove one of them.
413    // This is just not an optimization; DNSSECNegativeValidationCB does not handle
414    // identical NSEC3s very well.
415
416    if (closestEncloser == closerEncloser)
417        closestEncloser = mDNSNULL;
418    if (closerEncloser == wildcard)
419        closerEncloser = mDNSNULL;
420    if (closestEncloser == wildcard)
421        closestEncloser = mDNSNULL;
422
423    dv->pendingNSEC = mDNSNULL;
424    if (closestEncloser)
425    {
426        r = AllocateRRVerifier(&closestEncloser->resrec, &status);
427        if (!r)
428            return mDNSfalse;
429        r->next = dv->pendingNSEC;
430        dv->pendingNSEC = r;
431    }
432    if (closerEncloser)
433    {
434        r = AllocateRRVerifier(&closerEncloser->resrec, &status);
435        if (!r)
436            return mDNSfalse;
437        r->next = dv->pendingNSEC;
438        dv->pendingNSEC = r;
439    }
440    if (wildcard)
441    {
442        r = AllocateRRVerifier(&wildcard->resrec, &status);
443        if (!r)
444            return mDNSfalse;
445        r->next = dv->pendingNSEC;
446        dv->pendingNSEC = r;
447    }
448    if (!dv->pendingNSEC)
449    {
450        LogMsg("VerifyNSEC3: ERROR!! pending NSEC null");
451        return mDNSfalse;
452    }
453    r = dv->pendingNSEC;
454    dv->pendingNSEC = r->next;
455    r->next = mDNSNULL;
456
457    LogDNSSEC("VerifyNSEC3: Verifying %##s (%s)", r->name.c, DNSTypeName(r->rrtype));
458    if (!dv->pendingNSEC)
459        VerifyNSEC(m, mDNSNULL, r, dv, ncr, mDNSNULL);
460    else
461        VerifyNSEC(m, mDNSNULL, r, dv, ncr, callback);
462    return mDNStrue;
463}
464
465mDNSexport void NSEC3NameErrorProof(mDNS *const m, DNSSECVerifier *dv, CacheRecord *ncr)
466{
467    CacheRecord *closerEncloser;
468    CacheRecord *closestEncloser;
469    CacheRecord *wildcard;
470    const domainname *ce = mDNSNULL;
471    domainname wild;
472
473    if (!NSEC3ClosestEncloserProof(m, ncr, &dv->q.qname, &closestEncloser, &closerEncloser, &ce, dv->q.qtype))
474    {
475        goto error;
476    }
477    LogDNSSEC("NSEC3NameErrorProof: ClosestEncloser %s, ce %##s", CRDisplayString(m, closestEncloser), ce->c);
478    LogDNSSEC("NSEC3NameErrorProof: CloserEncloser %s", CRDisplayString(m, closerEncloser));
479
480    // *.closestEncloser should be covered by some nsec3 which would then prove
481    // that the wildcard does not exist
482    wild.c[0] = 1;
483    wild.c[1] = '*';
484    wild.c[2] = 0;
485    if (!AppendDomainName(&wild, ce))
486    {
487        LogMsg("NSEC3NameErrorProof: Can't append domainname to closest encloser name %##s", ce->c);
488        goto error;
489    }
490    if (!NSEC3Find(m, NSEC3Covers, ncr, &wild, mDNSNULL, &wildcard, mDNSNULL, dv->q.qtype))
491    {
492        LogMsg("NSEC3NameErrorProof: Cannot find encloser for wildcard");
493        goto error;
494    }
495    else
496    {
497        LogDNSSEC("NSEC3NameErrorProof: Wildcard %##s covered by %s", wild.c, CRDisplayString(m, wildcard));
498        if (wildcard == closestEncloser)
499        {
500            LogDNSSEC("NSEC3NameErrorProof: ClosestEncloser matching Wildcard %s", CRDisplayString(m, wildcard));
501        }
502    }
503    if (NSEC3OptOut(closerEncloser))
504    {
505        dv->flags |= NSEC3_OPT_OUT;
506    }
507    if (!VerifyNSEC3(m, dv, ncr, closestEncloser, closerEncloser, wildcard, NameErrorNSECCallback))
508        goto error;
509    else
510        return;
511
512error:
513    dv->DVCallback(m, dv, DNSSEC_Bogus);
514}
515
516// Section 8.5, 8.6 of RFC 5155 first paragraph
517mDNSlocal mDNSBool NSEC3NoDataError(mDNS *const m, CacheRecord *ncr, domainname *name, mDNSu16 qtype, CacheRecord **closestEncloser)
518{
519    const domainname *ce = mDNSNULL;
520
521    *closestEncloser = mDNSNULL;
522    // Note: This also covers ENT in which case the bitmap is empty
523    if (NSEC3Find(m, NSEC3ClosestEncloser, ncr, name, closestEncloser, mDNSNULL, &ce, qtype))
524    {
525        int bmaplen;
526        mDNSu8 *bmap;
527        mDNSBool ns, soa;
528
529        NSEC3Parse(&(*closestEncloser)->resrec, mDNSNULL, mDNSNULL, mDNSNULL, &bmaplen, &bmap);
530        if (BitmapTypeCheck(bmap, bmaplen, qtype) || BitmapTypeCheck(bmap, bmaplen, kDNSType_CNAME))
531        {
532            LogMsg("NSEC3NoDataError: qtype %s exists in %s", DNSTypeName(qtype), CRDisplayString(m, *closestEncloser));
533            return mDNSfalse;
534        }
535        ns = BitmapTypeCheck(bmap, bmaplen, kDNSType_NS);
536        soa = BitmapTypeCheck(bmap, bmaplen, kDNSType_SOA);
537        if (qtype != kDNSType_DS)
538        {
539            // For non-DS type questions, we don't want to use the parent side records to
540            // answer it
541            if (ns && !soa)
542            {
543                LogDNSSEC("NSEC3NoDataError: Parent side NSEC %s, can't use for child qname %##s (%s)",
544                    CRDisplayString(m, *closestEncloser), name->c, DNSTypeName(qtype));
545                return mDNSfalse;
546            }
547        }
548        else
549        {
550            if (soa)
551            {
552                LogDNSSEC("NSEC3NoDataError: Child side NSEC %s, can't use for parent qname %##s (%s)",
553                    CRDisplayString(m, *closestEncloser), name->c, DNSTypeName(qtype));
554                return mDNSfalse;
555            }
556        }
557        LogDNSSEC("NSEC3NoDataError: Name -%##s- exists, but qtype %s does not exist in %s", name->c, DNSTypeName(qtype), CRDisplayString(m, *closestEncloser));
558        return mDNStrue;
559    }
560    return mDNSfalse;
561}
562
563mDNSexport void NSEC3NoDataProof(mDNS *const m, DNSSECVerifier *dv, CacheRecord *ncr)
564{
565    CacheRecord *closerEncloser = mDNSNULL;
566    CacheRecord *closestEncloser = mDNSNULL;
567    CacheRecord *wildcard = mDNSNULL;
568    const domainname *ce = mDNSNULL;
569    domainname wild;
570
571    // Section 8.5, 8.6 of RFC 5155
572    if (NSEC3NoDataError(m, ncr, &dv->q.qname, dv->q.qtype, &closestEncloser))
573    {
574        goto verify;
575    }
576    // Section 8.6, 8.7: if we can't find the NSEC3 RR, verify the closest encloser proof
577    // for QNAME and the "next closer" should have the opt out
578    if (!NSEC3ClosestEncloserProof(m, ncr, &dv->q.qname, &closestEncloser, &closerEncloser, &ce, dv->q.qtype))
579    {
580        goto error;
581    }
582
583    // Section 8.7: find a matching NSEC3 for *.closestEncloser
584    wild.c[0] = 1;
585    wild.c[1] = '*';
586    wild.c[2] = 0;
587    if (!AppendDomainName(&wild, ce))
588    {
589        LogMsg("NSEC3NameErrorProof: Can't append domainname to closest encloser name %##s", ce->c);
590        goto error;
591    }
592    if (!NSEC3Find(m, NSEC3ClosestEncloser, ncr, &wild, &wildcard, mDNSNULL, &ce, dv->q.qtype))
593    {
594        // Not a wild card case. Section 8.6 second para applies.
595        LogDNSSEC("NSEC3NoDataProof: Cannot find encloser for wildcard, perhaps not a wildcard case");
596        if (!NSEC3OptOut(closerEncloser))
597        {
598            LogDNSSEC("NSEC3DataProof: opt out not set for %##s (%s), bogus", dv->q.qname.c, DNSTypeName(dv->q.qtype));
599            goto error;
600        }
601        LogDNSSEC("NSEC3DataProof: opt out set, proof complete for %##s (%s)", dv->q.qname.c, DNSTypeName(dv->q.qtype));
602        dv->flags |= NSEC3_OPT_OUT;
603    }
604    else
605    {
606        int bmaplen;
607        mDNSu8 *bmap;
608        NSEC3Parse(&wildcard->resrec, mDNSNULL, mDNSNULL, mDNSNULL, &bmaplen, &bmap);
609        if (BitmapTypeCheck(bmap, bmaplen, dv->q.qtype) || BitmapTypeCheck(bmap, bmaplen, kDNSType_CNAME))
610        {
611            LogDNSSEC("NSEC3NoDataProof: qtype %s exists in %s", DNSTypeName(dv->q.qtype), CRDisplayString(m, wildcard));
612            goto error;
613        }
614        if (dv->q.qtype == kDNSType_DS && BitmapTypeCheck(bmap, bmaplen, kDNSType_SOA))
615        {
616            LogDNSSEC("NSEC3NoDataProof: Child side wildcard NSEC3 %s, can't use for parent qname %##s (%s)",
617                CRDisplayString(m, wildcard), dv->q.qname.c, DNSTypeName(dv->q.qtype));
618            goto error;
619        }
620        else if (dv->q.qtype != kDNSType_DS && !BitmapTypeCheck(bmap, bmaplen, kDNSType_SOA) &&
621            BitmapTypeCheck(bmap, bmaplen, kDNSType_NS))
622        {
623            // Don't use the parent side record for this
624            LogDNSSEC("NSEC3NoDataProof: Parent side wildcard NSEC3 %s, can't use for child qname %##s (%s)",
625                CRDisplayString(m, wildcard), dv->q.qname.c, DNSTypeName(dv->q.qtype));
626            goto error;
627        }
628        LogDNSSEC("NSEC3NoDataProof: Wildcard %##s matched by %s", wild.c, CRDisplayString(m, wildcard));
629    }
630verify:
631
632    if (!VerifyNSEC3(m, dv, ncr, closestEncloser, closerEncloser, wildcard, NoDataNSECCallback))
633        goto error;
634    else
635        return;
636error:
637    dv->DVCallback(m, dv, DNSSEC_Bogus);
638}
639
640mDNSexport mDNSBool NSEC3WildcardAnswerProof(mDNS *const m, CacheRecord *ncr, DNSSECVerifier *dv)
641{
642    int skip;
643    const domainname *nc;
644    CacheRecord *closerEncloser;
645
646    (void) m;
647
648    // Find the next closer name and prove that it is covered by the NSEC3
649    skip = CountLabels(&dv->origName) - CountLabels(dv->wildcardName) - 1;
650    if (skip)
651        nc = SkipLeadingLabels(&dv->origName, skip);
652    else
653        nc = &dv->origName;
654
655    LogDNSSEC("NSEC3WildcardAnswerProof: wildcard name %##s", nc->c);
656
657    if (!NSEC3Find(m, NSEC3Covers, ncr, (domainname *)nc, mDNSNULL, &closerEncloser, mDNSNULL, dv->q.qtype))
658    {
659        LogMsg("NSEC3WildcardAnswerProof: Cannot find closer encloser");
660        return mDNSfalse;
661    }
662    if (!closerEncloser)
663    {
664        LogMsg("NSEC3WildcardAnswerProof: closerEncloser NULL");
665        return mDNSfalse;
666    }
667    if (NSEC3OptOut(closerEncloser))
668    {
669        dv->flags |= NSEC3_OPT_OUT;
670    }
671    // NSEC3 Verification is done by the caller
672    return mDNStrue;
673}
674
675mDNSexport CacheRecord *NSEC3RecordIsDelegation(mDNS *const m, domainname *name, mDNSu16 qtype)
676{
677    CacheGroup *cg;
678    CacheRecord *cr;
679    CacheRecord *ncr;
680    mDNSu32 slot, namehash;
681
682    slot = HashSlot(name);
683    namehash = DomainNameHashValue(name);
684
685    cg = CacheGroupForName(m, (const mDNSu32)slot, namehash, name);
686    if (!cg)
687    {
688        LogDNSSEC("NSEC3RecordForName: cg NULL for %##s", name);
689        return mDNSNULL;
690    }
691    for (ncr = cg->members; ncr; ncr = ncr->next)
692    {
693        if (ncr->resrec.RecordType != kDNSRecordTypePacketNegative ||
694            ncr->resrec.rrtype != qtype)
695        {
696            continue;
697        }
698        for (cr = ncr->nsec; cr; cr = cr->next)
699        {
700            int hlen, b32len;
701            const mDNSu8 hashName[NSEC3_MAX_HASH_LEN];
702            const mDNSu8 b32Name[NSEC3_MAX_B32_LEN+1];
703            const RDataBody2 *const rdb = (RDataBody2 *)cr->resrec.rdata->u.data;
704            rdataNSEC3 *nsec3;
705
706            if (cr->resrec.rrtype != kDNSType_NSEC3)
707                continue;
708
709            nsec3 = (rdataNSEC3 *)rdb->data;
710
711            if (!NSEC3HashName(name, nsec3, mDNSNULL, 0, hashName, &hlen))
712            {
713                LogMsg("NSEC3RecordIsDelegation: NSEC3HashName failed for ##s", name->c);
714                return mDNSNULL;
715            }
716
717            b32len = baseEncode((char *)b32Name, sizeof(b32Name), (mDNSu8 *)hashName, hlen, ENC_BASE32);
718            if (!b32len)
719            {
720                LogMsg("NSEC3RecordIsDelegation: baseEncode of name %##s failed", name->c);
721                return mDNSNULL;
722            }
723            // Section 2.3 of RFC 4035 states that:
724            //
725            // Each owner name in the zone that has authoritative data or a delegation point NS RRset MUST
726            // have an NSEC resource record.
727            //
728            // This applies to NSEC3 record. So, if we have an NSEC3 record matching the question name with the
729            // NS bit set, then this is a delegation.
730            //
731            if (!NSEC3SameName(&cr->resrec.name->c[1], cr->resrec.name->c[0], (const mDNSu8 *)b32Name, b32len))
732            {
733                int bmaplen;
734                mDNSu8 *bmap;
735
736                LogDNSSEC("NSEC3RecordIsDelegation: CacheRecord %s matches name %##s, b32name %s", CRDisplayString(m, cr), name->c, b32Name);
737                NSEC3Parse(&cr->resrec, mDNSNULL, mDNSNULL, mDNSNULL, &bmaplen, &bmap);
738
739                // See the Insecure Delegation Proof section in dnssec-bis: DS bit and SOA bit
740                // should be absent
741                if (BitmapTypeCheck(bmap, bmaplen, kDNSType_SOA) ||
742                    BitmapTypeCheck(bmap, bmaplen, kDNSType_DS))
743                {
744                    LogDNSSEC("NSEC3RecordIsDelegation: CacheRecord %s has DS or SOA bit set, ignoring", CRDisplayString(m, cr));
745                    return mDNSNULL;
746                }
747                if (BitmapTypeCheck(bmap, bmaplen, kDNSType_NS))
748                    return cr;
749                else
750                    return mDNSNULL;
751            }
752            // If opt-out is not set, then it does not cover any delegations
753            if (!(nsec3->flags & NSEC3_FLAGS_OPTOUT))
754                continue;
755            // Opt-out allows insecure delegations to exist without the NSEC3 RR at the
756            // hashed owner name (see RFC 5155 section 6.0).
757            if (NSEC3CoversName(m, cr, hashName, hlen, b32Name, b32len))
758            {
759                LogDNSSEC("NSEC3RecordIsDelegation: CacheRecord %s covers name %##s with optout", CRDisplayString(m, cr), name->c);
760                return cr;
761            }
762        }
763    }
764    return mDNSNULL;
765}
766
767#else // !DNSSEC_DISABLED
768
769#endif // !DNSSEC_DISABLED
770