]>
Commit | Line | Data |
---|---|---|
83fb1e36 A |
1 | /* -*- Mode: C; tab-width: 4 -*- |
2 | * | |
3 | * Copyright (c) 2011 Apple Computer, Inc. All rights reserved. | |
4 | * | |
5 | * Licensed under the Apache License, Version 2.0 (the "License"); | |
6 | * you may not use this file except in compliance with the License. | |
7 | * You may obtain a copy of the License at | |
8 | * | |
9 | * http://www.apache.org/licenses/LICENSE-2.0 | |
10 | * | |
11 | * Unless required by applicable law or agreed to in writing, software | |
12 | * distributed under the License is distributed on an "AS IS" BASIS, | |
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
14 | * See the License for the specific language governing permissions and | |
15 | * limitations under the License. | |
16 | */ | |
17 | #include "mDNSEmbeddedAPI.h" | |
51601d48 | 18 | #include "DNSSECSupport.h" |
83fb1e36 A |
19 | #include "DNSCommon.h" |
20 | #include "dnssec.h" | |
21 | #include "CryptoAlg.h" | |
22 | #include "nsec.h" | |
51601d48 A |
23 | #include "nsec3.h" |
24 | ||
25 | // Define DNSSEC_DISABLED to remove all the DNSSEC functionality | |
26 | // and use the stub functions implemented later in this file. | |
27 | ||
28 | #ifndef DNSSEC_DISABLED | |
83fb1e36 A |
29 | |
30 | //#define DNSSEC_DEBUG | |
31 | ||
32 | #ifdef DNSSEC_DEBUG | |
33 | #define debugdnssec LogMsg | |
34 | #else | |
35 | #define debugdnssec debug_noop | |
36 | #endif | |
37 | // | |
38 | // Implementation Notes | |
39 | // | |
40 | // The entry point to DNSSEC Verification is VerifySignature. This function is called from the "core" when | |
41 | // the answer delivered to the application needs DNSSEC validation. If a question needs DNSSEC | |
42 | // validation, "ValidationRequired" would be set. As we need to issue more queries to validate the | |
43 | // original question, we create another question as part of the verification process (question is part of | |
44 | // DNSSECVerifier). This question sets "ValidatingResponse" to distinguish itself from the original | |
45 | // question. Without this, it will be a duplicate and never sent out. The "core" almost treats both the | |
46 | // types identically (like adding EDNS0 option with DO bit etc.) except for a few differences. When RRSIGs | |
47 | // are added to the cache, "ValidatingResponse" question gets called back as long as the typeCovered matches | |
48 | // the question's qtype. See the comment in DNSSECRecordAnswersQuestion for the details. The other big | |
49 | // difference is that "ValidationRequired" question kicks off the verification process by calling into | |
50 | // "VerifySignature" whereas ValidationResponse don't do that as it gets callback for its questions. | |
51 | // | |
52 | // VerifySignature does not retain the original question that started the verification process. It just | |
53 | // remembers the name and the type. It takes a snapshot of the cache at that instance which will be | |
54 | // verified using DNSSEC. If the cache changes subsequently e.g., network change etc., it will be detected | |
55 | // when the validation is completed. If there is a change, it will be revalidated. | |
56 | // | |
57 | // The verification flow looks like this: | |
58 | // | |
59 | // VerifySignature -> StartDNSSECVerification - GetAllRRSetsForVerification -> FinishDNSSECVerification -> VerifySignature | |
60 | // | |
61 | // Verification is a recursive process. It stops when we find a trust anchor or if we have recursed too deep. | |
62 | // | |
63 | // If the original question resulted in NODATA/NXDOMAIN error, there should have been NSECs as part of the response. | |
64 | // These nsecs are cached along with the negative cache record. These are validated using ValidateWithNSECS called | |
65 | // from Verifysignature. | |
66 | // | |
67 | // The flow in this case looks like this: | |
68 | // | |
69 | // VerifySignature -> ValidateWithNSECS -> {NoDataProof, NameErrorProof} -> VerifyNSECS -> StartDNSSECVerification | |
70 | // | |
71 | // Once the DNSSEC verification is started, it is similar to the previous flow described above. When the verification | |
72 | // is done, DNSSECPositiveValidationCB or DNSSECNegativeValidationCB will be called which will then deliver the | |
73 | // validation results to the original question that started the validation. | |
74 | // | |
51601d48 A |
75 | // Insecure proofs are done when the verification ends up bogus. The flow would look like this |
76 | // | |
77 | // VerifySignature -> StartDNSSECVerification - GetAllRRSetsForVerification -> FinishDNSSECVerification -> DNSSECValidationCB | |
78 | // {DNSSECPositiveValidationCB, DNSSECNegativeValidationCB} -> ProveInsecure -> VerifySignaure -> | |
79 | // | |
80 | // ProveInsecure finds the break in trust in a top-down fashion. | |
81 | // | |
83fb1e36 A |
82 | // Forward declaration |
83 | mDNSlocal void VerifySigCallback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord); | |
84 | mDNSlocal mStatus TrustedKey(mDNS *const m, DNSSECVerifier *dv); | |
85 | mDNSlocal mDNSBool TrustedKeyPresent(mDNS *const m, DNSSECVerifier *dv); | |
86 | mDNSlocal mStatus ValidateDS(DNSSECVerifier *dv); | |
51601d48 A |
87 | mDNSlocal void DNSSECNegativeValidationCB(mDNS *const m, DNSSECVerifier *dv, CacheGroup *cg, ResourceRecord *answer, DNSSECStatus status); |
88 | mDNSlocal RRVerifier* CopyRRVerifier(RRVerifier *from); | |
83fb1e36 A |
89 | |
90 | // Currently we use this to convert a RRVerifier to resource record so that we can | |
91 | // use the standard DNS utility functions | |
92 | LargeCacheRecord largerec; | |
93 | ||
94 | // Verification is a recursive process. We arbitrarily limit to 10 just to be cautious which should be | |
95 | // removed in the future. | |
96 | #define MAX_RECURSE_COUNT 10 | |
97 | ||
51601d48 A |
98 | // TTL (in seconds) when the DNSSEC status is Bogus |
99 | #define RR_BOGUS_TTL 60 | |
100 | ||
83fb1e36 A |
101 | // RFC 4034 Appendix B: Get the keyid of a DNS KEY. It is not transmitted |
102 | // explicitly on the wire. | |
103 | // | |
104 | // Note: This just helps narrow down the list of keys to look at. It is possible | |
105 | // for two DNS keys to have the same ID i.e., key ID is not a unqiue tag | |
106 | // | |
107 | // 1st argument - the RDATA part of the DNSKEY RR | |
108 | // 2nd argument - the RDLENGTH | |
109 | // | |
110 | mDNSlocal mDNSu32 keytag(mDNSu8 *key, mDNSu32 keysize) | |
111 | { | |
112 | unsigned long ac; | |
113 | unsigned int i; | |
114 | ||
115 | // DST_ALG_RSAMD5 will be rejected automatically as the keytag | |
116 | // is calculated wrongly | |
117 | ||
118 | for (ac = 0, i = 0; i < keysize; ++i) | |
119 | ac += (i & 1) ? key[i] : key[i] << 8; | |
120 | ac += (ac >> 16) & 0xFFFF; | |
121 | return ac & 0xFFFF; | |
122 | } | |
123 | ||
51601d48 | 124 | mDNSexport int DNSMemCmp(const mDNSu8 *const m1, const mDNSu8 *const m2, int len) |
83fb1e36 A |
125 | { |
126 | int res; | |
127 | ||
128 | res = mDNSPlatformMemCmp(m1, m2, len); | |
129 | if (res != 0) | |
130 | return (res < 0 ? -1 : 1); | |
131 | return 0; | |
132 | } | |
133 | ||
51601d48 A |
134 | // RFC 4034: |
135 | // | |
136 | // Section 6.1: | |
137 | // | |
138 | // For the purposes of DNS security, owner names are ordered by treating | |
139 | // individual labels as unsigned left-justified octet strings. The | |
140 | // absence of a octet sorts before a zero value octet, and uppercase | |
141 | // US-ASCII letters are treated as if they were lowercase US-ASCII | |
142 | // letters. | |
143 | // | |
144 | // To compute the canonical ordering of a set of DNS names, start by | |
145 | // sorting the names according to their most significant (rightmost) | |
146 | // labels. For names in which the most significant label is identical, | |
147 | // continue sorting according to their next most significant label, and | |
148 | // so forth. | |
149 | // | |
150 | // Returns 0 if the names are same | |
151 | // Returns -1 if d1 < d2 | |
152 | // Returns 1 if d1 > d2 | |
153 | // | |
154 | // subdomain is set if there is at least one label match (starting from the end) | |
155 | // and d1 has more labels than d2 e.g., a.b.com is a subdomain of b.com | |
156 | // | |
157 | mDNSexport int DNSSECCanonicalOrder(const domainname *const d1, const domainname *const d2, int *subdomain) | |
83fb1e36 | 158 | { |
51601d48 A |
159 | int count, c1, c2; |
160 | int i, skip1, skip2; | |
161 | ||
162 | c1 = CountLabels(d1); | |
163 | skip1 = c1 - 1; | |
164 | c2 = CountLabels(d2); | |
165 | skip2 = c2 - 1; | |
83fb1e36 | 166 | |
51601d48 A |
167 | if (subdomain) *subdomain = 0; |
168 | ||
169 | // Compare as many labels as possible starting from the rightmost | |
170 | count = c1 < c2 ? c1 : c2; | |
171 | for (i = count; i > 0; i--) | |
83fb1e36 | 172 | { |
51601d48 A |
173 | mDNSu8 *a, *b; |
174 | int j, len, lena, lenb; | |
175 | ||
176 | a = (mDNSu8 *)SkipLeadingLabels(d1, skip1); | |
177 | b = (mDNSu8 *)SkipLeadingLabels(d2, skip2); | |
178 | lena = *a; | |
179 | lenb = *b; | |
180 | // Compare label by label. Note that "z" > "yak" because z > y, but z < za | |
181 | // (lena - lenb check below) because 'za' has two characters. Hence compare the | |
182 | // letters first and then compare the length of the label at the end. | |
183 | len = lena < lenb ? lena : lenb; | |
184 | a++; b++; | |
185 | for (j = 0; j < len; j++) | |
83fb1e36 A |
186 | { |
187 | mDNSu8 ac = *a++; | |
51601d48 | 188 | mDNSu8 bc = *b++; |
83fb1e36 | 189 | if (mDNSIsUpperCase(ac)) ac += 'a' - 'A'; |
51601d48 A |
190 | if (mDNSIsUpperCase(bc)) bc += 'a' - 'A'; |
191 | if (ac != bc) | |
192 | { | |
193 | verbosedebugf("DNSSECCanonicalOrder: returning ac %c, bc %c", ac, bc); | |
194 | return ((ac < bc) ? -1 : 1); | |
195 | } | |
196 | } | |
197 | if ((lena - lenb) != 0) | |
198 | { | |
199 | verbosedebugf("DNSSECCanonicalOrder: returning lena %d lenb %d", lena, lenb); | |
200 | return ((lena < lenb) ? -1 : 1); | |
83fb1e36 | 201 | } |
51601d48 A |
202 | |
203 | // Continue with the next label | |
204 | skip1--; | |
205 | skip2--; | |
83fb1e36 | 206 | } |
51601d48 A |
207 | // We have compared label by label. Both of them are same if we are here. |
208 | // | |
209 | // Two possibilities. | |
210 | // | |
211 | // 1) Both names have same number of labels. In that case, return zero. | |
212 | // 2) The number of labels is not same. As zero label sorts before, names | |
213 | // with more number of labels is greater. | |
83fb1e36 | 214 | |
51601d48 A |
215 | // a.b.com is a subdomain of b.com |
216 | if ((c1 > c2) && subdomain) | |
217 | *subdomain = 1; | |
218 | ||
219 | verbosedebugf("DNSSECCanonicalOrder: returning c1 %d c2 %d\n", c1, c2); | |
220 | if (c1 != c2) | |
221 | return ((c1 < c2) ? -1 : 1); | |
222 | else | |
223 | return 0; | |
83fb1e36 A |
224 | } |
225 | ||
226 | // Initialize the question enough so that it can be answered from the cache using SameNameRecordAnswersQuestion or | |
227 | // ResourceRecordAnswersQuestion. | |
228 | mDNSexport void InitializeQuestion(mDNS *const m, DNSQuestion *question, mDNSInterfaceID InterfaceID, const domainname *qname, | |
229 | mDNSu16 qtype, mDNSQuestionCallback *callback, void *context) | |
230 | { | |
51601d48 | 231 | debugf("InitializeQuestion: Called for %##s (%s)", qname->c, DNSTypeName(qtype)); |
83fb1e36 A |
232 | |
233 | if (question->ThisQInterval != -1) mDNS_StopQuery(m, question); | |
234 | ||
235 | mDNS_SetupQuestion(question, InterfaceID, qname, qtype, callback, context); | |
236 | question->qnamehash = DomainNameHashValue(qname); | |
237 | question->ValidatingResponse = mDNStrue; | |
238 | ||
51601d48 A |
239 | // Need to hold the lock, as GetServerForQuestion (its callers) references m->timenow. |
240 | mDNS_Lock(m); | |
83fb1e36 A |
241 | // We need to set the DNS server appropriately to match the question against the cache record. |
242 | // Though not all callers of this function need it, we always do it to keep it simple. | |
243 | SetValidDNSServers(m, question); | |
244 | question->qDNSServer = GetServerForQuestion(m, question); | |
51601d48 | 245 | mDNS_Unlock(m); |
83fb1e36 A |
246 | |
247 | // Make it look like unicast | |
248 | question->TargetQID = onesID; | |
249 | question->TimeoutQuestion = 1; | |
250 | question->ReturnIntermed = 1; | |
251 | // SetupQuestion sets LongLived if qtype == PTR | |
252 | question->LongLived = 0; | |
253 | } | |
254 | ||
255 | mDNSexport DNSSECVerifier *AllocateDNSSECVerifier(mDNS *const m, const domainname *name, mDNSu16 rrtype, mDNSInterfaceID InterfaceID, | |
51601d48 | 256 | mDNSu8 ValidationRequired, DNSSECVerifierCallback dvcallback, mDNSQuestionCallback qcallback) |
83fb1e36 A |
257 | { |
258 | DNSSECVerifier *dv; | |
259 | ||
260 | dv = (DNSSECVerifier *)mDNSPlatformMemAllocate(sizeof(DNSSECVerifier)); | |
261 | if (!dv) { LogMsg("AllocateDNSSECVerifier: ERROR!! memory alloc failed"); return mDNSNULL; } | |
262 | mDNSPlatformMemZero(dv, sizeof(*dv)); | |
263 | ||
51601d48 A |
264 | LogDNSSEC("AllocateDNSSECVerifier called %p", dv); |
265 | ||
83fb1e36 A |
266 | // Remember the question's name and type so that when we are done processing all |
267 | // the verifications, we can trace the original question back | |
268 | AssignDomainName(&dv->origName, name); | |
269 | dv->origType = rrtype; | |
270 | dv->InterfaceID = InterfaceID; | |
271 | dv->DVCallback = dvcallback; | |
272 | dv->q.ThisQInterval = -1; | |
51601d48 A |
273 | ResetAuthChain(dv); |
274 | // These two are used for Insecure proof if we end up doing it. | |
275 | // -Value of ValidationRequired so that we know whether this is a secure or insecure validation | |
276 | // -InsecureProofDone tells us whether the proof has been done or not | |
277 | dv->ValidationRequired = ValidationRequired; | |
278 | dv->InsecureProofDone = 0; | |
279 | dv->NumPackets = 0; | |
280 | mDNS_Lock(m); | |
281 | dv->StartTime = m->timenow; | |
282 | mDNS_Unlock(m); | |
83fb1e36 A |
283 | // The verifier's question has to be initialized as some of the callers assume it |
284 | InitializeQuestion(m, &dv->q, InterfaceID, name, rrtype, qcallback, dv); | |
285 | return dv; | |
286 | } | |
287 | ||
51601d48 A |
288 | mDNSlocal AuthChain *AuthChainCopy(AuthChain *ae) |
289 | { | |
290 | RRVerifier *rvfrom, **rvto; | |
291 | AuthChain **prev = mDNSNULL; | |
292 | AuthChain *retac = mDNSNULL; | |
293 | AuthChain *ac; | |
294 | ||
295 | ||
296 | while (ae) | |
297 | { | |
298 | ac = mDNSPlatformMemAllocate(sizeof(AuthChain)); | |
299 | if (!ac) | |
300 | { | |
301 | LogMsg("AuthChainCopy: AuthChain alloc failure"); | |
302 | return mDNSfalse; | |
303 | } | |
304 | ||
305 | ac->next = mDNSNULL; | |
306 | ||
307 | if (!retac) | |
308 | retac = ac; | |
309 | ||
310 | rvfrom = ae->rrset; | |
311 | rvto = &ac->rrset; | |
312 | while (rvfrom) | |
313 | { | |
314 | *rvto = CopyRRVerifier(rvfrom); | |
315 | rvfrom = rvfrom->next; | |
316 | rvto = &((*rvto)->next); | |
317 | } | |
318 | ||
319 | rvfrom = ae->rrsig; | |
320 | rvto = &ac->rrsig; | |
321 | while (rvfrom) | |
322 | { | |
323 | *rvto = CopyRRVerifier(rvfrom); | |
324 | rvfrom = rvfrom->next; | |
325 | rvto = &((*rvto)->next); | |
326 | } | |
327 | ||
328 | rvfrom = ae->key; | |
329 | rvto = &ac->key; | |
330 | while (rvfrom) | |
331 | { | |
332 | *rvto = CopyRRVerifier(rvfrom); | |
333 | rvfrom = rvfrom->next; | |
334 | rvto = &((*rvto)->next); | |
335 | } | |
336 | ||
337 | if (prev) | |
338 | { | |
339 | *prev = ac; | |
340 | } | |
341 | prev = &(ac->next); | |
342 | ae = ae->next; | |
343 | } | |
344 | return retac; | |
345 | } | |
346 | ||
347 | mDNSlocal void FreeDNSSECAuthChainInfo(AuthChain *ac) | |
83fb1e36 A |
348 | { |
349 | RRVerifier *rrset; | |
350 | RRVerifier *next; | |
51601d48 | 351 | AuthChain *acnext; |
83fb1e36 | 352 | |
51601d48 | 353 | LogDNSSEC("FreeDNSSECAuthChainInfo: called"); |
83fb1e36 A |
354 | |
355 | while (ac) | |
356 | { | |
357 | acnext = ac->next; | |
358 | rrset = ac->rrset; | |
359 | while (rrset) | |
360 | { | |
361 | next = rrset->next; | |
362 | mDNSPlatformMemFree(rrset); | |
363 | rrset = next; | |
364 | } | |
365 | ac->rrset = mDNSNULL; | |
366 | ||
367 | rrset = ac->rrsig; | |
368 | while (rrset) | |
369 | { | |
370 | next = rrset->next; | |
371 | mDNSPlatformMemFree(rrset); | |
372 | rrset = next; | |
373 | } | |
374 | ac->rrsig = mDNSNULL; | |
375 | ||
376 | rrset = ac->key; | |
377 | while (rrset) | |
378 | { | |
379 | next = rrset->next; | |
380 | mDNSPlatformMemFree(rrset); | |
381 | rrset = next; | |
382 | } | |
383 | ac->key = mDNSNULL; | |
384 | ||
385 | mDNSPlatformMemFree(ac); | |
386 | ac = acnext; | |
387 | } | |
51601d48 A |
388 | } |
389 | ||
390 | mDNSlocal void FreeDNSSECAuthChain(DNSSECVerifier *dv) | |
391 | { | |
392 | if (dv->ac) | |
393 | { | |
394 | FreeDNSSECAuthChainInfo(dv->ac); | |
395 | // if someone reuses the "dv", it will be initialized properly | |
396 | ResetAuthChain(dv); | |
397 | } | |
398 | if (dv->saveac) | |
399 | { | |
400 | FreeDNSSECAuthChainInfo(dv->saveac); | |
401 | dv->saveac = mDNSNULL; | |
402 | } | |
403 | } | |
404 | ||
405 | mDNSlocal void FreeAuthChain(mDNS *const m, void *context) | |
406 | { | |
407 | AuthChain *ac = (AuthChain *)context; | |
408 | (void) m; // unused | |
409 | ||
410 | FreeDNSSECAuthChainInfo(ac); | |
83fb1e36 A |
411 | } |
412 | ||
413 | mDNSlocal void FreeDNSSECVerifierRRSets(DNSSECVerifier *dv) | |
414 | { | |
415 | RRVerifier *rrset; | |
416 | RRVerifier *next; | |
417 | ||
418 | //debugdnssec("FreeDNSSECVerifierRRSets called %p", dv); | |
419 | rrset = dv->rrset; | |
420 | while (rrset) | |
421 | { | |
422 | next = rrset->next; | |
423 | mDNSPlatformMemFree(rrset); | |
424 | rrset = next; | |
425 | } | |
426 | dv->rrset = mDNSNULL; | |
427 | ||
428 | rrset = dv->rrsig; | |
429 | while (rrset) | |
430 | { | |
431 | next = rrset->next; | |
432 | mDNSPlatformMemFree(rrset); | |
433 | rrset = next; | |
434 | } | |
435 | dv->rrsig = mDNSNULL; | |
436 | ||
437 | rrset = dv->key; | |
438 | while (rrset) | |
439 | { | |
440 | next = rrset->next; | |
441 | mDNSPlatformMemFree(rrset); | |
442 | rrset = next; | |
443 | } | |
444 | dv->key = mDNSNULL; | |
445 | ||
446 | rrset = dv->rrsigKey; | |
447 | while (rrset) | |
448 | { | |
449 | next = rrset->next; | |
450 | mDNSPlatformMemFree(rrset); | |
451 | rrset = next; | |
452 | } | |
453 | dv->rrsigKey = mDNSNULL; | |
454 | ||
455 | rrset = dv->ds; | |
456 | while (rrset) | |
457 | { | |
458 | next = rrset->next; | |
459 | mDNSPlatformMemFree(rrset); | |
460 | rrset = next; | |
461 | } | |
462 | dv->ds = mDNSNULL; | |
51601d48 A |
463 | rrset = dv->pendingNSEC; |
464 | while (rrset) | |
83fb1e36 | 465 | { |
51601d48 A |
466 | next = rrset->next; |
467 | mDNSPlatformMemFree(rrset); | |
468 | rrset = next; | |
83fb1e36 | 469 | } |
51601d48 | 470 | dv->pendingNSEC = mDNSNULL; |
83fb1e36 A |
471 | } |
472 | ||
473 | mDNSexport void FreeDNSSECVerifier(mDNS *const m, DNSSECVerifier *dv) | |
474 | { | |
475 | LogDNSSEC("FreeDNSSECVerifier called %p", dv); | |
51601d48 A |
476 | if (dv->q.ThisQInterval != -1) |
477 | mDNS_StopQuery(m, &dv->q); | |
83fb1e36 | 478 | FreeDNSSECVerifierRRSets(dv); |
51601d48 A |
479 | if (dv->ctx) |
480 | AlgDestroy(dv->ctx); | |
481 | if (dv->ac || dv->saveac) | |
482 | FreeDNSSECAuthChain(dv); | |
83fb1e36 A |
483 | if (dv->parent) |
484 | { | |
485 | LogDNSSEC("FreeDNSSECVerifier freeing parent %p", dv->parent); | |
486 | FreeDNSSECVerifier(m, dv->parent); | |
487 | } | |
488 | mDNSPlatformMemFree(dv); | |
489 | } | |
490 | ||
51601d48 A |
491 | mDNSlocal RRVerifier* CopyRRVerifier(RRVerifier *from) |
492 | { | |
493 | RRVerifier *r; | |
494 | ||
495 | r = mDNSPlatformMemAllocate(sizeof (RRVerifier) + from->rdlength); | |
496 | if (!r) | |
497 | { | |
498 | LogMsg("CopyRRVerifier: memory failure"); | |
499 | return mDNSNULL; | |
500 | } | |
501 | mDNSPlatformMemCopy(r, from, sizeof(RRVerifier)); | |
502 | r->next = mDNSNULL; | |
503 | r->rdata = (mDNSu8*) ((mDNSu8 *)r + sizeof(RRVerifier)); | |
504 | mDNSPlatformMemCopy(r->rdata, from->rdata, r->rdlength); | |
505 | return r; | |
506 | } | |
507 | ||
83fb1e36 A |
508 | mDNSexport RRVerifier* AllocateRRVerifier(const ResourceRecord *const rr, mStatus *status) |
509 | { | |
510 | RRVerifier *r; | |
511 | ||
512 | r = mDNSPlatformMemAllocate(sizeof (RRVerifier) + rr->rdlength); | |
513 | if (!r) | |
514 | { | |
515 | LogMsg("AllocateRRVerifier: memory failure"); | |
516 | *status = mStatus_NoMemoryErr; | |
517 | return mDNSNULL; | |
518 | } | |
519 | r->next = mDNSNULL; | |
520 | r->rrtype = rr->rrtype; | |
521 | r->rrclass = rr->rrclass; | |
522 | r->rroriginalttl = rr->rroriginalttl; | |
523 | r->rdlength = rr->rdlength; | |
524 | r->namehash = rr->namehash; | |
525 | r->rdatahash = rr->rdatahash; | |
526 | AssignDomainName(&r->name, rr->name); | |
527 | r->rdata = (mDNSu8*) ((mDNSu8 *)r + sizeof(RRVerifier)); | |
528 | ||
529 | // When we parsed the DNS response in GeLargeResourceRecord, for some records, we parse them into | |
530 | // host order so that the rest of the code does not have to bother with converting from network order | |
531 | // to host order. For signature verification, we need them back in network order. For DNSSEC records | |
532 | // like DNSKEY and DS, we just copy over the data both in GetLargeResourceRecord and putRData. | |
533 | ||
534 | if (!putRData(mDNSNULL, r->rdata, r->rdata + rr->rdlength, rr)) | |
535 | { | |
536 | LogMsg("AllocateRRVerifier: putRData failed"); | |
537 | *status = mStatus_BadParamErr; | |
538 | return mDNSNULL; | |
539 | } | |
540 | *status = mStatus_NoError; | |
541 | return r; | |
542 | } | |
543 | ||
544 | mDNSexport mStatus AddRRSetToVerifier(DNSSECVerifier *dv, const ResourceRecord *const rr, RRVerifier *rv, RRVerifierSet set) | |
545 | { | |
546 | RRVerifier *r; | |
547 | RRVerifier **v; | |
548 | mStatus status; | |
549 | ||
550 | if (!rv) | |
551 | { | |
552 | r = AllocateRRVerifier(rr, &status); | |
553 | if (!r) return status; | |
554 | } | |
555 | else | |
556 | r = rv; | |
557 | ||
558 | switch (set) | |
559 | { | |
560 | case RRVS_rr: | |
561 | v = &dv->rrset; | |
562 | break; | |
563 | case RRVS_rrsig: | |
564 | v = &dv->rrsig; | |
565 | break; | |
566 | case RRVS_key: | |
567 | v = &dv->key; | |
568 | break; | |
569 | case RRVS_rrsig_key: | |
570 | v = &dv->rrsigKey; | |
571 | break; | |
572 | case RRVS_ds: | |
573 | v = &dv->ds; | |
574 | break; | |
575 | default: | |
576 | LogMsg("AddRRSetToVerifier: ERROR!! default case %d", set); | |
577 | return mStatus_BadParamErr; | |
578 | } | |
579 | while (*v) | |
580 | v = &(*v)->next; | |
581 | *v = r; | |
582 | return mStatus_NoError; | |
583 | } | |
584 | ||
585 | // Validate the RRSIG. "type" tells which RRSIG that we are supposed to validate. We fetch RRSIG for | |
586 | // the rrset (type is RRVS_rrsig) and RRSIG for the key (type is RRVS_rrsig_key). | |
587 | mDNSexport void ValidateRRSIG(DNSSECVerifier *dv, RRVerifierSet type, const ResourceRecord *const rr) | |
588 | { | |
589 | RRVerifier *rv; | |
590 | mDNSu32 currentTime; | |
591 | rdataRRSig *rrsigRData = (rdataRRSig *)((mDNSu8 *)rr->rdata + sizeofRDataHeader); | |
592 | ||
593 | if (type == RRVS_rrsig) | |
594 | { | |
595 | rv = dv->rrset; | |
596 | } | |
597 | else if (type == RRVS_rrsig_key) | |
598 | { | |
599 | rv = dv->key; | |
600 | } | |
601 | else | |
602 | { | |
603 | LogMsg("ValidateRRSIG: ERROR!! type not valid %d", type); | |
604 | return; | |
605 | } | |
606 | ||
607 | // RFC 4035: | |
608 | // For each authoritative RRset in a signed zone, there MUST be at least | |
609 | // one RRSIG record that meets the following requirements: | |
610 | // | |
611 | // RRSet is defined by same name, class and type | |
612 | // | |
613 | // 1. The RRSIG RR and the RRset MUST have the same owner name and the same class. | |
614 | if (!SameDomainName(&rv->name, rr->name) || (rr->rrclass != rv->rrclass)) | |
615 | { | |
616 | debugdnssec("ValidateRRSIG: name mismatch or class mismatch"); | |
617 | return; | |
618 | } | |
619 | ||
620 | // 2. The RRSIG RR's Type Covered field MUST equal the RRset's type. | |
621 | if ((swap16(rrsigRData->typeCovered)) != rv->rrtype) | |
622 | { | |
623 | debugdnssec("ValidateRRSIG: typeCovered mismatch rrsig %d, rr type %d", swap16(rrsigRData->typeCovered), rv->rrtype); | |
624 | return; | |
625 | } | |
626 | ||
627 | // 3. The number of labels in the RRset owner name MUST be greater than or equal | |
628 | // to the value in the RRSIG RR's Labels field. | |
629 | if (rrsigRData->labels > CountLabels(&rv->name)) | |
630 | { | |
631 | debugdnssec("ValidateRRSIG: labels count problem rrsig %d, rr %d", rrsigRData->labels, CountLabels(&rv->name)); | |
632 | return; | |
633 | } | |
634 | ||
635 | // 4. The RRSIG RR's Signer's Name field MUST be the name of the zone that contains | |
636 | // the RRset. For a stub resolver, this can't be done in a secure way. Hence we | |
637 | // do it this way (discussed in dnsext mailing list) | |
638 | switch (rv->rrtype) | |
639 | { | |
640 | case kDNSType_NS: | |
641 | case kDNSType_SOA: | |
642 | case kDNSType_DNSKEY: | |
643 | //Signed by the owner | |
644 | if (!SameDomainName(&rv->name, (domainname *)&rrsigRData->signerName)) | |
645 | { | |
646 | debugdnssec("ValidateRRSIG: Signer Name does not match the record name for %s", DNSTypeName(rv->rrtype)); | |
647 | return; | |
648 | } | |
649 | break; | |
650 | case kDNSType_DS: | |
651 | // Should be signed by the parent | |
652 | if (SameDomainName(&rv->name, (domainname *)&rrsigRData->signerName)) | |
653 | { | |
654 | debugdnssec("ValidateRRSIG: Signer Name matches the record name for %s", DNSTypeName(rv->rrtype)); | |
655 | return; | |
656 | } | |
657 | // FALLTHROUGH | |
658 | default: | |
659 | { | |
660 | int c1 = CountLabels(&rv->name); | |
661 | int c2 = CountLabels((domainname *)&rrsigRData->signerName); | |
662 | if (c1 < c2) | |
663 | { | |
664 | debugdnssec("ValidateRRSIG: Signer Name not a subdomain label count %d < %d ", c1, c2); | |
665 | return; | |
666 | } | |
667 | domainname *d = (domainname *)SkipLeadingLabels(&rv->name, c1 - c2); | |
668 | if (!SameDomainName(d, (domainname *)&rrsigRData->signerName)) | |
669 | { | |
670 | debugdnssec("ValidateRRSIG: Signer Name not a subdomain"); | |
671 | return; | |
672 | } | |
673 | break; | |
674 | } | |
675 | } | |
676 | ||
677 | // 5. The validator's notion of the current time MUST be less than or equal to the | |
678 | // time listed in the RRSIG RR's Expiration field. | |
679 | // | |
680 | // 6. The validator's notion of the current time MUST be greater than or equal to the | |
681 | // time listed in the RRSIG RR's Inception field. | |
682 | currentTime = mDNSPlatformUTC(); | |
683 | ||
684 | if (DNS_SERIAL_LT(swap32(rrsigRData->sigExpireTime), currentTime)) | |
685 | { | |
686 | LogDNSSEC("ValidateRRSIG: Expired: currentTime %d, ExpireTime %d", (int)currentTime, | |
687 | swap32((int)rrsigRData->sigExpireTime)); | |
688 | return; | |
689 | } | |
690 | if (DNS_SERIAL_LT(currentTime, swap32(rrsigRData->sigInceptTime))) | |
691 | { | |
692 | LogDNSSEC("ValidateRRSIG: Future: currentTime %d, InceptTime %d", (int)currentTime, | |
693 | swap32((int)rrsigRData->sigInceptTime)); | |
694 | return; | |
695 | } | |
696 | ||
697 | if (AddRRSetToVerifier(dv, rr, mDNSNULL, type) != mStatus_NoError) | |
698 | { | |
699 | LogMsg("ValidateRRSIG: ERROR!! cannot allocate RRSet"); | |
700 | return; | |
701 | } | |
702 | } | |
703 | ||
704 | mDNSlocal mStatus CheckRRSIGForRRSet(mDNS *const m, DNSSECVerifier *dv, CacheRecord **negcr) | |
705 | { | |
706 | mDNSu32 slot; | |
707 | CacheGroup *cg; | |
708 | CacheRecord *cr; | |
709 | RRVerifier *rv; | |
51601d48 | 710 | mDNSBool expectRRSIG = mDNSfalse; |
83fb1e36 A |
711 | |
712 | *negcr = mDNSNULL; | |
713 | if (!dv->rrset) | |
714 | { | |
715 | LogMsg("CheckRRSIGForRRSet: ERROR!! rrset NULL for origName %##s (%s)", dv->origName.c, | |
716 | DNSTypeName(dv->origType)); | |
717 | return mStatus_BadParamErr; | |
718 | } | |
719 | ||
720 | rv = dv->rrset; | |
721 | slot = HashSlot(&rv->name); | |
722 | cg = CacheGroupForName(m, slot, rv->namehash, &rv->name); | |
723 | if (!cg) | |
724 | { | |
725 | debugdnssec("CheckRRSIGForRRSet: cg null"); | |
726 | return mStatus_NoSuchRecord; | |
727 | } | |
728 | ||
729 | for (cr=cg->members; cr; cr=cr->next) | |
730 | { | |
731 | debugdnssec("CheckRRSIGForRRSet: checking the validity of rrsig"); | |
51601d48 A |
732 | if (cr->resrec.rrtype != kDNSType_RRSIG) |
733 | { | |
734 | // Check to see if we should expect RRSIGs for the type that we are looking for. | |
735 | // We would expect RRSIGs, if we had previously issued the question with the | |
736 | // EDNS0/DOK bit set. | |
737 | if (cr->resrec.rrtype == dv->rrset->rrtype) | |
738 | { | |
739 | expectRRSIG = cr->CRDNSSECQuestion; | |
740 | LogDNSSEC("CheckRRSIGForRRSet: %s RRSIG for %s", (expectRRSIG ? "Expecting" : "Not Expecting"), CRDisplayString(m, cr)); | |
741 | } | |
742 | continue; | |
743 | } | |
83fb1e36 A |
744 | if (cr->resrec.RecordType == kDNSRecordTypePacketNegative) |
745 | { | |
746 | if (!(*negcr)) | |
747 | { | |
748 | LogDNSSEC("CheckRRSIGForRRSet: Negative cache record %s encountered for %##s (%s)", CRDisplayString(m, cr), | |
51601d48 | 749 | rv->name.c, DNSTypeName(rv->rrtype)); |
83fb1e36 A |
750 | *negcr = cr; |
751 | } | |
752 | else | |
753 | { | |
754 | LogMsg("CheckRRSIGForRRSet: ERROR!! Negative cache record %s already set for %##s (%s)", CRDisplayString(m, cr), | |
51601d48 | 755 | rv->name.c, DNSTypeName(rv->rrtype)); |
83fb1e36 A |
756 | } |
757 | continue; | |
758 | } | |
759 | ValidateRRSIG(dv, RRVS_rrsig, &cr->resrec); | |
760 | } | |
761 | if (*negcr && dv->rrsig) | |
762 | { | |
763 | // Encountered both RRSIG and negative CR | |
764 | LogMsg("CheckRRSIGForRRSet: ERROR!! Encountered negative cache record %s and RRSIG for %##s (%s)", | |
51601d48 | 765 | CRDisplayString(m, *negcr), rv->name.c, DNSTypeName(rv->rrtype)); |
83fb1e36 A |
766 | return mStatus_BadParamErr; |
767 | } | |
51601d48 A |
768 | // If we can't find RRSIGs, but we find a negative response then we need to validate that |
769 | // which the caller will do it. Otherwise, if we should be expecting RRSIGs to be in the | |
770 | // cache already, then return error. | |
83fb1e36 A |
771 | if (dv->rrsig || *negcr) |
772 | return mStatus_NoError; | |
51601d48 A |
773 | else if (expectRRSIG) |
774 | return mStatus_BadParamErr; | |
83fb1e36 A |
775 | else |
776 | return mStatus_NoSuchRecord; | |
777 | } | |
778 | ||
779 | mDNSlocal void CheckOneKeyForRRSIG(DNSSECVerifier *dv, const ResourceRecord *const rr) | |
780 | { | |
781 | rdataRRSig *rrsig; | |
782 | ||
783 | if (!dv->rrsig) | |
784 | { | |
785 | LogMsg("CheckOneKeyForRRSIG: ERROR!! rrsig NULL"); | |
786 | return; | |
787 | } | |
788 | rrsig = (rdataRRSig *)dv->rrsig->rdata; | |
789 | if (!SameDomainName((domainname *)&rrsig->signerName, rr->name)) | |
790 | { | |
791 | debugdnssec("CheckOneKeyForRRSIG: name mismatch"); | |
792 | return; | |
793 | } | |
794 | ||
795 | // We store all the keys including the ZSK and KSK and use them appropriately | |
796 | // later | |
797 | if (AddRRSetToVerifier(dv, rr, mDNSNULL, RRVS_key) != mStatus_NoError) | |
798 | { | |
799 | LogMsg("CheckOneKeyForRRSIG: ERROR!! cannot allocate RRSet"); | |
800 | return; | |
801 | } | |
802 | } | |
803 | ||
804 | mDNSlocal mStatus CheckKeyForRRSIG(mDNS *const m, DNSSECVerifier *dv, CacheRecord **negcr) | |
805 | { | |
806 | mDNSu32 slot; | |
807 | mDNSu32 namehash; | |
808 | CacheGroup *cg; | |
809 | CacheRecord *cr; | |
810 | rdataRRSig *rrsig; | |
811 | domainname *name; | |
812 | ||
813 | *negcr = mDNSNULL; | |
814 | if (!dv->rrsig) | |
815 | { | |
816 | LogMsg("CheckKeyForRRSIG: ERROR!! rrsig NULL"); | |
817 | return mStatus_BadParamErr; | |
818 | } | |
819 | ||
820 | // Signer name should be the same on all rrsig ?? | |
821 | rrsig = (rdataRRSig *)dv->rrsig->rdata; | |
822 | name = (domainname *)&rrsig->signerName; | |
823 | ||
824 | slot = HashSlot(name); | |
825 | namehash = DomainNameHashValue(name); | |
826 | cg = CacheGroupForName(m, slot, namehash, name); | |
827 | if (!cg) | |
828 | { | |
829 | debugdnssec("CheckKeyForRRSIG: cg null for %##s", name->c); | |
830 | return mStatus_NoSuchRecord; | |
831 | } | |
832 | ||
833 | for (cr=cg->members; cr; cr=cr->next) | |
834 | { | |
835 | if (cr->resrec.rrtype != kDNSType_DNSKEY) continue; | |
836 | if (cr->resrec.RecordType == kDNSRecordTypePacketNegative) | |
837 | { | |
838 | if (!(*negcr)) | |
839 | { | |
840 | LogDNSSEC("CheckKeyForRRSIG: Negative cache record %s encountered for %##s (DNSKEY)", CRDisplayString(m, cr), | |
841 | name->c); | |
842 | *negcr = cr; | |
843 | } | |
844 | else | |
845 | { | |
846 | LogMsg("CheckKeyForRRSIG: ERROR!! Negative cache record %s already set for %##s (DNSKEY)", CRDisplayString(m, cr), | |
847 | name->c); | |
848 | } | |
849 | continue; | |
850 | } | |
851 | debugdnssec("CheckKeyForRRSIG: checking the validity of key record"); | |
852 | CheckOneKeyForRRSIG(dv, &cr->resrec); | |
853 | } | |
854 | if (*negcr && dv->key) | |
855 | { | |
856 | // Encountered both RRSIG and negative CR | |
857 | LogMsg("CheckKeyForRRSIG: ERROR!! Encountered negative cache record %s and DNSKEY for %##s", | |
858 | CRDisplayString(m, *negcr), name->c); | |
859 | return mStatus_BadParamErr; | |
860 | } | |
861 | if (dv->key || *negcr) | |
862 | return mStatus_NoError; | |
863 | else | |
864 | return mStatus_NoSuchRecord; | |
865 | } | |
866 | ||
867 | mDNSlocal void CheckOneRRSIGForKey(DNSSECVerifier *dv, const ResourceRecord *const rr) | |
868 | { | |
869 | rdataRRSig *rrsig; | |
870 | if (!dv->rrsig) | |
871 | { | |
872 | LogMsg("CheckOneRRSIGForKey: ERROR!! rrsig NULL"); | |
873 | return; | |
874 | } | |
875 | rrsig = (rdataRRSig *)dv->rrsig->rdata; | |
876 | if (!SameDomainName((domainname *)&rrsig->signerName, rr->name)) | |
877 | { | |
878 | debugdnssec("CheckOneRRSIGForKey: name mismatch"); | |
879 | return; | |
880 | } | |
881 | ValidateRRSIG(dv, RRVS_rrsig_key, rr); | |
882 | } | |
883 | ||
884 | mDNSlocal mStatus CheckRRSIGForKey(mDNS *const m, DNSSECVerifier *dv, CacheRecord **negcr) | |
885 | { | |
886 | mDNSu32 slot; | |
887 | mDNSu32 namehash; | |
888 | CacheGroup *cg; | |
889 | CacheRecord *cr; | |
890 | rdataRRSig *rrsig; | |
891 | domainname *name; | |
51601d48 | 892 | mDNSBool expectRRSIG = mDNSfalse; |
83fb1e36 A |
893 | |
894 | *negcr = mDNSNULL; | |
895 | if (!dv->rrsig) | |
896 | { | |
897 | LogMsg("CheckRRSIGForKey: ERROR!! rrsig NULL"); | |
898 | return mStatus_BadParamErr; | |
899 | } | |
900 | if (!dv->key) | |
901 | { | |
902 | LogMsg("CheckRRSIGForKey: ERROR!! key NULL"); | |
903 | return mStatus_BadParamErr; | |
904 | } | |
905 | rrsig = (rdataRRSig *)dv->rrsig->rdata; | |
906 | name = (domainname *)&rrsig->signerName; | |
907 | ||
908 | slot = HashSlot(name); | |
909 | namehash = DomainNameHashValue(name); | |
910 | cg = CacheGroupForName(m, slot, namehash, name); | |
911 | if (!cg) | |
912 | { | |
913 | debugdnssec("CheckRRSIGForKey: cg null %##s", name->c); | |
914 | return mStatus_NoSuchRecord; | |
915 | } | |
916 | for (cr=cg->members; cr; cr=cr->next) | |
917 | { | |
51601d48 A |
918 | if (cr->resrec.rrtype != kDNSType_RRSIG) |
919 | { | |
920 | // Check to see if we should expect RRSIGs for the DNSKEY record that we are | |
921 | // looking for. We would expect RRSIGs, if we had previously issued the question | |
922 | // with the EDNS0/DOK bit set. | |
923 | if (cr->resrec.rrtype == kDNSType_DNSKEY) | |
924 | { | |
925 | expectRRSIG = cr->CRDNSSECQuestion; | |
926 | LogDNSSEC("CheckRRSIGForKey: %s RRSIG for %s", (expectRRSIG ? "Expecting" : "Not Expecting"), CRDisplayString(m, cr)); | |
927 | } | |
928 | continue; | |
929 | } | |
83fb1e36 A |
930 | if (cr->resrec.RecordType == kDNSRecordTypePacketNegative) |
931 | { | |
932 | if (!(*negcr)) | |
933 | { | |
934 | LogDNSSEC("CheckRRSIGForKey: Negative cache record %s encountered for %##s (RRSIG)", CRDisplayString(m, cr), | |
935 | name->c); | |
936 | *negcr = cr; | |
937 | } | |
938 | else | |
939 | { | |
940 | LogMsg("CheckRRSIGForKey: ERROR!! Negative cache record %s already set for %##s (RRSIG)", CRDisplayString(m, cr), | |
941 | name->c); | |
942 | } | |
943 | continue; | |
944 | } | |
945 | debugdnssec("CheckRRSIGForKey: checking the validity of rrsig"); | |
946 | CheckOneRRSIGForKey(dv, &cr->resrec); | |
947 | } | |
948 | if (*negcr && dv->rrsigKey) | |
949 | { | |
950 | // Encountered both RRSIG and negative CR | |
951 | LogMsg("CheckRRSIGForKey: ERROR!! Encountered negative cache record %s and DNSKEY for %##s", | |
952 | CRDisplayString(m, *negcr), name->c); | |
953 | return mStatus_BadParamErr; | |
954 | } | |
51601d48 A |
955 | // If we can't find RRSIGs, but we find a negative response then we need to validate that |
956 | // which the caller will do it. Finally, make sure that we are not expecting RRSIGS. | |
83fb1e36 A |
957 | if (dv->rrsigKey || *negcr) |
958 | return mStatus_NoError; | |
51601d48 A |
959 | else if (expectRRSIG) |
960 | return mStatus_BadParamErr; | |
83fb1e36 A |
961 | else |
962 | return mStatus_NoSuchRecord; | |
963 | } | |
964 | ||
965 | mDNSlocal void CheckOneDSForKey(DNSSECVerifier *dv, const ResourceRecord *const rr) | |
966 | { | |
967 | mDNSu16 tag; | |
968 | rdataDS *DS; | |
969 | RRVerifier *keyv; | |
970 | rdataDNSKey *key; | |
971 | rdataRRSig *rrsig; | |
972 | ||
973 | if (!dv->rrsig) | |
974 | { | |
975 | LogMsg("CheckOneDSForKey: ERROR!! rrsig NULL"); | |
976 | return; | |
977 | } | |
978 | rrsig = (rdataRRSig *)dv->rrsig->rdata; | |
979 | DS = (rdataDS *)((mDNSu8 *)rr->rdata + sizeofRDataHeader); | |
980 | ||
981 | if (!SameDomainName((domainname *)&rrsig->signerName, rr->name)) | |
982 | { | |
983 | debugdnssec("CheckOneDSForKey: name mismatch"); | |
984 | return; | |
985 | } | |
986 | for (keyv = dv->key; keyv; keyv = keyv->next) | |
987 | { | |
988 | key = (rdataDNSKey *)keyv->rdata; | |
989 | tag = (mDNSu16)keytag((mDNSu8 *)key, keyv->rdlength); | |
990 | if (tag != swap16(DS->keyTag)) | |
991 | { | |
992 | debugdnssec("CheckOneDSForKey: keyTag mismatch keyTag %d, DStag %d", tag, swap16(DS->keyTag)); | |
993 | continue; | |
994 | } | |
995 | if (key->alg != DS->alg) | |
996 | { | |
997 | debugdnssec("CheckOneDSForKey: alg mismatch key alg%d, DS alg %d", key->alg, swap16(DS->alg)); | |
998 | continue; | |
999 | } | |
1000 | if (AddRRSetToVerifier(dv, rr, mDNSNULL, RRVS_ds) != mStatus_NoError) | |
1001 | { | |
1002 | debugdnssec("CheckOneDSForKey: cannot allocate RRSet"); | |
1003 | } | |
1004 | } | |
1005 | } | |
1006 | ||
1007 | mDNSlocal mStatus CheckDSForKey(mDNS *const m, DNSSECVerifier *dv, CacheRecord **negcr) | |
1008 | { | |
1009 | mDNSu32 slot; | |
1010 | mDNSu32 namehash; | |
1011 | CacheGroup *cg; | |
1012 | CacheRecord *cr; | |
1013 | rdataRRSig *rrsig; | |
1014 | domainname *name; | |
1015 | ||
1016 | *negcr = mDNSNULL; | |
1017 | if (!dv->rrsig) | |
1018 | { | |
1019 | LogMsg("CheckDSForKey: ERROR!! rrsig NULL"); | |
1020 | return mStatus_BadParamErr; | |
1021 | } | |
1022 | if (!dv->key) | |
1023 | { | |
1024 | LogMsg("CheckDSForKey: ERROR!! key NULL"); | |
1025 | return mStatus_BadParamErr; | |
1026 | } | |
1027 | rrsig = (rdataRRSig *)dv->rrsig->rdata; | |
1028 | name = (domainname *)&rrsig->signerName; | |
1029 | slot = HashSlot(name); | |
1030 | namehash = DomainNameHashValue(name); | |
1031 | cg = CacheGroupForName(m, slot, namehash, name); | |
1032 | if (!cg) | |
1033 | { | |
1034 | debugdnssec("CheckDSForKey: cg null for %s", name->c); | |
1035 | return mStatus_NoSuchRecord; | |
1036 | } | |
1037 | for (cr=cg->members; cr; cr=cr->next) | |
1038 | { | |
1039 | if (cr->resrec.rrtype != kDNSType_DS) continue; | |
1040 | if (cr->resrec.RecordType == kDNSRecordTypePacketNegative) | |
1041 | { | |
1042 | if (!(*negcr)) | |
1043 | { | |
1044 | LogDNSSEC("CheckDSForKey: Negative cache record %s encountered for %##s (DS)", CRDisplayString(m, cr), | |
1045 | name->c); | |
1046 | *negcr = cr; | |
1047 | } | |
1048 | else | |
1049 | { | |
1050 | LogMsg("CheckDSForKey: ERROR!! Negative cache record %s already set for %##s (DS)", CRDisplayString(m, cr), | |
1051 | name->c); | |
1052 | } | |
1053 | continue; | |
1054 | } | |
1055 | CheckOneDSForKey(dv, &cr->resrec); | |
1056 | } | |
1057 | if (*negcr && dv->ds) | |
1058 | { | |
1059 | // Encountered both RRSIG and negative CR | |
1060 | LogMsg("CheckDSForKey: ERROR!! Encountered negative cache record %s and DS for %##s", | |
1061 | CRDisplayString(m, *negcr), name->c); | |
1062 | return mStatus_BadParamErr; | |
1063 | } | |
1064 | if (dv->ds || *negcr) | |
1065 | return mStatus_NoError; | |
1066 | else | |
1067 | return mStatus_NoSuchRecord; | |
1068 | return (dv->ds ? mStatus_NoError : mStatus_NoSuchRecord); | |
1069 | } | |
1070 | ||
1071 | // It returns mDNStrue if we have all the rrsets for verification and mDNSfalse otherwise. | |
1072 | mDNSlocal mDNSBool GetAllRRSetsForVerification(mDNS *const m, DNSSECVerifier *dv) | |
1073 | { | |
1074 | mStatus err; | |
1075 | CacheRecord *negcr; | |
1076 | rdataRRSig *rrsig; | |
1077 | ||
1078 | if (!dv->rrset) | |
1079 | { | |
1080 | LogMsg("GetAllRRSetsForVerification: ERROR!! rrset NULL"); | |
51601d48 | 1081 | dv->DVCallback(m, dv, DNSSEC_Bogus); |
83fb1e36 A |
1082 | return mDNSfalse; |
1083 | } | |
1084 | ||
1085 | if (dv->next == RRVS_done) return mDNStrue; | |
1086 | ||
1087 | debugdnssec("GetAllRRSetsForVerification: next %d", dv->next); | |
1088 | switch (dv->next) | |
1089 | { | |
1090 | case RRVS_rrsig: | |
1091 | // If we can't find the RRSIG for the rrset, re-issue the query. | |
1092 | // | |
1093 | // NOTE: It is possible that the cache might answer partially e.g., RRSIGs match qtype but the | |
1094 | // whole set is not there. In that case the validation will fail. Ideally we should flush the | |
1095 | // cache and reissue the query (TBD). | |
1096 | err = CheckRRSIGForRRSet(m, dv, &negcr); | |
1097 | if (err != mStatus_NoSuchRecord && err != mStatus_NoError) | |
1098 | { | |
51601d48 | 1099 | dv->DVCallback(m, dv, DNSSEC_Bogus); |
83fb1e36 A |
1100 | return mDNSfalse; |
1101 | } | |
1102 | // Need to initialize the question as if we end up in ValidateWithNSECS below, the nsec proofs | |
1103 | // looks in "dv->q" for the proof. Note that we have to use currQtype as the response could be | |
1104 | // a CNAME and dv->rrset->rrtype would be set to CNAME and not the original question type that | |
1105 | // resulted in CNAME. | |
1106 | InitializeQuestion(m, &dv->q, dv->InterfaceID, &dv->rrset->name, dv->currQtype, VerifySigCallback, dv); | |
1107 | // We may not have the NSECS if the previous query was a non-DNSSEC query | |
1108 | if (negcr && negcr->nsec) | |
1109 | { | |
83fb1e36 A |
1110 | ValidateWithNSECS(m, dv, negcr); |
1111 | return mDNSfalse; | |
1112 | } | |
1113 | ||
1114 | dv->next = RRVS_key; | |
1115 | if (!dv->rrsig) | |
1116 | { | |
1117 | // We already found the rrset to verify. Ideally we should just issue the query for the RRSIG. Unfortunately, | |
1118 | // that does not work well as the response may not contain the RRSIG whose typeCovered matches the | |
1119 | // rrset->rrtype (recursive server returns what is in its cache). Hence, we send the original query with the | |
1120 | // DO bit set again to get the RRSIG. Normally this would happen if there was question which did not require | |
1121 | // DNSSEC validation (ValidationRequied = 0) populated the cache and later when the ValidationRequired question | |
1122 | // comes along, we need to get the RRSIGs. If we started off with ValidationRequired question we would have | |
1123 | // already set the DO bit and not able to get RRSIGs e.g., bad CPE device, we would reissue the query here | |
1124 | // again once more. | |
1125 | // | |
1126 | // Also, if it is a wildcard expanded answer, we need to issue the query with the original type for it to | |
1127 | // elicit the right NSEC records. Just querying for RRSIG alone is not sufficient. | |
1128 | // | |
1129 | // Note: For this to work, the core needs to deliver RRSIGs when they are added to the cache even if the | |
1130 | // "qtype" is not RRSIG. | |
1131 | debugdnssec("GetAllRRSetsForVerification: Fetching RRSIGS for RRSET"); | |
51601d48 | 1132 | dv->NumPackets++; |
83fb1e36 A |
1133 | mDNS_StartQuery(m, &dv->q); |
1134 | return mDNSfalse; | |
1135 | } | |
51601d48 | 1136 | // if we found the RRSIG, then fall through to find the DNSKEY |
83fb1e36 A |
1137 | case RRVS_key: |
1138 | err = CheckKeyForRRSIG(m, dv, &negcr); | |
1139 | if (err != mStatus_NoSuchRecord && err != mStatus_NoError) | |
1140 | { | |
51601d48 | 1141 | dv->DVCallback(m, dv, DNSSEC_Bogus); |
83fb1e36 A |
1142 | return mDNSfalse; |
1143 | } | |
1144 | // Need to initialize the question as if we end up in ValidateWithNSECS below, the nsec proofs | |
1145 | // looks in "dv->q" for the proof. | |
1146 | rrsig = (rdataRRSig *)dv->rrsig->rdata; | |
1147 | InitializeQuestion(m, &dv->q, dv->InterfaceID, (domainname *)&rrsig->signerName, kDNSType_DNSKEY, VerifySigCallback, dv); | |
1148 | // We may not have the NSECS if the previous query was a non-DNSSEC query | |
1149 | if (negcr && negcr->nsec) | |
1150 | { | |
83fb1e36 A |
1151 | ValidateWithNSECS(m, dv, negcr); |
1152 | return mDNSfalse; | |
1153 | } | |
1154 | ||
1155 | dv->next = RRVS_rrsig_key; | |
1156 | if (!dv->key) | |
1157 | { | |
1158 | debugdnssec("GetAllRRSetsForVerification: Fetching DNSKEY for RRSET"); | |
51601d48 | 1159 | dv->NumPackets++; |
83fb1e36 A |
1160 | mDNS_StartQuery(m, &dv->q); |
1161 | return mDNSfalse; | |
1162 | } | |
1163 | // if we found the DNSKEY, then fall through to find the RRSIG for the DNSKEY | |
1164 | case RRVS_rrsig_key: | |
1165 | err = CheckRRSIGForKey(m, dv, &negcr); | |
1166 | // if we are falling through, then it is okay if we don't find the record | |
1167 | if (err != mStatus_NoSuchRecord && err != mStatus_NoError) | |
1168 | { | |
51601d48 | 1169 | dv->DVCallback(m, dv, DNSSEC_Bogus); |
83fb1e36 A |
1170 | return mDNSfalse; |
1171 | } | |
1172 | // Need to initialize the question as if we end up in ValidateWithNSECS below, the nsec proofs | |
1173 | // looks in "dv->q" for the proof. | |
1174 | rrsig = (rdataRRSig *)dv->rrsig->rdata; | |
1175 | InitializeQuestion(m, &dv->q, dv->InterfaceID, (domainname *)&rrsig->signerName, kDNSType_DNSKEY, VerifySigCallback, dv); | |
1176 | // We may not have the NSECS if the previous query was a non-DNSSEC query | |
1177 | if (negcr && negcr->nsec) | |
1178 | { | |
83fb1e36 A |
1179 | ValidateWithNSECS(m, dv, negcr); |
1180 | return mDNSfalse; | |
1181 | } | |
1182 | dv->next = RRVS_ds; | |
51601d48 | 1183 | debugdnssec("GetAllRRSetsForVerification: RRVS_rrsig_key %p", dv->rrsigKey); |
83fb1e36 A |
1184 | if (!dv->rrsigKey) |
1185 | { | |
1186 | debugdnssec("GetAllRRSetsForVerification: Fetching RRSIGS for DNSKEY"); | |
51601d48 | 1187 | dv->NumPackets++; |
83fb1e36 A |
1188 | mDNS_StartQuery(m, &dv->q); |
1189 | return mDNSfalse; | |
1190 | } | |
1191 | // if we found RRSIG for the DNSKEY, then fall through to find the DS | |
1192 | case RRVS_ds: | |
1193 | { | |
1194 | domainname *qname; | |
1195 | rrsig = (rdataRRSig *)dv->rrsig->rdata; | |
1196 | qname = (domainname *)&rrsig->signerName; | |
1197 | ||
1198 | err = CheckDSForKey(m, dv, &negcr); | |
1199 | if (err != mStatus_NoSuchRecord && err != mStatus_NoError) | |
1200 | { | |
51601d48 | 1201 | dv->DVCallback(m, dv, DNSSEC_Bogus); |
83fb1e36 A |
1202 | return mDNSfalse; |
1203 | } | |
1204 | // Need to initialize the question as if we end up in ValidateWithNSECS below, the nsec proofs | |
1205 | // looks in "dv->q" for the proof. | |
1206 | InitializeQuestion(m, &dv->q, dv->InterfaceID, qname, kDNSType_DS, VerifySigCallback, dv); | |
1207 | // We may not have the NSECS if the previous query was a non-DNSSEC query | |
1208 | if (negcr && negcr->nsec) | |
1209 | { | |
83fb1e36 A |
1210 | ValidateWithNSECS(m, dv, negcr); |
1211 | return mDNSfalse; | |
1212 | } | |
1213 | dv->next = RRVS_done; | |
1214 | // If we have a trust anchor, then don't bother looking up the DS record | |
1215 | if (!dv->ds && !TrustedKeyPresent(m, dv)) | |
1216 | { | |
1217 | // There is no DS for the root. Hence, if we don't have the trust | |
1218 | // anchor for root, just fail. | |
1219 | if (SameDomainName(qname, (const domainname *)"\000")) | |
1220 | { | |
1221 | LogDNSSEC("GetAllRRSetsForVerification: Reached root"); | |
1222 | dv->DVCallback(m, dv, DNSSEC_Bogus); | |
1223 | return mDNSfalse; | |
1224 | } | |
1225 | debugdnssec("GetAllRRSetsForVerification: Fetching DS"); | |
51601d48 | 1226 | dv->NumPackets++; |
83fb1e36 A |
1227 | mDNS_StartQuery(m, &dv->q); |
1228 | return mDNSfalse; | |
1229 | } | |
1230 | else | |
1231 | { | |
1232 | debugdnssec("GetAllRRSetsForVerification: Skipped fetching the DS"); | |
1233 | return mDNStrue; | |
1234 | } | |
1235 | } | |
1236 | default: | |
1237 | LogMsg("GetAllRRSetsForVerification: ERROR!! unknown next %d", dv->next); | |
1238 | dv->DVCallback(m, dv, DNSSEC_Bogus); | |
1239 | return mDNSfalse; | |
1240 | } | |
1241 | } | |
1242 | ||
1243 | #ifdef DNSSEC_DEBUG | |
1244 | mDNSlocal void PrintFixedSignInfo(rdataRRSig *rrsig, domainname *signerName, int sigNameLen, mDNSu8 *fixedPart, int fixedPartLen) | |
1245 | { | |
1246 | int j; | |
1247 | char buf[RRSIG_FIXED_SIZE *3 + 1]; // 3 bytes count for %2x + 1 and the one byte for null at the end | |
1248 | char sig[sigNameLen * 3 + 1]; | |
1249 | char fp[fixedPartLen * 3 + 1]; | |
1250 | int length; | |
1251 | ||
1252 | length = 0; | |
1253 | for (j = 0; j < RRSIG_FIXED_SIZE; j++) | |
1254 | length += mDNS_snprintf(buf+length, sizeof(buf) - length - 1, "%2x ", ((mDNSu8 *)rrsig)[j]); | |
1255 | LogMsg("RRSIG(%d) %s", RRSIG_FIXED_SIZE, buf); | |
1256 | ||
1257 | ||
1258 | length = 0; | |
1259 | for (j = 0; j < sigNameLen; j++) | |
1260 | length += mDNS_snprintf(sig+length, sizeof(sig) - length - 1, "%2x ", signerName->c[j]); | |
1261 | LogMsg("SIGNAME(%d) %s", sigNameLen, sig); | |
1262 | ||
1263 | length = 0; | |
1264 | for (j = 0; j < fixedPartLen; j++) | |
1265 | length += mDNS_snprintf(fp+length, sizeof(fp) - length - 1, "%2x ", fixedPart[j]); | |
1266 | LogMsg("fixedPart(%d) %s", fixedPartLen, fp); | |
1267 | } | |
1268 | ||
1269 | mDNSlocal void PrintVarSignInfo(mDNSu16 rdlen, mDNSu8 *rdata) | |
1270 | { | |
1271 | unsigned int j; | |
1272 | mDNSu8 *r; | |
1273 | unsigned int blen = swap16(rdlen); | |
1274 | char buf[blen * 3 + 1]; // 3 bytes count for %2x + 1 and the one byte for null at the end | |
1275 | int length; | |
1276 | ||
1277 | length = 0; | |
1278 | ||
1279 | r = (mDNSu8 *)&rdlen; | |
1280 | for (j = 0; j < sizeof(mDNSu16); j++) | |
1281 | length += mDNS_snprintf(buf+length, sizeof(buf) - length - 1, "%2x ", r[j]); | |
1282 | LogMsg("RDLENGTH(%d) %s", sizeof(mDNSu16), buf); | |
1283 | ||
1284 | length = 0; | |
1285 | for (j = 0; j < blen; j++) | |
1286 | length += mDNS_snprintf(buf+length, sizeof(buf) - length - 1, "%2x ", rdata[j]); | |
1287 | LogMsg("RDATA(%d) %s", blen, buf); | |
1288 | } | |
1289 | #else | |
1290 | mDNSlocal void PrintVarSignInfo(mDNSu16 rdlen, mDNSu8 *rdata) | |
1291 | { | |
1292 | (void)rdlen; | |
1293 | (void)rdata; | |
1294 | } | |
1295 | mDNSlocal void PrintFixedSignInfo(rdataRRSig *rrsig, domainname *signerName, int sigNameLen, mDNSu8 *fixedPart, int fixedPartLen) | |
1296 | { | |
1297 | (void)rrsig; | |
1298 | (void)signerName; | |
1299 | (void)sigNameLen; | |
1300 | (void)fixedPart; | |
1301 | (void)fixedPartLen; | |
1302 | } | |
1303 | #endif | |
1304 | ||
1305 | // Used for RDATA comparison | |
1306 | typedef struct | |
1307 | { | |
1308 | mDNSu16 rdlength; | |
1309 | mDNSu16 rrtype; | |
1310 | mDNSu8 *rdata; | |
1311 | } rdataComp; | |
1312 | ||
1313 | mDNSlocal int rdata_compare(mDNSu8 *const rdata1, mDNSu8 *const rdata2, int rdlen1, int rdlen2) | |
1314 | { | |
1315 | int len; | |
1316 | int ret; | |
1317 | ||
1318 | len = (rdlen1 < rdlen2) ? rdlen1 : rdlen2; | |
1319 | ||
1320 | ret = DNSMemCmp(rdata1, rdata2, len); | |
1321 | if (ret != 0) return ret; | |
1322 | ||
1323 | // RDATA is same at this stage. Consider them equal if they are of same length. Otherwise | |
1324 | // decide based on their lengths. | |
1325 | return ((rdlen1 == rdlen2) ? 0 : (rdlen1 < rdlen2) ? -1 : 1); | |
1326 | } | |
1327 | ||
1328 | mDNSlocal int name_compare(mDNSu8 *const rdata1, mDNSu8 *const rdata2, int rdlen1, int rdlen2) | |
1329 | { | |
1330 | domainname *n1 = (domainname *)rdata1; | |
1331 | domainname *n2 = (domainname *)rdata2; | |
1332 | mDNSu8 *a = n1->c; | |
1333 | mDNSu8 *b = n2->c; | |
1334 | int count, c1, c2; | |
1335 | int i, j, len; | |
1336 | ||
1337 | c1 = CountLabels(n1); | |
1338 | c2 = CountLabels(n2); | |
1339 | ||
1340 | count = c1 < c2 ? c1 : c2; | |
1341 | ||
1342 | // We can't use SameDomainName as we need to know exactly which is greater/smaller | |
1343 | // for sorting purposes. Hence, we need to compare label by label | |
1344 | for (i = 0; i < count; i++) | |
1345 | { | |
1346 | // Are the lengths same ? | |
1347 | if (*a != *b) | |
1348 | { | |
1349 | debugdnssec("compare_name: returning c1 %d, c2 %d", *a, *b); | |
1350 | return ((*a < *b) ? -1 : 1); | |
1351 | } | |
1352 | len = *a; | |
1353 | rdlen1 -= (len + 1); | |
1354 | rdlen2 -= (len + 1); | |
1355 | if (rdlen1 < 0 || rdlen2 < 0) | |
1356 | { | |
1357 | LogMsg("name_compare: ERROR!! not enough data rdlen1 %d, rdlen2 %d", rdlen1, rdlen2); | |
1358 | return -1; | |
1359 | } | |
1360 | a++; b++; | |
1361 | for (j = 0; j < len; j++) | |
1362 | { | |
1363 | mDNSu8 ac = *a++; | |
1364 | mDNSu8 bc = *b++; | |
1365 | if (mDNSIsUpperCase(ac)) ac += 'a' - 'A'; | |
1366 | if (mDNSIsUpperCase(bc)) bc += 'a' - 'A'; | |
1367 | if (ac != bc) | |
1368 | { | |
1369 | debugdnssec("compare_name: returning ac %c, bc %c", ac, bc); | |
1370 | return ((ac < bc) ? -1 : 1); | |
1371 | } | |
1372 | } | |
1373 | } | |
1374 | ||
1375 | return 0; | |
1376 | } | |
1377 | ||
1378 | mDNSlocal int srv_compare(rdataComp *const r1, rdataComp *const r2) | |
1379 | { | |
1380 | int res; | |
1381 | int length1, length2; | |
1382 | ||
1383 | length1 = r1->rdlength; | |
1384 | length2 = r2->rdlength; | |
1385 | // We should have at least priority, weight, port plus 1 byte | |
1386 | if (length1 < 7 || length2 < 7) | |
1387 | { | |
1388 | LogMsg("srv_compare: ERROR!! Length smaller than 7 bytes"); | |
1389 | return -1; | |
1390 | } | |
1391 | // Compare priority, weight and port | |
1392 | res = DNSMemCmp(r1->rdata, r2->rdata, 6); | |
1393 | if (res != 0) return res; | |
1394 | length1 -= 6; | |
1395 | length2 -= 6; | |
1396 | return (name_compare(r1->rdata + 6, r2->rdata + 6, length1, length2)); | |
1397 | } | |
1398 | ||
1399 | mDNSlocal int tsig_compare(rdataComp *const r1, rdataComp *const r2) | |
1400 | { | |
1401 | int offset1, offset2; | |
1402 | int length1, length2; | |
1403 | int res, dlen; | |
1404 | ||
1405 | offset1 = offset2 = 0; | |
1406 | length1 = r1->rdlength; | |
1407 | length2 = r2->rdlength; | |
1408 | ||
1409 | // we should have at least one byte to start with | |
1410 | if (length1 < 1 || length2 < 1) | |
1411 | { | |
1412 | LogMsg("sig_compare: Length smaller than 18 bytes"); | |
1413 | return -1; | |
1414 | } | |
1415 | ||
1416 | res = name_compare(r1->rdata, r2->rdata, length1, length2); | |
1417 | if (res != 0) return res; | |
1418 | ||
1419 | dlen = DomainNameLength((domainname *)r1->rdata); | |
1420 | offset1 += dlen; | |
1421 | offset2 += dlen; | |
1422 | length1 -= dlen; | |
1423 | length2 -= dlen; | |
1424 | ||
1425 | if (length1 <= 1 || length2 <= 1) | |
1426 | { | |
1427 | LogMsg("tsig_compare: data too small to compare length1 %d, length2 %d", length1, length2); | |
1428 | return -1; | |
1429 | } | |
1430 | ||
1431 | return (rdata_compare(r1->rdata + offset1, r2->rdata + offset2, length1, length2)); | |
1432 | } | |
1433 | ||
1434 | // Compares types that conform to : <length><Value> | |
1435 | mDNSlocal int lenval_compare(mDNSu8 *d1, mDNSu8 *d2, int *len1, int *len2, int rem1, int rem2) | |
1436 | { | |
1437 | int len; | |
1438 | int res; | |
1439 | ||
1440 | if (rem1 <= 1 || rem2 <= 1) | |
1441 | { | |
1442 | LogMsg("lenval_compare: data too small to compare length1 %d, length2 %d", rem1, rem2); | |
1443 | return -1; | |
1444 | } | |
1445 | *len1 = (int)d1[0]; | |
1446 | *len2 = (int)d2[0]; | |
1447 | len = (*len1 < *len2 ? *len1 : *len2); | |
1448 | res = DNSMemCmp(d1, d2, len + 1); | |
1449 | return res; | |
1450 | } | |
1451 | ||
1452 | // RFC 2915: Order (2) Preference(2) and variable length: Flags Service Regexp Replacement | |
1453 | mDNSlocal int naptr_compare(rdataComp *const r1, rdataComp *const r2) | |
1454 | { | |
1455 | mDNSu8 *d1 = r1->rdata; | |
1456 | mDNSu8 *d2 = r2->rdata; | |
1457 | int len1, len2, res; | |
1458 | int length1, length2; | |
1459 | ||
1460 | length1 = r1->rdlength; | |
1461 | length2 = r2->rdlength; | |
1462 | ||
1463 | // Order, Preference plus at least 1 byte | |
1464 | if (length1 < 5 || length2 < 5) | |
1465 | { | |
1466 | LogMsg("naptr_compare: Length smaller than 18 bytes"); | |
1467 | return -1; | |
1468 | } | |
1469 | // Compare order and preference | |
1470 | res = DNSMemCmp(d1, d2, 4); | |
1471 | if (res != 0) return res; | |
1472 | ||
1473 | d1 += 4; | |
1474 | d2 += 4; | |
1475 | length1 -= 4; | |
1476 | length2 -= 4; | |
1477 | ||
1478 | // Compare Flags (including the length byte) | |
1479 | res = lenval_compare(d1, d2, &len1, &len2, length1, length2); | |
1480 | if (res != 0) return res; | |
1481 | d1 += (len1 + 1); | |
1482 | d2 += (len2 + 1); | |
1483 | length1 -= (len1 + 1); | |
1484 | length2 -= (len2 + 1); | |
1485 | ||
1486 | // Compare Service (including the length byte) | |
1487 | res = lenval_compare(d1, d2, &len1, &len2, length1, length2); | |
1488 | if (res != 0) return res; | |
1489 | d1 += (len1 + 1); | |
1490 | d2 += (len2 + 1); | |
1491 | length1 -= (len1 + 1); | |
1492 | length2 -= (len2 + 1); | |
1493 | ||
1494 | // Compare regexp (including the length byte) | |
1495 | res = lenval_compare(d1, d2, &len1, &len2, length1, length2); | |
1496 | if (res != 0) return res; | |
1497 | d1 += (len1 + 1); | |
1498 | d2 += (len2 + 1); | |
1499 | length1 -= (len1 + 1); | |
1500 | length2 -= (len2 + 1); | |
1501 | ||
1502 | // Compare Replacement | |
1503 | return name_compare(d1, d2, length1, length2); | |
1504 | } | |
1505 | ||
1506 | // RFC 1035: MINFO: Two domain names | |
1507 | // RFC 1183: RP: Two domain names | |
1508 | mDNSlocal int dom2_compare(mDNSu8 *d1, mDNSu8 *d2, int length1, int length2) | |
1509 | { | |
1510 | int res, dlen; | |
1511 | ||
1512 | // We need at least one byte to start with | |
1513 | if (length1 < 1 || length2 < 1) | |
1514 | { | |
1515 | LogMsg("dom2_compare:1: data too small length1 %d, length2 %d", length1, length2); | |
1516 | return -1; | |
1517 | } | |
1518 | res = name_compare(d1, d2, length1, length2); | |
1519 | if (res != 0) return res; | |
1520 | dlen = DomainNameLength((domainname *)d1); | |
1521 | ||
1522 | length1 -= dlen; | |
1523 | length2 -= dlen; | |
1524 | // We need at least one byte to start with | |
1525 | if (length1 < 1 || length2 < 1) | |
1526 | { | |
1527 | LogMsg("dom2_compare:2: data too small length1 %d, length2 %d", length1, length2); | |
1528 | return -1; | |
1529 | } | |
1530 | ||
1531 | d1 += dlen; | |
1532 | d2 += dlen; | |
1533 | ||
1534 | return name_compare(d1, d2, length1, length2); | |
1535 | } | |
1536 | ||
1537 | // MX : preference (2 bytes), domainname | |
1538 | mDNSlocal int mx_compare(rdataComp *const r1, rdataComp *const r2) | |
1539 | { | |
1540 | int res; | |
1541 | int length1, length2; | |
1542 | ||
1543 | length1 = r1->rdlength; | |
1544 | length2 = r2->rdlength; | |
1545 | ||
1546 | // We need at least two bytes + 1 extra byte for the domainname to start with | |
1547 | if (length1 < 3 || length2 < 3) | |
1548 | { | |
1549 | LogMsg("mx_compare: data too small length1 %d, length2 %d", length1, length2); | |
1550 | return -1; | |
1551 | } | |
1552 | ||
1553 | res = DNSMemCmp(r1->rdata, r2->rdata, 2); | |
1554 | if (res != 0) return res; | |
1555 | length1 -= 2; | |
1556 | length2 -= 2; | |
1557 | return name_compare(r1->rdata + 2, r2->rdata + 2, length1, length2); | |
1558 | } | |
1559 | ||
1560 | // RFC 2163 (PX) : preference (2 bytes), map822. mapx400 (domainnames) | |
1561 | mDNSlocal int px_compare(rdataComp *const r1, rdataComp *const r2) | |
1562 | { | |
1563 | int res; | |
1564 | ||
1565 | // We need at least two bytes + 1 extra byte for the domainname to start with | |
1566 | if (r1->rdlength < 3 || r2->rdlength < 3) | |
1567 | { | |
1568 | LogMsg("px_compare: data too small length1 %d, length2 %d", r1->rdlength, r2->rdlength); | |
1569 | return -1; | |
1570 | } | |
1571 | ||
1572 | res = DNSMemCmp(r1->rdata, r2->rdata, 2); | |
1573 | if (res != 0) return res; | |
1574 | ||
1575 | return dom2_compare(r1->rdata + 2, r2->rdata + 2, r1->rdlength - 2, r2->rdlength - 2); | |
1576 | } | |
1577 | ||
1578 | mDNSlocal int soa_compare(rdataComp *r1, rdataComp *r2) | |
1579 | { | |
1580 | int res, dlen; | |
1581 | int offset1, offset2; | |
1582 | int length1, length2; | |
1583 | ||
1584 | length1 = r1->rdlength; | |
1585 | length2 = r2->rdlength; | |
1586 | offset1 = offset2 = 0; | |
1587 | ||
1588 | // We need at least 20 bytes plus 1 byte for each domainname | |
1589 | if (length1 < 22 || length2 < 22) | |
1590 | { | |
1591 | LogMsg("soa_compare:1: data too small length1 %d, length2 %d", length1, length2); | |
1592 | return -1; | |
1593 | } | |
1594 | ||
1595 | // There are two domainnames followed by 20 bytes of serial, refresh, retry, expire and min | |
1596 | // Compare the names and then the rest of the bytes | |
1597 | ||
1598 | res = name_compare(r1->rdata, r2->rdata, length1, length2); | |
1599 | if (res != 0) return res; | |
1600 | ||
1601 | dlen = DomainNameLength((domainname *)r1->rdata); | |
1602 | ||
1603 | length1 -= dlen; | |
1604 | length2 -= dlen; | |
1605 | if (length1 < 1 || length2 < 1) | |
1606 | { | |
1607 | LogMsg("soa_compare:2: data too small length1 %d, length2 %d", length1, length2); | |
1608 | return -1; | |
1609 | } | |
1610 | offset1 += dlen; | |
1611 | offset2 += dlen; | |
1612 | ||
1613 | res = name_compare(r1->rdata + offset1, r2->rdata + offset2, length1, length2); | |
1614 | if (res != 0) return res; | |
1615 | ||
1616 | dlen = DomainNameLength((domainname *)r1->rdata); | |
1617 | length1 -= dlen; | |
1618 | length2 -= dlen; | |
1619 | if (length1 < 20 || length2 < 20) | |
1620 | { | |
1621 | LogMsg("soa_compare:3: data too small length1 %d, length2 %d", length1, length2); | |
1622 | return -1; | |
1623 | } | |
1624 | offset1 += dlen; | |
1625 | offset2 += dlen; | |
1626 | ||
1627 | return (rdata_compare(r1->rdata + offset1, r2->rdata + offset2, length1, length2)); | |
1628 | } | |
1629 | ||
1630 | // RFC 4034 Section 6.0 states that: | |
1631 | // | |
1632 | // A canonical RR form and ordering within an RRset are required in order to | |
1633 | // construct and verify RRSIG RRs. | |
1634 | // | |
1635 | // This function is called to order within an RRset. We can't just do a memcmp as | |
1636 | // as stated in 6.3. This function is responsible for the third bullet in 6.2, where | |
1637 | // the RDATA has to be converted to lower case if it has domain names. | |
1638 | mDNSlocal int RDATACompare(const void *rdata1, const void *rdata2) | |
1639 | { | |
1640 | rdataComp *r1 = (rdataComp *)rdata1; | |
1641 | rdataComp *r2 = (rdataComp *)rdata2; | |
1642 | ||
1643 | if (r1->rrtype != r2->rrtype) | |
1644 | { | |
1645 | LogMsg("RDATACompare: ERROR!! comparing rdata of wrong types type1: %d, type2: %d", r1->rrtype, r2->rrtype); | |
1646 | return -1; | |
1647 | } | |
1648 | switch (r1->rrtype) | |
1649 | { | |
1650 | case kDNSType_A: // 1. Address Record | |
1651 | case kDNSType_NULL: // 10 NULL RR | |
1652 | case kDNSType_WKS: // 11 Well-known-service | |
1653 | case kDNSType_HINFO: // 13 Host information | |
1654 | case kDNSType_TXT: // 16 Arbitrary text string | |
1655 | case kDNSType_X25: // 19 X_25 calling address | |
1656 | case kDNSType_ISDN: // 20 ISDN calling address | |
1657 | case kDNSType_NSAP: // 22 NSAP address | |
1658 | case kDNSType_KEY: // 25 Security key | |
1659 | case kDNSType_GPOS: // 27 Geographical position (withdrawn) | |
1660 | case kDNSType_AAAA: // 28 IPv6 Address | |
1661 | case kDNSType_LOC: // 29 Location Information | |
1662 | case kDNSType_EID: // 31 Endpoint identifier | |
1663 | case kDNSType_NIMLOC: // 32 Nimrod Locator | |
1664 | case kDNSType_ATMA: // 34 ATM Address | |
1665 | case kDNSType_CERT: // 37 Certification record | |
1666 | case kDNSType_A6: // 38 IPv6 Address (deprecated) | |
1667 | case kDNSType_SINK: // 40 Kitchen sink (experimental) | |
1668 | case kDNSType_OPT: // 41 EDNS0 option (meta-RR) | |
1669 | case kDNSType_APL: // 42 Address Prefix List | |
1670 | case kDNSType_DS: // 43 Delegation Signer | |
1671 | case kDNSType_SSHFP: // 44 SSH Key Fingerprint | |
1672 | case kDNSType_IPSECKEY: // 45 IPSECKEY | |
1673 | case kDNSType_RRSIG: // 46 RRSIG | |
1674 | case kDNSType_NSEC: // 47 Denial of Existence | |
1675 | case kDNSType_DNSKEY: // 48 DNSKEY | |
1676 | case kDNSType_DHCID: // 49 DHCP Client Identifier | |
1677 | case kDNSType_NSEC3: // 50 Hashed Authenticated Denial of Existence | |
1678 | case kDNSType_NSEC3PARAM: // 51 Hashed Authenticated Denial of Existence | |
1679 | case kDNSType_HIP: // 55 Host Identity Protocol | |
1680 | case kDNSType_SPF: // 99 Sender Policy Framework for E-Mail | |
1681 | default: | |
1682 | return rdata_compare(r1->rdata, r2->rdata, r1->rdlength, r2->rdlength); | |
1683 | case kDNSType_NS: // 2 Name Server | |
1684 | case kDNSType_MD: // 3 Mail Destination | |
1685 | case kDNSType_MF: // 4 Mail Forwarder | |
1686 | case kDNSType_CNAME: // 5 Canonical Name | |
1687 | case kDNSType_MB: // 7 Mailbox | |
1688 | case kDNSType_MG: // 8 Mail Group | |
1689 | case kDNSType_MR: // 9 Mail Rename | |
1690 | case kDNSType_PTR: // 12 Domain name pointer | |
1691 | case kDNSType_NSAP_PTR: // 23 Reverse NSAP lookup (deprecated) | |
1692 | case kDNSType_DNAME: // 39 Non-terminal DNAME (for IPv6) | |
1693 | return name_compare(r1->rdata, r2->rdata, r1->rdlength, r2->rdlength); | |
1694 | case kDNSType_SRV: // 33 Service record | |
1695 | return srv_compare(r1, r2); | |
1696 | case kDNSType_SOA: // 6 Start of Authority | |
1697 | return soa_compare(r1, r2); | |
1698 | ||
1699 | case kDNSType_RP: // 17 Responsible person | |
1700 | case kDNSType_MINFO: // 14 Mailbox information | |
1701 | return dom2_compare(r1->rdata, r2->rdata, r1->rdlength, r2->rdlength); | |
1702 | case kDNSType_MX: // 15 Mail Exchanger | |
1703 | case kDNSType_AFSDB: // 18 AFS cell database | |
1704 | case kDNSType_RT: // 21 Router | |
1705 | case kDNSType_KX: // 36 Key Exchange | |
1706 | return mx_compare(r1, r2); | |
1707 | case kDNSType_PX: // 26 X.400 mail mapping | |
1708 | return px_compare(r1, r2); | |
1709 | case kDNSType_NAPTR: // 35 Naming Authority PoinTeR | |
1710 | return naptr_compare(r1, r2); | |
1711 | case kDNSType_TKEY: // 249 Transaction key | |
1712 | case kDNSType_TSIG: // 250 Transaction signature | |
1713 | // TSIG and TKEY have a domainname followed by data | |
1714 | return tsig_compare(r1, r2); | |
1715 | // TBD: We are comparing them as opaque types, perhaps not right | |
1716 | case kDNSType_SIG: // 24 Security signature | |
1717 | case kDNSType_NXT: // 30 Next domain (security) | |
1718 | LogMsg("RDATACompare: WARNING!! explicit support has not been added, using default"); | |
1719 | return rdata_compare(r1->rdata, r2->rdata, r1->rdlength, r2->rdlength); | |
1720 | } | |
1721 | } | |
1722 | ||
1723 | ||
1724 | ||
1725 | // RFC 4034 section 6.2 requirement for verifying signature. | |
1726 | // | |
1727 | // 3. if the type of the RR is NS, MD, MF, CNAME, SOA, MB, MG, MR, PTR, | |
1728 | // HINFO, MINFO, MX, HINFO, RP, AFSDB, RT, SIG, PX, NXT, NAPTR, KX, | |
1729 | // SRV, DNAME, A6, RRSIG, or NSEC, all uppercase US-ASCII letters in | |
1730 | // the DNS names contained within the RDATA are replaced by the | |
1731 | // corresponding lowercase US-ASCII letters; | |
1732 | // | |
1733 | // NSEC and HINFO is not needed as per dnssec-bis update. RRSIG is done elsewhere | |
1734 | // as part of signature verification | |
1735 | mDNSlocal void ConvertRDATAToCanonical(mDNSu16 rrtype, mDNSu16 rdlength, mDNSu8 *rdata) | |
1736 | { | |
1737 | domainname name; | |
1738 | int len; | |
1739 | mDNSu8 *origRdata = rdata; | |
1740 | ||
1741 | // Ensure that we have at least one byte of data to examine and modify. | |
1742 | ||
1743 | if (!rdlength) { LogMsg("ConvertRDATAToCanonical: rdlength zero for rrtype %s", DNSTypeName(rrtype)); return; } | |
1744 | ||
1745 | switch (rrtype) | |
1746 | { | |
1747 | // Not adding suppot for A6 as it is deprecated | |
1748 | case kDNSType_A6: // 38 IPv6 Address (deprecated) | |
1749 | default: | |
1750 | debugdnssec("ConvertRDATAToCanonical: returning from default %s", DNSTypeName(rrtype)); | |
1751 | return; | |
1752 | case kDNSType_NS: // 2 Name Server | |
1753 | case kDNSType_MD: // 3 Mail Destination | |
1754 | case kDNSType_MF: // 4 Mail Forwarder | |
1755 | case kDNSType_CNAME: // 5 Canonical Name | |
1756 | case kDNSType_MB: // 7 Mailbox | |
1757 | case kDNSType_MG: // 8 Mail Group | |
1758 | case kDNSType_MR: // 9 Mail Rename | |
1759 | case kDNSType_PTR: // 12 Domain name pointer | |
1760 | case kDNSType_DNAME: // 39 Non-terminal DNAME (for IPv6) | |
1761 | case kDNSType_NXT: // 30 Next domain (security) | |
1762 | ||
1763 | // TSIG and TKEY are not mentioned in RFC 4034, but we just leave it here | |
1764 | case kDNSType_TSIG: // 250 Transaction signature | |
1765 | case kDNSType_TKEY: // 249 Transaction key | |
1766 | ||
1767 | if (DNSNameToLowerCase((domainname *)rdata, &name) != mStatus_NoError) | |
1768 | { | |
1769 | LogMsg("ConvertRDATAToCanonical: ERROR!! DNSNameToLowerCase failed"); | |
1770 | return; | |
1771 | } | |
1772 | AssignDomainName((domainname *)rdata, &name); | |
1773 | return; | |
1774 | case kDNSType_MX: // 15 Mail Exchanger | |
1775 | case kDNSType_AFSDB: // 18 AFS cell database | |
1776 | case kDNSType_RT: // 21 Router | |
1777 | case kDNSType_KX: // 36 Key Exchange | |
1778 | ||
1779 | // format: preference - 2 bytes, followed by name | |
1780 | // Ensure that we have at least 3 bytes (preference + 1 byte for the domain name) | |
1781 | if (rdlength <= 3) | |
1782 | { | |
1783 | LogMsg("ConvertRDATAToCanonical:MX: rdlength %d for rrtype %s too small", rdlength, DNSTypeName(rrtype)); | |
1784 | return; | |
1785 | } | |
1786 | if (DNSNameToLowerCase((domainname *)(rdata + 2), &name) != mStatus_NoError) | |
1787 | { | |
1788 | LogMsg("ConvertRDATAToCanonical: MX: ERROR!! DNSNameToLowerCase failed"); | |
1789 | return; | |
1790 | } | |
1791 | AssignDomainName((domainname *)(rdata + 2), &name); | |
1792 | return; | |
1793 | case kDNSType_SRV: // 33 Service record | |
1794 | // format : priority, weight and port - 6 bytes, followed by name | |
1795 | if (rdlength <= 7) | |
1796 | { | |
1797 | LogMsg("ConvertRDATAToCanonical:SRV: rdlength %d for rrtype %s too small", rdlength, DNSTypeName(rrtype)); | |
1798 | return; | |
1799 | } | |
1800 | if (DNSNameToLowerCase((domainname *)(rdata + 6), &name) != mStatus_NoError) | |
1801 | { | |
1802 | LogMsg("ConvertRDATAToCanonical: SRV: ERROR!! DNSNameToLowerCase failed"); | |
1803 | return; | |
1804 | } | |
1805 | AssignDomainName((domainname *)(rdata + 6), &name); | |
1806 | return; | |
1807 | case kDNSType_PX: // 26 X.400 mail mapping | |
1808 | if (rdlength <= 3) | |
1809 | { | |
1810 | LogMsg("ConvertRDATAToCanonical:PX: rdlength %d for rrtype %s too small", rdlength, DNSTypeName(rrtype)); | |
1811 | return; | |
1812 | } | |
1813 | // Preference followed by two domain names | |
1814 | rdata += 2; | |
1815 | /* FALLTHROUGH */ | |
1816 | case kDNSType_RP: // 17 Responsible person | |
1817 | case kDNSType_SOA: // 6 Start of Authority | |
1818 | case kDNSType_MINFO: // 14 Mailbox information | |
1819 | if (DNSNameToLowerCase((domainname *)rdata, &name) != mStatus_NoError) | |
1820 | { | |
1821 | LogMsg("ConvertRDATAToCanonical: SOA1: ERROR!! DNSNameToLowerCase failed"); | |
1822 | return; | |
1823 | } | |
1824 | ||
1825 | AssignDomainName((domainname *)rdata, &name); | |
1826 | len = DomainNameLength((domainname *)rdata); | |
1827 | if (rdlength <= len + 1) | |
1828 | { | |
1829 | LogMsg("ConvertRDATAToCanonical:RP: rdlength %d for rrtype %s too small", rdlength, DNSTypeName(rrtype)); | |
1830 | return; | |
1831 | } | |
1832 | rdata += len; | |
1833 | ||
1834 | if (DNSNameToLowerCase((domainname *)rdata, &name) != mStatus_NoError) | |
1835 | { | |
1836 | LogMsg("ConvertRDATAToCanonical: SOA2: ERROR!! DNSNameToLowerCase failed"); | |
1837 | return; | |
1838 | } | |
1839 | AssignDomainName((domainname *)rdata, &name); | |
1840 | return; | |
1841 | case kDNSType_NAPTR: // 35 Naming Authority Pointer | |
1842 | // order and preference | |
1843 | rdata += 4; | |
1844 | // Flags (including the length byte) | |
1845 | rdata += (((int) rdata[0]) + 1); | |
1846 | // Service (including the length byte) | |
1847 | rdata += (((int) rdata[0]) + 1); | |
1848 | // regexp (including the length byte) | |
1849 | rdata += (((int) rdata[0]) + 1); | |
1850 | ||
1851 | // Replacement field is a domainname. If we have at least one more byte, then we are okay. | |
1852 | if ((origRdata + rdlength) < rdata + 1) | |
1853 | { | |
1854 | LogMsg("ConvertRDATAToCanonical:NAPTR: origRdata %p, rdlength %d, rdata %p for rrtype %s too small", origRdata, rdlength, rdata, DNSTypeName(rrtype)); | |
1855 | return; | |
1856 | } | |
1857 | if (DNSNameToLowerCase((domainname *)rdata, &name) != mStatus_NoError) | |
1858 | { | |
1859 | LogMsg("ConvertRDATAToCanonical: NAPTR2: ERROR!! DNSNameToLowerCase failed"); | |
1860 | return; | |
1861 | } | |
1862 | AssignDomainName((domainname *)rdata, &name); | |
1863 | case kDNSType_SIG: // 24 Security signature | |
1864 | // format: <18 bytes> <domainname> <data> | |
1865 | if (rdlength <= 19) | |
1866 | { | |
1867 | LogMsg("ConvertRDATAToCanonical:SIG: rdlength %d for rrtype %s too small", rdlength, DNSTypeName(rrtype)); | |
1868 | return; | |
1869 | } | |
1870 | // Preference followed by two domain names | |
1871 | rdata += 18; | |
1872 | if (DNSNameToLowerCase((domainname *)rdata, &name) != mStatus_NoError) | |
1873 | { | |
1874 | LogMsg("ConvertRDATAToCanonical: SIG: ERROR!! DNSNameToLowerCase failed"); | |
1875 | return; | |
1876 | } | |
1877 | AssignDomainName((domainname *)rdata, &name); | |
1878 | return; | |
1879 | } | |
1880 | } | |
1881 | ||
1882 | mDNSlocal mDNSBool ValidateSignatureWithKey(DNSSECVerifier *dv, RRVerifier *rrset, RRVerifier *keyv, RRVerifier *sig) | |
1883 | { | |
1884 | domainname name; | |
1885 | domainname signerName; | |
1886 | int labels; | |
1887 | mDNSu8 fixedPart[MAX_DOMAIN_NAME + 8]; // domainname + type + class + ttl | |
1888 | int fixedPartLen; | |
1889 | RRVerifier *tmp; | |
1890 | int nrrsets; | |
1891 | rdataComp *ptr, *start, *p; | |
1892 | rdataRRSig *rrsig; | |
1893 | rdataDNSKey *key; | |
1894 | int i; | |
1895 | int sigNameLen; | |
1896 | mDNSu16 temp; | |
1897 | mStatus algRet; | |
1898 | ||
1899 | ||
1900 | key = (rdataDNSKey *)keyv->rdata; | |
1901 | rrsig = (rdataRRSig *)sig->rdata; | |
1902 | ||
1903 | LogDNSSEC("ValidateSignatureWithKey: Validating signature with key with tag %d", (mDNSu16)keytag((mDNSu8 *)key, keyv->rdlength)); | |
1904 | ||
1905 | if (DNSNameToLowerCase((domainname *)&rrsig->signerName, &signerName) != mStatus_NoError) | |
1906 | { | |
1907 | LogMsg("ValidateSignatureWithKey: ERROR!! cannot convert signer name to lower case"); | |
1908 | return mDNSfalse; | |
1909 | } | |
1910 | ||
1911 | if (DNSNameToLowerCase((domainname *)&rrset->name, &name) != mStatus_NoError) | |
1912 | { | |
1913 | LogMsg("ValidateSignatureWithKey: ERROR!! cannot convert rrset name to lower case"); | |
1914 | return mDNSfalse; | |
1915 | } | |
1916 | ||
1917 | sigNameLen = DomainNameLength(&signerName); | |
1918 | labels = CountLabels(&name); | |
1919 | // RFC 4034: RRSIG validation | |
1920 | // | |
1921 | // signature = sign(RRSIG_RDATA | RR(1) | RR(2)... ) | |
1922 | // | |
1923 | // where RRSIG_RDATA excludes the signature and signer name in canonical form | |
1924 | ||
1925 | if (dv->ctx) AlgDestroy(dv->ctx); | |
1926 | dv->ctx = AlgCreate(CRYPTO_ALG, rrsig->alg); | |
1927 | if (!dv->ctx) | |
1928 | { | |
51601d48 | 1929 | LogDNSSEC("ValidateSignatureWithKey: ERROR!! No algorithm support for %d", rrsig->alg); |
83fb1e36 A |
1930 | return mDNSfalse; |
1931 | } | |
51601d48 | 1932 | AlgAdd(dv->ctx, (const mDNSu8 *)rrsig, RRSIG_FIXED_SIZE); |
83fb1e36 A |
1933 | AlgAdd(dv->ctx, signerName.c, sigNameLen); |
1934 | ||
1935 | if (labels - rrsig->labels > 0) | |
1936 | { | |
1937 | domainname *d; | |
1938 | LogDNSSEC("ValidateSignatureWithKey: ====splitting labels %d, rrsig->labels %d====", labels,rrsig->labels); | |
1939 | d = (domainname *)SkipLeadingLabels(&name, labels - rrsig->labels); | |
1940 | fixedPart[0] = 1; | |
1941 | fixedPart[1] = '*'; | |
1942 | AssignDomainName((domainname *)(fixedPart + 2), d); | |
1943 | fixedPartLen = DomainNameLength(d) + 2; | |
1944 | // See RFC 4034 section 3.1.3. If you are looking up *.example.com, | |
1945 | // the labels count in the RRSIG is 2, but this is not considered as | |
1946 | // a wildcard answer | |
1947 | if (name.c[0] != 1 || name.c[1] != '*') | |
1948 | { | |
1949 | LogDNSSEC("ValidateSignatureWithKey: Wildcard exapnded answer for %##s (%s)", dv->origName.c, DNSTypeName(dv->origType)); | |
1950 | dv->flags |= WILDCARD_PROVES_ANSWER_EXPANDED; | |
1951 | dv->wildcardName = (domainname *)SkipLeadingLabels(&dv->origName, labels - rrsig->labels); | |
1952 | if (!dv->wildcardName) return mDNSfalse; | |
1953 | } | |
1954 | } | |
1955 | else | |
1956 | { | |
1957 | debugdnssec("ValidateSignatureWithKey: assigning domainname"); | |
1958 | AssignDomainName((domainname *)fixedPart, &name); | |
1959 | fixedPartLen = DomainNameLength(&name); | |
1960 | } | |
1961 | temp = swap16(rrset->rrtype); | |
1962 | mDNSPlatformMemCopy(fixedPart + fixedPartLen, (mDNSu8 *)&temp, sizeof(rrset->rrtype)); | |
1963 | fixedPartLen += sizeof(rrset->rrtype); | |
1964 | temp = swap16(rrset->rrclass); | |
1965 | mDNSPlatformMemCopy(fixedPart + fixedPartLen, (mDNSu8 *)&temp, sizeof(rrset->rrclass)); | |
1966 | fixedPartLen += sizeof(rrset->rrclass); | |
1967 | mDNSPlatformMemCopy(fixedPart + fixedPartLen, (mDNSu8 *)&rrsig->origTTL, sizeof(rrsig->origTTL)); | |
1968 | fixedPartLen += sizeof(rrsig->origTTL); | |
1969 | ||
1970 | ||
1971 | for (tmp = rrset, nrrsets = 0; tmp; tmp = tmp->next) | |
1972 | nrrsets++; | |
1973 | ||
1974 | tmp = rrset; | |
1975 | start = ptr = mDNSPlatformMemAllocate(nrrsets * sizeof (rdataComp)); | |
1976 | debugdnssec("ValidateSignatureWithKey: start %p, nrrsets %d", start, nrrsets); | |
1977 | if (ptr) | |
1978 | { | |
1979 | // Need to initialize for failure case below | |
1980 | mDNSPlatformMemZero(ptr, nrrsets * (sizeof (rdataComp))); | |
1981 | while (tmp) | |
1982 | { | |
1983 | ptr->rdlength = tmp->rdlength; | |
1984 | ptr->rrtype = tmp->rrtype; | |
1985 | if (ptr->rdlength) | |
1986 | { | |
1987 | ptr->rdata = mDNSPlatformMemAllocate(ptr->rdlength); | |
1988 | if (ptr->rdata) | |
51601d48 | 1989 | { |
83fb1e36 | 1990 | mDNSPlatformMemCopy(ptr->rdata, tmp->rdata, tmp->rdlength); |
51601d48 | 1991 | } |
83fb1e36 A |
1992 | else |
1993 | { | |
1994 | for (i = 0; i < nrrsets; i++) | |
1995 | if (start[i].rdata) mDNSPlatformMemFree(start[i].rdata); | |
1996 | mDNSPlatformMemFree(start); | |
1997 | LogMsg("ValidateSignatureWithKey:1: ERROR!! RDATA memory alloation failure"); | |
1998 | return mDNSfalse; | |
1999 | } | |
2000 | } | |
2001 | ptr++; | |
2002 | tmp = tmp->next; | |
2003 | } | |
2004 | } | |
2005 | else | |
2006 | { | |
2007 | LogMsg("ValidateSignatureWithKey:2: ERROR!! RDATA memory alloation failure"); | |
2008 | return mDNSfalse; | |
2009 | } | |
2010 | ||
2011 | PrintFixedSignInfo(rrsig, &signerName, sigNameLen, fixedPart, fixedPartLen); | |
2012 | ||
2013 | mDNSPlatformQsort(start, nrrsets, sizeof(rdataComp), RDATACompare); | |
2014 | for (p = start, i = 0; i < nrrsets; p++, i++) | |
2015 | { | |
2016 | int rdlen; | |
2017 | ||
2018 | // The array is sorted and hence checking adjacent entries for duplicate is sufficient | |
2019 | if (i > 0) | |
2020 | { | |
2021 | rdataComp *q = p - 1; | |
2022 | if (!RDATACompare((void *)p, (void *)q)) continue; | |
2023 | } | |
2024 | ||
2025 | // Add the fixed part | |
51601d48 | 2026 | AlgAdd(dv->ctx, (const mDNSu8 *)fixedPart, fixedPartLen); |
83fb1e36 A |
2027 | |
2028 | // Add the rdlength | |
2029 | rdlen = swap16(p->rdlength); | |
51601d48 | 2030 | AlgAdd(dv->ctx, (const mDNSu8 *)&rdlen, sizeof(mDNSu16)); |
83fb1e36 A |
2031 | |
2032 | ConvertRDATAToCanonical(p->rrtype, p->rdlength, p->rdata); | |
2033 | ||
2034 | PrintVarSignInfo(rdlen, p->rdata); | |
51601d48 | 2035 | AlgAdd(dv->ctx, (const mDNSu8 *)p->rdata, p->rdlength); |
83fb1e36 A |
2036 | } |
2037 | // free the memory as we don't need it anymore | |
2038 | for (i = 0; i < nrrsets; i++) | |
2039 | if (start[i].rdata) mDNSPlatformMemFree(start[i].rdata); | |
2040 | mDNSPlatformMemFree(start); | |
2041 | ||
2042 | algRet = AlgVerify(dv->ctx, (mDNSu8 *)&key->data, keyv->rdlength - DNSKEY_FIXED_SIZE, (mDNSu8 *)(sig->rdata + sigNameLen + RRSIG_FIXED_SIZE), sig->rdlength - RRSIG_FIXED_SIZE - sigNameLen); | |
2043 | AlgDestroy(dv->ctx); | |
2044 | dv->ctx = mDNSNULL; | |
2045 | if (algRet != mStatus_NoError) | |
2046 | { | |
2047 | LogDNSSEC("ValidateSignatureWithKey: AlgVerify failed for %##s (%s)", dv->origName.c, DNSTypeName(dv->origType)); | |
2048 | // Reset the state if we set any above. | |
2049 | if (dv->flags & WILDCARD_PROVES_ANSWER_EXPANDED) | |
2050 | { | |
2051 | dv->flags &= ~WILDCARD_PROVES_ANSWER_EXPANDED; | |
2052 | dv->wildcardName = mDNSNULL; | |
2053 | } | |
2054 | return mDNSfalse; | |
2055 | } | |
2056 | return mDNStrue; | |
2057 | } | |
2058 | ||
2059 | // Walk all the keys and for each key walk all the RRSIGS that signs the original rrset | |
2060 | mDNSlocal mStatus ValidateSignature(DNSSECVerifier *dv, RRVerifier **resultKey, RRVerifier **resultRRSIG) | |
2061 | { | |
2062 | RRVerifier *rrset; | |
2063 | RRVerifier *keyv; | |
2064 | RRVerifier *rrsigv; | |
2065 | RRVerifier *sig; | |
2066 | rdataDNSKey *key; | |
2067 | rdataRRSig *rrsig; | |
2068 | mDNSu16 tag; | |
2069 | ||
2070 | rrset = dv->rrset; | |
2071 | sig = dv->rrsig; | |
2072 | ||
2073 | for (keyv = dv->key; keyv; keyv = keyv->next) | |
2074 | { | |
2075 | key = (rdataDNSKey *)keyv->rdata; | |
2076 | tag = (mDNSu16)keytag((mDNSu8 *)key, keyv->rdlength); | |
2077 | for (rrsigv = sig; rrsigv; rrsigv = rrsigv->next) | |
2078 | { | |
2079 | rrsig = (rdataRRSig *)rrsigv->rdata; | |
2080 | // 7. The RRSIG RR's Signer's Name, Algorithm, and Key Tag fields MUST match the owner | |
2081 | // name, algorithm, and key tag for some DNSKEY RR in the zone's apex DNSKEY RRset. | |
2082 | if (!SameDomainName((domainname *)&rrsig->signerName, &keyv->name)) | |
2083 | { | |
2084 | debugdnssec("ValidateSignature: name mismatch"); | |
2085 | continue; | |
2086 | } | |
2087 | if (key->alg != rrsig->alg) | |
2088 | { | |
2089 | debugdnssec("ValidateSignature: alg mismatch"); | |
2090 | continue; | |
2091 | } | |
2092 | if (tag != swap16(rrsig->keyTag)) | |
2093 | { | |
2094 | debugdnssec("ValidateSignature: keyTag mismatch rrsig tag %d(0x%x), keyTag %d(0x%x)", swap16(rrsig->keyTag), | |
2095 | swap16(rrsig->keyTag), tag, tag); | |
2096 | continue; | |
2097 | } | |
2098 | // 8. The matching DNSKEY RR MUST be present in the zone's apex DNSKEY RRset, and MUST | |
2099 | // have the Zone Flag bit (DNSKEY RDATA Flag bit 7) set. | |
2100 | if (!((swap16(key->flags)) & DNSKEY_ZONE_SIGN_KEY)) | |
2101 | { | |
2102 | debugdnssec("ValidateSignature: ZONE flag bit not set"); | |
2103 | continue; | |
2104 | } | |
2105 | debugdnssec("ValidateSignature:Found a key and RRSIG tag: %d", tag); | |
2106 | if (ValidateSignatureWithKey(dv, rrset, keyv, rrsigv)) | |
2107 | { | |
2108 | LogDNSSEC("ValidateSignature: Validated successfully with key tag %d", tag); | |
2109 | *resultKey = keyv; | |
2110 | *resultRRSIG = rrsigv; | |
2111 | return mStatus_NoError; | |
2112 | } | |
2113 | } | |
2114 | } | |
2115 | *resultKey = mDNSNULL; | |
2116 | *resultRRSIG = mDNSNULL; | |
2117 | return mStatus_NoSuchRecord; | |
2118 | } | |
2119 | ||
2120 | mDNSlocal mDNSBool ValidateSignatureWithKeyForAllRRSigs(DNSSECVerifier *dv, RRVerifier *rrset, RRVerifier *keyv, RRVerifier *sig) | |
2121 | { | |
2122 | rdataRRSig *rrsig; | |
2123 | mDNSu16 tag; | |
2124 | ||
2125 | while (sig) | |
2126 | { | |
2127 | rrsig = (rdataRRSig *)sig->rdata; | |
2128 | tag = (mDNSu16)keytag(keyv->rdata, keyv->rdlength); | |
2129 | if (tag == swap16(rrsig->keyTag)) | |
2130 | { | |
2131 | if (ValidateSignatureWithKey(dv, rrset, keyv, sig)) | |
2132 | { | |
2133 | LogDNSSEC("ValidateSignatureWithKeyForAllRRSigs: Validated"); | |
2134 | return mDNStrue; | |
2135 | } | |
2136 | } | |
2137 | sig = sig->next; | |
2138 | } | |
2139 | return mDNSfalse; | |
2140 | } | |
2141 | ||
2142 | mDNSlocal mStatus ValidateDS(DNSSECVerifier *dv) | |
2143 | { | |
2144 | mDNSu8 *digest; | |
2145 | int digestLen; | |
2146 | domainname name; | |
2147 | rdataRRSig *rrsig; | |
2148 | rdataDS *ds; | |
2149 | rdataDNSKey *key; | |
2150 | RRVerifier *keyv; | |
2151 | RRVerifier *dsv; | |
2152 | mStatus algRet; | |
2153 | ||
2154 | rrsig = (rdataRRSig *)dv->rrsig->rdata; | |
2155 | ||
2156 | // Walk all the DS Records to see if we have a matching DNS KEY record that verifies | |
2157 | // the hash. If we find one, verify that this key was used to sign the KEY rrsets in | |
2158 | // this zone. Loop till we find one. | |
2159 | for (dsv = dv->ds; dsv; dsv = dsv->next) | |
2160 | { | |
2161 | ds = (rdataDS *)dsv->rdata; | |
2162 | if ((ds->digestType != SHA1_DIGEST_TYPE) && (ds->digestType != SHA256_DIGEST_TYPE)) | |
2163 | { | |
2164 | LogDNSSEC("ValidateDS: Unsupported digest %d", ds->digestType); | |
2165 | return mStatus_BadParamErr; | |
2166 | } | |
2167 | else debugdnssec("ValidateDS: digest type %d", ds->digestType); | |
2168 | for (keyv = dv->key; keyv; keyv = keyv->next) | |
2169 | { | |
2170 | key = (rdataDNSKey *)keyv->rdata; | |
2171 | mDNSu16 tag = (mDNSu16)keytag((mDNSu8 *)key, keyv->rdlength); | |
2172 | if (tag != swap16(ds->keyTag)) | |
2173 | { | |
2174 | debugdnssec("ValidateDS:Not a valid keytag %d", tag); | |
2175 | continue; | |
2176 | } | |
2177 | ||
2178 | if (DNSNameToLowerCase((domainname *)&rrsig->signerName, &name) != mStatus_NoError) | |
2179 | { | |
2180 | LogMsg("ValidateDS: ERROR!! cannot convert to lower case"); | |
2181 | continue; | |
2182 | } | |
2183 | ||
2184 | if (dv->ctx) AlgDestroy(dv->ctx); | |
2185 | dv->ctx = AlgCreate(DIGEST_ALG, ds->digestType); | |
2186 | if (!dv->ctx) | |
2187 | { | |
2188 | LogMsg("ValidateDS: ERROR!! Cannot allocate context"); | |
2189 | continue; | |
2190 | } | |
2191 | digest = (mDNSu8 *)&ds->digest; | |
2192 | digestLen = dsv->rdlength - DS_FIXED_SIZE; | |
2193 | ||
2194 | AlgAdd(dv->ctx, name.c, DomainNameLength(&name)); | |
51601d48 | 2195 | AlgAdd(dv->ctx, (const mDNSu8 *)key, keyv->rdlength); |
83fb1e36 A |
2196 | |
2197 | algRet = AlgVerify(dv->ctx, mDNSNULL, 0, digest, digestLen); | |
2198 | AlgDestroy(dv->ctx); | |
2199 | dv->ctx = mDNSNULL; | |
2200 | if (algRet == mStatus_NoError) | |
2201 | { | |
2202 | LogDNSSEC("ValidateDS: DS Validated Successfully, need to verify the key %d", tag); | |
2203 | // We found the DNS KEY that is authenticated by the DS in our parent zone. Check to see if this key | |
2204 | // was used to sign the DNS KEY RRSET. If so, then the keys in our DNS KEY RRSET are valid | |
2205 | if (ValidateSignatureWithKeyForAllRRSigs(dv, dv->key, keyv, dv->rrsigKey)) | |
2206 | { | |
2207 | LogDNSSEC("ValidateDS: DS Validated Successfully %d", tag); | |
2208 | return mStatus_NoError; | |
2209 | } | |
2210 | } | |
2211 | } | |
2212 | } | |
2213 | return mStatus_NoSuchRecord; | |
2214 | } | |
2215 | ||
2216 | mDNSlocal mDNSBool UnlinkRRVerifier(DNSSECVerifier *dv, RRVerifier *elem, RRVerifierSet set) | |
2217 | { | |
2218 | RRVerifier **v; | |
2219 | ||
2220 | switch (set) | |
2221 | { | |
2222 | case RRVS_rr: | |
2223 | v = &dv->rrset; | |
2224 | break; | |
2225 | case RRVS_rrsig: | |
2226 | v = &dv->rrsig; | |
2227 | break; | |
2228 | case RRVS_key: | |
2229 | v = &dv->key; | |
2230 | break; | |
2231 | case RRVS_rrsig_key: | |
2232 | v = &dv->rrsigKey; | |
2233 | break; | |
2234 | case RRVS_ds: | |
2235 | v = &dv->ds; | |
2236 | break; | |
2237 | default: | |
2238 | LogMsg("UnlinkRRVerifier: ERROR!! default case %d", set); | |
2239 | return mDNSfalse; | |
2240 | } | |
2241 | while (*v && *v != elem) | |
2242 | v = &(*v)->next; | |
2243 | if (!(*v)) | |
2244 | { | |
2245 | LogMsg("UnlinkRRVerifier: ERROR!! cannot find element in set %d", set); | |
2246 | return mDNSfalse; | |
2247 | } | |
2248 | *v = elem->next; // Cut this record from the list | |
2249 | elem->next = mDNSNULL; | |
2250 | return mDNStrue; | |
2251 | } | |
2252 | ||
2253 | // This can link a single AuthChain element or a list of AuthChain elements to | |
2254 | // DNSSECVerifier. The latter happens when we have multiple NSEC proofs and | |
2255 | // we gather up all the proofs in one place. | |
2256 | mDNSexport void AuthChainLink(DNSSECVerifier *dv, AuthChain *ae) | |
2257 | { | |
2258 | AuthChain *head; | |
2259 | ||
2260 | LogDNSSEC("AuthChainLink: called"); | |
2261 | ||
2262 | head = ae; | |
2263 | // Get to the last element | |
2264 | while (ae->next) | |
2265 | ae = ae->next; | |
2266 | *(dv->actail) = head; // Append this record to tail of auth chain | |
2267 | dv->actail = &(ae->next); // Advance tail pointer | |
2268 | } | |
2269 | ||
2270 | mDNSlocal mDNSBool AuthChainAdd(DNSSECVerifier *dv, RRVerifier *resultKey, RRVerifier *resultRRSig) | |
2271 | { | |
2272 | AuthChain *ae; | |
2273 | rdataDNSKey *key; | |
2274 | mDNSu16 tag; | |
2275 | ||
2276 | if (!dv->rrset || !resultKey || !resultRRSig) | |
2277 | { | |
2278 | LogMsg("AuthChainAdd: ERROR!! input argument NULL"); | |
2279 | return mDNSfalse; | |
2280 | } | |
2281 | ||
2282 | // Unlink resultKey and resultRRSig and store as part of AuthChain | |
2283 | if (!UnlinkRRVerifier(dv, resultKey, RRVS_key)) | |
2284 | { | |
2285 | LogMsg("AuthChainAdd: ERROR!! cannot unlink key"); | |
2286 | return mDNSfalse; | |
2287 | } | |
2288 | if (!UnlinkRRVerifier(dv, resultRRSig, RRVS_rrsig)) | |
2289 | { | |
2290 | LogMsg("AuthChainAdd: ERROR!! cannot unlink rrsig"); | |
2291 | return mDNSfalse; | |
2292 | } | |
2293 | ||
2294 | ae = mDNSPlatformMemAllocate(sizeof(AuthChain)); | |
2295 | if (!ae) | |
2296 | { | |
2297 | LogMsg("AuthChainAdd: AuthChain alloc failure"); | |
2298 | return mDNSfalse; | |
2299 | } | |
2300 | ||
2301 | ae->next = mDNSNULL; | |
2302 | ae->rrset = dv->rrset; | |
2303 | dv->rrset = mDNSNULL; | |
2304 | ||
2305 | ae->rrsig = resultRRSig; | |
2306 | ae->key = resultKey; | |
2307 | ||
2308 | key = (rdataDNSKey *)resultKey->rdata; | |
2309 | tag = (mDNSu16)keytag((mDNSu8 *)key, resultKey->rdlength); | |
2310 | LogDNSSEC("AuthChainAdd: inserting AuthChain element with rrset %##s (%s), DNSKEY tag %d", ae->rrset->name.c, DNSTypeName(ae->rrset->rrtype), tag); | |
2311 | ||
2312 | AuthChainLink(dv, ae); | |
2313 | return mDNStrue; | |
2314 | } | |
2315 | ||
2316 | // RFC 4035: Section 5.3.3 | |
2317 | // | |
2318 | // If the resolver accepts the RRset as authentic, the validator MUST set the TTL of | |
2319 | // the RRSIG RR and each RR in the authenticated RRset to a value no greater than the | |
2320 | // minimum of: | |
2321 | // | |
2322 | // o the RRset's TTL as received in the response; | |
2323 | // | |
2324 | // o the RRSIG RR's TTL as received in the response; | |
2325 | // | |
2326 | // o the value in the RRSIG RR's Original TTL field; and | |
2327 | // | |
2328 | // o the difference of the RRSIG RR's Signature Expiration time and the | |
2329 | // current time. | |
2330 | mDNSlocal void SetTTLRRSet(mDNS *const m, DNSSECVerifier *dv, DNSSECStatus status) | |
2331 | { | |
2332 | DNSQuestion question; | |
2333 | CacheRecord *rr; | |
51601d48 | 2334 | RRVerifier *rrsigv; |
83fb1e36 A |
2335 | rdataRRSig *rrsig; |
2336 | mDNSu32 slot; | |
2337 | CacheGroup *cg; | |
2338 | int sigNameLen, len; | |
2339 | mDNSu8 *ptr; | |
2340 | mDNSu32 rrTTL, rrsigTTL, rrsigOrigTTL, rrsigTimeTTL; | |
2341 | domainname *qname; | |
2342 | mDNSu16 qtype; | |
2343 | CacheRecord *rrsigRR; | |
51601d48 | 2344 | mDNSs32 now; |
83fb1e36 A |
2345 | |
2346 | debugdnssec("SetTTLRRSet called"); | |
2347 | ||
51601d48 | 2348 | if (status == DNSSEC_Insecure || status == DNSSEC_Indeterminate) |
83fb1e36 | 2349 | { |
51601d48 | 2350 | LogDNSSEC("SetTTLRRSET: not setting ttl for status %s", DNSSECStatusName(status)); |
83fb1e36 A |
2351 | return; |
2352 | } | |
2353 | ||
51601d48 A |
2354 | mDNS_Lock(m); |
2355 | now = m->timenow; | |
2356 | mDNS_Unlock(m); | |
2357 | ||
83fb1e36 A |
2358 | mDNSPlatformMemZero(&question, sizeof(DNSQuestion)); |
2359 | rrTTL = rrsigTTL = rrsigOrigTTL = rrsigTimeTTL = 0; | |
2360 | ||
2361 | // 1. Locate the rrset name and get its TTL (take the first one as a representative | |
51601d48 A |
2362 | // of the rrset). Ideally, we should set the TTL on the first validation. Instead, |
2363 | // we do it whenever we validate which happens whenever a ValidationRequired question | |
2364 | // finishes validation. | |
83fb1e36 A |
2365 | qname = &dv->origName; |
2366 | qtype = dv->origType; | |
2367 | ||
2368 | question.ThisQInterval = -1; | |
2369 | InitializeQuestion(m, &question, dv->InterfaceID, qname, qtype, mDNSNULL, mDNSNULL); | |
2370 | slot = HashSlot(&question.qname); | |
2371 | cg = CacheGroupForName(m, slot, question.qnamehash, &question.qname); | |
2372 | ||
51601d48 A |
2373 | if (!cg) |
2374 | { | |
2375 | LogMsg("SetTTLRRSet cg NULL for %##s (%s)", dv->origName.c, DNSTypeName(dv->origType)); | |
2376 | return; | |
2377 | } | |
2378 | ||
83fb1e36 A |
2379 | for (rr = cg->members; rr; rr = rr->next) |
2380 | if (SameNameRecordAnswersQuestion(&rr->resrec, &question)) | |
2381 | { | |
51601d48 A |
2382 | // originalttl is never touched. The actual TTL is derived based on when it was |
2383 | // received. | |
2384 | rrTTL = rr->resrec.rroriginalttl - (now - rr->TimeRcvd)/mDNSPlatformOneSecond; | |
83fb1e36 A |
2385 | break; |
2386 | } | |
2387 | ||
2388 | // Should we check to see if it matches the record in dv->ac->rrset ? | |
2389 | if (!rr) | |
2390 | { | |
2391 | LogMsg("SetTTLRRSet: ERROR!! cannot locate main rrset for %##s (%s)", qname->c, DNSTypeName(qtype)); | |
2392 | return; | |
2393 | } | |
2394 | ||
2395 | ||
2396 | // 2. Get the RRSIG ttl. For NSEC records we need to get the NSEC record's TTL as | |
2397 | // the negative cache record that we created may not be right. | |
2398 | ||
51601d48 A |
2399 | if (dv->ac && dv->ac->rrsig) |
2400 | { | |
2401 | rrsigv = dv->ac->rrsig; | |
2402 | rrsig = (rdataRRSig *)rrsigv->rdata; | |
2403 | sigNameLen = DomainNameLength((domainname *)&rrsig->signerName); | |
2404 | // pointer to signature and the length | |
2405 | ptr = (mDNSu8 *)(rrsigv->rdata + sigNameLen + RRSIG_FIXED_SIZE); | |
2406 | len = rrsigv->rdlength - RRSIG_FIXED_SIZE - sigNameLen; | |
2407 | } | |
2408 | else | |
2409 | { | |
2410 | rrsigv = mDNSNULL; | |
2411 | rrsig = mDNSNULL; | |
2412 | ptr = mDNSNULL; | |
2413 | sigNameLen = len = 0; | |
2414 | } | |
83fb1e36 A |
2415 | |
2416 | rrsigRR = mDNSNULL; | |
51601d48 | 2417 | if (rr->resrec.RecordType == kDNSRecordTypePacketNegative && status == DNSSEC_Secure) |
83fb1e36 A |
2418 | { |
2419 | CacheRecord *ncr; | |
2420 | rrTTL = 0; | |
2421 | for (ncr = rr->nsec; ncr; ncr = ncr->next) | |
2422 | { | |
51601d48 | 2423 | if (ncr->resrec.rrtype == kDNSType_NSEC || ncr->resrec.rrtype == kDNSType_NSEC3) |
83fb1e36 | 2424 | { |
51601d48 | 2425 | rrTTL = ncr->resrec.rroriginalttl - (now - ncr->TimeRcvd)/mDNSPlatformOneSecond; |
83fb1e36 A |
2426 | debugdnssec("SetTTLRRSet: NSEC TTL %u", rrTTL); |
2427 | } | |
2428 | // Note: we can't use dv->origName here as the NSEC record's RRSIG may not match | |
2429 | // the original name | |
51601d48 | 2430 | if (rrsigv && ncr->resrec.rrtype == kDNSType_RRSIG && SameDomainName(ncr->resrec.name, &rrsigv->name)) |
83fb1e36 A |
2431 | { |
2432 | RDataBody2 *rdb = (RDataBody2 *)ncr->resrec.rdata->u.data; | |
2433 | rdataRRSig *sig = (rdataRRSig *)rdb->data; | |
51601d48 | 2434 | if (rrsigv->rdlength != ncr->resrec.rdlength) |
83fb1e36 A |
2435 | { |
2436 | debugdnssec("SetTTLRRSet length mismatch"); | |
2437 | continue; | |
2438 | } | |
51601d48 | 2439 | if (mDNSPlatformMemSame(sig, rrsig, rrsigv->rdlength)) |
83fb1e36 | 2440 | { |
51601d48 A |
2441 | mDNSu32 remain = (now - ncr->TimeRcvd)/mDNSPlatformOneSecond; |
2442 | rrsigTTL = ncr->resrec.rroriginalttl - remain; | |
2443 | rrsigOrigTTL = swap32(rrsig->origTTL) - remain; | |
83fb1e36 A |
2444 | rrsigTimeTTL = swap32(rrsig->sigExpireTime) - swap32(rrsig->sigInceptTime); |
2445 | } | |
2446 | } | |
51601d48 | 2447 | if (rrTTL && (!rrsigv || rrsigTTL)) break; |
83fb1e36 A |
2448 | } |
2449 | } | |
51601d48 | 2450 | else if (rrsigv) |
83fb1e36 A |
2451 | { |
2452 | // Look for the matching RRSIG so that we can get its TTL | |
2453 | for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next) | |
51601d48 | 2454 | if (rr->resrec.rrtype == kDNSType_RRSIG && SameDomainName(rr->resrec.name, &rrsigv->name)) |
83fb1e36 A |
2455 | { |
2456 | RDataBody2 *rdb = (RDataBody2 *)rr->resrec.rdata->u.data; | |
2457 | rdataRRSig *sig = (rdataRRSig *)rdb->data; | |
51601d48 | 2458 | if (rrsigv->rdlength != rr->resrec.rdlength) |
83fb1e36 A |
2459 | { |
2460 | debugdnssec("SetTTLRRSet length mismatch"); | |
2461 | continue; | |
2462 | } | |
51601d48 | 2463 | if (mDNSPlatformMemSame(sig, rrsig, rrsigv->rdlength)) |
83fb1e36 | 2464 | { |
51601d48 A |
2465 | mDNSu32 remain = (now - rr->TimeRcvd)/mDNSPlatformOneSecond; |
2466 | rrsigTTL = rr->resrec.rroriginalttl - remain; | |
2467 | rrsigOrigTTL = swap32(rrsig->origTTL) - remain; | |
83fb1e36 A |
2468 | rrsigTimeTTL = swap32(rrsig->sigExpireTime) - swap32(rrsig->sigInceptTime); |
2469 | rrsigRR = rr; | |
2470 | break; | |
2471 | } | |
2472 | } | |
2473 | } | |
2474 | ||
51601d48 A |
2475 | // It is possible that there are no RRSIGs and in that case it is not an error |
2476 | // to find the rrsigTTL. | |
2477 | if (!rrTTL || (rrsigv && (!rrsigTTL || !rrsigOrigTTL || !rrsigTimeTTL))) | |
83fb1e36 | 2478 | { |
51601d48 | 2479 | LogDNSSEC("SetTTLRRSet: ERROR!! Bad TTL rrtl %u, rrsigTTL %u, rrsigOrigTTL %u, rrsigTimeTTL %u for %##s (%s)", |
83fb1e36 A |
2480 | rrTTL, rrsigTTL, rrsigOrigTTL, rrsigTimeTTL, qname->c, DNSTypeName(qtype)); |
2481 | return; | |
2482 | } | |
51601d48 A |
2483 | LogDNSSEC("SetTTLRRSet: TTL rrtl %u, rrsigTTL %u, rrsigOrigTTL %u, rrsigTimeTTL %u for %##s (%s)", |
2484 | rrTTL, rrsigTTL, rrsigOrigTTL, rrsigTimeTTL, qname->c, DNSTypeName(qtype)); | |
2485 | ||
2486 | if (status == DNSSEC_Bogus) | |
83fb1e36 | 2487 | { |
51601d48 A |
2488 | rrTTL = RR_BOGUS_TTL; |
2489 | LogDNSSEC("SetTTLRRSet: setting to bogus TTL %d", rrTTL); | |
83fb1e36 A |
2490 | } |
2491 | ||
51601d48 A |
2492 | if (rrsigv) |
2493 | { | |
2494 | if (rrsigTTL < rrTTL) | |
2495 | rrTTL = rrsigTTL; | |
2496 | if (rrsigOrigTTL < rrTTL) | |
2497 | rrTTL = rrsigOrigTTL; | |
2498 | if (rrsigTimeTTL < rrTTL) | |
2499 | rrTTL = rrsigTimeTTL; | |
2500 | } | |
83fb1e36 A |
2501 | |
2502 | // Set the rrsig's TTL. For NSEC records, rrsigRR is NULL which means it expires when | |
2503 | // the negative cache record expires. | |
2504 | if (rrsigRR) | |
51601d48 | 2505 | { |
83fb1e36 | 2506 | rrsigRR->resrec.rroriginalttl = rrTTL; |
51601d48 A |
2507 | rrsigRR->TimeRcvd = now; |
2508 | rrsigRR->UnansweredQueries = 0; | |
2509 | } | |
83fb1e36 A |
2510 | |
2511 | // Find the RRset and set its TTL | |
2512 | for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next) | |
2513 | { | |
2514 | if (SameNameRecordAnswersQuestion(&rr->resrec, &question)) | |
2515 | { | |
2516 | LogDNSSEC("SetTTLRRSet: Setting the TTL %d for %s, question %##s (%s)", rrTTL, CRDisplayString(m, rr), | |
2517 | question.qname.c, DNSTypeName(rr->resrec.rrtype)); | |
2518 | rr->resrec.rroriginalttl = rrTTL; | |
51601d48 A |
2519 | rr->TimeRcvd = now; |
2520 | rr->UnansweredQueries = 0; | |
83fb1e36 A |
2521 | SetNextCacheCheckTimeForRecord(m, rr); |
2522 | } | |
2523 | } | |
2524 | } | |
2525 | ||
2526 | mDNSlocal void FinishDNSSECVerification(mDNS *const m, DNSSECVerifier *dv) | |
2527 | { | |
2528 | RRVerifier *resultKey; | |
2529 | RRVerifier *resultRRSig; | |
2530 | ||
2531 | LogDNSSEC("FinishDNSSECVerification: all rdata sets available for sig verification for %##s (%s)", | |
2532 | dv->origName.c, DNSTypeName(dv->origType)); | |
2533 | ||
2534 | mDNS_StopQuery(m, &dv->q); | |
2535 | if (ValidateSignature(dv, &resultKey, &resultRRSig) == mStatus_NoError) | |
2536 | { | |
2537 | rdataDNSKey *key; | |
2538 | mDNSu16 tag; | |
2539 | key = (rdataDNSKey *)resultKey->rdata; | |
2540 | tag = (mDNSu16)keytag((mDNSu8 *)key, resultKey->rdlength); | |
2541 | ||
2542 | LogDNSSEC("FinishDNSSECVerification: RRSIG validated by DNSKEY tag %d, %##s (%s)", tag, dv->rrset->name.c, | |
2543 | DNSTypeName(dv->rrset->rrtype)); | |
2544 | ||
2545 | if (TrustedKey(m, dv) == mStatus_NoError) | |
2546 | { | |
2547 | // Need to call this after we called TrustedKey, as AuthChainAdd | |
2548 | // unlinks the resultKey and resultRRSig | |
2549 | if (!AuthChainAdd(dv, resultKey, resultRRSig)) | |
2550 | { | |
51601d48 | 2551 | dv->DVCallback(m, dv, DNSSEC_Bogus); |
83fb1e36 A |
2552 | return; |
2553 | } | |
2554 | // The callback will be called when NSEC verification is done. | |
2555 | if ((dv->flags & WILDCARD_PROVES_ANSWER_EXPANDED)) | |
2556 | { | |
2557 | WildcardAnswerProof(m, dv); | |
2558 | return; | |
2559 | } | |
2560 | else | |
2561 | { | |
2562 | dv->DVCallback(m, dv, DNSSEC_Secure); | |
2563 | return; | |
2564 | } | |
2565 | } | |
2566 | if (!ValidateDS(dv)) | |
2567 | { | |
2568 | // Need to call this after we called ValidateDS, as AuthChainAdd | |
2569 | // unlinks the resultKey and resultRRSig | |
2570 | if (!AuthChainAdd(dv, resultKey, resultRRSig)) | |
2571 | { | |
51601d48 | 2572 | dv->DVCallback(m, dv, DNSSEC_Bogus); |
83fb1e36 A |
2573 | return; |
2574 | } | |
2575 | FreeDNSSECVerifierRRSets(dv); | |
2576 | dv->recursed++; | |
2577 | if (dv->recursed < MAX_RECURSE_COUNT) | |
2578 | { | |
2579 | LogDNSSEC("FinishDNSSECVerification: Recursion level %d for %##s (%s)", dv->recursed, dv->origName.c, | |
2580 | DNSTypeName(dv->origType)); | |
2581 | VerifySignature(m, dv, &dv->q); | |
2582 | return; | |
2583 | } | |
2584 | } | |
2585 | else | |
2586 | { | |
2587 | LogDNSSEC("FinishDNSSECVerification: ValidateDS failed %##s (%s)", dv->rrset->name.c, DNSTypeName(dv->rrset->rrtype)); | |
51601d48 | 2588 | dv->DVCallback(m, dv, DNSSEC_Bogus); |
83fb1e36 A |
2589 | return; |
2590 | } | |
2591 | } | |
2592 | else | |
2593 | { | |
2594 | LogDNSSEC("FinishDNSSECVerification: Could not validate the rrset %##s (%s)", dv->origName.c, DNSTypeName(dv->origType)); | |
51601d48 | 2595 | dv->DVCallback(m, dv, DNSSEC_Bogus); |
83fb1e36 A |
2596 | return; |
2597 | } | |
2598 | } | |
2599 | ||
51601d48 | 2600 | mDNSexport void StartDNSSECVerification(mDNS *const m, void *context) |
83fb1e36 A |
2601 | { |
2602 | mDNSBool done; | |
51601d48 | 2603 | DNSSECVerifier *dv = (DNSSECVerifier *)context; |
83fb1e36 A |
2604 | |
2605 | done = GetAllRRSetsForVerification(m, dv); | |
2606 | if (done) | |
2607 | { | |
2608 | if (dv->next != RRVS_done) | |
2609 | LogMsg("StartDNSSECVerification: ERROR!! dv->next is not done"); | |
2610 | else | |
2611 | LogDNSSEC("StartDNSSECVerification: all rdata sets available for sig verification"); | |
2612 | FinishDNSSECVerification(m, dv); | |
2613 | return; | |
2614 | } | |
2615 | else debugdnssec("StartDNSSECVerification: all rdata sets not available for sig verification next %d", dv->next); | |
2616 | } | |
2617 | ||
51601d48 | 2618 | mDNSexport char *DNSSECStatusName(DNSSECStatus status) |
83fb1e36 A |
2619 | { |
2620 | switch (status) | |
2621 | { | |
2622 | case DNSSEC_Secure: return "Secure"; | |
2623 | case DNSSEC_Insecure: return "Insecure"; | |
2624 | case DNSSEC_Indeterminate: return "Indeterminate"; | |
2625 | case DNSSEC_Bogus: return "Bogus"; | |
2626 | default: return "Invalid"; | |
2627 | } | |
2628 | } | |
2629 | ||
2630 | // We could not use GenerateNegativeResponse as it assumes m->CurrentQuestion to be set. Even if | |
2631 | // we change that, we needs to fix its callers and so on. It is much simpler to call the callback. | |
51601d48 | 2632 | mDNSlocal void DeliverDNSSECStatus(mDNS *const m, DNSSECVerifier *dv, ResourceRecord *answer, DNSSECStatus status) |
83fb1e36 A |
2633 | { |
2634 | ||
2635 | // Can't use m->CurrentQuestion as it may already be in use | |
2636 | if (m->ValidationQuestion) | |
2637 | LogMsg("DeliverDNSSECStatus: ERROR!! m->ValidationQuestion already set: %##s (%s)", | |
2638 | m->ValidationQuestion->qname.c, DNSTypeName(m->ValidationQuestion->qtype)); | |
2639 | ||
51601d48 A |
2640 | BumpDNSSECStats(m, kStatsActionSet, kStatsTypeStatus, status); |
2641 | BumpDNSSECStats(m, kStatsActionSet, kStatsTypeExtraPackets, dv->NumPackets); | |
2642 | mDNS_Lock(m); | |
2643 | BumpDNSSECStats(m, kStatsActionSet, kStatsTypeLatency, m->timenow - dv->StartTime); | |
2644 | mDNS_Unlock(m); | |
2645 | ||
83fb1e36 A |
2646 | m->ValidationQuestion = m->Questions; |
2647 | while (m->ValidationQuestion && m->ValidationQuestion != m->NewQuestions) | |
2648 | { | |
2649 | DNSQuestion *q = m->ValidationQuestion; | |
2650 | ||
2651 | if (q->ValidatingResponse || !q->ValidationRequired || | |
2652 | (q->ValidationState != DNSSECValInProgress) || !ResourceRecordAnswersQuestion(answer, q)) | |
2653 | { | |
2654 | m->ValidationQuestion = q->next; | |
2655 | continue; | |
2656 | } | |
2657 | ||
2658 | q->ValidationState = DNSSECValDone; | |
2659 | q->ValidationStatus = status; | |
2660 | ||
2661 | MakeNegativeCacheRecord(m, &largerec.r, &q->qname, q->qnamehash, q->qtype, q->qclass, 60, mDNSInterface_Any, mDNSNULL); | |
2662 | if (q->qtype == answer->rrtype || status != DNSSEC_Secure) | |
2663 | { | |
2664 | LogDNSSEC("DeliverDNSSECStatus: Generating dnssec status %s for %##s (%s)", DNSSECStatusName(status), | |
2665 | q->qname.c, DNSTypeName(q->qtype)); | |
51601d48 A |
2666 | if (q->QuestionCallback) |
2667 | { | |
2668 | if (q->DNSSECAuthInfo) | |
2669 | FreeDNSSECAuthChainInfo((AuthChain *)q->DNSSECAuthInfo); | |
2670 | q->DNSSECAuthInfo = AuthChainCopy(dv->ac); | |
2671 | q->DAIFreeCallback = FreeAuthChain; | |
2672 | q->QuestionCallback(m, q, &largerec.r.resrec, QC_dnssec); | |
2673 | } | |
83fb1e36 | 2674 | } |
51601d48 | 2675 | else if (FollowCNAME(q, answer, QC_add)) |
83fb1e36 A |
2676 | { |
2677 | LogDNSSEC("DeliverDNSSECStatus: Following CNAME dnssec status %s for %##s (%s)", DNSSECStatusName(status), | |
2678 | q->qname.c, DNSTypeName(q->qtype)); | |
2679 | mDNS_Lock(m); | |
2680 | AnswerQuestionByFollowingCNAME(m, q, answer); | |
2681 | mDNS_Unlock(m); | |
2682 | } | |
2683 | ||
2684 | if (m->ValidationQuestion == q) // If m->ValidationQuestion was not auto-advanced, do it ourselves now | |
2685 | m->ValidationQuestion = q->next; | |
2686 | } | |
2687 | m->ValidationQuestion = mDNSNULL; | |
2688 | } | |
2689 | ||
51601d48 A |
2690 | // There is no work to be done if we could not validate DNSSEC (as the actual response for |
2691 | // the query has already been delivered) except in the case of CNAMEs where we did not follow | |
2692 | // CNAMEs until we finished the DNSSEC processing. | |
2693 | mDNSlocal void DNSSECNoResponse(mDNS *const m, DNSSECVerifier *dv) | |
83fb1e36 | 2694 | { |
83fb1e36 A |
2695 | CacheGroup *cg; |
2696 | CacheRecord *cr; | |
2697 | mDNSu32 slot, namehash; | |
51601d48 A |
2698 | ResourceRecord *answer = mDNSNULL; |
2699 | ||
2700 | LogDNSSEC("DNSSECNoResponse: called"); | |
2701 | ||
2702 | if (dv->ValidationRequired != DNSSEC_VALIDATION_SECURE_OPTIONAL) | |
2703 | { | |
2704 | LogMsg("DNSSECNoResponse: ERROR!! ValidationRequired incorrect %d", dv->ValidationRequired); | |
2705 | return; | |
2706 | } | |
2707 | ||
2708 | BumpDNSSECStats(m, kStatsActionSet, kStatsTypeStatus, DNSSEC_NoResponse); | |
2709 | ||
2710 | slot = HashSlot(&dv->origName); | |
2711 | namehash = DomainNameHashValue(&dv->origName); | |
2712 | ||
2713 | cg = CacheGroupForName(m, (const mDNSu32)slot, namehash, &dv->origName); | |
2714 | if (!cg) | |
2715 | { | |
2716 | LogDNSSEC("DNSSECNoResponse: cg NULL for %##s (%s)", dv->origName.c, DNSTypeName(dv->origType)); | |
2717 | goto done; | |
2718 | } | |
2719 | ||
2720 | InitializeQuestion(m, &dv->q, dv->InterfaceID, &dv->origName, dv->origType, mDNSNULL, mDNSNULL); | |
2721 | ||
2722 | // We don't have to reset ValidatingResponse (unlike in DeliverDNSSECStatus) as there are no | |
2723 | // RRSIGs that can match the original question | |
2724 | for (cr = cg->members; cr; cr = cr->next) | |
2725 | { | |
2726 | if (SameNameRecordAnswersQuestion(&cr->resrec, &dv->q)) | |
2727 | { | |
2728 | answer = &cr->resrec; | |
2729 | break; | |
2730 | } | |
2731 | } | |
2732 | ||
2733 | // It is not an error for things to disappear underneath | |
2734 | if (!answer) | |
2735 | { | |
2736 | LogDNSSEC("DNSSECNoResponse: answer NULL for %##s, %s", dv->origName.c, DNSTypeName(dv->origType)); | |
2737 | goto done; | |
2738 | } | |
2739 | if (answer->rrtype == kDNSType_RRSIG) | |
2740 | { | |
2741 | LogDNSSEC("DNSSECNoResponse: RRSIG present for %##s, %s", dv->origName.c, DNSTypeName(dv->origType)); | |
2742 | goto done; | |
2743 | } | |
2744 | ||
2745 | // Can't use m->CurrentQuestion as it may already be in use | |
2746 | if (m->ValidationQuestion) | |
2747 | LogMsg("DNSSECNoResponse: ERROR!! m->ValidationQuestion already set: %##s (%s)", | |
2748 | m->ValidationQuestion->qname.c, DNSTypeName(m->ValidationQuestion->qtype)); | |
2749 | ||
2750 | m->ValidationQuestion = m->Questions; | |
2751 | while (m->ValidationQuestion && m->ValidationQuestion != m->NewQuestions) | |
2752 | { | |
2753 | DNSQuestion *q = m->ValidationQuestion; | |
2754 | ||
2755 | if (q->ValidatingResponse || !q->ValidationRequired || | |
2756 | (q->ValidationState != DNSSECValInProgress) || !ResourceRecordAnswersQuestion(answer, q)) | |
2757 | { | |
2758 | m->ValidationQuestion = q->next; | |
2759 | continue; | |
2760 | } | |
2761 | ||
2762 | // If we could not validate e.g., zone was not signed or bad delegation etc., | |
2763 | // disable validation. Ideally, for long outstanding questions, we should try again when | |
2764 | // we switch networks. But for now, keep it simple. | |
2765 | // | |
2766 | // Note: If we followed a CNAME with no dnssec protection, it is even more important that | |
2767 | // we disable validation as we don't want to deliver a "secure" dnssec response later e.g., | |
2768 | // it is possible that the CNAME is not secure but the address records are secure. In this | |
2769 | // case, we don't want to deliver the secure response later as we followed a CNAME that was | |
2770 | // not protected with DNSSEC. | |
2771 | ||
2772 | q->ValidationRequired = 0; | |
2773 | q->ValidationState = DNSSECValNotRequired; | |
2774 | ||
2775 | if (FollowCNAME(q, answer, QC_add)) | |
2776 | { | |
2777 | LogDNSSEC("DNSSECNoResponse: Following CNAME for %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); | |
2778 | ||
2779 | mDNS_Lock(m); | |
2780 | AnswerQuestionByFollowingCNAME(m, q, answer); | |
2781 | mDNS_Unlock(m); | |
2782 | } | |
2783 | ||
2784 | if (m->ValidationQuestion == q) // If m->ValidationQuestion was not auto-advanced, do it ourselves now | |
2785 | m->ValidationQuestion = q->next; | |
2786 | } | |
2787 | m->ValidationQuestion = mDNSNULL; | |
2788 | ||
2789 | done: | |
2790 | FreeDNSSECVerifier(m, dv); | |
2791 | } | |
2792 | ||
2793 | mDNSlocal void DNSSECPositiveValidationCB(mDNS *const m, DNSSECVerifier *dv, CacheGroup *cg, ResourceRecord *answer, DNSSECStatus status) | |
2794 | { | |
2795 | RRVerifier *rrset; | |
2796 | RRVerifier *rv; | |
2797 | CacheRecord *cr; | |
83fb1e36 A |
2798 | mDNSu16 rrtype, rrclass; |
2799 | CacheRecord *const lrr = &largerec.r; | |
83fb1e36 | 2800 | |
51601d48 | 2801 | LogDNSSEC("DNSSECPositiveValidationCB: called %s for %##s (%s)", DNSSECStatusName(status), dv->origName.c, DNSTypeName(dv->origType)); |
83fb1e36 A |
2802 | |
2803 | // | |
2804 | // 1. Check to see if the rrset that was validated is the same as in cache. If they are not same, | |
2805 | // this validation result is not valid. When the rrset changed while the validation was in | |
2806 | // progress, the act of delivering the changed rrset again should have kicked off another | |
2807 | // verification. | |
2808 | // | |
2809 | // 2. Walk the question list to find the matching question. The original question that started | |
2810 | // the DNSSEC verification may or may not be there. As long as there is a matching question | |
2811 | // and waiting for the response, deliver the response. | |
2812 | // | |
2813 | // 3. If we are answering with CNAME, it is time to follow the CNAME if the response is secure | |
2814 | ||
51601d48 | 2815 | if (!dv->ac || status == DNSSEC_Insecure) |
83fb1e36 | 2816 | { |
51601d48 A |
2817 | // For Insecure status, the auth chain contains information about the trust |
2818 | // chain starting from the known trust anchor. The rrsets are not related to | |
2819 | // the origName like in Bogus or Secure. | |
2820 | if (!answer) | |
2821 | LogMsg("DNSSECPositiveValidationCB: ERROR: answer NULL"); | |
83fb1e36 A |
2822 | } |
2823 | else | |
2824 | { | |
2825 | if (!dv->ac->rrset) | |
2826 | { | |
2827 | LogMsg("DNSSECPositiveValidationCB: ERROR!! Validated RRSET NULL"); | |
2828 | goto done; | |
2829 | } | |
2830 | ||
2831 | rrset = dv->ac->rrset; | |
2832 | rrtype = rrset->rrtype; | |
2833 | rrclass = rrset->rrclass; | |
2834 | ||
2835 | lrr->resrec.name = &largerec.namestorage; | |
2836 | ||
2837 | for (rv = dv->ac->rrset; rv; rv = rv->next) | |
2838 | rv->found = 0; | |
2839 | ||
2840 | // Check to see if we can find all the elements in the rrset | |
2841 | for (cr = cg ? cg->members : mDNSNULL; cr; cr = cr->next) | |
2842 | { | |
2843 | if (cr->resrec.rrtype == rrtype && cr->resrec.rrclass == rrclass) | |
2844 | { | |
2845 | for (rv = dv->ac->rrset; rv; rv = rv->next) | |
2846 | { | |
2847 | if (rv->rdlength == cr->resrec.rdlength && rv->rdatahash == cr->resrec.rdatahash) | |
2848 | { | |
2849 | lrr->resrec.namehash = rv->namehash; | |
2850 | lrr->resrec.rrtype = rv->rrtype; | |
2851 | lrr->resrec.rrclass = rv->rrclass; | |
2852 | lrr->resrec.rdata = (RData*)&lrr->smallrdatastorage; | |
2853 | lrr->resrec.rdata->MaxRDLength = MaximumRDSize; | |
2854 | ||
2855 | // Convert the "rdata" to a suitable form before we can call SameRDataBody which expects | |
2856 | // some of the resource records in host order and also domainnames fully expanded. We | |
2857 | // converted the resource records into network order for verification purpose and hence | |
2858 | // need to convert them back again before comparing them. | |
2859 | if (!SetRData(mDNSNULL, rv->rdata, rv->rdata + rv->rdlength, &largerec, rv->rdlength)) | |
2860 | { | |
2861 | LogMsg("DNSSECPositiveValidationCB: SetRData failed for %##s (%s)", rv->name.c, DNSTypeName(rv->rrtype)); | |
2862 | } | |
2863 | else if (SameRDataBody(&cr->resrec, &lrr->resrec.rdata->u, SameDomainName)) | |
2864 | { | |
2865 | answer = &cr->resrec; | |
2866 | rv->found = 1; | |
2867 | break; | |
2868 | } | |
2869 | } | |
2870 | } | |
2871 | if (!rv) | |
2872 | { | |
2873 | // The validated rrset does not have the element in the cache, re-validate | |
2874 | LogDNSSEC("DNSSECPositiveValidationCB: CacheRecord %s, not found in the validated set", CRDisplayString(m, cr)); | |
2875 | goto done; | |
2876 | } | |
2877 | } | |
2878 | } | |
2879 | // Check to see if we have elements that were not in the cache | |
2880 | for (rv = dv->ac->rrset; rv; rv = rv->next) | |
2881 | { | |
2882 | if (!rv->found) | |
2883 | { | |
2884 | // We had more elements in the validated set, re-validate | |
2885 | LogDNSSEC("DNSSECPositiveValidationCB: Record %##s (%s) not found in the cache", rv->name.c, DNSTypeName(rv->rrtype)); | |
2886 | goto done; | |
2887 | } | |
2888 | } | |
2889 | } | |
2890 | ||
2891 | // It is not an error for things to disappear underneath | |
2892 | if (!answer) | |
2893 | { | |
51601d48 | 2894 | LogDNSSEC("DNSSECPositiveValidationCB: answer NULL for %##s, %s", dv->origName.c, DNSTypeName(dv->origType)); |
83fb1e36 A |
2895 | goto done; |
2896 | } | |
2897 | ||
51601d48 | 2898 | DeliverDNSSECStatus(m, dv, answer, status); |
83fb1e36 A |
2899 | SetTTLRRSet(m, dv, status); |
2900 | ||
2901 | done: | |
2902 | FreeDNSSECVerifier(m, dv); | |
2903 | } | |
2904 | ||
51601d48 | 2905 | mDNSlocal void DNSSECNegativeValidationCB(mDNS *const m, DNSSECVerifier *dv, CacheGroup *cg, ResourceRecord *answer, DNSSECStatus status) |
83fb1e36 A |
2906 | { |
2907 | RRVerifier *rv; | |
83fb1e36 | 2908 | CacheRecord *cr; |
83fb1e36 | 2909 | mDNSu16 rrtype, rrclass; |
83fb1e36 A |
2910 | AuthChain *ac; |
2911 | ||
51601d48 A |
2912 | LogDNSSEC("DNSSECNegativeValidationCB: called %s for %##s (%s)", DNSSECStatusName(status), dv->origName.c, DNSTypeName(dv->origType)); |
2913 | ||
2914 | if (dv->parent) | |
2915 | { | |
2916 | // When NSEC/NSEC3s validation is completed, it calls the parent's DVCallback with the | |
2917 | // parent DNSSECVerifier which is the original one that started the verification. It itself | |
2918 | // should not have a parent. If the NSEC/NSEC3 validation results in another NSEC/NSEC3 | |
2919 | // validation, it should chain up via the dv->parent all the way to the top. | |
2920 | LogMsg("DNSSECNegativeValidationCB: ERROR!! dv->parent is set for %##s (%s)", dv->origName.c, DNSTypeName(dv->origType)); | |
2921 | goto done; | |
2922 | } | |
83fb1e36 | 2923 | |
51601d48 A |
2924 | // 1. Locate the negative cache record and check the cached NSEC/NSEC3 records to see if it matches the |
2925 | // NSEC/NSEC3s that were valiated. If the cached NSEC/NSEC3s changed while the validation was in progress, | |
83fb1e36 A |
2926 | // we ignore the validation results. |
2927 | // | |
2928 | // 2. Walk the question list to find the matching question. The original question that started | |
2929 | // the DNSSEC verification may or may not be there. As long as there is a matching question | |
2930 | // and waiting for the response, deliver the response. | |
2931 | // | |
51601d48 | 2932 | if (!dv->ac || status == DNSSEC_Insecure) |
83fb1e36 | 2933 | { |
51601d48 A |
2934 | // For Insecure status, the auth chain contains information about the trust |
2935 | // chain starting from the known trust anchor. The rrsets are not related to | |
2936 | // the origName like in Bogus or Secure. | |
2937 | if (!answer) | |
2938 | LogMsg("DNSSECNegativeValidationCB: ERROR: answer NULL"); | |
83fb1e36 | 2939 | } |
51601d48 | 2940 | else |
83fb1e36 | 2941 | { |
51601d48 | 2942 | if (!dv->ac->rrset) |
83fb1e36 A |
2943 | { |
2944 | LogMsg("DNSSECNegativeValidationCB: ERROR!! Validated RRSET NULL"); | |
2945 | goto done; | |
2946 | } | |
2947 | ||
2948 | rrtype = dv->origType; | |
2949 | rrclass = dv->ac->rrset->rrclass; | |
2950 | ||
2951 | for (ac = dv->ac; ac; ac = ac->next) | |
2952 | { | |
2953 | for (rv = ac->rrset; rv; rv = rv->next) | |
2954 | { | |
51601d48 A |
2955 | if (rv->rrtype == kDNSType_NSEC || rv->rrtype == kDNSType_NSEC3) |
2956 | { | |
2957 | LogDNSSEC("DNSSECNegativeValidationCB: Record %p %##s (%s) marking zero", rv, rv->name.c, DNSTypeName(rv->rrtype)); | |
83fb1e36 | 2958 | rv->found = 0; |
51601d48 | 2959 | } |
83fb1e36 A |
2960 | } |
2961 | } | |
2962 | ||
2963 | // Check to see if we can find all the elements in the rrset | |
2964 | for (cr = cg->members; cr; cr = cr->next) | |
2965 | { | |
2966 | if (cr->resrec.RecordType == kDNSRecordTypePacketNegative && | |
2967 | cr->resrec.rrtype == rrtype && cr->resrec.rrclass == rrclass) | |
2968 | { | |
2969 | CacheRecord *ncr; | |
2970 | for (ncr = cr->nsec; ncr; ncr = ncr->next) | |
2971 | { | |
2972 | // We have RRSIGs for the NSECs cached there too | |
51601d48 | 2973 | if (ncr->resrec.rrtype != kDNSType_NSEC && ncr->resrec.rrtype != kDNSType_NSEC3) |
83fb1e36 A |
2974 | continue; |
2975 | for (ac = dv->ac; ac; ac = ac->next) | |
2976 | { | |
2977 | for (rv = ac->rrset; rv; rv = rv->next) | |
2978 | { | |
51601d48 | 2979 | if ((rv->rrtype == kDNSType_NSEC || rv->rrtype == kDNSType_NSEC3) && rv->rdlength == ncr->resrec.rdlength && |
83fb1e36 A |
2980 | rv->rdatahash == ncr->resrec.rdatahash) |
2981 | { | |
2982 | if (SameDomainName(ncr->resrec.name, &rv->name) && | |
2983 | SameRDataBody(&ncr->resrec, (const RDataBody *)rv->rdata, SameDomainName)) | |
2984 | { | |
51601d48 | 2985 | LogDNSSEC("DNSSECNegativeValidationCB: Record %p %##s (%s) marking one", rv, rv->name.c, DNSTypeName(rv->rrtype)); |
83fb1e36 A |
2986 | answer = &cr->resrec; |
2987 | rv->found = 1; | |
2988 | break; | |
2989 | } | |
2990 | } | |
2991 | } | |
2992 | if (rv) | |
2993 | break; | |
2994 | } | |
2995 | } | |
2996 | if (!rv) | |
2997 | { | |
2998 | // The validated rrset does not have the element in the cache, re-validate | |
2999 | LogDNSSEC("DNSSECNegativeValidationCB: CacheRecord %s, not found in the validated set", CRDisplayString(m, cr)); | |
3000 | goto done; | |
3001 | } | |
3002 | } | |
3003 | } | |
3004 | // Check to see if we have elements that were not in the cache | |
3005 | for (ac = dv->ac; ac; ac = ac->next) | |
3006 | { | |
3007 | for (rv = ac->rrset; rv; rv = rv->next) | |
3008 | { | |
51601d48 | 3009 | if (rv->rrtype == kDNSType_NSEC || rv->rrtype == kDNSType_NSEC3) |
83fb1e36 A |
3010 | { |
3011 | if (!rv->found) | |
3012 | { | |
3013 | // We had more elements in the validated set, re-validate | |
51601d48 | 3014 | LogDNSSEC("DNSSECNegativeValidationCB: Record %p %##s (%s) not found in the cache", rv, rv->name.c, DNSTypeName(rv->rrtype)); |
83fb1e36 A |
3015 | goto done; |
3016 | } | |
3017 | rv->found = 0; | |
3018 | } | |
3019 | } | |
3020 | } | |
3021 | } | |
3022 | ||
3023 | // It is not an error for things to disappear underneath | |
3024 | if (!answer) | |
3025 | { | |
51601d48 | 3026 | LogDNSSEC("DNSSECNegativeValidationCB: answer NULL for %##s, %s", dv->origName.c, DNSTypeName(dv->origType)); |
83fb1e36 A |
3027 | goto done; |
3028 | } | |
3029 | ||
51601d48 | 3030 | DeliverDNSSECStatus(m, dv, answer, status); |
83fb1e36 A |
3031 | SetTTLRRSet(m, dv, status); |
3032 | ||
3033 | done: | |
3034 | FreeDNSSECVerifier(m, dv); | |
3035 | } | |
3036 | ||
51601d48 A |
3037 | mDNSlocal void DNSSECValidationCB(mDNS *const m, DNSSECVerifier *dv, DNSSECStatus status) |
3038 | { | |
3039 | mDNSu32 slot, namehash; | |
3040 | CacheGroup *cg; | |
3041 | CacheRecord *cr; | |
3042 | ||
3043 | LogDNSSEC("DNSSECValidationCB: called %s for %##s (%s)", DNSSECStatusName(status), dv->origName.c, DNSTypeName(dv->origType)); | |
3044 | ||
3045 | // Currently, if we receive anything other than secure, we abort DNSSEC validation for | |
3046 | // the optional case. | |
3047 | if (dv->ValidationRequired == DNSSEC_VALIDATION_SECURE_OPTIONAL && status != DNSSEC_Secure) | |
3048 | { | |
3049 | DNSSECNoResponse(m, dv); | |
3050 | return; | |
3051 | } | |
3052 | ||
3053 | if (dv->ValidationRequired == DNSSEC_VALIDATION_SECURE && !dv->InsecureProofDone && status == DNSSEC_Bogus) | |
3054 | { | |
3055 | dv->InsecureProofDone = 1; | |
3056 | ProveInsecure(m, dv, mDNSNULL, mDNSNULL); | |
3057 | return; | |
3058 | } | |
3059 | slot = HashSlot(&dv->origName); | |
3060 | namehash = DomainNameHashValue(&dv->origName); | |
3061 | ||
3062 | cg = CacheGroupForName(m, (const mDNSu32)slot, namehash, &dv->origName); | |
3063 | if (!cg) | |
3064 | { | |
3065 | LogDNSSEC("DNSSECValidationCB: cg NULL for %##s (%s)", dv->origName.c, DNSTypeName(dv->origType)); | |
3066 | FreeDNSSECVerifier(m, dv); | |
3067 | return; | |
3068 | } | |
3069 | InitializeQuestion(m, &dv->q, dv->InterfaceID, &dv->origName, dv->origType, mDNSNULL, mDNSNULL); | |
3070 | // Need to be reset ValidatingResponse as we are looking for the cache record that would answer | |
3071 | // the original question | |
3072 | dv->q.ValidatingResponse = mDNSfalse; | |
3073 | for (cr = cg->members; cr; cr = cr->next) | |
3074 | { | |
3075 | if (SameNameRecordAnswersQuestion(&cr->resrec, &dv->q)) | |
3076 | { | |
3077 | if (cr->resrec.RecordType == kDNSRecordTypePacketNegative) | |
3078 | DNSSECNegativeValidationCB(m, dv, cg, &cr->resrec, status); | |
3079 | else | |
3080 | DNSSECPositiveValidationCB(m, dv, cg, &cr->resrec, status); | |
3081 | return; | |
3082 | } | |
3083 | } | |
3084 | } | |
3085 | ||
83fb1e36 A |
3086 | mDNSexport void VerifySignature(mDNS *const m, DNSSECVerifier *dv, DNSQuestion *q) |
3087 | { | |
3088 | mDNSu32 slot = HashSlot(&q->qname); | |
3089 | CacheGroup *const cg = CacheGroupForName(m, slot, q->qnamehash, &q->qname); | |
3090 | CacheRecord *rr; | |
51601d48 A |
3091 | mDNSBool first = mDNSfalse; |
3092 | static mDNSBool TrustAnchorsUpdated = mDNSfalse; | |
83fb1e36 A |
3093 | |
3094 | LogDNSSEC("VerifySignature called for %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); | |
51601d48 A |
3095 | if (!TrustAnchorsUpdated) |
3096 | { | |
3097 | TrustAnchorsUpdated = mDNStrue; | |
3098 | UpdateTrustAnchors(m); | |
3099 | } | |
83fb1e36 A |
3100 | if (!dv) |
3101 | { | |
51601d48 | 3102 | first = mDNStrue; |
83fb1e36 A |
3103 | if (!q->qDNSServer || q->qDNSServer->cellIntf) |
3104 | { | |
3105 | LogDNSSEC("VerifySignature: Disabled"); | |
3106 | return; | |
3107 | } | |
3108 | // We assume that the verifier's question has been initialized here so that ValidateWithNSECS below | |
3109 | // knows what it has prove the non-existence of. | |
51601d48 A |
3110 | dv = AllocateDNSSECVerifier(m, &q->qname, q->qtype, q->InterfaceID, q->ValidationRequired, DNSSECValidationCB, VerifySigCallback); |
3111 | if (!dv) | |
3112 | { | |
3113 | LogMsg("VerifySignature: ERROR!! memory alloc failed"); | |
3114 | return; | |
3115 | } | |
83fb1e36 A |
3116 | } |
3117 | ||
3118 | // If we find a CNAME response to the question, remember what qtype | |
3119 | // caused the CNAME response. origType is not sufficient as we | |
3120 | // recursively validate the response and origType is initialized above | |
3121 | // the first time this function is called. | |
3122 | dv->currQtype = q->qtype; | |
3123 | ||
3124 | // Walk the cache and get all the rrsets for verification. | |
3125 | for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next) | |
3126 | if (SameNameRecordAnswersQuestion(&rr->resrec, q)) | |
3127 | { | |
3128 | // We also get called for RRSIGs which matches qtype. We don't need that here as we are | |
3129 | // building rrset for matching q->qname. Checking for RRSIG type is important as otherwise | |
3130 | // we would miss the CNAME answering any qtype. | |
3131 | if (rr->resrec.rrtype == kDNSType_RRSIG && rr->resrec.rrtype != q->qtype) | |
3132 | { | |
3133 | LogDNSSEC("VerifySignature: Question %##s (%s) answered with RRSIG record %s, not using it", q->qname.c, DNSTypeName(q->qtype), CRDisplayString(m, rr)); | |
3134 | continue; | |
3135 | } | |
3136 | ||
3137 | // See DNSSECRecordAnswersQuestion: This should never happen. NSEC records are | |
3138 | // answered directly only when the qtype is NSEC. Otherwise, NSEC records are | |
3139 | // used only for denial of existence and hence should go through negative cache | |
3140 | // entry. | |
3141 | if (rr->resrec.rrtype == kDNSType_NSEC && q->qtype != kDNSType_NSEC) | |
3142 | { | |
3143 | LogMsg("VerifySignature: ERROR!! Question %##s (%s) answered using NSEC record %s", q->qname.c, DNSTypeName(q->qtype), CRDisplayString(m, rr)); | |
3144 | continue; | |
3145 | } | |
3146 | ||
3147 | // We might get a NSEC response when we first send the query out from the "core" for ValidationRequired | |
3148 | // questions. Later as part of validating the response, we might get a NSEC response. | |
3149 | if (rr->resrec.RecordType == kDNSRecordTypePacketNegative && DNSSECQuestion(q)) | |
3150 | { | |
83fb1e36 A |
3151 | // If we can't find the NSEC, we can't validate. This can happens if we are |
3152 | // behind a non-DNSSEC aware CPE/server. | |
3153 | if (!rr->nsec) | |
3154 | { | |
3155 | LogDNSSEC("VerifySignature: No nsecs found for %s", CRDisplayString(m, rr)); | |
51601d48 | 3156 | dv->DVCallback(m, dv, DNSSEC_Bogus); |
83fb1e36 A |
3157 | return; |
3158 | } | |
3159 | ValidateWithNSECS(m, dv, rr); | |
3160 | return; | |
3161 | } | |
3162 | ||
3163 | if (AddRRSetToVerifier(dv, &rr->resrec, mDNSNULL, RRVS_rr) != mStatus_NoError) | |
3164 | { | |
51601d48 | 3165 | dv->DVCallback(m, dv, DNSSEC_Bogus); |
83fb1e36 A |
3166 | return; |
3167 | } | |
3168 | } | |
3169 | if (!dv->rrset) | |
3170 | { | |
3171 | LogMsg("VerifySignature: rrset mDNSNULL for %##s (%s)", dv->origName.c, DNSTypeName(dv->origType)); | |
51601d48 | 3172 | dv->DVCallback(m, dv, DNSSEC_Bogus); |
83fb1e36 A |
3173 | return; |
3174 | } | |
3175 | dv->next = RRVS_rrsig; | |
51601d48 A |
3176 | // Delay this so that the mDNS "core" can deliver all the results before |
3177 | // we can deliver the dnssec result | |
3178 | if (first) | |
3179 | { | |
3180 | mDNSPlatformDispatchAsync(m, dv, StartDNSSECVerification); | |
3181 | } | |
3182 | else | |
3183 | { | |
3184 | StartDNSSECVerification(m, dv); | |
3185 | } | |
83fb1e36 A |
3186 | } |
3187 | ||
83fb1e36 A |
3188 | mDNSlocal mDNSBool TrustedKeyPresent(mDNS *const m, DNSSECVerifier *dv) |
3189 | { | |
3190 | rdataRRSig *rrsig; | |
3191 | rdataDS *ds; | |
3192 | rdataDNSKey *key; | |
3193 | TrustAnchor *ta; | |
3194 | RRVerifier *keyv; | |
3195 | ||
3196 | rrsig = (rdataRRSig *)dv->rrsig->rdata; | |
3197 | ||
3198 | // Walk all our trusted DS Records to see if we have a matching DNS KEY record that verifies | |
3199 | // the hash. If we find one, verify that this key was used to sign the KEY rrsets in | |
3200 | // this zone. Loop till we find one. | |
3201 | for (ta = m->TrustAnchors; ta; ta = ta->next) | |
3202 | { | |
3203 | ds = (rdataDS *)&ta->rds; | |
3204 | if ((ds->digestType != SHA1_DIGEST_TYPE) && (ds->digestType != SHA256_DIGEST_TYPE)) | |
3205 | { | |
3206 | LogMsg("TrustedKeyPresent: Unsupported digest %d", ds->digestType); | |
3207 | continue; | |
3208 | } | |
3209 | else | |
3210 | { | |
3211 | debugdnssec("TrustedKeyPresent: digest type %d", ds->digestType); | |
3212 | } | |
3213 | for (keyv = dv->key; keyv; keyv = keyv->next) | |
3214 | { | |
3215 | key = (rdataDNSKey *)keyv->rdata; | |
3216 | mDNSu16 tag = (mDNSu16)keytag((mDNSu8 *)key, keyv->rdlength); | |
3217 | if (tag != ds->keyTag) | |
3218 | { | |
3219 | debugdnssec("TrustedKeyPresent:Not a valid keytag %d", tag); | |
3220 | continue; | |
3221 | } | |
3222 | if (!SameDomainName(&keyv->name, &ta->zone)) | |
3223 | { | |
3224 | debugdnssec("TrustedKeyPresent: domainame mismatch key %##s, ta %##s", keyv->name.c, ta->zone.c); | |
3225 | continue; | |
3226 | } | |
3227 | return mDNStrue; | |
3228 | } | |
3229 | } | |
3230 | return mDNSfalse; | |
3231 | } | |
3232 | ||
3233 | mDNSlocal mStatus TrustedKey(mDNS *const m, DNSSECVerifier *dv) | |
3234 | { | |
3235 | mDNSu8 *digest; | |
3236 | int digestLen; | |
3237 | domainname name; | |
3238 | rdataRRSig *rrsig; | |
3239 | rdataDS *ds; | |
3240 | rdataDNSKey *key; | |
3241 | TrustAnchor *ta; | |
3242 | RRVerifier *keyv; | |
3243 | mStatus algRet; | |
3244 | mDNSu32 currTime = mDNSPlatformUTC(); | |
3245 | ||
3246 | rrsig = (rdataRRSig *)dv->rrsig->rdata; | |
3247 | ||
3248 | // Walk all our trusted DS Records to see if we have a matching DNS KEY record that verifies | |
3249 | // the hash. If we find one, verify that this key was used to sign the KEY rrsets in | |
3250 | // this zone. Loop till we find one. | |
3251 | for (ta = m->TrustAnchors; ta; ta = ta->next) | |
3252 | { | |
3253 | ds = (rdataDS *)&ta->rds; | |
3254 | if ((ds->digestType != SHA1_DIGEST_TYPE) && (ds->digestType != SHA256_DIGEST_TYPE)) | |
3255 | { | |
3256 | LogMsg("TrustedKey: Unsupported digest %d", ds->digestType); | |
3257 | continue; | |
3258 | } | |
3259 | else | |
3260 | { | |
3261 | debugdnssec("TrustedKey: Zone %##s, digest type %d, tag %d", ta->zone.c, ds->digestType, ds->keyTag); | |
3262 | } | |
3263 | for (keyv = dv->key; keyv; keyv = keyv->next) | |
3264 | { | |
3265 | key = (rdataDNSKey *)keyv->rdata; | |
3266 | mDNSu16 tag = (mDNSu16)keytag((mDNSu8 *)key, keyv->rdlength); | |
3267 | if (tag != ds->keyTag) | |
3268 | { | |
3269 | debugdnssec("TrustedKey:Not a valid keytag %d", tag); | |
3270 | continue; | |
3271 | } | |
3272 | if (!SameDomainName(&keyv->name, &ta->zone)) | |
3273 | { | |
3274 | debugdnssec("TrustedKey: domainame mismatch key %##s, ta %##s", keyv->name.c, ta->zone.c); | |
3275 | continue; | |
3276 | } | |
3277 | if (DNS_SERIAL_LT(ta->validUntil, currTime)) | |
3278 | { | |
3279 | LogDNSSEC("TrustedKey: Expired: currentTime %d, ExpireTime %d", (int)currTime, ta->validUntil); | |
3280 | continue; | |
3281 | } | |
3282 | if (DNS_SERIAL_LT(currTime, ta->validFrom)) | |
3283 | { | |
3284 | LogDNSSEC("TrustedKey: Future: currentTime %d, InceptTime %d", (int)currTime, ta->validFrom); | |
3285 | continue; | |
3286 | } | |
3287 | ||
3288 | if (DNSNameToLowerCase((domainname *)&rrsig->signerName, &name) != mStatus_NoError) | |
3289 | { | |
3290 | LogMsg("TrustedKey: ERROR!! cannot convert to lower case"); | |
3291 | continue; | |
3292 | } | |
3293 | ||
3294 | if (dv->ctx) AlgDestroy(dv->ctx); | |
3295 | dv->ctx = AlgCreate(DIGEST_ALG, ds->digestType); | |
3296 | if (!dv->ctx) | |
3297 | { | |
3298 | LogMsg("TrustedKey: ERROR!! No digest support"); | |
3299 | continue; | |
3300 | } | |
3301 | digest = ds->digest; | |
3302 | digestLen = ta->digestLen; | |
3303 | ||
3304 | AlgAdd(dv->ctx, name.c, DomainNameLength(&name)); | |
51601d48 | 3305 | AlgAdd(dv->ctx, (const mDNSu8 *)key, keyv->rdlength); |
83fb1e36 A |
3306 | |
3307 | algRet = AlgVerify(dv->ctx, mDNSNULL, 0, digest, digestLen); | |
3308 | AlgDestroy(dv->ctx); | |
3309 | dv->ctx = mDNSNULL; | |
3310 | if (algRet == mStatus_NoError) | |
3311 | { | |
3312 | LogDNSSEC("TrustedKey: DS Validated Successfully, need to verify the key %d", tag); | |
3313 | // We found the DNS KEY that is authenticated by the DS in our parent zone. Check to see if this key | |
3314 | // was used to sign the DNS KEY RRSET. If so, then the keys in our DNS KEY RRSET are valid | |
3315 | if (ValidateSignatureWithKeyForAllRRSigs(dv, dv->key, keyv, dv->rrsigKey)) | |
3316 | { | |
3317 | LogDNSSEC("TrustedKey: DS Validated Successfully %d", tag); | |
3318 | return mStatus_NoError; | |
3319 | } | |
3320 | } | |
3321 | } | |
3322 | } | |
3323 | return mStatus_NoSuchRecord; | |
3324 | } | |
3325 | ||
3326 | mDNSlocal CacheRecord* NegativeCacheRecordForRR(mDNS *const m, const ResourceRecord *const rr) | |
3327 | { | |
3328 | mDNSu32 slot; | |
3329 | mDNSu32 namehash; | |
3330 | CacheGroup *cg; | |
3331 | CacheRecord *cr; | |
3332 | ||
3333 | slot = HashSlot(rr->name); | |
3334 | namehash = DomainNameHashValue(rr->name); | |
3335 | cg = CacheGroupForName(m, slot, namehash, rr->name); | |
3336 | if (!cg) | |
3337 | { | |
3338 | LogMsg("NegativeCacheRecordForRR: cg null %##s", rr->name->c); | |
3339 | return mDNSNULL; | |
3340 | } | |
3341 | for (cr=cg->members; cr; cr=cr->next) | |
3342 | { | |
3343 | if (cr->resrec.RecordType == kDNSRecordTypePacketNegative && (&cr->resrec == rr)) | |
3344 | return cr; | |
3345 | } | |
3346 | return mDNSNULL; | |
3347 | } | |
3348 | ||
3349 | mDNSlocal void VerifySigCallback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord) | |
3350 | { | |
3351 | DNSSECVerifier *dv = (DNSSECVerifier *)question->QuestionContext; | |
3352 | mDNSu16 rrtype; | |
3353 | CacheRecord *negcr; | |
3354 | ||
3355 | debugdnssec("VerifySigCallback: AddRecord %d, dv %p", AddRecord, dv); | |
3356 | ||
51601d48 A |
3357 | if (!AddRecord) |
3358 | return; | |
83fb1e36 | 3359 | |
51601d48 A |
3360 | // After the first ADD event, we should ideally stop the question. If we don't stop |
3361 | // the question, we might get more callbacks and that can cause problems. For example, | |
3362 | // in the first callback, we could start a insecure proof and while that is in progress, | |
3363 | // if we get more callbacks, we will try to start another insecure proof. As we already | |
3364 | // started an insecure proof, we won't start another but terminate the verification | |
3365 | // process where we free the current DNSSECVerifier while the first insecure proof is | |
3366 | // still referencing it. | |
3367 | // | |
3368 | // But there are cases below which might return if we have not received the right answer | |
3369 | // yet e.g., no RRSIGs. In that case if the question is stopped, we will never get any | |
3370 | // callbacks again and also we leak "dv". Hence it is important that we either process | |
3371 | // the result or wait for more results. Note that the question eventually times out | |
3372 | // and cleans up the "dv" i.e., we don't wait forever. | |
3373 | ||
3374 | if (!answer) | |
3375 | { | |
3376 | LogDNSSEC("VerifySigCallback: Question %##s (%s) no dnssec response", question->qname.c, DNSTypeName(question->qtype)); | |
3377 | mDNS_StopQuery(m, question); | |
3378 | dv->DVCallback(m, dv, DNSSEC_Bogus); | |
3379 | return; | |
3380 | } | |
83fb1e36 | 3381 | |
51601d48 A |
3382 | LogDNSSEC("VerifySigCallback(%p): Called with record %s for question %##s (%s)", dv, RRDisplayString(m, answer), question->qname.c, |
3383 | DNSTypeName(question->qtype)); | |
83fb1e36 A |
3384 | mDNS_Lock(m); |
3385 | if ((m->timenow - question->StopTime) >= 0) | |
3386 | { | |
3387 | mDNS_Unlock(m); | |
3388 | LogDNSSEC("VerifySigCallback: Question %##s (%s) timed out", question->qname.c, DNSTypeName(question->qtype)); | |
51601d48 A |
3389 | mDNS_StopQuery(m, question); |
3390 | dv->DVCallback(m, dv, DNSSEC_Bogus); | |
83fb1e36 A |
3391 | return; |
3392 | } | |
3393 | mDNS_Unlock(m); | |
3394 | ||
3395 | if (answer->RecordType == kDNSRecordTypePacketNegative) | |
3396 | { | |
3397 | CacheRecord *cr; | |
3398 | LogDNSSEC("VerifySigCallback: Received a negative answer with record %s, AddRecord %d", | |
3399 | RRDisplayString(m, answer), AddRecord); | |
51601d48 | 3400 | mDNS_StopQuery(m, question); |
83fb1e36 A |
3401 | cr = NegativeCacheRecordForRR(m, answer); |
3402 | if (cr && cr->nsec) | |
3403 | { | |
83fb1e36 A |
3404 | ValidateWithNSECS(m, dv, cr); |
3405 | } | |
3406 | else | |
3407 | { | |
51601d48 | 3408 | |
83fb1e36 A |
3409 | LogDNSSEC("VerifySigCallback: Missing record (%s) Negative Cache Record %p", RRDisplayString(m, answer), cr); |
3410 | dv->DVCallback(m, dv, DNSSEC_Bogus); | |
3411 | } | |
3412 | return; | |
3413 | } | |
3414 | ||
3415 | if (!dv->rrset) | |
3416 | { | |
3417 | LogMsg("VerifySigCallback: ERROR!! rrset NULL"); | |
51601d48 A |
3418 | mDNS_StopQuery(m, question); |
3419 | dv->DVCallback(m, dv, DNSSEC_Bogus); | |
83fb1e36 A |
3420 | return; |
3421 | } | |
3422 | ||
3423 | rrtype = answer->rrtype; | |
3424 | // Check whether we got any answers for the question. If there are no answers, we | |
3425 | // can't do the verification. | |
3426 | // | |
3427 | // We need to look at the whole rrset for verifying the signatures. This callback gets | |
3428 | // called back for each record in the rrset sequentially and we won't know when to start the | |
3429 | // verification. Hence, we look for all the records in the rrset ourselves using the | |
3430 | // CheckXXX function below. The caller has to ensure that all the records in the rrset are | |
3431 | // added to the cache before calling this callback which happens naturally because all | |
3432 | // unicast records are marked for DelayDelivery and hence added to the cache before the | |
3433 | // callback is done. | |
3434 | // | |
3435 | // We also need the RRSIGs for the rrset to do the validation. It is possible that the | |
3436 | // cache contains RRSIG records but it may not be a valid record when we filter them | |
3437 | // in CheckXXX function. For example, some application can query for RRSIG records which | |
3438 | // might come back with a partial set of RRSIG records from the recursive server and | |
3439 | // they may not be the right ones for the current validation. In this case, we still | |
3440 | // need to send the query out to get the right RRSIGs but the "core" should not answer | |
3441 | // this query with the same records that we checked and found them to be unusable. | |
3442 | // | |
3443 | // We handle this in two ways: | |
3444 | // | |
3445 | // 1) AnswerNewQuestion always sends the "ValidatingResponse" query out bypassing the cache. | |
3446 | // | |
3447 | // 2) DNSSECRecordAnswersQuestion does not answer a question with RRSIGs matching the | |
3448 | // same name as the query until the typeCovered also matches the query's type. | |
3449 | // | |
3450 | // NOTE: We use "next - 1" as next always points to what we are going to fetch next and not the one | |
3451 | // we are fetching currently | |
3452 | switch(dv->next - 1) | |
3453 | { | |
3454 | case RRVS_rr: | |
3455 | // Verification always starts at RRVS_rrsig (which means dv->next points at RRVS_key) as verification does | |
3456 | // not begin until we have the main rrset. | |
3457 | LogDNSSEC("VerifySigCallback: ERROR!! rrset %##s dv->next is RRVS_rr", dv->rrset->name.c); | |
3458 | return; | |
3459 | case RRVS_rrsig: | |
3460 | // We can get called back with rrtype matching qtype as new records are added to the cache | |
3461 | // triggered by other questions. This could potentially mean that the rrset that is being | |
3462 | // validated by this "dv" whose rrsets were initialized at the beginning of the verification | |
3463 | // may not be the right one. If this case happens, we will detect this at the end of validation | |
3464 | // and throw away the validation results. This should not be a common case. | |
3465 | if (rrtype != kDNSType_RRSIG) | |
3466 | { | |
3467 | LogDNSSEC("VerifySigCallback: RRVS_rrsig called with %s", RRDisplayString(m, answer)); | |
3468 | return; | |
3469 | } | |
51601d48 | 3470 | mDNS_StopQuery(m, question); |
83fb1e36 A |
3471 | if (CheckRRSIGForRRSet(m, dv, &negcr) != mStatus_NoError) |
3472 | { | |
3473 | LogDNSSEC("VerifySigCallback: Unable to find RRSIG for %##s (%s), question %##s", dv->rrset->name.c, | |
3474 | DNSTypeName(dv->rrset->rrtype), question->qname.c); | |
3475 | dv->DVCallback(m, dv, DNSSEC_Bogus); | |
3476 | return; | |
3477 | } | |
3478 | break; | |
3479 | case RRVS_key: | |
3480 | // We are waiting for the DNSKEY record and hence dv->key should be NULL. If RRSIGs are being | |
3481 | // returned first, ignore them for now. | |
3482 | if (dv->key) | |
3483 | LogDNSSEC("VerifySigCallback: ERROR!! RRVS_key dv->key non-NULL for %##s", question->qname.c); | |
3484 | if (rrtype == kDNSType_RRSIG) | |
3485 | { | |
3486 | LogDNSSEC("VerifySigCallback: RRVS_key rrset type %s, %##s received before DNSKEY", DNSTypeName(rrtype), question->qname.c); | |
3487 | return; | |
3488 | } | |
3489 | if (rrtype != question->qtype) | |
3490 | { | |
3491 | LogDNSSEC("VerifySigCallback: ERROR!! RRVS_key rrset type %s, %##s not matching qtype %d", DNSTypeName(rrtype), question->qname.c, | |
3492 | question->qtype); | |
3493 | return; | |
3494 | } | |
51601d48 | 3495 | mDNS_StopQuery(m, question); |
83fb1e36 A |
3496 | if (CheckKeyForRRSIG(m, dv, &negcr) != mStatus_NoError) |
3497 | { | |
3498 | LogDNSSEC("VerifySigCallback: Unable to find DNSKEY for %##s (%s), question %##s", dv->rrset->name.c, | |
3499 | DNSTypeName(dv->rrset->rrtype), question->qname.c); | |
51601d48 | 3500 | dv->DVCallback(m, dv, DNSSEC_Bogus); |
83fb1e36 A |
3501 | return; |
3502 | } | |
3503 | break; | |
3504 | case RRVS_rrsig_key: | |
3505 | // If we are in RRVS_rrsig_key, it means that we already found the relevant DNSKEYs (dv->key should be non-NULL). | |
3506 | // If DNSKEY record is being returned i.e., it means it is being added to the cache, then it can't be in our | |
3507 | // list. | |
3508 | if (!dv->key) | |
3509 | LogDNSSEC("VerifySigCallback: ERROR!! RRVS_rrsig_key dv->key NULL for %##s", question->qname.c); | |
3510 | if (rrtype == question->qtype) | |
3511 | { | |
3512 | LogDNSSEC("VerifySigCallback: RRVS_rrsig_key rrset type %s, %##s", DNSTypeName(rrtype), question->qname.c); | |
3513 | CheckOneKeyForRRSIG(dv, answer); | |
3514 | return; | |
3515 | } | |
3516 | if (rrtype != kDNSType_RRSIG) | |
3517 | { | |
3518 | LogDNSSEC("VerifySigCallback: RRVS_rrsig_key rrset type %s, %##s not matching qtype %d", DNSTypeName(rrtype), question->qname.c, | |
3519 | question->qtype); | |
3520 | return; | |
3521 | } | |
51601d48 | 3522 | mDNS_StopQuery(m, question); |
83fb1e36 A |
3523 | if (CheckRRSIGForKey(m, dv, &negcr) != mStatus_NoError) |
3524 | { | |
3525 | LogDNSSEC("VerifySigCallback: Unable to find RRSIG for %##s (%s), question %##s", dv->rrset->name.c, | |
3526 | DNSTypeName(dv->rrset->rrtype), question->qname.c); | |
3527 | dv->DVCallback(m, dv, DNSSEC_Bogus); | |
3528 | return; | |
3529 | } | |
3530 | break; | |
3531 | case RRVS_ds: | |
3532 | if (rrtype == question->qtype) | |
3533 | { | |
3534 | LogDNSSEC("VerifySigCallback: RRVS_ds rrset type %s, %##s", DNSTypeName(rrtype), question->qname.c); | |
3535 | } | |
3536 | else | |
3537 | { | |
3538 | LogDNSSEC("VerifySigCallback: RRVS_ds rrset type %s, %##s received before DS", DNSTypeName(rrtype), question->qname.c); | |
3539 | } | |
51601d48 | 3540 | mDNS_StopQuery(m, question); |
83fb1e36 A |
3541 | // It is not an error if we don't find the DS record as we could have |
3542 | // a trusted key. Or this is not a secure delegation which will be handled | |
3543 | // below. | |
3544 | if (CheckDSForKey(m, dv, &negcr) != mStatus_NoError) | |
3545 | { | |
3546 | LogDNSSEC("VerifySigCallback: Unable find DS for %##s (%s), question %##s", dv->rrset->name.c, | |
3547 | DNSTypeName(dv->rrset->rrtype), question->qname.c); | |
3548 | } | |
3549 | // dv->next is already at RRVS_done, so if we "break" from here, we will end up | |
3550 | // in FinishDNSSECVerification. We should not do that if we receive a negative | |
3551 | // response. For all other cases above, GetAllRRSetsForVerification handles | |
3552 | // negative cache record | |
3553 | if (negcr) | |
3554 | { | |
3555 | if (!negcr->nsec) | |
3556 | { | |
3557 | LogDNSSEC("VerifySigCallback: No nsec records for %##s (DS)", dv->ds->name.c); | |
3558 | dv->DVCallback(m, dv, DNSSEC_Bogus); | |
3559 | return; | |
3560 | } | |
83fb1e36 A |
3561 | ValidateWithNSECS(m, dv, negcr); |
3562 | return; | |
3563 | } | |
3564 | break; | |
3565 | default: | |
3566 | LogDNSSEC("VerifySigCallback: ERROR!! default case rrset %##s question %##s", dv->rrset->name.c, question->qname.c); | |
51601d48 | 3567 | mDNS_StopQuery(m, question); |
83fb1e36 A |
3568 | dv->DVCallback(m, dv, DNSSEC_Bogus); |
3569 | return; | |
3570 | } | |
3571 | if (dv->next != RRVS_done) | |
3572 | { | |
3573 | mDNSBool done = GetAllRRSetsForVerification(m, dv); | |
3574 | if (done) | |
3575 | { | |
3576 | if (dv->next != RRVS_done) | |
3577 | LogMsg("VerifySigCallback ERROR!! dv->next is not done"); | |
3578 | else | |
3579 | LogDNSSEC("VerifySigCallback: all rdata sets available for sig verification"); | |
3580 | } | |
3581 | else | |
3582 | { | |
3583 | LogDNSSEC("VerifySigCallback: all rdata sets not available for sig verification"); | |
3584 | return; | |
3585 | } | |
3586 | } | |
3587 | FinishDNSSECVerification(m, dv); | |
3588 | } | |
51601d48 A |
3589 | |
3590 | mDNSlocal TrustAnchor *FindTrustAnchor(mDNS *const m, const domainname *const name) | |
3591 | { | |
3592 | TrustAnchor *ta; | |
3593 | TrustAnchor *matchTA = mDNSNULL; | |
3594 | TrustAnchor *rootTA = mDNSNULL; | |
3595 | int currmatch = 0; | |
3596 | int match; | |
3597 | mDNSu32 currTime = mDNSPlatformUTC(); | |
3598 | ||
3599 | for (ta = m->TrustAnchors; ta; ta = ta->next) | |
3600 | { | |
3601 | if (DNS_SERIAL_LT(ta->validUntil, currTime)) | |
3602 | { | |
3603 | LogDNSSEC("FindTrustAnchor: Expired: currentTime %d, ExpireTime %d", (int)currTime, ta->validUntil); | |
3604 | continue; | |
3605 | } | |
3606 | if (DNS_SERIAL_LT(currTime, ta->validFrom)) | |
3607 | { | |
3608 | LogDNSSEC("FindTrustAnchor: Future: currentTime %d, InceptTime %d", (int)currTime, ta->validFrom); | |
3609 | continue; | |
3610 | } | |
3611 | ||
3612 | if (SameDomainName((const domainname *)"\000", &ta->zone)) | |
3613 | rootTA = ta; | |
3614 | ||
3615 | match = CountLabelsMatch(&ta->zone, name); | |
3616 | if (match > currmatch) | |
3617 | { | |
3618 | currmatch = match; | |
3619 | matchTA = ta; | |
3620 | } | |
3621 | } | |
3622 | if (matchTA) | |
3623 | { | |
3624 | LogDNSSEC("FindTrustAnhcor: matched %##s", matchTA->zone.c); | |
3625 | return matchTA; | |
3626 | } | |
3627 | else if (rootTA) | |
3628 | { | |
3629 | LogDNSSEC("FindTrustAnhcor: matched rootTA %##s", rootTA->zone.c); | |
3630 | return rootTA; | |
3631 | } | |
3632 | else | |
3633 | { | |
3634 | LogDNSSEC("FindTrustAnhcor: No Trust Anchor"); | |
3635 | return mDNSNULL; | |
3636 | } | |
3637 | } | |
3638 | ||
3639 | mDNSlocal void DeliverInsecureProofResultAsync(mDNS *const m, void *context) | |
3640 | { | |
3641 | InsecureContext *ic = (InsecureContext *)context; | |
3642 | ic->dv->DVCallback(m, ic->dv, ic->status); | |
3643 | if (ic->q.ThisQInterval != -1) | |
3644 | { | |
3645 | LogMsg("DeliverInsecureProofResultAsync: ERROR!! Question %##s (%s) not stopped already", ic->q.qname.c, DNSTypeName(ic->q.qtype)); | |
3646 | mDNS_StopQuery(m, &ic->q); | |
3647 | } | |
3648 | mDNSPlatformMemFree(ic); | |
3649 | } | |
3650 | ||
3651 | mDNSlocal void DeliverInsecureProofResult(mDNS *const m, InsecureContext *ic, DNSSECStatus status) | |
3652 | { | |
3653 | // If the status is Bogus, restore the original auth chain before the insecure | |
3654 | // proof. | |
3655 | if (status == DNSSEC_Bogus) | |
3656 | { | |
3657 | LogDNSSEC("DeliverInsecureProofResult: Restoring the auth chain"); | |
3658 | if (ic->dv->ac) | |
3659 | { | |
3660 | FreeDNSSECAuthChainInfo(ic->dv->ac); | |
3661 | } | |
3662 | ResetAuthChain(ic->dv); | |
3663 | ic->dv->ac = ic->dv->saveac; | |
3664 | if (ic->dv->ac) | |
3665 | { | |
3666 | AuthChain *tmp = ic->dv->ac; | |
3667 | AuthChain **tail = &tmp->next; | |
3668 | while (tmp->next) | |
3669 | { | |
3670 | tail = &tmp->next; | |
3671 | tmp = tmp->next; | |
3672 | } | |
3673 | ic->dv->actail = tail; | |
3674 | } | |
3675 | ic->dv->saveac = mDNSNULL; | |
3676 | } | |
3677 | else if (ic->dv->saveac) | |
3678 | { | |
3679 | FreeDNSSECAuthChainInfo(ic->dv->saveac); | |
3680 | ic->dv->saveac = mDNSNULL; | |
3681 | } | |
3682 | ic->status = status; | |
3683 | // Stop the question before we schedule the block so that we don't receive additional | |
3684 | // callbacks again. Once the block runs, it will free the "ic" and you can't | |
3685 | // have another block queued up. This can happen if we receive a callback after we | |
3686 | // queue the block below. | |
3687 | if (ic->q.ThisQInterval != -1) | |
3688 | mDNS_StopQuery(m, &ic->q); | |
3689 | mDNSPlatformDispatchAsync(m, ic, DeliverInsecureProofResultAsync); | |
3690 | } | |
3691 | ||
3692 | mDNSlocal mDNSBool AlgorithmSupported(rdataDS *ds) | |
3693 | { | |
3694 | switch(ds->digestType) | |
3695 | { | |
3696 | case SHA1_DIGEST_TYPE: | |
3697 | case SHA256_DIGEST_TYPE: | |
3698 | break; | |
3699 | default: | |
3700 | LogDNSSEC("AlgorithmSupported: Unsupported digest %d", ds->digestType); | |
3701 | return mDNSfalse; | |
3702 | } | |
3703 | ||
3704 | switch(ds->alg) | |
3705 | { | |
3706 | case CRYPTO_RSA_NSEC3_SHA1: | |
3707 | case CRYPTO_RSA_SHA1: | |
3708 | case CRYPTO_RSA_SHA256: | |
3709 | case CRYPTO_RSA_SHA512: | |
3710 | return mDNStrue; | |
3711 | default: | |
3712 | LogDNSSEC("AlgorithmSupported: Unsupported algorithm %d", ds->alg); | |
3713 | return mDNSfalse; | |
3714 | } | |
3715 | } | |
3716 | ||
3717 | // Note: This function is called when DNSSEC results are delivered (from DeliverDNSSECStatus) and we can't deliver DNSSEC result | |
3718 | // again within this function as "m->ValidationQuestion" is already in use. Hence we should dispatch off the delivery of insecure | |
3719 | // results asynchronously. | |
3720 | // | |
3721 | // Insecure proof callback can deliver either insecure or bogus, but never secure result. | |
3722 | mDNSlocal void ProveInsecureCallback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord) | |
3723 | { | |
3724 | InsecureContext *ic = (InsecureContext *)question->QuestionContext; | |
3725 | DNSSECVerifier *pdv = ic->dv; | |
3726 | AuthChain *ac; | |
3727 | ||
3728 | (void) answer; | |
3729 | ||
3730 | if (!AddRecord) | |
3731 | return; | |
3732 | ||
3733 | mDNS_Lock(m); | |
3734 | if ((m->timenow - question->StopTime) >= 0) | |
3735 | { | |
3736 | mDNS_Unlock(m); | |
3737 | LogDNSSEC("ProveInsecureCallback: Question %##s (%s) timed out", question->qname.c, DNSTypeName(question->qtype)); | |
3738 | DeliverInsecureProofResult(m, ic, DNSSEC_Bogus); | |
3739 | return; | |
3740 | } | |
3741 | mDNS_Unlock(m); | |
3742 | ||
3743 | // We only need to handle the actual DNSSEC results and the ones that are secure. Anything else results in | |
3744 | // bogus. | |
3745 | if (AddRecord != QC_dnssec) | |
3746 | { | |
3747 | LogDNSSEC("ProveInsecureCallback: Question %##s (%s), AddRecord %d, answer %s", question->qname.c, | |
3748 | DNSTypeName(question->qtype), AddRecord, RRDisplayString(m, answer)); | |
3749 | return; | |
3750 | } | |
3751 | ||
3752 | LogDNSSEC("ProveInsecureCallback: ic %p Question %##s (%s), DNSSEC status %s", ic, question->qname.c, DNSTypeName(question->qtype), | |
3753 | DNSSECStatusName(question->ValidationStatus)); | |
3754 | ||
3755 | // Insecure is delivered for NSEC3 OptOut | |
3756 | if (question->ValidationStatus != DNSSEC_Secure && question->ValidationStatus != DNSSEC_Insecure) | |
3757 | { | |
3758 | LogDNSSEC("ProveInsecureCallback: Question %##s (%s) returned DNSSEC status %s", question->qname.c, | |
3759 | DNSTypeName(question->qtype), DNSSECStatusName(question->ValidationStatus)); | |
3760 | goto done; | |
3761 | } | |
3762 | ac = (AuthChain *)question->DNSSECAuthInfo; | |
3763 | if (!ac) | |
3764 | { | |
3765 | LogDNSSEC("ProveInsecureCallback: ac NULL for question %##s, %s", question->qname.c, DNSTypeName(question->qtype)); | |
3766 | goto done; | |
3767 | } | |
3768 | if (!ac->rrset) | |
3769 | { | |
3770 | LogDNSSEC("ProveInsecureCallback: ac->rrset NULL for question %##s, %s", question->qname.c, DNSTypeName(question->qtype)); | |
3771 | goto done; | |
3772 | } | |
3773 | if (ac->rrset->rrtype != kDNSType_DS && ac->rrset->rrtype != kDNSType_NSEC && ac->rrset->rrtype != kDNSType_NSEC3) | |
3774 | { | |
3775 | LogDNSSEC("ProveInsecureCallback: ac->rrset->rrtype %##s (%s) not handled", ac->rrset->name.c, | |
3776 | DNSTypeName(ac->rrset->rrtype)); | |
3777 | goto done; | |
3778 | } | |
3779 | AuthChainLink(pdv, ac); | |
3780 | question->DNSSECAuthInfo = mDNSNULL; | |
3781 | if (ac->rrset->rrtype == kDNSType_DS) | |
3782 | { | |
3783 | rdataDS *ds = (rdataDS *)ac->rrset->rdata; | |
3784 | ||
3785 | // If the delegation is secure, but the underlying zone is signed with an unsupported | |
3786 | // algorithm, then we can't verify it. Deliver insecure in that case. | |
3787 | if (!AlgorithmSupported(ds)) | |
3788 | { | |
3789 | LogDNSSEC("ProveInsecureCallback: Unsupported algorithm %d or digest %d", ds->alg, ds->digestType); | |
3790 | DeliverInsecureProofResult(m, ic, DNSSEC_Insecure); | |
3791 | return; | |
3792 | } | |
3793 | ||
3794 | // If the delegation is secure and the name that we queried for is same as the original | |
3795 | // name that started the insecure proof, then something is not right. We started the | |
3796 | // insecure proof e.g., the zone is not signed, but we are able to validate a DS for | |
3797 | // the same name which implies that the zone is signed (whose algorithm we support) and | |
3798 | // we should not have started the insecurity proof in the first place. | |
3799 | if (SameDomainName(&question->qname, &pdv->origName)) | |
3800 | { | |
3801 | LogDNSSEC("ProveInsecureCallback: Insecure proof reached original name %##s, error", question->qname.c); | |
3802 | DeliverInsecureProofResult(m, ic, DNSSEC_Bogus); | |
3803 | return; | |
3804 | } | |
3805 | ||
3806 | LogDNSSEC("ProveInsecureCallback: Trying one more level down"); | |
3807 | ProveInsecure(m, pdv, ic, mDNSNULL); | |
3808 | } | |
3809 | else if (ac->rrset->rrtype == kDNSType_NSEC || ac->rrset->rrtype == kDNSType_NSEC3) | |
3810 | { | |
3811 | CacheRecord *cr; | |
3812 | ||
3813 | if (ac->rrset->rrtype == kDNSType_NSEC) | |
3814 | cr = NSECRecordIsDelegation(m, &question->qname, question->qtype); | |
3815 | else | |
3816 | cr = NSEC3RecordIsDelegation(m, &question->qname, question->qtype); | |
3817 | if (cr) | |
3818 | { | |
3819 | LogDNSSEC("ProveInsecureCallback: Non-existence proved and %s is a delegation for %##s (%s)", CRDisplayString(m, cr), | |
3820 | question->qname.c, DNSTypeName(question->qtype)); | |
3821 | DeliverInsecureProofResult(m, ic, DNSSEC_Insecure); | |
3822 | return; | |
3823 | } | |
3824 | // Could be a ENT. Go one more level down to see whether it is a secure delegation or not. | |
3825 | if (!SameDomainName(&question->qname, &pdv->origName)) | |
3826 | { | |
3827 | LogDNSSEC("ProveInsecureCallback: Not a delegation %##s (%s), go one more level down", question->qname.c, DNSTypeName(question->qtype)); | |
3828 | ProveInsecure(m, pdv, ic, mDNSNULL); | |
3829 | } | |
3830 | else | |
3831 | { | |
3832 | // Secure denial of existence and the name matches the original query. This means we should have | |
3833 | // received an NSEC (if the type does not exist) or signed records (if the name and type exists) | |
3834 | // and verified it successfully instead of starting the insecure proof. This could happen e.g., | |
3835 | // Wildcard expanded answer received without NSEC/NSEC3s etc. Also, is it possible that the | |
3836 | // zone went from unsigned to signed in a short time ? For now, we return bogus. | |
3837 | LogDNSSEC("ProveInsecureCallback: Not a delegation %##s (%s), but reached original name", question->qname.c, | |
3838 | DNSTypeName(question->qtype)); | |
3839 | DeliverInsecureProofResult(m, ic, DNSSEC_Bogus); | |
3840 | } | |
3841 | } | |
3842 | return; | |
3843 | done: | |
3844 | DeliverInsecureProofResult(m, ic, DNSSEC_Bogus); | |
3845 | } | |
3846 | ||
3847 | // We return Insecure if we don't have a trust anchor or we have a trust anchor and | |
3848 | // can prove that the delegation is not secure (and hence can't establish the trust | |
3849 | // chain) or the delegation is possibly secure but we don't have the algorithm support | |
3850 | // to prove that. | |
3851 | mDNSexport void ProveInsecure(mDNS *const m, DNSSECVerifier *dv, InsecureContext *ic, domainname *trigger) | |
3852 | { | |
3853 | TrustAnchor *ta; | |
3854 | domainname *sname; | |
3855 | ||
3856 | if (ic == mDNSNULL) | |
3857 | { | |
3858 | ic = (InsecureContext *)mDNSPlatformMemAllocate(sizeof(InsecureContext)); | |
3859 | if (!ic) | |
3860 | { | |
3861 | LogMsg("mDNSPlatformMemAllocate: ERROR!! memory alloc failed for ic"); | |
3862 | return; | |
3863 | } | |
3864 | ||
3865 | // Save the AuthInfo while we are proving insecure. We don't want to mix up | |
3866 | // the auth chain for Bogus and Insecure. If we prove it to be insecure, we | |
3867 | // will add the chain corresponding to the insecure proof. Otherwise, we will | |
3868 | // restore this chain. | |
3869 | if (dv->ac) | |
3870 | { | |
3871 | if (!dv->saveac) | |
3872 | { | |
3873 | LogDNSSEC("ProveInsecure: saving authinfo"); | |
3874 | } | |
3875 | else | |
3876 | { | |
3877 | LogDNSSEC("ProveInsecure: ERROR!! authinfo already set"); | |
3878 | FreeDNSSECAuthChainInfo(dv->saveac); | |
3879 | } | |
3880 | dv->saveac = dv->ac; | |
3881 | ResetAuthChain(dv); | |
3882 | } | |
3883 | ic->dv = dv; | |
3884 | ic->q.ThisQInterval = -1; | |
3885 | ||
3886 | if (trigger) | |
3887 | { | |
3888 | LogDNSSEC("ProveInsecure: Setting Trigger %##s", trigger->c); | |
3889 | ic->triggerLabelCount = CountLabels(trigger); | |
3890 | } | |
3891 | else | |
3892 | { | |
3893 | LogDNSSEC("ProveInsecure: No Trigger"); | |
3894 | ic->triggerLabelCount = CountLabels(&dv->origName); | |
3895 | } | |
3896 | ||
3897 | ta = FindTrustAnchor(m, &dv->origName); | |
3898 | if (!ta) | |
3899 | { | |
3900 | LogDNSSEC("ProveInsecure: TrustAnchor NULL"); | |
3901 | DeliverInsecureProofResult(m, ic, DNSSEC_Insecure); | |
3902 | return; | |
3903 | } | |
3904 | // We want to skip the labels that is already matched by the trust anchor so | |
3905 | // that the first query starts just below the trust anchor | |
3906 | ic->skip = CountLabels(&dv->origName) - CountLabels(&ta->zone); | |
3907 | if (!ic->skip) | |
3908 | { | |
3909 | LogDNSSEC("ProveInsecure: origName %##s, skip is zero", dv->origName.c); | |
3910 | DeliverInsecureProofResult(m, ic, DNSSEC_Bogus); | |
3911 | return; | |
3912 | } | |
3913 | } | |
3914 | // Look for the DS record starting just below the trust anchor. | |
3915 | // | |
3916 | // 1. If we find an NSEC record, then see if it is a delegation. If it is, then | |
3917 | // we are done. Otherwise, go down one more level. | |
3918 | // | |
3919 | // 2. If we find a DS record and no algorithm support, return "insecure". Otherwise, go | |
3920 | // down one more level. | |
3921 | // | |
3922 | sname = (domainname *)SkipLeadingLabels(&dv->origName, (ic->skip ? ic->skip - 1 : 0)); | |
3923 | if (!sname) | |
3924 | { | |
3925 | LogDNSSEC("ProveInsecure: sname NULL, origName %##s, skip %d", dv->origName.c, ic->skip); | |
3926 | DeliverInsecureProofResult(m, ic, DNSSEC_Bogus); | |
3927 | return; | |
3928 | } | |
3929 | ||
3930 | // Insecurity proof is started during the normal bottom-up validation when we have a break in the trust | |
3931 | // chain e.g., we get NSEC/NSEC3s when looking up a DS record. Insecurity proof is top-down looking | |
3932 | // for a break in the trust chain. If we have already tried the validation (before the insecurity | |
3933 | // proof started) for this "sname", then don't bother with the proof. This happens sometimes, when | |
3934 | // we can't prove whether a zone is insecurely delegated or not. For example, if we are looking up | |
3935 | // host1.secure-nods.secure.example and when we encounter secure-nods, there is no DS record in the | |
3936 | // parent. We start the insecurity proof remembering that "secure-nods.secure.example" is the trigger | |
3937 | // point. As part of the proof we reach "secure-nods.secure.example". Even though secure.example | |
3938 | // prove that the name "secure-nods.secure.example/DS" does not exist, it can't prove that it is a | |
3939 | // delegation. So, we continue one more level down to host1.secure-nods.secure.example and we | |
3940 | // realize that we already tried the validation and hence abort here. | |
3941 | ||
3942 | if (CountLabels(sname) > ic->triggerLabelCount) | |
3943 | { | |
3944 | LogDNSSEC("ProveInsecure: Beyond the trigger current name %##s, origName %##s", sname->c, dv->origName.c); | |
3945 | DeliverInsecureProofResult(m, ic, DNSSEC_Bogus); | |
3946 | return; | |
3947 | } | |
3948 | ||
3949 | LogDNSSEC("ProveInsecure: OrigName %##s (%s), Current %##s", dv->origName.c, DNSTypeName(dv->origType), sname->c); | |
3950 | ic->skip--; | |
3951 | InitializeQuestion(m, &ic->q, dv->InterfaceID, sname, kDNSType_DS, ProveInsecureCallback, ic); | |
3952 | ic->q.ValidationRequired = DNSSEC_VALIDATION_INSECURE; | |
3953 | ic->q.ValidatingResponse = 0; | |
3954 | ic->q.DNSSECAuthInfo = mDNSNULL; | |
3955 | mDNS_StartQuery(m, &ic->q); | |
3956 | } | |
3957 | ||
3958 | mDNSexport void BumpDNSSECStats(mDNS *const m, DNSSECStatsAction action, DNSSECStatsType type, mDNSu32 value) | |
3959 | { | |
3960 | switch (type) | |
3961 | { | |
3962 | case kStatsTypeMemoryUsage: | |
3963 | if (action == kStatsActionIncrement) | |
3964 | { | |
3965 | m->DNSSECStats.TotalMemUsed += value; | |
3966 | } | |
3967 | else if (action == kStatsActionDecrement) | |
3968 | { | |
3969 | m->DNSSECStats.TotalMemUsed -= value; | |
3970 | } | |
3971 | break; | |
3972 | case kStatsTypeLatency: | |
3973 | if (action == kStatsActionSet) | |
3974 | { | |
3975 | if (value <= 4) | |
3976 | { | |
3977 | m->DNSSECStats.Latency0++; | |
3978 | } | |
3979 | else if (value <= 9) | |
3980 | { | |
3981 | m->DNSSECStats.Latency5++; | |
3982 | } | |
3983 | else if (value <= 19) | |
3984 | { | |
3985 | m->DNSSECStats.Latency10++; | |
3986 | } | |
3987 | else if (value <= 49) | |
3988 | { | |
3989 | m->DNSSECStats.Latency20++; | |
3990 | } | |
3991 | else if (value <= 99) | |
3992 | { | |
3993 | m->DNSSECStats.Latency50++; | |
3994 | } | |
3995 | else | |
3996 | { | |
3997 | m->DNSSECStats.Latency100++; | |
3998 | } | |
3999 | } | |
4000 | break; | |
4001 | case kStatsTypeExtraPackets: | |
4002 | if (action == kStatsActionSet) | |
4003 | { | |
4004 | if (value <= 2) | |
4005 | { | |
4006 | m->DNSSECStats.ExtraPackets0++; | |
4007 | } | |
4008 | else if (value <= 6) | |
4009 | { | |
4010 | m->DNSSECStats.ExtraPackets3++; | |
4011 | } | |
4012 | else if (value <= 9) | |
4013 | { | |
4014 | m->DNSSECStats.ExtraPackets7++; | |
4015 | } | |
4016 | else | |
4017 | { | |
4018 | m->DNSSECStats.ExtraPackets10++; | |
4019 | } | |
4020 | } | |
4021 | break; | |
4022 | case kStatsTypeStatus: | |
4023 | if (action == kStatsActionSet) | |
4024 | { | |
4025 | switch(value) | |
4026 | { | |
4027 | case DNSSEC_Secure: | |
4028 | m->DNSSECStats.SecureStatus++; | |
4029 | break; | |
4030 | case DNSSEC_Insecure: | |
4031 | m->DNSSECStats.InsecureStatus++; | |
4032 | break; | |
4033 | case DNSSEC_Indeterminate: | |
4034 | m->DNSSECStats.IndeterminateStatus++; | |
4035 | break; | |
4036 | case DNSSEC_Bogus: | |
4037 | m->DNSSECStats.BogusStatus++; | |
4038 | break; | |
4039 | case DNSSEC_NoResponse: | |
4040 | m->DNSSECStats.NoResponseStatus++; | |
4041 | break; | |
4042 | default: | |
4043 | LogMsg("BumpDNSSECStats: unknown status %d", value); | |
4044 | } | |
4045 | } | |
4046 | break; | |
4047 | case kStatsTypeMsgSize: | |
4048 | if (action == kStatsActionSet) | |
4049 | { | |
4050 | if (value <= 1024) | |
4051 | { | |
4052 | m->DNSSECStats.MsgSize0++; | |
4053 | } | |
4054 | else if (value <= 2048) | |
4055 | { | |
4056 | m->DNSSECStats.MsgSize1++; | |
4057 | } | |
4058 | else | |
4059 | { | |
4060 | m->DNSSECStats.MsgSize2++; | |
4061 | } | |
4062 | } | |
4063 | break; | |
4064 | case kStatsTypeProbe: | |
4065 | if (action == kStatsActionIncrement) | |
4066 | { | |
4067 | m->DNSSECStats.NumProbesSent += value; | |
4068 | } | |
4069 | break; | |
4070 | default: | |
4071 | LogMsg("BumpDNSSECStats: unknown type %d", type); | |
4072 | } | |
4073 | return; | |
4074 | } | |
4075 | ||
4076 | #else // !DNSSEC_DISABLED | |
4077 | ||
4078 | mDNSexport void VerifySignature(mDNS *const m, DNSSECVerifier *dv, DNSQuestion *q) | |
4079 | { | |
4080 | (void)m; | |
4081 | (void)dv; | |
4082 | (void)q; | |
4083 | } | |
4084 | ||
4085 | mDNSexport void BumpDNSSECStats(mDNS *const m, DNSSECStatsAction action, DNSSECStatsType type, mDNSu32 value) | |
4086 | { | |
4087 | (void)m; | |
4088 | (void)action; | |
4089 | (void)type; | |
4090 | (void)value; | |
4091 | } | |
4092 | ||
4093 | mDNSexport void InitializeQuestion(mDNS *const m, DNSQuestion *question, mDNSInterfaceID InterfaceID, const domainname *qname, mDNSu16 qtype, mDNSQuestionCallback *callback, void *context) | |
4094 | { | |
4095 | (void) m; | |
4096 | (void) question; | |
4097 | (void) InterfaceID; | |
4098 | (void) qname; | |
4099 | (void) qtype; | |
4100 | (void) callback; | |
4101 | (void) context; | |
4102 | } | |
4103 | ||
4104 | mDNSexport char *DNSSECStatusName(DNSSECStatus status) | |
4105 | { | |
4106 | (void) status; | |
4107 | ||
4108 | return mDNSNULL; | |
4109 | } | |
4110 | ||
4111 | #endif // !DNSSEC_DISABLED |