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