2 * Copyright (c) 2003-2004,2006-2010,2013-2016 Apple Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
26 #include "OTATrustUtilities.h"
35 #include <sys/syslimits.h>
38 #include <CoreFoundation/CoreFoundation.h>
40 #include "SecFramework.h"
42 #include <sys/param.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>
53 //#define VERBOSE_LOGGING 1
57 static void TestOTALog(const char* sz
, ...)
62 FILE* fp
= fopen("/tmp/secd_OTAUtil.log", "a");
71 static void TestOTAResourceLog(const char *msg
,
72 CFStringRef resourceName
,
73 CFStringRef resourceType
,
74 CFStringRef subDirName
,
77 CFStringRef tmpStr
= NULL
;
78 CFIndex maxLength
= 0;
81 tmpStr
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
,
82 CFSTR("%s (name=%@, type=%@, subdir=%@), url=%@"),
83 msg
, resourceName
, resourceType
, subDirName
, url
);
85 maxLength
= CFStringGetMaximumSizeForEncoding(CFStringGetLength(tmpStr
), kCFStringEncodingUTF8
) + 1;
86 buf
= (char*) malloc(maxLength
);
88 TestOTALog("TestOTAResourceLog: failed to create string of length %ld\n", (long)maxLength
);
91 if (CFStringGetCString(tmpStr
, buf
, (CFIndex
)maxLength
, kCFStringEncodingUTF8
)) {
92 TestOTALog("%s\n", buf
);
96 CFReleaseSafe(tmpStr
);
101 #define TestOTALog(sz, ...)
102 #define TestOTAResourceLog(msg, resourceName, resourceType, subDirName, url)
107 //#define NEW_LOCATION 1
110 static const char* kBaseAssetDirectory
= "/var/OTAPKI/Assets";
112 static const char* kBaseAssetDirectory
= "/var/Keychains/Assets";
115 static const char* kVersionDirectoryNamePrefix
= "Version_";
116 static const char* kNumberString
= "%d";
120 unsigned char hash
[CC_SHA1_DIGEST_LENGTH
];
123 typedef struct index_record index_record
;
126 struct _OpaqueSecOTAPKI
129 CFSetRef _blackListSet
;
130 CFSetRef _grayListSet
;
131 CFDictionaryRef _allowList
;
132 CFArrayRef _trustedCTLogs
;
133 CFDataRef _CTWhiteListData
;
134 CFArrayRef _escrowCertificates
;
135 CFArrayRef _escrowPCSCertificates
;
136 CFDictionaryRef _evPolicyToAnchorMapping
;
137 CFDictionaryRef _anchorLookupTable
;
138 const char* _anchorTable
;
139 const char* _assetPath
;
143 CFGiblisFor(SecOTAPKI
)
145 static CF_RETURNS_RETAINED CFStringRef
SecOTAPKICopyFormatDescription(CFTypeRef cf
, CFDictionaryRef formatOptions
)
147 SecOTAPKIRef otapkiRef
= (SecOTAPKIRef
)cf
;
148 return CFStringCreateWithFormat(kCFAllocatorDefault
,NULL
,CFSTR("<SecOTAPKIRef: version %d>"), otapkiRef
->_assetVersion
);
151 static void SecOTAPKIDestroy(CFTypeRef cf
)
153 SecOTAPKIRef otapkiref
= (SecOTAPKIRef
)cf
;
155 CFReleaseNull(otapkiref
->_blackListSet
);
156 CFReleaseNull(otapkiref
->_grayListSet
);
157 CFReleaseNull(otapkiref
->_escrowCertificates
);
158 CFReleaseNull(otapkiref
->_escrowPCSCertificates
);
160 CFReleaseNull(otapkiref
->_evPolicyToAnchorMapping
);
161 CFReleaseNull(otapkiref
->_anchorLookupTable
);
163 CFReleaseNull(otapkiref
->_trustedCTLogs
);
164 CFReleaseNull(otapkiref
->_CTWhiteListData
);
166 if (otapkiref
->_anchorTable
) {
167 free((void *)otapkiref
->_anchorTable
);
168 otapkiref
->_anchorTable
= NULL
;
170 if (otapkiref
->_assetPath
) {
171 free((void *)otapkiref
->_assetPath
);
172 otapkiref
->_assetPath
= NULL
;
176 static CFDataRef
SecOTACopyFileContents(const char *path
)
178 CFMutableDataRef data
= NULL
;
179 int fd
= open(path
, O_RDONLY
, 0666);
186 off_t fsize
= lseek(fd
, 0, SEEK_END
);
187 if (fsize
== (off_t
)-1)
192 if (fsize
> (off_t
)INT32_MAX
)
197 data
= CFDataCreateMutable(kCFAllocatorDefault
, (CFIndex
)fsize
);
203 CFDataSetLength(data
, (CFIndex
)fsize
);
204 void *buf
= CFDataGetMutableBytePtr(data
);
210 off_t total_read
= 0;
211 while (total_read
< fsize
)
215 bytes_read
= pread(fd
, buf
, (size_t)(fsize
- total_read
), total_read
);
216 if (bytes_read
== -1)
224 total_read
+= bytes_read
;
244 static Boolean
PathExists(const char* path
, size_t* pFileSize
)
246 TestOTALog("In PathExists: checking path %s\n", path
);
247 Boolean result
= false;
250 if (NULL
!= pFileSize
)
255 int stat_result
= stat(path
, &sb
);
256 result
= (stat_result
== 0);
261 TestOTALog("In PathExists: stat returned 0 for %s\n", path
);
262 if (S_ISDIR(sb
.st_mode
))
264 TestOTALog("In PathExists: %s is a directory\n", path
);
270 TestOTALog("In PathExists: %s is a file\n", path
);
272 if (NULL
!= pFileSize
)
274 *pFileSize
= (size_t)sb
.st_size
;
281 TestOTALog("In PathExists: stat returned %d for %s\n", stat_result
, path
);
282 int local_errno
= errno
;
286 TestOTALog("In PathExists: stat failed because of EACCES\n");
290 TestOTALog("In PathExists: stat failed because of EBADF (Not likely)\n");
294 TestOTALog("In PathExists: stat failed because of EFAULT (huh?)\n");
298 TestOTALog("In PathExists: stat failed because of ELOOP (huh?)\n");
302 TestOTALog("In PathExists: stat failed because of ENAMETOOLONG (huh?)\n");
306 TestOTALog("In PathExists: stat failed because of ENOENT (missing?)\n");
310 TestOTALog("In PathExists: stat failed because of ENOMEM (really?)\n");
314 TestOTALog("In PathExists: stat failed because of ENOTDIR (really?)\n");
318 TestOTALog("In PathExists: stat failed because of EOVERFLOW (really?)\n");
322 TestOTALog("In PathExists: unknown errno of %d\n", local_errno
);
326 #endif // #if VERBOSE_LOGGING
331 static int unlink_cb(const char *fpath
, const struct stat
*sb
, int typeflag
, struct FTW
*ftwbuf
)
333 int rv
= remove(fpath
);
337 static int rmrf(char *path
)
339 const char* p1
= NULL
;
340 char path_buffer
[PATH_MAX
];
341 memset(path_buffer
, 0, sizeof(path_buffer
));
343 p1
= realpath(path
, path_buffer
);
344 if (!strncmp(path
, p1
, PATH_MAX
))
346 return nftw(path
, unlink_cb
, 64, FTW_DEPTH
| FTW_PHYS
);
352 static CFStringRef kSecSystemTrustStoreBundlePath
= CFSTR("/System/Library/Security/Certificates.bundle");
354 CFGiblisGetSingleton(CFBundleRef
, SecSystemTrustStoreGetBundle
, bundle
, ^{
355 CFStringRef bundlePath
= NULL
;
356 #if TARGET_IPHONE_SIMULATOR
357 char *simulatorRoot
= getenv("SIMULATOR_ROOT");
359 bundlePath
= CFStringCreateWithFormat(NULL
, NULL
, CFSTR("%s%@"), simulatorRoot
, kSecSystemTrustStoreBundlePath
);
362 bundlePath
= CFRetainSafe(kSecSystemTrustStoreBundlePath
);
363 TestOTAResourceLog("SecSystemTrustStoreGetBundle", bundlePath
, NULL
, NULL
, NULL
);
364 CFURLRef url
= CFURLCreateWithFileSystemPath(kCFAllocatorDefault
, bundlePath
, kCFURLPOSIXPathStyle
, true);
365 *bundle
= (url
) ? CFBundleCreate(kCFAllocatorDefault
, url
) : NULL
;
367 CFReleaseSafe(bundlePath
);
370 static CFURLRef
SecSystemTrustStoreCopyResourceURL(CFStringRef resourceName
,
371 CFStringRef resourceType
, CFStringRef subDirName
)
374 CFBundleRef bundle
= SecSystemTrustStoreGetBundle();
375 TestOTALog("SecSystemTrustStoreCopyResourceURL: bundle = %p\n", (void*)bundle
);
377 url
= CFBundleCopyResourceURL(bundle
, resourceName
,
378 resourceType
, subDirName
);
380 secwarning("resource: %@.%@ in %@ not found", resourceName
,
381 resourceType
, subDirName
);
385 TestOTAResourceLog("SecSystemTrustStoreCopyResourceURL: unable to get URL!",
386 resourceName
, resourceType
, subDirName
, url
);
388 TestOTAResourceLog("SecSystemTrustStoreCopyResourceURL: got URL from bundle",
389 resourceName
, resourceType
, subDirName
, url
);
394 static CFDataRef
SecSystemTrustStoreCopyResourceContents(CFStringRef resourceName
,
395 CFStringRef resourceType
, CFStringRef subDirName
)
397 CFURLRef url
= SecSystemTrustStoreCopyResourceURL(resourceName
, resourceType
, subDirName
);
398 CFDataRef data
= NULL
;
401 if (!CFURLCreateDataAndPropertiesFromResource(kCFAllocatorDefault
,
402 url
, &data
, NULL
, NULL
, &error
)) {
403 secwarning("read: %ld", (long) error
);
407 TestOTALog("SecSystemTrustStoreCopyResourceContents: data = %p\n", data
);
411 static CFPropertyListRef
CFPropertyListCopyFromAsset(const char *ota_assets_path
, CFStringRef asset
)
413 CFPropertyListRef plist
= NULL
;
414 // Check to see if the <asset>.plist file is in the asset location
415 CFDataRef xmlData
= NULL
;
416 if (ota_assets_path
) {
417 CFStringRef filePath
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
, CFSTR("%s/%@.%@"), ota_assets_path
, asset
, CFSTR("plist"));
418 CFURLRef url
= CFURLCreateWithFileSystemPath(kCFAllocatorDefault
, filePath
, kCFURLPOSIXPathStyle
, false);
420 plist
= CFPropertyListReadFromFile(url
);
422 CFReleaseSafe(filePath
);
426 // no OTA asset file, so use the file in the system trust store bundle
427 xmlData
= SecSystemTrustStoreCopyResourceContents(asset
, CFSTR("plist"), NULL
);
430 plist
= CFPropertyListCreateWithData(kCFAllocatorDefault
, xmlData
, kCFPropertyListImmutable
, NULL
, NULL
);
438 static CFSetRef
CFSetCreateFromPropertyList(CFPropertyListRef plist
)
440 CFSetRef result
= NULL
;
443 CFMutableSetRef tempSet
= NULL
;
444 if (CFGetTypeID(plist
) == CFArrayGetTypeID()) {
445 tempSet
= CFSetCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeSetCallBacks
);
446 if (NULL
== tempSet
) {
449 CFArrayRef array
= (CFArrayRef
)plist
;
450 CFIndex num_keys
= CFArrayGetCount(array
);
451 for (CFIndex idx
= 0; idx
< num_keys
; idx
++) {
452 CFDataRef data
= (CFDataRef
)CFArrayGetValueAtIndex(array
, idx
);
453 CFSetAddValue(tempSet
, data
);
460 if (NULL
!= tempSet
) {
467 static const char* InitOTADirectory(int* pAssetVersion
)
469 TestOTALog("In InitOTADirectory\n");
470 const char* result
= NULL
;
472 char buffer
[PATH_MAX
];
476 int current_version
= 0;
477 int system_asset_version
= 0;
478 CFIndex asset_number
= 0;
480 // Look in the system trust store for an AssetVersion.plist file.
481 // This is needed to ensure that a software update did not put down
482 // a version of the trust store that is greater than the OTA assets.
484 CFDataRef assetVersionData
= SecSystemTrustStoreCopyResourceContents(CFSTR("AssetVersion"), CFSTR("plist"), NULL
);
485 if (NULL
!= assetVersionData
)
487 CFPropertyListFormat propFormat
;
488 CFDictionaryRef versionPlist
= CFPropertyListCreateWithData(kCFAllocatorDefault
, assetVersionData
, 0, &propFormat
, NULL
);
489 if (NULL
!= versionPlist
&& CFDictionaryGetTypeID() == CFGetTypeID(versionPlist
))
491 CFNumberRef versionNumber
= (CFNumberRef
)CFDictionaryGetValue(versionPlist
, (const void *)CFSTR("VersionNumber"));
492 if (NULL
!= versionNumber
)
494 CFNumberGetValue(versionNumber
, kCFNumberCFIndexType
, &asset_number
);
495 system_asset_version
= (int)asset_number
;
498 CFReleaseSafe(versionPlist
);
499 CFReleaseSafe(assetVersionData
);
502 // Now check to see if the OTA asset directory exists.
503 // If it does, get the greatest asset number in the OTA asset directory.
505 bool assetDirectoryExists
= PathExists(kBaseAssetDirectory
, NULL
);
506 if (assetDirectoryExists
)
508 TestOTALog("InitOTADirectory: %s exists\n", kBaseAssetDirectory
);
509 dp
= opendir (kBaseAssetDirectory
);
512 TestOTALog("InitOTADirectory: opendir sucessfully open %s\n", kBaseAssetDirectory
);
513 while ((ep
= readdir(dp
)))
515 TestOTALog("InitOTADirectory: processing name %s\n", ep
->d_name
);
516 if (strstr(ep
->d_name
, kVersionDirectoryNamePrefix
))
518 TestOTALog("InitOTADirectory: %s matches\n", ep
->d_name
);
519 memset(buffer
, 0, sizeof(buffer
));
520 snprintf(buffer
, sizeof(buffer
), "%s%s", kVersionDirectoryNamePrefix
, kNumberString
);
522 sscanf(ep
->d_name
, buffer
, &version
);
524 TestOTALog("InitOTADirectory: version = %d\n", version
);
526 if (current_version
> 0)
528 if (version
> current_version
)
530 // There is more than one Version_ directory.
531 // Delete the one with the smaller version number
532 memset(buffer
, 0, sizeof(buffer
));
533 snprintf(buffer
, sizeof(buffer
), "%s/%s%d", kBaseAssetDirectory
, kVersionDirectoryNamePrefix
, current_version
);
534 if (PathExists(buffer
, NULL
))
538 current_version
= version
;
543 current_version
= version
;
551 TestOTALog("InitOTADirectory: opendir failed to open %s\n", kBaseAssetDirectory
);
556 TestOTALog("InitOTADirectory: PathExists returned false for %s\n", kBaseAssetDirectory
);
559 // Check to see which version number is greater.
560 // If the current_version is greater then the OTA asset is newer.
561 // If the system_asset_version is greater than the system asset is newer.
562 if (current_version
> system_asset_version
)
564 // The OTA asset is newer than the system asset number
565 memset(buffer
, 0, sizeof(buffer
));
566 TestOTALog("InitOTADirectory: current_version = %d\n", current_version
);
567 snprintf(buffer
, sizeof(buffer
), "%s/%s%d", kBaseAssetDirectory
, kVersionDirectoryNamePrefix
, current_version
);
568 size_t length
= strlen(buffer
);
569 char* temp_str
= (char*)malloc(length
+ 1);
570 memset(temp_str
, 0, (length
+ 1));
571 strncpy(temp_str
, buffer
, length
);
576 // The system asset number is newer than the OTA asset number
577 current_version
= system_asset_version
;
580 free((void *)result
);
585 if (NULL
!= pAssetVersion
)
587 *pAssetVersion
= current_version
;
592 static CFSetRef
InitializeBlackList(const char* path_ptr
)
594 CFPropertyListRef plist
= CFPropertyListCopyFromAsset(path_ptr
, CFSTR("Blocked"));
595 CFSetRef result
= CFSetCreateFromPropertyList(plist
);
596 CFReleaseSafe(plist
);
601 static CFSetRef
InitializeGrayList(const char* path_ptr
)
603 CFPropertyListRef plist
= CFPropertyListCopyFromAsset(path_ptr
, CFSTR("GrayListedKeys"));
604 CFSetRef result
= CFSetCreateFromPropertyList(plist
);
605 CFReleaseSafe(plist
);
610 static CFDataRef
InitializeCTWhiteListData(const char* path_ptr
)
612 CFPropertyListRef data
= CFPropertyListCopyFromAsset(path_ptr
, CFSTR("CTWhiteListData"));
614 if (data
&& (CFGetTypeID(data
) == CFDataGetTypeID())) {
622 static CFDictionaryRef
InitializeAllowList(const char* path_ptr
)
624 CFPropertyListRef allowList
= CFPropertyListCopyFromAsset(path_ptr
, CFSTR("Allowed"));
626 if (allowList
&& (CFGetTypeID(allowList
) == CFDictionaryGetTypeID())) {
629 CFReleaseNull(allowList
);
634 static CFArrayRef
InitializeTrustedCTLogs(const char* path_ptr
)
636 CFPropertyListRef trustedCTLogs
= CFPropertyListCopyFromAsset(path_ptr
, CFSTR("TrustedCTLogs"));
638 if (trustedCTLogs
&& (CFGetTypeID(trustedCTLogs
) == CFArrayGetTypeID())) {
639 return trustedCTLogs
;
641 CFReleaseNull(trustedCTLogs
);
646 static CFDictionaryRef
InitializeEVPolicyToAnchorDigestsTable(const char* path_ptr
)
648 CFDictionaryRef result
= NULL
;
649 CFPropertyListRef evroots
= CFPropertyListCopyFromAsset(path_ptr
, CFSTR("EVRoots"));
652 if (CFGetTypeID(evroots
) == CFDictionaryGetTypeID()) {
653 /* @@@ Ensure that each dictionary key is a dotted list of digits,
654 each value is an NSArrayRef and each element in the array is a
656 result
= (CFDictionaryRef
)evroots
;
659 secwarning("EVRoot.plist is wrong type.");
667 static void* MapFile(const char* path
, int* out_fd
, size_t* out_file_size
)
670 void* temp_result
= NULL
;
671 if (NULL
== path
|| NULL
== out_fd
|| NULL
== out_file_size
)
680 *out_fd
= open(path
, O_RDONLY
, 0666);
687 off_t fsize
= lseek(*out_fd
, 0, SEEK_END
);
688 if (fsize
== (off_t
)-1)
693 if (fsize
> (off_t
)INT32_MAX
)
700 size_t malloc_size
= (size_t)fsize
;
702 temp_result
= malloc(malloc_size
);
703 if (NULL
== temp_result
)
710 *out_file_size
= malloc_size
;
712 off_t total_read
= 0;
713 while (total_read
< fsize
)
717 bytes_read
= pread(*out_fd
, temp_result
, (size_t)(fsize
- total_read
), total_read
);
718 if (bytes_read
== -1)
734 total_read
+= bytes_read
;
737 if (NULL
!= temp_result
)
739 result
= temp_result
;
745 static void UnMapFile(void* mapped_data
, size_t data_size
)
747 #pragma unused(mapped_data, data_size)
748 if (NULL
!= mapped_data
)
750 free((void *)mapped_data
);
755 static bool InitializeAnchorTable(const char* path_ptr
, CFDictionaryRef
* pLookupTable
, const char** ppAnchorTable
)
760 if (NULL
== pLookupTable
|| NULL
== ppAnchorTable
)
765 *pLookupTable
= NULL
;
766 *ppAnchorTable
= NULL
;;
768 const char* dir_path
= NULL
;
769 CFDataRef cert_index_file_data
= NULL
;
770 char file_path_buffer
[PATH_MAX
];
771 CFURLRef table_data_url
= NULL
;
772 CFStringRef table_data_cstr_path
= NULL
;
773 const char* table_data_path
= NULL
;
774 const index_record
* pIndex
= NULL
;
775 size_t index_offset
= 0;
776 size_t index_data_size
= 0;
777 CFMutableDictionaryRef anchorLookupTable
= NULL
;
778 uint32_t offset_int_value
= 0;
779 CFNumberRef index_offset_value
= NULL
;
780 CFDataRef index_hash
= NULL
;
781 CFMutableArrayRef offsets
= NULL
;
782 Boolean release_offset
= false;
784 char* local_anchorTable
= NULL
;
785 size_t local_anchorTableSize
= 0;
786 int local_anchorTable_fd
= -1;
788 // ------------------------------------------------------------------------
789 // First determine if there are asset files at /var/Keychains. If there
790 // are files use them for the trust table. Otherwise, use the files in the
791 // Security.framework bundle.
793 // The anchor table file is mapped into memory. This SHOULD be OK as the
794 // size of the data is around 250K.
795 // ------------------------------------------------------------------------
798 if (NULL
!= dir_path
)
800 // There is a set of OTA asset files
801 memset(file_path_buffer
, 0, PATH_MAX
);
802 snprintf(file_path_buffer
, PATH_MAX
, "%s/certsIndex.data", dir_path
);
803 cert_index_file_data
= SecOTACopyFileContents(file_path_buffer
);
805 if (NULL
!= cert_index_file_data
)
807 memset(file_path_buffer
, 0, PATH_MAX
);
808 snprintf(file_path_buffer
, PATH_MAX
, "%s/certsTable.data", dir_path
);
809 local_anchorTable
= (char *)MapFile(file_path_buffer
, &local_anchorTable_fd
, &local_anchorTableSize
);
812 free((void *)dir_path
);
816 // Check to see if kAnchorTable was indeed set
817 if (NULL
== local_anchorTable
)
819 // local_anchorTable is still NULL so the asset in the system trust store bundle needs to be used.
820 CFReleaseSafe(cert_index_file_data
);
821 cert_index_file_data
= SecSystemTrustStoreCopyResourceContents(CFSTR("certsIndex"), CFSTR("data"), NULL
);
822 if (!cert_index_file_data
) {
823 secerror("could not find certsIndex");
825 table_data_url
= SecSystemTrustStoreCopyResourceURL(CFSTR("certsTable"), CFSTR("data"), NULL
);
826 if (!table_data_url
) {
827 secerror("could not find certsTable");
830 if (NULL
!= table_data_url
)
832 table_data_cstr_path
= CFURLCopyFileSystemPath(table_data_url
, kCFURLPOSIXPathStyle
);
833 if (NULL
!= table_data_cstr_path
)
835 memset(file_path_buffer
, 0, PATH_MAX
);
836 table_data_path
= CFStringGetCStringPtr(table_data_cstr_path
, kCFStringEncodingUTF8
);
837 if (NULL
== table_data_path
)
839 if (CFStringGetCString(table_data_cstr_path
, file_path_buffer
, PATH_MAX
, kCFStringEncodingUTF8
))
841 table_data_path
= file_path_buffer
;
844 local_anchorTable
= (char *)MapFile(table_data_path
, &local_anchorTable_fd
, &local_anchorTableSize
);
845 CFReleaseSafe(table_data_cstr_path
);
848 CFReleaseSafe(table_data_url
);
851 if (NULL
== local_anchorTable
|| NULL
== cert_index_file_data
)
854 if (NULL
!= local_anchorTable
)
856 UnMapFile(local_anchorTable
, local_anchorTableSize
);
857 local_anchorTable
= NULL
;
858 local_anchorTableSize
= 0;
860 CFReleaseSafe(cert_index_file_data
);
864 // ------------------------------------------------------------------------
865 // Now that the locations of the files are known and the table file has
866 // been mapped into memory, create a dictionary that maps the SHA1 hash of
867 // normalized issuer to the offset in the mapped anchor table file which
868 // contains a index_record to the correct certificate
869 // ------------------------------------------------------------------------
870 pIndex
= (const index_record
*)CFDataGetBytePtr(cert_index_file_data
);
871 index_data_size
= CFDataGetLength(cert_index_file_data
);
873 anchorLookupTable
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0,
874 &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
876 for (index_offset
= index_data_size
; index_offset
> 0; index_offset
-= sizeof(index_record
), pIndex
++)
878 offset_int_value
= pIndex
->offset
;
880 index_offset_value
= CFNumberCreate(kCFAllocatorDefault
, kCFNumberIntType
, &offset_int_value
);
881 index_hash
= CFDataCreate(kCFAllocatorDefault
, pIndex
->hash
, CC_SHA1_DIGEST_LENGTH
);
883 // see if the dictionary already has this key
884 release_offset
= false;
885 offsets
= (CFMutableArrayRef
)CFDictionaryGetValue(anchorLookupTable
, index_hash
);
888 offsets
= CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
889 release_offset
= true;
893 CFArrayAppendValue(offsets
, index_offset_value
);
895 // set the key value pair in the dictionary
896 CFDictionarySetValue(anchorLookupTable
, index_hash
, offsets
);
898 CFRelease(index_offset_value
);
899 CFRelease(index_hash
);
906 CFRelease(cert_index_file_data
);
908 if (NULL
!= anchorLookupTable
&& NULL
!= local_anchorTable
)
910 *pLookupTable
= anchorLookupTable
;
911 *ppAnchorTable
= local_anchorTable
;
916 CFReleaseSafe(anchorLookupTable
);
917 if (NULL
!= local_anchorTable
)
919 UnMapFile(local_anchorTable
, local_anchorTableSize
);
920 //munmap(kAnchorTable, local_anchorTableSize);
921 local_anchorTable
= NULL
;
922 local_anchorTableSize
= 0;
929 static void InitializeEscrowCertificates(const char* path_ptr
, CFArrayRef
*escrowRoots
, CFArrayRef
*escrowPCSRoots
)
931 CFDataRef file_data
= NULL
;
933 const char* dir_path
= path_ptr
;
934 if (NULL
== dir_path
)
936 file_data
= SecSystemTrustStoreCopyResourceContents(CFSTR("AppleESCertificates"), CFSTR("plist"), NULL
);
941 memset(buffer
, 0, 1024);
942 snprintf(buffer
, 1024, "%s/AppleESCertificates.plist", dir_path
);
943 file_data
= SecOTACopyFileContents(buffer
);
946 if (NULL
!= file_data
)
948 CFPropertyListFormat propFormat
;
949 CFDictionaryRef certsDictionary
= CFPropertyListCreateWithData(kCFAllocatorDefault
, file_data
, 0, &propFormat
, NULL
);
950 if (NULL
!= certsDictionary
&& CFDictionaryGetTypeID() == CFGetTypeID((CFTypeRef
)certsDictionary
))
952 CFArrayRef certs
= (CFArrayRef
)CFDictionaryGetValue(certsDictionary
, CFSTR("ProductionEscrowKey"));
953 if (NULL
!= certs
&& CFArrayGetTypeID() == CFGetTypeID((CFTypeRef
)certs
) && CFArrayGetCount(certs
) > 0)
955 *escrowRoots
= CFArrayCreateCopy(kCFAllocatorDefault
, certs
);
957 CFArrayRef pcs_certs
= (CFArrayRef
)CFDictionaryGetValue(certsDictionary
, CFSTR("ProductionPCSEscrowKey"));
958 if (NULL
!= pcs_certs
&& CFArrayGetTypeID() == CFGetTypeID((CFTypeRef
)pcs_certs
) && CFArrayGetCount(pcs_certs
) > 0)
960 *escrowPCSRoots
= CFArrayCreateCopy(kCFAllocatorDefault
, pcs_certs
);
963 CFReleaseSafe(certsDictionary
);
964 CFRelease(file_data
);
970 static SecOTAPKIRef
SecOTACreate()
972 TestOTALog("In SecOTACreate\n");
974 SecOTAPKIRef otapkiref
= NULL
;
976 otapkiref
= CFTypeAllocate(SecOTAPKI
, struct _OpaqueSecOTAPKI
, kCFAllocatorDefault
);
978 if (NULL
== otapkiref
)
983 // Make sure that if this routine has to bail that the clean up
984 // will do the right thing
985 otapkiref
->_blackListSet
= NULL
;
986 otapkiref
->_grayListSet
= NULL
;
987 otapkiref
->_allowList
= NULL
;
988 otapkiref
->_trustedCTLogs
= NULL
;
989 otapkiref
->_CTWhiteListData
= NULL
;
990 otapkiref
->_escrowCertificates
= NULL
;
991 otapkiref
->_escrowPCSCertificates
= NULL
;
992 otapkiref
->_evPolicyToAnchorMapping
= NULL
;
993 otapkiref
->_anchorLookupTable
= NULL
;
994 otapkiref
->_anchorTable
= NULL
;
995 otapkiref
->_assetPath
= NULL
;
996 otapkiref
->_assetVersion
= 0;
998 // Start off by getting the correct asset directory info
999 int asset_version
= 0;
1000 const char* path_ptr
= InitOTADirectory(&asset_version
);
1001 otapkiref
->_assetPath
= path_ptr
;
1002 otapkiref
->_assetVersion
= asset_version
;
1004 TestOTALog("SecOTACreate: asset_path = %s\n", path_ptr
);
1005 TestOTALog("SecOTACreate: asset_version = %d\n", asset_version
);
1007 // Get the set of black listed keys
1008 CFSetRef blackKeysSet
= InitializeBlackList(path_ptr
);
1009 if (NULL
== blackKeysSet
)
1011 CFReleaseNull(otapkiref
);
1014 otapkiref
->_blackListSet
= blackKeysSet
;
1016 // Get the set of gray listed keys
1017 CFSetRef grayKeysSet
= InitializeGrayList(path_ptr
);
1018 if (NULL
== grayKeysSet
)
1020 CFReleaseNull(otapkiref
);
1023 otapkiref
->_grayListSet
= grayKeysSet
;
1025 // Get the allow list dictionary
1026 // (now loaded lazily in SecOTAPKICopyAllowList)
1028 // Get the trusted Certificate Transparency Logs
1029 otapkiref
->_trustedCTLogs
= InitializeTrustedCTLogs(path_ptr
);
1031 // Get the EV whitelist
1032 otapkiref
->_CTWhiteListData
= InitializeCTWhiteListData(path_ptr
);
1034 CFArrayRef escrowCerts
= NULL
;
1035 CFArrayRef escrowPCSCerts
= NULL
;
1036 InitializeEscrowCertificates(path_ptr
, &escrowCerts
, &escrowPCSCerts
);
1037 if (NULL
== escrowCerts
|| NULL
== escrowPCSCerts
)
1039 CFReleaseNull(escrowCerts
);
1040 CFReleaseNull(escrowPCSCerts
);
1041 CFReleaseNull(otapkiref
);
1044 otapkiref
->_escrowCertificates
= escrowCerts
;
1045 otapkiref
->_escrowPCSCertificates
= escrowPCSCerts
;
1047 // Get the mapping of EV Policy OIDs to Anchor digest
1048 CFDictionaryRef evOidToAnchorDigestMap
= InitializeEVPolicyToAnchorDigestsTable(path_ptr
);
1049 if (NULL
== evOidToAnchorDigestMap
)
1051 CFReleaseNull(otapkiref
);
1054 otapkiref
->_evPolicyToAnchorMapping
= evOidToAnchorDigestMap
;
1056 CFDictionaryRef anchorLookupTable
= NULL
;
1057 const char* anchorTablePtr
= NULL
;
1059 if (!InitializeAnchorTable(path_ptr
, &anchorLookupTable
, &anchorTablePtr
))
1061 CFReleaseSafe(anchorLookupTable
);
1062 if (anchorTablePtr
) {
1063 free((void *)anchorTablePtr
);
1065 CFReleaseNull(otapkiref
);
1068 otapkiref
->_anchorLookupTable
= anchorLookupTable
;
1069 otapkiref
->_anchorTable
= anchorTablePtr
;
1073 static dispatch_once_t kInitializeOTAPKI
= 0;
1074 static const char* kOTAQueueLabel
= "com.apple.security.OTAPKIQueue";
1075 static dispatch_queue_t kOTAQueue
;
1076 static SecOTAPKIRef kCurrentOTAPKIRef
= NULL
;
1078 SecOTAPKIRef
SecOTAPKICopyCurrentOTAPKIRef()
1080 __block SecOTAPKIRef result
= NULL
;
1081 dispatch_once(&kInitializeOTAPKI
,
1083 kOTAQueue
= dispatch_queue_create(kOTAQueueLabel
, NULL
);
1084 kCurrentOTAPKIRef
= SecOTACreate();
1087 dispatch_sync(kOTAQueue
,
1089 result
= kCurrentOTAPKIRef
;
1090 CFRetainSafe(result
);
1096 CFSetRef
SecOTAPKICopyBlackListSet(SecOTAPKIRef otapkiRef
)
1098 CFSetRef result
= NULL
;
1099 if (NULL
== otapkiRef
)
1104 result
= otapkiRef
->_blackListSet
;
1105 CFRetainSafe(result
);
1110 CFSetRef
SecOTAPKICopyGrayList(SecOTAPKIRef otapkiRef
)
1112 CFSetRef result
= NULL
;
1113 if (NULL
== otapkiRef
)
1118 result
= otapkiRef
->_grayListSet
;
1119 CFRetainSafe(result
);
1123 CFDictionaryRef
SecOTAPKICopyAllowList(SecOTAPKIRef otapkiRef
)
1125 CFDictionaryRef result
= NULL
;
1126 if (NULL
== otapkiRef
)
1131 result
= otapkiRef
->_allowList
;
1133 result
= InitializeAllowList(otapkiRef
->_assetPath
);
1134 otapkiRef
->_allowList
= result
;
1137 CFRetainSafe(result
);
1141 CFArrayRef
SecOTAPKICopyAllowListForAuthKeyID(SecOTAPKIRef otapkiRef
, CFStringRef authKeyID
)
1143 // %%% temporary performance optimization:
1144 // only load dictionary if we know an allow list exists for this key
1145 const CFStringRef keyIDs
[3] = {
1146 CFSTR("7C724B39C7C0DB62A54F9BAA183492A2CA838259"),
1147 CFSTR("65F231AD2AF7F7DD52960AC702C10EEFA6D53B11"),
1148 CFSTR("D2A716207CAFD9959EEB430A19F2E0B9740EA8C7")
1150 CFArrayRef result
= NULL
;
1151 bool hasAllowList
= false;
1152 CFIndex count
= (sizeof(keyIDs
) / sizeof(keyIDs
[0]));
1153 for (CFIndex ix
=0; ix
<count
&& authKeyID
; ix
++) {
1154 if (kCFCompareEqualTo
== CFStringCompare(authKeyID
, keyIDs
[ix
], 0)) {
1155 hasAllowList
= true;
1159 if (!hasAllowList
|| !otapkiRef
) {
1163 CFDictionaryRef allowListDict
= SecOTAPKICopyAllowList(otapkiRef
);
1164 if (!allowListDict
) {
1168 // return a retained copy of the allow list array (or NULL)
1169 result
= CFDictionaryGetValue(allowListDict
, authKeyID
);
1170 CFRetainSafe(result
);
1171 CFReleaseSafe(allowListDict
);
1175 CFArrayRef
SecOTAPKICopyTrustedCTLogs(SecOTAPKIRef otapkiRef
)
1177 CFArrayRef result
= NULL
;
1178 if (NULL
== otapkiRef
)
1183 result
= otapkiRef
->_trustedCTLogs
;
1184 CFRetainSafe(result
);
1188 CFDataRef
SecOTAPKICopyCTWhiteList(SecOTAPKIRef otapkiRef
)
1190 CFDataRef result
= NULL
;
1191 if (NULL
== otapkiRef
)
1196 result
= otapkiRef
->_CTWhiteListData
;
1197 CFRetainSafe(result
);
1202 /* Returns an array of certificate data (CFDataRef) */
1203 CFArrayRef
SecOTAPKICopyEscrowCertificates(uint32_t escrowRootType
, SecOTAPKIRef otapkiRef
)
1205 CFMutableArrayRef result
= CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
1206 if (NULL
== otapkiRef
) {
1210 switch (escrowRootType
) {
1211 // Note: we shouldn't be getting called to return baseline roots,
1212 // since this function vends production roots by definition.
1213 case kSecCertificateBaselineEscrowRoot
:
1214 case kSecCertificateProductionEscrowRoot
:
1215 case kSecCertificateBaselineEscrowBackupRoot
:
1216 case kSecCertificateProductionEscrowBackupRoot
:
1217 if (otapkiRef
->_escrowCertificates
) {
1218 CFArrayRef escrowCerts
= otapkiRef
->_escrowCertificates
;
1219 CFArrayAppendArray(result
, escrowCerts
, CFRangeMake(0, CFArrayGetCount(escrowCerts
)));
1222 case kSecCertificateBaselineEscrowEnrollmentRoot
:
1223 case kSecCertificateProductionEscrowEnrollmentRoot
:
1224 if (otapkiRef
->_escrowCertificates
) {
1225 // for enrollment purposes, exclude the v100 root
1226 static const unsigned char V100EscrowRoot
[] = {
1227 0x65,0x5C,0xB0,0x3C,0x39,0x3A,0x32,0xA6,0x0B,0x96,
1228 0x40,0xC0,0xCA,0x73,0x41,0xFD,0xC3,0x9E,0x96,0xB3
1230 CFArrayRef escrowCerts
= otapkiRef
->_escrowCertificates
;
1231 CFIndex idx
, count
= CFArrayGetCount(escrowCerts
);
1232 for (idx
=0; idx
< count
; idx
++) {
1233 CFDataRef tmpData
= (CFDataRef
) CFArrayGetValueAtIndex(escrowCerts
, idx
);
1234 SecCertificateRef tmpCert
= (tmpData
) ? SecCertificateCreateWithData(NULL
, tmpData
) : NULL
;
1235 CFDataRef sha1Hash
= (tmpCert
) ? SecCertificateGetSHA1Digest(tmpCert
) : NULL
;
1236 const uint8_t *dp
= (sha1Hash
) ? CFDataGetBytePtr(sha1Hash
) : NULL
;
1237 if (!(dp
&& !memcmp(V100EscrowRoot
, dp
, sizeof(V100EscrowRoot
))) && tmpData
) {
1238 CFArrayAppendValue(result
, tmpData
);
1240 CFReleaseSafe(tmpCert
);
1244 case kSecCertificateBaselinePCSEscrowRoot
:
1245 case kSecCertificateProductionPCSEscrowRoot
:
1246 if (otapkiRef
->_escrowPCSCertificates
) {
1247 CFArrayRef escrowPCSCerts
= otapkiRef
->_escrowPCSCertificates
;
1248 CFArrayAppendArray(result
, escrowPCSCerts
, CFRangeMake(0, CFArrayGetCount(escrowPCSCerts
)));
1259 CFDictionaryRef
SecOTAPKICopyEVPolicyToAnchorMapping(SecOTAPKIRef otapkiRef
)
1261 CFDictionaryRef result
= NULL
;
1262 if (NULL
== otapkiRef
)
1267 result
= otapkiRef
->_evPolicyToAnchorMapping
;
1268 CFRetainSafe(result
);
1273 CFDictionaryRef
SecOTAPKICopyAnchorLookupTable(SecOTAPKIRef otapkiRef
)
1275 CFDictionaryRef result
= NULL
;
1276 if (NULL
== otapkiRef
)
1281 result
= otapkiRef
->_anchorLookupTable
;
1282 CFRetainSafe(result
);
1286 const char* SecOTAPKIGetAnchorTable(SecOTAPKIRef otapkiRef
)
1288 const char* result
= NULL
;
1289 if (NULL
== otapkiRef
)
1294 result
= otapkiRef
->_anchorTable
;
1298 int SecOTAPKIGetAssetVersion(SecOTAPKIRef otapkiRef
)
1301 if (NULL
== otapkiRef
)
1306 result
= otapkiRef
->_assetVersion
;
1310 void SecOTAPKIRefreshData()
1312 TestOTALog("In SecOTAPKIRefreshData\n");
1313 SecOTAPKIRef new_otaPKRef
= SecOTACreate();
1314 dispatch_sync(kOTAQueue
,
1316 CFReleaseSafe(kCurrentOTAPKIRef
);
1317 kCurrentOTAPKIRef
= new_otaPKRef
;
1321 /* Returns an array of certificate data (CFDataRef) */
1322 CFArrayRef
SecOTAPKICopyCurrentEscrowCertificates(uint32_t escrowRootType
, CFErrorRef
* error
)
1324 CFArrayRef result
= NULL
;
1326 SecOTAPKIRef otapkiref
= SecOTAPKICopyCurrentOTAPKIRef();
1327 if (NULL
== otapkiref
)
1329 SecError(errSecInternal
, error
, CFSTR("Unable to get the current OTAPKIRef"));
1333 result
= SecOTAPKICopyEscrowCertificates(escrowRootType
, otapkiref
);
1334 CFRelease(otapkiref
);
1338 SecError(errSecInternal
, error
, CFSTR("Could not get escrow certificates from the current OTAPKIRef"));
1343 int SecOTAPKIGetCurrentAssetVersion(CFErrorRef
* error
)
1347 SecOTAPKIRef otapkiref
= SecOTAPKICopyCurrentOTAPKIRef();
1348 if (NULL
== otapkiref
)
1350 SecError(errSecInternal
, error
, CFSTR("Unable to get the current OTAPKIRef"));
1354 result
= otapkiref
->_assetVersion
;
1358 int SecOTAPKISignalNewAsset(CFErrorRef
* error
)
1360 TestOTALog("SecOTAPKISignalNewAsset has been called!\n");
1361 SecOTAPKIRefreshData();