1 /* -*- Mode: C; tab-width: 4 -*-
3 * Copyright (c) 2012-2013 Apple Inc. All rights reserved.
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
9 * http://www.apache.org/licenses/LICENSE-2.0
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.
18 #include "mDNSEmbeddedAPI.h"
19 #include "CryptoAlg.h"
20 #include "anonymous.h"
21 #include "DNSCommon.h"
23 // Define ANONYMOUS_DISABLED to remove all the anonymous functionality
24 // and use the stub functions implemented later in this file.
26 #ifndef ANONYMOUS_DISABLED
28 #define ANON_NSEC3_ITERATIONS 1
30 struct AnonInfoResourceRecord_struct
32 ResourceRecord resrec
;
36 typedef struct AnonInfoResourceRecord_struct AnonInfoResourceRecord
;
38 mDNSlocal mDNSBool
InitializeNSEC3Record(ResourceRecord
*rr
, const mDNSu8
*AnonData
, int len
, mDNSu32 salt
)
41 rdataNSEC3
*nsec3
= (rdataNSEC3
*)rr
->rdata
->u
.data
;
43 unsigned short iter
= ANON_NSEC3_ITERATIONS
;
45 const mDNSu8 hashName
[NSEC3_MAX_HASH_LEN
];
47 // Construct the RDATA first and construct the owner name based on that.
48 ptr
= (const mDNSu8
*)&salt
;
49 debugf("InitializeNSEC3Record: %x%x%x%x, name %##s", ptr
[0], ptr
[1], ptr
[2], ptr
[3], rr
->name
->c
);
52 nsec3
->alg
= SHA1_DIGEST_TYPE
;
54 nsec3
->iterations
= swap16(iter
);
55 nsec3
->saltLength
= 4;
56 tmp
= (mDNSu8
*)&nsec3
->salt
;
62 // hashLength, nxt, bitmap
63 *tmp
++ = SHA1_HASH_LENGTH
; // hash length
65 tmp
+= SHA1_HASH_LENGTH
;
66 *tmp
++ = 0; // window number
67 *tmp
++ = NSEC_MCAST_WINDOW_SIZE
; // window length
68 mDNSPlatformMemZero(tmp
, NSEC_MCAST_WINDOW_SIZE
);
69 tmp
[kDNSType_PTR
>> 3] |= 128 >> (kDNSType_PTR
& 7);
71 // Hash the base service name + salt + AnonData
72 if (!NSEC3HashName(rr
->name
, nsec3
, AnonData
, len
, hashName
, &hlen
))
74 LogMsg("InitializeNSEC3Record: NSEC3HashName failed for %##s", rr
->name
->c
);
77 if (hlen
!= SHA1_HASH_LENGTH
)
79 LogMsg("InitializeNSEC3Record: hlen wrong %d", hlen
);
82 mDNSPlatformMemCopy(nxt
, hashName
, hlen
);
87 mDNSlocal ResourceRecord
*ConstructNSEC3Record(const domainname
*service
, const mDNSu8
*AnonData
, int len
, mDNSu32 salt
)
93 // We are just allocating an RData which has StandardAuthRDSize
94 if (StandardAuthRDSize
< MCAST_NSEC3_RDLENGTH
)
96 LogMsg("ConstructNSEC3Record: StandardAuthRDSize %d smaller than MCAST_NSEC3_RDLENGTH %d", StandardAuthRDSize
, MCAST_NSEC3_RDLENGTH
);
100 dlen
= DomainNameLength(service
);
102 // Allocate space for the name and RData.
103 rr
= mDNSPlatformMemAllocate(sizeof(ResourceRecord
) + dlen
+ sizeof(RData
));
106 name
= (domainname
*)((mDNSu8
*)rr
+ sizeof(ResourceRecord
));
107 rr
->RecordType
= kDNSRecordTypePacketAuth
;
108 rr
->InterfaceID
= mDNSInterface_Any
;
109 rr
->name
= (const domainname
*)name
;
110 rr
->rrtype
= kDNSType_NSEC3
;
111 rr
->rrclass
= kDNSClass_IN
;
112 rr
->rroriginalttl
= kStandardTTL
;
113 rr
->rDNSServer
= mDNSNULL
;
114 rr
->rdlength
= MCAST_NSEC3_RDLENGTH
;
115 rr
->rdestimate
= MCAST_NSEC3_RDLENGTH
;
116 rr
->rdata
= (RData
*)((mDNSu8
*)rr
->name
+ dlen
);
118 AssignDomainName(name
, service
);
119 if (!InitializeNSEC3Record(rr
, AnonData
, len
, salt
))
121 mDNSPlatformMemFree(rr
);
127 mDNSlocal ResourceRecord
*CopyNSEC3ResourceRecord(AnonymousInfo
*si
, const ResourceRecord
*rr
)
129 AnonInfoResourceRecord
*anonRR
;
134 if (rr
->rdlength
< MCAST_NSEC3_RDLENGTH
)
136 LogMsg("CopyNSEC3ResourceRecord: rdlength %d smaller than MCAST_NSEC3_RDLENGTH %d", rr
->rdlength
, MCAST_NSEC3_RDLENGTH
);
139 // Allocate space for the name and the rdata along with the ResourceRecord
140 neededLen
= rr
->rdlength
+ DomainNameLength(rr
->name
);
141 extraLen
= (neededLen
> sizeof(RDataBody
)) ? (neededLen
- sizeof(RDataBody
)) : 0;
142 anonRR
= (AnonInfoResourceRecord
*)mDNSPlatformMemAllocate(sizeof(AnonInfoResourceRecord
) + extraLen
);
146 anonRR
->resrec
= *rr
;
148 anonRR
->rdatastorage
.MaxRDLength
= rr
->rdlength
;
149 mDNSPlatformMemCopy(anonRR
->rdatastorage
.u
.data
, rr
->rdata
->u
.data
, rr
->rdlength
);
151 name
= (domainname
*)(anonRR
->rdatastorage
.u
.data
+ rr
->rdlength
);
152 AssignDomainName(name
, rr
->name
);
154 anonRR
->resrec
.name
= name
;
155 anonRR
->resrec
.rdata
= &anonRR
->rdatastorage
;
157 si
->nsec3RR
= (ResourceRecord
*)anonRR
;
162 // When a service is started or a browse is started with the Anonymous data, we allocate a new random
163 // number and based on that allocate a new NSEC3 resource record whose hash is a function of random number (salt) and
164 // the anonymous data.
166 // If we receive a packet with the NSEC3 option, we need to cache that along with the resource record so that we can
167 // check against the question to see whether it answers them or not. In that case, we pass the "rr" that we received.
168 mDNSexport AnonymousInfo
*AllocateAnonInfo(const domainname
*service
, const mDNSu8
*data
, int len
, const ResourceRecord
*rr
)
171 ai
= (AnonymousInfo
*)mDNSPlatformMemAllocate(sizeof(AnonymousInfo
));
176 mDNSPlatformMemZero(ai
, sizeof(AnonymousInfo
));
179 if (!CopyNSEC3ResourceRecord(ai
, rr
))
181 mDNSPlatformMemFree(ai
);
186 ai
->salt
= mDNSRandom(0xFFFFFFFF);
187 ai
->AnonData
= mDNSPlatformMemAllocate(len
);
190 mDNSPlatformMemFree(ai
);
193 ai
->AnonDataLen
= len
;
194 mDNSPlatformMemCopy(ai
->AnonData
, data
, len
);
195 ai
->nsec3RR
= ConstructNSEC3Record(service
, data
, len
, ai
->salt
);
198 mDNSPlatformMemFree(ai
);
204 mDNSexport
void FreeAnonInfo(AnonymousInfo
*ai
)
207 mDNSPlatformMemFree(ai
->nsec3RR
);
209 mDNSPlatformMemFree(ai
->AnonData
);
210 mDNSPlatformMemFree(ai
);
213 mDNSexport
void ReInitAnonInfo(AnonymousInfo
**AnonInfo
, const domainname
*name
)
217 AnonymousInfo
*ai
= *AnonInfo
;
218 *AnonInfo
= AllocateAnonInfo(name
, ai
->AnonData
, ai
->AnonDataLen
, mDNSNULL
);
226 // This function should be used only if you know that the question and
227 // the resource record belongs to the same set. The main usage is
228 // in ProcessQuery where we find the question to be part of the same
229 // set as the resource record, but it needs the AnonData to be
230 // initialized so that it can walk the cache records to see if they
231 // answer the question.
232 mDNSexport
void SetAnonData(DNSQuestion
*q
, ResourceRecord
*rr
, mDNSBool ForQuestion
)
234 if (!q
->AnonInfo
|| !rr
->AnonInfo
)
236 LogMsg("SetAnonData: question %##s(%p), rr %##s(%p), NULL", q
->qname
.c
, q
->AnonInfo
, rr
->name
->c
, rr
->AnonInfo
);
240 debugf("SetAnonData: question %##s(%p), rr %##s(%p)", q
->qname
.c
, q
->AnonInfo
, rr
->name
->c
, rr
->AnonInfo
);
243 if (q
->AnonInfo
->AnonDataLen
< rr
->AnonInfo
->AnonDataLen
)
245 mDNSPlatformMemFree(q
->AnonInfo
->AnonData
);
246 q
->AnonInfo
->AnonData
= mDNSNULL
;
249 if (!q
->AnonInfo
->AnonData
)
251 q
->AnonInfo
->AnonData
= mDNSPlatformMemAllocate(rr
->AnonInfo
->AnonDataLen
);
252 if (!q
->AnonInfo
->AnonData
)
255 mDNSPlatformMemCopy(q
->AnonInfo
->AnonData
, rr
->AnonInfo
->AnonData
, rr
->AnonInfo
->AnonDataLen
);
256 q
->AnonInfo
->AnonDataLen
= rr
->AnonInfo
->AnonDataLen
;
260 if (rr
->AnonInfo
->AnonDataLen
< q
->AnonInfo
->AnonDataLen
)
262 mDNSPlatformMemFree(rr
->AnonInfo
->AnonData
);
263 rr
->AnonInfo
->AnonData
= mDNSNULL
;
266 if (!rr
->AnonInfo
->AnonData
)
268 rr
->AnonInfo
->AnonData
= mDNSPlatformMemAllocate(q
->AnonInfo
->AnonDataLen
);
269 if (!rr
->AnonInfo
->AnonData
)
272 mDNSPlatformMemCopy(rr
->AnonInfo
->AnonData
, q
->AnonInfo
->AnonData
, q
->AnonInfo
->AnonDataLen
);
273 rr
->AnonInfo
->AnonDataLen
= q
->AnonInfo
->AnonDataLen
;
277 // returns -1 if the caller should ignore the result
278 // returns 1 if the record answers the question
279 // returns 0 if the record does not answer the question
280 mDNSexport
int AnonInfoAnswersQuestion(const ResourceRecord
*const rr
, const DNSQuestion
*const q
)
282 mDNSexport mDNS mDNSStorage
;
283 ResourceRecord
*nsec3RR
;
285 AnonymousInfo
*qai
, *rai
;
292 mDNSu8 hashName
[NSEC3_MAX_HASH_LEN
];
293 mDNSPlatformMemZero(hashName
, sizeof(hashName
));
295 debugf("AnonInfoAnswersQuestion: question qname %##s", q
->qname
.c
);
297 // Currently only PTR records can have anonymous information
298 if (q
->qtype
!= kDNSType_PTR
)
303 // We allow anonymous questions to be answered by both normal services (without the
304 // anonymous information) and anonymous services that are part of the same set. And
305 // normal questions discover normal services and all anonymous services.
307 // The three cases have been enumerated clearly even though they all behave the
311 debugf("AnonInfoAnswersQuestion: not a anonymous type question");
320 debugf("AnonInfoAnswersQuestion: Question %##s not answered using anonymous record %##s", q
->qname
.c
, rr
->name
->c
);
329 debugf("AnonInfoAnswersQuestion: not a anonymous type record");
334 // case 4: We have the anonymous information both in the question and the record. We need
335 // two sets of information to validate.
337 // 1) Anonymous data that identifies the set/group
338 // 2) NSEC3 record that contains the hash and the salt
340 // If the question is a remote one, it does not have the anonymous information to validate (just
341 // the NSEC3 record) and hence the anonymous data should come from the local resource record. If the
342 // question is local, it can come from either of them and if there is a mismatch between the
343 // question and record, it won't validate.
348 if (qai
->AnonData
&& rai
->AnonData
)
350 // Before a cache record is created, if there is a matching question i.e., part
351 // of the same set, then when the cache is created we also set the anonymous
352 // information. Otherwise, the cache record contains just the NSEC3 record and we
353 // won't be here for that case.
355 // It is also possible that a local question is matched against the local AuthRecord
356 // as that is also the case for which the AnonData would be non-NULL for both.
357 // We match questions against AuthRecords (rather than the cache) for LocalOnly case and
358 // to see whether a .local query should be suppressed or not. The latter never happens
359 // because PTR queries are never suppressed.
361 // If they don't belong to the same anonymous set, then no point in validating.
362 if ((qai
->AnonDataLen
!= rai
->AnonDataLen
) ||
363 mDNSPlatformMemCmp(qai
->AnonData
, rai
->AnonData
, qai
->AnonDataLen
) != 0)
365 debugf("AnonInfoAnswersQuestion: AnonData mis-match for record %s question %##s ",
366 RRDisplayString(&mDNSStorage
, rr
), q
->qname
.c
);
369 // AnonData matches i.e they belong to the same group and the same service.
370 LogInfo("AnonInfoAnswersQuestion: Answering qname %##s, rname %##s, without validation", q
->qname
.c
,
376 debugf("AnonInfoAnswersQuestion: question %p, record %p", qai
->AnonData
, rai
->AnonData
);
381 // If there is AnonData, then this is a local question. The
382 // NSEC3 RR comes from the resource record which could be part
383 // of the cache or local auth record. The cache entry could
384 // be from a remote host or created when we heard our own
385 // announcements. In any case, we use that to see if it matches
387 AnonData
= qai
->AnonData
;
388 AnonDataLen
= qai
->AnonDataLen
;
389 nsec3RR
= rai
->nsec3RR
;
393 // Remote question or hearing our own question back
394 AnonData
= rai
->AnonData
;
395 AnonDataLen
= rai
->AnonDataLen
;
396 nsec3RR
= qai
->nsec3RR
;
399 if (!AnonData
|| !nsec3RR
)
401 // AnonData can be NULL for the cache entry and if we are hearing our own question back, AnonData is NULL for
402 // that too and we can end up here for that case.
403 debugf("AnonInfoAnswersQuestion: AnonData %p or nsec3RR %p, NULL for question %##s, record %s", AnonData
, nsec3RR
,
404 q
->qname
.c
, RRDisplayString(&mDNSStorage
, rr
));
407 debugf("AnonInfoAnswersQuestion: Validating question %##s, ResourceRecord %s", q
->qname
.c
, RRDisplayString(&mDNSStorage
, nsec3RR
));
410 nsec3
= (rdataNSEC3
*)nsec3RR
->rdata
->u
.data
;
412 if (!NSEC3HashName(nsec3RR
->name
, nsec3
, AnonData
, AnonDataLen
, hashName
, &hlen
))
414 LogMsg("AnonInfoAnswersQuestion: NSEC3HashName failed for %##s", nsec3RR
->name
->c
);
417 if (hlen
!= SHA1_HASH_LENGTH
)
419 LogMsg("AnonInfoAnswersQuestion: hlen wrong %d", hlen
);
423 NSEC3Parse(nsec3RR
, mDNSNULL
, &nxtLength
, &nxtName
, mDNSNULL
, mDNSNULL
);
425 if (hlen
!= nxtLength
)
427 LogMsg("AnonInfoAnswersQuestion: ERROR!! hlen %d not same as nxtLength %d", hlen
, nxtLength
);
431 for (i
= 0; i
< nxtLength
; i
++)
433 if (nxtName
[i
] != hashName
[i
])
435 debugf("AnonInfoAnswersQuestion: mismatch output %x, digest %x, i %d", nxtName
[i
+1], hashName
[i
], i
);
439 LogInfo("AnonInfoAnswersQuestion: ResourceRecord %s matched question %##s (%s)", RRDisplayString(&mDNSStorage
, nsec3RR
), q
->qname
.c
, DNSTypeName(q
->qtype
));
443 // Find a matching NSEC3 record for the name. We parse the questions and the records in the packet in order.
444 // Similarly we also parse the NSEC3 records in order and this mapping to the questions and records
446 mDNSlocal CacheRecord
*FindMatchingNSEC3ForName(mDNS
*const m
, CacheRecord
**nsec3
, const domainname
*name
)
449 CacheRecord
**prev
= nsec3
;
453 for (cr
= *nsec3
; cr
; cr
= cr
->next
)
455 if (SameDomainName(cr
->resrec
.name
, name
))
457 debugf("FindMatchingNSEC3ForName: NSEC3 record %s matched %##s", CRDisplayString(m
, cr
), name
->c
);
467 mDNSexport
void InitializeAnonInfoForQuestion(mDNS
*const m
, CacheRecord
**McastNSEC3Records
, DNSQuestion
*q
)
469 CacheRecord
*nsec3CR
;
471 if (q
->qtype
!= kDNSType_PTR
)
474 nsec3CR
= FindMatchingNSEC3ForName(m
, McastNSEC3Records
, &q
->qname
);
477 q
->AnonInfo
= AllocateAnonInfo(mDNSNULL
, mDNSNULL
, 0, &nsec3CR
->resrec
);
480 debugf("InitializeAnonInfoForQuestion: Found a matching NSEC3 record %s, for %##s (%s)",
481 RRDisplayString(m
, q
->AnonInfo
->nsec3RR
), q
->qname
.c
, DNSTypeName(q
->qtype
));
483 ReleaseCacheRecord(m
, nsec3CR
);
487 mDNSexport
void InitializeAnonInfoForCR(mDNS
*const m
, CacheRecord
**McastNSEC3Records
, CacheRecord
*cr
)
489 CacheRecord
*nsec3CR
;
491 if (!(*McastNSEC3Records
))
494 // If already initialized or not a PTR type, we don't have to do anything
495 if (cr
->resrec
.AnonInfo
|| cr
->resrec
.rrtype
!= kDNSType_PTR
)
498 nsec3CR
= FindMatchingNSEC3ForName(m
, McastNSEC3Records
, cr
->resrec
.name
);
501 cr
->resrec
.AnonInfo
= AllocateAnonInfo(mDNSNULL
, mDNSNULL
, 0, &nsec3CR
->resrec
);
502 if (cr
->resrec
.AnonInfo
)
504 debugf("InitializeAnonInfoForCR: Found a matching NSEC3 record %s, for %##s (%s)",
505 RRDisplayString(m
, cr
->resrec
.AnonInfo
->nsec3RR
), cr
->resrec
.name
->c
,
506 DNSTypeName(cr
->resrec
.rrtype
));
508 ReleaseCacheRecord(m
, nsec3CR
);
512 mDNSexport mDNSBool
IdenticalAnonInfo(AnonymousInfo
*a1
, AnonymousInfo
*a2
)
514 // if a1 is NULL and a2 is not NULL AND vice-versa
515 // return false as there is a change.
516 if ((a1
!= mDNSNULL
) != (a2
!= mDNSNULL
))
519 // Both could be NULL or non-NULL
522 // The caller already verified that the owner name is the same.
523 // Check whether the RData is same.
524 if (!IdenticalSameNameRecord(a1
->nsec3RR
, a2
->nsec3RR
))
526 debugf("IdenticalAnonInfo: nsec3RR mismatch");
533 mDNSexport
void CopyAnonInfoForCR(mDNS
*const m
, CacheRecord
*crto
, CacheRecord
*crfrom
)
535 AnonymousInfo
*aifrom
= crfrom
->resrec
.AnonInfo
;
536 AnonymousInfo
*aito
= crto
->resrec
.AnonInfo
;
545 crto
->resrec
.AnonInfo
= aifrom
;
547 crfrom
->resrec
.AnonInfo
= mDNSNULL
;
551 FreeAnonInfo(aifrom
);
552 crfrom
->resrec
.AnonInfo
= mDNSNULL
;
556 #else // !ANONYMOUS_DISABLED
558 mDNSexport
void ReInitAnonInfo(AnonymousInfo
**si
, const domainname
*name
)
564 mDNSexport AnonymousInfo
* AllocateAnonInfo(const domainname
*service
, const mDNSu8
*AnonData
, int len
, const ResourceRecord
*rr
)
574 mDNSexport
void FreeAnonInfo(AnonymousInfo
*ai
)
579 mDNSexport
void SetAnonData(DNSQuestion
*q
, ResourceRecord
*rr
, mDNSBool ForQuestion
)
586 mDNSexport
int AnonInfoAnswersQuestion(const ResourceRecord
*const rr
, const DNSQuestion
*const q
)
594 mDNSexport
void InitializeAnonInfoForQuestion(mDNS
*const m
, CacheRecord
**McastNSEC3Records
, DNSQuestion
*q
)
597 (void)McastNSEC3Records
;
601 mDNSexport
void InitializeAnonInfoForCR(mDNS
*const m
, CacheRecord
**McastNSEC3Records
, CacheRecord
*cr
)
604 (void)McastNSEC3Records
;
608 mDNSexport
void CopyAnonInfoForCR(mDNS
*const m
, CacheRecord
*crto
, CacheRecord
*crfrom
)
615 mDNSexport mDNSBool
IdenticalAnonInfo(AnonymousInfo
*a1
, AnonymousInfo
*a2
)
623 #endif // !ANONYMOUS_DISABLED