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 *kOTATrustTrustedCTLogsNonTLSFilename = @"TrustedCTLogs_nonTLS.plist";
210 NSString *kOTATrustAnalyticsSamplingRatesFilename = @"AnalyticsSamplingRates.plist";
211 NSString *kOTATrustAppleCertifcateAuthoritiesFilename = @"AppleCertificateAuthorities.plist";
212 NSString *kOTASecExperimentConfigFilename = @"SecExperimentAssets.plist";
214 /* A device will honor a kill switch until it gets a new asset xml that sets the kill switch value to 0/false
215 * OR the asset is (completely) reset to shipping version. Such resets can happen if asset files cannot be
216 * read properly or if the OS is updated and contains a newer asset version or pinning DB version. */
217 const CFStringRef kOTAPKIKillSwitchCT = CFSTR("CTKillSwitch");
218 const CFStringRef kOTAPKIKillSwitchNonTLSCT = CFSTR("CTKillSwitch_nonTLS");
220 #if !TARGET_OS_BRIDGE
221 NSString *OTATrustMobileAssetType = @"com.apple.MobileAsset.PKITrustSupplementals";
222 NSString *OTASecExperimentMobileAssetType = @"com.apple.MobileAsset.SecExperimentAssets";
223 #define kOTATrustMobileAssetNotification "com.apple.MobileAsset.PKITrustSupplementals.ma.cached-metadata-updated"
224 #define kOTATrustOnDiskAssetNotification "com.apple.trustd.asset-updated"
225 #define kOTATrustCheckInNotification "com.apple.trustd.asset-check-in"
226 #define kOTATrustKillSwitchNotification "com.apple.trustd.kill-switch"
227 #define kOTASecExperimentNewAssetNotification "com.apple.trustd.secexperiment.asset-updated"
228 const NSUInteger OTATrustMobileAssetCompatibilityVersion = 1;
229 const NSUInteger OTASecExperimentMobileAssetCompatibilityVersion = 1;
230 #define kOTATrustDefaultUpdatePeriod 60*60*12 // 12 hours
231 #define kOTATrustMinimumUpdatePeriod 60*5 // 5 min
232 #define kOTATrustDefaultWaitPeriod 60 // 1 min
235 const CFStringRef kSecSUPrefDomain = CFSTR("com.apple.SoftwareUpdate");
236 const CFStringRef kSecSUScanPrefConfigDataInstallKey = CFSTR("ConfigDataInstall");
239 // MARK: Helper functions
241 OTATrustLogLevelNone,
242 OTATrustLogLevelDebug,
243 OTATrustLogLevelInfo,
244 OTATrustLogLevelNotice,
245 OTATrustLogLevelError,
248 static void MakeOTATrustError(NSString *assetType, NSError **error, OTATrustLogLevel level, NSErrorDomain errDomain, OSStatus errCode, NSString *format,...) NS_FORMAT_FUNCTION(6,7);
249 static void MakeOTATrustErrorWithAttributes(NSString *assetType, NSError **error, OTATrustLogLevel level, NSErrorDomain errDomain, OSStatus errCode,
250 NSDictionary *attributes, NSString *format,...)
251 NS_FORMAT_FUNCTION(7,8);
253 static void MakeOTATrustErrorArgs(NSString *assetType, NSError **error, OTATrustLogLevel level, NSErrorDomain errDomain, OSStatus errCode,
254 NSDictionary *attributes, NSString *format, va_list arguments)
255 NS_FORMAT_FUNCTION(7,0);
257 static void LogLocally(OTATrustLogLevel level, NSString *errorString) {
259 case OTATrustLogLevelNone:
261 case OTATrustLogLevelDebug:
262 secdebug("OTATrust", "%@", errorString);
264 case OTATrustLogLevelInfo:
265 secinfo("OTATrust", "%@", errorString);
267 case OTATrustLogLevelNotice:
268 secnotice("OTATrust", "%@", errorString);
270 case OTATrustLogLevelError:
271 secerror("OTATrust: %@", errorString);
276 static void LogRemotelyWithAttributes(OTATrustLogLevel level, NSError **error, NSDictionary *attributes) {
277 #if ENABLE_TRUSTD_ANALYTICS
278 /* only report errors and notices */
279 if (error && level == OTATrustLogLevelError) {
280 [[TrustAnalytics logger] logResultForEvent:TrustdHealthAnalyticsEventOTAPKIEvent hardFailure:YES result:*error withAttributes:attributes];
281 } else if (error && level == OTATrustLogLevelNotice) {
282 [[TrustAnalytics logger] logResultForEvent:TrustdHealthAnalyticsEventOTAPKIEvent hardFailure:NO result:*error withAttributes:attributes];
284 #endif // ENABLE_TRUSTD_ANALYTICS
287 static void LogRemotely(OTATrustLogLevel level, NSError **error) {
288 LogRemotelyWithAttributes(level, error, nil);
291 static void MakeOTATrustErrorArgs(NSString *assetType, NSError **error, OTATrustLogLevel level, NSErrorDomain errDomain, OSStatus errCode,
292 NSDictionary *attributes, NSString *format, va_list args) {
293 NSString *formattedString = nil;
295 formattedString = [[NSString alloc] initWithFormat:format arguments:args];
298 NSError *localError = nil;
299 NSMutableDictionary *userInfo = [[NSMutableDictionary alloc] init];
301 [userInfo setObject:formattedString forKey:NSLocalizedDescriptionKey];
303 if (error && *error) {
304 userInfo[NSUnderlyingErrorKey] = *error;
306 localError = [NSError errorWithDomain:errDomain
310 LogLocally(level, formattedString);
311 if ([assetType isEqualToString:OTATrustMobileAssetType]) {
312 LogRemotelyWithAttributes(level, &localError, attributes);
314 if (error) { *error = localError; }
317 static void MakeOTATrustErrorWithAttributes(NSString *assetType, NSError **error, OTATrustLogLevel level, NSErrorDomain errDomain, OSStatus errCode,
318 NSDictionary *attributes, NSString *format,...) {
320 va_start(args, format);
321 MakeOTATrustErrorArgs(assetType, error, level, errDomain, errCode, attributes, format, args);
325 static void MakeOTATrustError(NSString *assetType, NSError **error, OTATrustLogLevel level, NSErrorDomain errDomain, OSStatus errCode, NSString *format,...) {
327 va_start(args, format);
328 MakeOTATrustErrorArgs(assetType, error, level, errDomain, errCode, nil, format, args);
332 static BOOL CanCheckMobileAsset(void) {
335 /* Check the user's SU preferences to determine if "Install system data files" is off */
336 if (!CFPreferencesSynchronize(kSecSUPrefDomain, kCFPreferencesAnyUser, kCFPreferencesCurrentHost)) {
337 secerror("OTATrust: unable to synchronize SoftwareUpdate prefs");
342 if (CFPreferencesAppValueIsForced(kSecSUScanPrefConfigDataInstallKey, kSecSUPrefDomain)) {
343 value = CFBridgingRelease(CFPreferencesCopyAppValue(kSecSUScanPrefConfigDataInstallKey, kSecSUPrefDomain));
345 value = CFBridgingRelease(CFPreferencesCopyValue(kSecSUScanPrefConfigDataInstallKey, kSecSUPrefDomain,
346 kCFPreferencesAnyUser, kCFPreferencesCurrentHost));
348 if (isNSNumber(value)) {
349 result = [value boolValue];
352 if (!result) { secnotice("OTATrust", "User has disabled system data installation."); }
354 /* MobileAsset.framework isn't mastered into the BaseSystem. Check that the MA classes are linked. */
355 if (![ASAssetQuery class] || ![ASAsset class] || ![MAAssetQuery class] || ![MAAsset class]) {
356 secnotice("OTATrust", "Weak-linked MobileAsset framework missing.");
363 static BOOL ShouldUpdateWithAsset(NSString *assetType, NSNumber *asset_version) {
364 if (![asset_version isKindOfClass:[NSNumber class]]) {
367 CFErrorRef error = nil;
368 uint64_t current_version;
369 if ([assetType isEqualToString:OTATrustMobileAssetType]) {
370 current_version = SecOTAPKIGetCurrentAssetVersion(&error);
371 } else if ([assetType isEqualToString:OTASecExperimentMobileAssetType]) {
372 current_version = SecOTASecExperimentGetCurrentAssetVersion(&error);
377 CFReleaseNull(error);
380 if ([asset_version compare:[NSNumber numberWithUnsignedLongLong:current_version]] == NSOrderedDescending) {
386 // MARK: File management functions
387 static bool verify_create_path(const char *path) {
388 int ret = mkpath_np(path, 0755);
389 if (!(ret == 0 || ret == EEXIST)) {
390 secerror("could not create path: %s (%s)", path, strerror(ret));
396 static NSURL *GetAssetFileURL(NSString *filename) {
397 /* Make sure the /Library/Keychains directory is there */
398 NSURL *keychainsDirectory = CFBridgingRelease(SecCopyURLForFileInSystemKeychainDirectory(nil));
399 NSURL *directory = [keychainsDirectory URLByAppendingPathComponent:@"SupplementalsAssets/" isDirectory:YES];
400 if (!verify_create_path([directory fileSystemRepresentation])) {
405 return [directory URLByAppendingPathComponent:filename];
411 static void DeleteFileWithName(NSString *filename) {
412 NSURL *fileURL = GetAssetFileURL(filename);
413 if (remove([fileURL fileSystemRepresentation]) == -1) {
415 if (error != ENOENT) {
416 secnotice("OTATrust", "failed to remove %@: %s", fileURL, strerror(error));
421 static BOOL UpdateOTAContextOnDisk(NSString *key, id value, NSError **error) {
422 if (SecOTAPKIIsSystemTrustd()) {
423 /* Get current context, if applicable, and update/add key/value */
424 NSURL *otaContextFile = GetAssetFileURL(kOTATrustContextFilename);
425 NSDictionary *currentContext = [NSDictionary dictionaryWithContentsOfURL:otaContextFile];
426 NSMutableDictionary *newContext = nil;
427 if (currentContext) {
428 newContext = [currentContext mutableCopy];
430 newContext = [NSMutableDictionary dictionary];
432 newContext[key] = value;
434 /* Write dictionary to disk */
435 if (![newContext writeToURL:otaContextFile error:error]) {
436 secerror("OTATrust: unable to write OTA Context to disk: %@", error ? *error : nil);
437 LogRemotely(OTATrustLogLevelError, error);
445 static BOOL UpdateOTAContext(NSNumber *asset_version, NSError **error) {
446 return UpdateOTAContextOnDisk(kOTATrustContentVersionKey, asset_version, error) && UpdateOTACheckInDate();
449 /* Delete only the asset data but not the check-in time. */
450 static void DeleteOldAssetData(void) {
451 if (SecOTAPKIIsSystemTrustd()) {
452 /* Delete the asset files, but keep the check-in time and version */
453 DeleteFileWithName(kOTATrustTrustedCTLogsFilename);
454 DeleteFileWithName(kOTATrustTrustedCTLogsNonTLSFilename);
455 DeleteFileWithName(kOTATrustAnalyticsSamplingRatesFilename);
456 DeleteFileWithName(kOTATrustAppleCertifcateAuthoritiesFilename);
460 /* Delete all asset data, intended for error cases */
461 static BOOL DeleteAssetFromDisk(void) {
462 if (SecOTAPKIIsSystemTrustd()) {
463 DeleteOldAssetData();
464 DeleteFileWithName(kOTATrustContextFilename);
470 #if TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR
471 static bool ChangeFileProtectionToClassD(NSURL *fileURL, NSError **error) {
473 int file_fd = open([fileURL fileSystemRepresentation], O_RDONLY);
475 int retval = fcntl(file_fd, F_SETPROTECTIONCLASS, PROTECTION_CLASS_D);
477 MakeOTATrustError(OTATrustMobileAssetType, error, OTATrustLogLevelError, NSPOSIXErrorDomain, errno,
478 @"set proteciton class error for asset %d: %s", errno, strerror(errno));
483 MakeOTATrustError(OTATrustMobileAssetType, error, OTATrustLogLevelError, NSPOSIXErrorDomain, errno,
484 @"open error for asset %d: %s", errno, strerror(errno));
491 static BOOL CopyFileToDisk(NSString *filename, NSURL *localURL, NSError **error) {
492 if (SecOTAPKIIsSystemTrustd()) {
493 NSURL *toFileURL = GetAssetFileURL(filename);
494 secdebug("OTATrust", "will copy asset file data from \"%@\"", localURL);
495 copyfile_state_t state = copyfile_state_alloc();
496 int retval = copyfile([localURL fileSystemRepresentation], [toFileURL fileSystemRepresentation],
497 state, COPYFILE_DATA);
498 copyfile_state_free(state);
500 MakeOTATrustError(OTATrustMobileAssetType, error, OTATrustLogLevelError, NSPOSIXErrorDomain, errno,
501 @"copyfile error for asset %d: %s", errno, strerror(errno));
504 /* make sure we can read this file before first unlock */
505 #if TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR
506 return ChangeFileProtectionToClassD(toFileURL, error);
515 static void DisableKillSwitches() {
516 UpdateOTAContextOnDisk((__bridge NSString*)kOTAPKIKillSwitchCT, @0, nil);
517 UpdateOTAContextOnDisk((__bridge NSString*)kOTAPKIKillSwitchNonTLSCT, @0, nil);
520 static void GetKillSwitchAttributes(NSDictionary *attributes) {
521 bool killSwitchEnabled = false;
524 NSNumber *ctKillSwitch = [attributes objectForKey:(__bridge NSString*)kOTAPKIKillSwitchCT];
525 if (isNSNumber(ctKillSwitch)) {
526 NSError *error = nil;
527 UpdateOTAContextOnDisk((__bridge NSString*)kOTAPKIKillSwitchCT, ctKillSwitch, &error);
528 UpdateKillSwitch((__bridge NSString*)kOTAPKIKillSwitchCT, [ctKillSwitch boolValue]);
529 secnotice("OTATrust", "got CT kill switch = %d", [ctKillSwitch boolValue]);
530 killSwitchEnabled = true;
533 // Non-TLS CT Kill Switch
534 ctKillSwitch = [attributes objectForKey:(__bridge NSString*)kOTAPKIKillSwitchNonTLSCT];
535 if (isNSNumber(ctKillSwitch)) {
536 NSError *error = nil;
537 UpdateOTAContextOnDisk((__bridge NSString*)kOTAPKIKillSwitchNonTLSCT, ctKillSwitch, &error);
538 UpdateKillSwitch((__bridge NSString*)kOTAPKIKillSwitchNonTLSCT, [ctKillSwitch boolValue]);
539 secnotice("OTATrust", "got non-TLS CT kill switch = %d", [ctKillSwitch boolValue]);
540 killSwitchEnabled = true;
543 /* Other kill switches TBD.
544 * When adding one, make sure to add to the Analytics Samplers since these kill switches
545 * are installed before the full asset is downloaded and installed. (A device can have the
546 * kill switches without having the asset version that contained them.) */
548 // notify the other trustds if any kill switch was read
549 if (SecOTAPKIIsSystemTrustd() && killSwitchEnabled) {
550 notify_post(kOTATrustKillSwitchNotification);
554 // MARK: Fetch and Update Functions
555 static NSNumber *PKIUpdateAndPurgeAsset(MAAsset *asset, NSNumber *asset_version, NSError **error) {
556 if (SecPinningDbUpdateFromURL([asset getLocalFileUrl], error) &&
557 UpdateFromAsset([asset getLocalFileUrl], asset_version, error)) {
558 secnotice("OTATrust", "finished update to version %@ from installed asset. purging asset.", asset_version);
559 #if ENABLE_TRUSTD_ANALYTICS
560 [[TrustAnalytics logger] logSuccessForEventNamed:TrustdHealthAnalyticsEventOTAPKIEvent];
561 #endif // ENABLE_TRUSTD_ANALYTICS
562 [asset purge:^(MAPurgeResult purge_result) {
563 if (purge_result != MAPurgeSucceeded) {
564 secerror("OTATrust: purge failed: %ld", (long)purge_result);
567 return asset_version;
569 MakeOTATrustError(OTATrustMobileAssetType, error, OTATrustLogLevelError, NSOSStatusErrorDomain, errSecCallbackFailed,
570 @"Failed to install new asset version %@ from %@", asset_version, [asset getLocalFileUrl]);
575 static NSNumber *UpdateAndPurgeAsset(NSString* assetType, MAAsset *asset, NSNumber *asset_version, NSError **error) {
576 if ([assetType isEqualToString:OTATrustMobileAssetType]) {
577 return PKIUpdateAndPurgeAsset(asset, asset_version, error);
578 } else if ([assetType isEqualToString:OTASecExperimentMobileAssetType]) {
579 return SecExperimentUpdateAsset(asset, asset_version, error);
585 static MADownloadOptions *GetMADownloadOptions(BOOL wait) {
586 /* default behavior */
587 MADownloadOptions *options = [[MADownloadOptions alloc] init];
588 options.discretionary = YES;
589 options.allowsCellularAccess = NO;
591 /* If an XPC interface is waiting on this, all expenses allowed */
593 options.discretionary = NO;
594 options.allowsCellularAccess = YES;
598 /* If last asset check-in was too long ago, use more expensive options */
599 SecOTAPKIRef otapkiref = SecOTAPKICopyCurrentOTAPKIRef();
600 if (!SecOTAPKIAssetStalenessLessThanSeconds(otapkiref, kSecOTAPKIAssetStalenessWarning)) {
601 secnotice("OTATrust", "Asset staleness state: warning");
602 options.allowsCellularAccess = YES;
603 options.discretionary = NO;
604 } else if (!SecOTAPKIAssetStalenessLessThanSeconds(otapkiref, kSecOTAPKIAssetStalenessAtRisk)) {
605 secnotice("OTATrust", "Asset staleness state: at risk");
606 options.discretionary = NO;
608 CFReleaseNull(otapkiref);
612 static BOOL assetVersionCheck(NSString *assetType, MAAsset *asset) {
613 NSUInteger compatVersion;
614 NSError *ma_error = nil;
615 if ([assetType isEqualToString:OTATrustMobileAssetType]) {
616 compatVersion = OTATrustMobileAssetCompatibilityVersion;
618 compatVersion = OTASecExperimentMobileAssetCompatibilityVersion;
620 /* Check Compatibility Version against this software version */
621 NSNumber *compatibilityVersion = [asset assetProperty:@"_CompatibilityVersion"];
622 if (!isNSNumber(compatibilityVersion) ||
623 [compatibilityVersion unsignedIntegerValue] != compatVersion) {
624 MakeOTATrustError(assetType, &ma_error, OTATrustLogLevelNotice, NSOSStatusErrorDomain, errSecIncompatibleVersion,
625 @"skipping asset %@ because Compatibility Version doesn't match %@", assetType, compatibilityVersion);
628 /* Check Content Version against the current content version */
629 NSNumber *asset_version = [asset assetProperty:@"_ContentVersion"];
630 if (!ShouldUpdateWithAsset(assetType, asset_version)) {
631 /* write the version and last (successful) check-in time */
632 if ([assetType isEqualToString:OTATrustMobileAssetType]) {
633 UpdateOTAContext(asset_version, &ma_error);
634 NSDictionary *eventAttributes = @{
635 @"assetVersion" : asset_version,
636 @"systemVersion" : @(GetSystemVersion((__bridge CFStringRef)kOTATrustContentVersionKey)),
637 @"installedVersion" : @(SecOTAPKIGetCurrentAssetVersion(nil)),
639 MakeOTATrustErrorWithAttributes(assetType, &ma_error, OTATrustLogLevelNotice, NSOSStatusErrorDomain, errSecDuplicateItem, eventAttributes,
640 @"skipping asset %@ because we already have _ContentVersion %@ (or newer)", assetType, asset_version);
642 MakeOTATrustError(assetType, &ma_error, OTATrustLogLevelNotice, NSOSStatusErrorDomain, errSecDuplicateItem,
643 @"skipping asset %@ because we already have _ContentVersion %@ (or newer)", assetType, asset_version);
650 static int downloadWaitTime(void) {
651 NSUserDefaults *defaults = [[NSUserDefaults alloc] initWithSuiteName:@"com.apple.security"];
652 NSNumber *updateTimeout = [defaults valueForKey:@"TrustdAssetDownloadWaitTimeout"];
653 int timeout = kOTATrustDefaultWaitPeriod;
654 if (isNSNumber(updateTimeout)) {
655 timeout = [updateTimeout intValue];
660 static BOOL DownloadOTATrustAsset(BOOL isLocalOnly, BOOL wait, NSString *assetType, NSError **error) {
661 if (!CanCheckMobileAsset()) {
662 MakeOTATrustError(assetType, error, OTATrustLogLevelNotice, NSOSStatusErrorDomain, errSecServiceNotAvailable,
663 @"MobileAsset disabled, skipping check.");
667 __block NSNumber *updated_version = nil;
668 __block dispatch_semaphore_t done = wait ? dispatch_semaphore_create(0) : nil;
669 __block NSError *ma_error = nil;
671 secnotice("OTATrust", "begin MobileAsset query for catalog %@", assetType);
672 [MAAsset startCatalogDownload:assetType options:GetMADownloadOptions(wait) then:^(MADownLoadResult result) {
674 os_transaction_t transaction = os_transaction_create("com.apple.trustd.asset.download");
675 if (result != MADownloadSuccessful) {
676 MakeOTATrustError(assetType, &ma_error, OTATrustLogLevelError, @"MADownLoadResult", (OSStatus)result,
677 @"failed to download catalog for asset %@: %ld", assetType, (long)result);
678 if (result == MADownloadDaemonNotReady && ([assetType isEqualToString:OTATrustMobileAssetType])) {
679 /* mobileassetd has to wait for first unlock to download. trustd usually launches before first unlock. */
680 TriggerUnlockNotificationOTATrustAssetCheck(assetType, kOTABackgroundQueue);
684 MAAssetQuery *query = [[MAAssetQuery alloc] initWithType:(NSString *)assetType];
685 [query augmentResultsWithState:true];
687 secnotice("OTATrust", "begin MobileAsset metadata sync request %{public}@", assetType);
688 MAQueryResult queryResult = [query queryMetaDataSync];
689 if (queryResult != MAQuerySuccessful) {
690 MakeOTATrustError(assetType, &ma_error, OTATrustLogLevelError, @"MAQueryResult", (OSStatus)queryResult,
691 @"failed to query MobileAsset %@ metadata: %ld", assetType, (long)queryResult);
695 if (!query.results) {
696 MakeOTATrustError(assetType, &ma_error, OTATrustLogLevelError, NSOSStatusErrorDomain, errSecInternal,
697 @"no results in MobileAsset query for %@", assetType);
701 bool began_async_job = false;
702 for (MAAsset *asset in query.results) {
703 /* Check Compatibility Version against this software version */
704 NSNumber *asset_version = [asset assetProperty:@"_ContentVersion"];
705 if (!assetVersionCheck(assetType, asset)) {
709 if ([assetType isEqualToString:OTATrustMobileAssetType]) {
710 GetKillSwitchAttributes(asset.attributes);
713 switch (asset.state) {
715 MakeOTATrustError(assetType, &ma_error, OTATrustLogLevelError, NSOSStatusErrorDomain, errSecInternal,
716 @"unknown asset state %ld", (long)asset.state);
719 /* The asset is already in the cache, get it from disk. */
720 secdebug("OTATrust", "OTATrust asset %{public}@ already installed", assetType);
721 updated_version = UpdateAndPurgeAsset(assetType, asset, asset_version, &ma_error);
724 MakeOTATrustError(assetType, &ma_error, OTATrustLogLevelError, @"MAAssetState", (OSStatus)asset.state,
725 @"asset %@ is unknown", assetType);
728 secnotice("OTATrust", "asset %{public}@ is downloading", assetType);
731 secnotice("OTATrust", "begin download of OTATrust asset");
732 began_async_job = true;
733 [asset startDownload:GetMADownloadOptions(wait) then:^(MADownLoadResult downloadResult) {
735 os_transaction_t inner_transaction = os_transaction_create("com.apple.trustd.asset.downloadAsset");
736 if (downloadResult != MADownloadSuccessful) {
737 MakeOTATrustError(assetType, &ma_error, OTATrustLogLevelError, @"MADownLoadResult", (OSStatus)downloadResult,
738 @"failed to download asset %@: %ld", assetType, (long)downloadResult);
741 updated_version = UpdateAndPurgeAsset(assetType, asset, asset_version, &ma_error);
743 dispatch_semaphore_signal(done);
745 (void)inner_transaction; // dead store
746 inner_transaction = nil;
750 } /* switch (asset.state) */
751 } /* for (MAAsset.. */
752 if (wait && !began_async_job) {
753 dispatch_semaphore_signal(done);
755 /* Done with the transaction */
756 (void)transaction; // dead store
758 } /* autoreleasepool */
759 }]; /* [MAAsset startCatalogDownload: ] */
761 /* If the caller is waiting for a response, wait up to one minute for the update to complete.
762 * If the MAAsset callback does not complete in that time, report a timeout.
763 * If the MAAsset callback completes and did not successfully update, it should report an error;
764 * forward that error to the caller.
765 * If the MAAsset callback completes and did not update and did not provide an error; report
766 * an unknown error. */
769 if (dispatch_semaphore_wait(done, dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC * downloadWaitTime())) != 0) {
770 MakeOTATrustError(assetType, error, OTATrustLogLevelError, NSOSStatusErrorDomain, errSecNetworkFailure,
771 @"Failed to get asset %@ metadata within %d seconds.", assetType, downloadWaitTime());
773 result = (updated_version != nil);
774 if (error && ma_error) {
776 } else if (!result) {
777 MakeOTATrustError(assetType, error, OTATrustLogLevelError, NSOSStatusErrorDomain, errSecInternalComponent,
778 @"Unknown error occurred.");
785 static void TriggerUnlockNotificationOTATrustAssetCheck(NSString* assetType, dispatch_queue_t queue) {
786 SecOTAPKIRef otapkiref = SecOTAPKICopyCurrentOTAPKIRef();
787 /* If the last check-in is recent enough, wait for our regularly scheduled check-in. */
788 if (SecOTAPKIAssetStalenessLessThanSeconds(otapkiref, kSecOTAPKIAssetStalenessAtRisk)) {
789 CFReleaseNull(otapkiref);
792 CFReleaseNull(otapkiref);
793 #if !TARGET_OS_SIMULATOR
794 /* register for unlock notifications */
796 notify_register_dispatch(kMobileKeyBagLockStatusNotificationID, &out_token, queue, ^(int token) {
797 secnotice("OTATrust", "Got lock status notification for at-risk last check-in after MA daemon error");
798 (void)DownloadOTATrustAsset(NO, NO, assetType, nil);
799 notify_cancel(token);
804 static bool InitializeKillSwitch(NSString *key) {
805 #if !TARGET_OS_BRIDGE
806 NSError *error = nil;
807 NSDictionary *OTAPKIContext = [NSDictionary dictionaryWithContentsOfURL:GetAssetFileURL(kOTATrustContextFilename) error:&error];
808 if (isNSDictionary(OTAPKIContext)) {
809 NSNumber *killSwitchValue = OTAPKIContext[key];
810 if (isNSNumber(killSwitchValue)) {
811 secinfo("OTATrust", "found on-disk kill switch %{public}@ with value %d", key, [killSwitchValue boolValue]);
812 return [killSwitchValue boolValue];
814 MakeOTATrustError(OTATrustMobileAssetType, &error, OTATrustLogLevelNotice, NSOSStatusErrorDomain, errSecInvalidValue,
815 @"OTAContext.plist missing check-in");
818 MakeOTATrustError(OTATrustMobileAssetType, &error, OTATrustLogLevelNotice, NSOSStatusErrorDomain, errSecMissingValue,
819 @"OTAContext.plist missing dictionary");
825 static void InitializeOTATrustAsset(dispatch_queue_t queue) {
826 /* Only the "system" trustd does updates */
827 if (SecOTAPKIIsSystemTrustd()) {
828 /* Asynchronously ask MobileAsset for most recent asset. */
829 dispatch_async(queue, ^{
830 secnotice("OTATrust", "Initial check with MobileAsset for newer PKITrustSupplementals asset");
831 (void)DownloadOTATrustAsset(NO, NO, OTATrustMobileAssetType, nil);
834 /* Register for changes in our asset */
835 if (CanCheckMobileAsset()) {
837 notify_register_dispatch(kOTATrustMobileAssetNotification, &out_token, queue, ^(int __unused token) {
838 secnotice("OTATrust", "Got notification about a new PKITrustSupplementals asset from mobileassetd.");
839 (void)DownloadOTATrustAsset(YES, NO, OTATrustMobileAssetType, nil);
843 /* Register for changes signaled by the system trustd */
844 secnotice("OTATrust", "Initializing listener for PKI Asset changes from system trustd.");
846 notify_register_dispatch(kOTATrustOnDiskAssetNotification, &out_token, queue, ^(int __unused token) {
847 secnotice("OTATrust", "Got notification about a new PKITrustSupplementals asset from system trustd.");
848 NSError *nserror = nil;
849 CFErrorRef error = nil;
850 NSNumber *asset_version = [NSNumber numberWithUnsignedLongLong:GetAssetVersion(&error)];
852 nserror = CFBridgingRelease(error);
854 if (!UpdateFromAsset(GetAssetFileURL(nil), asset_version, &nserror)) {
855 secerror("OTATrust: failed to update from asset after notification: %@", nserror);
856 /* Reset our last check-in time and reset the asset version to the system asset version -- even
857 * though we may be using something newer than that (but not as new as what's on disk). On re-launch
858 * (provided reading from disk still fails) we'd be using the system asset version anyway. */
859 SecOTAPKIResetCurrentAssetVersion(&error);
863 notify_register_dispatch(kOTATrustCheckInNotification, &out_token2, queue, ^(int __unused token) {
864 secinfo("OTATrust", "Got notification about successful PKITrustSupplementals asset check-in");
865 (void)UpdateOTACheckInDate();
868 notify_register_dispatch(kOTATrustKillSwitchNotification, &out_token3, queue, ^(int __unused token) {
869 UpdateKillSwitch((__bridge NSString*)kOTAPKIKillSwitchCT, InitializeKillSwitch((__bridge NSString*)kOTAPKIKillSwitchCT));
870 UpdateKillSwitch((__bridge NSString*)kOTAPKIKillSwitchNonTLSCT, InitializeKillSwitch((__bridge NSString*)kOTAPKIKillSwitchNonTLSCT));
875 static void InitializeOTASecExperimentAsset(dispatch_queue_t queue) {
876 /* Only the "system" trustd does updates */
877 if (SecOTAPKIIsSystemTrustd()) {
878 /* Asynchronously ask MobileAsset for most recent asset. */
879 dispatch_async(queue, ^{
880 secnotice("OTATrust", "Initial check with MobileAsset for newer SecExperiment asset");
881 (void)DownloadOTATrustAsset(NO, NO, OTASecExperimentMobileAssetType, nil);
884 /* Register for changes signaled by the system trustd */
885 secnotice("OTATrust", "Initializing listener for SecExperiment Asset changes from system trustd.");
887 notify_register_dispatch(kOTASecExperimentNewAssetNotification, &out_token, queue, ^(int __unused token) {
888 NSError *error = nil;
889 secnotice("OTATrust", "Got notification about a new SecExperiment asset from system trustd.");
890 MAAssetQuery *assetQuery = [[MAAssetQuery alloc] initWithType:OTASecExperimentMobileAssetType];
891 [assetQuery returnTypes:MAInstalledOnly];
892 MAQueryResult queryResult = [assetQuery queryMetaDataSync];
894 if (queryResult != MAQuerySuccessful) {
895 secerror("OTATrust: failed to update SecExperiment Asset after notification: %ld", (long)queryResult);
897 secnotice("OTATrust", "Updated SecExperiment asset successfully");
898 for (MAAsset *asset in assetQuery.results) {
899 NSNumber *asset_version = [asset assetProperty:@"_ContentVersion"];
900 if (!assetVersionCheck(OTASecExperimentMobileAssetType, asset)) {
903 UpdateAndPurgeAsset(OTASecExperimentMobileAssetType, asset, asset_version, &error);
910 static void TriggerPeriodicOTATrustAssetChecks(dispatch_queue_t queue) {
911 if (SecOTAPKIIsSystemTrustd()) {
912 static sec_action_t action;
913 static bool first_launch = true;
914 static dispatch_once_t onceToken;
915 dispatch_once(&onceToken, ^{
916 NSUserDefaults *defaults = [[NSUserDefaults alloc] initWithSuiteName:@"com.apple.security"];
917 NSNumber *updateDeltas = [defaults valueForKey:@"PKITrustSupplementalsUpdatePeriod"];
918 int delta = kOTATrustDefaultUpdatePeriod;
919 if (isNSNumber(updateDeltas)) {
920 delta = [updateDeltas intValue];
921 if (delta < kOTATrustMinimumUpdatePeriod) {
922 delta = kOTATrustMinimumUpdatePeriod;
925 secnotice("OTATrust", "Setting periodic update delta to %d seconds", delta);
926 action = sec_action_create_with_queue(queue,"OTATrust", delta);
927 sec_action_set_handler(action, ^{
929 (void)DownloadOTATrustAsset(NO, NO, OTASecExperimentMobileAssetType, nil);
930 (void)DownloadOTATrustAsset(NO, NO, OTATrustMobileAssetType, nil);
932 first_launch = false;
935 sec_action_perform(action);
938 #endif /* !TARGET_OS_BRIDGE */
941 /* MARK: Initialization functions */
942 static CFPropertyListRef CFPropertyListCopyFromSystem(CFStringRef asset) {
943 CFPropertyListRef plist = NULL;
944 CFDataRef xmlData = SecSystemTrustStoreCopyResourceContents(asset, CFSTR("plist"), NULL);
947 plist = CFPropertyListCreateWithData(kCFAllocatorDefault, xmlData, kCFPropertyListImmutable, NULL, NULL);
954 static uint64_t GetSystemVersion(CFStringRef key) {
955 uint64_t system_version = 0;
956 int64_t asset_number = 0;
958 CFDataRef assetVersionData = SecSystemTrustStoreCopyResourceContents(CFSTR("AssetVersion"), CFSTR("plist"), NULL);
959 if (NULL != assetVersionData) {
960 CFPropertyListFormat propFormat;
961 CFDictionaryRef versionPlist = CFPropertyListCreateWithData(kCFAllocatorDefault, assetVersionData, 0, &propFormat, NULL);
962 if (NULL != versionPlist && CFDictionaryGetTypeID() == CFGetTypeID(versionPlist)) {
963 CFNumberRef versionNumber = (CFNumberRef)CFDictionaryGetValue(versionPlist, (const void *)key);
964 if (NULL != versionNumber){
965 CFNumberGetValue(versionNumber, kCFNumberSInt64Type, &asset_number);
966 if (asset_number < 0) { // Not valid
969 system_version = (uint64_t)asset_number;
972 CFReleaseSafe(versionPlist);
973 CFReleaseSafe(assetVersionData);
976 return system_version;
979 static bool initialization_error_from_asset_data = false;
981 static bool ShouldInitializeWithAsset(void) {
982 uint64_t system_version = GetSystemVersion((__bridge CFStringRef)kOTATrustContentVersionKey);
983 uint64_t asset_version = GetAssetVersion(nil);
985 if (asset_version > system_version) {
986 secnotice("OTATrust", "Using asset v%llu instead of system v%llu", asset_version, system_version);
987 return !initialization_error_from_asset_data;
992 static CFSetRef CFSetCreateFromPropertyList(CFPropertyListRef plist) {
993 CFSetRef result = NULL;
996 CFMutableSetRef tempSet = NULL;
997 if (CFGetTypeID(plist) == CFArrayGetTypeID()) {
998 tempSet = CFSetCreateMutable(kCFAllocatorDefault, 0, &kCFTypeSetCallBacks);
999 if (NULL == tempSet) {
1002 CFArrayRef array = (CFArrayRef)plist;
1003 CFIndex num_keys = CFArrayGetCount(array);
1004 for (CFIndex idx = 0; idx < num_keys; idx++) {
1005 CFDataRef data = (CFDataRef)CFArrayGetValueAtIndex(array, idx);
1006 CFSetAddValue(tempSet, data);
1017 static CF_RETURNS_RETAINED CFSetRef InitializeBlackList() {
1018 CFPropertyListRef plist = CFPropertyListCopyFromSystem(CFSTR("Blocked"));
1019 CFSetRef result = CFSetCreateFromPropertyList(plist);
1020 CFReleaseSafe(plist);
1025 static CF_RETURNS_RETAINED CFSetRef InitializeGrayList() {
1026 CFPropertyListRef plist = CFPropertyListCopyFromSystem(CFSTR("GrayListedKeys"));
1027 CFSetRef result = CFSetCreateFromPropertyList(plist);
1028 CFReleaseSafe(plist);
1033 static CF_RETURNS_RETAINED CFURLRef InitializePinningList() {
1034 return SecSystemTrustStoreCopyResourceURL(CFSTR("CertificatePinning"), CFSTR("plist"), NULL);
1037 static CF_RETURNS_RETAINED CFDictionaryRef InitializeAllowList() {
1038 CFPropertyListRef allowList = CFPropertyListCopyFromSystem(CFSTR("Allowed"));
1040 if (allowList && (CFGetTypeID(allowList) == CFDictionaryGetTypeID())) {
1043 CFReleaseNull(allowList);
1048 static NSDictionary <NSData *, NSDictionary *> *ConvertTrustedCTLogsArrayToDictionary(NSArray *trustedLogsArray) {
1049 NSMutableDictionary <NSData *, NSDictionary *> *result = [NSMutableDictionary dictionaryWithCapacity:trustedLogsArray.count];
1050 [trustedLogsArray enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
1051 if (!isNSDictionary(obj)) {
1052 secerror("OTATrust: failed to read entry from trusted CT logs array at index %lu", (unsigned long)idx);
1055 NSDictionary *log_data = (NSDictionary *)obj;
1056 NSData *log_id = log_data[@"log_id"];
1057 if (!isNSData(log_id)) {
1058 secinfo("OTATrust", "failed to read log_id from trusted CT log array entry at index %lu, computing log_id", (unsigned long)idx);
1059 // We can make the log_id from the key
1060 NSData *key = log_data[@"key"];
1061 if (!isNSData(key)) {
1062 secerror("failed to read key from trusted CT log array entry at index %lu", (unsigned long)idx);
1065 log_id = CFBridgingRelease(SecSHA256DigestCreateFromData(NULL, (__bridge CFDataRef)key));
1067 [result setObject:log_data forKey:log_id];
1072 CFDictionaryRef SecOTAPKICreateTrustedCTLogsDictionaryFromArray(CFArrayRef trustedCTLogsArray)
1075 return CFBridgingRetain(ConvertTrustedCTLogsArrayToDictionary((__bridge NSArray*)trustedCTLogsArray));
1079 static CF_RETURNS_RETAINED CFDictionaryRef InitializeTrustedCTLogs(NSString *filename) {
1081 NSArray *trustedCTLogs = nil;
1082 NSError *error = nil;
1083 #if !TARGET_OS_BRIDGE
1084 if (ShouldInitializeWithAsset()) {
1085 trustedCTLogs = [NSArray arrayWithContentsOfURL:GetAssetFileURL(filename) error:&error];
1086 if (!isNSArray(trustedCTLogs)) {
1087 secerror("OTATrust: failed to read CT list from asset data: %@", error);
1088 LogRemotely(OTATrustLogLevelError, &error);
1089 if (!DeleteAssetFromDisk()) {
1090 initialization_error_from_asset_data = true;
1095 if (!isNSArray(trustedCTLogs)) {
1096 trustedCTLogs = [NSArray arrayWithContentsOfURL:SecSystemTrustStoreCopyResourceNSURL(filename)];
1098 if (isNSArray(trustedCTLogs)) {
1099 return CFBridgingRetain(ConvertTrustedCTLogsArrayToDictionary(trustedCTLogs));
1105 static CF_RETURNS_RETAINED CFDictionaryRef InitializeEVPolicyToAnchorDigestsTable() {
1106 CFDictionaryRef result = NULL;
1107 CFPropertyListRef evroots = CFPropertyListCopyFromSystem(CFSTR("EVRoots"));
1110 if (CFGetTypeID(evroots) == CFDictionaryGetTypeID()) {
1111 /* @@@ Ensure that each dictionary key is a dotted list of digits,
1112 each value is an NSArrayRef and each element in the array is a
1114 result = (CFDictionaryRef)evroots;
1117 secwarning("EVRoot.plist is wrong type.");
1125 static CFIndex InitializeValidSnapshotVersion(CFIndex *outFormat) {
1126 CFIndex validVersion = 0;
1127 CFIndex validFormat = 0;
1128 CFDataRef validVersionData = SecSystemTrustStoreCopyResourceContents(CFSTR("ValidUpdate"), CFSTR("plist"), NULL);
1129 if (NULL != validVersionData)
1131 CFPropertyListFormat propFormat;
1132 CFDictionaryRef versionPlist = CFPropertyListCreateWithData(kCFAllocatorDefault, validVersionData, 0, &propFormat, NULL);
1133 if (NULL != versionPlist && CFDictionaryGetTypeID() == CFGetTypeID(versionPlist))
1135 CFNumberRef versionNumber = (CFNumberRef)CFDictionaryGetValue(versionPlist, (const void *)CFSTR("Version"));
1136 if (NULL != versionNumber)
1138 CFNumberGetValue(versionNumber, kCFNumberCFIndexType, &validVersion);
1140 CFNumberRef formatNumber = (CFNumberRef)CFDictionaryGetValue(versionPlist, (const void *)CFSTR("Format"));
1141 if (NULL != formatNumber)
1143 CFNumberGetValue(formatNumber, kCFNumberCFIndexType, &validFormat);
1146 CFReleaseSafe(versionPlist);
1147 CFReleaseSafe(validVersionData);
1150 *outFormat = validFormat;
1152 return validVersion;
1155 static Boolean PathExists(const char* path, size_t* pFileSize) {
1156 const char *checked_path = (path) ? path : "";
1157 Boolean result = false;
1160 if (NULL != pFileSize) {
1164 int stat_result = stat(checked_path, &sb);
1165 result = (stat_result == 0);
1167 if (result && !S_ISDIR(sb.st_mode)) {
1169 if (NULL != pFileSize) {
1170 *pFileSize = (size_t)sb.st_size;
1177 static const char* InitializeValidSnapshotData(CFStringRef filename_str) {
1178 char *result = NULL;
1179 const char *base_error_str = "could not get valid snapshot";
1181 CFURLRef valid_url = SecSystemTrustStoreCopyResourceURL(filename_str, CFSTR("sqlite3"), NULL);
1182 if (NULL == valid_url) {
1183 secerror("%s", base_error_str);
1185 CFStringRef valid_str = CFURLCopyFileSystemPath(valid_url, kCFURLPOSIXPathStyle);
1186 char file_path_buffer[PATH_MAX];
1187 memset(file_path_buffer, 0, PATH_MAX);
1188 if (NULL == valid_str) {
1189 secerror("%s path", base_error_str);
1191 const char *valid_cstr = CFStringGetCStringPtr(valid_str, kCFStringEncodingUTF8);
1192 if (NULL == valid_cstr) {
1193 if (CFStringGetCString(valid_str, file_path_buffer, PATH_MAX, kCFStringEncodingUTF8)) {
1194 valid_cstr = file_path_buffer;
1197 if (NULL == valid_cstr) {
1198 secerror("%s path as UTF8 string", base_error_str);
1200 asprintf(&result, "%s", valid_cstr);
1203 CFReleaseSafe(valid_str);
1205 CFReleaseSafe(valid_url);
1206 if (result && !PathExists(result, NULL)) {
1210 return (const char*)result;
1213 static const char* InitializeValidDatabaseSnapshot() {
1214 return InitializeValidSnapshotData(CFSTR("valid"));
1217 static const uint8_t* MapFile(const char* path, size_t* out_file_size) {
1219 const uint8_t *buf = NULL;
1223 if (NULL == path || NULL == out_file_size) {
1229 fd = open(path, O_RDONLY);
1230 if (fd < 0) { return NULL; }
1231 rtn = fstat(fd, &sb);
1232 if (rtn || (sb.st_size > (off_t) ((UINT32_MAX >> 1)-1))) {
1236 size = (size_t)sb.st_size;
1238 buf = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
1239 if (!buf || buf == MAP_FAILED) {
1240 secerror("unable to map %s (errno %d)", path, errno);
1246 *out_file_size = size;
1250 static void UnMapFile(void* mapped_data, size_t data_size) {
1254 int rtn = munmap(mapped_data, data_size);
1256 secerror("unable to unmap %ld bytes at %p (error %d)", data_size, mapped_data, rtn);
1260 struct index_record {
1261 unsigned char hash[CC_SHA1_DIGEST_LENGTH];
1264 typedef struct index_record index_record;
1266 static bool InitializeAnchorTable(CFDictionaryRef* pLookupTable, const char** ppAnchorTable) {
1268 bool result = false;
1270 if (NULL == pLookupTable || NULL == ppAnchorTable) {
1274 *pLookupTable = NULL;
1275 *ppAnchorTable = NULL;;
1277 CFDataRef cert_index_file_data = NULL;
1278 char file_path_buffer[PATH_MAX];
1279 CFURLRef table_data_url = NULL;
1280 CFStringRef table_data_cstr_path = NULL;
1281 const char* table_data_path = NULL;
1282 const index_record* pIndex = NULL;
1283 size_t index_offset = 0;
1284 size_t index_data_size = 0;
1285 CFMutableDictionaryRef anchorLookupTable = NULL;
1286 uint32_t offset_int_value = 0;
1287 CFNumberRef index_offset_value = NULL;
1288 CFDataRef index_hash = NULL;
1289 CFMutableArrayRef offsets = NULL;
1290 Boolean release_offset = false;
1292 char* local_anchorTable = NULL;
1293 size_t local_anchorTableSize = 0;
1295 // local_anchorTable is still NULL so the asset in the system trust store bundle needs to be used.
1296 CFReleaseSafe(cert_index_file_data);
1297 cert_index_file_data = SecSystemTrustStoreCopyResourceContents(CFSTR("certsIndex"), CFSTR("data"), NULL);
1298 if (!cert_index_file_data) {
1299 secerror("could not find certsIndex");
1301 table_data_url = SecSystemTrustStoreCopyResourceURL(CFSTR("certsTable"), CFSTR("data"), NULL);
1302 if (!table_data_url) {
1303 secerror("could not find certsTable");
1306 if (NULL != table_data_url) {
1307 table_data_cstr_path = CFURLCopyFileSystemPath(table_data_url, kCFURLPOSIXPathStyle);
1308 if (NULL != table_data_cstr_path) {
1309 memset(file_path_buffer, 0, PATH_MAX);
1310 table_data_path = CFStringGetCStringPtr(table_data_cstr_path, kCFStringEncodingUTF8);
1311 if (NULL == table_data_path) {
1312 if (CFStringGetCString(table_data_cstr_path, file_path_buffer, PATH_MAX, kCFStringEncodingUTF8)) {
1313 table_data_path = file_path_buffer;
1316 local_anchorTable = (char *)MapFile(table_data_path, &local_anchorTableSize);
1317 CFReleaseSafe(table_data_cstr_path);
1320 CFReleaseSafe(table_data_url);
1322 if (NULL == local_anchorTable || NULL == cert_index_file_data) {
1323 // we are in trouble
1324 if (NULL != local_anchorTable) {
1325 UnMapFile(local_anchorTable, local_anchorTableSize);
1326 local_anchorTable = NULL;
1327 local_anchorTableSize = 0;
1329 CFReleaseSafe(cert_index_file_data);
1333 // ------------------------------------------------------------------------
1334 // Now that the locations of the files are known and the table file has
1335 // been mapped into memory, create a dictionary that maps the SHA1 hash of
1336 // normalized issuer to the offset in the mapped anchor table file which
1337 // contains a index_record to the correct certificate
1338 // ------------------------------------------------------------------------
1339 pIndex = (const index_record*)CFDataGetBytePtr(cert_index_file_data);
1340 index_data_size = CFDataGetLength(cert_index_file_data);
1342 anchorLookupTable = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
1343 &kCFTypeDictionaryKeyCallBacks,
1344 &kCFTypeDictionaryValueCallBacks);
1346 for (index_offset = index_data_size; index_offset > 0; index_offset -= sizeof(index_record), pIndex++) {
1347 offset_int_value = pIndex->offset;
1349 index_offset_value = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &offset_int_value);
1350 index_hash = CFDataCreate(kCFAllocatorDefault, pIndex->hash, CC_SHA1_DIGEST_LENGTH);
1352 // see if the dictionary already has this key
1353 release_offset = false;
1354 offsets = (CFMutableArrayRef)CFDictionaryGetValue(anchorLookupTable, index_hash);
1355 if (NULL == offsets) {
1356 offsets = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
1357 release_offset = true;
1361 CFArrayAppendValue(offsets, index_offset_value);
1363 // set the key value pair in the dictionary
1364 CFDictionarySetValue(anchorLookupTable, index_hash, offsets);
1366 CFRelease(index_offset_value);
1367 CFRelease(index_hash);
1368 if (release_offset) {
1373 CFRelease(cert_index_file_data);
1375 if (NULL != anchorLookupTable && NULL != local_anchorTable) {
1376 *pLookupTable = anchorLookupTable;
1377 *ppAnchorTable = local_anchorTable;
1380 CFReleaseSafe(anchorLookupTable);
1381 if (NULL != local_anchorTable) {
1382 UnMapFile(local_anchorTable, local_anchorTableSize);
1383 local_anchorTable = NULL;
1384 local_anchorTableSize = 0;
1391 static void InitializeEscrowCertificates(CFArrayRef *escrowRoots, CFArrayRef *escrowPCSRoots) {
1392 CFDataRef file_data = SecSystemTrustStoreCopyResourceContents(CFSTR("AppleESCertificates"), CFSTR("plist"), NULL);
1394 if (NULL == file_data) {
1398 CFPropertyListFormat propFormat;
1399 CFDictionaryRef certsDictionary = CFPropertyListCreateWithData(kCFAllocatorDefault, file_data, 0, &propFormat, NULL);
1400 if (NULL != certsDictionary && CFDictionaryGetTypeID() == CFGetTypeID((CFTypeRef)certsDictionary)) {
1401 CFArrayRef certs = (CFArrayRef)CFDictionaryGetValue(certsDictionary, CFSTR("ProductionEscrowKey"));
1402 if (NULL != certs && CFArrayGetTypeID() == CFGetTypeID((CFTypeRef)certs) && CFArrayGetCount(certs) > 0) {
1403 *escrowRoots = CFArrayCreateCopy(kCFAllocatorDefault, certs);
1405 CFArrayRef pcs_certs = (CFArrayRef)CFDictionaryGetValue(certsDictionary, CFSTR("ProductionPCSEscrowKey"));
1406 if (NULL != pcs_certs && CFArrayGetTypeID() == CFGetTypeID((CFTypeRef)pcs_certs) && CFArrayGetCount(pcs_certs) > 0) {
1407 *escrowPCSRoots = CFArrayCreateCopy(kCFAllocatorDefault, pcs_certs);
1410 CFReleaseSafe(certsDictionary);
1411 CFRelease(file_data);
1414 static CF_RETURNS_RETAINED CFDictionaryRef InitializeEventSamplingRates() {
1415 NSDictionary *analyticsSamplingRates = nil;
1416 NSDictionary *eventSamplingRates = nil;
1417 NSError *error = nil;
1418 #if !TARGET_OS_BRIDGE
1419 if (ShouldInitializeWithAsset()) {
1420 analyticsSamplingRates = [NSDictionary dictionaryWithContentsOfURL:GetAssetFileURL(kOTATrustAnalyticsSamplingRatesFilename) error:&error];
1421 if (!isNSDictionary(analyticsSamplingRates)) {
1422 secerror("OTATrust: failed to read sampling rates from asset data: %@", error);
1423 LogRemotely(OTATrustLogLevelError, &error);
1424 if (!DeleteAssetFromDisk()) {
1425 initialization_error_from_asset_data = true;
1428 eventSamplingRates = analyticsSamplingRates[@"Events"];
1431 if (!isNSDictionary(eventSamplingRates)) {
1432 analyticsSamplingRates = [NSDictionary dictionaryWithContentsOfURL:SecSystemTrustStoreCopyResourceNSURL(kOTATrustAnalyticsSamplingRatesFilename)];
1434 if (isNSDictionary(analyticsSamplingRates)) {
1435 eventSamplingRates = analyticsSamplingRates[@"Events"];
1436 if (isNSDictionary(eventSamplingRates)) {
1437 return CFBridgingRetain(eventSamplingRates);
1443 static CF_RETURNS_RETAINED CFArrayRef InitializeAppleCertificateAuthorities() {
1444 NSArray *appleCAs = nil;
1445 NSError *error = nil;
1446 #if !TARGET_OS_BRIDGE
1447 if (ShouldInitializeWithAsset()) {
1448 appleCAs = [NSArray arrayWithContentsOfURL:GetAssetFileURL(kOTATrustAppleCertifcateAuthoritiesFilename) error:&error];
1449 if (!isNSArray(appleCAs)) {
1450 secerror("OTATrust: failed to read Apple CAs list from asset data: %@", error);
1451 LogRemotely(OTATrustLogLevelError, &error);
1452 if (!DeleteAssetFromDisk()) {
1453 initialization_error_from_asset_data = true;
1458 if (!isNSArray(appleCAs)) {
1459 appleCAs = [NSArray arrayWithContentsOfURL:SecSystemTrustStoreCopyResourceNSURL(kOTATrustAppleCertifcateAuthoritiesFilename)];
1461 if (isNSArray(appleCAs)) {
1462 return CFBridgingRetain(appleCAs);
1470 /* We keep track of one OTAPKI reference */
1471 static SecOTAPKIRef kCurrentOTAPKIRef = NULL;
1472 /* This queue is for making changes to the OTAPKI reference */
1473 static dispatch_queue_t kOTAQueue = NULL;
1475 struct _OpaqueSecOTAPKI {
1476 CFRuntimeBase _base;
1477 CFSetRef _blackListSet;
1478 CFSetRef _grayListSet;
1479 CFDictionaryRef _allowList;
1480 CFDictionaryRef _trustedCTLogs;
1481 CFDictionaryRef _nonTlsTrustedCTLogs;
1482 CFURLRef _pinningList;
1483 CFArrayRef _escrowCertificates;
1484 CFArrayRef _escrowPCSCertificates;
1485 CFDictionaryRef _evPolicyToAnchorMapping;
1486 CFDictionaryRef _anchorLookupTable;
1487 const char* _anchorTable;
1488 uint64_t _trustStoreVersion;
1489 const char* _validDatabaseSnapshot;
1490 CFIndex _validSnapshotVersion;
1491 CFIndex _validSnapshotFormat;
1492 uint64_t _assetVersion;
1493 CFDateRef _lastAssetCheckIn;
1494 CFDictionaryRef _eventSamplingRates;
1495 CFArrayRef _appleCAs;
1496 CFDictionaryRef _secExperimentConfig;
1497 uint64_t _secExperimentAssetVersion;
1499 bool _nonTlsCtKillSwitch;
1502 CFGiblisFor(SecOTAPKI)
1504 static CF_RETURNS_RETAINED CFStringRef SecOTAPKICopyFormatDescription(CFTypeRef cf, CFDictionaryRef formatOptions) {
1505 SecOTAPKIRef otapkiRef = (SecOTAPKIRef)cf;
1506 return CFStringCreateWithFormat(kCFAllocatorDefault,NULL,CFSTR("<SecOTAPKIRef: version %llu/%llu>"),
1507 otapkiRef->_trustStoreVersion, otapkiRef->_assetVersion);
1510 static void SecOTAPKIDestroy(CFTypeRef cf) {
1511 SecOTAPKIRef otapkiref = (SecOTAPKIRef)cf;
1513 CFReleaseNull(otapkiref->_blackListSet);
1514 CFReleaseNull(otapkiref->_grayListSet);
1515 CFReleaseNull(otapkiref->_escrowCertificates);
1516 CFReleaseNull(otapkiref->_escrowPCSCertificates);
1518 CFReleaseNull(otapkiref->_evPolicyToAnchorMapping);
1519 CFReleaseNull(otapkiref->_anchorLookupTable);
1521 CFReleaseNull(otapkiref->_trustedCTLogs);
1522 CFReleaseNull(otapkiref->_nonTlsTrustedCTLogs);
1523 CFReleaseNull(otapkiref->_pinningList);
1524 CFReleaseNull(otapkiref->_eventSamplingRates);
1525 CFReleaseNull(otapkiref->_appleCAs);
1526 CFReleaseNull(otapkiref->_lastAssetCheckIn);
1527 CFReleaseNull(otapkiref->_secExperimentConfig);
1529 if (otapkiref->_anchorTable) {
1530 free((void *)otapkiref->_anchorTable);
1531 otapkiref->_anchorTable = NULL;
1533 if (otapkiref->_validDatabaseSnapshot) {
1534 free((void *)otapkiref->_validDatabaseSnapshot);
1535 otapkiref->_validDatabaseSnapshot = NULL;
1539 static uint64_t GetSystemTrustStoreVersion(void) {
1540 return GetSystemVersion(CFSTR("VersionNumber"));
1543 static uint64_t GetAssetVersion(CFErrorRef *error) {
1545 /* Get system asset version */
1546 uint64_t version = GetSystemVersion((__bridge CFStringRef)kOTATrustContentVersionKey);
1548 #if !TARGET_OS_BRIDGE
1549 uint64_t asset_version = 0;
1550 NSError *nserror = nil;
1551 NSDictionary *OTAPKIContext = [NSDictionary dictionaryWithContentsOfURL:GetAssetFileURL(kOTATrustContextFilename) error:&nserror];
1552 if (isNSDictionary(OTAPKIContext)) {
1553 NSNumber *tmpNumber = OTAPKIContext[kOTATrustContentVersionKey];
1554 if (isNSNumber(tmpNumber)) {
1555 asset_version = [tmpNumber unsignedLongLongValue];
1557 MakeOTATrustError(OTATrustMobileAssetType, &nserror, OTATrustLogLevelNotice, NSOSStatusErrorDomain, errSecInvalidValue,
1558 @"OTAContext.plist missing version");
1561 MakeOTATrustError(OTATrustMobileAssetType, &nserror, OTATrustLogLevelNotice, NSOSStatusErrorDomain, errSecMissingValue,
1562 @"OTAContext.plist missing dictionary");
1565 if (asset_version > version) {
1566 return asset_version;
1568 /* Don't delete the last check-in time so that we know we're up to date with the MobileAsset. */
1570 /* only log this if we're tracking errors */
1571 secnotice("OTATrust", "asset (%llu) is not newer than the system version (%llu); deleting stale data", asset_version, version);
1572 *error = CFRetainSafe((__bridge CFErrorRef)nserror);
1574 DeleteOldAssetData();
1575 DisableKillSwitches();
1582 static CF_RETURNS_RETAINED CFDateRef InitializeLastAssetCheckIn(void) {
1583 #if !TARGET_OS_BRIDGE
1584 NSError *error = nil;
1585 NSDictionary *OTAPKIContext = [NSDictionary dictionaryWithContentsOfURL:GetAssetFileURL(kOTATrustContextFilename) error:&error];
1586 if (isNSDictionary(OTAPKIContext)) {
1587 NSDate *checkIn = OTAPKIContext[kOTATrustLastCheckInKey];
1588 if (isNSDate(checkIn)) {
1589 return CFRetainSafe((__bridge CFDateRef)checkIn);
1591 MakeOTATrustError(OTATrustMobileAssetType, &error, OTATrustLogLevelNotice, NSOSStatusErrorDomain, errSecInvalidValue,
1592 @"OTAContext.plist missing check-in");
1595 MakeOTATrustError(OTATrustMobileAssetType, &error, OTATrustLogLevelNotice, NSOSStatusErrorDomain, errSecMissingValue,
1596 @"OTAContext.plist missing dictionary");
1602 static SecOTAPKIRef SecOTACreate() {
1604 SecOTAPKIRef otapkiref = NULL;
1606 otapkiref = CFTypeAllocate(SecOTAPKI, struct _OpaqueSecOTAPKI , kCFAllocatorDefault);
1608 if (NULL == otapkiref) {
1612 // Make sure that if this routine has to bail that the clean up
1613 // will do the right thing
1614 memset(otapkiref, 0, sizeof(*otapkiref));
1616 // Start off by getting the trust store version
1617 otapkiref->_trustStoreVersion = GetSystemTrustStoreVersion();
1619 // Get the set of black listed keys
1620 CFSetRef blackKeysSet = InitializeBlackList();
1621 if (NULL == blackKeysSet) {
1622 CFReleaseNull(otapkiref);
1625 otapkiref->_blackListSet = blackKeysSet;
1627 // Get the set of gray listed keys
1628 CFSetRef grayKeysSet = InitializeGrayList();
1629 if (NULL == grayKeysSet) {
1630 CFReleaseNull(otapkiref);
1633 otapkiref->_grayListSet = grayKeysSet;
1635 // Get the allow list dictionary
1636 // (now loaded lazily in SecOTAPKICopyAllowList)
1638 // Get the trusted Certificate Transparency Logs
1639 otapkiref->_trustedCTLogs = InitializeTrustedCTLogs(kOTATrustTrustedCTLogsFilename);
1640 otapkiref->_nonTlsTrustedCTLogs = InitializeTrustedCTLogs(kOTATrustTrustedCTLogsNonTLSFilename);
1642 // Get the pinning list
1643 otapkiref->_pinningList = InitializePinningList();
1645 // Get the Event Sampling Rates
1646 otapkiref->_eventSamplingRates = InitializeEventSamplingRates();
1648 // Get the list of CAs used by Apple
1649 otapkiref->_appleCAs = InitializeAppleCertificateAuthorities();
1651 // Get the asset version (after possible reset due to missing asset data)
1652 if (!initialization_error_from_asset_data) {
1653 CFErrorRef error = nil;
1654 otapkiref->_assetVersion = GetAssetVersion(&error);
1655 otapkiref->_lastAssetCheckIn = InitializeLastAssetCheckIn();
1656 CFReleaseNull(error);
1658 otapkiref->_assetVersion = GetSystemVersion((__bridge CFStringRef)kOTATrustContentVersionKey);
1660 otapkiref->_secExperimentAssetVersion = 0;
1661 // Get the valid update snapshot version and format
1662 CFIndex update_format = 0;
1663 otapkiref->_validSnapshotVersion = InitializeValidSnapshotVersion(&update_format);
1664 otapkiref->_validSnapshotFormat = update_format;
1666 // Get the valid database snapshot path (if it exists, NULL otherwise)
1667 otapkiref->_validDatabaseSnapshot = InitializeValidDatabaseSnapshot();
1669 CFArrayRef escrowCerts = NULL;
1670 CFArrayRef escrowPCSCerts = NULL;
1671 InitializeEscrowCertificates(&escrowCerts, &escrowPCSCerts);
1672 if (NULL == escrowCerts || NULL == escrowPCSCerts) {
1673 CFReleaseNull(escrowCerts);
1674 CFReleaseNull(escrowPCSCerts);
1675 CFReleaseNull(otapkiref);
1678 otapkiref->_escrowCertificates = escrowCerts;
1679 otapkiref->_escrowPCSCertificates = escrowPCSCerts;
1681 // Get the mapping of EV Policy OIDs to Anchor digest
1682 CFDictionaryRef evOidToAnchorDigestMap = InitializeEVPolicyToAnchorDigestsTable();
1683 if (NULL == evOidToAnchorDigestMap) {
1684 CFReleaseNull(otapkiref);
1687 otapkiref->_evPolicyToAnchorMapping = evOidToAnchorDigestMap;
1689 CFDictionaryRef anchorLookupTable = NULL;
1690 const char* anchorTablePtr = NULL;
1692 if (!InitializeAnchorTable(&anchorLookupTable, &anchorTablePtr)) {
1693 CFReleaseSafe(anchorLookupTable);
1694 if (anchorTablePtr) {
1695 free((void *)anchorTablePtr);
1697 CFReleaseNull(otapkiref);
1700 otapkiref->_anchorLookupTable = anchorLookupTable;
1701 otapkiref->_anchorTable = anchorTablePtr;
1703 #if !TARGET_OS_BRIDGE
1704 /* Initialize our update handling */
1705 InitializeOTATrustAsset(kOTABackgroundQueue);
1706 otapkiref->_ctKillSwitch = InitializeKillSwitch((__bridge NSString*)kOTAPKIKillSwitchCT);
1707 otapkiref->_nonTlsCtKillSwitch = InitializeKillSwitch((__bridge NSString*)kOTAPKIKillSwitchNonTLSCT);
1708 InitializeOTASecExperimentAsset(kOTABackgroundQueue);
1709 #else // TARGET_OS_BRIDGE
1710 otapkiref->_ctKillSwitch = true; // bridgeOS never enforces CT
1711 otapkiref->_nonTlsCtKillSwitch = true;
1712 #endif // TARGET_OS_BRIDGE
1717 SecOTAPKIRef SecOTAPKICopyCurrentOTAPKIRef() {
1718 __block SecOTAPKIRef result = NULL;
1719 static dispatch_once_t kInitializeOTAPKI = 0;
1720 dispatch_once(&kInitializeOTAPKI, ^{
1722 kOTAQueue = dispatch_queue_create("com.apple.security.OTAPKIQueue", NULL);
1723 dispatch_queue_attr_t attr = dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_SERIAL,
1724 QOS_CLASS_BACKGROUND, 0);
1725 attr = dispatch_queue_attr_make_with_autorelease_frequency(attr, DISPATCH_AUTORELEASE_FREQUENCY_WORK_ITEM);
1726 kOTABackgroundQueue = dispatch_queue_create("com.apple.security.OTAPKIBackgroundQueue", attr);
1727 if (!kOTAQueue || !kOTABackgroundQueue) {
1728 secerror("Failed to create OTAPKI Queues. May crash later.");
1730 dispatch_sync(kOTAQueue, ^{
1731 kCurrentOTAPKIRef = SecOTACreate();
1736 dispatch_sync(kOTAQueue, ^{
1737 result = kCurrentOTAPKIRef;
1738 CFRetainSafe(result);
1743 #if !TARGET_OS_BRIDGE
1744 BOOL UpdateOTACheckInDate(void) {
1745 __block NSDate *checkIn = [NSDate date];
1746 dispatch_sync(kOTAQueue, ^{
1747 CFRetainAssign(kCurrentOTAPKIRef->_lastAssetCheckIn, (__bridge CFDateRef)checkIn);
1750 if (SecOTAPKIIsSystemTrustd()) {
1751 /* Let the other trustds know we successfully checked in */
1752 notify_post(kOTATrustCheckInNotification);
1754 /* Update the on-disk check-in date, so when we re-launch we remember */
1755 NSError *error = nil;
1757 if (!(result = UpdateOTAContextOnDisk(kOTATrustLastCheckInKey, checkIn, &error))) {
1758 secerror("OTATrust: failed to write last check-in time: %@", error);
1766 void UpdateKillSwitch(NSString *key, bool value) {
1767 dispatch_sync(kOTAQueue, ^{
1768 if ([key isEqualToString:(__bridge NSString*)kOTAPKIKillSwitchCT]) {
1769 kCurrentOTAPKIRef->_ctKillSwitch = value;
1770 } else if ([key isEqualToString:(__bridge NSString*)kOTAPKIKillSwitchNonTLSCT]) {
1771 kCurrentOTAPKIRef->_nonTlsCtKillSwitch = value;
1776 static BOOL UpdateFromAsset(NSURL *localURL, NSNumber *asset_version, NSError **error) {
1777 if (!localURL || !asset_version) {
1778 MakeOTATrustError(OTATrustMobileAssetType, error, OTATrustLogLevelError, NSOSStatusErrorDomain, errSecInternal,
1779 @"missing url and version for downloaded asset");
1782 __block NSArray *newTrustedCTLogs = NULL;
1783 __block NSArray *newNonTlsTrustedCTLogs = NULL;
1784 __block uint64_t version = [asset_version unsignedLongLongValue];
1785 __block NSDictionary *newAnalyticsSamplingRates = NULL;
1786 __block NSArray *newAppleCAs = NULL;
1788 NSURL *TrustedCTLogsFileLoc = [NSURL URLWithString:kOTATrustTrustedCTLogsFilename
1789 relativeToURL:localURL];
1790 newTrustedCTLogs = [NSArray arrayWithContentsOfURL:TrustedCTLogsFileLoc error:error];
1791 if (!newTrustedCTLogs) {
1792 secerror("OTATrust: unable to create TrustedCTLogs from asset file: %@", error ? *error: nil);
1793 LogRemotely(OTATrustLogLevelError, error);
1797 NSURL *nonTLSTrustedCTLogsFileLoc = [NSURL URLWithString:kOTATrustTrustedCTLogsNonTLSFilename
1798 relativeToURL:localURL];
1799 newNonTlsTrustedCTLogs = [NSArray arrayWithContentsOfURL:nonTLSTrustedCTLogsFileLoc error:error];
1800 if (!newNonTlsTrustedCTLogs) {
1801 secerror("OTATrust: unable to create TrustedCTLogs_nonTLS from asset file: %@", error ? *error: nil);
1802 LogRemotely(OTATrustLogLevelError, error);
1806 NSURL *AnalyticsSamplingRatesFileLoc = [NSURL URLWithString:kOTATrustAnalyticsSamplingRatesFilename
1807 relativeToURL:localURL];
1808 newAnalyticsSamplingRates = [NSDictionary dictionaryWithContentsOfURL:AnalyticsSamplingRatesFileLoc error:error];
1809 if (!newAnalyticsSamplingRates) {
1810 secerror("OTATrust: unable to create AnalyticsSamplingRates from asset file: %@", error ? *error: nil);
1811 LogRemotely(OTATrustLogLevelError, error);
1815 NSURL *AppleCAsFileLoc = [NSURL URLWithString:kOTATrustAppleCertifcateAuthoritiesFilename
1816 relativeToURL:localURL];
1817 newAppleCAs = [NSArray arrayWithContentsOfURL:AppleCAsFileLoc error:error];
1819 secerror("OTATrust: unable to create AppleCAs from asset file: %@", error ? *error: nil);
1820 LogRemotely(OTATrustLogLevelError, error);
1824 /* Update the Current OTAPKIRef with the new data */
1825 dispatch_sync(kOTAQueue, ^{
1826 secnotice("OTATrust", "updating asset version from %llu to %llu", kCurrentOTAPKIRef->_assetVersion, version);
1827 CFRetainAssign(kCurrentOTAPKIRef->_trustedCTLogs, (__bridge CFDictionaryRef)ConvertTrustedCTLogsArrayToDictionary(newTrustedCTLogs));
1828 CFRetainAssign(kCurrentOTAPKIRef->_nonTlsTrustedCTLogs, (__bridge CFDictionaryRef)ConvertTrustedCTLogsArrayToDictionary(newNonTlsTrustedCTLogs));
1829 CFRetainAssign(kCurrentOTAPKIRef->_eventSamplingRates, (__bridge CFDictionaryRef)newAnalyticsSamplingRates);
1830 CFRetainAssign(kCurrentOTAPKIRef->_appleCAs, (__bridge CFArrayRef)newAppleCAs);
1831 kCurrentOTAPKIRef->_assetVersion = version;
1834 /* Reset the current files, version, and checkin so that in the case of write failures, we'll re-try
1835 * to update the data. We don't call DeleteAssetFromDisk() here to preserve any kill switches. */
1836 DeleteOldAssetData();
1837 UpdateOTAContext(@(0), nil);
1838 UpdateOTAContextOnDisk(kOTATrustLastCheckInKey, [NSDate dateWithTimeIntervalSince1970:0], nil);
1840 /* Write the data to disk (so that we don't have to re-download the asset on re-launch). */
1841 if (CopyFileToDisk(kOTATrustTrustedCTLogsFilename, TrustedCTLogsFileLoc, error) &&
1842 CopyFileToDisk(kOTATrustTrustedCTLogsNonTLSFilename, nonTLSTrustedCTLogsFileLoc, error) &&
1843 CopyFileToDisk(kOTATrustAnalyticsSamplingRatesFilename, AnalyticsSamplingRatesFileLoc, error) &&
1844 CopyFileToDisk(kOTATrustAppleCertifcateAuthoritiesFilename, AppleCAsFileLoc, error) &&
1845 UpdateOTAContext(asset_version, error)) { // Set version and check-in time last (after success)
1846 /* If we successfully updated the "asset" on disk, signal the other trustds to pick up the changes */
1847 notify_post(kOTATrustOnDiskAssetNotification);
1854 #endif // !TARGET_OS_BRIDGE
1856 #if !TARGET_OS_BRIDGE
1857 static NSNumber *SecExperimentUpdateAsset(MAAsset *asset, NSNumber *asset_version, NSError **error) {
1858 NSURL *localURL = [asset getLocalFileUrl];
1859 if (!localURL || !asset_version) {
1860 MakeOTATrustError(OTASecExperimentMobileAssetType, error, OTATrustLogLevelError, NSOSStatusErrorDomain, errSecInternal,
1861 @"missing url and version for downloaded SecExperiment asset");
1864 NSDictionary *newSecExpConfig = NULL;
1865 uint64_t version = [asset_version unsignedLongLongValue];
1867 NSURL *secExpConfigFileLoc = [NSURL URLWithString:kOTASecExperimentConfigFilename
1868 relativeToURL:localURL];
1869 newSecExpConfig = [NSDictionary dictionaryWithContentsOfURL:secExpConfigFileLoc error:error];
1870 if (!newSecExpConfig) {
1871 secerror("OTATrust: unable to create SecExperiment from asset file: %@", error ? *error: nil);
1874 /* Update the Current OTAPKIRef with the new data */
1875 dispatch_sync(kOTAQueue, ^{
1876 secnotice("OTATrust", "updating SecExperiment asset version from %llu to %llu", kCurrentOTAPKIRef->_secExperimentAssetVersion, version);
1877 CFRetainAssign(kCurrentOTAPKIRef->_secExperimentConfig, (__bridge CFDictionaryRef)newSecExpConfig);
1878 kCurrentOTAPKIRef->_secExperimentAssetVersion = version;
1880 /* Signal the other trustds to pick up the changes */
1881 notify_post(kOTASecExperimentNewAssetNotification);
1882 return asset_version;
1884 #endif // !TARGET_OS_BRIDGE
1886 CFSetRef SecOTAPKICopyBlackListSet(SecOTAPKIRef otapkiRef) {
1887 if (NULL == otapkiRef) {
1891 return CFRetainSafe(otapkiRef->_blackListSet);
1895 CFSetRef SecOTAPKICopyGrayList(SecOTAPKIRef otapkiRef) {
1896 if (NULL == otapkiRef) {
1900 return CFRetainSafe(otapkiRef->_grayListSet);
1903 CFDictionaryRef SecOTAPKICopyAllowList(SecOTAPKIRef otapkiRef) {
1904 if (NULL == otapkiRef) {
1908 CFDictionaryRef result = otapkiRef->_allowList;
1910 result = InitializeAllowList();
1911 otapkiRef->_allowList = result;
1914 return CFRetainSafe(result);
1917 CFArrayRef SecOTAPKICopyAllowListForAuthKeyID(SecOTAPKIRef otapkiRef, CFStringRef authKeyID) {
1918 // %%% temporary performance optimization:
1919 // only load dictionary if we know an allow list exists for this key
1920 const CFStringRef keyIDs[3] = {
1921 CFSTR("7C724B39C7C0DB62A54F9BAA183492A2CA838259"),
1922 CFSTR("65F231AD2AF7F7DD52960AC702C10EEFA6D53B11"),
1923 CFSTR("D2A716207CAFD9959EEB430A19F2E0B9740EA8C7")
1925 CFArrayRef result = NULL;
1926 bool hasAllowList = false;
1927 CFIndex count = (sizeof(keyIDs) / sizeof(keyIDs[0]));
1928 for (CFIndex ix=0; ix<count && authKeyID; ix++) {
1929 if (kCFCompareEqualTo == CFStringCompare(authKeyID, keyIDs[ix], 0)) {
1930 hasAllowList = true;
1934 if (!hasAllowList || !otapkiRef) {
1938 CFDictionaryRef allowListDict = SecOTAPKICopyAllowList(otapkiRef);
1939 if (!allowListDict) {
1943 // return a retained copy of the allow list array (or NULL)
1944 result = CFDictionaryGetValue(allowListDict, authKeyID);
1945 CFRetainSafe(result);
1946 CFReleaseSafe(allowListDict);
1950 CFDictionaryRef SecOTAPKICopyTrustedCTLogs(SecOTAPKIRef otapkiRef) {
1951 CFDictionaryRef result = NULL;
1952 if (NULL == otapkiRef) {
1956 #if !TARGET_OS_BRIDGE
1957 /* Trigger periodic background MA checks in system trustd
1958 * We also check on trustd launch and listen for notifications. */
1959 TriggerPeriodicOTATrustAssetChecks(kOTABackgroundQueue);
1962 result = otapkiRef->_trustedCTLogs;
1963 CFRetainSafe(result);
1967 CFDictionaryRef SecOTAPKICopyNonTlsTrustedCTLogs(SecOTAPKIRef otapkiRef) {
1968 CFDictionaryRef result = NULL;
1969 if (NULL == otapkiRef) {
1973 #if !TARGET_OS_BRIDGE
1974 /* Trigger periodic background MA checks in system trustd
1975 * We also check on trustd launch and listen for notifications. */
1976 TriggerPeriodicOTATrustAssetChecks(kOTABackgroundQueue);
1979 result = otapkiRef->_nonTlsTrustedCTLogs;
1980 CFRetainSafe(result);
1984 CFURLRef SecOTAPKICopyPinningList(SecOTAPKIRef otapkiRef) {
1985 if (NULL == otapkiRef) {
1989 return CFRetainSafe(otapkiRef->_pinningList);
1993 /* Returns an array of certificate data (CFDataRef) */
1994 CFArrayRef SecOTAPKICopyEscrowCertificates(uint32_t escrowRootType, SecOTAPKIRef otapkiRef) {
1995 CFMutableArrayRef result = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
1996 if (NULL == otapkiRef) {
2000 switch (escrowRootType) {
2001 // Note: we shouldn't be getting called to return baseline roots,
2002 // since this function vends production roots by definition.
2003 case kSecCertificateBaselineEscrowRoot:
2004 case kSecCertificateProductionEscrowRoot:
2005 case kSecCertificateBaselineEscrowBackupRoot:
2006 case kSecCertificateProductionEscrowBackupRoot:
2007 if (otapkiRef->_escrowCertificates) {
2008 CFArrayRef escrowCerts = otapkiRef->_escrowCertificates;
2009 CFArrayAppendArray(result, escrowCerts, CFRangeMake(0, CFArrayGetCount(escrowCerts)));
2012 case kSecCertificateBaselineEscrowEnrollmentRoot:
2013 case kSecCertificateProductionEscrowEnrollmentRoot:
2014 if (otapkiRef->_escrowCertificates) {
2015 // for enrollment purposes, exclude the v100 root
2016 static const unsigned char V100EscrowRoot[] = {
2017 0x65,0x5C,0xB0,0x3C,0x39,0x3A,0x32,0xA6,0x0B,0x96,
2018 0x40,0xC0,0xCA,0x73,0x41,0xFD,0xC3,0x9E,0x96,0xB3
2020 CFArrayRef escrowCerts = otapkiRef->_escrowCertificates;
2021 CFIndex idx, count = CFArrayGetCount(escrowCerts);
2022 for (idx=0; idx < count; idx++) {
2023 CFDataRef tmpData = (CFDataRef) CFArrayGetValueAtIndex(escrowCerts, idx);
2024 SecCertificateRef tmpCert = (tmpData) ? SecCertificateCreateWithData(NULL, tmpData) : NULL;
2025 CFDataRef sha1Hash = (tmpCert) ? SecCertificateGetSHA1Digest(tmpCert) : NULL;
2026 const uint8_t *dp = (sha1Hash) ? CFDataGetBytePtr(sha1Hash) : NULL;
2027 if (!(dp && !memcmp(V100EscrowRoot, dp, sizeof(V100EscrowRoot))) && tmpData) {
2028 CFArrayAppendValue(result, tmpData);
2030 CFReleaseSafe(tmpCert);
2034 case kSecCertificateBaselinePCSEscrowRoot:
2035 case kSecCertificateProductionPCSEscrowRoot:
2036 if (otapkiRef->_escrowPCSCertificates) {
2037 CFArrayRef escrowPCSCerts = otapkiRef->_escrowPCSCertificates;
2038 CFArrayAppendArray(result, escrowPCSCerts, CFRangeMake(0, CFArrayGetCount(escrowPCSCerts)));
2049 CFDictionaryRef SecOTAPKICopyEVPolicyToAnchorMapping(SecOTAPKIRef otapkiRef) {
2050 if (NULL == otapkiRef) {
2054 return CFRetainSafe(otapkiRef->_evPolicyToAnchorMapping);
2058 CFDictionaryRef SecOTAPKICopyAnchorLookupTable(SecOTAPKIRef otapkiRef) {
2059 if (NULL == otapkiRef) {
2063 return CFRetainSafe(otapkiRef->_anchorLookupTable);
2066 const char* SecOTAPKIGetAnchorTable(SecOTAPKIRef otapkiRef) {
2067 if (NULL == otapkiRef) {
2071 return otapkiRef->_anchorTable;
2074 const char* SecOTAPKIGetValidDatabaseSnapshot(SecOTAPKIRef otapkiRef) {
2075 if (NULL == otapkiRef) {
2079 return otapkiRef->_validDatabaseSnapshot;
2082 CFIndex SecOTAPKIGetValidSnapshotVersion(SecOTAPKIRef otapkiRef) {
2083 if (NULL == otapkiRef) {
2087 return otapkiRef->_validSnapshotVersion;
2090 CFIndex SecOTAPKIGetValidSnapshotFormat(SecOTAPKIRef otapkiRef) {
2091 if (NULL == otapkiRef) {
2095 return otapkiRef->_validSnapshotFormat;
2098 uint64_t SecOTAPKIGetTrustStoreVersion(SecOTAPKIRef otapkiRef) {
2099 if (NULL == otapkiRef) {
2103 return otapkiRef->_trustStoreVersion;
2106 uint64_t SecOTAPKIGetAssetVersion(SecOTAPKIRef otapkiRef) {
2107 if (NULL == otapkiRef) {
2111 return otapkiRef->_assetVersion;
2114 CFDateRef SecOTAPKICopyLastAssetCheckInDate(SecOTAPKIRef otapkiRef) {
2115 if (NULL == otapkiRef) {
2118 return CFRetainSafe(otapkiRef->_lastAssetCheckIn);
2121 bool SecOTAPKIAssetStalenessLessThanSeconds(SecOTAPKIRef otapkiRef, CFTimeInterval seconds) {
2122 if (NULL == otapkiRef) {
2126 bool result = false;
2127 CFDateRef lastCheckIn = CFRetainSafe(otapkiRef->_lastAssetCheckIn);
2128 if (isDate(lastCheckIn) && (fabs([(__bridge NSDate *)lastCheckIn timeIntervalSinceNow]) < seconds)) {
2131 CFReleaseNull(lastCheckIn);
2135 NSNumber *SecOTAPKIGetSamplingRateForEvent(SecOTAPKIRef otapkiRef, NSString *eventName) {
2136 if (NULL == otapkiRef) {
2140 #if !TARGET_OS_BRIDGE
2141 /* Trigger periodic background MA checks in system trustd
2142 * We also check on trustd launch and listen for notifications. */
2143 TriggerPeriodicOTATrustAssetChecks(kOTABackgroundQueue);
2146 if (otapkiRef->_eventSamplingRates) {
2147 CFTypeRef value = CFDictionaryGetValue(otapkiRef->_eventSamplingRates, (__bridge CFStringRef)eventName);
2148 if (isNumberOfType(value, kCFNumberSInt64Type)) {
2149 return (__bridge NSNumber *)value;
2155 CFArrayRef SecOTAPKICopyAppleCertificateAuthorities(SecOTAPKIRef otapkiRef) {
2156 if (NULL == otapkiRef) {
2160 #if !TARGET_OS_BRIDGE
2161 /* Trigger periodic background MA checks in system trustd
2162 * We also check on trustd launch and listen for notifications. */
2163 TriggerPeriodicOTATrustAssetChecks(kOTABackgroundQueue);
2166 return CFRetainSafe(otapkiRef->_appleCAs);
2169 bool SecOTAPKIKillSwitchEnabled(SecOTAPKIRef otapkiRef, CFStringRef key) {
2170 if (NULL == otapkiRef || NULL == key) {
2173 if (CFEqualSafe(key, kOTAPKIKillSwitchCT)) {
2174 return otapkiRef->_ctKillSwitch;
2175 } else if (CFEqualSafe(key, kOTAPKIKillSwitchNonTLSCT)) {
2176 return otapkiRef->_nonTlsCtKillSwitch;
2181 /* Returns an array of certificate data (CFDataRef) */
2182 CFArrayRef SecOTAPKICopyCurrentEscrowCertificates(uint32_t escrowRootType, CFErrorRef* error) {
2183 SecOTAPKIRef otapkiref = SecOTAPKICopyCurrentOTAPKIRef();
2184 if (NULL == otapkiref) {
2185 SecError(errSecInternal, error, CFSTR("Unable to get the current OTAPKIRef"));
2189 CFArrayRef result = SecOTAPKICopyEscrowCertificates(escrowRootType, otapkiref);
2190 CFRelease(otapkiref);
2192 if (NULL == result) {
2193 SecError(errSecInternal, error, CFSTR("Could not get escrow certificates from the current OTAPKIRef"));
2198 static CF_RETURNS_RETAINED CFDictionaryRef convertDataKeysToBase64Strings(CFDictionaryRef CF_CONSUMED dictionaryWithDataKeys) {
2200 NSMutableDictionary *result = [NSMutableDictionary dictionaryWithCapacity:((__bridge NSDictionary*)dictionaryWithDataKeys).count];
2201 NSDictionary <NSData *, id> *input = CFBridgingRelease(dictionaryWithDataKeys);
2202 for (NSData *key in input) {
2203 id obj = [input objectForKey:key];
2204 NSString *base64Key = [key base64EncodedStringWithOptions:0];
2205 result[base64Key] = obj;
2207 return CFBridgingRetain(result);
2211 /* Returns an dictionary of dictionaries for currently trusted CT logs, indexed by the base64-encoded LogID */
2212 CFDictionaryRef SecOTAPKICopyCurrentTrustedCTLogs(CFErrorRef* error) {
2213 SecOTAPKIRef otapkiref = SecOTAPKICopyCurrentOTAPKIRef();
2214 if (NULL == otapkiref) {
2215 SecError(errSecInternal, error, CFSTR("Unable to get the current OTAPKIRef"));
2219 CFDictionaryRef result = convertDataKeysToBase64Strings(SecOTAPKICopyTrustedCTLogs(otapkiref));
2220 CFReleaseSafe(otapkiref);
2222 if (NULL == result) {
2223 SecError(errSecInternal, error, CFSTR("Could not get CT logs from the current OTAPKIRef"));
2228 /* Returns a dictionary for the CT log matching specified LogID */
2229 CFDictionaryRef SecOTAPKICopyCTLogForKeyID(CFDataRef keyID, CFErrorRef* error) {
2230 SecOTAPKIRef otapkiref = SecOTAPKICopyCurrentOTAPKIRef();
2231 if (NULL == otapkiref) {
2232 SecError(errSecInternal, error, CFSTR("Unable to get the current OTAPKIRef"));
2236 /* Get the log lists */
2237 CFDictionaryRef trustedTlsLogs = SecOTAPKICopyTrustedCTLogs(otapkiref);
2238 CFDictionaryRef trustedNonTlsLogs = SecOTAPKICopyNonTlsTrustedCTLogs(otapkiref);
2239 CFReleaseNull(otapkiref);
2240 if (!trustedTlsLogs || !trustedNonTlsLogs) {
2241 CFReleaseNull(trustedTlsLogs);
2242 CFReleaseNull(trustedNonTlsLogs);
2247 CFDictionaryRef logDict = CFDictionaryGetValue(trustedTlsLogs, keyID);
2249 logDict = CFDictionaryGetValue(trustedNonTlsLogs, keyID);
2251 CFRetainSafe(logDict);
2252 CFReleaseNull(trustedTlsLogs);
2253 CFReleaseNull(trustedNonTlsLogs);
2257 uint64_t SecOTAPKIGetCurrentTrustStoreVersion(CFErrorRef* error){
2258 SecOTAPKIRef otapkiref = SecOTAPKICopyCurrentOTAPKIRef();
2259 if (NULL == otapkiref) {
2260 SecError(errSecInternal, error, CFSTR("Unable to get the current OTAPKIRef"));
2264 uint64_t result = otapkiref->_trustStoreVersion;
2265 CFReleaseNull(otapkiref);
2269 uint64_t SecOTAPKIGetCurrentAssetVersion(CFErrorRef* error) {
2270 SecOTAPKIRef otapkiref = SecOTAPKICopyCurrentOTAPKIRef();
2271 if (NULL == otapkiref) {
2272 SecError(errSecInternal, error, CFSTR("Unable to get the current OTAPKIRef"));
2276 uint64_t result = otapkiref->_assetVersion;
2277 CFReleaseNull(otapkiref);
2281 uint64_t SecOTASecExperimentGetCurrentAssetVersion(CFErrorRef* error) {
2282 SecOTAPKIRef otapkiref = SecOTAPKICopyCurrentOTAPKIRef();
2283 if (NULL == otapkiref) {
2284 SecError(errSecInternal, error, CFSTR("Unable to get the current OTAPKIRef"));
2288 uint64_t result = otapkiref->_secExperimentAssetVersion;
2289 CFReleaseNull(otapkiref);
2293 uint64_t SecOTAPKIResetCurrentAssetVersion(CFErrorRef* error) {
2294 uint64_t system_version = GetSystemVersion((__bridge CFStringRef)kOTATrustContentVersionKey);
2296 dispatch_sync(kOTAQueue, ^{
2297 kCurrentOTAPKIRef->_assetVersion = system_version;
2298 CFReleaseNull(kCurrentOTAPKIRef->_lastAssetCheckIn);
2299 kCurrentOTAPKIRef->_lastAssetCheckIn = NULL;
2302 #if !TARGET_OS_BRIDGE
2303 DeleteAssetFromDisk();
2305 return system_version;
2308 uint64_t SecOTAPKISignalNewAsset(CFErrorRef* error) {
2309 NSError *nserror = nil;
2310 uint64_t version = 0;
2311 #if !TARGET_OS_BRIDGE
2312 if (SecOTAPKIIsSystemTrustd()) {
2313 if (!DownloadOTATrustAsset(NO, YES, OTATrustMobileAssetType, &nserror) && error) {
2314 *error = CFRetainSafe((__bridge CFErrorRef)nserror);
2317 SecError(errSecServiceNotAvailable, error, CFSTR("This function may only be performed by the system trustd."));
2319 version = GetAssetVersion(nil);
2321 SecError(errSecUnsupportedService, error, CFSTR("This function is not available on this platform"));
2322 version = GetAssetVersion(error);
2327 uint64_t SecOTASecExperimentGetNewAsset(CFErrorRef* error) {
2328 NSError *nserror = nil;
2329 #if !TARGET_OS_BRIDGE
2330 if (SecOTAPKIIsSystemTrustd()) {
2331 if (!DownloadOTATrustAsset(NO, YES, OTASecExperimentMobileAssetType, &nserror) && error) {
2332 *error = CFRetainSafe((__bridge CFErrorRef)nserror);
2335 SecError(errSecServiceNotAvailable, error, CFSTR("This function may only be performed by the system trustd."));
2338 SecError(errSecUnsupportedService, error, CFSTR("This function is not available on this platform"));
2340 return SecOTASecExperimentGetCurrentAssetVersion(error);
2343 CFDictionaryRef SecOTASecExperimentCopyAsset(CFErrorRef* error) {
2344 SecOTAPKIRef otapkiRef = SecOTAPKICopyCurrentOTAPKIRef();
2345 if (NULL == otapkiRef) {
2346 secnotice("OTATrust", "Null otapkiref");
2349 CFDictionaryRef asset = NULL;
2350 if (otapkiRef->_secExperimentConfig) {
2351 asset = CFRetainSafe(otapkiRef->_secExperimentConfig);
2352 secnotice("OTATrust", "asset found");
2354 secnotice("OTATrust", "asset NULL");
2356 CFReleaseNull(otapkiRef);