]> git.saurik.com Git - apple/security.git/blob - OSX/sec/securityd/OTATrustUtilities.c
Security-57336.10.29.tar.gz
[apple/security.git] / OSX / sec / securityd / OTATrustUtilities.c
1 /*
2 * Copyright (c) 2003-2004,2006-2010,2013-2015 Apple Inc. All Rights Reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 *
23 * OTATrustUtilities.c
24 */
25
26 #include "OTATrustUtilities.h"
27
28 #include <errno.h>
29 #include <fcntl.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <unistd.h>
34 #include <dirent.h>
35 #include <sys/syslimits.h>
36 #include <sys/mman.h>
37 #include <sys/stat.h>
38 #include <CoreFoundation/CoreFoundation.h>
39 #include <ftw.h>
40 #include "SecFramework.h"
41 #include <pthread.h>
42 #include <sys/param.h>
43 #include <stdlib.h>
44 #include <utilities/SecCFRelease.h>
45 #include <utilities/SecCFError.h>
46 #include <utilities/SecCFWrappers.h>
47 #include <Security/SecBasePriv.h>
48 #include <Security/SecCertificatePriv.h>
49 #include <Security/SecFramework.h>
50 #include <dispatch/dispatch.h>
51 #include <CommonCrypto/CommonDigest.h>
52
53 //#define VERBOSE_LOGGING 1
54
55 #if VERBOSE_LOGGING
56
57 static void TestOTALog(const char* sz, ...)
58 {
59 va_list va;
60 va_start(va, sz);
61
62 FILE* fp = fopen("/tmp/secd_OTAUtil.log", "a");
63 if (NULL != fp)
64 {
65 vfprintf(fp, sz, va);
66 fclose(fp);
67 }
68 va_end(va);
69 }
70
71 static void TestOTAResourceLog(const char *msg,
72 CFStringRef resourceName,
73 CFStringRef resourceType,
74 CFStringRef subDirName,
75 CFURLRef url)
76 {
77 CFStringRef tmpStr = NULL;
78 CFIndex maxLength = 0;
79 char *buf = NULL;
80
81 tmpStr = CFStringCreateWithFormat(kCFAllocatorDefault, NULL,
82 CFSTR("%s (name=%@, type=%@, subdir=%@), url=%@"),
83 msg, resourceName, resourceType, subDirName, url);
84 if (tmpStr) {
85 maxLength = CFStringGetMaximumSizeForEncoding(CFStringGetLength(tmpStr), kCFStringEncodingUTF8) + 1;
86 buf = (char*) malloc(maxLength);
87 } else {
88 TestOTALog("TestOTAResourceLog: failed to create string of length %ld\n", (long)maxLength);
89 }
90 if (buf) {
91 if (CFStringGetCString(tmpStr, buf, (CFIndex)maxLength, kCFStringEncodingUTF8)) {
92 TestOTALog("%s\n", buf);
93 }
94 free(buf);
95 }
96 CFReleaseSafe(tmpStr);
97 }
98
99 #else
100
101 #define TestOTALog(sz, ...)
102 #define TestOTAResourceLog(msg, resourceName, resourceType, subDirName, url)
103
104 #endif
105
106
107 //#define NEW_LOCATION 1
108
109 #if NEW_LOCATION
110 static const char* kBaseAssetDirectory = "/var/OTAPKI/Assets";
111 #else
112 static const char* kBaseAssetDirectory = "/var/Keychains/Assets";
113 #endif
114
115 static const char* kVersionDirectoryNamePrefix = "Version_";
116 static const char* kNumberString = "%d";
117
118 struct index_record
119 {
120 unsigned char hash[CC_SHA1_DIGEST_LENGTH];
121 uint32_t offset;
122 };
123 typedef struct index_record index_record;
124
125
126 struct _OpaqueSecOTAPKI
127 {
128 CFRuntimeBase _base;
129 CFSetRef _blackListSet;
130 CFSetRef _grayListSet;
131 CFDictionaryRef _allowList;
132 CFArrayRef _trustedCTLogs;
133 CFArrayRef _escrowCertificates;
134 CFArrayRef _escrowPCSCertificates;
135 CFDictionaryRef _evPolicyToAnchorMapping;
136 CFDictionaryRef _anchorLookupTable;
137 const char* _anchorTable;
138 int _assetVersion;
139 };
140
141 CFGiblisFor(SecOTAPKI)
142
143 static CF_RETURNS_RETAINED CFStringRef SecOTAPKICopyFormatDescription(CFTypeRef cf, CFDictionaryRef formatOptions)
144 {
145 SecOTAPKIRef otapkiRef = (SecOTAPKIRef)cf;
146 return CFStringCreateWithFormat(kCFAllocatorDefault,NULL,CFSTR("<SecOTAPKIRef: version %d>"), otapkiRef->_assetVersion);
147 }
148
149 static void SecOTAPKIDestroy(CFTypeRef cf)
150 {
151 SecOTAPKIRef otapkiref = (SecOTAPKIRef)cf;
152
153 CFReleaseNull(otapkiref->_blackListSet);
154 CFReleaseNull(otapkiref->_grayListSet);
155 CFReleaseNull(otapkiref->_escrowCertificates);
156 CFReleaseNull(otapkiref->_escrowPCSCertificates);
157
158 CFReleaseNull(otapkiref->_evPolicyToAnchorMapping);
159 CFReleaseNull(otapkiref->_anchorLookupTable);
160
161 free((void *)otapkiref->_anchorTable);
162
163 CFReleaseNull(otapkiref->_trustedCTLogs);
164 }
165
166 static CFDataRef SecOTACopyFileContents(const char *path)
167 {
168 CFMutableDataRef data = NULL;
169 int fd = open(path, O_RDONLY, 0666);
170
171 if (fd == -1)
172 {
173 goto badFile;
174 }
175
176 off_t fsize = lseek(fd, 0, SEEK_END);
177 if (fsize == (off_t)-1)
178 {
179 goto badFile;
180 }
181
182 if (fsize > (off_t)INT32_MAX)
183 {
184 goto badFile;
185 }
186
187 data = CFDataCreateMutable(kCFAllocatorDefault, (CFIndex)fsize);
188 if (NULL == data)
189 {
190 goto badFile;
191 }
192
193 CFDataSetLength(data, (CFIndex)fsize);
194 void *buf = CFDataGetMutableBytePtr(data);
195 if (NULL == buf)
196 {
197 goto badFile;
198 }
199
200 off_t total_read = 0;
201 while (total_read < fsize)
202 {
203 ssize_t bytes_read;
204
205 bytes_read = pread(fd, buf, (size_t)(fsize - total_read), total_read);
206 if (bytes_read == -1)
207 {
208 goto badFile;
209 }
210 if (bytes_read == 0)
211 {
212 goto badFile;
213 }
214 total_read += bytes_read;
215 }
216
217 close(fd);
218 return data;
219
220 badFile:
221 if (fd != -1)
222 {
223 close(fd);
224 }
225
226 if (data)
227 {
228 CFRelease(data);
229 }
230
231 return NULL;
232 }
233
234 static Boolean PathExists(const char* path, size_t* pFileSize)
235 {
236 TestOTALog("In PathExists: checking path %s\n", path);
237 Boolean result = false;
238 struct stat sb;
239
240 if (NULL != pFileSize)
241 {
242 *pFileSize = 0;
243 }
244
245 int stat_result = stat(path, &sb);
246 result = (stat_result == 0);
247
248
249 if (result)
250 {
251 TestOTALog("In PathExists: stat returned 0 for %s\n", path);
252 if (S_ISDIR(sb.st_mode))
253 {
254 TestOTALog("In PathExists: %s is a directory\n", path);
255 // It is a directory
256 ;
257 }
258 else
259 {
260 TestOTALog("In PathExists: %s is a file\n", path);
261 // It is a file
262 if (NULL != pFileSize)
263 {
264 *pFileSize = (size_t)sb.st_size;
265 }
266 }
267 }
268 #if VERBOSE_LOGGING
269 else
270 {
271 TestOTALog("In PathExists: stat returned %d for %s\n", stat_result, path);
272 int local_errno = errno;
273 switch(local_errno)
274 {
275 case EACCES:
276 TestOTALog("In PathExists: stat failed because of EACCES\n");
277 break;
278
279 case EBADF:
280 TestOTALog("In PathExists: stat failed because of EBADF (Not likely)\n");
281 break;
282
283 case EFAULT:
284 TestOTALog("In PathExists: stat failed because of EFAULT (huh?)\n");
285 break;
286
287 case ELOOP:
288 TestOTALog("In PathExists: stat failed because of ELOOP (huh?)\n");
289 break;
290
291 case ENAMETOOLONG:
292 TestOTALog("In PathExists: stat failed because of ENAMETOOLONG (huh?)\n");
293 break;
294
295 case ENOENT:
296 TestOTALog("In PathExists: stat failed because of ENOENT (missing?)\n");
297 break;
298
299 case ENOMEM:
300 TestOTALog("In PathExists: stat failed because of ENOMEM (really?)\n");
301 break;
302
303 case ENOTDIR:
304 TestOTALog("In PathExists: stat failed because of ENOTDIR (really?)\n");
305 break;
306
307 case EOVERFLOW:
308 TestOTALog("In PathExists: stat failed because of EOVERFLOW (really?)\n");
309 break;
310
311 default:
312 TestOTALog("In PathExists: unknown errno of %d\n", local_errno);
313 break;
314 }
315 }
316 #endif // #if VERBOSE_LOGGING
317
318 return result;
319 }
320
321 static int unlink_cb(const char *fpath, const struct stat *sb, int typeflag, struct FTW *ftwbuf)
322 {
323 int rv = remove(fpath);
324 return rv;
325 }
326
327 static int rmrf(char *path)
328 {
329 const char* p1 = NULL;
330 char path_buffer[PATH_MAX];
331 memset(path_buffer, 0, sizeof(path_buffer));
332
333 p1 = realpath(path, path_buffer);
334 if (!strncmp(path, p1, PATH_MAX))
335 {
336 return nftw(path, unlink_cb, 64, FTW_DEPTH | FTW_PHYS);
337 }
338 return -1;
339 }
340
341
342 static CFStringRef kSecSystemTrustStoreBundlePath = CFSTR("/System/Library/Security/Certificates.bundle");
343
344 CFGiblisGetSingleton(CFBundleRef, SecSystemTrustStoreGetBundle, bundle, ^{
345 CFStringRef bundlePath = NULL;
346 #if TARGET_IPHONE_SIMULATOR
347 char *simulatorRoot = getenv("SIMULATOR_ROOT");
348 if (simulatorRoot)
349 bundlePath = CFStringCreateWithFormat(NULL, NULL, CFSTR("%s%@"), simulatorRoot, kSecSystemTrustStoreBundlePath);
350 #endif
351 if (!bundlePath)
352 bundlePath = CFRetainSafe(kSecSystemTrustStoreBundlePath);
353 TestOTAResourceLog("SecSystemTrustStoreGetBundle", bundlePath, NULL, NULL, NULL);
354 CFURLRef url = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, bundlePath, kCFURLPOSIXPathStyle, true);
355 *bundle = (url) ? CFBundleCreate(kCFAllocatorDefault, url) : NULL;
356 CFReleaseSafe(url);
357 CFReleaseSafe(bundlePath);
358 })
359
360 static CFURLRef SecSystemTrustStoreCopyResourceURL(CFStringRef resourceName,
361 CFStringRef resourceType, CFStringRef subDirName)
362 {
363 CFURLRef url = NULL;
364 CFBundleRef bundle = SecSystemTrustStoreGetBundle();
365 TestOTALog("SecSystemTrustStoreCopyResourceURL: bundle = %p\n", (void*)bundle);
366 if (bundle) {
367 url = CFBundleCopyResourceURL(bundle, resourceName,
368 resourceType, subDirName);
369 if (!url) {
370 secwarning("resource: %@.%@ in %@ not found", resourceName,
371 resourceType, subDirName);
372 }
373 }
374 if (!url) {
375 TestOTAResourceLog("SecSystemTrustStoreCopyResourceURL: unable to get URL!",
376 resourceName, resourceType, subDirName, url);
377 } else {
378 TestOTAResourceLog("SecSystemTrustStoreCopyResourceURL: got URL from bundle",
379 resourceName, resourceType, subDirName, url);
380 }
381 return url;
382 }
383
384 static CFDataRef SecSystemTrustStoreCopyResourceContents(CFStringRef resourceName,
385 CFStringRef resourceType, CFStringRef subDirName)
386 {
387 CFURLRef url = SecSystemTrustStoreCopyResourceURL(resourceName, resourceType, subDirName);
388 CFDataRef data = NULL;
389 if (url) {
390 SInt32 error;
391 if (!CFURLCreateDataAndPropertiesFromResource(kCFAllocatorDefault,
392 url, &data, NULL, NULL, &error)) {
393 secwarning("read: %ld", (long) error);
394 }
395 CFRelease(url);
396 }
397 TestOTALog("SecSystemTrustStoreCopyResourceContents: data = %p\n", data);
398 return data;
399 }
400
401
402 static const char* InitOTADirectory(int* pAssetVersion)
403 {
404 TestOTALog("In InitOTADirectory\n");
405 const char* result = NULL;
406
407 char buffer[PATH_MAX];
408 DIR *dp;
409 struct dirent *ep;
410 int version = 0;
411 int current_version = 0;
412 int system_asset_version = 0;
413 CFIndex asset_number = 0;
414
415 // Look in the system trust store for an AssetVersion.plist file.
416 // This is needed to ensure that a software update did not put down
417 // a version of the trust store that is greater than the OTA assets.
418
419 CFDataRef assetVersionData = SecSystemTrustStoreCopyResourceContents(CFSTR("AssetVersion"), CFSTR("plist"), NULL);
420 if (NULL != assetVersionData)
421 {
422 CFPropertyListFormat propFormat;
423 CFDictionaryRef versionPlist = CFPropertyListCreateWithData(kCFAllocatorDefault, assetVersionData, 0, &propFormat, NULL);
424 if (NULL != versionPlist && CFDictionaryGetTypeID() == CFGetTypeID(versionPlist))
425 {
426 CFNumberRef versionNumber = (CFNumberRef)CFDictionaryGetValue(versionPlist, (const void *)CFSTR("VersionNumber"));
427 if (NULL != versionNumber)
428 {
429 CFNumberGetValue(versionNumber, kCFNumberCFIndexType, &asset_number);
430 system_asset_version = (int)asset_number;
431 }
432 }
433 CFReleaseSafe(versionPlist);
434 CFReleaseSafe(assetVersionData);
435 }
436
437 // Now check to see if the OTA asset directory exists.
438 // If it does, get the greatest asset number in the OTA asset directory.
439
440 bool assetDirectoryExists = PathExists(kBaseAssetDirectory, NULL);
441 if (assetDirectoryExists)
442 {
443 TestOTALog("InitOTADirectory: %s exists\n", kBaseAssetDirectory);
444 dp = opendir (kBaseAssetDirectory);
445 if (NULL != dp)
446 {
447 TestOTALog("InitOTADirectory: opendir sucessfully open %s\n", kBaseAssetDirectory);
448 while ((ep = readdir(dp)))
449 {
450 TestOTALog("InitOTADirectory: processing name %s\n", ep->d_name);
451 if (strstr(ep->d_name, kVersionDirectoryNamePrefix))
452 {
453 TestOTALog("InitOTADirectory: %s matches\n", ep->d_name);
454 memset(buffer, 0, sizeof(buffer));
455 snprintf(buffer, sizeof(buffer), "%s%s", kVersionDirectoryNamePrefix, kNumberString);
456
457 sscanf(ep->d_name, buffer, &version);
458
459 TestOTALog("InitOTADirectory: version = %d\n", version);
460
461 if (current_version > 0)
462 {
463 if (version > current_version)
464 {
465 // There is more than one Version_ directory.
466 // Delete the one with the smaller version number
467 memset(buffer, 0, sizeof(buffer));
468 snprintf(buffer, sizeof(buffer), "%s/%s%d", kBaseAssetDirectory, kVersionDirectoryNamePrefix, current_version);
469 if (PathExists(buffer, NULL))
470 {
471 rmrf(buffer);
472 }
473 current_version = version;
474 }
475 }
476 else
477 {
478 current_version = version;
479 }
480 }
481 }
482 closedir(dp);
483 }
484 else
485 {
486 TestOTALog("InitOTADirectory: opendir failed to open %s\n", kBaseAssetDirectory);
487 }
488 }
489 else
490 {
491 TestOTALog("InitOTADirectory: PathExists returned false for %s\n", kBaseAssetDirectory);
492 }
493
494 // Check to see which version number is greater.
495 // If the current_version is greater then the OTA asset is newer.
496 // If the system_asset_version is greater than the system asset is newer.
497 if (current_version > system_asset_version)
498 {
499 // The OTA asset is newer than the system asset number
500 memset(buffer, 0, sizeof(buffer));
501 TestOTALog("InitOTADirectory: current_version = %d\n", current_version);
502 snprintf(buffer, sizeof(buffer), "%s/%s%d", kBaseAssetDirectory, kVersionDirectoryNamePrefix, current_version);
503 size_t length = strlen(buffer);
504 char* temp_str = (char*)malloc(length + 1);
505 memset(temp_str, 0, (length + 1));
506 strncpy(temp_str, buffer, length);
507 result = temp_str;
508 }
509 else
510 {
511 // The system asset number is newer than the OTA asset number
512 current_version = system_asset_version;
513 if (NULL != result)
514 {
515 free((void *)result);
516 }
517 result = NULL;
518 }
519
520 if (NULL != pAssetVersion)
521 {
522 *pAssetVersion = current_version;
523 }
524 return result;
525 }
526
527 static CFSetRef InitializeBlackList(const char* path_ptr)
528 {
529 CFSetRef result = NULL;
530
531 // Check to see if the EVRoots.plist file is in the asset location
532 CFDataRef xmlData = NULL;
533 const char* asset_path = path_ptr;
534 if (asset_path) {
535 char file_path_buffer[PATH_MAX];
536 memset(file_path_buffer, 0, PATH_MAX);
537 snprintf(file_path_buffer, PATH_MAX, "%s/Blocked.plist", asset_path);
538
539 xmlData = SecOTACopyFileContents(file_path_buffer);
540 }
541
542 if (!xmlData) {
543 // no OTA asset file, so use the file in the system trust store bundle
544 xmlData = SecSystemTrustStoreCopyResourceContents(CFSTR("Blocked"), CFSTR("plist"), NULL);
545 }
546
547 CFPropertyListRef blackKeys = NULL;
548 if (xmlData) {
549 blackKeys = CFPropertyListCreateWithData(kCFAllocatorDefault, xmlData, kCFPropertyListImmutable, NULL, NULL);
550 CFRelease(xmlData);
551 }
552
553 if (blackKeys) {
554 CFMutableSetRef tempSet = NULL;
555 if (CFGetTypeID(blackKeys) == CFArrayGetTypeID()) {
556 tempSet = CFSetCreateMutable(kCFAllocatorDefault, 0, &kCFTypeSetCallBacks);
557 if (NULL == tempSet) {
558 CFRelease(blackKeys);
559 return result;
560 }
561 CFArrayRef blackKeyArray = (CFArrayRef)blackKeys;
562 CFIndex num_keys = CFArrayGetCount(blackKeyArray);
563 for (CFIndex idx = 0; idx < num_keys; idx++) {
564 CFDataRef key_data = (CFDataRef)CFArrayGetValueAtIndex(blackKeyArray, idx);
565 CFSetAddValue(tempSet, key_data);
566 }
567 }
568 else {
569 CFRelease(blackKeys);
570 return result;
571 }
572
573 if (NULL != tempSet) {
574 result = tempSet;
575 }
576 CFRelease(blackKeys);
577 }
578
579 return result;
580 }
581
582 static CFSetRef InitializeGrayList(const char* path_ptr)
583 {
584 CFSetRef result = NULL;
585
586 // Check to see if the EVRoots.plist file is in the asset location
587 CFDataRef xmlData = NULL;
588 const char* asset_path = path_ptr;
589 if (asset_path) {
590 char file_path_buffer[PATH_MAX];
591 memset(file_path_buffer, 0, PATH_MAX);
592 snprintf(file_path_buffer, PATH_MAX, "%s/GrayListedKeys.plist", asset_path);
593
594 xmlData = SecOTACopyFileContents(file_path_buffer);
595 }
596
597 if (!xmlData) {
598 // no OTA asset file, so use the file in the system trust store bundle
599 xmlData = SecSystemTrustStoreCopyResourceContents(CFSTR("GrayListedKeys"), CFSTR("plist"), NULL);
600 }
601
602 CFPropertyListRef grayKeys = NULL;
603 if (xmlData) {
604 grayKeys = CFPropertyListCreateWithData(kCFAllocatorDefault, xmlData, kCFPropertyListImmutable, NULL, NULL);
605 CFRelease(xmlData);
606 }
607
608 if (grayKeys) {
609 CFMutableSetRef tempSet = NULL;
610 if (CFGetTypeID(grayKeys) == CFArrayGetTypeID()) {
611 tempSet = CFSetCreateMutable(kCFAllocatorDefault, 0, &kCFTypeSetCallBacks);
612 if (NULL == tempSet) {
613 CFRelease(grayKeys);
614 return result;
615 }
616 CFArrayRef grayKeyArray = (CFArrayRef)grayKeys;
617 CFIndex num_keys = CFArrayGetCount(grayKeyArray);
618 for (CFIndex idx = 0; idx < num_keys; idx++) {
619 CFDataRef key_data = (CFDataRef)CFArrayGetValueAtIndex(grayKeyArray, idx);
620 CFSetAddValue(tempSet, key_data);
621 }
622 }
623 else {
624 CFRelease(grayKeys);
625 return result;
626 }
627
628 if (NULL != tempSet) {
629 result = tempSet;
630 }
631
632 CFRelease(grayKeys);
633 }
634 return result;
635 }
636
637 static CFDictionaryRef InitializeAllowList(const char* path_ptr)
638 {
639 // Check to see if the Allowed.plist file is in the asset location
640 CFDataRef xmlData = NULL;
641 const char* asset_path = path_ptr;
642 if (asset_path) {
643 char file_path_buffer[PATH_MAX];
644 memset(file_path_buffer, 0, PATH_MAX);
645 snprintf(file_path_buffer, PATH_MAX, "%s/Allowed.plist", asset_path);
646
647 xmlData = SecOTACopyFileContents(file_path_buffer);
648 }
649
650 if (!xmlData) {
651 // no OTA asset file, so use the file in the system trust store bundle
652 xmlData = SecSystemTrustStoreCopyResourceContents(CFSTR("Allowed"), CFSTR("plist"), NULL);
653 }
654
655 CFPropertyListRef allowList = NULL;
656 if (xmlData) {
657 allowList = CFPropertyListCreateWithData(kCFAllocatorDefault, xmlData, kCFPropertyListImmutable, NULL, NULL);
658 CFRelease(xmlData);
659 }
660
661 if (allowList && (CFGetTypeID(allowList) == CFDictionaryGetTypeID())) {
662 return allowList;
663 } else {
664 CFReleaseNull(allowList);
665 return NULL;
666 }
667 }
668
669 static CFArrayRef InitializeTrustedCTLogs(const char* path_ptr)
670 {
671 // Check to see if the TrustedCTLogs.plist file is in the asset location
672 CFDataRef xmlData = NULL;
673 const char* asset_path = path_ptr;
674 if (asset_path) {
675 char file_path_buffer[PATH_MAX];
676 memset(file_path_buffer, 0, PATH_MAX);
677 snprintf(file_path_buffer, PATH_MAX, "%s/TrustedCTLogs.plist", asset_path);
678
679 xmlData = SecOTACopyFileContents(file_path_buffer);
680 }
681
682 if (!xmlData) {
683 // no OTA asset file, so use the file in the system trust store bundle
684 xmlData = SecSystemTrustStoreCopyResourceContents(CFSTR("TrustedCTLogs"), CFSTR("plist"), NULL);
685 }
686
687 CFPropertyListRef trustedCTLogs = NULL;
688 if (xmlData) {
689 trustedCTLogs = CFPropertyListCreateWithData(kCFAllocatorDefault, xmlData, kCFPropertyListImmutable, NULL, NULL);
690 CFRelease(xmlData);
691 }
692
693 if (trustedCTLogs && (CFGetTypeID(trustedCTLogs) == CFArrayGetTypeID())) {
694 return trustedCTLogs;
695 } else {
696 CFReleaseNull(trustedCTLogs);
697 return NULL;
698 }
699 }
700
701
702 static CFDictionaryRef InitializeEVPolicyToAnchorDigestsTable(const char* path_ptr)
703 {
704 CFDictionaryRef result = NULL;
705
706 // Check to see if the EVRoots.plist file is in the asset location
707 CFDataRef xmlData = NULL;
708 const char* asset_path = path_ptr;
709 if (asset_path)
710 {
711 char file_path_buffer[PATH_MAX];
712 memset(file_path_buffer, 0, PATH_MAX);
713 snprintf(file_path_buffer, PATH_MAX, "%s/EVRoots.plist", asset_path);
714
715 xmlData = SecOTACopyFileContents(file_path_buffer);
716 }
717
718 if (!xmlData) {
719 // no OTA asset file, so use the file in the system trust store bundle
720 xmlData = SecSystemTrustStoreCopyResourceContents(CFSTR("EVRoots"), CFSTR("plist"), NULL);
721 }
722
723 CFPropertyListRef evroots = NULL;
724 if (xmlData) {
725 evroots = CFPropertyListCreateWithData(
726 kCFAllocatorDefault, xmlData, kCFPropertyListImmutable, NULL, NULL);
727 CFRelease(xmlData);
728 }
729
730 if (evroots) {
731 if (CFGetTypeID(evroots) == CFDictionaryGetTypeID()) {
732 /* @@@ Ensure that each dictionary key is a dotted list of digits,
733 each value is an NSArrayRef and each element in the array is a
734 20 byte digest. */
735 result = (CFDictionaryRef)evroots;
736 }
737 else {
738 secwarning("EVRoot.plist is wrong type.");
739 CFRelease(evroots);
740 }
741 }
742
743 return result;
744 }
745
746 static void* MapFile(const char* path, int* out_fd, size_t* out_file_size)
747 {
748 void* result = NULL;
749 void* temp_result = NULL;
750 if (NULL == path || NULL == out_fd || NULL == out_file_size)
751 {
752 return result;
753 }
754
755 *out_fd = -1;
756 *out_file_size = 0;
757
758
759 *out_fd = open(path, O_RDONLY, 0666);
760
761 if (*out_fd == -1)
762 {
763 return result;
764 }
765
766 off_t fsize = lseek(*out_fd, 0, SEEK_END);
767 if (fsize == (off_t)-1)
768 {
769 return result;
770 }
771
772 if (fsize > (off_t)INT32_MAX)
773 {
774 close(*out_fd);
775 *out_fd = -1;
776 return result;
777 }
778
779 size_t malloc_size = (size_t)fsize;
780
781 temp_result = malloc(malloc_size);
782 if (NULL == temp_result)
783 {
784 close(*out_fd);
785 *out_fd = -1;
786 return result;
787 }
788
789 *out_file_size = malloc_size;
790
791 off_t total_read = 0;
792 while (total_read < fsize)
793 {
794 ssize_t bytes_read;
795
796 bytes_read = pread(*out_fd, temp_result, (size_t)(fsize - total_read), total_read);
797 if (bytes_read == -1)
798 {
799 free(temp_result);
800 temp_result = NULL;
801 close(*out_fd);
802 *out_fd = -1;
803 return result;
804 }
805 if (bytes_read == 0)
806 {
807 free(temp_result);
808 temp_result = NULL;
809 close(*out_fd);
810 *out_fd = -1;
811 return result;
812 }
813 total_read += bytes_read;
814 }
815
816 if (NULL != temp_result)
817 {
818 result = temp_result;
819 }
820
821 return result;
822 }
823
824 static void UnMapFile(void* mapped_data, size_t data_size)
825 {
826 #pragma unused(mapped_data, data_size)
827 if (NULL != mapped_data)
828 {
829 free((void *)mapped_data);
830 mapped_data = NULL;
831 }
832 }
833
834 static bool InitializeAnchorTable(const char* path_ptr, CFDictionaryRef* pLookupTable, const char** ppAnchorTable)
835 {
836
837 bool result = false;
838
839 if (NULL == pLookupTable || NULL == ppAnchorTable)
840 {
841 return result;
842 }
843
844 *pLookupTable = NULL;
845 *ppAnchorTable = NULL;;
846
847 const char* dir_path = NULL;
848 CFDataRef cert_index_file_data = NULL;
849 char file_path_buffer[PATH_MAX];
850 CFURLRef table_data_url = NULL;
851 CFStringRef table_data_cstr_path = NULL;
852 const char* table_data_path = NULL;
853 const index_record* pIndex = NULL;
854 size_t index_offset = 0;
855 size_t index_data_size = 0;
856 CFMutableDictionaryRef anchorLookupTable = NULL;
857 uint32_t offset_int_value = 0;
858 CFNumberRef index_offset_value = NULL;
859 CFDataRef index_hash = NULL;
860 CFMutableArrayRef offsets = NULL;
861 Boolean release_offset = false;
862
863 char* local_anchorTable = NULL;
864 size_t local_anchorTableSize = 0;
865 int local_anchorTable_fd = -1;
866
867 // ------------------------------------------------------------------------
868 // First determine if there are asset files at /var/Keychains. If there
869 // are files use them for the trust table. Otherwise, use the files in the
870 // Security.framework bundle.
871 //
872 // The anchor table file is mapped into memory. This SHOULD be OK as the
873 // size of the data is around 250K.
874 // ------------------------------------------------------------------------
875 dir_path = path_ptr;
876
877 if (NULL != dir_path)
878 {
879 // There is a set of OTA asset files
880 memset(file_path_buffer, 0, PATH_MAX);
881 snprintf(file_path_buffer, PATH_MAX, "%s/certsIndex.data", dir_path);
882 cert_index_file_data = SecOTACopyFileContents(file_path_buffer);
883
884 if (NULL != cert_index_file_data)
885 {
886 memset(file_path_buffer, 0, PATH_MAX);
887 snprintf(file_path_buffer, PATH_MAX, "%s/certsTable.data", dir_path);
888 local_anchorTable = (char *)MapFile(file_path_buffer, &local_anchorTable_fd, &local_anchorTableSize);
889 }
890
891 free((void *)dir_path);
892 dir_path = NULL;
893 }
894
895 // Check to see if kAnchorTable was indeed set
896 if (NULL == local_anchorTable)
897 {
898 // local_anchorTable is still NULL so the asset in the system trust store bundle needs to be used.
899 CFReleaseSafe(cert_index_file_data);
900 cert_index_file_data = SecSystemTrustStoreCopyResourceContents(CFSTR("certsIndex"), CFSTR("data"), NULL);
901 if (!cert_index_file_data) {
902 secerror("could not find certsIndex");
903 }
904 table_data_url = SecSystemTrustStoreCopyResourceURL(CFSTR("certsTable"), CFSTR("data"), NULL);
905 if (!table_data_url) {
906 secerror("could not find certsTable");
907 }
908
909 if (NULL != table_data_url)
910 {
911 table_data_cstr_path = CFURLCopyFileSystemPath(table_data_url, kCFURLPOSIXPathStyle);
912 if (NULL != table_data_cstr_path)
913 {
914 memset(file_path_buffer, 0, PATH_MAX);
915 table_data_path = CFStringGetCStringPtr(table_data_cstr_path, kCFStringEncodingUTF8);
916 if (NULL == table_data_path)
917 {
918 if (CFStringGetCString(table_data_cstr_path, file_path_buffer, PATH_MAX, kCFStringEncodingUTF8))
919 {
920 table_data_path = file_path_buffer;
921 }
922 }
923 local_anchorTable = (char *)MapFile(table_data_path, &local_anchorTable_fd, &local_anchorTableSize);
924 CFReleaseSafe(table_data_cstr_path);
925 }
926 }
927 CFReleaseSafe(table_data_url);
928 }
929
930 if (NULL == local_anchorTable || NULL == cert_index_file_data)
931 {
932 // we are in trouble
933 if (NULL != local_anchorTable)
934 {
935 UnMapFile(local_anchorTable, local_anchorTableSize);
936 local_anchorTable = NULL;
937 local_anchorTableSize = 0;
938 }
939 CFReleaseSafe(cert_index_file_data);
940 return result;
941 }
942
943 // ------------------------------------------------------------------------
944 // Now that the locations of the files are known and the table file has
945 // been mapped into memory, create a dictionary that maps the SHA1 hash of
946 // normalized issuer to the offset in the mapped anchor table file which
947 // contains a index_record to the correct certificate
948 // ------------------------------------------------------------------------
949 pIndex = (const index_record*)CFDataGetBytePtr(cert_index_file_data);
950 index_data_size = CFDataGetLength(cert_index_file_data);
951
952 anchorLookupTable = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
953 &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
954
955 for (index_offset = index_data_size; index_offset > 0; index_offset -= sizeof(index_record), pIndex++)
956 {
957 offset_int_value = pIndex->offset;
958
959 index_offset_value = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &offset_int_value);
960 index_hash = CFDataCreate(kCFAllocatorDefault, pIndex->hash, CC_SHA1_DIGEST_LENGTH);
961
962 // see if the dictionary already has this key
963 release_offset = false;
964 offsets = (CFMutableArrayRef)CFDictionaryGetValue(anchorLookupTable, index_hash);
965 if (NULL == offsets)
966 {
967 offsets = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
968 release_offset = true;
969 }
970
971 // Add the offset
972 CFArrayAppendValue(offsets, index_offset_value);
973
974 // set the key value pair in the dictionary
975 CFDictionarySetValue(anchorLookupTable, index_hash, offsets);
976
977 CFRelease(index_offset_value);
978 CFRelease(index_hash);
979 if (release_offset)
980 {
981 CFRelease(offsets);
982 }
983 }
984
985 CFRelease(cert_index_file_data);
986
987 if (NULL != anchorLookupTable && NULL != local_anchorTable)
988 {
989 *pLookupTable = anchorLookupTable;
990 *ppAnchorTable = local_anchorTable;
991 result = true;
992 }
993 else
994 {
995 CFReleaseSafe(anchorLookupTable);
996 if (NULL != local_anchorTable)
997 {
998 UnMapFile(local_anchorTable, local_anchorTableSize);
999 //munmap(kAnchorTable, local_anchorTableSize);
1000 local_anchorTable = NULL;
1001 local_anchorTableSize = 0;
1002 }
1003 }
1004
1005 return result;
1006 }
1007
1008 static void InitializeEscrowCertificates(const char* path_ptr, CFArrayRef *escrowRoots, CFArrayRef *escrowPCSRoots)
1009 {
1010 CFDataRef file_data = NULL;
1011
1012 const char* dir_path = path_ptr;
1013 if (NULL == dir_path)
1014 {
1015 file_data = SecSystemTrustStoreCopyResourceContents(CFSTR("AppleESCertificates"), CFSTR("plist"), NULL);
1016 }
1017 else
1018 {
1019 char buffer[1024];
1020 memset(buffer, 0, 1024);
1021 snprintf(buffer, 1024, "%s/AppleESCertificates.plist", dir_path);
1022 file_data = SecOTACopyFileContents(buffer);
1023 }
1024
1025 if (NULL != file_data)
1026 {
1027 CFPropertyListFormat propFormat;
1028 CFDictionaryRef certsDictionary = CFPropertyListCreateWithData(kCFAllocatorDefault, file_data, 0, &propFormat, NULL);
1029 if (NULL != certsDictionary && CFDictionaryGetTypeID() == CFGetTypeID((CFTypeRef)certsDictionary))
1030 {
1031 CFArrayRef certs = (CFArrayRef)CFDictionaryGetValue(certsDictionary, CFSTR("ProductionEscrowKey"));
1032 if (NULL != certs && CFArrayGetTypeID() == CFGetTypeID((CFTypeRef)certs) && CFArrayGetCount(certs) > 0)
1033 {
1034 *escrowRoots = CFArrayCreateCopy(kCFAllocatorDefault, certs);
1035 }
1036 CFArrayRef pcs_certs = (CFArrayRef)CFDictionaryGetValue(certsDictionary, CFSTR("ProductionPCSEscrowKey"));
1037 if (NULL != pcs_certs && CFArrayGetTypeID() == CFGetTypeID((CFTypeRef)pcs_certs) && CFArrayGetCount(pcs_certs) > 0)
1038 {
1039 *escrowPCSRoots = CFArrayCreateCopy(kCFAllocatorDefault, pcs_certs);
1040 }
1041 }
1042 CFReleaseSafe(certsDictionary);
1043 CFRelease(file_data);
1044 }
1045
1046 }
1047
1048
1049 static SecOTAPKIRef SecOTACreate()
1050 {
1051 TestOTALog("In SecOTACreate\n");
1052
1053 SecOTAPKIRef otapkiref = NULL;
1054
1055 otapkiref = CFTypeAllocate(SecOTAPKI, struct _OpaqueSecOTAPKI , kCFAllocatorDefault);
1056
1057 if (NULL == otapkiref)
1058 {
1059 return otapkiref;
1060 }
1061
1062 // Make sure that if this routine has to bail that the clean up
1063 // will do the right thing
1064 otapkiref->_blackListSet = NULL;
1065 otapkiref->_grayListSet = NULL;
1066 otapkiref->_allowList = NULL;
1067 otapkiref->_trustedCTLogs = NULL;
1068 otapkiref->_escrowCertificates = NULL;
1069 otapkiref->_escrowPCSCertificates = NULL;
1070 otapkiref->_evPolicyToAnchorMapping = NULL;
1071 otapkiref->_anchorLookupTable = NULL;
1072 otapkiref->_anchorTable = NULL;
1073 otapkiref->_assetVersion = 0;
1074
1075 // Start off by getting the correct asset directory info
1076 int asset_version = 0;
1077 const char* path_ptr = InitOTADirectory(&asset_version);
1078 otapkiref->_assetVersion = asset_version;
1079
1080 TestOTALog("SecOTACreate: asset_path = %s\n", path_ptr);
1081 TestOTALog("SecOTACreate: asset_version = %d\n", asset_version);
1082
1083 // Get the set of black listed keys
1084 CFSetRef blackKeysSet = InitializeBlackList(path_ptr);
1085 if (NULL == blackKeysSet)
1086 {
1087 if (path_ptr) {
1088 free((void *)path_ptr);
1089 }
1090 CFReleaseNull(otapkiref);
1091 return otapkiref;
1092 }
1093 otapkiref->_blackListSet = blackKeysSet;
1094
1095 // Get the set of gray listed keys
1096 CFSetRef grayKeysSet = InitializeGrayList(path_ptr);
1097 if (NULL == grayKeysSet)
1098 {
1099 if (path_ptr) {
1100 free((void *)path_ptr);
1101 }
1102 CFReleaseNull(otapkiref);
1103 return otapkiref;
1104 }
1105 otapkiref->_grayListSet = grayKeysSet;
1106
1107 // Get the allow list dictionary
1108 otapkiref->_allowList = InitializeAllowList(path_ptr);
1109
1110 // Get the trusted Certificate Transparency Logs
1111 otapkiref->_trustedCTLogs = InitializeTrustedCTLogs(path_ptr);
1112
1113 CFArrayRef escrowCerts = NULL;
1114 CFArrayRef escrowPCSCerts = NULL;
1115 InitializeEscrowCertificates(path_ptr, &escrowCerts, &escrowPCSCerts);
1116 if (NULL == escrowCerts || NULL == escrowPCSCerts)
1117 {
1118 if (path_ptr) {
1119 free((void *)path_ptr);
1120 }
1121 CFReleaseNull(escrowCerts);
1122 CFReleaseNull(escrowPCSCerts);
1123 CFReleaseNull(otapkiref);
1124 return otapkiref;
1125 }
1126 otapkiref->_escrowCertificates = escrowCerts;
1127 otapkiref->_escrowPCSCertificates = escrowPCSCerts;
1128
1129 // Get the mapping of EV Policy OIDs to Anchor digest
1130 CFDictionaryRef evOidToAnchorDigestMap = InitializeEVPolicyToAnchorDigestsTable(path_ptr);
1131 if (NULL == evOidToAnchorDigestMap)
1132 {
1133 if (path_ptr) {
1134 free((void *)path_ptr);
1135 }
1136 CFReleaseNull(otapkiref);
1137 return otapkiref;
1138 }
1139 otapkiref->_evPolicyToAnchorMapping = evOidToAnchorDigestMap;
1140
1141 CFDictionaryRef anchorLookupTable = NULL;
1142 const char* anchorTablePtr = NULL;
1143
1144 if (!InitializeAnchorTable(path_ptr, &anchorLookupTable, &anchorTablePtr))
1145 {
1146 CFReleaseSafe(anchorLookupTable);
1147 if (anchorTablePtr) {
1148 free((void *)anchorTablePtr);
1149 }
1150 if (path_ptr) {
1151 free((void *)path_ptr);
1152 }
1153 CFReleaseNull(otapkiref);
1154 return otapkiref;
1155 }
1156 otapkiref->_anchorLookupTable = anchorLookupTable;
1157 otapkiref->_anchorTable = anchorTablePtr;
1158 return otapkiref;
1159 }
1160
1161 static dispatch_once_t kInitializeOTAPKI = 0;
1162 static const char* kOTAQueueLabel = "com.apple.security.OTAPKIQueue";
1163 static dispatch_queue_t kOTAQueue;
1164 static SecOTAPKIRef kCurrentOTAPKIRef = NULL;
1165
1166 SecOTAPKIRef SecOTAPKICopyCurrentOTAPKIRef()
1167 {
1168 __block SecOTAPKIRef result = NULL;
1169 dispatch_once(&kInitializeOTAPKI,
1170 ^{
1171 kOTAQueue = dispatch_queue_create(kOTAQueueLabel, NULL);
1172 kCurrentOTAPKIRef = SecOTACreate();
1173 });
1174
1175 dispatch_sync(kOTAQueue,
1176 ^{
1177 result = kCurrentOTAPKIRef;
1178 CFRetainSafe(result);
1179 });
1180 return result;
1181 }
1182
1183
1184 CFSetRef SecOTAPKICopyBlackListSet(SecOTAPKIRef otapkiRef)
1185 {
1186 CFSetRef result = NULL;
1187 if (NULL == otapkiRef)
1188 {
1189 return result;
1190 }
1191
1192 result = otapkiRef->_blackListSet;
1193 CFRetainSafe(result);
1194 return result;
1195 }
1196
1197
1198 CFSetRef SecOTAPKICopyGrayList(SecOTAPKIRef otapkiRef)
1199 {
1200 CFSetRef result = NULL;
1201 if (NULL == otapkiRef)
1202 {
1203 return result;
1204 }
1205
1206 result = otapkiRef->_grayListSet;
1207 CFRetainSafe(result);
1208 return result;
1209 }
1210
1211 CFDictionaryRef SecOTAPKICopyAllowList(SecOTAPKIRef otapkiRef)
1212 {
1213 CFDictionaryRef result = NULL;
1214 if (NULL == otapkiRef)
1215 {
1216 return result;
1217 }
1218
1219 result = otapkiRef->_allowList;
1220 CFRetainSafe(result);
1221 return result;
1222 }
1223
1224 CFArrayRef SecOTAPKICopyTrustedCTLogs(SecOTAPKIRef otapkiRef)
1225 {
1226 CFArrayRef result = NULL;
1227 if (NULL == otapkiRef)
1228 {
1229 return result;
1230 }
1231
1232 result = otapkiRef->_trustedCTLogs;
1233 CFRetainSafe(result);
1234 return result;
1235 }
1236
1237
1238 CFArrayRef SecOTAPKICopyEscrowCertificates(uint32_t escrowRootType, SecOTAPKIRef otapkiRef)
1239 {
1240 CFArrayRef result = NULL;
1241 if (NULL == otapkiRef)
1242 {
1243 return result;
1244 }
1245
1246 switch (escrowRootType) {
1247 // Note: we shouldn't be getting called to return baseline roots,
1248 // since this function vends production roots by definition.
1249 case kSecCertificateBaselineEscrowRoot:
1250 case kSecCertificateProductionEscrowRoot:
1251 result = otapkiRef->_escrowCertificates;
1252 break;
1253 case kSecCertificateBaselinePCSEscrowRoot:
1254 case kSecCertificateProductionPCSEscrowRoot:
1255 result = otapkiRef->_escrowPCSCertificates;
1256 break;
1257 default:
1258 break;
1259 }
1260
1261 CFRetainSafe(result);
1262 return result;
1263 }
1264
1265
1266 CFDictionaryRef SecOTAPKICopyEVPolicyToAnchorMapping(SecOTAPKIRef otapkiRef)
1267 {
1268 CFDictionaryRef result = NULL;
1269 if (NULL == otapkiRef)
1270 {
1271 return result;
1272 }
1273
1274 result = otapkiRef->_evPolicyToAnchorMapping;
1275 CFRetainSafe(result);
1276 return result;
1277 }
1278
1279
1280 CFDictionaryRef SecOTAPKICopyAnchorLookupTable(SecOTAPKIRef otapkiRef)
1281 {
1282 CFDictionaryRef result = NULL;
1283 if (NULL == otapkiRef)
1284 {
1285 return result;
1286 }
1287
1288 result = otapkiRef->_anchorLookupTable;
1289 CFRetainSafe(result);
1290 return result;
1291 }
1292
1293 const char* SecOTAPKIGetAnchorTable(SecOTAPKIRef otapkiRef)
1294 {
1295 const char* result = NULL;
1296 if (NULL == otapkiRef)
1297 {
1298 return result;
1299 }
1300
1301 result = otapkiRef->_anchorTable;
1302 return result;
1303 }
1304
1305 int SecOTAPKIGetAssetVersion(SecOTAPKIRef otapkiRef)
1306 {
1307 int result = 0;
1308 if (NULL == otapkiRef)
1309 {
1310 return result;
1311 }
1312
1313 result = otapkiRef->_assetVersion;
1314 return result;
1315 }
1316
1317 void SecOTAPKIRefreshData()
1318 {
1319 TestOTALog("In SecOTAPKIRefreshData\n");
1320 SecOTAPKIRef new_otaPKRef = SecOTACreate();
1321 dispatch_sync(kOTAQueue,
1322 ^{
1323 CFReleaseSafe(kCurrentOTAPKIRef);
1324 kCurrentOTAPKIRef = new_otaPKRef;
1325 });
1326 }
1327
1328 CFArrayRef SecOTAPKICopyCurrentEscrowCertificates(uint32_t escrowRootType, CFErrorRef* error)
1329 {
1330 CFArrayRef result = NULL;
1331
1332 SecOTAPKIRef otapkiref = SecOTAPKICopyCurrentOTAPKIRef();
1333 if (NULL == otapkiref)
1334 {
1335 SecError(errSecInternal, error, CFSTR("Unable to get the current OTAPKIRef"));
1336 return result;
1337 }
1338
1339 result = SecOTAPKICopyEscrowCertificates(escrowRootType, otapkiref);
1340 CFRelease(otapkiref);
1341
1342 if (NULL == result)
1343 {
1344 SecError(errSecInternal, error, CFSTR("Could not get escrow certificates from the current OTAPKIRef"));
1345 }
1346 return result;
1347 }
1348
1349 int SecOTAPKIGetCurrentAssetVersion(CFErrorRef* error)
1350 {
1351 int result = 0;
1352
1353 SecOTAPKIRef otapkiref = SecOTAPKICopyCurrentOTAPKIRef();
1354 if (NULL == otapkiref)
1355 {
1356 SecError(errSecInternal, error, CFSTR("Unable to get the current OTAPKIRef"));
1357 return result;
1358 }
1359
1360 result = otapkiref->_assetVersion;
1361 return result;
1362 }
1363
1364 int SecOTAPKISignalNewAsset(CFErrorRef* error)
1365 {
1366 TestOTALog("SecOTAPKISignalNewAsset has been called!\n");
1367 SecOTAPKIRefreshData();
1368 return 1;
1369 }