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