1/* -*- Mode: C; tab-width: 4 -*-
2 *
3 * Copyright (c) 2011-2013 Apple 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 || !(*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 && closestEncloser && *closestEncloser)
353        {
354            if (closerEncloser && *closerEncloser)
355            {
356                LogDNSSEC("NSEC3Find: Found closest and closer encloser");
357                return mDNStrue;
358            }
359            else
360            {
361                // 2.4) If there is a matching NSEC3 RR in the response, but the flag
362                // is not set, then the response is bogus.
363                //
364                // Note: We don't have to wait till we finish trying all the names. If the matchName
365                // happens, we found the closest encloser which means we should have found the closer
366                // encloser before.
367
368                LogDNSSEC("NSEC3Find: Found closest, but not closer encloser");
369                return mDNSfalse;
370            }
371        }
372        // 3.  Truncate SNAME by one label from the left, go to step 2.
373    }
374    LogDNSSEC("NSEC3Find: Cannot find name %##s (%s)", origName->c, DNSTypeName(qtype));
375    return mDNSfalse;
376}
377
378mDNSlocal mDNSBool NSEC3ClosestEncloserProof(mDNS *const m, CacheRecord *ncr, domainname *name, CacheRecord **closestEncloser, CacheRecord **closerEncloser,
379	const domainname **ce, mDNSu16 qtype)
380{
381    if (!NSEC3Find(m, NSEC3CEProof, ncr, name, closestEncloser, closerEncloser, ce, qtype))
382    {
383        LogDNSSEC("NSEC3ClosestEncloserProof: ERROR!! Cannot do closest encloser proof");
384        return mDNSfalse;
385    }
386
387    // Note: It is possible that closestEncloser and closerEncloser are the same.
388    if (!closestEncloser || !closerEncloser || !ce)
389    {
390        LogMsg("NSEC3ClosestEncloserProof: ClosestEncloser %p or CloserEncloser %p ce %p, something is NULL", closestEncloser, closerEncloser, ce);
391        return mDNSfalse;
392    }
393
394    // If the name exists, we should not have gotten the name error
395    if (SameDomainName((*ce), name))
396    {
397        LogMsg("NSEC3ClosestEncloserProof: ClosestEncloser %s same as origName %##s", CRDisplayString(m, *closestEncloser),
398            (*ce)->c);
399        return mDNSfalse;
400    }
401    return mDNStrue;
402}
403
404mDNSlocal mDNSBool VerifyNSEC3(mDNS *const m, DNSSECVerifier *dv, CacheRecord *ncr, CacheRecord *closestEncloser,
405    CacheRecord *closerEncloser, CacheRecord *wildcard, DNSSECVerifierCallback callback)
406{
407    mStatus status;
408    RRVerifier *r;
409
410    // We have three NSEC3s. If any of two are same, we should just prove one of them.
411    // This is just not an optimization; DNSSECNegativeValidationCB does not handle
412    // identical NSEC3s very well.
413
414    if (closestEncloser == closerEncloser)
415        closestEncloser = mDNSNULL;
416    if (closerEncloser == wildcard)
417        closerEncloser = mDNSNULL;
418    if (closestEncloser == wildcard)
419        closestEncloser = mDNSNULL;
420
421    dv->pendingNSEC = mDNSNULL;
422    if (closestEncloser)
423    {
424        r = AllocateRRVerifier(&closestEncloser->resrec, &status);
425        if (!r)
426            return mDNSfalse;
427        r->next = dv->pendingNSEC;
428        dv->pendingNSEC = r;
429    }
430    if (closerEncloser)
431    {
432        r = AllocateRRVerifier(&closerEncloser->resrec, &status);
433        if (!r)
434            return mDNSfalse;
435        r->next = dv->pendingNSEC;
436        dv->pendingNSEC = r;
437    }
438    if (wildcard)
439    {
440        r = AllocateRRVerifier(&wildcard->resrec, &status);
441        if (!r)
442            return mDNSfalse;
443        r->next = dv->pendingNSEC;
444        dv->pendingNSEC = r;
445    }
446    if (!dv->pendingNSEC)
447    {
448        LogMsg("VerifyNSEC3: ERROR!! pending NSEC null");
449        return mDNSfalse;
450    }
451    r = dv->pendingNSEC;
452    dv->pendingNSEC = r->next;
453    r->next = mDNSNULL;
454
455    LogDNSSEC("VerifyNSEC3: Verifying %##s (%s)", r->name.c, DNSTypeName(r->rrtype));
456    if (!dv->pendingNSEC)
457        VerifyNSEC(m, mDNSNULL, r, dv, ncr, mDNSNULL);
458    else
459        VerifyNSEC(m, mDNSNULL, r, dv, ncr, callback);
460    return mDNStrue;
461}
462
463mDNSexport void NSEC3NameErrorProof(mDNS *const m, DNSSECVerifier *dv, CacheRecord *ncr)
464{
465    CacheRecord *closerEncloser;
466    CacheRecord *closestEncloser;
467    CacheRecord *wildcard;
468    const domainname *ce = mDNSNULL;
469    domainname wild;
470
471    if (!NSEC3ClosestEncloserProof(m, ncr, &dv->q.qname, &closestEncloser, &closerEncloser, &ce, dv->q.qtype))
472    {
473        goto error;
474    }
475    LogDNSSEC("NSEC3NameErrorProof: ClosestEncloser %s, ce %##s", CRDisplayString(m, closestEncloser), ce->c);
476    LogDNSSEC("NSEC3NameErrorProof: CloserEncloser %s", CRDisplayString(m, closerEncloser));
477
478    // *.closestEncloser should be covered by some nsec3 which would then prove
479    // that the wildcard does not exist
480    wild.c[0] = 1;
481    wild.c[1] = '*';
482    wild.c[2] = 0;
483    if (!AppendDomainName(&wild, ce))
484    {
485        LogMsg("NSEC3NameErrorProof: Can't append domainname to closest encloser name %##s", ce->c);
486        goto error;
487    }
488    if (!NSEC3Find(m, NSEC3Covers, ncr, &wild, mDNSNULL, &wildcard, mDNSNULL, dv->q.qtype))
489    {
490        LogMsg("NSEC3NameErrorProof: Cannot find encloser for wildcard");
491        goto error;
492    }
493    else
494    {
495        LogDNSSEC("NSEC3NameErrorProof: Wildcard %##s covered by %s", wild.c, CRDisplayString(m, wildcard));
496        if (wildcard == closestEncloser)
497        {
498            LogDNSSEC("NSEC3NameErrorProof: ClosestEncloser matching Wildcard %s", CRDisplayString(m, wildcard));
499        }
500    }
501    if (NSEC3OptOut(closerEncloser))
502    {
503        dv->flags |= NSEC3_OPT_OUT;
504    }
505    if (!VerifyNSEC3(m, dv, ncr, closestEncloser, closerEncloser, wildcard, NameErrorNSECCallback))
506        goto error;
507    else
508        return;
509
510error:
511    dv->DVCallback(m, dv, DNSSEC_Bogus);
512}
513
514// Section 8.5, 8.6 of RFC 5155 first paragraph
515mDNSlocal mDNSBool NSEC3NoDataError(mDNS *const m, CacheRecord *ncr, domainname *name, mDNSu16 qtype, CacheRecord **closestEncloser)
516{
517    const domainname *ce = mDNSNULL;
518
519    *closestEncloser = mDNSNULL;
520    // Note: This also covers ENT in which case the bitmap is empty
521    if (NSEC3Find(m, NSEC3ClosestEncloser, ncr, name, closestEncloser, mDNSNULL, &ce, qtype))
522    {
523        int bmaplen;
524        mDNSu8 *bmap;
525        mDNSBool ns, soa;
526
527        NSEC3Parse(&(*closestEncloser)->resrec, mDNSNULL, mDNSNULL, mDNSNULL, &bmaplen, &bmap);
528        if (BitmapTypeCheck(bmap, bmaplen, qtype) || BitmapTypeCheck(bmap, bmaplen, kDNSType_CNAME))
529        {
530            LogMsg("NSEC3NoDataError: qtype %s exists in %s", DNSTypeName(qtype), CRDisplayString(m, *closestEncloser));
531            return mDNSfalse;
532        }
533        ns = BitmapTypeCheck(bmap, bmaplen, kDNSType_NS);
534        soa = BitmapTypeCheck(bmap, bmaplen, kDNSType_SOA);
535        if (qtype != kDNSType_DS)
536        {
537            // For non-DS type questions, we don't want to use the parent side records to
538            // answer it
539            if (ns && !soa)
540            {
541                LogDNSSEC("NSEC3NoDataError: Parent side NSEC %s, can't use for child qname %##s (%s)",
542                    CRDisplayString(m, *closestEncloser), name->c, DNSTypeName(qtype));
543                return mDNSfalse;
544            }
545        }
546        else
547        {
548            if (soa)
549            {
550                LogDNSSEC("NSEC3NoDataError: Child side NSEC %s, can't use for parent qname %##s (%s)",
551                    CRDisplayString(m, *closestEncloser), name->c, DNSTypeName(qtype));
552                return mDNSfalse;
553            }
554        }
555        LogDNSSEC("NSEC3NoDataError: Name -%##s- exists, but qtype %s does not exist in %s", name->c, DNSTypeName(qtype), CRDisplayString(m, *closestEncloser));
556        return mDNStrue;
557    }
558    return mDNSfalse;
559}
560
561mDNSexport void NSEC3NoDataProof(mDNS *const m, DNSSECVerifier *dv, CacheRecord *ncr)
562{
563    CacheRecord *closerEncloser = mDNSNULL;
564    CacheRecord *closestEncloser = mDNSNULL;
565    CacheRecord *wildcard = mDNSNULL;
566    const domainname *ce = mDNSNULL;
567    domainname wild;
568
569    // Section 8.5, 8.6 of RFC 5155
570    if (NSEC3NoDataError(m, ncr, &dv->q.qname, dv->q.qtype, &closestEncloser))
571    {
572        goto verify;
573    }
574    // Section 8.6, 8.7: if we can't find the NSEC3 RR, verify the closest encloser proof
575    // for QNAME and the "next closer" should have the opt out
576    if (!NSEC3ClosestEncloserProof(m, ncr, &dv->q.qname, &closestEncloser, &closerEncloser, &ce, dv->q.qtype))
577    {
578        goto error;
579    }
580
581    // Section 8.7: find a matching NSEC3 for *.closestEncloser
582    wild.c[0] = 1;
583    wild.c[1] = '*';
584    wild.c[2] = 0;
585    if (!AppendDomainName(&wild, ce))
586    {
587        LogMsg("NSEC3NameErrorProof: Can't append domainname to closest encloser name %##s", ce->c);
588        goto error;
589    }
590    if (!NSEC3Find(m, NSEC3ClosestEncloser, ncr, &wild, &wildcard, mDNSNULL, &ce, dv->q.qtype))
591    {
592        // Not a wild card case. Section 8.6 second para applies.
593        LogDNSSEC("NSEC3NoDataProof: Cannot find encloser for wildcard, perhaps not a wildcard case");
594        if (!NSEC3OptOut(closerEncloser))
595        {
596            LogDNSSEC("NSEC3DataProof: opt out not set for %##s (%s), bogus", dv->q.qname.c, DNSTypeName(dv->q.qtype));
597            goto error;
598        }
599        LogDNSSEC("NSEC3DataProof: opt out set, proof complete for %##s (%s)", dv->q.qname.c, DNSTypeName(dv->q.qtype));
600        dv->flags |= NSEC3_OPT_OUT;
601    }
602    else
603    {
604        int bmaplen;
605        mDNSu8 *bmap;
606        NSEC3Parse(&wildcard->resrec, mDNSNULL, mDNSNULL, mDNSNULL, &bmaplen, &bmap);
607        if (BitmapTypeCheck(bmap, bmaplen, dv->q.qtype) || BitmapTypeCheck(bmap, bmaplen, kDNSType_CNAME))
608        {
609            LogDNSSEC("NSEC3NoDataProof: qtype %s exists in %s", DNSTypeName(dv->q.qtype), CRDisplayString(m, wildcard));
610            goto error;
611        }
612        if (dv->q.qtype == kDNSType_DS && BitmapTypeCheck(bmap, bmaplen, kDNSType_SOA))
613        {
614            LogDNSSEC("NSEC3NoDataProof: Child side wildcard NSEC3 %s, can't use for parent qname %##s (%s)",
615                CRDisplayString(m, wildcard), dv->q.qname.c, DNSTypeName(dv->q.qtype));
616            goto error;
617        }
618        else if (dv->q.qtype != kDNSType_DS && !BitmapTypeCheck(bmap, bmaplen, kDNSType_SOA) &&
619            BitmapTypeCheck(bmap, bmaplen, kDNSType_NS))
620        {
621            // Don't use the parent side record for this
622            LogDNSSEC("NSEC3NoDataProof: Parent side wildcard NSEC3 %s, can't use for child qname %##s (%s)",
623                CRDisplayString(m, wildcard), dv->q.qname.c, DNSTypeName(dv->q.qtype));
624            goto error;
625        }
626        LogDNSSEC("NSEC3NoDataProof: Wildcard %##s matched by %s", wild.c, CRDisplayString(m, wildcard));
627    }
628verify:
629
630    if (!VerifyNSEC3(m, dv, ncr, closestEncloser, closerEncloser, wildcard, NoDataNSECCallback))
631        goto error;
632    else
633        return;
634error:
635    dv->DVCallback(m, dv, DNSSEC_Bogus);
636}
637
638mDNSexport mDNSBool NSEC3WildcardAnswerProof(mDNS *const m, CacheRecord *ncr, DNSSECVerifier *dv)
639{
640    int skip;
641    const domainname *nc;
642    CacheRecord *closerEncloser;
643
644    (void) m;
645
646    // Find the next closer name and prove that it is covered by the NSEC3
647    skip = CountLabels(&dv->origName) - CountLabels(dv->wildcardName) - 1;
648    if (skip)
649        nc = SkipLeadingLabels(&dv->origName, skip);
650    else
651        nc = &dv->origName;
652
653    LogDNSSEC("NSEC3WildcardAnswerProof: wildcard name %##s", nc->c);
654
655    if (!NSEC3Find(m, NSEC3Covers, ncr, (domainname *)nc, mDNSNULL, &closerEncloser, mDNSNULL, dv->q.qtype))
656    {
657        LogMsg("NSEC3WildcardAnswerProof: Cannot find closer encloser");
658        return mDNSfalse;
659    }
660    if (!closerEncloser)
661    {
662        LogMsg("NSEC3WildcardAnswerProof: closerEncloser NULL");
663        return mDNSfalse;
664    }
665    if (NSEC3OptOut(closerEncloser))
666    {
667        dv->flags |= NSEC3_OPT_OUT;
668    }
669    // NSEC3 Verification is done by the caller
670    return mDNStrue;
671}
672
673mDNSexport CacheRecord *NSEC3RecordIsDelegation(mDNS *const m, domainname *name, mDNSu16 qtype)
674{
675    CacheGroup *cg;
676    CacheRecord *cr;
677    CacheRecord *ncr;
678    mDNSu32 namehash;
679
680    namehash = DomainNameHashValue(name);
681
682    cg = CacheGroupForName(m, namehash, name);
683    if (!cg)
684    {
685        LogDNSSEC("NSEC3RecordForName: cg NULL for %##s", name);
686        return mDNSNULL;
687    }
688    for (ncr = cg->members; ncr; ncr = ncr->next)
689    {
690        if (ncr->resrec.RecordType != kDNSRecordTypePacketNegative ||
691            ncr->resrec.rrtype != qtype)
692        {
693            continue;
694        }
695        for (cr = ncr->nsec; cr; cr = cr->next)
696        {
697            int hlen, b32len;
698            const mDNSu8 hashName[NSEC3_MAX_HASH_LEN];
699            const mDNSu8 b32Name[NSEC3_MAX_B32_LEN+1];
700            const RDataBody2 *const rdb = (RDataBody2 *)cr->resrec.rdata->u.data;
701            rdataNSEC3 *nsec3;
702
703            if (cr->resrec.rrtype != kDNSType_NSEC3)
704                continue;
705
706            nsec3 = (rdataNSEC3 *)rdb->data;
707
708            if (!NSEC3HashName(name, nsec3, mDNSNULL, 0, hashName, &hlen))
709            {
710                LogMsg("NSEC3RecordIsDelegation: NSEC3HashName failed for %##s", name->c);
711                return mDNSNULL;
712            }
713
714            b32len = baseEncode((char *)b32Name, sizeof(b32Name), (mDNSu8 *)hashName, hlen, ENC_BASE32);
715            if (!b32len)
716            {
717                LogMsg("NSEC3RecordIsDelegation: baseEncode of name %##s failed", name->c);
718                return mDNSNULL;
719            }
720            // Section 2.3 of RFC 4035 states that:
721            //
722            // Each owner name in the zone that has authoritative data or a delegation point NS RRset MUST
723            // have an NSEC resource record.
724            //
725            // This applies to NSEC3 record. So, if we have an NSEC3 record matching the question name with the
726            // NS bit set, then this is a delegation.
727            //
728            if (!NSEC3SameName(&cr->resrec.name->c[1], cr->resrec.name->c[0], (const mDNSu8 *)b32Name, b32len))
729            {
730                int bmaplen;
731                mDNSu8 *bmap;
732
733                LogDNSSEC("NSEC3RecordIsDelegation: CacheRecord %s matches name %##s, b32name %s", CRDisplayString(m, cr), name->c, b32Name);
734                NSEC3Parse(&cr->resrec, mDNSNULL, mDNSNULL, mDNSNULL, &bmaplen, &bmap);
735
736                // See the Insecure Delegation Proof section in dnssec-bis: DS bit and SOA bit
737                // should be absent
738                if (BitmapTypeCheck(bmap, bmaplen, kDNSType_SOA) ||
739                    BitmapTypeCheck(bmap, bmaplen, kDNSType_DS))
740                {
741                    LogDNSSEC("NSEC3RecordIsDelegation: CacheRecord %s has DS or SOA bit set, ignoring", CRDisplayString(m, cr));
742                    return mDNSNULL;
743                }
744                if (BitmapTypeCheck(bmap, bmaplen, kDNSType_NS))
745                    return cr;
746                else
747                    return mDNSNULL;
748            }
749            // If opt-out is not set, then it does not cover any delegations
750            if (!(nsec3->flags & NSEC3_FLAGS_OPTOUT))
751                continue;
752            // Opt-out allows insecure delegations to exist without the NSEC3 RR at the
753            // hashed owner name (see RFC 5155 section 6.0).
754            if (NSEC3CoversName(m, cr, hashName, hlen, b32Name, b32len))
755            {
756                LogDNSSEC("NSEC3RecordIsDelegation: CacheRecord %s covers name %##s with optout", CRDisplayString(m, cr), name->c);
757                return cr;
758            }
759        }
760    }
761    return mDNSNULL;
762}
763
764#else // !DNSSEC_DISABLED
765
766#endif // !DNSSEC_DISABLED
767