2 * Copyright (c) 2003-2018 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 #import <Foundation/Foundation.h>
27 #include "OTATrustUtilities.h"
37 #include <sys/syslimits.h>
40 #include <CoreFoundation/CoreFoundation.h>
42 #include "SecFramework.h"
44 #include <sys/param.h>
46 #include <os/transaction_private.h>
47 #include <utilities/SecCFRelease.h>
48 #include <utilities/SecCFError.h>
49 #include <utilities/SecCFWrappers.h>
50 #include <Security/SecBasePriv.h>
51 #include <Security/SecCertificatePriv.h>
52 #include <Security/SecFramework.h>
53 #include <dispatch/dispatch.h>
54 #include <dispatch/private.h>
55 #include <CommonCrypto/CommonDigest.h>
56 #include "trust/trustd/SecPinningDb.h"
57 #import <ipc/securityd_client.h>
60 #import <MobileAsset/MAAsset.h>
61 #import <MobileAsset/MAAssetQuery.h>
63 #include <utilities/sec_action.h>
64 #include <utilities/SecFileLocations.h>
65 #import "trust/trustd/SecTrustLoggingServer.h"
68 #if TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR
69 #import <MobileKeyBag/MobileKeyBag.h>
70 #include <System/sys/content_protection.h>
73 static inline bool isNSNumber(id nsType) {
74 return nsType && [nsType isKindOfClass:[NSNumber class]];
77 static inline bool isNSDictionary(id nsType) {
78 return nsType && [nsType isKindOfClass:[NSDictionary class]];
81 static inline bool isNSArray(id nsType) {
82 return nsType && [nsType isKindOfClass:[NSArray class]];
85 static inline bool isNSDate(id nsType) {
86 return nsType && [nsType isKindOfClass:[NSDate class]];
89 static inline bool isNSData(id nsType) {
90 return nsType && [nsType isKindOfClass:[NSData class]];
93 #define SECURITYD_ROLE_ACCOUNT 64
94 #define ROOT_ACCOUNT 0
96 bool SecOTAPKIIsSystemTrustd() {
97 static bool result = false;
98 static dispatch_once_t onceToken;
99 dispatch_once(&onceToken, ^{
101 // Test app running as trustd
102 #elif TARGET_OS_IPHONE
103 if (getuid() == SECURITYD_ROLE_ACCOUNT ||
104 (getuid() == ROOT_ACCOUNT && gTrustd)) // Test app running as trustd
106 if (getuid() == ROOT_ACCOUNT)
115 dispatch_queue_t SecTrustServerGetWorkloop(void) {
116 static dispatch_workloop_t workloop = NULL;
117 static dispatch_once_t onceToken;
118 dispatch_once(&onceToken, ^{
119 workloop = dispatch_workloop_create("com.apple.trustd.evaluation");
125 /* MARK: System Trust Store */
126 static CFStringRef kSecSystemTrustStoreBundlePath = CFSTR("/System/Library/Security/Certificates.bundle");
128 CFGiblisGetSingleton(CFBundleRef, SecSystemTrustStoreGetBundle, bundle, ^{
129 CFStringRef bundlePath = NULL;
130 #if TARGET_OS_SIMULATOR
131 char *simulatorRoot = getenv("SIMULATOR_ROOT");
133 bundlePath = CFStringCreateWithFormat(NULL, NULL, CFSTR("%s%@"), simulatorRoot, kSecSystemTrustStoreBundlePath);
136 bundlePath = CFRetainSafe(kSecSystemTrustStoreBundlePath);
137 CFURLRef url = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, bundlePath, kCFURLPOSIXPathStyle, true);
138 *bundle = (url) ? CFBundleCreate(kCFAllocatorDefault, url) : NULL;
140 CFReleaseSafe(bundlePath);
143 static CFURLRef SecSystemTrustStoreCopyResourceURL(CFStringRef resourceName,
144 CFStringRef resourceType, CFStringRef subDirName) {
146 CFBundleRef bundle = SecSystemTrustStoreGetBundle();
148 url = CFBundleCopyResourceURL(bundle, resourceName,
149 resourceType, subDirName);
152 secwarning("resource: %@.%@ in %@ not found", resourceName,
153 resourceType, subDirName);
158 static NSURL *SecSystemTrustStoreCopyResourceNSURL(NSString *resourceFileName) {
159 CFBundleRef bundle = SecSystemTrustStoreGetBundle();
163 NSURL *resourceDir = CFBridgingRelease(CFBundleCopyResourcesDirectoryURL(bundle));
167 NSURL *fileURL = [NSURL URLWithString:resourceFileName
168 relativeToURL:resourceDir];
170 secwarning("resource: %@ not found", resourceFileName);
175 static CFDataRef SecSystemTrustStoreCopyResourceContents(CFStringRef resourceName,
176 CFStringRef resourceType, CFStringRef subDirName) {
177 CFURLRef url = SecSystemTrustStoreCopyResourceURL(resourceName, resourceType, subDirName);
178 CFDataRef data = NULL;
181 if (!CFURLCreateDataAndPropertiesFromResource(kCFAllocatorDefault,
182 url, &data, NULL, NULL, &error)) {
183 secwarning("read: %ld", (long) error);
191 /* MARK: MobileAsset Updates */
192 // MARK: Forward Declarations
193 static uint64_t GetAssetVersion(CFErrorRef *error);
194 static uint64_t GetSystemVersion(CFStringRef key);
195 #if !TARGET_OS_BRIDGE
196 static BOOL UpdateFromAsset(NSURL *localURL, NSNumber *asset_version, NSError **error);
197 static NSNumber *SecExperimentUpdateAsset(MAAsset *asset, NSNumber *asset_version, NSError **error);
198 static void TriggerUnlockNotificationOTATrustAssetCheck(NSString* assetType, dispatch_queue_t queue);
201 /* This queue is for fetching changes to the OTAPKI reference or otherwise doing maintenance activities */
202 static dispatch_queue_t kOTABackgroundQueue = NULL;
205 NSString *kOTATrustContentVersionKey = @"MobileAssetContentVersion";
206 NSString *kOTATrustLastCheckInKey = @"MobileAssetLastCheckIn";
207 NSString *kOTATrustContextFilename = @"OTAPKIContext.plist";
208 NSString *kOTATrustTrustedCTLogsFilename = @"TrustedCTLogs.plist";
209 NSString *kOTATrustAnalyticsSamplingRatesFilename = @"AnalyticsSamplingRates.plist";
210 NSString *kOTATrustAppleCertifcateAuthoritiesFilename = @"AppleCertificateAuthorities.plist";
211 NSString *kOTASecExperimentConfigFilename = @"SecExperimentAssets.plist";
213 const CFStringRef kOTAPKIKillSwitchCT = CFSTR("CTKillSwitch");
215 #if !TARGET_OS_BRIDGE
216 NSString *OTATrustMobileAssetType = @"com.apple.MobileAsset.PKITrustSupplementals";
217 NSString *OTASecExperimentMobileAssetType = @"com.apple.MobileAsset.SecExperimentAssets";
218 #define kOTATrustMobileAssetNotification "com.apple.MobileAsset.PKITrustSupplementals.ma.cached-metadata-updated"
219 #define kOTATrustOnDiskAssetNotification "com.apple.trustd.asset-updated"
220 #define kOTATrustCheckInNotification "com.apple.trustd.asset-check-in"
221 #define kOTATrustKillSwitchNotification "com.apple.trustd.kill-switch"
222 #define kOTASecExperimentNewAssetNotification "com.apple.trustd.secexperiment.asset-updated"
223 const NSUInteger OTATrustMobileAssetCompatibilityVersion = 1;
224 const NSUInteger OTASecExperimentMobileAssetCompatibilityVersion = 1;
225 #define kOTATrustDefaultUpdatePeriod 60*60*12 // 12 hours
226 #define kOTATrustMinimumUpdatePeriod 60*5 // 5 min
227 #define kOTATrustDefaultWaitPeriod 60 // 1 min
230 const CFStringRef kSecSUPrefDomain = CFSTR("com.apple.SoftwareUpdate");
231 const CFStringRef kSecSUScanPrefConfigDataInstallKey = CFSTR("ConfigDataInstall");
234 // MARK: Helper functions
236 OTATrustLogLevelNone,
237 OTATrustLogLevelDebug,
238 OTATrustLogLevelInfo,
239 OTATrustLogLevelNotice,
240 OTATrustLogLevelError,
243 static void MakeOTATrustError(NSString *assetType, NSError **error, OTATrustLogLevel level, NSErrorDomain errDomain, OSStatus errCode, NSString *format,...) NS_FORMAT_FUNCTION(6,7);
244 static void MakeOTATrustErrorWithAttributes(NSString *assetType, NSError **error, OTATrustLogLevel level, NSErrorDomain errDomain, OSStatus errCode,
245 NSDictionary *attributes, NSString *format,...)
246 NS_FORMAT_FUNCTION(7,8);
248 static void MakeOTATrustErrorArgs(NSString *assetType, NSError **error, OTATrustLogLevel level, NSErrorDomain errDomain, OSStatus errCode,
249 NSDictionary *attributes, NSString *format, va_list arguments)
250 NS_FORMAT_FUNCTION(7,0);
252 static void LogLocally(OTATrustLogLevel level, NSString *errorString) {
254 case OTATrustLogLevelNone:
256 case OTATrustLogLevelDebug:
257 secdebug("OTATrust", "%@", errorString);
259 case OTATrustLogLevelInfo:
260 secinfo("OTATrust", "%@", errorString);
262 case OTATrustLogLevelNotice:
263 secnotice("OTATrust", "%@", errorString);
265 case OTATrustLogLevelError:
266 secerror("OTATrust: %@", errorString);
271 static void LogRemotelyWithAttributes(OTATrustLogLevel level, NSError **error, NSDictionary *attributes) {
272 #if ENABLE_TRUSTD_ANALYTICS
273 /* only report errors and notices */
274 if (error && level == OTATrustLogLevelError) {
275 [[TrustdHealthAnalytics logger] logResultForEvent:TrustdHealthAnalyticsEventOTAPKIEvent hardFailure:YES result:*error withAttributes:attributes];
276 } else if (error && level == OTATrustLogLevelNotice) {
277 [[TrustdHealthAnalytics logger] logResultForEvent:TrustdHealthAnalyticsEventOTAPKIEvent hardFailure:NO result:*error withAttributes:attributes];
279 #endif // ENABLE_TRUSTD_ANALYTICS
282 static void LogRemotely(OTATrustLogLevel level, NSError **error) {
283 LogRemotelyWithAttributes(level, error, nil);
286 static void MakeOTATrustErrorArgs(NSString *assetType, NSError **error, OTATrustLogLevel level, NSErrorDomain errDomain, OSStatus errCode,
287 NSDictionary *attributes, NSString *format, va_list args) {
288 NSString *formattedString = nil;
290 formattedString = [[NSString alloc] initWithFormat:format arguments:args];
293 NSError *localError = nil;
294 NSMutableDictionary *userInfo = [[NSMutableDictionary alloc] init];
296 [userInfo setObject:formattedString forKey:NSLocalizedDescriptionKey];
298 if (error && *error) {
299 userInfo[NSUnderlyingErrorKey] = *error;
301 localError = [NSError errorWithDomain:errDomain
305 LogLocally(level, formattedString);
306 if ([assetType isEqualToString:OTATrustMobileAssetType]) {
307 LogRemotelyWithAttributes(level, &localError, attributes);
309 if (error) { *error = localError; }
312 static void MakeOTATrustErrorWithAttributes(NSString *assetType, NSError **error, OTATrustLogLevel level, NSErrorDomain errDomain, OSStatus errCode,
313 NSDictionary *attributes, NSString *format,...) {
315 va_start(args, format);
316 MakeOTATrustErrorArgs(assetType, error, level, errDomain, errCode, attributes, format, args);
320 static void MakeOTATrustError(NSString *assetType, NSError **error, OTATrustLogLevel level, NSErrorDomain errDomain, OSStatus errCode, NSString *format,...) {
322 va_start(args, format);
323 MakeOTATrustErrorArgs(assetType, error, level, errDomain, errCode, nil, format, args);
327 static BOOL CanCheckMobileAsset(void) {
330 /* Check the user's SU preferences to determine if "Install system data files" is off */
331 if (!CFPreferencesSynchronize(kSecSUPrefDomain, kCFPreferencesAnyUser, kCFPreferencesCurrentHost)) {
332 secerror("OTATrust: unable to synchronize SoftwareUpdate prefs");
337 if (CFPreferencesAppValueIsForced(kSecSUScanPrefConfigDataInstallKey, kSecSUPrefDomain)) {
338 value = CFBridgingRelease(CFPreferencesCopyAppValue(kSecSUScanPrefConfigDataInstallKey, kSecSUPrefDomain));
340 value = CFBridgingRelease(CFPreferencesCopyValue(kSecSUScanPrefConfigDataInstallKey, kSecSUPrefDomain,
341 kCFPreferencesAnyUser, kCFPreferencesCurrentHost));
343 if (isNSNumber(value)) {
344 result = [value boolValue];
347 if (!result) { secnotice("OTATrust", "User has disabled system data installation."); }
349 /* MobileAsset.framework isn't mastered into the BaseSystem. Check that the MA classes are linked. */
350 if (![ASAssetQuery class] || ![ASAsset class] || ![MAAssetQuery class] || ![MAAsset class]) {
351 secnotice("OTATrust", "Weak-linked MobileAsset framework missing.");
358 static BOOL ShouldUpdateWithAsset(NSString *assetType, NSNumber *asset_version) {
359 if (![asset_version isKindOfClass:[NSNumber class]]) {
362 CFErrorRef error = nil;
363 uint64_t current_version;
364 if ([assetType isEqualToString:OTATrustMobileAssetType]) {
365 current_version = SecOTAPKIGetCurrentAssetVersion(&error);
366 } else if ([assetType isEqualToString:OTASecExperimentMobileAssetType]) {
367 current_version = SecOTASecExperimentGetCurrentAssetVersion(&error);
372 CFReleaseNull(error);
375 if ([asset_version compare:[NSNumber numberWithUnsignedLongLong:current_version]] == NSOrderedDescending) {
381 // MARK: File management functions
382 static bool verify_create_path(const char *path) {
383 int ret = mkpath_np(path, 0755);
384 if (!(ret == 0 || ret == EEXIST)) {
385 secerror("could not create path: %s (%s)", path, strerror(ret));
391 static NSURL *GetAssetFileURL(NSString *filename) {
392 /* Make sure the /Library/Keychains directory is there */
393 NSURL *keychainsDirectory = CFBridgingRelease(SecCopyURLForFileInSystemKeychainDirectory(nil));
394 NSURL *directory = [keychainsDirectory URLByAppendingPathComponent:@"SupplementalsAssets/" isDirectory:YES];
395 if (!verify_create_path([directory fileSystemRepresentation])) {
400 return [directory URLByAppendingPathComponent:filename];
406 static void DeleteFileWithName(NSString *filename) {
407 NSURL *fileURL = GetAssetFileURL(filename);
408 if (remove([fileURL fileSystemRepresentation]) == -1) {
410 if (error != ENOENT) {
411 secnotice("OTATrust", "failed to remove %@: %s", fileURL, strerror(error));
416 static BOOL UpdateOTAContextOnDisk(NSString *key, id value, NSError **error) {
417 if (SecOTAPKIIsSystemTrustd()) {
418 /* Get current context, if applicable, and update/add key/value */
419 NSURL *otaContextFile = GetAssetFileURL(kOTATrustContextFilename);
420 NSDictionary *currentContext = [NSDictionary dictionaryWithContentsOfURL:otaContextFile];
421 NSMutableDictionary *newContext = nil;
422 if (currentContext) {
423 newContext = [currentContext mutableCopy];
425 newContext = [NSMutableDictionary dictionary];
427 newContext[key] = value;
429 /* Write dictionary to disk */
430 if (![newContext writeToURL:otaContextFile error:error]) {
431 secerror("OTATrust: unable to write OTA Context to disk: %@", error ? *error : nil);
432 LogRemotely(OTATrustLogLevelError, error);
440 static BOOL UpdateOTAContext(NSNumber *asset_version, NSError **error) {
441 return UpdateOTAContextOnDisk(kOTATrustContentVersionKey, asset_version, error) && UpdateOTACheckInDate();
444 /* Delete only the asset data but not the check-in time. */
445 static void DeleteOldAssetData(void) {
446 if (SecOTAPKIIsSystemTrustd()) {
447 /* Delete the asset files, but keep the check-in time and version */
448 DeleteFileWithName(kOTATrustTrustedCTLogsFilename);
449 DeleteFileWithName(kOTATrustAnalyticsSamplingRatesFilename);
450 DeleteFileWithName(kOTATrustAppleCertifcateAuthoritiesFilename);
454 /* Delete all asset data, intended for error cases */
455 static BOOL DeleteAssetFromDisk(void) {
456 if (SecOTAPKIIsSystemTrustd()) {
457 DeleteOldAssetData();
458 DeleteFileWithName(kOTATrustContextFilename);
464 #if TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR
465 static bool ChangeFileProtectionToClassD(NSURL *fileURL, NSError **error) {
467 int file_fd = open([fileURL fileSystemRepresentation], O_RDONLY);
469 int retval = fcntl(file_fd, F_SETPROTECTIONCLASS, PROTECTION_CLASS_D);
471 MakeOTATrustError(OTATrustMobileAssetType, error, OTATrustLogLevelError, NSPOSIXErrorDomain, errno,
472 @"set proteciton class error for asset %d: %s", errno, strerror(errno));
477 MakeOTATrustError(OTATrustMobileAssetType, error, OTATrustLogLevelError, NSPOSIXErrorDomain, errno,
478 @"open error for asset %d: %s", errno, strerror(errno));
485 static BOOL CopyFileToDisk(NSString *filename, NSURL *localURL, NSError **error) {
486 if (SecOTAPKIIsSystemTrustd()) {
487 NSURL *toFileURL = GetAssetFileURL(filename);
488 secdebug("OTATrust", "will copy asset file data from \"%@\"", localURL);
489 copyfile_state_t state = copyfile_state_alloc();
490 int retval = copyfile([localURL fileSystemRepresentation], [toFileURL fileSystemRepresentation],
491 state, COPYFILE_DATA);
492 copyfile_state_free(state);
494 MakeOTATrustError(OTATrustMobileAssetType, error, OTATrustLogLevelError, NSPOSIXErrorDomain, errno,
495 @"copyfile error for asset %d: %s", errno, strerror(errno));
498 /* make sure we can read this file before first unlock */
499 #if TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR
500 return ChangeFileProtectionToClassD(toFileURL, error);
509 static void GetKillSwitchAttributes(NSDictionary *attributes) {
510 bool killSwitchEnabled = false;
513 NSNumber *ctKillSwitch = [attributes objectForKey:(__bridge NSString*)kOTAPKIKillSwitchCT];
514 if (isNSNumber(ctKillSwitch)) {
515 NSError *error = nil;
516 UpdateOTAContextOnDisk((__bridge NSString*)kOTAPKIKillSwitchCT, ctKillSwitch, &error);
517 UpdateKillSwitch((__bridge NSString*)kOTAPKIKillSwitchCT, [ctKillSwitch boolValue]);
518 secnotice("OTATrust", "got CT kill switch = %d", [ctKillSwitch boolValue]);
519 killSwitchEnabled = true;
522 /* Other kill switches TBD.
523 * When adding one, make sure to add to the Analytics Samplers since these kill switches
524 * are installed before the full asset is downloaded and installed. (A device can have the
525 * kill switches without having the asset version that contained them.) */
527 // notify the other trustds if any kill switch was read
528 if (SecOTAPKIIsSystemTrustd() && killSwitchEnabled) {
529 notify_post(kOTATrustKillSwitchNotification);
533 // MARK: Fetch and Update Functions
534 static NSNumber *PKIUpdateAndPurgeAsset(MAAsset *asset, NSNumber *asset_version, NSError **error) {
535 if (SecPinningDbUpdateFromURL([asset getLocalFileUrl], error) &&
536 UpdateFromAsset([asset getLocalFileUrl], asset_version, error)) {
537 secnotice("OTATrust", "finished update to version %@ from installed asset. purging asset.", asset_version);
538 #if ENABLE_TRUSTD_ANALYTICS
539 [[TrustdHealthAnalytics logger] logSuccessForEventNamed:TrustdHealthAnalyticsEventOTAPKIEvent];
540 #endif // ENABLE_TRUSTD_ANALYTICS
541 [asset purge:^(MAPurgeResult purge_result) {
542 if (purge_result != MAPurgeSucceeded) {
543 secerror("OTATrust: purge failed: %ld", (long)purge_result);
546 return asset_version;
548 MakeOTATrustError(OTATrustMobileAssetType, error, OTATrustLogLevelError, NSOSStatusErrorDomain, errSecCallbackFailed,
549 @"Failed to install new asset version %@ from %@", asset_version, [asset getLocalFileUrl]);
554 static NSNumber *UpdateAndPurgeAsset(NSString* assetType, MAAsset *asset, NSNumber *asset_version, NSError **error) {
555 if ([assetType isEqualToString:OTATrustMobileAssetType]) {
556 return PKIUpdateAndPurgeAsset(asset, asset_version, error);
557 } else if ([assetType isEqualToString:OTASecExperimentMobileAssetType]) {
558 return SecExperimentUpdateAsset(asset, asset_version, error);
564 static MADownloadOptions *GetMADownloadOptions(BOOL wait) {
565 /* default behavior */
566 MADownloadOptions *options = [[MADownloadOptions alloc] init];
567 options.discretionary = YES;
568 options.allowsCellularAccess = NO;
570 /* If an XPC interface is waiting on this, all expenses allowed */
572 options.discretionary = NO;
573 options.allowsCellularAccess = YES;
577 /* If last asset check-in was too long ago, use more expensive options */
578 SecOTAPKIRef otapkiref = SecOTAPKICopyCurrentOTAPKIRef();
579 if (!SecOTAPKIAssetStalenessLessThanSeconds(otapkiref, kSecOTAPKIAssetStalenessWarning)) {
580 secnotice("OTATrust", "Asset staleness state: warning");
581 options.allowsCellularAccess = YES;
582 options.discretionary = NO;
583 } else if (!SecOTAPKIAssetStalenessLessThanSeconds(otapkiref, kSecOTAPKIAssetStalenessAtRisk)) {
584 secnotice("OTATrust", "Asset staleness state: at risk");
585 options.discretionary = NO;
587 CFReleaseNull(otapkiref);
591 static BOOL assetVersionCheck(NSString *assetType, MAAsset *asset) {
592 NSUInteger compatVersion;
593 NSError *ma_error = nil;
594 if ([assetType isEqualToString:OTATrustMobileAssetType]) {
595 compatVersion = OTATrustMobileAssetCompatibilityVersion;
597 compatVersion = OTASecExperimentMobileAssetCompatibilityVersion;
599 /* Check Compatibility Version against this software version */
600 NSNumber *compatibilityVersion = [asset assetProperty:@"_CompatibilityVersion"];
601 if (!isNSNumber(compatibilityVersion) ||
602 [compatibilityVersion unsignedIntegerValue] != compatVersion) {
603 MakeOTATrustError(assetType, &ma_error, OTATrustLogLevelNotice, NSOSStatusErrorDomain, errSecIncompatibleVersion,
604 @"skipping asset %@ because Compatibility Version doesn't match %@", assetType, compatibilityVersion);
607 /* Check Content Version against the current content version */
608 NSNumber *asset_version = [asset assetProperty:@"_ContentVersion"];
609 if (!ShouldUpdateWithAsset(assetType, asset_version)) {
610 /* write the version and last (successful) check-in time */
611 if ([assetType isEqualToString:OTATrustMobileAssetType]) {
612 UpdateOTAContext(asset_version, &ma_error);
613 NSDictionary *eventAttributes = @{
614 @"assetVersion" : asset_version,
615 @"systemVersion" : @(GetSystemVersion((__bridge CFStringRef)kOTATrustContentVersionKey)),
616 @"installedVersion" : @(SecOTAPKIGetCurrentAssetVersion(nil)),
618 MakeOTATrustErrorWithAttributes(assetType, &ma_error, OTATrustLogLevelNotice, NSOSStatusErrorDomain, errSecDuplicateItem, eventAttributes,
619 @"skipping asset %@ because we already have _ContentVersion %@ (or newer)", assetType, asset_version);
621 MakeOTATrustError(assetType, &ma_error, OTATrustLogLevelNotice, NSOSStatusErrorDomain, errSecDuplicateItem,
622 @"skipping asset %@ because we already have _ContentVersion %@ (or newer)", assetType, asset_version);
629 static int downloadWaitTime(void) {
630 NSUserDefaults *defaults = [[NSUserDefaults alloc] initWithSuiteName:@"com.apple.security"];
631 NSNumber *updateTimeout = [defaults valueForKey:@"TrustdAssetDownloadWaitTimeout"];
632 int timeout = kOTATrustDefaultWaitPeriod;
633 if (isNSNumber(updateTimeout)) {
634 timeout = [updateTimeout intValue];
639 static BOOL DownloadOTATrustAsset(BOOL isLocalOnly, BOOL wait, NSString *assetType, NSError **error) {
640 if (!CanCheckMobileAsset()) {
641 MakeOTATrustError(assetType, error, OTATrustLogLevelNotice, NSOSStatusErrorDomain, errSecServiceNotAvailable,
642 @"MobileAsset disabled, skipping check.");
646 __block NSNumber *updated_version = nil;
647 __block dispatch_semaphore_t done = wait ? dispatch_semaphore_create(0) : nil;
648 __block NSError *ma_error = nil;
650 secnotice("OTATrust", "begin MobileAsset query for catalog %@", assetType);
651 [MAAsset startCatalogDownload:assetType options:GetMADownloadOptions(wait) then:^(MADownLoadResult result) {
653 os_transaction_t transaction = os_transaction_create("com.apple.trustd.asset.download");
654 if (result != MADownloadSuccessful) {
655 MakeOTATrustError(assetType, &ma_error, OTATrustLogLevelError, @"MADownLoadResult", (OSStatus)result,
656 @"failed to download catalog for asset %@: %ld", assetType, (long)result);
657 if (result == MADownloadDaemonNotReady && ([assetType isEqualToString:OTATrustMobileAssetType])) {
658 /* mobileassetd has to wait for first unlock to download. trustd usually launches before first unlock. */
659 TriggerUnlockNotificationOTATrustAssetCheck(assetType, kOTABackgroundQueue);
663 MAAssetQuery *query = [[MAAssetQuery alloc] initWithType:(NSString *)assetType];
664 [query augmentResultsWithState:true];
666 secnotice("OTATrust", "begin MobileAsset metadata sync request %{public}@", assetType);
667 MAQueryResult queryResult = [query queryMetaDataSync];
668 if (queryResult != MAQuerySuccessful) {
669 MakeOTATrustError(assetType, &ma_error, OTATrustLogLevelError, @"MAQueryResult", (OSStatus)queryResult,
670 @"failed to query MobileAsset %@ metadata: %ld", assetType, (long)queryResult);
674 if (!query.results) {
675 MakeOTATrustError(assetType, &ma_error, OTATrustLogLevelError, NSOSStatusErrorDomain, errSecInternal,
676 @"no results in MobileAsset query for %@", assetType);
680 bool began_async_job = false;
681 for (MAAsset *asset in query.results) {
682 /* Check Compatibility Version against this software version */
683 NSNumber *asset_version = [asset assetProperty:@"_ContentVersion"];
684 if (!assetVersionCheck(assetType, asset)) {
688 if ([assetType isEqualToString:OTATrustMobileAssetType]) {
689 GetKillSwitchAttributes(asset.attributes);
692 switch (asset.state) {
694 MakeOTATrustError(assetType, &ma_error, OTATrustLogLevelError, NSOSStatusErrorDomain, errSecInternal,
695 @"unknown asset state %ld", (long)asset.state);
698 /* The asset is already in the cache, get it from disk. */
699 secdebug("OTATrust", "OTATrust asset %{public}@ already installed", assetType);
700 updated_version = UpdateAndPurgeAsset(assetType, asset, asset_version, &ma_error);
703 MakeOTATrustError(assetType, &ma_error, OTATrustLogLevelError, @"MAAssetState", (OSStatus)asset.state,
704 @"asset %@ is unknown", assetType);
707 secnotice("OTATrust", "asset %{public}@ is downloading", assetType);
710 secnotice("OTATrust", "begin download of OTATrust asset");
711 began_async_job = true;
712 [asset startDownload:GetMADownloadOptions(wait) then:^(MADownLoadResult downloadResult) {
714 os_transaction_t inner_transaction = os_transaction_create("com.apple.trustd.asset.downloadAsset");
715 if (downloadResult != MADownloadSuccessful) {
716 MakeOTATrustError(assetType, &ma_error, OTATrustLogLevelError, @"MADownLoadResult", (OSStatus)downloadResult,
717 @"failed to download asset %@: %ld", assetType, (long)downloadResult);
720 updated_version = UpdateAndPurgeAsset(assetType, asset, asset_version, &ma_error);
722 dispatch_semaphore_signal(done);
724 (void)inner_transaction; // dead store
725 inner_transaction = nil;
729 } /* switch (asset.state) */
730 } /* for (MAAsset.. */
731 if (wait && !began_async_job) {
732 dispatch_semaphore_signal(done);
734 /* Done with the transaction */
735 (void)transaction; // dead store
737 } /* autoreleasepool */
738 }]; /* [MAAsset startCatalogDownload: ] */
740 /* If the caller is waiting for a response, wait up to one minute for the update to complete.
741 * If the MAAsset callback does not complete in that time, report a timeout.
742 * If the MAAsset callback completes and did not successfully update, it should report an error;
743 * forward that error to the caller.
744 * If the MAAsset callback completes and did not update and did not provide an error; report
745 * an unknown error. */
748 if (dispatch_semaphore_wait(done, dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC * downloadWaitTime())) != 0) {
749 MakeOTATrustError(assetType, error, OTATrustLogLevelError, NSOSStatusErrorDomain, errSecNetworkFailure,
750 @"Failed to get asset %@ metadata within %d seconds.", assetType, downloadWaitTime());
752 result = (updated_version != nil);
753 if (error && ma_error) {
755 } else if (!result) {
756 MakeOTATrustError(assetType, error, OTATrustLogLevelError, NSOSStatusErrorDomain, errSecInternalComponent,
757 @"Unknown error occurred.");
764 static void TriggerUnlockNotificationOTATrustAssetCheck(NSString* assetType, dispatch_queue_t queue) {
765 SecOTAPKIRef otapkiref = SecOTAPKICopyCurrentOTAPKIRef();
766 /* If the last check-in is recent enough, wait for our regularly scheduled check-in. */
767 if (SecOTAPKIAssetStalenessLessThanSeconds(otapkiref, kSecOTAPKIAssetStalenessAtRisk)) {
768 CFReleaseNull(otapkiref);
771 CFReleaseNull(otapkiref);
772 #if !TARGET_OS_SIMULATOR
773 /* register for unlock notifications */
775 notify_register_dispatch(kMobileKeyBagLockStatusNotificationID, &out_token, queue, ^(int token) {
776 secnotice("OTATrust", "Got lock status notification for at-risk last check-in after MA daemon error");
777 (void)DownloadOTATrustAsset(NO, NO, assetType, nil);
778 notify_cancel(token);
783 static bool InitializeKillSwitch(NSString *key) {
784 #if !TARGET_OS_BRIDGE
785 NSError *error = nil;
786 NSDictionary *OTAPKIContext = [NSDictionary dictionaryWithContentsOfURL:GetAssetFileURL(kOTATrustContextFilename) error:&error];
787 if (isNSDictionary(OTAPKIContext)) {
788 NSNumber *killSwitchValue = OTAPKIContext[key];
789 if (isNSNumber(killSwitchValue)) {
790 secinfo("OTATrust", "found on-disk kill switch %{public}@ with value %d", key, [killSwitchValue boolValue]);
791 return [killSwitchValue boolValue];
793 MakeOTATrustError(OTATrustMobileAssetType, &error, OTATrustLogLevelNotice, NSOSStatusErrorDomain, errSecInvalidValue,
794 @"OTAContext.plist missing check-in");
797 MakeOTATrustError(OTATrustMobileAssetType, &error, OTATrustLogLevelNotice, NSOSStatusErrorDomain, errSecMissingValue,
798 @"OTAContext.plist missing dictionary");
804 static void InitializeOTATrustAsset(dispatch_queue_t queue) {
805 /* Only the "system" trustd does updates */
806 if (SecOTAPKIIsSystemTrustd()) {
807 /* Asynchronously ask MobileAsset for most recent asset. */
808 dispatch_async(queue, ^{
809 secnotice("OTATrust", "Initial check with MobileAsset for newer PKITrustSupplementals asset");
810 (void)DownloadOTATrustAsset(NO, NO, OTATrustMobileAssetType, nil);
813 /* Register for changes in our asset */
814 if (CanCheckMobileAsset()) {
816 notify_register_dispatch(kOTATrustMobileAssetNotification, &out_token, queue, ^(int __unused token) {
817 secnotice("OTATrust", "Got notification about a new PKITrustSupplementals asset from mobileassetd.");
818 (void)DownloadOTATrustAsset(YES, NO, OTATrustMobileAssetType, nil);
822 /* Register for changes signaled by the system trustd */
823 secnotice("OTATrust", "Initializing listener for PKI Asset changes from system trustd.");
825 notify_register_dispatch(kOTATrustOnDiskAssetNotification, &out_token, queue, ^(int __unused token) {
826 secnotice("OTATrust", "Got notification about a new PKITrustSupplementals asset from system trustd.");
827 NSError *nserror = nil;
828 CFErrorRef error = nil;
829 NSNumber *asset_version = [NSNumber numberWithUnsignedLongLong:GetAssetVersion(&error)];
831 nserror = CFBridgingRelease(error);
833 if (!UpdateFromAsset(GetAssetFileURL(nil), asset_version, &nserror)) {
834 secerror("OTATrust: failed to update from asset after notification: %@", nserror);
835 /* Reset our last check-in time and reset the asset version to the system asset version -- even
836 * though we may be using something newer than that (but not as new as what's on disk). On re-launch
837 * (provided reading from disk still fails) we'd be using the system asset version anyway. */
838 SecOTAPKIResetCurrentAssetVersion(&error);
842 notify_register_dispatch(kOTATrustCheckInNotification, &out_token2, queue, ^(int __unused token) {
843 secinfo("OTATrust", "Got notification about successful PKITrustSupplementals asset check-in");
844 (void)UpdateOTACheckInDate();
847 notify_register_dispatch(kOTATrustKillSwitchNotification, &out_token3, queue, ^(int __unused token) {
848 UpdateKillSwitch((__bridge NSString*)kOTAPKIKillSwitchCT, InitializeKillSwitch((__bridge NSString*)kOTAPKIKillSwitchCT));
853 static void InitializeOTASecExperimentAsset(dispatch_queue_t queue) {
854 /* Only the "system" trustd does updates */
855 if (SecOTAPKIIsSystemTrustd()) {
856 /* Asynchronously ask MobileAsset for most recent asset. */
857 dispatch_async(queue, ^{
858 secnotice("OTATrust", "Initial check with MobileAsset for newer SecExperiment asset");
859 (void)DownloadOTATrustAsset(NO, NO, OTASecExperimentMobileAssetType, nil);
862 /* Register for changes signaled by the system trustd */
863 secnotice("OTATrust", "Initializing listener for SecExperiment Asset changes from system trustd.");
865 notify_register_dispatch(kOTASecExperimentNewAssetNotification, &out_token, queue, ^(int __unused token) {
866 NSError *error = nil;
867 secnotice("OTATrust", "Got notification about a new SecExperiment asset from system trustd.");
868 MAAssetQuery *assetQuery = [[MAAssetQuery alloc] initWithType:OTASecExperimentMobileAssetType];
869 [assetQuery returnTypes:MAInstalledOnly];
870 MAQueryResult queryResult = [assetQuery queryMetaDataSync];
872 if (queryResult != MAQuerySuccessful) {
873 secerror("OTATrust: failed to update SecExperiment Asset after notification: %ld", (long)queryResult);
875 secnotice("OTATrust", "Updated SecExperiment asset successfully");
876 for (MAAsset *asset in assetQuery.results) {
877 NSNumber *asset_version = [asset assetProperty:@"_ContentVersion"];
878 if (!assetVersionCheck(OTASecExperimentMobileAssetType, asset)) {
881 UpdateAndPurgeAsset(OTASecExperimentMobileAssetType, asset, asset_version, &error);
888 static void TriggerPeriodicOTATrustAssetChecks(dispatch_queue_t queue) {
889 if (SecOTAPKIIsSystemTrustd()) {
890 static sec_action_t action;
891 static bool first_launch = true;
892 static dispatch_once_t onceToken;
893 dispatch_once(&onceToken, ^{
894 NSUserDefaults *defaults = [[NSUserDefaults alloc] initWithSuiteName:@"com.apple.security"];
895 NSNumber *updateDeltas = [defaults valueForKey:@"PKITrustSupplementalsUpdatePeriod"];
896 int delta = kOTATrustDefaultUpdatePeriod;
897 if (isNSNumber(updateDeltas)) {
898 delta = [updateDeltas intValue];
899 if (delta < kOTATrustMinimumUpdatePeriod) {
900 delta = kOTATrustMinimumUpdatePeriod;
903 secnotice("OTATrust", "Setting periodic update delta to %d seconds", delta);
904 action = sec_action_create_with_queue(queue,"OTATrust", delta);
905 sec_action_set_handler(action, ^{
907 (void)DownloadOTATrustAsset(NO, NO, OTASecExperimentMobileAssetType, nil);
908 (void)DownloadOTATrustAsset(NO, NO, OTATrustMobileAssetType, nil);
910 first_launch = false;
913 sec_action_perform(action);
916 #endif /* !TARGET_OS_BRIDGE */
919 /* MARK: Initialization functions */
920 static CFPropertyListRef CFPropertyListCopyFromSystem(CFStringRef asset) {
921 CFPropertyListRef plist = NULL;
922 CFDataRef xmlData = SecSystemTrustStoreCopyResourceContents(asset, CFSTR("plist"), NULL);
925 plist = CFPropertyListCreateWithData(kCFAllocatorDefault, xmlData, kCFPropertyListImmutable, NULL, NULL);
932 static uint64_t GetSystemVersion(CFStringRef key) {
933 uint64_t system_version = 0;
934 int64_t asset_number = 0;
936 CFDataRef assetVersionData = SecSystemTrustStoreCopyResourceContents(CFSTR("AssetVersion"), CFSTR("plist"), NULL);
937 if (NULL != assetVersionData) {
938 CFPropertyListFormat propFormat;
939 CFDictionaryRef versionPlist = CFPropertyListCreateWithData(kCFAllocatorDefault, assetVersionData, 0, &propFormat, NULL);
940 if (NULL != versionPlist && CFDictionaryGetTypeID() == CFGetTypeID(versionPlist)) {
941 CFNumberRef versionNumber = (CFNumberRef)CFDictionaryGetValue(versionPlist, (const void *)key);
942 if (NULL != versionNumber){
943 CFNumberGetValue(versionNumber, kCFNumberSInt64Type, &asset_number);
944 if (asset_number < 0) { // Not valid
947 system_version = (uint64_t)asset_number;
950 CFReleaseSafe(versionPlist);
951 CFReleaseSafe(assetVersionData);
954 return system_version;
957 static bool initialization_error_from_asset_data = false;
959 static bool ShouldInitializeWithAsset(void) {
960 uint64_t system_version = GetSystemVersion((__bridge CFStringRef)kOTATrustContentVersionKey);
961 uint64_t asset_version = GetAssetVersion(nil);
963 if (asset_version > system_version) {
964 secnotice("OTATrust", "Using asset v%llu instead of system v%llu", asset_version, system_version);
965 return !initialization_error_from_asset_data;
970 static CFSetRef CFSetCreateFromPropertyList(CFPropertyListRef plist) {
971 CFSetRef result = NULL;
974 CFMutableSetRef tempSet = NULL;
975 if (CFGetTypeID(plist) == CFArrayGetTypeID()) {
976 tempSet = CFSetCreateMutable(kCFAllocatorDefault, 0, &kCFTypeSetCallBacks);
977 if (NULL == tempSet) {
980 CFArrayRef array = (CFArrayRef)plist;
981 CFIndex num_keys = CFArrayGetCount(array);
982 for (CFIndex idx = 0; idx < num_keys; idx++) {
983 CFDataRef data = (CFDataRef)CFArrayGetValueAtIndex(array, idx);
984 CFSetAddValue(tempSet, data);
995 static CF_RETURNS_RETAINED CFSetRef InitializeBlackList() {
996 CFPropertyListRef plist = CFPropertyListCopyFromSystem(CFSTR("Blocked"));
997 CFSetRef result = CFSetCreateFromPropertyList(plist);
998 CFReleaseSafe(plist);
1003 static CF_RETURNS_RETAINED CFSetRef InitializeGrayList() {
1004 CFPropertyListRef plist = CFPropertyListCopyFromSystem(CFSTR("GrayListedKeys"));
1005 CFSetRef result = CFSetCreateFromPropertyList(plist);
1006 CFReleaseSafe(plist);
1011 static CF_RETURNS_RETAINED CFURLRef InitializePinningList() {
1012 return SecSystemTrustStoreCopyResourceURL(CFSTR("CertificatePinning"), CFSTR("plist"), NULL);
1015 static CF_RETURNS_RETAINED CFDictionaryRef InitializeAllowList() {
1016 CFPropertyListRef allowList = CFPropertyListCopyFromSystem(CFSTR("Allowed"));
1018 if (allowList && (CFGetTypeID(allowList) == CFDictionaryGetTypeID())) {
1021 CFReleaseNull(allowList);
1026 static NSDictionary <NSData *, NSDictionary *> *ConvertTrustedCTLogsArrayToDictionary(NSArray *trustedLogsArray) {
1027 NSMutableDictionary <NSData *, NSDictionary *> *result = [NSMutableDictionary dictionaryWithCapacity:trustedLogsArray.count];
1028 [trustedLogsArray enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
1029 if (!isNSDictionary(obj)) {
1030 secerror("OTATrust: failed to read entry from trusted CT logs array at index %lu", (unsigned long)idx);
1033 NSDictionary *log_data = (NSDictionary *)obj;
1034 NSData *log_id = log_data[@"log_id"];
1035 if (!isNSData(log_id)) {
1036 secinfo("OTATrust", "failed to read log_id from trusted CT log array entry at index %lu, computing log_id", (unsigned long)idx);
1037 // We can make the log_id from the key
1038 NSData *key = log_data[@"key"];
1039 if (!isNSData(key)) {
1040 secerror("failed to read key from trusted CT log array entry at index %lu", (unsigned long)idx);
1043 log_id = CFBridgingRelease(SecSHA256DigestCreateFromData(NULL, (__bridge CFDataRef)key));
1045 [result setObject:log_data forKey:log_id];
1050 CFDictionaryRef SecOTAPKICreateTrustedCTLogsDictionaryFromArray(CFArrayRef trustedCTLogsArray)
1053 return CFBridgingRetain(ConvertTrustedCTLogsArrayToDictionary((__bridge NSArray*)trustedCTLogsArray));
1057 static CF_RETURNS_RETAINED CFDictionaryRef InitializeTrustedCTLogs() {
1059 NSArray *trustedCTLogs = nil;
1060 NSError *error = nil;
1061 #if !TARGET_OS_BRIDGE
1062 if (ShouldInitializeWithAsset()) {
1063 trustedCTLogs = [NSArray arrayWithContentsOfURL:GetAssetFileURL(kOTATrustTrustedCTLogsFilename) error:&error];
1064 if (!isNSArray(trustedCTLogs)) {
1065 secerror("OTATrust: failed to read CT list from asset data: %@", error);
1066 LogRemotely(OTATrustLogLevelError, &error);
1067 if (!DeleteAssetFromDisk()) {
1068 initialization_error_from_asset_data = true;
1073 if (!isNSArray(trustedCTLogs)) {
1074 trustedCTLogs = [NSArray arrayWithContentsOfURL:SecSystemTrustStoreCopyResourceNSURL(kOTATrustTrustedCTLogsFilename)];
1076 if (isNSArray(trustedCTLogs)) {
1077 return CFBridgingRetain(ConvertTrustedCTLogsArrayToDictionary(trustedCTLogs));
1083 static CF_RETURNS_RETAINED CFDictionaryRef InitializeEVPolicyToAnchorDigestsTable() {
1084 CFDictionaryRef result = NULL;
1085 CFPropertyListRef evroots = CFPropertyListCopyFromSystem(CFSTR("EVRoots"));
1088 if (CFGetTypeID(evroots) == CFDictionaryGetTypeID()) {
1089 /* @@@ Ensure that each dictionary key is a dotted list of digits,
1090 each value is an NSArrayRef and each element in the array is a
1092 result = (CFDictionaryRef)evroots;
1095 secwarning("EVRoot.plist is wrong type.");
1103 static CFIndex InitializeValidSnapshotVersion(CFIndex *outFormat) {
1104 CFIndex validVersion = 0;
1105 CFIndex validFormat = 0;
1106 CFDataRef validVersionData = SecSystemTrustStoreCopyResourceContents(CFSTR("ValidUpdate"), CFSTR("plist"), NULL);
1107 if (NULL != validVersionData)
1109 CFPropertyListFormat propFormat;
1110 CFDictionaryRef versionPlist = CFPropertyListCreateWithData(kCFAllocatorDefault, validVersionData, 0, &propFormat, NULL);
1111 if (NULL != versionPlist && CFDictionaryGetTypeID() == CFGetTypeID(versionPlist))
1113 CFNumberRef versionNumber = (CFNumberRef)CFDictionaryGetValue(versionPlist, (const void *)CFSTR("Version"));
1114 if (NULL != versionNumber)
1116 CFNumberGetValue(versionNumber, kCFNumberCFIndexType, &validVersion);
1118 CFNumberRef formatNumber = (CFNumberRef)CFDictionaryGetValue(versionPlist, (const void *)CFSTR("Format"));
1119 if (NULL != formatNumber)
1121 CFNumberGetValue(formatNumber, kCFNumberCFIndexType, &validFormat);
1124 CFReleaseSafe(versionPlist);
1125 CFReleaseSafe(validVersionData);
1128 *outFormat = validFormat;
1130 return validVersion;
1133 static Boolean PathExists(const char* path, size_t* pFileSize) {
1134 const char *checked_path = (path) ? path : "";
1135 Boolean result = false;
1138 if (NULL != pFileSize) {
1142 int stat_result = stat(checked_path, &sb);
1143 result = (stat_result == 0);
1145 if (result && !S_ISDIR(sb.st_mode)) {
1147 if (NULL != pFileSize) {
1148 *pFileSize = (size_t)sb.st_size;
1155 static const char* InitializeValidSnapshotData(CFStringRef filename_str) {
1156 char *result = NULL;
1157 const char *base_error_str = "could not get valid snapshot";
1159 CFURLRef valid_url = SecSystemTrustStoreCopyResourceURL(filename_str, CFSTR("sqlite3"), NULL);
1160 if (NULL == valid_url) {
1161 secerror("%s", base_error_str);
1163 CFStringRef valid_str = CFURLCopyFileSystemPath(valid_url, kCFURLPOSIXPathStyle);
1164 char file_path_buffer[PATH_MAX];
1165 memset(file_path_buffer, 0, PATH_MAX);
1166 if (NULL == valid_str) {
1167 secerror("%s path", base_error_str);
1169 const char *valid_cstr = CFStringGetCStringPtr(valid_str, kCFStringEncodingUTF8);
1170 if (NULL == valid_cstr) {
1171 if (CFStringGetCString(valid_str, file_path_buffer, PATH_MAX, kCFStringEncodingUTF8)) {
1172 valid_cstr = file_path_buffer;
1175 if (NULL == valid_cstr) {
1176 secerror("%s path as UTF8 string", base_error_str);
1178 asprintf(&result, "%s", valid_cstr);
1181 CFReleaseSafe(valid_str);
1183 CFReleaseSafe(valid_url);
1184 if (result && !PathExists(result, NULL)) {
1188 return (const char*)result;
1191 static const char* InitializeValidDatabaseSnapshot() {
1192 return InitializeValidSnapshotData(CFSTR("valid"));
1195 static const uint8_t* MapFile(const char* path, size_t* out_file_size) {
1197 const uint8_t *buf = NULL;
1201 if (NULL == path || NULL == out_file_size) {
1207 fd = open(path, O_RDONLY);
1208 if (fd < 0) { return NULL; }
1209 rtn = fstat(fd, &sb);
1210 if (rtn || (sb.st_size > (off_t) ((UINT32_MAX >> 1)-1))) {
1214 size = (size_t)sb.st_size;
1216 buf = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
1217 if (!buf || buf == MAP_FAILED) {
1218 secerror("unable to map %s (errno %d)", path, errno);
1224 *out_file_size = size;
1228 static void UnMapFile(void* mapped_data, size_t data_size) {
1232 int rtn = munmap(mapped_data, data_size);
1234 secerror("unable to unmap %ld bytes at %p (error %d)", data_size, mapped_data, rtn);
1238 struct index_record {
1239 unsigned char hash[CC_SHA1_DIGEST_LENGTH];
1242 typedef struct index_record index_record;
1244 static bool InitializeAnchorTable(CFDictionaryRef* pLookupTable, const char** ppAnchorTable) {
1246 bool result = false;
1248 if (NULL == pLookupTable || NULL == ppAnchorTable) {
1252 *pLookupTable = NULL;
1253 *ppAnchorTable = NULL;;
1255 CFDataRef cert_index_file_data = NULL;
1256 char file_path_buffer[PATH_MAX];
1257 CFURLRef table_data_url = NULL;
1258 CFStringRef table_data_cstr_path = NULL;
1259 const char* table_data_path = NULL;
1260 const index_record* pIndex = NULL;
1261 size_t index_offset = 0;
1262 size_t index_data_size = 0;
1263 CFMutableDictionaryRef anchorLookupTable = NULL;
1264 uint32_t offset_int_value = 0;
1265 CFNumberRef index_offset_value = NULL;
1266 CFDataRef index_hash = NULL;
1267 CFMutableArrayRef offsets = NULL;
1268 Boolean release_offset = false;
1270 char* local_anchorTable = NULL;
1271 size_t local_anchorTableSize = 0;
1273 // local_anchorTable is still NULL so the asset in the system trust store bundle needs to be used.
1274 CFReleaseSafe(cert_index_file_data);
1275 cert_index_file_data = SecSystemTrustStoreCopyResourceContents(CFSTR("certsIndex"), CFSTR("data"), NULL);
1276 if (!cert_index_file_data) {
1277 secerror("could not find certsIndex");
1279 table_data_url = SecSystemTrustStoreCopyResourceURL(CFSTR("certsTable"), CFSTR("data"), NULL);
1280 if (!table_data_url) {
1281 secerror("could not find certsTable");
1284 if (NULL != table_data_url) {
1285 table_data_cstr_path = CFURLCopyFileSystemPath(table_data_url, kCFURLPOSIXPathStyle);
1286 if (NULL != table_data_cstr_path) {
1287 memset(file_path_buffer, 0, PATH_MAX);
1288 table_data_path = CFStringGetCStringPtr(table_data_cstr_path, kCFStringEncodingUTF8);
1289 if (NULL == table_data_path) {
1290 if (CFStringGetCString(table_data_cstr_path, file_path_buffer, PATH_MAX, kCFStringEncodingUTF8)) {
1291 table_data_path = file_path_buffer;
1294 local_anchorTable = (char *)MapFile(table_data_path, &local_anchorTableSize);
1295 CFReleaseSafe(table_data_cstr_path);
1298 CFReleaseSafe(table_data_url);
1300 if (NULL == local_anchorTable || NULL == cert_index_file_data) {
1301 // we are in trouble
1302 if (NULL != local_anchorTable) {
1303 UnMapFile(local_anchorTable, local_anchorTableSize);
1304 local_anchorTable = NULL;
1305 local_anchorTableSize = 0;
1307 CFReleaseSafe(cert_index_file_data);
1311 // ------------------------------------------------------------------------
1312 // Now that the locations of the files are known and the table file has
1313 // been mapped into memory, create a dictionary that maps the SHA1 hash of
1314 // normalized issuer to the offset in the mapped anchor table file which
1315 // contains a index_record to the correct certificate
1316 // ------------------------------------------------------------------------
1317 pIndex = (const index_record*)CFDataGetBytePtr(cert_index_file_data);
1318 index_data_size = CFDataGetLength(cert_index_file_data);
1320 anchorLookupTable = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
1321 &kCFTypeDictionaryKeyCallBacks,
1322 &kCFTypeDictionaryValueCallBacks);
1324 for (index_offset = index_data_size; index_offset > 0; index_offset -= sizeof(index_record), pIndex++) {
1325 offset_int_value = pIndex->offset;
1327 index_offset_value = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &offset_int_value);
1328 index_hash = CFDataCreate(kCFAllocatorDefault, pIndex->hash, CC_SHA1_DIGEST_LENGTH);
1330 // see if the dictionary already has this key
1331 release_offset = false;
1332 offsets = (CFMutableArrayRef)CFDictionaryGetValue(anchorLookupTable, index_hash);
1333 if (NULL == offsets) {
1334 offsets = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
1335 release_offset = true;
1339 CFArrayAppendValue(offsets, index_offset_value);
1341 // set the key value pair in the dictionary
1342 CFDictionarySetValue(anchorLookupTable, index_hash, offsets);
1344 CFRelease(index_offset_value);
1345 CFRelease(index_hash);
1346 if (release_offset) {
1351 CFRelease(cert_index_file_data);
1353 if (NULL != anchorLookupTable && NULL != local_anchorTable) {
1354 *pLookupTable = anchorLookupTable;
1355 *ppAnchorTable = local_anchorTable;
1358 CFReleaseSafe(anchorLookupTable);
1359 if (NULL != local_anchorTable) {
1360 UnMapFile(local_anchorTable, local_anchorTableSize);
1361 local_anchorTable = NULL;
1362 local_anchorTableSize = 0;
1369 static void InitializeEscrowCertificates(CFArrayRef *escrowRoots, CFArrayRef *escrowPCSRoots) {
1370 CFDataRef file_data = SecSystemTrustStoreCopyResourceContents(CFSTR("AppleESCertificates"), CFSTR("plist"), NULL);
1372 if (NULL == file_data) {
1376 CFPropertyListFormat propFormat;
1377 CFDictionaryRef certsDictionary = CFPropertyListCreateWithData(kCFAllocatorDefault, file_data, 0, &propFormat, NULL);
1378 if (NULL != certsDictionary && CFDictionaryGetTypeID() == CFGetTypeID((CFTypeRef)certsDictionary)) {
1379 CFArrayRef certs = (CFArrayRef)CFDictionaryGetValue(certsDictionary, CFSTR("ProductionEscrowKey"));
1380 if (NULL != certs && CFArrayGetTypeID() == CFGetTypeID((CFTypeRef)certs) && CFArrayGetCount(certs) > 0) {
1381 *escrowRoots = CFArrayCreateCopy(kCFAllocatorDefault, certs);
1383 CFArrayRef pcs_certs = (CFArrayRef)CFDictionaryGetValue(certsDictionary, CFSTR("ProductionPCSEscrowKey"));
1384 if (NULL != pcs_certs && CFArrayGetTypeID() == CFGetTypeID((CFTypeRef)pcs_certs) && CFArrayGetCount(pcs_certs) > 0) {
1385 *escrowPCSRoots = CFArrayCreateCopy(kCFAllocatorDefault, pcs_certs);
1388 CFReleaseSafe(certsDictionary);
1389 CFRelease(file_data);
1392 static CF_RETURNS_RETAINED CFDictionaryRef InitializeEventSamplingRates() {
1393 NSDictionary *analyticsSamplingRates = nil;
1394 NSDictionary *eventSamplingRates = nil;
1395 NSError *error = nil;
1396 #if !TARGET_OS_BRIDGE
1397 if (ShouldInitializeWithAsset()) {
1398 analyticsSamplingRates = [NSDictionary dictionaryWithContentsOfURL:GetAssetFileURL(kOTATrustAnalyticsSamplingRatesFilename) error:&error];
1399 if (!isNSDictionary(analyticsSamplingRates)) {
1400 secerror("OTATrust: failed to read sampling rates from asset data: %@", error);
1401 LogRemotely(OTATrustLogLevelError, &error);
1402 if (!DeleteAssetFromDisk()) {
1403 initialization_error_from_asset_data = true;
1406 eventSamplingRates = analyticsSamplingRates[@"Events"];
1409 if (!isNSDictionary(eventSamplingRates)) {
1410 analyticsSamplingRates = [NSDictionary dictionaryWithContentsOfURL:SecSystemTrustStoreCopyResourceNSURL(kOTATrustAnalyticsSamplingRatesFilename)];
1412 if (isNSDictionary(analyticsSamplingRates)) {
1413 eventSamplingRates = analyticsSamplingRates[@"Events"];
1414 if (isNSDictionary(eventSamplingRates)) {
1415 return CFBridgingRetain(eventSamplingRates);
1421 static CF_RETURNS_RETAINED CFArrayRef InitializeAppleCertificateAuthorities() {
1422 NSArray *appleCAs = nil;
1423 NSError *error = nil;
1424 #if !TARGET_OS_BRIDGE
1425 if (ShouldInitializeWithAsset()) {
1426 appleCAs = [NSArray arrayWithContentsOfURL:GetAssetFileURL(kOTATrustAppleCertifcateAuthoritiesFilename) error:&error];
1427 if (!isNSArray(appleCAs)) {
1428 secerror("OTATrust: failed to read Apple CAs list from asset data: %@", error);
1429 LogRemotely(OTATrustLogLevelError, &error);
1430 if (!DeleteAssetFromDisk()) {
1431 initialization_error_from_asset_data = true;
1436 if (!isNSArray(appleCAs)) {
1437 appleCAs = [NSArray arrayWithContentsOfURL:SecSystemTrustStoreCopyResourceNSURL(kOTATrustAppleCertifcateAuthoritiesFilename)];
1439 if (isNSArray(appleCAs)) {
1440 return CFBridgingRetain(appleCAs);
1448 /* We keep track of one OTAPKI reference */
1449 static SecOTAPKIRef kCurrentOTAPKIRef = NULL;
1450 /* This queue is for making changes to the OTAPKI reference */
1451 static dispatch_queue_t kOTAQueue = NULL;
1453 struct _OpaqueSecOTAPKI {
1454 CFRuntimeBase _base;
1455 CFSetRef _blackListSet;
1456 CFSetRef _grayListSet;
1457 CFDictionaryRef _allowList;
1458 CFDictionaryRef _trustedCTLogs;
1459 CFURLRef _pinningList;
1460 CFArrayRef _escrowCertificates;
1461 CFArrayRef _escrowPCSCertificates;
1462 CFDictionaryRef _evPolicyToAnchorMapping;
1463 CFDictionaryRef _anchorLookupTable;
1464 const char* _anchorTable;
1465 uint64_t _trustStoreVersion;
1466 const char* _validDatabaseSnapshot;
1467 CFIndex _validSnapshotVersion;
1468 CFIndex _validSnapshotFormat;
1469 uint64_t _assetVersion;
1470 CFDateRef _lastAssetCheckIn;
1471 CFDictionaryRef _eventSamplingRates;
1472 CFArrayRef _appleCAs;
1473 CFDictionaryRef _secExperimentConfig;
1474 uint64_t _secExperimentAssetVersion;
1478 CFGiblisFor(SecOTAPKI)
1480 static CF_RETURNS_RETAINED CFStringRef SecOTAPKICopyFormatDescription(CFTypeRef cf, CFDictionaryRef formatOptions) {
1481 SecOTAPKIRef otapkiRef = (SecOTAPKIRef)cf;
1482 return CFStringCreateWithFormat(kCFAllocatorDefault,NULL,CFSTR("<SecOTAPKIRef: version %llu/%llu>"),
1483 otapkiRef->_trustStoreVersion, otapkiRef->_assetVersion);
1486 static void SecOTAPKIDestroy(CFTypeRef cf) {
1487 SecOTAPKIRef otapkiref = (SecOTAPKIRef)cf;
1489 CFReleaseNull(otapkiref->_blackListSet);
1490 CFReleaseNull(otapkiref->_grayListSet);
1491 CFReleaseNull(otapkiref->_escrowCertificates);
1492 CFReleaseNull(otapkiref->_escrowPCSCertificates);
1494 CFReleaseNull(otapkiref->_evPolicyToAnchorMapping);
1495 CFReleaseNull(otapkiref->_anchorLookupTable);
1497 CFReleaseNull(otapkiref->_trustedCTLogs);
1498 CFReleaseNull(otapkiref->_pinningList);
1499 CFReleaseNull(otapkiref->_eventSamplingRates);
1500 CFReleaseNull(otapkiref->_appleCAs);
1501 CFReleaseNull(otapkiref->_lastAssetCheckIn);
1502 CFReleaseNull(otapkiref->_secExperimentConfig);
1504 if (otapkiref->_anchorTable) {
1505 free((void *)otapkiref->_anchorTable);
1506 otapkiref->_anchorTable = NULL;
1508 if (otapkiref->_validDatabaseSnapshot) {
1509 free((void *)otapkiref->_validDatabaseSnapshot);
1510 otapkiref->_validDatabaseSnapshot = NULL;
1514 static uint64_t GetSystemTrustStoreVersion(void) {
1515 return GetSystemVersion(CFSTR("VersionNumber"));
1518 static uint64_t GetAssetVersion(CFErrorRef *error) {
1520 /* Get system asset version */
1521 uint64_t version = GetSystemVersion((__bridge CFStringRef)kOTATrustContentVersionKey);
1523 #if !TARGET_OS_BRIDGE
1524 uint64_t asset_version = 0;
1525 NSError *nserror = nil;
1526 NSDictionary *OTAPKIContext = [NSDictionary dictionaryWithContentsOfURL:GetAssetFileURL(kOTATrustContextFilename) error:&nserror];
1527 if (isNSDictionary(OTAPKIContext)) {
1528 NSNumber *tmpNumber = OTAPKIContext[kOTATrustContentVersionKey];
1529 if (isNSNumber(tmpNumber)) {
1530 asset_version = [tmpNumber unsignedLongLongValue];
1532 MakeOTATrustError(OTATrustMobileAssetType, &nserror, OTATrustLogLevelNotice, NSOSStatusErrorDomain, errSecInvalidValue,
1533 @"OTAContext.plist missing version");
1536 MakeOTATrustError(OTATrustMobileAssetType, &nserror, OTATrustLogLevelNotice, NSOSStatusErrorDomain, errSecMissingValue,
1537 @"OTAContext.plist missing dictionary");
1540 if (asset_version > version) {
1541 return asset_version;
1543 /* Don't delete the last check-in time so that we know we're up to date with the MobileAsset. */
1545 /* only log this if we're tracking errors */
1546 secnotice("OTATrust", "asset (%llu) is not newer than the system version (%llu); deleting stale data", asset_version, version);
1547 *error = CFRetainSafe((__bridge CFErrorRef)nserror);
1549 DeleteOldAssetData();
1556 static CF_RETURNS_RETAINED CFDateRef InitializeLastAssetCheckIn(void) {
1557 #if !TARGET_OS_BRIDGE
1558 NSError *error = nil;
1559 NSDictionary *OTAPKIContext = [NSDictionary dictionaryWithContentsOfURL:GetAssetFileURL(kOTATrustContextFilename) error:&error];
1560 if (isNSDictionary(OTAPKIContext)) {
1561 NSDate *checkIn = OTAPKIContext[kOTATrustLastCheckInKey];
1562 if (isNSDate(checkIn)) {
1563 return CFRetainSafe((__bridge CFDateRef)checkIn);
1565 MakeOTATrustError(OTATrustMobileAssetType, &error, OTATrustLogLevelNotice, NSOSStatusErrorDomain, errSecInvalidValue,
1566 @"OTAContext.plist missing check-in");
1569 MakeOTATrustError(OTATrustMobileAssetType, &error, OTATrustLogLevelNotice, NSOSStatusErrorDomain, errSecMissingValue,
1570 @"OTAContext.plist missing dictionary");
1576 static SecOTAPKIRef SecOTACreate() {
1578 SecOTAPKIRef otapkiref = NULL;
1580 otapkiref = CFTypeAllocate(SecOTAPKI, struct _OpaqueSecOTAPKI , kCFAllocatorDefault);
1582 if (NULL == otapkiref) {
1586 // Make sure that if this routine has to bail that the clean up
1587 // will do the right thing
1588 memset(otapkiref, 0, sizeof(*otapkiref));
1590 // Start off by getting the trust store version
1591 otapkiref->_trustStoreVersion = GetSystemTrustStoreVersion();
1593 // Get the set of black listed keys
1594 CFSetRef blackKeysSet = InitializeBlackList();
1595 if (NULL == blackKeysSet) {
1596 CFReleaseNull(otapkiref);
1599 otapkiref->_blackListSet = blackKeysSet;
1601 // Get the set of gray listed keys
1602 CFSetRef grayKeysSet = InitializeGrayList();
1603 if (NULL == grayKeysSet) {
1604 CFReleaseNull(otapkiref);
1607 otapkiref->_grayListSet = grayKeysSet;
1609 // Get the allow list dictionary
1610 // (now loaded lazily in SecOTAPKICopyAllowList)
1612 // Get the trusted Certificate Transparency Logs
1613 otapkiref->_trustedCTLogs = InitializeTrustedCTLogs();
1615 // Get the pinning list
1616 otapkiref->_pinningList = InitializePinningList();
1618 // Get the Event Sampling Rates
1619 otapkiref->_eventSamplingRates = InitializeEventSamplingRates();
1621 // Get the list of CAs used by Apple
1622 otapkiref->_appleCAs = InitializeAppleCertificateAuthorities();
1624 // Get the asset version (after possible reset due to missing asset data)
1625 if (!initialization_error_from_asset_data) {
1626 CFErrorRef error = nil;
1627 otapkiref->_assetVersion = GetAssetVersion(&error);
1628 otapkiref->_lastAssetCheckIn = InitializeLastAssetCheckIn();
1629 CFReleaseNull(error);
1631 otapkiref->_assetVersion = GetSystemVersion((__bridge CFStringRef)kOTATrustContentVersionKey);
1633 otapkiref->_secExperimentAssetVersion = 0;
1634 // Get the valid update snapshot version and format
1635 CFIndex update_format = 0;
1636 otapkiref->_validSnapshotVersion = InitializeValidSnapshotVersion(&update_format);
1637 otapkiref->_validSnapshotFormat = update_format;
1639 // Get the valid database snapshot path (if it exists, NULL otherwise)
1640 otapkiref->_validDatabaseSnapshot = InitializeValidDatabaseSnapshot();
1642 CFArrayRef escrowCerts = NULL;
1643 CFArrayRef escrowPCSCerts = NULL;
1644 InitializeEscrowCertificates(&escrowCerts, &escrowPCSCerts);
1645 if (NULL == escrowCerts || NULL == escrowPCSCerts) {
1646 CFReleaseNull(escrowCerts);
1647 CFReleaseNull(escrowPCSCerts);
1648 CFReleaseNull(otapkiref);
1651 otapkiref->_escrowCertificates = escrowCerts;
1652 otapkiref->_escrowPCSCertificates = escrowPCSCerts;
1654 // Get the mapping of EV Policy OIDs to Anchor digest
1655 CFDictionaryRef evOidToAnchorDigestMap = InitializeEVPolicyToAnchorDigestsTable();
1656 if (NULL == evOidToAnchorDigestMap) {
1657 CFReleaseNull(otapkiref);
1660 otapkiref->_evPolicyToAnchorMapping = evOidToAnchorDigestMap;
1662 CFDictionaryRef anchorLookupTable = NULL;
1663 const char* anchorTablePtr = NULL;
1665 if (!InitializeAnchorTable(&anchorLookupTable, &anchorTablePtr)) {
1666 CFReleaseSafe(anchorLookupTable);
1667 if (anchorTablePtr) {
1668 free((void *)anchorTablePtr);
1670 CFReleaseNull(otapkiref);
1673 otapkiref->_anchorLookupTable = anchorLookupTable;
1674 otapkiref->_anchorTable = anchorTablePtr;
1676 #if !TARGET_OS_BRIDGE
1677 /* Initialize our update handling */
1678 InitializeOTATrustAsset(kOTABackgroundQueue);
1679 otapkiref->_ctKillSwitch = InitializeKillSwitch((__bridge NSString*)kOTAPKIKillSwitchCT);
1680 InitializeOTASecExperimentAsset(kOTABackgroundQueue);
1681 #else // TARGET_OS_BRIDGE
1682 otapkiref->_ctKillSwitch = true; // bridgeOS never enforces CT
1683 #endif // TARGET_OS_BRIDGE
1688 SecOTAPKIRef SecOTAPKICopyCurrentOTAPKIRef() {
1689 __block SecOTAPKIRef result = NULL;
1690 static dispatch_once_t kInitializeOTAPKI = 0;
1691 dispatch_once(&kInitializeOTAPKI, ^{
1693 kOTAQueue = dispatch_queue_create("com.apple.security.OTAPKIQueue", NULL);
1694 dispatch_queue_attr_t attr = dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_SERIAL,
1695 QOS_CLASS_BACKGROUND, 0);
1696 attr = dispatch_queue_attr_make_with_autorelease_frequency(attr, DISPATCH_AUTORELEASE_FREQUENCY_WORK_ITEM);
1697 kOTABackgroundQueue = dispatch_queue_create("com.apple.security.OTAPKIBackgroundQueue", attr);
1698 if (!kOTAQueue || !kOTABackgroundQueue) {
1699 secerror("Failed to create OTAPKI Queues. May crash later.");
1701 dispatch_sync(kOTAQueue, ^{
1702 kCurrentOTAPKIRef = SecOTACreate();
1707 dispatch_sync(kOTAQueue, ^{
1708 result = kCurrentOTAPKIRef;
1709 CFRetainSafe(result);
1714 #if !TARGET_OS_BRIDGE
1715 BOOL UpdateOTACheckInDate(void) {
1716 __block NSDate *checkIn = [NSDate date];
1717 dispatch_sync(kOTAQueue, ^{
1718 CFRetainAssign(kCurrentOTAPKIRef->_lastAssetCheckIn, (__bridge CFDateRef)checkIn);
1721 if (SecOTAPKIIsSystemTrustd()) {
1722 /* Let the other trustds know we successfully checked in */
1723 notify_post(kOTATrustCheckInNotification);
1725 /* Update the on-disk check-in date, so when we re-launch we remember */
1726 NSError *error = nil;
1728 if (!(result = UpdateOTAContextOnDisk(kOTATrustLastCheckInKey, checkIn, &error))) {
1729 secerror("OTATrust: failed to write last check-in time: %@", error);
1737 void UpdateKillSwitch(NSString *key, bool value) {
1738 dispatch_sync(kOTAQueue, ^{
1739 if ([key isEqualToString:(__bridge NSString*)kOTAPKIKillSwitchCT]) {
1740 kCurrentOTAPKIRef->_ctKillSwitch = value;
1745 static BOOL UpdateFromAsset(NSURL *localURL, NSNumber *asset_version, NSError **error) {
1746 if (!localURL || !asset_version) {
1747 MakeOTATrustError(OTATrustMobileAssetType, error, OTATrustLogLevelError, NSOSStatusErrorDomain, errSecInternal,
1748 @"missing url and version for downloaded asset");
1751 __block NSArray *newTrustedCTLogs = NULL;
1752 __block uint64_t version = [asset_version unsignedLongLongValue];
1753 __block NSDictionary *newAnalyticsSamplingRates = NULL;
1754 __block NSArray *newAppleCAs = NULL;
1756 NSURL *TrustedCTLogsFileLoc = [NSURL URLWithString:kOTATrustTrustedCTLogsFilename
1757 relativeToURL:localURL];
1758 newTrustedCTLogs = [NSArray arrayWithContentsOfURL:TrustedCTLogsFileLoc error:error];
1759 if (!newTrustedCTLogs) {
1760 secerror("OTATrust: unable to create TrustedCTLogs from asset file: %@", error ? *error: nil);
1761 LogRemotely(OTATrustLogLevelError, error);
1765 NSURL *AnalyticsSamplingRatesFileLoc = [NSURL URLWithString:kOTATrustAnalyticsSamplingRatesFilename
1766 relativeToURL:localURL];
1767 newAnalyticsSamplingRates = [NSDictionary dictionaryWithContentsOfURL:AnalyticsSamplingRatesFileLoc error:error];
1768 if (!newAnalyticsSamplingRates) {
1769 secerror("OTATrust: unable to create AnalyticsSamplingRates from asset file: %@", error ? *error: nil);
1770 LogRemotely(OTATrustLogLevelError, error);
1774 NSURL *AppleCAsFileLoc = [NSURL URLWithString:kOTATrustAppleCertifcateAuthoritiesFilename
1775 relativeToURL:localURL];
1776 newAppleCAs = [NSArray arrayWithContentsOfURL:AppleCAsFileLoc error:error];
1778 secerror("OTATrust: unable to create AppleCAs from asset file: %@", error ? *error: nil);
1779 LogRemotely(OTATrustLogLevelError, error);
1783 /* Update the Current OTAPKIRef with the new data */
1784 dispatch_sync(kOTAQueue, ^{
1785 secnotice("OTATrust", "updating asset version from %llu to %llu", kCurrentOTAPKIRef->_assetVersion, version);
1786 CFRetainAssign(kCurrentOTAPKIRef->_trustedCTLogs, (__bridge CFDictionaryRef)ConvertTrustedCTLogsArrayToDictionary(newTrustedCTLogs));
1787 CFRetainAssign(kCurrentOTAPKIRef->_eventSamplingRates, (__bridge CFDictionaryRef)newAnalyticsSamplingRates);
1788 CFRetainAssign(kCurrentOTAPKIRef->_appleCAs, (__bridge CFArrayRef)newAppleCAs);
1789 kCurrentOTAPKIRef->_assetVersion = version;
1792 /* Write the data to disk (so that we don't have to re-download the asset on re-launch) */
1793 DeleteAssetFromDisk();
1794 if (CopyFileToDisk(kOTATrustTrustedCTLogsFilename, TrustedCTLogsFileLoc, error) &&
1795 CopyFileToDisk(kOTATrustAnalyticsSamplingRatesFilename, AnalyticsSamplingRatesFileLoc, error) &&
1796 CopyFileToDisk(kOTATrustAppleCertifcateAuthoritiesFilename, AppleCAsFileLoc, error) &&
1797 UpdateOTAContext(asset_version, error)) { // Set version and check-in time last (after success)
1798 /* If we successfully updated the "asset" on disk, signal the other trustds to pick up the changes */
1799 notify_post(kOTATrustOnDiskAssetNotification);
1806 #endif // !TARGET_OS_BRIDGE
1808 #if !TARGET_OS_BRIDGE
1809 static NSNumber *SecExperimentUpdateAsset(MAAsset *asset, NSNumber *asset_version, NSError **error) {
1810 NSURL *localURL = [asset getLocalFileUrl];
1811 if (!localURL || !asset_version) {
1812 MakeOTATrustError(OTASecExperimentMobileAssetType, error, OTATrustLogLevelError, NSOSStatusErrorDomain, errSecInternal,
1813 @"missing url and version for downloaded SecExperiment asset");
1816 NSDictionary *newSecExpConfig = NULL;
1817 uint64_t version = [asset_version unsignedLongLongValue];
1819 NSURL *secExpConfigFileLoc = [NSURL URLWithString:kOTASecExperimentConfigFilename
1820 relativeToURL:localURL];
1821 newSecExpConfig = [NSDictionary dictionaryWithContentsOfURL:secExpConfigFileLoc error:error];
1822 if (!newSecExpConfig) {
1823 secerror("OTATrust: unable to create SecExperiment from asset file: %@", error ? *error: nil);
1826 /* Update the Current OTAPKIRef with the new data */
1827 dispatch_sync(kOTAQueue, ^{
1828 secnotice("OTATrust", "updating SecExperiment asset version from %llu to %llu", kCurrentOTAPKIRef->_secExperimentAssetVersion, version);
1829 CFRetainAssign(kCurrentOTAPKIRef->_secExperimentConfig, (__bridge CFDictionaryRef)newSecExpConfig);
1830 kCurrentOTAPKIRef->_secExperimentAssetVersion = version;
1832 /* Signal the other trustds to pick up the changes */
1833 notify_post(kOTASecExperimentNewAssetNotification);
1834 return asset_version;
1836 #endif // !TARGET_OS_BRIDGE
1838 CFSetRef SecOTAPKICopyBlackListSet(SecOTAPKIRef otapkiRef) {
1839 if (NULL == otapkiRef) {
1843 return CFRetainSafe(otapkiRef->_blackListSet);
1847 CFSetRef SecOTAPKICopyGrayList(SecOTAPKIRef otapkiRef) {
1848 if (NULL == otapkiRef) {
1852 return CFRetainSafe(otapkiRef->_grayListSet);
1855 CFDictionaryRef SecOTAPKICopyAllowList(SecOTAPKIRef otapkiRef) {
1856 if (NULL == otapkiRef) {
1860 CFDictionaryRef result = otapkiRef->_allowList;
1862 result = InitializeAllowList();
1863 otapkiRef->_allowList = result;
1866 return CFRetainSafe(result);
1869 CFArrayRef SecOTAPKICopyAllowListForAuthKeyID(SecOTAPKIRef otapkiRef, CFStringRef authKeyID) {
1870 // %%% temporary performance optimization:
1871 // only load dictionary if we know an allow list exists for this key
1872 const CFStringRef keyIDs[3] = {
1873 CFSTR("7C724B39C7C0DB62A54F9BAA183492A2CA838259"),
1874 CFSTR("65F231AD2AF7F7DD52960AC702C10EEFA6D53B11"),
1875 CFSTR("D2A716207CAFD9959EEB430A19F2E0B9740EA8C7")
1877 CFArrayRef result = NULL;
1878 bool hasAllowList = false;
1879 CFIndex count = (sizeof(keyIDs) / sizeof(keyIDs[0]));
1880 for (CFIndex ix=0; ix<count && authKeyID; ix++) {
1881 if (kCFCompareEqualTo == CFStringCompare(authKeyID, keyIDs[ix], 0)) {
1882 hasAllowList = true;
1886 if (!hasAllowList || !otapkiRef) {
1890 CFDictionaryRef allowListDict = SecOTAPKICopyAllowList(otapkiRef);
1891 if (!allowListDict) {
1895 // return a retained copy of the allow list array (or NULL)
1896 result = CFDictionaryGetValue(allowListDict, authKeyID);
1897 CFRetainSafe(result);
1898 CFReleaseSafe(allowListDict);
1902 CFDictionaryRef SecOTAPKICopyTrustedCTLogs(SecOTAPKIRef otapkiRef) {
1903 CFDictionaryRef result = NULL;
1904 if (NULL == otapkiRef) {
1908 #if !TARGET_OS_BRIDGE
1909 /* Trigger periodic background MA checks in system trustd
1910 * We also check on trustd launch and listen for notifications. */
1911 TriggerPeriodicOTATrustAssetChecks(kOTABackgroundQueue);
1914 result = otapkiRef->_trustedCTLogs;
1915 CFRetainSafe(result);
1919 CFURLRef SecOTAPKICopyPinningList(SecOTAPKIRef otapkiRef) {
1920 if (NULL == otapkiRef) {
1924 return CFRetainSafe(otapkiRef->_pinningList);
1928 /* Returns an array of certificate data (CFDataRef) */
1929 CFArrayRef SecOTAPKICopyEscrowCertificates(uint32_t escrowRootType, SecOTAPKIRef otapkiRef) {
1930 CFMutableArrayRef result = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
1931 if (NULL == otapkiRef) {
1935 switch (escrowRootType) {
1936 // Note: we shouldn't be getting called to return baseline roots,
1937 // since this function vends production roots by definition.
1938 case kSecCertificateBaselineEscrowRoot:
1939 case kSecCertificateProductionEscrowRoot:
1940 case kSecCertificateBaselineEscrowBackupRoot:
1941 case kSecCertificateProductionEscrowBackupRoot:
1942 if (otapkiRef->_escrowCertificates) {
1943 CFArrayRef escrowCerts = otapkiRef->_escrowCertificates;
1944 CFArrayAppendArray(result, escrowCerts, CFRangeMake(0, CFArrayGetCount(escrowCerts)));
1947 case kSecCertificateBaselineEscrowEnrollmentRoot:
1948 case kSecCertificateProductionEscrowEnrollmentRoot:
1949 if (otapkiRef->_escrowCertificates) {
1950 // for enrollment purposes, exclude the v100 root
1951 static const unsigned char V100EscrowRoot[] = {
1952 0x65,0x5C,0xB0,0x3C,0x39,0x3A,0x32,0xA6,0x0B,0x96,
1953 0x40,0xC0,0xCA,0x73,0x41,0xFD,0xC3,0x9E,0x96,0xB3
1955 CFArrayRef escrowCerts = otapkiRef->_escrowCertificates;
1956 CFIndex idx, count = CFArrayGetCount(escrowCerts);
1957 for (idx=0; idx < count; idx++) {
1958 CFDataRef tmpData = (CFDataRef) CFArrayGetValueAtIndex(escrowCerts, idx);
1959 SecCertificateRef tmpCert = (tmpData) ? SecCertificateCreateWithData(NULL, tmpData) : NULL;
1960 CFDataRef sha1Hash = (tmpCert) ? SecCertificateGetSHA1Digest(tmpCert) : NULL;
1961 const uint8_t *dp = (sha1Hash) ? CFDataGetBytePtr(sha1Hash) : NULL;
1962 if (!(dp && !memcmp(V100EscrowRoot, dp, sizeof(V100EscrowRoot))) && tmpData) {
1963 CFArrayAppendValue(result, tmpData);
1965 CFReleaseSafe(tmpCert);
1969 case kSecCertificateBaselinePCSEscrowRoot:
1970 case kSecCertificateProductionPCSEscrowRoot:
1971 if (otapkiRef->_escrowPCSCertificates) {
1972 CFArrayRef escrowPCSCerts = otapkiRef->_escrowPCSCertificates;
1973 CFArrayAppendArray(result, escrowPCSCerts, CFRangeMake(0, CFArrayGetCount(escrowPCSCerts)));
1984 CFDictionaryRef SecOTAPKICopyEVPolicyToAnchorMapping(SecOTAPKIRef otapkiRef) {
1985 if (NULL == otapkiRef) {
1989 return CFRetainSafe(otapkiRef->_evPolicyToAnchorMapping);
1993 CFDictionaryRef SecOTAPKICopyAnchorLookupTable(SecOTAPKIRef otapkiRef) {
1994 if (NULL == otapkiRef) {
1998 return CFRetainSafe(otapkiRef->_anchorLookupTable);
2001 const char* SecOTAPKIGetAnchorTable(SecOTAPKIRef otapkiRef) {
2002 if (NULL == otapkiRef) {
2006 return otapkiRef->_anchorTable;
2009 const char* SecOTAPKIGetValidDatabaseSnapshot(SecOTAPKIRef otapkiRef) {
2010 if (NULL == otapkiRef) {
2014 return otapkiRef->_validDatabaseSnapshot;
2017 CFIndex SecOTAPKIGetValidSnapshotVersion(SecOTAPKIRef otapkiRef) {
2018 if (NULL == otapkiRef) {
2022 return otapkiRef->_validSnapshotVersion;
2025 CFIndex SecOTAPKIGetValidSnapshotFormat(SecOTAPKIRef otapkiRef) {
2026 if (NULL == otapkiRef) {
2030 return otapkiRef->_validSnapshotFormat;
2033 uint64_t SecOTAPKIGetTrustStoreVersion(SecOTAPKIRef otapkiRef) {
2034 if (NULL == otapkiRef) {
2038 return otapkiRef->_trustStoreVersion;
2041 uint64_t SecOTAPKIGetAssetVersion(SecOTAPKIRef otapkiRef) {
2042 if (NULL == otapkiRef) {
2046 return otapkiRef->_assetVersion;
2049 CFDateRef SecOTAPKICopyLastAssetCheckInDate(SecOTAPKIRef otapkiRef) {
2050 if (NULL == otapkiRef) {
2053 return CFRetainSafe(otapkiRef->_lastAssetCheckIn);
2056 bool SecOTAPKIAssetStalenessLessThanSeconds(SecOTAPKIRef otapkiRef, CFTimeInterval seconds) {
2057 if (NULL == otapkiRef) {
2061 bool result = false;
2062 CFDateRef lastCheckIn = CFRetainSafe(otapkiRef->_lastAssetCheckIn);
2063 if (isDate(lastCheckIn) && (fabs([(__bridge NSDate *)lastCheckIn timeIntervalSinceNow]) < seconds)) {
2066 CFReleaseNull(lastCheckIn);
2070 NSNumber *SecOTAPKIGetSamplingRateForEvent(SecOTAPKIRef otapkiRef, NSString *eventName) {
2071 if (NULL == otapkiRef) {
2075 #if !TARGET_OS_BRIDGE
2076 /* Trigger periodic background MA checks in system trustd
2077 * We also check on trustd launch and listen for notifications. */
2078 TriggerPeriodicOTATrustAssetChecks(kOTABackgroundQueue);
2081 if (otapkiRef->_eventSamplingRates) {
2082 CFTypeRef value = CFDictionaryGetValue(otapkiRef->_eventSamplingRates, (__bridge CFStringRef)eventName);
2083 if (isNumberOfType(value, kCFNumberSInt64Type)) {
2084 return (__bridge NSNumber *)value;
2090 CFArrayRef SecOTAPKICopyAppleCertificateAuthorities(SecOTAPKIRef otapkiRef) {
2091 if (NULL == otapkiRef) {
2095 #if !TARGET_OS_BRIDGE
2096 /* Trigger periodic background MA checks in system trustd
2097 * We also check on trustd launch and listen for notifications. */
2098 TriggerPeriodicOTATrustAssetChecks(kOTABackgroundQueue);
2101 return CFRetainSafe(otapkiRef->_appleCAs);
2104 bool SecOTAPKIKillSwitchEnabled(SecOTAPKIRef otapkiRef, CFStringRef key) {
2105 if (NULL == otapkiRef || NULL == key) {
2108 if (CFEqualSafe(key, kOTAPKIKillSwitchCT)) {
2109 return otapkiRef->_ctKillSwitch;
2114 /* Returns an array of certificate data (CFDataRef) */
2115 CFArrayRef SecOTAPKICopyCurrentEscrowCertificates(uint32_t escrowRootType, CFErrorRef* error) {
2116 SecOTAPKIRef otapkiref = SecOTAPKICopyCurrentOTAPKIRef();
2117 if (NULL == otapkiref) {
2118 SecError(errSecInternal, error, CFSTR("Unable to get the current OTAPKIRef"));
2122 CFArrayRef result = SecOTAPKICopyEscrowCertificates(escrowRootType, otapkiref);
2123 CFRelease(otapkiref);
2125 if (NULL == result) {
2126 SecError(errSecInternal, error, CFSTR("Could not get escrow certificates from the current OTAPKIRef"));
2131 static CF_RETURNS_RETAINED CFDictionaryRef convertDataKeysToBase64Strings(CFDictionaryRef CF_CONSUMED dictionaryWithDataKeys) {
2133 NSMutableDictionary *result = [NSMutableDictionary dictionaryWithCapacity:((__bridge NSDictionary*)dictionaryWithDataKeys).count];
2134 NSDictionary <NSData *, id> *input = CFBridgingRelease(dictionaryWithDataKeys);
2135 for (NSData *key in input) {
2136 id obj = [input objectForKey:key];
2137 NSString *base64Key = [key base64EncodedStringWithOptions:0];
2138 result[base64Key] = obj;
2140 return CFBridgingRetain(result);
2144 /* Returns an dictionary of dictionaries for currently trusted CT logs, indexed by the base64-encoded LogID */
2145 CFDictionaryRef SecOTAPKICopyCurrentTrustedCTLogs(CFErrorRef* error) {
2146 SecOTAPKIRef otapkiref = SecOTAPKICopyCurrentOTAPKIRef();
2147 if (NULL == otapkiref) {
2148 SecError(errSecInternal, error, CFSTR("Unable to get the current OTAPKIRef"));
2152 CFDictionaryRef result = convertDataKeysToBase64Strings(SecOTAPKICopyTrustedCTLogs(otapkiref));
2153 CFReleaseSafe(otapkiref);
2155 if (NULL == result) {
2156 SecError(errSecInternal, error, CFSTR("Could not get CT logs from the current OTAPKIRef"));
2161 /* Returns a dictionary for the CT log matching specified LogID */
2162 CFDictionaryRef SecOTAPKICopyCTLogForKeyID(CFDataRef keyID, CFErrorRef* error) {
2163 SecOTAPKIRef otapkiref = SecOTAPKICopyCurrentOTAPKIRef();
2164 if (NULL == otapkiref) {
2165 SecError(errSecInternal, error, CFSTR("Unable to get the current OTAPKIRef"));
2169 CFDictionaryRef trustedLogs = SecOTAPKICopyTrustedCTLogs(otapkiref);
2170 CFReleaseNull(otapkiref);
2174 CFDictionaryRef logDict = CFDictionaryGetValue(trustedLogs, keyID);
2175 CFRetainSafe(logDict);
2176 CFReleaseSafe(trustedLogs);
2180 uint64_t SecOTAPKIGetCurrentTrustStoreVersion(CFErrorRef* error){
2181 SecOTAPKIRef otapkiref = SecOTAPKICopyCurrentOTAPKIRef();
2182 if (NULL == otapkiref) {
2183 SecError(errSecInternal, error, CFSTR("Unable to get the current OTAPKIRef"));
2187 uint64_t result = otapkiref->_trustStoreVersion;
2188 CFReleaseNull(otapkiref);
2192 uint64_t SecOTAPKIGetCurrentAssetVersion(CFErrorRef* error) {
2193 SecOTAPKIRef otapkiref = SecOTAPKICopyCurrentOTAPKIRef();
2194 if (NULL == otapkiref) {
2195 SecError(errSecInternal, error, CFSTR("Unable to get the current OTAPKIRef"));
2199 uint64_t result = otapkiref->_assetVersion;
2200 CFReleaseNull(otapkiref);
2204 uint64_t SecOTASecExperimentGetCurrentAssetVersion(CFErrorRef* error) {
2205 SecOTAPKIRef otapkiref = SecOTAPKICopyCurrentOTAPKIRef();
2206 if (NULL == otapkiref) {
2207 SecError(errSecInternal, error, CFSTR("Unable to get the current OTAPKIRef"));
2211 uint64_t result = otapkiref->_secExperimentAssetVersion;
2212 CFReleaseNull(otapkiref);
2216 uint64_t SecOTAPKIResetCurrentAssetVersion(CFErrorRef* error) {
2217 uint64_t system_version = GetSystemVersion((__bridge CFStringRef)kOTATrustContentVersionKey);
2219 dispatch_sync(kOTAQueue, ^{
2220 kCurrentOTAPKIRef->_assetVersion = system_version;
2221 CFReleaseNull(kCurrentOTAPKIRef->_lastAssetCheckIn);
2222 kCurrentOTAPKIRef->_lastAssetCheckIn = NULL;
2225 #if !TARGET_OS_BRIDGE
2226 DeleteAssetFromDisk();
2228 return system_version;
2231 uint64_t SecOTAPKISignalNewAsset(CFErrorRef* error) {
2232 NSError *nserror = nil;
2233 uint64_t version = 0;
2234 #if !TARGET_OS_BRIDGE
2235 if (SecOTAPKIIsSystemTrustd()) {
2236 if (!DownloadOTATrustAsset(NO, YES, OTATrustMobileAssetType, &nserror) && error) {
2237 *error = CFRetainSafe((__bridge CFErrorRef)nserror);
2240 SecError(errSecServiceNotAvailable, error, CFSTR("This function may only be performed by the system trustd."));
2242 version = GetAssetVersion(nil);
2244 SecError(errSecUnsupportedService, error, CFSTR("This function is not available on this platform"));
2245 version = GetAssetVersion(error);
2250 uint64_t SecOTASecExperimentGetNewAsset(CFErrorRef* error) {
2251 NSError *nserror = nil;
2252 #if !TARGET_OS_BRIDGE
2253 if (SecOTAPKIIsSystemTrustd()) {
2254 if (!DownloadOTATrustAsset(NO, YES, OTASecExperimentMobileAssetType, &nserror) && error) {
2255 *error = CFRetainSafe((__bridge CFErrorRef)nserror);
2258 SecError(errSecServiceNotAvailable, error, CFSTR("This function may only be performed by the system trustd."));
2261 SecError(errSecUnsupportedService, error, CFSTR("This function is not available on this platform"));
2263 return SecOTASecExperimentGetCurrentAssetVersion(error);
2266 CFDictionaryRef SecOTASecExperimentCopyAsset(CFErrorRef* error) {
2267 SecOTAPKIRef otapkiRef = SecOTAPKICopyCurrentOTAPKIRef();
2268 if (NULL == otapkiRef) {
2269 secnotice("OTATrust", "Null otapkiref");
2272 CFDictionaryRef asset = NULL;
2273 if (otapkiRef->_secExperimentConfig) {
2274 asset = CFRetainSafe(otapkiRef->_secExperimentConfig);
2275 secnotice("OTATrust", "asset found");
2277 secnotice("OTATrust", "asset NULL");
2279 CFReleaseNull(otapkiRef);