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