1 /* -*- Mode: C; tab-width: 4 -*- 2 * 3 * Copyright (c) 2012 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 #include "mDNSEmbeddedAPI.h" 19 #include "CryptoAlg.h" 20 #include "anonymous.h" 21 #include "DNSCommon.h" 22 23 // Define ANONYMOUS_DISABLED to remove all the anonymous functionality 24 // and use the stub functions implemented later in this file. 25 26 #ifndef ANONYMOUS_DISABLED 27 28 #define ANON_NSEC3_ITERATIONS 1 29 30 mDNSlocal mDNSBool InitializeNSEC3Record(ResourceRecord *rr, const mDNSu8 *AnonData, int len, mDNSu32 salt) 31 { 32 const mDNSu8 *ptr; 33 rdataNSEC3 *nsec3 = (rdataNSEC3 *)rr->rdata->u.data; 34 mDNSu8 *tmp, *nxt; 35 unsigned short iter = ANON_NSEC3_ITERATIONS; 36 int hlen; 37 const mDNSu8 hashName[NSEC3_MAX_HASH_LEN]; 38 39 // Construct the RDATA first and construct the owner name based on that. 40 ptr = (const mDNSu8 *)&salt; 41 debugf("InitializeNSEC3Record: %x%x%x%x, name %##s", ptr[0], ptr[1], ptr[2], ptr[3], rr->name->c); 42 43 // Set the RDATA 44 nsec3->alg = SHA1_DIGEST_TYPE; 45 nsec3->flags = 0; 46 nsec3->iterations = swap16(iter); 47 nsec3->saltLength = 4; 48 tmp = (mDNSu8 *)&nsec3->salt; 49 *tmp++ = ptr[0]; 50 *tmp++ = ptr[1]; 51 *tmp++ = ptr[2]; 52 *tmp++ = ptr[3]; 53 54 // hashLength, nxt, bitmap 55 *tmp++ = SHA1_HASH_LENGTH; // hash length 56 nxt = tmp; 57 tmp += SHA1_HASH_LENGTH; 58 *tmp++ = 0; // window number 59 *tmp++ = NSEC_MCAST_WINDOW_SIZE; // window length 60 mDNSPlatformMemZero(tmp, NSEC_MCAST_WINDOW_SIZE); 61 tmp[kDNSType_PTR >> 3] |= 128 >> (kDNSType_PTR & 7); 62 63 // Hash the base service name + salt + AnonData 64 if (!NSEC3HashName(rr->name, nsec3, AnonData, len, hashName, &hlen)) 65 { 66 LogMsg("InitializeNSEC3Record: NSEC3HashName failed for ##s", rr->name->c); 67 return mDNSfalse; 68 } 69 if (hlen != SHA1_HASH_LENGTH) 70 { 71 LogMsg("InitializeNSEC3Record: hlen wrong %d", hlen); 72 return mDNSfalse; 73 } 74 mDNSPlatformMemCopy(nxt, hashName, hlen); 75 76 return mDNStrue; 77 } 78 79 mDNSlocal ResourceRecord *ConstructNSEC3Record(const domainname *service, const mDNSu8 *AnonData, int len, mDNSu32 salt) 80 { 81 ResourceRecord *rr; 82 int dlen; 83 domainname *name; 84 85 // We are just allocating an RData which has StandardAuthRDSize 86 if (StandardAuthRDSize < MCAST_NSEC3_RDLENGTH) 87 { 88 LogMsg("ConstructNSEC3Record: StandardAuthRDSize %d smaller than MCAST_NSEC3_RDLENGTH %d", StandardAuthRDSize, MCAST_NSEC3_RDLENGTH); 89 return mDNSNULL; 90 } 91 92 dlen = DomainNameLength(service); 93 94 // Allocate space for the name and RData. 95 rr = mDNSPlatformMemAllocate(sizeof(ResourceRecord) + dlen + sizeof(RData)); 96 if (!rr) 97 return mDNSNULL; 98 name = (domainname *)((mDNSu8 *)rr + sizeof(ResourceRecord)); 99 rr->RecordType = kDNSRecordTypePacketAuth; 100 rr->InterfaceID = mDNSInterface_Any; 101 rr->name = (const domainname *)name; 102 rr->rrtype = kDNSType_NSEC3; 103 rr->rrclass = kDNSClass_IN; 104 rr->rroriginalttl = kStandardTTL; 105 rr->rDNSServer = mDNSNULL; 106 rr->rdlength = MCAST_NSEC3_RDLENGTH; 107 rr->rdestimate = MCAST_NSEC3_RDLENGTH; 108 rr->rdata = (RData *)((mDNSu8 *)rr->name + dlen); 109 110 AssignDomainName(name, service); 111 if (!InitializeNSEC3Record(rr, AnonData, len, salt)) 112 { 113 mDNSPlatformMemFree(rr); 114 return mDNSNULL; 115 } 116 return rr; 117 } 118 119 mDNSlocal ResourceRecord *CopyNSEC3ResourceRecord(AnonymousInfo *si, const ResourceRecord *rr) 120 { 121 int len; 122 domainname *name; 123 ResourceRecord *nsec3rr; 124 125 if (rr->rdlength < MCAST_NSEC3_RDLENGTH) 126 { 127 LogMsg("CopyNSEC3ResourceRecord: rdlength %d smaller than MCAST_NSEC3_RDLENGTH %d", rr->rdlength, MCAST_NSEC3_RDLENGTH); 128 return mDNSNULL; 129 } 130 // Allocate space for the name and the rdata along with the ResourceRecord 131 len = DomainNameLength(rr->name); 132 nsec3rr = mDNSPlatformMemAllocate(sizeof(ResourceRecord) + len + sizeof(RData)); 133 if (!nsec3rr) 134 return mDNSNULL; 135 136 *nsec3rr = *rr; 137 name = (domainname *)((mDNSu8 *)nsec3rr + sizeof(ResourceRecord)); 138 nsec3rr->name = (const domainname *)name; 139 AssignDomainName(name, rr->name); 140 141 nsec3rr->rdata = (RData *)((mDNSu8 *)nsec3rr->name + len); 142 mDNSPlatformMemCopy(nsec3rr->rdata->u.data, rr->rdata->u.data, rr->rdlength); 143 144 si->nsec3RR = nsec3rr; 145 146 return nsec3rr; 147 } 148 149 // When a service is started or a browse is started with the Anonymous data, we allocate a new random 150 // number and based on that allocate a new NSEC3 resource record whose hash is a function of random number (salt) and 151 // the anonymous data. 152 // 153 // If we receive a packet with the NSEC3 option, we need to cache that along with the resource record so that we can 154 // check against the question to see whether it answers them or not. In that case, we pass the "rr" that we received. 155 mDNSexport AnonymousInfo *AllocateAnonInfo(const domainname *service, const mDNSu8 *data, int len, const ResourceRecord *rr) 156 { 157 AnonymousInfo *ai; 158 ai = (AnonymousInfo *)mDNSPlatformMemAllocate(sizeof(AnonymousInfo)); 159 if (!ai) 160 { 161 return mDNSNULL; 162 } 163 mDNSPlatformMemZero(ai, sizeof(AnonymousInfo)); 164 if (rr) 165 { 166 if (!CopyNSEC3ResourceRecord(ai, rr)) 167 { 168 mDNSPlatformMemFree(ai); 169 return mDNSNULL; 170 } 171 return ai; 172 } 173 ai->salt = mDNSRandom(0xFFFFFFFF); 174 ai->AnonData = mDNSPlatformMemAllocate(len); 175 if (!ai->AnonData) 176 { 177 mDNSPlatformMemFree(ai); 178 return mDNSNULL; 179 } 180 ai->AnonDataLen = len; 181 mDNSPlatformMemCopy(ai->AnonData, data, len); 182 ai->nsec3RR = ConstructNSEC3Record(service, data, len, ai->salt); 183 if (!ai->nsec3RR) 184 { 185 mDNSPlatformMemFree(ai); 186 return mDNSNULL; 187 } 188 return ai; 189 } 190 191 mDNSexport void FreeAnonInfo(AnonymousInfo *ai) 192 { 193 if (ai->nsec3RR) 194 mDNSPlatformMemFree(ai->nsec3RR); 195 if (ai->AnonData) 196 mDNSPlatformMemFree(ai->AnonData); 197 mDNSPlatformMemFree(ai); 198 } 199 200 mDNSexport void ReInitAnonInfo(AnonymousInfo **AnonInfo, const domainname *name) 201 { 202 if (*AnonInfo) 203 { 204 AnonymousInfo *ai = *AnonInfo; 205 *AnonInfo = AllocateAnonInfo(name, ai->AnonData, ai->AnonDataLen, mDNSNULL); 206 if (!(*AnonInfo)) 207 *AnonInfo = ai; 208 else 209 FreeAnonInfo(ai); 210 } 211 } 212 213 // This function should be used only if you know that the question and 214 // the resource record belongs to the same set. The main usage is 215 // in ProcessQuery where we find the question to be part of the same 216 // set as the resource record, but it needs the AnonData to be 217 // initialized so that it can walk the cache records to see if they 218 // answer the question. 219 mDNSexport void SetAnonData(DNSQuestion *q, ResourceRecord *rr, mDNSBool ForQuestion) 220 { 221 if (!q->AnonInfo || !rr->AnonInfo) 222 { 223 LogMsg("SetAnonData: question %##s(%p), rr %##s(%p), NULL", q->qname.c, q->AnonInfo, rr->name->c, rr->AnonInfo); 224 return; 225 } 226 227 debugf("SetAnonData: question %##s(%p), rr %##s(%p)", q->qname.c, q->AnonInfo, rr->name->c, rr->AnonInfo); 228 if (ForQuestion) 229 { 230 if (!q->AnonInfo->AnonData) 231 { 232 q->AnonInfo->AnonData = mDNSPlatformMemAllocate(rr->AnonInfo->AnonDataLen); 233 if (!q->AnonInfo->AnonData) 234 return; 235 } 236 mDNSPlatformMemCopy(q->AnonInfo->AnonData, rr->AnonInfo->AnonData, rr->AnonInfo->AnonDataLen); 237 q->AnonInfo->AnonDataLen = rr->AnonInfo->AnonDataLen; 238 } 239 else 240 { 241 if (!rr->AnonInfo->AnonData) 242 { 243 rr->AnonInfo->AnonData = mDNSPlatformMemAllocate(q->AnonInfo->AnonDataLen); 244 if (!rr->AnonInfo->AnonData) 245 return; 246 } 247 mDNSPlatformMemCopy(rr->AnonInfo->AnonData, q->AnonInfo->AnonData, q->AnonInfo->AnonDataLen); 248 rr->AnonInfo->AnonDataLen = q->AnonInfo->AnonDataLen; 249 } 250 } 251 252 // returns -1 if the caller should ignore the result 253 // returns 1 if the record answers the question 254 // returns 0 if the record does not answer the question 255 mDNSexport int AnonInfoAnswersQuestion(const ResourceRecord *const rr, const DNSQuestion *const q) 256 { 257 mDNSexport mDNS mDNSStorage; 258 ResourceRecord *nsec3RR; 259 int i; 260 AnonymousInfo *qai, *rai; 261 mDNSu8 *AnonData; 262 int AnonDataLen; 263 rdataNSEC3 *nsec3; 264 int hlen; 265 const mDNSu8 hashName[NSEC3_MAX_HASH_LEN]; 266 int nxtLength; 267 mDNSu8 *nxtName; 268 269 debugf("AnonInfoAnswersQuestion: question qname %##s", q->qname.c); 270 271 // Currently only PTR records can have anonymous information 272 if (q->qtype != kDNSType_PTR) 273 { 274 return -1; 275 } 276 277 // We allow anonymous questions to be answered by both normal services (without the 278 // anonymous information) and anonymous services that are part of the same set. And 279 // normal questions discover normal services and all anonymous services. 280 // 281 // The three cases have been enumerated clearly even though they all behave the 282 // same way. 283 if (!q->AnonInfo) 284 { 285 debugf("AnonInfoAnswersQuestion: not a anonymous type question"); 286 if (!rr->AnonInfo) 287 { 288 // case 1 289 return -1; 290 } 291 else 292 { 293 // case 2 294 debugf("AnonInfoAnswersQuestion: Question %##s not answered using anonymous record %##s", q->qname.c, rr->name->c); 295 return -1; 296 } 297 } 298 else 299 { 300 // case 3 301 if (!rr->AnonInfo) 302 { 303 debugf("AnonInfoAnswersQuestion: not a anonymous type record"); 304 return -1; 305 } 306 } 307 308 // case 4: We have the anonymous information both in the question and the record. We need 309 // two sets of information to validate. 310 // 311 // 1) Anonymous data that identifies the set/group 312 // 2) NSEC3 record that contains the hash and the salt 313 // 314 // If the question is a remote one, it does not have the anonymous information to validate (just 315 // the NSEC3 record) and hence the anonymous data should come from the local resource record. If the 316 // question is local, it can come from either of them and if there is a mismatch between the 317 // question and record, it won't validate. 318 319 qai = q->AnonInfo; 320 rai = rr->AnonInfo; 321 322 if (qai->AnonData && rai->AnonData) 323 { 324 // Before a cache record is created, if there is a matching question i.e., part 325 // of the same set, then when the cache is created we also set the anonymous 326 // information. Otherwise, the cache record contains just the NSEC3 record and we 327 // won't be here for that case. 328 // 329 // It is also possible that a local question is matched against the local AuthRecord 330 // as that is also the case for which the AnonData would be non-NULL for both. 331 // We match questions against AuthRecords (rather than the cache) for LocalOnly case and 332 // to see whether a .local query should be suppressed or not. The latter never happens 333 // because PTR queries are never suppressed. 334 335 // If they don't belong to the same anonymous set, then no point in validating. 336 if ((qai->AnonDataLen != rai->AnonDataLen) || 337 mDNSPlatformMemCmp(qai->AnonData, rai->AnonData, qai->AnonDataLen) != 0) 338 { 339 debugf("AnonInfoAnswersQuestion: AnonData mis-match for record %s question %##s ", 340 RRDisplayString(&mDNSStorage, rr), q->qname.c); 341 return 0; 342 } 343 // AnonData matches i.e they belong to the same group and the same service. 344 LogInfo("AnonInfoAnswersQuestion: Answering qname %##s, rname %##s, without validation", q->qname.c, 345 rr->name->c); 346 return 1; 347 } 348 else 349 { 350 debugf("AnonInfoAnswersQuestion: question %p, record %p", qai->AnonData, rai->AnonData); 351 } 352 353 if (qai->AnonData) 354 { 355 // If there is AnonData, then this is a local question. The 356 // NSEC3 RR comes from the resource record which could be part 357 // of the cache or local auth record. The cache entry could 358 // be from a remote host or created when we heard our own 359 // announcements. In any case, we use that to see if it matches 360 // the question. 361 AnonData = qai->AnonData; 362 AnonDataLen = qai->AnonDataLen; 363 nsec3RR = rai->nsec3RR; 364 } 365 else 366 { 367 // Remote question or hearing our own question back 368 AnonData = rai->AnonData; 369 AnonDataLen = rai->AnonDataLen; 370 nsec3RR = qai->nsec3RR; 371 } 372 373 if (!AnonData || !nsec3RR) 374 { 375 // AnonData can be NULL for the cache entry and if we are hearing our own question back, AnonData is NULL for 376 // that too and we can end up here for that case. 377 debugf("AnonInfoAnswersQuestion: AnonData %p or nsec3RR %p, NULL for question %##s, record %s", AnonData, nsec3RR, 378 q->qname.c, RRDisplayString(&mDNSStorage, rr)); 379 return 0; 380 } 381 debugf("AnonInfoAnswersQuestion: Validating question %##s, ResourceRecord %s", q->qname.c, RRDisplayString(&mDNSStorage, nsec3RR)); 382 383 384 nsec3 = (rdataNSEC3 *)nsec3RR->rdata->u.data; 385 386 if (!NSEC3HashName(nsec3RR->name, nsec3, AnonData, AnonDataLen, hashName, &hlen)) 387 { 388 LogMsg("AnonInfoAnswersQuestion: NSEC3HashName failed for ##s", nsec3RR->name->c); 389 return mDNSfalse; 390 } 391 if (hlen != SHA1_HASH_LENGTH) 392 { 393 LogMsg("AnonInfoAnswersQuestion: hlen wrong %d", hlen); 394 return mDNSfalse; 395 } 396 397 NSEC3Parse(nsec3RR, mDNSNULL, &nxtLength, &nxtName, mDNSNULL, mDNSNULL); 398 399 if (hlen != nxtLength) 400 { 401 LogMsg("AnonInfoAnswersQuestion: ERROR!! hlen %d not same as nxtLength %d", hlen, nxtLength); 402 return mDNSfalse; 403 } 404 405 for (i = 0; i < nxtLength; i++) 406 { 407 if (nxtName[i] != hashName[i]) 408 { 409 debugf("AnonInfoAnswersQuestion: mismatch output %x, digest %x, i %d", nxtName[i+1], hashName[i], i); 410 return 0; 411 } 412 } 413 LogInfo("AnonInfoAnswersQuestion: ResourceRecord %s matched question %##s (%s)", RRDisplayString(&mDNSStorage, nsec3RR), q->qname.c, DNSTypeName(q->qtype)); 414 return 1; 415 } 416 417 // Find a matching NSEC3 record for the name. We parse the questions and the records in the packet in order. 418 // Similarly we also parse the NSEC3 records in order and this mapping to the questions and records 419 // respectively. 420 mDNSlocal CacheRecord *FindMatchingNSEC3ForName(mDNS *const m, CacheRecord **nsec3, const domainname *name) 421 { 422 CacheRecord *cr; 423 CacheRecord **prev = nsec3; 424 425 (void) m; 426 427 for (cr = *nsec3; cr; cr = cr->next) 428 { 429 if (SameDomainName(cr->resrec.name, name)) 430 { 431 debugf("FindMatchingNSEC3ForName: NSEC3 record %s matched %##s", CRDisplayString(m, cr), name->c); 432 *prev = cr->next; 433 cr->next = mDNSNULL; 434 return cr; 435 } 436 prev = &cr->next; 437 } 438 return mDNSNULL; 439 } 440 441 mDNSexport void InitializeAnonInfoForQuestion(mDNS *const m, CacheRecord **McastNSEC3Records, DNSQuestion *q) 442 { 443 CacheRecord *nsec3CR; 444 445 if (q->qtype != kDNSType_PTR) 446 return; 447 448 nsec3CR = FindMatchingNSEC3ForName(m, McastNSEC3Records, &q->qname); 449 if (nsec3CR) 450 { 451 q->AnonInfo = AllocateAnonInfo(mDNSNULL, mDNSNULL, 0, &nsec3CR->resrec); 452 if (q->AnonInfo) 453 { 454 debugf("InitializeAnonInfoForQuestion: Found a matching NSEC3 record %s, for %##s (%s)", 455 RRDisplayString(m, q->AnonInfo->nsec3RR), q->qname.c, DNSTypeName(q->qtype)); 456 } 457 ReleaseCacheRecord(m, nsec3CR); 458 } 459 } 460 461 mDNSexport void InitializeAnonInfoForCR(mDNS *const m, CacheRecord **McastNSEC3Records, CacheRecord *cr) 462 { 463 CacheRecord *nsec3CR; 464 465 if (!(*McastNSEC3Records)) 466 return; 467 468 // If already initialized or not a PTR type, we don't have to do anything 469 if (cr->resrec.AnonInfo || cr->resrec.rrtype != kDNSType_PTR) 470 return; 471 472 nsec3CR = FindMatchingNSEC3ForName(m, McastNSEC3Records, cr->resrec.name); 473 if (nsec3CR) 474 { 475 cr->resrec.AnonInfo = AllocateAnonInfo(mDNSNULL, mDNSNULL, 0, &nsec3CR->resrec); 476 if (cr->resrec.AnonInfo) 477 { 478 debugf("InitializeAnonInfoForCR: Found a matching NSEC3 record %s, for %##s (%s)", 479 RRDisplayString(m, cr->resrec.AnonInfo->nsec3RR), cr->resrec.name->c, 480 DNSTypeName(cr->resrec.rrtype)); 481 } 482 ReleaseCacheRecord(m, nsec3CR); 483 } 484 } 485 486 mDNSexport mDNSBool IdenticalAnonInfo(AnonymousInfo *a1, AnonymousInfo *a2) 487 { 488 // if a1 is NULL and a2 is not NULL AND vice-versa 489 // return false as there is a change. 490 if ((a1 != mDNSNULL) != (a2 != mDNSNULL)) 491 return mDNSfalse; 492 493 // Both could be NULL or non-NULL 494 if (a1 && a2) 495 { 496 // The caller already verified that the owner name is the same. 497 // Check whether the RData is same. 498 if (!IdenticalSameNameRecord(a1->nsec3RR, a2->nsec3RR)) 499 { 500 debugf("IdenticalAnonInfo: nsec3RR mismatch"); 501 return mDNSfalse; 502 } 503 } 504 return mDNStrue; 505 } 506 507 mDNSexport void CopyAnonInfoForCR(mDNS *const m, CacheRecord *crto, CacheRecord *crfrom) 508 { 509 AnonymousInfo *aifrom = crfrom->resrec.AnonInfo; 510 AnonymousInfo *aito = crto->resrec.AnonInfo; 511 512 (void) m; 513 514 if (!aifrom) 515 return; 516 517 if (aito) 518 { 519 crto->resrec.AnonInfo = aifrom; 520 FreeAnonInfo(aito); 521 crfrom->resrec.AnonInfo = mDNSNULL; 522 } 523 else 524 { 525 FreeAnonInfo(aifrom); 526 crfrom->resrec.AnonInfo = mDNSNULL; 527 } 528 } 529 530 #else // !ANONYMOUS_DISABLED 531 532 mDNSexport void ReInitAnonInfo(AnonymousInfo **si, const domainname *name) 533 { 534 (void)si; 535 (void)name; 536 } 537 538 mDNSexport AnonymousInfo * AllocateAnonInfo(const domainname *service, const mDNSu8 *AnonData, int len, const ResourceRecord *rr) 539 { 540 (void)service; 541 (void)AnonData; 542 (void)len; 543 (void)rr; 544 545 return mDNSNULL; 546 } 547 548 mDNSexport void FreeAnonInfo(AnonymousInfo *ai) 549 { 550 (void)ai; 551 } 552 553 mDNSexport void SetAnonData(DNSQuestion *q, ResourceRecord *rr, mDNSBool ForQuestion) 554 { 555 (void)q; 556 (void)rr; 557 (void)ForQuestion; 558 } 559 560 mDNSexport int AnonInfoAnswersQuestion(const ResourceRecord *const rr, const DNSQuestion *const q) 561 { 562 (void)rr; 563 (void)q; 564 565 return mDNSfalse; 566 } 567 568 mDNSexport void InitializeAnonInfoForQuestion(mDNS *const m, CacheRecord **McastNSEC3Records, DNSQuestion *q) 569 { 570 (void)m; 571 (void)McastNSEC3Records; 572 (void)q; 573 } 574 575 mDNSexport void InitializeAnonInfoForCR(mDNS *const m, CacheRecord **McastNSEC3Records, CacheRecord *cr) 576 { 577 (void)m; 578 (void)McastNSEC3Records; 579 (void)cr; 580 } 581 582 mDNSexport void CopyAnonInfoForCR(mDNS *const m, CacheRecord *crto, CacheRecord *crfrom) 583 { 584 (void)m; 585 (void)crto; 586 (void)crfrom; 587 } 588 589 mDNSexport mDNSBool IdenticalAnonInfo(AnonymousInfo *a1, AnonymousInfo *a2) 590 { 591 (void)a1; 592 (void)a2; 593 594 return mDNStrue; 595 } 596 597 #endif // !ANONYMOUS_DISABLED 598