1 | /* -*- Mode: C; tab-width: 4 -*- |
---|
2 | * |
---|
3 | * Copyright (c) 2012 Apple Computer, Inc. All rights reserved. |
---|
4 | * |
---|
5 | * Licensed under the Apache License, Version 2.0 (the "License"); |
---|
6 | * you may not use this file except in compliance with the License. |
---|
7 | * You may obtain a copy of the License at |
---|
8 | * |
---|
9 | * http://www.apache.org/licenses/LICENSE-2.0 |
---|
10 | * |
---|
11 | * Unless required by applicable law or agreed to in writing, software |
---|
12 | * distributed under the License is distributed on an "AS IS" BASIS, |
---|
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
---|
14 | * See the License for the specific language governing permissions and |
---|
15 | * limitations under the License. |
---|
16 | */ |
---|
17 | |
---|
18 | // *************************************************************************** |
---|
19 | // DNSSECSupport.c: Platform specific support for DNSSEC like fetching root |
---|
20 | // trust anchor and dnssec probes etc. |
---|
21 | // *************************************************************************** |
---|
22 | |
---|
23 | #include "mDNSEmbeddedAPI.h" |
---|
24 | #include "DNSCommon.h" // For mDNS_Lock, mDNS_Random |
---|
25 | #include "dnssec.h" |
---|
26 | #include "DNSSECSupport.h" |
---|
27 | |
---|
28 | #include <CommonCrypto/CommonDigest.h> // For Hash algorithms SHA1 etc. |
---|
29 | |
---|
30 | // Following are needed for fetching the root trust anchor dynamically |
---|
31 | #include <CoreFoundation/CoreFoundation.h> |
---|
32 | #include <libxml2/libxml/parser.h> |
---|
33 | #include <libxml2/libxml/tree.h> |
---|
34 | #include <libxml2/libxml/xmlmemory.h> |
---|
35 | #include <notify.h> |
---|
36 | |
---|
37 | // 30 days |
---|
38 | #define ROOT_TA_UPDATE_INTERVAL (30 * 24 * 3600) // seconds |
---|
39 | |
---|
40 | // After 100 days, the test anchors are not valid. Just an arbitrary number |
---|
41 | // to configure validUntil. |
---|
42 | #define TEST_TA_EXPIRE_INTERVAL (100 * 24 * 4600) |
---|
43 | |
---|
44 | // When we can't fetch the root TA due to network errors etc., we start off a timer |
---|
45 | // to fire at 60 seconds and then keep doubling it till we fetch it |
---|
46 | #define InitialTAFetchInterval 60 |
---|
47 | |
---|
48 | #if !TARGET_OS_IPHONE |
---|
49 | DNSQuestion DNSSECProbeQuestion; |
---|
50 | #endif |
---|
51 | |
---|
52 | mDNSlocal int RegisterNotification(mDNS *const m, unsigned int interval); |
---|
53 | |
---|
54 | mDNSlocal void LinkTrustAnchor(mDNS *const m, TrustAnchor *ta) |
---|
55 | { |
---|
56 | int length = 0; |
---|
57 | int i; |
---|
58 | mDNSu8 *p; |
---|
59 | TrustAnchor **t = &m->TrustAnchors; |
---|
60 | char buffer[256]; |
---|
61 | |
---|
62 | while (*t) |
---|
63 | t = &((*t)->next); |
---|
64 | *t = ta; |
---|
65 | |
---|
66 | buffer[0] = 0; |
---|
67 | p = ta->rds.digest; |
---|
68 | for (i = 0; i < ta->digestLen; i++) |
---|
69 | { |
---|
70 | length += mDNS_snprintf(buffer+length, sizeof(buffer)-1-length, "%x", p[i]); |
---|
71 | } |
---|
72 | LogInfo("LinkTrustAnchor: Zone %##s, keytag %d, alg %d, digestType %d, digestLen %d, digest %s", ta->zone.c, ta->rds.keyTag, |
---|
73 | ta->rds.alg, ta->rds.digestType, ta->digestLen, buffer); |
---|
74 | } |
---|
75 | |
---|
76 | mDNSlocal void DelTrustAnchor(mDNS *const m, const domainname *zone) |
---|
77 | { |
---|
78 | TrustAnchor **ta = &m->TrustAnchors; |
---|
79 | TrustAnchor *tmp; |
---|
80 | |
---|
81 | while (*ta && !SameDomainName(&(*ta)->zone, zone)) |
---|
82 | ta = &(*ta)->next; |
---|
83 | |
---|
84 | // First time, we won't find the TrustAnchor in the list as it has |
---|
85 | // not been added. |
---|
86 | if (!(*ta)) |
---|
87 | return; |
---|
88 | |
---|
89 | tmp = *ta; |
---|
90 | *ta = (*ta)->next; // Cut this record from the list |
---|
91 | tmp->next = mDNSNULL; |
---|
92 | if (tmp->rds.digest) |
---|
93 | mDNSPlatformMemFree(tmp->rds.digest); |
---|
94 | mDNSPlatformMemFree(tmp); |
---|
95 | } |
---|
96 | |
---|
97 | mDNSlocal void AddTrustAnchor(mDNS *const m, const domainname *zone, mDNSu16 keytag, mDNSu8 alg, mDNSu8 digestType, int diglen, |
---|
98 | mDNSu8 *digest) |
---|
99 | { |
---|
100 | TrustAnchor *ta, *tmp; |
---|
101 | mDNSu32 t = (mDNSu32) time(NULL); |
---|
102 | |
---|
103 | // Check for duplicates |
---|
104 | tmp = m->TrustAnchors; |
---|
105 | while (tmp) |
---|
106 | { |
---|
107 | if (SameDomainName(zone, &tmp->zone) && tmp->rds.keyTag == keytag && tmp->rds.alg == alg && tmp->rds.digestType == digestType && |
---|
108 | !memcmp(tmp->rds.digest, digest, diglen)) |
---|
109 | { |
---|
110 | LogMsg("AddTrustAnchors: Found a duplicate"); |
---|
111 | return; |
---|
112 | } |
---|
113 | tmp = tmp->next; |
---|
114 | } |
---|
115 | |
---|
116 | ta = (TrustAnchor *)mDNSPlatformMemAllocate(sizeof(TrustAnchor)); |
---|
117 | if (!ta) |
---|
118 | { |
---|
119 | LogMsg("AddTrustAnchor: malloc failure ta"); |
---|
120 | return; |
---|
121 | } |
---|
122 | ta->rds.keyTag = keytag; |
---|
123 | ta->rds.alg = alg; |
---|
124 | ta->rds.digestType = digestType; |
---|
125 | ta->rds.digest = digest; |
---|
126 | ta->digestLen = diglen; |
---|
127 | ta->validFrom = t; |
---|
128 | ta->validUntil = t + TEST_TA_EXPIRE_INTERVAL; |
---|
129 | AssignDomainName(&ta->zone, zone); |
---|
130 | ta->next = mDNSNULL; |
---|
131 | |
---|
132 | LinkTrustAnchor(m, ta); |
---|
133 | } |
---|
134 | |
---|
135 | #define HexVal(X) ( ((X) >= '0' && (X) <= '9') ? ((X) - '0' ) : \ |
---|
136 | ((X) >= 'A' && (X) <= 'F') ? ((X) - 'A' + 10) : \ |
---|
137 | ((X) >= 'a' && (X) <= 'f') ? ((X) - 'a' + 10) : -1) |
---|
138 | |
---|
139 | mDNSlocal mDNSu8 *ConvertDigest(char *digest, int digestType, int *diglen) |
---|
140 | { |
---|
141 | int i, j; |
---|
142 | mDNSu8 *dig; |
---|
143 | |
---|
144 | switch (digestType) |
---|
145 | { |
---|
146 | case SHA1_DIGEST_TYPE: |
---|
147 | *diglen = CC_SHA1_DIGEST_LENGTH; |
---|
148 | break; |
---|
149 | case SHA256_DIGEST_TYPE: |
---|
150 | *diglen = CC_SHA256_DIGEST_LENGTH; |
---|
151 | break; |
---|
152 | default: |
---|
153 | LogMsg("ConvertDigest: digest type %d not supported", digestType); |
---|
154 | return mDNSNULL; |
---|
155 | } |
---|
156 | dig = mDNSPlatformMemAllocate(*diglen); |
---|
157 | if (!dig) |
---|
158 | { |
---|
159 | LogMsg("ConvertDigest: malloc failure"); |
---|
160 | return mDNSNULL; |
---|
161 | } |
---|
162 | |
---|
163 | for (j=0,i=0; i<*diglen*2; i+=2) |
---|
164 | { |
---|
165 | int l, h; |
---|
166 | l = HexVal(digest[i]); |
---|
167 | h = HexVal(digest[i+1]); |
---|
168 | if (l<0 || h<0) { LogMsg("ConvertDigest: Cannot convert digest"); return NULL;} |
---|
169 | dig[j++] = (mDNSu8)((l << 4) | h); |
---|
170 | } |
---|
171 | return dig; |
---|
172 | } |
---|
173 | |
---|
174 | // All the children are in a linked list |
---|
175 | // |
---|
176 | // <TrustAnchor> has two children: <Zone> and <KeyDigest> |
---|
177 | // <KeyDigest> has four children <KeyTag> <Algorithm> <DigestType> <Digest> |
---|
178 | // |
---|
179 | // Returns false if failed to parse the element i.e., malformed xml document. |
---|
180 | // Validity of the actual values itself is done outside the function. |
---|
181 | mDNSlocal mDNSBool ParseElementChildren(xmlDocPtr tadoc, xmlNode *node, TrustAnchor *ta) |
---|
182 | { |
---|
183 | xmlNode *cur_node; |
---|
184 | xmlChar *val1, *val2, *val; |
---|
185 | char *invalid = NULL; |
---|
186 | |
---|
187 | val = val1 = val2 = NULL; |
---|
188 | |
---|
189 | for (cur_node = node; cur_node; cur_node = cur_node->next) |
---|
190 | { |
---|
191 | invalid = NULL; |
---|
192 | val1 = val2 = NULL; |
---|
193 | |
---|
194 | val = xmlNodeListGetString(tadoc, cur_node->xmlChildrenNode, 1); |
---|
195 | if (!val) |
---|
196 | { |
---|
197 | LogInfo("ParseElementChildren: NULL value for %s", cur_node->name); |
---|
198 | continue; |
---|
199 | } |
---|
200 | if (!xmlStrcmp(cur_node->name, (const xmlChar *)"Zone")) |
---|
201 | { |
---|
202 | // MaeDomainNameFromDNSNameString does not work for "." |
---|
203 | if (!xmlStrcmp(val, (const xmlChar *)".")) |
---|
204 | { |
---|
205 | ta->zone.c[0] = 0; |
---|
206 | } |
---|
207 | else if (!MakeDomainNameFromDNSNameString(&ta->zone, (char *)val)) |
---|
208 | { |
---|
209 | LogMsg("ParseElementChildren: Cannot parse Zone %s", val); |
---|
210 | goto error; |
---|
211 | } |
---|
212 | else |
---|
213 | { |
---|
214 | LogInfo("ParseElementChildren: Element %s, value %##s", cur_node->name, ta->zone.c); |
---|
215 | } |
---|
216 | } |
---|
217 | else if (!xmlStrcmp(cur_node->name, (const xmlChar *)"KeyTag")) |
---|
218 | { |
---|
219 | ta->rds.keyTag = strtol((const char *)val, &invalid, 10); |
---|
220 | if (*invalid != '\0') |
---|
221 | { |
---|
222 | LogMsg("ParseElementChildren: KeyTag invalid character %d", *invalid); |
---|
223 | goto error; |
---|
224 | } |
---|
225 | else |
---|
226 | { |
---|
227 | LogInfo("ParseElementChildren: Element %s, value %d", cur_node->name, ta->rds.keyTag); |
---|
228 | } |
---|
229 | } |
---|
230 | else if (!xmlStrcmp(cur_node->name, (const xmlChar *)"Algorithm")) |
---|
231 | { |
---|
232 | ta->rds.alg = strtol((const char *)val, &invalid, 10); |
---|
233 | if (*invalid != '\0') |
---|
234 | { |
---|
235 | LogMsg("ParseElementChildren: Algorithm invalid character %c", *invalid); |
---|
236 | goto error; |
---|
237 | } |
---|
238 | else |
---|
239 | { |
---|
240 | LogInfo("ParseElementChildren: Element %s, value %d", cur_node->name, ta->rds.alg); |
---|
241 | } |
---|
242 | } |
---|
243 | else if (!xmlStrcmp(cur_node->name, (const xmlChar *)"DigestType")) |
---|
244 | { |
---|
245 | ta->rds.digestType = strtol((const char *)val, &invalid, 10); |
---|
246 | if (*invalid != '\0') |
---|
247 | { |
---|
248 | LogMsg("ParseElementChildren: Algorithm invalid character %c", *invalid); |
---|
249 | goto error; |
---|
250 | } |
---|
251 | else |
---|
252 | { |
---|
253 | LogInfo("ParseElementChildren: Element %s, value %d", cur_node->name, ta->rds.digestType); |
---|
254 | } |
---|
255 | } |
---|
256 | else if (!xmlStrcmp(cur_node->name, (const xmlChar *)"Digest")) |
---|
257 | { |
---|
258 | int diglen; |
---|
259 | mDNSu8 *dig = ConvertDigest((char *)val, ta->rds.digestType, &diglen); |
---|
260 | if (dig) |
---|
261 | { |
---|
262 | LogInfo("ParseElementChildren: Element %s, digest %s", cur_node->name, val); |
---|
263 | ta->digestLen = diglen; |
---|
264 | ta->rds.digest = dig; |
---|
265 | } |
---|
266 | else |
---|
267 | { |
---|
268 | LogMsg("ParseElementChildren: Element %s, NULL digest", cur_node->name); |
---|
269 | goto error; |
---|
270 | } |
---|
271 | } |
---|
272 | else if (!xmlStrcmp(cur_node->name, (const xmlChar *)"KeyDigest")) |
---|
273 | { |
---|
274 | struct tm tm; |
---|
275 | val1 = xmlGetProp(cur_node, (const xmlChar *)"validFrom"); |
---|
276 | if (val1) |
---|
277 | { |
---|
278 | char *s = strptime((const char *)val1, "%Y-%m-%dT%H:%M:%S", &tm); |
---|
279 | if (!s) |
---|
280 | { |
---|
281 | LogMsg("ParseElementChildren: Parsing ValidFrom failed %s", val1); |
---|
282 | goto error; |
---|
283 | } |
---|
284 | else |
---|
285 | { |
---|
286 | ta->validFrom = (mDNSu32)timegm(&tm); |
---|
287 | } |
---|
288 | } |
---|
289 | val2 = xmlGetProp(cur_node, (const xmlChar *)"validUntil"); |
---|
290 | if (val2) |
---|
291 | { |
---|
292 | char *s = strptime((const char *)val2, "%Y-%m-%dT%H:%M:%S", &tm); |
---|
293 | if (!s) |
---|
294 | { |
---|
295 | LogMsg("ParseElementChildren: Parsing ValidFrom failed %s", val2); |
---|
296 | goto error; |
---|
297 | } |
---|
298 | else |
---|
299 | { |
---|
300 | ta->validUntil = (mDNSu32)timegm(&tm); |
---|
301 | } |
---|
302 | } |
---|
303 | else |
---|
304 | { |
---|
305 | // If there is no validUntil, set it to the next probing interval |
---|
306 | mDNSu32 t = (mDNSu32) time(NULL); |
---|
307 | ta->validUntil = t + ROOT_TA_UPDATE_INTERVAL; |
---|
308 | } |
---|
309 | LogInfo("ParseElementChildren: ValidFrom time %u, validUntil %u", (unsigned)ta->validFrom, (unsigned)ta->validUntil); |
---|
310 | } |
---|
311 | if (val1) |
---|
312 | xmlFree(val1); |
---|
313 | if (val2) |
---|
314 | xmlFree(val2); |
---|
315 | if (val) |
---|
316 | xmlFree(val); |
---|
317 | } |
---|
318 | return mDNStrue; |
---|
319 | error: |
---|
320 | if (val1) |
---|
321 | xmlFree(val1); |
---|
322 | if (val2) |
---|
323 | xmlFree(val2); |
---|
324 | if (val) |
---|
325 | xmlFree(val); |
---|
326 | return mDNSfalse; |
---|
327 | } |
---|
328 | |
---|
329 | mDNSlocal mDNSBool ValidateTrustAnchor(TrustAnchor *ta) |
---|
330 | { |
---|
331 | time_t currTime = time(NULL); |
---|
332 | |
---|
333 | // Currently only support trust anchor for root. |
---|
334 | if (!SameDomainName(&ta->zone, (const domainname *)"\000")) |
---|
335 | { |
---|
336 | LogInfo("ParseElementChildren: Zone %##s not root", ta->zone.c); |
---|
337 | return mDNSfalse; |
---|
338 | } |
---|
339 | |
---|
340 | switch (ta->rds.digestType) |
---|
341 | { |
---|
342 | case SHA1_DIGEST_TYPE: |
---|
343 | if (ta->digestLen != CC_SHA1_DIGEST_LENGTH) |
---|
344 | { |
---|
345 | LogMsg("ValidateTrustAnchor: Invalid digest len %d for SHA1", ta->digestLen); |
---|
346 | return mDNSfalse; |
---|
347 | } |
---|
348 | break; |
---|
349 | case SHA256_DIGEST_TYPE: |
---|
350 | if (ta->digestLen != CC_SHA256_DIGEST_LENGTH) |
---|
351 | { |
---|
352 | LogMsg("ValidateTrustAnchor: Invalid digest len %d for SHA256", ta->digestLen); |
---|
353 | return mDNSfalse; |
---|
354 | } |
---|
355 | break; |
---|
356 | default: |
---|
357 | LogMsg("ValidateTrustAnchor: digest type %d not supported", ta->rds.digestType); |
---|
358 | return mDNSfalse; |
---|
359 | } |
---|
360 | if (!ta->rds.digest) |
---|
361 | { |
---|
362 | LogMsg("ValidateTrustAnchor: digest NULL for %d", ta->rds.digestType); |
---|
363 | return mDNSfalse; |
---|
364 | } |
---|
365 | switch (ta->rds.alg) |
---|
366 | { |
---|
367 | case CRYPTO_RSA_SHA512: |
---|
368 | case CRYPTO_RSA_SHA256: |
---|
369 | case CRYPTO_RSA_NSEC3_SHA1: |
---|
370 | case CRYPTO_RSA_SHA1: |
---|
371 | break; |
---|
372 | default: |
---|
373 | LogMsg("ValidateTrustAnchor: Algorithm %d not supported", ta->rds.alg); |
---|
374 | return mDNSfalse; |
---|
375 | } |
---|
376 | |
---|
377 | if (DNS_SERIAL_LT(currTime, ta->validFrom)) |
---|
378 | { |
---|
379 | LogMsg("ValidateTrustAnchor: Invalid ValidFrom time %u, currtime %u", (unsigned)ta->validFrom, (unsigned)currTime); |
---|
380 | return mDNSfalse; |
---|
381 | } |
---|
382 | if (DNS_SERIAL_LT(ta->validUntil, currTime)) |
---|
383 | { |
---|
384 | LogMsg("ValidateTrustAnchor: Invalid ValidUntil time %u, currtime %u", (unsigned)ta->validUntil, (unsigned)currTime); |
---|
385 | return mDNSfalse; |
---|
386 | } |
---|
387 | return mDNStrue; |
---|
388 | } |
---|
389 | |
---|
390 | mDNSlocal mDNSBool ParseElement(xmlDocPtr tadoc, xmlNode * a_node, TrustAnchor *ta) |
---|
391 | { |
---|
392 | xmlNode *cur_node = NULL; |
---|
393 | |
---|
394 | for (cur_node = a_node; cur_node; cur_node = cur_node->next) |
---|
395 | { |
---|
396 | if (cur_node->type == XML_ELEMENT_NODE) |
---|
397 | { |
---|
398 | // There could be multiple KeyDigests per TrustAnchor. We keep parsing till we |
---|
399 | // reach the last one or we encounter an error in parsing the document. |
---|
400 | if (!xmlStrcmp(cur_node->name, (const xmlChar *)"KeyDigest")) |
---|
401 | { |
---|
402 | if (ta->rds.digest) |
---|
403 | mDNSPlatformMemFree(ta->rds.digest); |
---|
404 | ta->rds.digestType = 0; |
---|
405 | ta->digestLen = 0; |
---|
406 | } |
---|
407 | if (!ParseElementChildren(tadoc, cur_node->children, ta)) |
---|
408 | return mDNSfalse; |
---|
409 | if (!ParseElement(tadoc, cur_node->children, ta)) |
---|
410 | return mDNSfalse; |
---|
411 | } |
---|
412 | } |
---|
413 | return mDNStrue; |
---|
414 | } |
---|
415 | |
---|
416 | mDNSlocal void TAComplete(mDNS *const m, void *context) |
---|
417 | { |
---|
418 | TrustAnchor *ta = (TrustAnchor *)context; |
---|
419 | |
---|
420 | DelTrustAnchor(m, &ta->zone); |
---|
421 | LinkTrustAnchor(m, ta); |
---|
422 | } |
---|
423 | |
---|
424 | mDNSlocal void FetchRootTA(mDNS *const m) |
---|
425 | { |
---|
426 | CFStringRef urlString = CFSTR("https://data.iana.org/root-anchors/root-anchors.xml"); |
---|
427 | CFDataRef xmlData; |
---|
428 | CFStringRef fileRef = NULL; |
---|
429 | const char *xmlFileName = NULL; |
---|
430 | char buf[512]; |
---|
431 | CFURLRef url = NULL; |
---|
432 | static unsigned int RootTAFetchInterval = InitialTAFetchInterval; |
---|
433 | |
---|
434 | (void) m; |
---|
435 | |
---|
436 | TrustAnchor *ta = (TrustAnchor *)mDNSPlatformMemAllocate(sizeof(TrustAnchor)); |
---|
437 | if (!ta) |
---|
438 | { |
---|
439 | LogMsg("FetchRootTA: TrustAnchor alloc failed"); |
---|
440 | return; |
---|
441 | } |
---|
442 | memset(ta, 0, sizeof(TrustAnchor)); |
---|
443 | |
---|
444 | url = CFURLCreateWithString(NULL, urlString, NULL); |
---|
445 | if (!url) |
---|
446 | { |
---|
447 | LogMsg("FetchRootTA: CFURLCreateWithString error"); |
---|
448 | mDNSPlatformMemFree(ta); |
---|
449 | return; |
---|
450 | } |
---|
451 | |
---|
452 | // If we can't fetch the XML file e.g., network problems, trigger a timer. All other failures |
---|
453 | // should hardly happen in practice for which schedule the normal interval to refetch the TA. |
---|
454 | if (!CFURLCreateDataAndPropertiesFromResource(kCFAllocatorDefault, url, &xmlData, NULL, NULL, NULL)) |
---|
455 | { |
---|
456 | LogInfo("FetchRootTA: CFURLCreateDataAndPropertiesFromResource error"); |
---|
457 | CFRelease(url); |
---|
458 | mDNSPlatformMemFree(ta); |
---|
459 | RegisterNotification(m, RootTAFetchInterval); |
---|
460 | RootTAFetchInterval *= 2 + 1; |
---|
461 | return; |
---|
462 | } |
---|
463 | |
---|
464 | // get the name of the last component from the url, libxml will use it if |
---|
465 | // it has to report an error |
---|
466 | fileRef = CFURLCopyLastPathComponent(url); |
---|
467 | if (fileRef) |
---|
468 | { |
---|
469 | xmlFileName = CFStringGetCStringPtr(fileRef, kCFStringEncodingUTF8); |
---|
470 | if (!xmlFileName) |
---|
471 | { |
---|
472 | if (!CFStringGetCString(fileRef, buf, sizeof(buf), kCFStringEncodingUTF8) ) |
---|
473 | strlcpy(buf, "nofile.xml", sizeof(buf)); |
---|
474 | xmlFileName = (const char *)buf; |
---|
475 | } |
---|
476 | } |
---|
477 | |
---|
478 | // Parse the XML and get the CFXMLTree. |
---|
479 | xmlDocPtr tadoc = xmlReadMemory((const char*)CFDataGetBytePtr(xmlData), |
---|
480 | (int)CFDataGetLength(xmlData), xmlFileName, NULL, 0); |
---|
481 | |
---|
482 | CFRelease(fileRef); |
---|
483 | CFRelease(url); |
---|
484 | CFRelease(xmlData); |
---|
485 | |
---|
486 | if (!tadoc) |
---|
487 | { |
---|
488 | LogMsg("FetchRootTA: xmlReadMemory failed"); |
---|
489 | goto done; |
---|
490 | } |
---|
491 | |
---|
492 | xmlNodePtr root = xmlDocGetRootElement(tadoc); |
---|
493 | if (!root) |
---|
494 | { |
---|
495 | LogMsg("FetchRootTA: Cannot get root element"); |
---|
496 | goto done; |
---|
497 | } |
---|
498 | |
---|
499 | if (ParseElement(tadoc, root, ta) && ValidateTrustAnchor(ta)) |
---|
500 | { |
---|
501 | // Do the actual addition of TA on the main queue. |
---|
502 | mDNSPlatformDispatchAsync(m, ta, TAComplete); |
---|
503 | } |
---|
504 | else |
---|
505 | { |
---|
506 | if (ta->rds.digest) |
---|
507 | mDNSPlatformMemFree(ta->rds.digest); |
---|
508 | mDNSPlatformMemFree(ta); |
---|
509 | } |
---|
510 | done: |
---|
511 | if (tadoc) |
---|
512 | xmlFreeDoc(tadoc); |
---|
513 | RegisterNotification(m, ROOT_TA_UPDATE_INTERVAL); |
---|
514 | RootTAFetchInterval = InitialTAFetchInterval; |
---|
515 | return; |
---|
516 | } |
---|
517 | |
---|
518 | |
---|
519 | #if APPLE_OSX_mDNSResponder && !TARGET_OS_IPHONE |
---|
520 | mDNSlocal void DNSSECProbeCallback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord) |
---|
521 | { |
---|
522 | if (!AddRecord) |
---|
523 | return; |
---|
524 | |
---|
525 | mDNS_Lock(m); |
---|
526 | if ((m->timenow - question->StopTime) >= 0) |
---|
527 | { |
---|
528 | mDNS_Unlock(m); |
---|
529 | LogDNSSEC("DNSSECProbeCallback: Question %##s (%s) timed out", question->qname.c, DNSTypeName(question->qtype)); |
---|
530 | mDNS_StopQuery(m, question); |
---|
531 | return; |
---|
532 | } |
---|
533 | mDNS_Unlock(m); |
---|
534 | |
---|
535 | // Wait till we get the DNSSEC results. If we get a negative response e.g., no DNS servers, the |
---|
536 | // question will be restarted by the core and we should have the DNSSEC results eventually. |
---|
537 | if (AddRecord != QC_dnssec) |
---|
538 | { |
---|
539 | LogDNSSEC("DNSSECProbeCallback: Question %##s (%s)", question->qname.c, DNSTypeName(question->qtype), RRDisplayString(m, answer)); |
---|
540 | return; |
---|
541 | } |
---|
542 | |
---|
543 | LogDNSSEC("DNSSECProbeCallback: Question %##s (%s), DNSSEC status %s", question->qname.c, DNSTypeName(question->qtype), |
---|
544 | DNSSECStatusName(question->ValidationStatus)); |
---|
545 | |
---|
546 | mDNS_StopQuery(m, question); |
---|
547 | } |
---|
548 | |
---|
549 | // Send a DNSSEC probe just for the sake of collecting DNSSEC statistics. |
---|
550 | mDNSexport void DNSSECProbe(mDNS *const m) |
---|
551 | { |
---|
552 | mDNSu32 rand; |
---|
553 | |
---|
554 | if (DNSSECProbeQuestion.ThisQInterval != -1) |
---|
555 | return; |
---|
556 | |
---|
557 | rand = mDNSRandom(0x3FFFFFFF) % 100; |
---|
558 | // Probe 5% of the time |
---|
559 | if (rand > 5) |
---|
560 | return; |
---|
561 | |
---|
562 | mDNS_DropLockBeforeCallback(); |
---|
563 | InitializeQuestion(m, &DNSSECProbeQuestion, mDNSInterface_Any, (const domainname *)"\003com", kDNSType_DS, DNSSECProbeCallback, mDNSNULL); |
---|
564 | DNSSECProbeQuestion.ValidatingResponse = 0; |
---|
565 | DNSSECProbeQuestion.ValidationRequired = DNSSEC_VALIDATION_SECURE; |
---|
566 | |
---|
567 | BumpDNSSECStats(m, kStatsActionIncrement, kStatsTypeProbe, 1); |
---|
568 | mDNS_StartQuery(m, &DNSSECProbeQuestion); |
---|
569 | mDNS_ReclaimLockAfterCallback(); |
---|
570 | } |
---|
571 | #endif // APPLE_OSX_mDNSResponder && !TARGET_OS_IPHONE |
---|
572 | |
---|
573 | // For now we fetch the root trust anchor and update the local copy |
---|
574 | mDNSexport void UpdateTrustAnchors(mDNS *const m) |
---|
575 | { |
---|
576 | // Register for a notification to fire immediately which in turn will update |
---|
577 | // the trust anchor |
---|
578 | if (RegisterNotification(m, 1)) |
---|
579 | { |
---|
580 | LogMsg("UpdateTrustAnchors: ERROR!! failed to register for notification"); |
---|
581 | } |
---|
582 | } |
---|
583 | |
---|
584 | mDNSlocal int RegisterNotification(mDNS *const m, unsigned int interval) |
---|
585 | { |
---|
586 | int len = strlen("com.apple.system.notify.service.timer:+") + 21; // 21 bytes to accomodate the interval |
---|
587 | char buffer[len]; |
---|
588 | unsigned int blen; |
---|
589 | int status; |
---|
590 | |
---|
591 | // Starting "interval" second from now (+ below indicates relative) register for a notification |
---|
592 | blen = mDNS_snprintf(buffer, sizeof(buffer), "com.apple.system.notify.service.timer:+%us", interval); |
---|
593 | if (blen >= sizeof(buffer)) |
---|
594 | { |
---|
595 | LogMsg("RegisterNotification: Buffer too small blen %d, buffer size %d", blen, sizeof(buffer)); |
---|
596 | return -1; |
---|
597 | } |
---|
598 | LogInfo("RegisterNotification: buffer %s", buffer); |
---|
599 | if (m->notifyToken) |
---|
600 | { |
---|
601 | notify_cancel(m->notifyToken); |
---|
602 | m->notifyToken = 0; |
---|
603 | } |
---|
604 | status = notify_register_dispatch(buffer, &m->notifyToken, |
---|
605 | dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), |
---|
606 | ^(int t) { (void) t; FetchRootTA(m); }); |
---|
607 | |
---|
608 | if (status != NOTIFY_STATUS_OK) |
---|
609 | { |
---|
610 | LogMsg("RegisterNotification: notify_register_dispatch failed"); |
---|
611 | return -1; |
---|
612 | } |
---|
613 | return 0; |
---|
614 | } |
---|
615 | |
---|
616 | mDNSexport mStatus DNSSECPlatformInit(mDNS *const m) |
---|
617 | { |
---|
618 | int diglen; |
---|
619 | |
---|
620 | m->TrustAnchors = mDNSNULL; |
---|
621 | m->notifyToken = 0; |
---|
622 | |
---|
623 | // Add a couple of trust anchors for testing purposes. |
---|
624 | mDNSlocal const domainname *testZone = (const domainname*)"\007example"; |
---|
625 | |
---|
626 | char *digest = "F122E47B5B7D2B6A5CC0A21EADA11D96BB9CC927"; |
---|
627 | mDNSu8 *dig = ConvertDigest(digest, 1, &diglen); |
---|
628 | AddTrustAnchor(m, testZone, 23044, 5, 1, diglen, dig); |
---|
629 | |
---|
630 | char *digest1 = "D795AE5E1AFB200C6139474199B70EAD3F3484553FD97BE5A43704B8A4791F21"; |
---|
631 | dig = ConvertDigest(digest1, 2, &diglen); |
---|
632 | AddTrustAnchor(m, testZone, 23044, 5, 2, diglen, dig); |
---|
633 | |
---|
634 | // Add the TA for root zone manually here. We will dynamically fetch the root TA and |
---|
635 | // update it shortly. If that fails e.g., disconnected from the network, we still |
---|
636 | // have something to work with. |
---|
637 | char *digest2 = "49AAC11D7B6F6446702E54A1607371607A1A41855200FD2CE1CDDE32F24E8FB5"; |
---|
638 | dig = ConvertDigest(digest2, 2, &diglen); |
---|
639 | AddTrustAnchor(m, (const domainname *)"\000", 19036, 8, 2, diglen, dig); |
---|
640 | |
---|
641 | #if !TARGET_OS_IPHONE |
---|
642 | DNSSECProbeQuestion.ThisQInterval = -1; |
---|
643 | #endif |
---|
644 | return mStatus_NoError; |
---|
645 | } |
---|