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