]> git.saurik.com Git - apple/security.git/blob - OSX/sec/securityd/OTATrustUtilities.m
Security-58286.260.20.tar.gz
[apple/security.git] / OSX / sec / securityd / OTATrustUtilities.m
1 /*
2 * Copyright (c) 2003-2018 Apple Inc. All Rights Reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 *
23 * OTATrustUtilities.m
24 */
25
26 #import <Foundation/Foundation.h>
27 #include "OTATrustUtilities.h"
28
29 #include <errno.h>
30 #include <fcntl.h>
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <unistd.h>
35 #include <dirent.h>
36 #include <copyfile.h>
37 #include <sys/syslimits.h>
38 #include <sys/mman.h>
39 #include <sys/stat.h>
40 #include <CoreFoundation/CoreFoundation.h>
41 #include <ftw.h>
42 #include "SecFramework.h"
43 #include <pthread.h>
44 #include <sys/param.h>
45 #include <stdlib.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 <CommonCrypto/CommonDigest.h>
55 #include <securityd/SecPinningDb.h>
56
57 #if !TARGET_OS_BRIDGE
58 #import <MobileAsset/MAAsset.h>
59 #import <MobileAsset/MAAssetQuery.h>
60 #include <notify.h>
61 #include <utilities/sec_action.h>
62 #include <utilities/SecFileLocations.h>
63 #import <securityd/SecTrustLoggingServer.h>
64 #endif
65
66 #if TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR
67 #import <MobileKeyBag/MobileKeyBag.h>
68 #include <System/sys/content_protection.h>
69 #endif
70
71 #if TARGET_OS_OSX
72 #import <MobileAsset/MobileAsset.h>
73 #endif
74
75 static inline bool isNSNumber(id nsType) {
76 return nsType && [nsType isKindOfClass:[NSNumber class]];
77 }
78
79 static inline bool isNSDictionary(id nsType) {
80 return nsType && [nsType isKindOfClass:[NSDictionary class]];
81 }
82
83 static inline bool isNSArray(id nsType) {
84 return nsType && [nsType isKindOfClass:[NSArray class]];
85 }
86
87 static inline bool isNSDate(id nsType) {
88 return nsType && [nsType isKindOfClass:[NSDate class]];
89 }
90
91 #define SECURITYD_ROLE_ACCOUNT 64
92 #define ROOT_ACCOUNT 0
93
94 bool SecOTAPKIIsSystemTrustd() {
95 static bool result = false;
96 static dispatch_once_t onceToken;
97 dispatch_once(&onceToken, ^{
98 #ifdef NO_SERVER
99 // Test app running as securityd
100 #elif TARGET_OS_IPHONE
101 if (getuid() == SECURITYD_ROLE_ACCOUNT)
102 #else
103 if (getuid() == ROOT_ACCOUNT)
104 #endif
105 {
106 result = true;
107 }
108 });
109 return result;
110 }
111
112 /* MARK: - */
113 /* MARK: System Trust Store */
114 static CFStringRef kSecSystemTrustStoreBundlePath = CFSTR("/System/Library/Security/Certificates.bundle");
115
116 CFGiblisGetSingleton(CFBundleRef, SecSystemTrustStoreGetBundle, bundle, ^{
117 CFStringRef bundlePath = NULL;
118 #if TARGET_OS_SIMULATOR
119 char *simulatorRoot = getenv("SIMULATOR_ROOT");
120 if (simulatorRoot)
121 bundlePath = CFStringCreateWithFormat(NULL, NULL, CFSTR("%s%@"), simulatorRoot, kSecSystemTrustStoreBundlePath);
122 #endif
123 if (!bundlePath)
124 bundlePath = CFRetainSafe(kSecSystemTrustStoreBundlePath);
125 CFURLRef url = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, bundlePath, kCFURLPOSIXPathStyle, true);
126 *bundle = (url) ? CFBundleCreate(kCFAllocatorDefault, url) : NULL;
127 CFReleaseSafe(url);
128 CFReleaseSafe(bundlePath);
129 })
130
131 static CFURLRef SecSystemTrustStoreCopyResourceURL(CFStringRef resourceName,
132 CFStringRef resourceType, CFStringRef subDirName) {
133 CFURLRef url = NULL;
134 CFBundleRef bundle = SecSystemTrustStoreGetBundle();
135 if (bundle) {
136 url = CFBundleCopyResourceURL(bundle, resourceName,
137 resourceType, subDirName);
138 }
139 if (!url) {
140 secwarning("resource: %@.%@ in %@ not found", resourceName,
141 resourceType, subDirName);
142 }
143 return url;
144 }
145
146 static NSURL *SecSystemTrustStoreCopyResourceNSURL(NSString *resourceFileName) {
147 CFBundleRef bundle = SecSystemTrustStoreGetBundle();
148 if (!bundle) {
149 return NULL;
150 }
151 NSURL *resourceDir = CFBridgingRelease(CFBundleCopyResourcesDirectoryURL(bundle));
152 if (!resourceDir) {
153 return NULL;
154 }
155 NSURL *fileURL = [NSURL URLWithString:resourceFileName
156 relativeToURL:resourceDir];
157 if (!fileURL) {
158 secwarning("resource: %@ not found", resourceFileName);
159 }
160 return fileURL;
161 }
162
163 static CFDataRef SecSystemTrustStoreCopyResourceContents(CFStringRef resourceName,
164 CFStringRef resourceType, CFStringRef subDirName) {
165 CFURLRef url = SecSystemTrustStoreCopyResourceURL(resourceName, resourceType, subDirName);
166 CFDataRef data = NULL;
167 if (url) {
168 SInt32 error;
169 if (!CFURLCreateDataAndPropertiesFromResource(kCFAllocatorDefault,
170 url, &data, NULL, NULL, &error)) {
171 secwarning("read: %ld", (long) error);
172 }
173 CFRelease(url);
174 }
175 return data;
176 }
177
178 /* MARK: - */
179 /* MARK: MobileAsset Updates */
180 // MARK: Forward Declarations
181 static uint64_t GetAssetVersion(CFErrorRef *error);
182 static uint64_t GetSystemVersion(CFStringRef key);
183 #if !TARGET_OS_BRIDGE
184 static BOOL UpdateFromAsset(NSURL *localURL, NSNumber *asset_version, NSError **error);
185 static BOOL UpdateOTACheckInDate(void);
186 static void UpdateKillSwitch(NSString *key, bool value);
187 #endif
188 #if TARGET_OS_IPHONE
189 static void TriggerUnlockNotificationOTATrustAssetCheck(dispatch_queue_t queue);
190 #endif
191
192 /* This queue is for fetching changes to the OTAPKI reference or otherwise doing maintenance activities */
193 static dispatch_queue_t kOTABackgroundQueue = NULL;
194
195 // MARK: Constants
196 NSString *kOTATrustContentVersionKey = @"MobileAssetContentVersion";
197 NSString *kOTATrustLastCheckInKey = @"MobileAssetLastCheckIn";
198 NSString *kOTATrustContextFilename = @"OTAPKIContext.plist";
199 NSString *kOTATrustTrustedCTLogsFilename = @"TrustedCTLogs.plist";
200 NSString *kOTATrustAnalyticsSamplingRatesFilename = @"AnalyticsSamplingRates.plist";
201 NSString *kOTATrustAppleCertifcateAuthoritiesFilename = @"AppleCertificateAuthorities.plist";
202 NSString *kOTATrustCTKillSwitch = @"CTKillSwitch";
203
204 const CFStringRef kOTAPKIKillSwitchCT = CFSTR("CTKillSwitch");
205
206 #if !TARGET_OS_BRIDGE
207 const NSString *OTATrustMobileAssetType = @"com.apple.MobileAsset.PKITrustSupplementals";
208 #define kOTATrustMobileAssetNotification "com.apple.MobileAsset.PKITrustSupplementals.cached-metadata-updated"
209 #define kOTATrustOnDiskAssetNotification "com.apple.trustd.asset-updated"
210 #define kOTATrustCheckInNotification "com.apple.trustd.asset-check-in"
211 #define kOTATrustKillSwitchNotification "com.apple.trustd.kill-switch"
212 const NSUInteger OTATrustMobileAssetCompatibilityVersion = 1;
213 #define kOTATrustDefaultUpdatePeriod 60*60*12 // 12 hours
214 #define kOTATrustMinimumUpdatePeriod 60*5 // 5 min
215
216 #if TARGET_OS_OSX
217 const CFStringRef kSecSUPrefDomain = CFSTR("com.apple.SoftwareUpdate");
218 const CFStringRef kSecSUScanPrefConfigDataInstallKey = CFSTR("ConfigDataInstall");
219 #endif
220
221 // MARK: Helper functions
222 typedef enum {
223 OTATrustLogLevelNone,
224 OTATrustLogLevelDebug,
225 OTATrustLogLevelInfo,
226 OTATrustLogLevelNotice,
227 OTATrustLogLevelError,
228 } OTATrustLogLevel;
229
230 static void MakeOTATrustError(NSError **error, OTATrustLogLevel level, NSErrorDomain errDomain, OSStatus errCode, NSString *format,...) NS_FORMAT_FUNCTION(5,6);
231 static void MakeOTATrustErrorWithAttributes(NSError **error, OTATrustLogLevel level, NSErrorDomain errDomain, OSStatus errCode,
232 NSDictionary *attributes, NSString *format,...)
233 NS_FORMAT_FUNCTION(6,7);
234
235 static void MakeOTATrustErrorArgs(NSError **error, OTATrustLogLevel level, NSErrorDomain errDomain, OSStatus errCode,
236 NSDictionary *attributes, NSString *format, va_list arguments)
237 NS_FORMAT_FUNCTION(6,0);
238
239 static void LogLocally(OTATrustLogLevel level, NSString *errorString) {
240 switch (level) {
241 case OTATrustLogLevelNone:
242 break;
243 case OTATrustLogLevelDebug:
244 secdebug("OTATrust", "%@", errorString);
245 break;
246 case OTATrustLogLevelInfo:
247 secinfo("OTATrust", "%@", errorString);
248 break;
249 case OTATrustLogLevelNotice:
250 secnotice("OTATrust", "%@", errorString);
251 break;
252 case OTATrustLogLevelError:
253 secerror("OTATrust: %@", errorString);
254 break;
255 }
256 }
257
258 static void LogRemotelyWithAttributes(OTATrustLogLevel level, NSError **error, NSDictionary *attributes) {
259 #if ENABLE_TRUSTD_ANALYTICS
260 /* only report errors and notices */
261 if (error && level == OTATrustLogLevelError) {
262 [[TrustdHealthAnalytics logger] logResultForEvent:TrustdHealthAnalyticsEventOTAPKIEvent hardFailure:YES result:*error withAttributes:attributes];
263 } else if (error && level == OTATrustLogLevelNotice) {
264 [[TrustdHealthAnalytics logger] logResultForEvent:TrustdHealthAnalyticsEventOTAPKIEvent hardFailure:NO result:*error withAttributes:attributes];
265 }
266 #endif // ENABLE_TRUSTD_ANALYTICS
267 }
268
269 static void LogRemotely(OTATrustLogLevel level, NSError **error) {
270 LogRemotelyWithAttributes(level, error, nil);
271 }
272
273 static void MakeOTATrustErrorArgs(NSError **error, OTATrustLogLevel level, NSErrorDomain errDomain, OSStatus errCode,
274 NSDictionary *attributes, NSString *format, va_list args) {
275 NSString *formattedString = nil;
276 if (format) {
277 formattedString = [[NSString alloc] initWithFormat:format arguments:args];
278 }
279
280 NSError *localError = nil;
281 NSMutableDictionary *userInfo = [[NSMutableDictionary alloc] init];
282 if (format) {
283 [userInfo setObject:formattedString forKey:NSLocalizedDescriptionKey];
284 }
285 if (error && *error) {
286 userInfo[NSUnderlyingErrorKey] = *error;
287 }
288 localError = [NSError errorWithDomain:errDomain
289 code:errCode
290 userInfo:userInfo];
291
292 LogLocally(level, formattedString);
293 LogRemotelyWithAttributes(level, &localError, attributes);
294 if (error) { *error = localError; }
295 }
296
297 static void MakeOTATrustErrorWithAttributes(NSError **error, OTATrustLogLevel level, NSErrorDomain errDomain, OSStatus errCode,
298 NSDictionary *attributes, NSString *format,...) {
299 va_list args;
300 va_start(args, format);
301 MakeOTATrustErrorArgs(error, level, errDomain, errCode, attributes, format, args);
302 va_end(args);
303 }
304
305 static void MakeOTATrustError(NSError **error, OTATrustLogLevel level, NSErrorDomain errDomain, OSStatus errCode, NSString *format,...) {
306 va_list args;
307 va_start(args, format);
308 MakeOTATrustErrorArgs(error, level, errDomain, errCode, nil, format, args);
309 va_end(args);
310 }
311
312 static BOOL CanCheckMobileAsset(void) {
313 BOOL result = YES;
314 #if TARGET_OS_OSX
315 /* Check the user's SU preferences to determine if "Install system data files" is off */
316 if (!CFPreferencesSynchronize(kSecSUPrefDomain, kCFPreferencesAnyUser, kCFPreferencesCurrentHost)) {
317 secerror("OTATrust: unable to synchronize SoftwareUpdate prefs");
318 return NO;
319 }
320
321 id value = nil;
322 if (CFPreferencesAppValueIsForced(kSecSUScanPrefConfigDataInstallKey, kSecSUPrefDomain)) {
323 value = CFBridgingRelease(CFPreferencesCopyAppValue(kSecSUScanPrefConfigDataInstallKey, kSecSUPrefDomain));
324 } else {
325 value = CFBridgingRelease(CFPreferencesCopyValue(kSecSUScanPrefConfigDataInstallKey, kSecSUPrefDomain,
326 kCFPreferencesAnyUser, kCFPreferencesCurrentHost));
327 }
328 if (isNSNumber(value)) {
329 result = [value boolValue];
330 }
331
332 if (!result) { secnotice("OTATrust", "User has disabled system data installation."); }
333
334 /* MobileAsset.framework isn't mastered into the BaseSystem. Check that the MA classes are linked. */
335 if (![ASAssetQuery class] || ![ASAsset class] || ![MAAssetQuery class] || ![MAAsset class]) {
336 secnotice("OTATrust", "Weak-linked MobileAsset framework missing.");
337 result = NO;
338 }
339 #endif
340 return result;
341 }
342
343 static BOOL ShouldUpdateWithAsset(NSNumber *asset_version) {
344 if (![asset_version isKindOfClass:[NSNumber class]]) {
345 return NO;
346 }
347 CFErrorRef error = nil;
348 uint64_t current_version = SecOTAPKIGetCurrentAssetVersion(&error);
349 if (error) {
350 CFReleaseNull(error);
351 return NO;
352 }
353 if ([asset_version compare:[NSNumber numberWithUnsignedLongLong:current_version]] == NSOrderedDescending) {
354 return YES;
355 }
356 return NO;
357 }
358
359 static bool verify_create_path(const char *path) {
360 int ret = mkpath_np(path, 0755);
361 if (!(ret == 0 || ret == EEXIST)) {
362 secerror("could not create path: %s (%s)", path, strerror(ret));
363 return false;
364 }
365 return true;
366 }
367
368 // MARK: File management functions
369 static NSURL *GetAssetFileURL(NSString *filename) {
370 /* Make sure the /Library/Keychains directory is there */
371 #if TARGET_OS_IPHONE
372 NSURL *keychainsDirectory = CFBridgingRelease(SecCopyURLForFileInKeychainDirectory(nil));
373 #else
374 NSURL *keychainsDirectory = [NSURL fileURLWithFileSystemRepresentation:"/Library/Keychains/" isDirectory:YES relativeToURL:nil];
375 #endif
376 NSURL *directory = [keychainsDirectory URLByAppendingPathComponent:@"SupplementalsAssets/" isDirectory:YES];
377 if (!verify_create_path([directory fileSystemRepresentation])) {
378 return nil;
379 }
380
381 if (filename) {
382 return [directory URLByAppendingPathComponent:filename];
383 } else {
384 return directory;
385 }
386 }
387
388 static void DeleteFileWithName(NSString *filename) {
389 NSURL *fileURL = GetAssetFileURL(filename);
390 if (remove([fileURL fileSystemRepresentation]) == -1) {
391 int error = errno;
392 if (error != ENOENT) {
393 secnotice("OTATrust", "failed to remove %@: %s", fileURL, strerror(error));
394 }
395 }
396 }
397
398 static BOOL UpdateOTAContextOnDisk(NSString *key, id value, NSError **error) {
399 if (SecOTAPKIIsSystemTrustd()) {
400 /* Get current context, if applicable, and update/add key/value */
401 NSURL *otaContextFile = GetAssetFileURL(kOTATrustContextFilename);
402 NSDictionary *currentContext = [NSDictionary dictionaryWithContentsOfURL:otaContextFile];
403 NSMutableDictionary *newContext = nil;
404 if (currentContext) {
405 newContext = [currentContext mutableCopy];
406 } else {
407 newContext = [NSMutableDictionary dictionary];
408 }
409 newContext[key] = value;
410
411 /* Write dictionary to disk */
412 if (![newContext writeToURL:otaContextFile error:error]) {
413 secerror("OTATrust: unable to write OTA Context to disk: %@", error ? *error : nil);
414 LogRemotely(OTATrustLogLevelError, error);
415 return NO;
416 }
417 return YES;
418 }
419 return NO;
420 }
421
422 static BOOL UpdateOTAContext(NSNumber *asset_version, NSError **error) {
423 return UpdateOTAContextOnDisk(kOTATrustContentVersionKey, asset_version, error) && UpdateOTACheckInDate();
424 }
425
426 /* Delete only the asset data but not the check-in time. */
427 static void DeleteOldAssetData(void) {
428 if (SecOTAPKIIsSystemTrustd()) {
429 /* Delete the asset files, but keep the check-in time and version */
430 DeleteFileWithName(kOTATrustTrustedCTLogsFilename);
431 DeleteFileWithName(kOTATrustAnalyticsSamplingRatesFilename);
432 DeleteFileWithName(kOTATrustAppleCertifcateAuthoritiesFilename);
433 }
434 }
435
436 /* Delete all asset data, intended for error cases */
437 static BOOL DeleteAssetFromDisk(void) {
438 if (SecOTAPKIIsSystemTrustd()) {
439 DeleteOldAssetData();
440 DeleteFileWithName(kOTATrustContextFilename);
441 return YES;
442 }
443 return NO;
444 }
445
446 #if TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR
447 static bool ChangeFileProtectionToClassD(NSURL *fileURL, NSError **error) {
448 BOOL result = YES;
449 int file_fd = open([fileURL fileSystemRepresentation], O_RDONLY);
450 if (file_fd) {
451 int retval = fcntl(file_fd, F_SETPROTECTIONCLASS, PROTECTION_CLASS_D);
452 if (retval < 0) {
453 MakeOTATrustError(error, OTATrustLogLevelError, NSPOSIXErrorDomain, errno,
454 @"set proteciton class error for asset %d: %s", errno, strerror(errno));
455 result = NO;
456 }
457 close(file_fd);
458 } else {
459 MakeOTATrustError(error, OTATrustLogLevelError, NSPOSIXErrorDomain, errno,
460 @"open error for asset %d: %s", errno, strerror(errno));
461 result = NO;
462 }
463 return result;
464 }
465 #endif
466
467 static BOOL CopyFileToDisk(NSString *filename, NSURL *localURL, NSError **error) {
468 if (SecOTAPKIIsSystemTrustd()) {
469 NSURL *toFileURL = GetAssetFileURL(filename);
470 secdebug("OTATrust", "will copy asset file data from \"%@\"", localURL);
471 copyfile_state_t state = copyfile_state_alloc();
472 int retval = copyfile([localURL fileSystemRepresentation], [toFileURL fileSystemRepresentation],
473 state, COPYFILE_DATA);
474 copyfile_state_free(state);
475 if (retval < 0) {
476 MakeOTATrustError(error, OTATrustLogLevelError, NSPOSIXErrorDomain, errno,
477 @"copyfile error for asset %d: %s", errno, strerror(errno));
478 return NO;
479 } else {
480 /* make sure we can read this file before first unlock */
481 #if TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR
482 return ChangeFileProtectionToClassD(toFileURL, error);
483 #else
484 return YES;
485 #endif
486 }
487 }
488 return NO;
489 }
490
491 static void GetKillSwitchAttributes(NSDictionary *attributes) {
492 bool killSwitchEnabled = false;
493
494 // CT Kill Switch
495 NSNumber *ctKillSwitch = [attributes objectForKey:kOTATrustCTKillSwitch];
496 if (isNSNumber(ctKillSwitch)) {
497 NSError *error = nil;
498 UpdateOTAContextOnDisk(kOTATrustCTKillSwitch, ctKillSwitch, &error);
499 UpdateKillSwitch(kOTATrustCTKillSwitch, [ctKillSwitch boolValue]);
500 secnotice("OTATrust", "got CT kill switch = %d", [ctKillSwitch boolValue]);
501 killSwitchEnabled = true;
502 }
503
504 /* Other kill switches TBD.
505 * When adding one, make sure to add to the Analytics Samplers since these kill switches
506 * are installed before the full asset is downloaded and installed. (A device can have the
507 * kill switches without having the asset version that contained them.) */
508
509 // notify the other trustds if any kill switch was read
510 if (SecOTAPKIIsSystemTrustd() && killSwitchEnabled) {
511 notify_post(kOTATrustKillSwitchNotification);
512 }
513 }
514
515 // MARK: Fetch and Update Functions
516 #if TARGET_OS_IPHONE
517 static NSNumber *UpdateAndPurgeAsset(MAAsset *asset, NSNumber *asset_version, NSError **error) {
518 if (SecPinningDbUpdateFromURL([asset getLocalFileUrl], error) &&
519 UpdateFromAsset([asset getLocalFileUrl], asset_version, error)) {
520 secnotice("OTATrust", "finished update to version %@ from installed asset. purging asset.", asset_version);
521 #if ENABLE_TRUSTD_ANALYTICS
522 [[TrustdHealthAnalytics logger] logSuccessForEventNamed:TrustdHealthAnalyticsEventOTAPKIEvent];
523 #endif // ENABLE_TRUSTD_ANALYTICS
524 [asset purge:^(MAPurgeResult purge_result) {
525 if (purge_result != MAPurgeSucceeded) {
526 secerror("OTATrust: purge failed: %ld", (long)purge_result);
527 }
528 }];
529 return asset_version;
530 } else {
531 MakeOTATrustError(error, OTATrustLogLevelError, NSOSStatusErrorDomain, errSecCallbackFailed,
532 @"Failed to install new asset version %@ from %@", asset_version, [asset getLocalFileUrl]);
533 return nil;
534 }
535 }
536
537 static MADownloadOptions *GetMADownloadOptions(BOOL wait) {
538 /* default behavior */
539 MADownloadOptions *options = [[MADownloadOptions alloc] init];
540 options.discretionary = YES;
541 options.allowsCellularAccess = NO;
542
543 /* If an XPC interface is waiting on this, all expenses allowed */
544 if (wait) {
545 options.discretionary = NO;
546 options.allowsCellularAccess = YES;
547 return options;
548 }
549
550 /* If last asset check-in was too long ago, use more expensive options */
551 SecOTAPKIRef otapkiref = SecOTAPKICopyCurrentOTAPKIRef();
552 if (!SecOTAPKIAssetStalenessLessThanSeconds(otapkiref, kSecOTAPKIAssetStalenessWarning)) {
553 secnotice("OTATrust", "Asset staleness state: warning");
554 options.allowsCellularAccess = YES;
555 options.discretionary = NO;
556 } else if (!SecOTAPKIAssetStalenessLessThanSeconds(otapkiref, kSecOTAPKIAssetStalenessAtRisk)) {
557 secnotice("OTATrust", "Asset staleness state: at risk");
558 options.discretionary = NO;
559 }
560 CFReleaseNull(otapkiref);
561 return options;
562 }
563
564 static BOOL DownloadOTATrustAsset(BOOL isLocalOnly, BOOL wait, NSError **error) {
565 if (!CanCheckMobileAsset()) {
566 MakeOTATrustError(error, OTATrustLogLevelNotice, NSOSStatusErrorDomain, errSecServiceNotAvailable,
567 @"MobileAsset disabled, skipping check.");
568 return NO;
569 }
570
571 __block NSNumber *updated_version = nil;
572 __block dispatch_semaphore_t done = wait ? dispatch_semaphore_create(0) : nil;
573 __block NSError *ma_error = nil;
574 secnotice("OTATrust", "begin MobileAsset query for catalog");
575 [MAAsset startCatalogDownload:(NSString *)OTATrustMobileAssetType options:GetMADownloadOptions(wait) then:^(MADownLoadResult result) {
576 @autoreleasepool {
577 os_transaction_t transaction = os_transaction_create("com.apple.trustd.PKITrustSupplementals.download");
578 if (result != MADownloadSucceesful) {
579 MakeOTATrustError(&ma_error, OTATrustLogLevelError, @"MADownLoadResult", (OSStatus)result,
580 @"failed to download catalog: %ld", (long)result);
581 if (result == MADownloadDaemonNotReady) {
582 /* mobileassetd has to wait for first unlock to downalod. trustd usually launches before first unlock. */
583 TriggerUnlockNotificationOTATrustAssetCheck(kOTABackgroundQueue);
584 }
585 return;
586 }
587 MAAssetQuery *query = [[MAAssetQuery alloc] initWithType:(NSString *)OTATrustMobileAssetType];
588 [query augmentResultsWithState:true];
589
590 secnotice("OTATrust", "begin MobileAsset metadata sync request");
591 MAQueryResult queryResult = [query queryMetaDataSync];
592 if (queryResult != MAQuerySucceesful) {
593 MakeOTATrustError(&ma_error, OTATrustLogLevelError, @"MAQueryResult", (OSStatus)queryResult,
594 @"failed to query MobileAsset metadata: %ld", (long)queryResult);
595 return;
596 }
597
598 if (!query.results) {
599 MakeOTATrustError(&ma_error, OTATrustLogLevelError, NSOSStatusErrorDomain, errSecInternal,
600 @"no results in MobileAsset query");
601 return;
602 }
603
604 bool began_async_job = false;
605 for (MAAsset *asset in query.results) {
606 /* Check Compatibility Version against this software version */
607 NSNumber *compatibilityVersion = [asset assetProperty:@"_CompatibilityVersion"];
608 if (!isNSNumber(compatibilityVersion) ||
609 [compatibilityVersion unsignedIntegerValue] != OTATrustMobileAssetCompatibilityVersion) {
610 MakeOTATrustError(&ma_error, OTATrustLogLevelNotice, NSOSStatusErrorDomain, errSecIncompatibleVersion,
611 @"skipping asset because Compatibility Version doesn't match %@", compatibilityVersion);
612 continue;
613 }
614
615 /* Check Content Version against the current content version */
616 NSNumber *asset_version = [asset assetProperty:@"_ContentVersion"];
617 if (!ShouldUpdateWithAsset(asset_version)) {
618 /* write the version and last (successful) check-in time */
619 UpdateOTAContext(asset_version, &ma_error);
620 NSDictionary *eventAttributes = @{
621 @"assetVersion" : asset_version,
622 @"systemVersion" : @(GetSystemVersion((__bridge CFStringRef)kOTATrustContentVersionKey)),
623 @"installedVersion" : @(SecOTAPKIGetCurrentAssetVersion(nil)),
624 };
625 MakeOTATrustErrorWithAttributes(&ma_error, OTATrustLogLevelNotice, NSOSStatusErrorDomain, errSecDuplicateItem, eventAttributes,
626 @"skipping asset because we already have _ContentVersion %@ (or newer)", asset_version);
627 continue;
628 }
629
630 GetKillSwitchAttributes(asset.attributes);
631
632 switch (asset.state) {
633 default:
634 MakeOTATrustError(&ma_error, OTATrustLogLevelError, NSOSStatusErrorDomain, errSecInternal,
635 @"unknown asset state %ld", (long)asset.state);
636 continue;
637 case MAInstalled:
638 /* The asset is already in the cache, get it from disk. */
639 secdebug("OTATrust", "OTATrust asset already installed");
640 updated_version = UpdateAndPurgeAsset(asset, asset_version, &ma_error);
641 break;
642 case MAUnknown:
643 MakeOTATrustError(&ma_error, OTATrustLogLevelError, @"MAAssetState", (OSStatus)asset.state,
644 @"asset is unknown");
645 continue;
646 case MADownloading:
647 secnotice("OTATrust", "asset is downloading");
648 /* fall through */
649 case MANotPresent:
650 secnotice("OTATrust", "begin download of OTATrust asset");
651 began_async_job = true;
652 [asset startDownload:GetMADownloadOptions(wait) then:^(MADownLoadResult downloadResult) {
653 @autoreleasepool {
654 os_transaction_t inner_transaction = os_transaction_create("com.apple.trustd.PKITrustSupplementals.downloadAsset");
655 if (downloadResult != MADownloadSucceesful) {
656 MakeOTATrustError(&ma_error, OTATrustLogLevelError, @"MADownLoadResult", (OSStatus)downloadResult,
657 @"failed to download asset: %ld", (long)downloadResult);
658 return;
659 }
660 updated_version = UpdateAndPurgeAsset(asset, asset_version, &ma_error);
661 if (wait) {
662 dispatch_semaphore_signal(done);
663 }
664 (void)inner_transaction; // dead store
665 inner_transaction = nil;
666 }
667 }];
668 break;
669 } /* switch (asset.state) */
670 } /* for (MAAsset.. */
671 if (wait && !began_async_job) {
672 dispatch_semaphore_signal(done);
673 }
674 /* Done with the transaction */
675 (void)transaction; // dead store
676 transaction = nil;
677 } /* autoreleasepool */
678 }]; /* [MAAsset startCatalogDownload: ] */
679
680 /* If the caller is waiting for a response, wait up to one minute for the update to complete.
681 * If the MAAsset callback does not complete in that time, report a timeout.
682 * If the MAAsset callback completes and did not successfully update, it should report an error;
683 * forward that error to the caller.
684 * If the MAAsset callback completes and did not update and did not provide an error; report
685 * an unknown error. */
686 BOOL result = NO;
687 if (wait) {
688 if (dispatch_semaphore_wait(done, dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC * 60)) != 0) {
689 MakeOTATrustError(error, OTATrustLogLevelError, NSOSStatusErrorDomain, errSecNetworkFailure,
690 @"Failed to get asset metadata within 1 minute.");
691 } else {
692 result = (updated_version != nil);
693 if (error && ma_error) {
694 *error = ma_error;
695 } else if (!result) {
696 MakeOTATrustError(error, OTATrustLogLevelError, NSOSStatusErrorDomain, errSecInternalComponent,
697 @"Unknown error occurred.");
698 }
699 }
700 }
701 return result;
702 }
703
704 static void TriggerUnlockNotificationOTATrustAssetCheck(dispatch_queue_t queue) {
705 SecOTAPKIRef otapkiref = SecOTAPKICopyCurrentOTAPKIRef();
706 /* If the last check-in is recent enough, wait for our regularly scheduled check-in. */
707 if (SecOTAPKIAssetStalenessLessThanSeconds(otapkiref, kSecOTAPKIAssetStalenessAtRisk)) {
708 CFReleaseNull(otapkiref);
709 return;
710 }
711 #if !TARGET_OS_SIMULATOR
712 /* register for unlock notifications */
713 int out_token = 0;
714 notify_register_dispatch(kMobileKeyBagLockStatusNotificationID, &out_token, queue, ^(int token) {
715 secnotice("OTATrust", "Got lock status notification for at-risk last check-in after MA daemon error");
716 (void)DownloadOTATrustAsset(NO, NO, nil);
717 notify_cancel(token);
718 });
719 #endif
720 }
721 #else /* !TARGET_OS_IPHONE */
722 /* <rdar://problem/30879827> MobileAssetV2 fails on macOS, so use V1 */
723 static NSNumber *UpdateAndPurgeAsset(ASAsset *asset, NSNumber *asset_version, NSError **error) {
724 if (SecPinningDbUpdateFromURL([asset localURL], error) &&
725 UpdateFromAsset([asset localURL], asset_version, error)) {
726 secnotice("OTATrust", "finished update to version %@ from installed asset. purging asset.", asset_version);
727 #if ENABLE_TRUSTD_ANALYTICS
728 [[TrustdHealthAnalytics logger] logSuccessForEventNamed:TrustdHealthAnalyticsEventOTAPKIEvent];
729 #endif // ENABLE_TRUSTD_ANALYTICS
730 [asset purge:^(NSError *ma_error) {
731 if (ma_error) {
732 secerror("OTATrust: purge failed %@", ma_error);
733 }
734 }];
735 return asset_version;
736 } else {
737 MakeOTATrustError(error, OTATrustLogLevelError, NSOSStatusErrorDomain, errSecCallbackFailed,
738 @"Failed to install new asset version %@ from %@", asset_version, [asset localURL]);
739 return nil;
740 }
741 }
742
743 static NSDictionary *GetASDownloadOptions(BOOL wait) {
744 /* default behavior */
745 NSMutableDictionary *options = [NSMutableDictionary dictionary];
746 options[ASDownloadOptionPriority] = ASDownloadPriorityNormal;
747
748 /* If an XPC interface is waiting on this, all expenses allowed */
749 if (wait) {
750 options[ASDownloadOptionPriority] = ASDownloadPriorityHigh;
751 options[ASDownloadOptionAllowBatteryPower] = @YES;
752 return options;
753 }
754
755 /* If last asset check-in was too long ago, use more expensive options */
756 SecOTAPKIRef otapkiref = SecOTAPKICopyCurrentOTAPKIRef();
757 if (!SecOTAPKIAssetStalenessLessThanSeconds(otapkiref, kSecOTAPKIAssetStalenessWarning)) {
758 secnotice("OTATrust", "Asset staleness state: warning");
759 options[ASDownloadOptionPriority] = ASDownloadPriorityHigh;
760 options[ASDownloadOptionAllowBatteryPower] = @YES;
761 } else if (!SecOTAPKIAssetStalenessLessThanSeconds(otapkiref, kSecOTAPKIAssetStalenessAtRisk)) {
762 secnotice("OTATrust", "Asset staleness state: at risk");
763 options[ASDownloadOptionPriority] = ASDownloadPriorityHigh;
764 }
765 CFReleaseNull(otapkiref);
766 return options;
767 }
768
769 static BOOL DownloadOTATrustAsset(BOOL isLocalOnly, BOOL wait, NSError **error) {
770 if (!CanCheckMobileAsset()) {
771 MakeOTATrustError(error, OTATrustLogLevelNotice, NSOSStatusErrorDomain, errSecServiceNotAvailable,
772 @"MobileAsset disabled, skipping check.");
773 return NO;
774 }
775
776 NSError *localError = nil; // -[ASAssetQuery runQueryAndReturnError:] leaks if you pass null
777 ASAssetQuery *query = [[ASAssetQuery alloc] initWithAssetType:(NSString *)OTATrustMobileAssetType];
778 [query setQueriesLocalAssetInformationOnly:isLocalOnly]; // Omitting this leads to a notifcation loop.
779 NSArray<ASAsset *>*query_results = [query runQueryAndReturnError:&localError];
780 if (!query_results) {
781 if (localError) {
782 secerror("OTATrust: asset query failed: %@", localError);
783 LogRemotely(OTATrustLogLevelError, &localError);
784 if (error) { *error = localError; }
785 }
786 return NO;
787 }
788
789 __block NSNumber *updated_version = nil;
790 __block NSError *handler_error = nil;
791 __block dispatch_semaphore_t done = wait ? dispatch_semaphore_create(0) : nil;
792 bool began_async_job = false;
793 for (ASAsset *asset in query_results) {
794 NSDictionary *attributes = [asset attributes];
795
796 /* Check Compatibility Version against this software version */
797 NSNumber *compatibilityVersion = [attributes objectForKey:ASAttributeCompatibilityVersion];
798 if (!isNSNumber(compatibilityVersion) ||
799 [compatibilityVersion unsignedIntegerValue] != OTATrustMobileAssetCompatibilityVersion) {
800 MakeOTATrustError(error, OTATrustLogLevelNotice, NSOSStatusErrorDomain, errSecIncompatibleVersion,
801 @"skipping asset because Compatibility Version doesn't match %@", compatibilityVersion);
802 continue;
803 }
804
805 /* Check Content Version against the current content version */
806 NSNumber *contentVersion = [attributes objectForKey:ASAttributeContentVersion];
807 if (!ShouldUpdateWithAsset(contentVersion)) {
808 /* write the version and last (successful) check-in time */
809 UpdateOTAContext(contentVersion, error);
810 NSDictionary *eventAttributes = @{
811 @"assetVersion" : contentVersion,
812 @"systemVersion" : @(GetSystemVersion((__bridge CFStringRef)kOTATrustContentVersionKey)),
813 @"installedVersion" : @(SecOTAPKIGetCurrentAssetVersion(nil)),
814 };
815 MakeOTATrustErrorWithAttributes(error, OTATrustLogLevelNotice, NSOSStatusErrorDomain, errSecDuplicateItem, eventAttributes,
816 @"skipping asset because we already have _ContentVersion %@ (or newer)", contentVersion);
817 continue;
818 }
819
820 GetKillSwitchAttributes(attributes);
821
822 ASProgressHandler OTATrustHandler = ^(NSDictionary *state, NSError *progressError){
823 NSString *operationState = nil;
824 if (progressError) {
825 secerror("OTATrust: progress handler failed: %@", progressError);
826 LogRemotely(OTATrustLogLevelError, &progressError);
827 handler_error = [progressError copy];
828 if (wait) {
829 dispatch_semaphore_signal(done);
830 }
831 return;
832 }
833
834 if (!state) {
835 MakeOTATrustError(&handler_error, OTATrustLogLevelError, NSOSStatusErrorDomain, errSecInternal,
836 @"no asset state in progress handler");
837 if (wait) {
838 dispatch_semaphore_signal(done);
839 }
840 return;
841 }
842
843 operationState = [state objectForKey:ASStateOperation];
844 secdebug("OTATrust", "Asset state is %@", operationState);
845
846 if (operationState && [operationState isEqualToString:ASOperationCompleted]) {
847 updated_version = UpdateAndPurgeAsset(asset, contentVersion, &handler_error);
848 if (wait) {
849 dispatch_semaphore_signal(done);
850 }
851 }
852 /* Other states keep calling our progress handler until completed so don't signal */
853 };
854
855 switch ([asset state]) {
856 case ASAssetStateNotPresent:
857 secdebug("OTATrust", "OTATrust asset needs to be downloaded");
858 asset.progressHandler= OTATrustHandler;
859 asset.userInitiatedDownload = YES;
860 [asset beginDownloadWithOptions:GetASDownloadOptions(wait)];
861 began_async_job = true;
862 break;
863 case ASAssetStateInstalled:
864 /* The asset is already in the cache, get it from disk. */
865 secdebug("OTATrust", "OTATrust asset already installed");
866 updated_version = UpdateAndPurgeAsset(asset, contentVersion, error);
867 break;
868 case ASAssetStatePaused:
869 secdebug("OTATrust", "OTATrust asset download paused");
870 asset.progressHandler = OTATrustHandler;
871 asset.userInitiatedDownload = YES;
872 [asset adjustDownloadOptions:GetASDownloadOptions(wait) completion:nil];
873 if (![asset resumeDownloadAndReturnError:&localError]) {
874 if (localError) {
875 secerror("OTATrust: failed to resume download of asset: %@", localError);
876 LogRemotely(OTATrustLogLevelError, &localError);
877 if (error) { *error = localError; }
878 }
879 } else {
880 began_async_job = true;
881 }
882 break;
883 case ASAssetStateStalled:
884 secdebug("OTATrust", "OTATrust asset stalled");
885 // drop through
886 case ASAssetStateDownloading:
887 secdebug("OTATrust", "OTATrust asset downloading");
888 asset.progressHandler = OTATrustHandler;
889 asset.userInitiatedDownload = YES;
890 [asset adjustDownloadOptions:GetASDownloadOptions(wait) completion:nil];
891 began_async_job = true;
892 break;
893 default:
894 MakeOTATrustError(error, OTATrustLogLevelError, NSOSStatusErrorDomain, errSecInternal,
895 @"unhandled asset state %ld", (long)asset.state);
896 continue;
897 }
898 }
899
900 /* If the caller is waiting for a response, wait up to one minute for the update to complete.
901 * If the OTATrustHandler does not complete in the time, report a timeout.
902 * If the OTATrustHandler completes and did not successfully update and it reported an error;
903 * forward that error to the caller. */
904 BOOL result = (updated_version != nil);
905 if (wait && began_async_job) {
906 if (dispatch_semaphore_wait(done, dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC * 60)) != 0) {
907 MakeOTATrustError(error, OTATrustLogLevelError, NSOSStatusErrorDomain, errSecNetworkFailure,
908 @"Failed to get asset metadata within 1 minute.");
909 } else {
910 /* finished an async job, update the result */
911 result = (updated_version != nil);
912 if (error && handler_error) {
913 *error = handler_error;
914 }
915 }
916 }
917
918 /* If we failed and don't know why, report an unknown error */
919 if (!result && error && (*error == NULL)) {
920 MakeOTATrustError(error, OTATrustLogLevelError, NSOSStatusErrorDomain, errSecInternalComponent,
921 @"Unknown error occurred.");
922 }
923 return result;
924 }
925 #endif /* !TARGET_OS_IPHONE */
926
927 static bool InitializeKillSwitch(NSString *key) {
928 #if !TARGET_OS_BRIDGE
929 NSError *error = nil;
930 NSDictionary *OTAPKIContext = [NSDictionary dictionaryWithContentsOfURL:GetAssetFileURL(kOTATrustContextFilename) error:&error];
931 if (isNSDictionary(OTAPKIContext)) {
932 NSNumber *killSwitchValue = OTAPKIContext[key];
933 if (isNSNumber(killSwitchValue)) {
934 secinfo("OTATrust", "found on-disk kill switch %{public}@ with value %d", key, [killSwitchValue boolValue]);
935 return [killSwitchValue boolValue];
936 } else {
937 MakeOTATrustError(&error, OTATrustLogLevelNotice, NSOSStatusErrorDomain, errSecInvalidValue,
938 @"OTAContext.plist missing check-in");
939 }
940 } else {
941 MakeOTATrustError(&error, OTATrustLogLevelNotice, NSOSStatusErrorDomain, errSecMissingValue,
942 @"OTAContext.plist missing dictionary");
943 }
944 #endif
945 return false;
946 }
947
948 static void InitializeOTATrustAsset(dispatch_queue_t queue) {
949 /* Only the "system" trustd does updates */
950 if (SecOTAPKIIsSystemTrustd()) {
951 /* Asynchronously ask MobileAsset for most recent asset. */
952 dispatch_async(queue, ^{
953 secnotice("OTATrust", "Initial check with MobileAsset for newer PKITrustSupplementals asset");
954 (void)DownloadOTATrustAsset(NO, NO, nil);
955 });
956
957 /* Register for changes in our asset */
958 if (CanCheckMobileAsset()) {
959 int out_token = 0;
960 notify_register_dispatch(kOTATrustMobileAssetNotification, &out_token, queue, ^(int __unused token) {
961 secnotice("OTATrust", "Got notification about a new PKITrustSupplementals asset from mobileassetd.");
962 (void)DownloadOTATrustAsset(YES, NO, nil);
963 });
964 }
965 } else {
966 /* Register for changes signaled by the system trustd */
967 secnotice("OTATrust", "Intializing listener for Asset changes from system trustd.");
968 int out_token = 0;
969 notify_register_dispatch(kOTATrustOnDiskAssetNotification, &out_token, queue, ^(int __unused token) {
970 secnotice("OTATrust", "Got notification about a new PKITrustSupplementals asset from system trustd.");
971 NSError *nserror = nil;
972 CFErrorRef error = nil;
973 NSNumber *asset_version = [NSNumber numberWithUnsignedLongLong:GetAssetVersion(&error)];
974 if (error) {
975 nserror = CFBridgingRelease(error);
976 }
977 if (!UpdateFromAsset(GetAssetFileURL(nil), asset_version, &nserror)) {
978 secerror("OTATrust: failed to update from asset after notification: %@", nserror);
979 /* Reset our last check-in time and reset the asset version to the system asset version -- even
980 * though we may be using something newer than that (but not as new as what's on disk). On re-launch
981 * (provided reading from disk still fails) we'd be using the system asset version anyway. */
982 SecOTAPKIResetCurrentAssetVersion(&error);
983 }
984 });
985 int out_token2 = 0;
986 notify_register_dispatch(kOTATrustCheckInNotification, &out_token2, queue, ^(int __unused token) {
987 secinfo("OTATrust", "Got notification about successful PKITrustSupplementals asset check-in");
988 (void)UpdateOTACheckInDate();
989 });
990 int out_token3 = 0;
991 notify_register_dispatch(kOTATrustKillSwitchNotification, &out_token3, queue, ^(int __unused token) {
992 UpdateKillSwitch(kOTATrustCTKillSwitch, InitializeKillSwitch(kOTATrustCTKillSwitch));
993 });
994 }
995 }
996
997 static void TriggerPeriodicOTATrustAssetChecks(dispatch_queue_t queue) {
998 if (SecOTAPKIIsSystemTrustd()) {
999 static sec_action_t action;
1000 static bool first_launch = true;
1001 static dispatch_once_t onceToken;
1002 dispatch_once(&onceToken, ^{
1003 NSUserDefaults *defaults = [[NSUserDefaults alloc] initWithSuiteName:@"com.apple.security"];
1004 NSNumber *updateDeltas = [defaults valueForKey:@"PKITrustSupplementalsUpdatePeriod"];
1005 int delta = kOTATrustDefaultUpdatePeriod;
1006 if (isNSNumber(updateDeltas)) {
1007 delta = [updateDeltas intValue];
1008 if (delta < kOTATrustMinimumUpdatePeriod) {
1009 delta = kOTATrustMinimumUpdatePeriod;
1010 }
1011 }
1012 secnotice("OTATrust", "Setting periodic update delta to %d seconds", delta);
1013 action = sec_action_create_with_queue(queue,"OTATrust", delta);
1014 sec_action_set_handler(action, ^{
1015 if (!first_launch) {
1016 (void)DownloadOTATrustAsset(NO, NO, nil);
1017 }
1018 first_launch = false;
1019 });
1020 });
1021 sec_action_perform(action);
1022 }
1023 }
1024 #endif /* !TARGET_OS_BRIDGE */
1025
1026 /* MARK: - */
1027 /* MARK: Initialization functions */
1028 static CFPropertyListRef CFPropertyListCopyFromSystem(CFStringRef asset) {
1029 CFPropertyListRef plist = NULL;
1030 CFDataRef xmlData = SecSystemTrustStoreCopyResourceContents(asset, CFSTR("plist"), NULL);
1031
1032 if (xmlData) {
1033 plist = CFPropertyListCreateWithData(kCFAllocatorDefault, xmlData, kCFPropertyListImmutable, NULL, NULL);
1034 CFRelease(xmlData);
1035 }
1036
1037 return plist;
1038 }
1039
1040 static uint64_t GetSystemVersion(CFStringRef key) {
1041 uint64_t system_version = 0;
1042 int64_t asset_number = 0;
1043
1044 CFDataRef assetVersionData = SecSystemTrustStoreCopyResourceContents(CFSTR("AssetVersion"), CFSTR("plist"), NULL);
1045 if (NULL != assetVersionData) {
1046 CFPropertyListFormat propFormat;
1047 CFDictionaryRef versionPlist = CFPropertyListCreateWithData(kCFAllocatorDefault, assetVersionData, 0, &propFormat, NULL);
1048 if (NULL != versionPlist && CFDictionaryGetTypeID() == CFGetTypeID(versionPlist)) {
1049 CFNumberRef versionNumber = (CFNumberRef)CFDictionaryGetValue(versionPlist, (const void *)key);
1050 if (NULL != versionNumber){
1051 CFNumberGetValue(versionNumber, kCFNumberSInt64Type, &asset_number);
1052 if (asset_number < 0) { // Not valid
1053 asset_number = 0;
1054 }
1055 system_version = (uint64_t)asset_number;
1056 }
1057 }
1058 CFReleaseSafe(versionPlist);
1059 CFReleaseSafe(assetVersionData);
1060 }
1061
1062 return system_version;
1063 }
1064
1065 static bool initialization_error_from_asset_data = false;
1066
1067 static bool ShouldInitializeWithAsset(void) {
1068 uint64_t system_version = GetSystemVersion((__bridge CFStringRef)kOTATrustContentVersionKey);
1069 uint64_t asset_version = GetAssetVersion(nil);
1070
1071 if (asset_version > system_version) {
1072 secnotice("OTATrust", "Using asset v%llu instead of system v%llu", asset_version, system_version);
1073 return !initialization_error_from_asset_data;
1074 }
1075 return false;
1076 }
1077
1078 static CFSetRef CFSetCreateFromPropertyList(CFPropertyListRef plist) {
1079 CFSetRef result = NULL;
1080
1081 if (plist) {
1082 CFMutableSetRef tempSet = NULL;
1083 if (CFGetTypeID(plist) == CFArrayGetTypeID()) {
1084 tempSet = CFSetCreateMutable(kCFAllocatorDefault, 0, &kCFTypeSetCallBacks);
1085 if (NULL == tempSet) {
1086 return result;
1087 }
1088 CFArrayRef array = (CFArrayRef)plist;
1089 CFIndex num_keys = CFArrayGetCount(array);
1090 for (CFIndex idx = 0; idx < num_keys; idx++) {
1091 CFDataRef data = (CFDataRef)CFArrayGetValueAtIndex(array, idx);
1092 CFSetAddValue(tempSet, data);
1093 }
1094 } else {
1095 return result;
1096 }
1097
1098 result = tempSet;
1099 }
1100 return result;
1101 }
1102
1103 static CF_RETURNS_RETAINED CFSetRef InitializeBlackList() {
1104 CFPropertyListRef plist = CFPropertyListCopyFromSystem(CFSTR("Blocked"));
1105 CFSetRef result = CFSetCreateFromPropertyList(plist);
1106 CFReleaseSafe(plist);
1107
1108 return result;
1109 }
1110
1111 static CF_RETURNS_RETAINED CFSetRef InitializeGrayList() {
1112 CFPropertyListRef plist = CFPropertyListCopyFromSystem(CFSTR("GrayListedKeys"));
1113 CFSetRef result = CFSetCreateFromPropertyList(plist);
1114 CFReleaseSafe(plist);
1115
1116 return result;
1117 }
1118
1119 static CF_RETURNS_RETAINED CFURLRef InitializePinningList() {
1120 return SecSystemTrustStoreCopyResourceURL(CFSTR("CertificatePinning"), CFSTR("plist"), NULL);
1121 }
1122
1123 static CF_RETURNS_RETAINED CFDictionaryRef InitializeAllowList() {
1124 CFPropertyListRef allowList = CFPropertyListCopyFromSystem(CFSTR("Allowed"));
1125
1126 if (allowList && (CFGetTypeID(allowList) == CFDictionaryGetTypeID())) {
1127 return allowList;
1128 } else {
1129 CFReleaseNull(allowList);
1130 return NULL;
1131 }
1132 }
1133
1134 static CF_RETURNS_RETAINED CFArrayRef InitializeTrustedCTLogs() {
1135 NSArray *trustedCTLogs = nil;
1136 NSError *error = nil;
1137 #if !TARGET_OS_BRIDGE
1138 if (ShouldInitializeWithAsset()) {
1139 trustedCTLogs = [NSArray arrayWithContentsOfURL:GetAssetFileURL(kOTATrustTrustedCTLogsFilename) error:&error];
1140 if (!isNSArray(trustedCTLogs)) {
1141 secerror("OTATrust: failed to read CT list from asset data: %@", error);
1142 LogRemotely(OTATrustLogLevelError, &error);
1143 if (!DeleteAssetFromDisk()) {
1144 initialization_error_from_asset_data = true;
1145 }
1146 }
1147 }
1148 #endif
1149 if (!isNSArray(trustedCTLogs)) {
1150 trustedCTLogs = [NSArray arrayWithContentsOfURL:SecSystemTrustStoreCopyResourceNSURL(kOTATrustTrustedCTLogsFilename)];
1151 }
1152 if (isNSArray(trustedCTLogs)) {
1153 return CFBridgingRetain(trustedCTLogs);
1154 }
1155 return NULL;
1156 }
1157
1158 static CF_RETURNS_RETAINED CFDictionaryRef InitializeEVPolicyToAnchorDigestsTable() {
1159 CFDictionaryRef result = NULL;
1160 CFPropertyListRef evroots = CFPropertyListCopyFromSystem(CFSTR("EVRoots"));
1161
1162 if (evroots) {
1163 if (CFGetTypeID(evroots) == CFDictionaryGetTypeID()) {
1164 /* @@@ Ensure that each dictionary key is a dotted list of digits,
1165 each value is an NSArrayRef and each element in the array is a
1166 20 byte digest. */
1167 result = (CFDictionaryRef)evroots;
1168 }
1169 else {
1170 secwarning("EVRoot.plist is wrong type.");
1171 CFRelease(evroots);
1172 }
1173 }
1174
1175 return result;
1176 }
1177
1178 static CFIndex InitializeValidSnapshotVersion(CFIndex *outFormat) {
1179 CFIndex validVersion = 0;
1180 CFIndex validFormat = 0;
1181 CFDataRef validVersionData = SecSystemTrustStoreCopyResourceContents(CFSTR("ValidUpdate"), CFSTR("plist"), NULL);
1182 if (NULL != validVersionData)
1183 {
1184 CFPropertyListFormat propFormat;
1185 CFDictionaryRef versionPlist = CFPropertyListCreateWithData(kCFAllocatorDefault, validVersionData, 0, &propFormat, NULL);
1186 if (NULL != versionPlist && CFDictionaryGetTypeID() == CFGetTypeID(versionPlist))
1187 {
1188 CFNumberRef versionNumber = (CFNumberRef)CFDictionaryGetValue(versionPlist, (const void *)CFSTR("Version"));
1189 if (NULL != versionNumber)
1190 {
1191 CFNumberGetValue(versionNumber, kCFNumberCFIndexType, &validVersion);
1192 }
1193 CFNumberRef formatNumber = (CFNumberRef)CFDictionaryGetValue(versionPlist, (const void *)CFSTR("Format"));
1194 if (NULL != formatNumber)
1195 {
1196 CFNumberGetValue(formatNumber, kCFNumberCFIndexType, &validFormat);
1197 }
1198 }
1199 CFReleaseSafe(versionPlist);
1200 CFReleaseSafe(validVersionData);
1201 }
1202 if (outFormat) {
1203 *outFormat = validFormat;
1204 }
1205 return validVersion;
1206 }
1207
1208 static Boolean PathExists(const char* path, size_t* pFileSize) {
1209 const char *checked_path = (path) ? path : "";
1210 Boolean result = false;
1211 struct stat sb;
1212
1213 if (NULL != pFileSize) {
1214 *pFileSize = 0;
1215 }
1216
1217 int stat_result = stat(checked_path, &sb);
1218 result = (stat_result == 0);
1219
1220 if (result && !S_ISDIR(sb.st_mode)) {
1221 // It is a file
1222 if (NULL != pFileSize) {
1223 *pFileSize = (size_t)sb.st_size;
1224 }
1225 }
1226
1227 return result;
1228 }
1229
1230 static const char* InitializeValidSnapshotData(CFStringRef filename_str) {
1231 char *result = NULL;
1232 const char *base_error_str = "could not get valid snapshot";
1233
1234 CFURLRef valid_url = SecSystemTrustStoreCopyResourceURL(filename_str, CFSTR("sqlite3"), NULL);
1235 if (NULL == valid_url) {
1236 secerror("%s", base_error_str);
1237 } else {
1238 CFStringRef valid_str = CFURLCopyFileSystemPath(valid_url, kCFURLPOSIXPathStyle);
1239 char file_path_buffer[PATH_MAX];
1240 memset(file_path_buffer, 0, PATH_MAX);
1241 if (NULL == valid_str) {
1242 secerror("%s path", base_error_str);
1243 } else {
1244 const char *valid_cstr = CFStringGetCStringPtr(valid_str, kCFStringEncodingUTF8);
1245 if (NULL == valid_cstr) {
1246 if (CFStringGetCString(valid_str, file_path_buffer, PATH_MAX, kCFStringEncodingUTF8)) {
1247 valid_cstr = file_path_buffer;
1248 }
1249 }
1250 if (NULL == valid_cstr) {
1251 secerror("%s path as UTF8 string", base_error_str);
1252 } else {
1253 asprintf(&result, "%s", valid_cstr);
1254 }
1255 }
1256 CFReleaseSafe(valid_str);
1257 }
1258 CFReleaseSafe(valid_url);
1259 if (result && !PathExists(result, NULL)) {
1260 free(result);
1261 result = NULL;
1262 }
1263 return (const char*)result;
1264 }
1265
1266 static const char* InitializeValidDatabaseSnapshot() {
1267 return InitializeValidSnapshotData(CFSTR("valid"));
1268 }
1269
1270 static const uint8_t* MapFile(const char* path, size_t* out_file_size) {
1271 int rtn, fd;
1272 const uint8_t *buf = NULL;
1273 struct stat sb;
1274 size_t size = 0;
1275
1276 if (NULL == path || NULL == out_file_size) {
1277 return NULL;
1278 }
1279
1280 *out_file_size = 0;
1281
1282 fd = open(path, O_RDONLY);
1283 if (fd < 0) { return NULL; }
1284 rtn = fstat(fd, &sb);
1285 if (rtn || (sb.st_size > (off_t) ((UINT32_MAX >> 1)-1))) {
1286 close(fd);
1287 return NULL;
1288 }
1289 size = (size_t)sb.st_size;
1290
1291 buf = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
1292 if (!buf || buf == MAP_FAILED) {
1293 secerror("unable to map %s (errno %d)", path, errno);
1294 close(fd);
1295 return NULL;
1296 }
1297
1298 close(fd);
1299 *out_file_size = size;
1300 return buf;
1301 }
1302
1303 static void UnMapFile(void* mapped_data, size_t data_size) {
1304 if (!mapped_data) {
1305 return;
1306 }
1307 int rtn = munmap(mapped_data, data_size);
1308 if (rtn != 0) {
1309 secerror("unable to unmap %ld bytes at %p (error %d)", data_size, mapped_data, rtn);
1310 }
1311 }
1312
1313 struct index_record {
1314 unsigned char hash[CC_SHA1_DIGEST_LENGTH];
1315 uint32_t offset;
1316 };
1317 typedef struct index_record index_record;
1318
1319 static bool InitializeAnchorTable(CFDictionaryRef* pLookupTable, const char** ppAnchorTable) {
1320
1321 bool result = false;
1322
1323 if (NULL == pLookupTable || NULL == ppAnchorTable) {
1324 return result;
1325 }
1326
1327 *pLookupTable = NULL;
1328 *ppAnchorTable = NULL;;
1329
1330 CFDataRef cert_index_file_data = NULL;
1331 char file_path_buffer[PATH_MAX];
1332 CFURLRef table_data_url = NULL;
1333 CFStringRef table_data_cstr_path = NULL;
1334 const char* table_data_path = NULL;
1335 const index_record* pIndex = NULL;
1336 size_t index_offset = 0;
1337 size_t index_data_size = 0;
1338 CFMutableDictionaryRef anchorLookupTable = NULL;
1339 uint32_t offset_int_value = 0;
1340 CFNumberRef index_offset_value = NULL;
1341 CFDataRef index_hash = NULL;
1342 CFMutableArrayRef offsets = NULL;
1343 Boolean release_offset = false;
1344
1345 char* local_anchorTable = NULL;
1346 size_t local_anchorTableSize = 0;
1347
1348 // local_anchorTable is still NULL so the asset in the system trust store bundle needs to be used.
1349 CFReleaseSafe(cert_index_file_data);
1350 cert_index_file_data = SecSystemTrustStoreCopyResourceContents(CFSTR("certsIndex"), CFSTR("data"), NULL);
1351 if (!cert_index_file_data) {
1352 secerror("could not find certsIndex");
1353 }
1354 table_data_url = SecSystemTrustStoreCopyResourceURL(CFSTR("certsTable"), CFSTR("data"), NULL);
1355 if (!table_data_url) {
1356 secerror("could not find certsTable");
1357 }
1358
1359 if (NULL != table_data_url) {
1360 table_data_cstr_path = CFURLCopyFileSystemPath(table_data_url, kCFURLPOSIXPathStyle);
1361 if (NULL != table_data_cstr_path) {
1362 memset(file_path_buffer, 0, PATH_MAX);
1363 table_data_path = CFStringGetCStringPtr(table_data_cstr_path, kCFStringEncodingUTF8);
1364 if (NULL == table_data_path) {
1365 if (CFStringGetCString(table_data_cstr_path, file_path_buffer, PATH_MAX, kCFStringEncodingUTF8)) {
1366 table_data_path = file_path_buffer;
1367 }
1368 }
1369 local_anchorTable = (char *)MapFile(table_data_path, &local_anchorTableSize);
1370 CFReleaseSafe(table_data_cstr_path);
1371 }
1372 }
1373 CFReleaseSafe(table_data_url);
1374
1375 if (NULL == local_anchorTable || NULL == cert_index_file_data) {
1376 // we are in trouble
1377 if (NULL != local_anchorTable) {
1378 UnMapFile(local_anchorTable, local_anchorTableSize);
1379 local_anchorTable = NULL;
1380 local_anchorTableSize = 0;
1381 }
1382 CFReleaseSafe(cert_index_file_data);
1383 return result;
1384 }
1385
1386 // ------------------------------------------------------------------------
1387 // Now that the locations of the files are known and the table file has
1388 // been mapped into memory, create a dictionary that maps the SHA1 hash of
1389 // normalized issuer to the offset in the mapped anchor table file which
1390 // contains a index_record to the correct certificate
1391 // ------------------------------------------------------------------------
1392 pIndex = (const index_record*)CFDataGetBytePtr(cert_index_file_data);
1393 index_data_size = CFDataGetLength(cert_index_file_data);
1394
1395 anchorLookupTable = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
1396 &kCFTypeDictionaryKeyCallBacks,
1397 &kCFTypeDictionaryValueCallBacks);
1398
1399 for (index_offset = index_data_size; index_offset > 0; index_offset -= sizeof(index_record), pIndex++) {
1400 offset_int_value = pIndex->offset;
1401
1402 index_offset_value = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &offset_int_value);
1403 index_hash = CFDataCreate(kCFAllocatorDefault, pIndex->hash, CC_SHA1_DIGEST_LENGTH);
1404
1405 // see if the dictionary already has this key
1406 release_offset = false;
1407 offsets = (CFMutableArrayRef)CFDictionaryGetValue(anchorLookupTable, index_hash);
1408 if (NULL == offsets) {
1409 offsets = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
1410 release_offset = true;
1411 }
1412
1413 // Add the offset
1414 CFArrayAppendValue(offsets, index_offset_value);
1415
1416 // set the key value pair in the dictionary
1417 CFDictionarySetValue(anchorLookupTable, index_hash, offsets);
1418
1419 CFRelease(index_offset_value);
1420 CFRelease(index_hash);
1421 if (release_offset) {
1422 CFRelease(offsets);
1423 }
1424 }
1425
1426 CFRelease(cert_index_file_data);
1427
1428 if (NULL != anchorLookupTable && NULL != local_anchorTable) {
1429 *pLookupTable = anchorLookupTable;
1430 *ppAnchorTable = local_anchorTable;
1431 result = true;
1432 } else {
1433 CFReleaseSafe(anchorLookupTable);
1434 if (NULL != local_anchorTable) {
1435 UnMapFile(local_anchorTable, local_anchorTableSize);
1436 local_anchorTable = NULL;
1437 local_anchorTableSize = 0;
1438 }
1439 }
1440
1441 return result;
1442 }
1443
1444 static void InitializeEscrowCertificates(CFArrayRef *escrowRoots, CFArrayRef *escrowPCSRoots) {
1445 CFDataRef file_data = SecSystemTrustStoreCopyResourceContents(CFSTR("AppleESCertificates"), CFSTR("plist"), NULL);
1446
1447 if (NULL == file_data) {
1448 return;
1449 }
1450
1451 CFPropertyListFormat propFormat;
1452 CFDictionaryRef certsDictionary = CFPropertyListCreateWithData(kCFAllocatorDefault, file_data, 0, &propFormat, NULL);
1453 if (NULL != certsDictionary && CFDictionaryGetTypeID() == CFGetTypeID((CFTypeRef)certsDictionary)) {
1454 CFArrayRef certs = (CFArrayRef)CFDictionaryGetValue(certsDictionary, CFSTR("ProductionEscrowKey"));
1455 if (NULL != certs && CFArrayGetTypeID() == CFGetTypeID((CFTypeRef)certs) && CFArrayGetCount(certs) > 0) {
1456 *escrowRoots = CFArrayCreateCopy(kCFAllocatorDefault, certs);
1457 }
1458 CFArrayRef pcs_certs = (CFArrayRef)CFDictionaryGetValue(certsDictionary, CFSTR("ProductionPCSEscrowKey"));
1459 if (NULL != pcs_certs && CFArrayGetTypeID() == CFGetTypeID((CFTypeRef)pcs_certs) && CFArrayGetCount(pcs_certs) > 0) {
1460 *escrowPCSRoots = CFArrayCreateCopy(kCFAllocatorDefault, pcs_certs);
1461 }
1462 }
1463 CFReleaseSafe(certsDictionary);
1464 CFRelease(file_data);
1465 }
1466
1467 static CF_RETURNS_RETAINED CFDictionaryRef InitializeEventSamplingRates() {
1468 NSDictionary *analyticsSamplingRates = nil;
1469 NSDictionary *eventSamplingRates = nil;
1470 NSError *error = nil;
1471 #if !TARGET_OS_BRIDGE
1472 if (ShouldInitializeWithAsset()) {
1473 analyticsSamplingRates = [NSDictionary dictionaryWithContentsOfURL:GetAssetFileURL(kOTATrustAnalyticsSamplingRatesFilename) error:&error];
1474 if (!isNSDictionary(analyticsSamplingRates)) {
1475 secerror("OTATrust: failed to read sampling rates from asset data: %@", error);
1476 LogRemotely(OTATrustLogLevelError, &error);
1477 if (!DeleteAssetFromDisk()) {
1478 initialization_error_from_asset_data = true;
1479 }
1480 }
1481 eventSamplingRates = analyticsSamplingRates[@"Events"];
1482 }
1483 #endif
1484 if (!isNSDictionary(eventSamplingRates)) {
1485 analyticsSamplingRates = [NSDictionary dictionaryWithContentsOfURL:SecSystemTrustStoreCopyResourceNSURL(kOTATrustAnalyticsSamplingRatesFilename)];
1486 }
1487 if (isNSDictionary(analyticsSamplingRates)) {
1488 eventSamplingRates = analyticsSamplingRates[@"Events"];
1489 if (isNSDictionary(eventSamplingRates)) {
1490 return CFBridgingRetain(eventSamplingRates);
1491 }
1492 }
1493 return NULL;
1494 }
1495
1496 static CF_RETURNS_RETAINED CFArrayRef InitializeAppleCertificateAuthorities() {
1497 NSArray *appleCAs = nil;
1498 NSError *error = nil;
1499 #if !TARGET_OS_BRIDGE
1500 if (ShouldInitializeWithAsset()) {
1501 appleCAs = [NSArray arrayWithContentsOfURL:GetAssetFileURL(kOTATrustAppleCertifcateAuthoritiesFilename) error:&error];
1502 if (!isNSArray(appleCAs)) {
1503 secerror("OTATrust: failed to read Apple CAs list from asset data: %@", error);
1504 LogRemotely(OTATrustLogLevelError, &error);
1505 if (!DeleteAssetFromDisk()) {
1506 initialization_error_from_asset_data = true;
1507 }
1508 }
1509 }
1510 #endif
1511 if (!isNSArray(appleCAs)) {
1512 appleCAs = [NSArray arrayWithContentsOfURL:SecSystemTrustStoreCopyResourceNSURL(kOTATrustAppleCertifcateAuthoritiesFilename)];
1513 }
1514 if (isNSArray(appleCAs)) {
1515 return CFBridgingRetain(appleCAs);
1516 }
1517 return NULL;
1518 }
1519
1520 /* MARK: - */
1521 /* MARK: SecOTA */
1522
1523 /* We keep track of one OTAPKI reference */
1524 static SecOTAPKIRef kCurrentOTAPKIRef = NULL;
1525 /* This queue is for making changes to the OTAPKI reference */
1526 static dispatch_queue_t kOTAQueue = NULL;
1527
1528 struct _OpaqueSecOTAPKI {
1529 CFRuntimeBase _base;
1530 CFSetRef _blackListSet;
1531 CFSetRef _grayListSet;
1532 CFDictionaryRef _allowList;
1533 CFArrayRef _trustedCTLogs;
1534 CFURLRef _pinningList;
1535 CFArrayRef _escrowCertificates;
1536 CFArrayRef _escrowPCSCertificates;
1537 CFDictionaryRef _evPolicyToAnchorMapping;
1538 CFDictionaryRef _anchorLookupTable;
1539 const char* _anchorTable;
1540 uint64_t _trustStoreVersion;
1541 const char* _validDatabaseSnapshot;
1542 CFIndex _validSnapshotVersion;
1543 CFIndex _validSnapshotFormat;
1544 uint64_t _assetVersion;
1545 CFDateRef _lastAssetCheckIn;
1546 CFDictionaryRef _eventSamplingRates;
1547 CFArrayRef _appleCAs;
1548 bool _ctKillSwitch;
1549 };
1550
1551 CFGiblisFor(SecOTAPKI)
1552
1553 static CF_RETURNS_RETAINED CFStringRef SecOTAPKICopyFormatDescription(CFTypeRef cf, CFDictionaryRef formatOptions) {
1554 SecOTAPKIRef otapkiRef = (SecOTAPKIRef)cf;
1555 return CFStringCreateWithFormat(kCFAllocatorDefault,NULL,CFSTR("<SecOTAPKIRef: version %llu/%llu>"),
1556 otapkiRef->_trustStoreVersion, otapkiRef->_assetVersion);
1557 }
1558
1559 static void SecOTAPKIDestroy(CFTypeRef cf) {
1560 SecOTAPKIRef otapkiref = (SecOTAPKIRef)cf;
1561
1562 CFReleaseNull(otapkiref->_blackListSet);
1563 CFReleaseNull(otapkiref->_grayListSet);
1564 CFReleaseNull(otapkiref->_escrowCertificates);
1565 CFReleaseNull(otapkiref->_escrowPCSCertificates);
1566
1567 CFReleaseNull(otapkiref->_evPolicyToAnchorMapping);
1568 CFReleaseNull(otapkiref->_anchorLookupTable);
1569
1570 CFReleaseNull(otapkiref->_trustedCTLogs);
1571 CFReleaseNull(otapkiref->_pinningList);
1572 CFReleaseNull(otapkiref->_eventSamplingRates);
1573 CFReleaseNull(otapkiref->_appleCAs);
1574 CFReleaseNull(otapkiref->_lastAssetCheckIn);
1575
1576 if (otapkiref->_anchorTable) {
1577 free((void *)otapkiref->_anchorTable);
1578 otapkiref->_anchorTable = NULL;
1579 }
1580 if (otapkiref->_validDatabaseSnapshot) {
1581 free((void *)otapkiref->_validDatabaseSnapshot);
1582 otapkiref->_validDatabaseSnapshot = NULL;
1583 }
1584 }
1585
1586 static uint64_t GetSystemTrustStoreVersion(void) {
1587 return GetSystemVersion(CFSTR("VersionNumber"));
1588 }
1589
1590 static uint64_t GetAssetVersion(CFErrorRef *error) {
1591 @autoreleasepool {
1592 /* Get system asset version */
1593 uint64_t version = GetSystemVersion((__bridge CFStringRef)kOTATrustContentVersionKey);
1594
1595 #if !TARGET_OS_BRIDGE
1596 uint64_t asset_version = 0;
1597 NSError *nserror = nil;
1598 NSDictionary *OTAPKIContext = [NSDictionary dictionaryWithContentsOfURL:GetAssetFileURL(kOTATrustContextFilename) error:&nserror];
1599 if (isNSDictionary(OTAPKIContext)) {
1600 NSNumber *tmpNumber = OTAPKIContext[kOTATrustContentVersionKey];
1601 if (isNSNumber(tmpNumber)) {
1602 asset_version = [tmpNumber unsignedLongLongValue];
1603 } else if (error) {
1604 MakeOTATrustError(&nserror, OTATrustLogLevelNotice, NSOSStatusErrorDomain, errSecInvalidValue,
1605 @"OTAContext.plist missing version");
1606 }
1607 } else if (error) {
1608 MakeOTATrustError(&nserror, OTATrustLogLevelNotice, NSOSStatusErrorDomain, errSecMissingValue,
1609 @"OTAContext.plist missing dictionary");
1610 }
1611
1612 if (asset_version > version) {
1613 return asset_version;
1614 } else {
1615 /* Don't delete the last check-in time so that we know we're up to date with the MobileAsset. */
1616 if (error) {
1617 /* only log this if we're tracking errors */
1618 secnotice("OTATrust", "asset (%llu) is not newer than the system version (%llu); deleting stale data", asset_version, version);
1619 *error = CFRetainSafe((__bridge CFErrorRef)nserror);
1620 }
1621 DeleteOldAssetData();
1622 }
1623 #endif
1624 return version;
1625 }
1626 }
1627
1628 static CF_RETURNS_RETAINED CFDateRef InitializeLastAssetCheckIn(void) {
1629 #if !TARGET_OS_BRIDGE
1630 NSError *error = nil;
1631 NSDictionary *OTAPKIContext = [NSDictionary dictionaryWithContentsOfURL:GetAssetFileURL(kOTATrustContextFilename) error:&error];
1632 if (isNSDictionary(OTAPKIContext)) {
1633 NSDate *checkIn = OTAPKIContext[kOTATrustLastCheckInKey];
1634 if (isNSDate(checkIn)) {
1635 return CFRetainSafe((__bridge CFDateRef)checkIn);
1636 } else {
1637 MakeOTATrustError(&error, OTATrustLogLevelNotice, NSOSStatusErrorDomain, errSecInvalidValue,
1638 @"OTAContext.plist missing check-in");
1639 }
1640 } else {
1641 MakeOTATrustError(&error, OTATrustLogLevelNotice, NSOSStatusErrorDomain, errSecMissingValue,
1642 @"OTAContext.plist missing dictionary");
1643 }
1644 #endif
1645 return NULL;
1646 }
1647
1648 static SecOTAPKIRef SecOTACreate() {
1649
1650 SecOTAPKIRef otapkiref = NULL;
1651
1652 otapkiref = CFTypeAllocate(SecOTAPKI, struct _OpaqueSecOTAPKI , kCFAllocatorDefault);
1653
1654 if (NULL == otapkiref) {
1655 return otapkiref;
1656 }
1657
1658 // Make sure that if this routine has to bail that the clean up
1659 // will do the right thing
1660 memset(otapkiref, 0, sizeof(*otapkiref));
1661
1662 // Start off by getting the trust store version
1663 otapkiref->_trustStoreVersion = GetSystemTrustStoreVersion();
1664
1665 // Get the set of black listed keys
1666 CFSetRef blackKeysSet = InitializeBlackList();
1667 if (NULL == blackKeysSet) {
1668 CFReleaseNull(otapkiref);
1669 return otapkiref;
1670 }
1671 otapkiref->_blackListSet = blackKeysSet;
1672
1673 // Get the set of gray listed keys
1674 CFSetRef grayKeysSet = InitializeGrayList();
1675 if (NULL == grayKeysSet) {
1676 CFReleaseNull(otapkiref);
1677 return otapkiref;
1678 }
1679 otapkiref->_grayListSet = grayKeysSet;
1680
1681 // Get the allow list dictionary
1682 // (now loaded lazily in SecOTAPKICopyAllowList)
1683
1684 // Get the trusted Certificate Transparency Logs
1685 otapkiref->_trustedCTLogs = InitializeTrustedCTLogs();
1686
1687 // Get the pinning list
1688 otapkiref->_pinningList = InitializePinningList();
1689
1690 // Get the Event Sampling Rates
1691 otapkiref->_eventSamplingRates = InitializeEventSamplingRates();
1692
1693 // Get the list of CAs used by Apple
1694 otapkiref->_appleCAs = InitializeAppleCertificateAuthorities();
1695
1696 // Get the asset version (after possible reset due to missing asset data)
1697 if (!initialization_error_from_asset_data) {
1698 CFErrorRef error = nil;
1699 otapkiref->_assetVersion = GetAssetVersion(&error);
1700 otapkiref->_lastAssetCheckIn = InitializeLastAssetCheckIn();
1701 CFReleaseNull(error);
1702 } else {
1703 otapkiref->_assetVersion = GetSystemVersion((__bridge CFStringRef)kOTATrustContentVersionKey);
1704 }
1705
1706 // Get the valid update snapshot version and format
1707 CFIndex update_format = 0;
1708 otapkiref->_validSnapshotVersion = InitializeValidSnapshotVersion(&update_format);
1709 otapkiref->_validSnapshotFormat = update_format;
1710
1711 // Get the valid database snapshot path (if it exists, NULL otherwise)
1712 otapkiref->_validDatabaseSnapshot = InitializeValidDatabaseSnapshot();
1713
1714 CFArrayRef escrowCerts = NULL;
1715 CFArrayRef escrowPCSCerts = NULL;
1716 InitializeEscrowCertificates(&escrowCerts, &escrowPCSCerts);
1717 if (NULL == escrowCerts || NULL == escrowPCSCerts) {
1718 CFReleaseNull(escrowCerts);
1719 CFReleaseNull(escrowPCSCerts);
1720 CFReleaseNull(otapkiref);
1721 return otapkiref;
1722 }
1723 otapkiref->_escrowCertificates = escrowCerts;
1724 otapkiref->_escrowPCSCertificates = escrowPCSCerts;
1725
1726 // Get the mapping of EV Policy OIDs to Anchor digest
1727 CFDictionaryRef evOidToAnchorDigestMap = InitializeEVPolicyToAnchorDigestsTable();
1728 if (NULL == evOidToAnchorDigestMap) {
1729 CFReleaseNull(otapkiref);
1730 return otapkiref;
1731 }
1732 otapkiref->_evPolicyToAnchorMapping = evOidToAnchorDigestMap;
1733
1734 CFDictionaryRef anchorLookupTable = NULL;
1735 const char* anchorTablePtr = NULL;
1736
1737 if (!InitializeAnchorTable(&anchorLookupTable, &anchorTablePtr)) {
1738 CFReleaseSafe(anchorLookupTable);
1739 if (anchorTablePtr) {
1740 free((void *)anchorTablePtr);
1741 }
1742 CFReleaseNull(otapkiref);
1743 return otapkiref;
1744 }
1745 otapkiref->_anchorLookupTable = anchorLookupTable;
1746 otapkiref->_anchorTable = anchorTablePtr;
1747
1748 #if !TARGET_OS_BRIDGE
1749 /* Initialize our update handling */
1750 InitializeOTATrustAsset(kOTABackgroundQueue);
1751 otapkiref->_ctKillSwitch = InitializeKillSwitch(kOTATrustCTKillSwitch);
1752 #else // TARGET_OS_BRIDGE
1753 otapkiref->_ctKillSwitch = true; // bridgeOS never enforces CT
1754 #endif // TARGET_OS_BRIDGE
1755
1756 return otapkiref;
1757 }
1758
1759 SecOTAPKIRef SecOTAPKICopyCurrentOTAPKIRef() {
1760 __block SecOTAPKIRef result = NULL;
1761 static dispatch_once_t kInitializeOTAPKI = 0;
1762 dispatch_once(&kInitializeOTAPKI, ^{
1763 @autoreleasepool {
1764 kOTAQueue = dispatch_queue_create("com.apple.security.OTAPKIQueue", NULL);
1765 dispatch_queue_attr_t attr = dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_SERIAL,
1766 QOS_CLASS_BACKGROUND, 0);
1767 attr = dispatch_queue_attr_make_with_autorelease_frequency(attr, DISPATCH_AUTORELEASE_FREQUENCY_WORK_ITEM);
1768 kOTABackgroundQueue = dispatch_queue_create("com.apple.security.OTAPKIBackgroundQueue", attr);
1769 if (!kOTAQueue || !kOTABackgroundQueue) {
1770 secerror("Failed to create OTAPKI Queues. May crash later.");
1771 }
1772 dispatch_sync(kOTAQueue, ^{
1773 kCurrentOTAPKIRef = SecOTACreate();
1774 });
1775 }
1776 });
1777
1778 dispatch_sync(kOTAQueue, ^{
1779 result = kCurrentOTAPKIRef;
1780 CFRetainSafe(result);
1781 });
1782 return result;
1783 }
1784
1785 #if !TARGET_OS_BRIDGE
1786 static BOOL UpdateOTACheckInDate(void) {
1787 __block NSDate *checkIn = [NSDate date];
1788 dispatch_sync(kOTAQueue, ^{
1789 CFRetainAssign(kCurrentOTAPKIRef->_lastAssetCheckIn, (__bridge CFDateRef)checkIn);
1790 });
1791
1792 if (SecOTAPKIIsSystemTrustd()) {
1793 /* Let the other trustds know we successfully checked in */
1794 notify_post(kOTATrustCheckInNotification);
1795
1796 /* Update the on-disk check-in date, so when we re-launch we remember */
1797 NSError *error = nil;
1798 BOOL result = NO;
1799 if (!(result = UpdateOTAContextOnDisk(kOTATrustLastCheckInKey, checkIn, &error))) {
1800 secerror("OTATrust: failed to write last check-in time: %@", error);
1801 }
1802 return result;
1803 } else {
1804 return NO;
1805 }
1806 }
1807
1808 static void UpdateKillSwitch(NSString *key, bool value) {
1809 dispatch_sync(kOTAQueue, ^{
1810 if ([key isEqualToString:kOTATrustCTKillSwitch]) {
1811 kCurrentOTAPKIRef->_ctKillSwitch = value;
1812 }
1813 });
1814 }
1815
1816 static BOOL UpdateFromAsset(NSURL *localURL, NSNumber *asset_version, NSError **error) {
1817 if (!localURL || !asset_version) {
1818 MakeOTATrustError(error, OTATrustLogLevelError, NSOSStatusErrorDomain, errSecInternal,
1819 @"missing url and version for downloaded asset");
1820 return NO;
1821 }
1822 __block NSArray *newTrustedCTLogs = NULL;
1823 __block uint64_t version = [asset_version unsignedLongLongValue];
1824 __block NSDictionary *newAnalyticsSamplingRates = NULL;
1825 __block NSArray *newAppleCAs = NULL;
1826
1827 NSURL *TrustedCTLogsFileLoc = [NSURL URLWithString:kOTATrustTrustedCTLogsFilename
1828 relativeToURL:localURL];
1829 newTrustedCTLogs = [NSArray arrayWithContentsOfURL:TrustedCTLogsFileLoc error:error];
1830 if (!newTrustedCTLogs) {
1831 secerror("OTATrust: unable to create TrustedCTLogs from asset file: %@", error ? *error: nil);
1832 LogRemotely(OTATrustLogLevelError, error);
1833 return NO;
1834 }
1835
1836 NSURL *AnalyticsSamplingRatesFileLoc = [NSURL URLWithString:kOTATrustAnalyticsSamplingRatesFilename
1837 relativeToURL:localURL];
1838 newAnalyticsSamplingRates = [NSDictionary dictionaryWithContentsOfURL:AnalyticsSamplingRatesFileLoc error:error];
1839 if (!newAnalyticsSamplingRates) {
1840 secerror("OTATrust: unable to create AnalyticsSamplingRates from asset file: %@", error ? *error: nil);
1841 LogRemotely(OTATrustLogLevelError, error);
1842 return NO;
1843 }
1844
1845 NSURL *AppleCAsFileLoc = [NSURL URLWithString:kOTATrustAppleCertifcateAuthoritiesFilename
1846 relativeToURL:localURL];
1847 newAppleCAs = [NSArray arrayWithContentsOfURL:AppleCAsFileLoc error:error];
1848 if (!newAppleCAs) {
1849 secerror("OTATrust: unable to create AppleCAs from asset file: %@", error ? *error: nil);
1850 LogRemotely(OTATrustLogLevelError, error);
1851 return NO;
1852 }
1853
1854 /* Update the Current OTAPKIRef with the new data */
1855 dispatch_sync(kOTAQueue, ^{
1856 secnotice("OTATrust", "updating asset version from %llu to %llu", kCurrentOTAPKIRef->_assetVersion, version);
1857 CFRetainAssign(kCurrentOTAPKIRef->_trustedCTLogs, (__bridge CFArrayRef)newTrustedCTLogs);
1858 CFRetainAssign(kCurrentOTAPKIRef->_eventSamplingRates, (__bridge CFDictionaryRef)newAnalyticsSamplingRates);
1859 CFRetainAssign(kCurrentOTAPKIRef->_appleCAs, (__bridge CFArrayRef)newAppleCAs);
1860 kCurrentOTAPKIRef->_assetVersion = version;
1861 });
1862
1863 /* Write the data to disk (so that we don't have to re-download the asset on re-launch) */
1864 DeleteAssetFromDisk();
1865 if (CopyFileToDisk(kOTATrustTrustedCTLogsFilename, TrustedCTLogsFileLoc, error) &&
1866 CopyFileToDisk(kOTATrustAnalyticsSamplingRatesFilename, AnalyticsSamplingRatesFileLoc, error) &&
1867 CopyFileToDisk(kOTATrustAppleCertifcateAuthoritiesFilename, AppleCAsFileLoc, error) &&
1868 UpdateOTAContext(asset_version, error)) { // Set version and check-in time last (after success)
1869 /* If we successfully updated the "asset" on disk, signal the other trustds to pick up the changes */
1870 notify_post(kOTATrustOnDiskAssetNotification);
1871 } else {
1872 return NO;
1873 }
1874
1875 return YES;
1876 }
1877 #endif // !TARGET_OS_BRIDGE
1878
1879
1880 CFSetRef SecOTAPKICopyBlackListSet(SecOTAPKIRef otapkiRef) {
1881 if (NULL == otapkiRef) {
1882 return NULL;
1883 }
1884
1885 return CFRetainSafe(otapkiRef->_blackListSet);
1886 }
1887
1888
1889 CFSetRef SecOTAPKICopyGrayList(SecOTAPKIRef otapkiRef) {
1890 if (NULL == otapkiRef) {
1891 return NULL;
1892 }
1893
1894 return CFRetainSafe(otapkiRef->_grayListSet);
1895 }
1896
1897 CFDictionaryRef SecOTAPKICopyAllowList(SecOTAPKIRef otapkiRef) {
1898 if (NULL == otapkiRef) {
1899 return NULL;
1900 }
1901
1902 CFDictionaryRef result = otapkiRef->_allowList;
1903 if (!result) {
1904 result = InitializeAllowList();
1905 otapkiRef->_allowList = result;
1906 }
1907
1908 return CFRetainSafe(result);
1909 }
1910
1911 CFArrayRef SecOTAPKICopyAllowListForAuthKeyID(SecOTAPKIRef otapkiRef, CFStringRef authKeyID) {
1912 // %%% temporary performance optimization:
1913 // only load dictionary if we know an allow list exists for this key
1914 const CFStringRef keyIDs[3] = {
1915 CFSTR("7C724B39C7C0DB62A54F9BAA183492A2CA838259"),
1916 CFSTR("65F231AD2AF7F7DD52960AC702C10EEFA6D53B11"),
1917 CFSTR("D2A716207CAFD9959EEB430A19F2E0B9740EA8C7")
1918 };
1919 CFArrayRef result = NULL;
1920 bool hasAllowList = false;
1921 CFIndex count = (sizeof(keyIDs) / sizeof(keyIDs[0]));
1922 for (CFIndex ix=0; ix<count && authKeyID; ix++) {
1923 if (kCFCompareEqualTo == CFStringCompare(authKeyID, keyIDs[ix], 0)) {
1924 hasAllowList = true;
1925 break;
1926 }
1927 }
1928 if (!hasAllowList || !otapkiRef) {
1929 return result;
1930 }
1931
1932 CFDictionaryRef allowListDict = SecOTAPKICopyAllowList(otapkiRef);
1933 if (!allowListDict) {
1934 return result;
1935 }
1936
1937 // return a retained copy of the allow list array (or NULL)
1938 result = CFDictionaryGetValue(allowListDict, authKeyID);
1939 CFRetainSafe(result);
1940 CFReleaseSafe(allowListDict);
1941 return result;
1942 }
1943
1944 CFArrayRef SecOTAPKICopyTrustedCTLogs(SecOTAPKIRef otapkiRef) {
1945 CFArrayRef result = NULL;
1946 if (NULL == otapkiRef) {
1947 return result;
1948 }
1949
1950 #if !TARGET_OS_BRIDGE
1951 /* Trigger periodic background MA checks in system trustd
1952 * We also check on trustd launch and listen for notifications. */
1953 TriggerPeriodicOTATrustAssetChecks(kOTABackgroundQueue);
1954 #endif
1955
1956 result = otapkiRef->_trustedCTLogs;
1957 CFRetainSafe(result);
1958 return result;
1959 }
1960
1961 CFURLRef SecOTAPKICopyPinningList(SecOTAPKIRef otapkiRef) {
1962 if (NULL == otapkiRef) {
1963 return NULL;
1964 }
1965
1966 return CFRetainSafe(otapkiRef->_pinningList);
1967 }
1968
1969
1970 /* Returns an array of certificate data (CFDataRef) */
1971 CFArrayRef SecOTAPKICopyEscrowCertificates(uint32_t escrowRootType, SecOTAPKIRef otapkiRef) {
1972 CFMutableArrayRef result = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
1973 if (NULL == otapkiRef) {
1974 return result;
1975 }
1976
1977 switch (escrowRootType) {
1978 // Note: we shouldn't be getting called to return baseline roots,
1979 // since this function vends production roots by definition.
1980 case kSecCertificateBaselineEscrowRoot:
1981 case kSecCertificateProductionEscrowRoot:
1982 case kSecCertificateBaselineEscrowBackupRoot:
1983 case kSecCertificateProductionEscrowBackupRoot:
1984 if (otapkiRef->_escrowCertificates) {
1985 CFArrayRef escrowCerts = otapkiRef->_escrowCertificates;
1986 CFArrayAppendArray(result, escrowCerts, CFRangeMake(0, CFArrayGetCount(escrowCerts)));
1987 }
1988 break;
1989 case kSecCertificateBaselineEscrowEnrollmentRoot:
1990 case kSecCertificateProductionEscrowEnrollmentRoot:
1991 if (otapkiRef->_escrowCertificates) {
1992 // for enrollment purposes, exclude the v100 root
1993 static const unsigned char V100EscrowRoot[] = {
1994 0x65,0x5C,0xB0,0x3C,0x39,0x3A,0x32,0xA6,0x0B,0x96,
1995 0x40,0xC0,0xCA,0x73,0x41,0xFD,0xC3,0x9E,0x96,0xB3
1996 };
1997 CFArrayRef escrowCerts = otapkiRef->_escrowCertificates;
1998 CFIndex idx, count = CFArrayGetCount(escrowCerts);
1999 for (idx=0; idx < count; idx++) {
2000 CFDataRef tmpData = (CFDataRef) CFArrayGetValueAtIndex(escrowCerts, idx);
2001 SecCertificateRef tmpCert = (tmpData) ? SecCertificateCreateWithData(NULL, tmpData) : NULL;
2002 CFDataRef sha1Hash = (tmpCert) ? SecCertificateGetSHA1Digest(tmpCert) : NULL;
2003 const uint8_t *dp = (sha1Hash) ? CFDataGetBytePtr(sha1Hash) : NULL;
2004 if (!(dp && !memcmp(V100EscrowRoot, dp, sizeof(V100EscrowRoot))) && tmpData) {
2005 CFArrayAppendValue(result, tmpData);
2006 }
2007 CFReleaseSafe(tmpCert);
2008 }
2009 }
2010 break;
2011 case kSecCertificateBaselinePCSEscrowRoot:
2012 case kSecCertificateProductionPCSEscrowRoot:
2013 if (otapkiRef->_escrowPCSCertificates) {
2014 CFArrayRef escrowPCSCerts = otapkiRef->_escrowPCSCertificates;
2015 CFArrayAppendArray(result, escrowPCSCerts, CFRangeMake(0, CFArrayGetCount(escrowPCSCerts)));
2016 }
2017 break;
2018 default:
2019 break;
2020 }
2021
2022 return result;
2023 }
2024
2025
2026 CFDictionaryRef SecOTAPKICopyEVPolicyToAnchorMapping(SecOTAPKIRef otapkiRef) {
2027 if (NULL == otapkiRef) {
2028 return NULL;
2029 }
2030
2031 return CFRetainSafe(otapkiRef->_evPolicyToAnchorMapping);
2032 }
2033
2034
2035 CFDictionaryRef SecOTAPKICopyAnchorLookupTable(SecOTAPKIRef otapkiRef) {
2036 if (NULL == otapkiRef) {
2037 return NULL;
2038 }
2039
2040 return CFRetainSafe(otapkiRef->_anchorLookupTable);
2041 }
2042
2043 const char* SecOTAPKIGetAnchorTable(SecOTAPKIRef otapkiRef) {
2044 if (NULL == otapkiRef) {
2045 return NULL;
2046 }
2047
2048 return otapkiRef->_anchorTable;
2049 }
2050
2051 const char* SecOTAPKIGetValidDatabaseSnapshot(SecOTAPKIRef otapkiRef) {
2052 if (NULL == otapkiRef) {
2053 return NULL;
2054 }
2055
2056 return otapkiRef->_validDatabaseSnapshot;
2057 }
2058
2059 CFIndex SecOTAPKIGetValidSnapshotVersion(SecOTAPKIRef otapkiRef) {
2060 if (NULL == otapkiRef) {
2061 return 0;
2062 }
2063
2064 return otapkiRef->_validSnapshotVersion;
2065 }
2066
2067 CFIndex SecOTAPKIGetValidSnapshotFormat(SecOTAPKIRef otapkiRef) {
2068 if (NULL == otapkiRef) {
2069 return 0;
2070 }
2071
2072 return otapkiRef->_validSnapshotFormat;
2073 }
2074
2075 uint64_t SecOTAPKIGetTrustStoreVersion(SecOTAPKIRef otapkiRef) {
2076 if (NULL == otapkiRef) {
2077 return 0;
2078 }
2079
2080 return otapkiRef->_trustStoreVersion;
2081 }
2082
2083 uint64_t SecOTAPKIGetAssetVersion(SecOTAPKIRef otapkiRef) {
2084 if (NULL == otapkiRef) {
2085 return 0;
2086 }
2087
2088 return otapkiRef->_assetVersion;
2089 }
2090
2091 CFDateRef SecOTAPKICopyLastAssetCheckInDate(SecOTAPKIRef otapkiRef) {
2092 if (NULL == otapkiRef) {
2093 return NULL;
2094 }
2095 return CFRetainSafe(otapkiRef->_lastAssetCheckIn);
2096 }
2097
2098 bool SecOTAPKIAssetStalenessLessThanSeconds(SecOTAPKIRef otapkiRef, CFTimeInterval seconds) {
2099 if (NULL == otapkiRef || !isDate(otapkiRef->_lastAssetCheckIn)) {
2100 return false;
2101 }
2102 if(fabs([(__bridge NSDate *)otapkiRef->_lastAssetCheckIn timeIntervalSinceNow]) < seconds) {
2103 return true;
2104 }
2105 return false;
2106 }
2107
2108 NSNumber *SecOTAPKIGetSamplingRateForEvent(SecOTAPKIRef otapkiRef, NSString *eventName) {
2109 if (NULL == otapkiRef) {
2110 return nil;
2111 }
2112
2113 #if !TARGET_OS_BRIDGE
2114 /* Trigger periodic background MA checks in system trustd
2115 * We also check on trustd launch and listen for notifications. */
2116 TriggerPeriodicOTATrustAssetChecks(kOTABackgroundQueue);
2117 #endif
2118
2119 if (otapkiRef->_eventSamplingRates) {
2120 CFTypeRef value = CFDictionaryGetValue(otapkiRef->_eventSamplingRates, (__bridge CFStringRef)eventName);
2121 if (isNumberOfType(value, kCFNumberSInt64Type)) {
2122 return (__bridge NSNumber *)value;
2123 }
2124 }
2125 return nil;
2126 }
2127
2128 CFArrayRef SecOTAPKICopyAppleCertificateAuthorities(SecOTAPKIRef otapkiRef) {
2129 if (NULL == otapkiRef) {
2130 return NULL;
2131 }
2132
2133 #if !TARGET_OS_BRIDGE
2134 /* Trigger periodic background MA checks in system trustd
2135 * We also check on trustd launch and listen for notifications. */
2136 TriggerPeriodicOTATrustAssetChecks(kOTABackgroundQueue);
2137 #endif
2138
2139 return CFRetainSafe(otapkiRef->_appleCAs);
2140 }
2141
2142 bool SecOTAPKIKillSwitchEnabled(SecOTAPKIRef otapkiRef, CFStringRef key) {
2143 if (NULL == otapkiRef || NULL == key) {
2144 return false;
2145 }
2146 if (CFEqualSafe(key, kOTAPKIKillSwitchCT)) {
2147 return otapkiRef->_ctKillSwitch;
2148 }
2149 return false;
2150 }
2151
2152 /* Returns an array of certificate data (CFDataRef) */
2153 CFArrayRef SecOTAPKICopyCurrentEscrowCertificates(uint32_t escrowRootType, CFErrorRef* error) {
2154 SecOTAPKIRef otapkiref = SecOTAPKICopyCurrentOTAPKIRef();
2155 if (NULL == otapkiref) {
2156 SecError(errSecInternal, error, CFSTR("Unable to get the current OTAPKIRef"));
2157 return NULL;
2158 }
2159
2160 CFArrayRef result = SecOTAPKICopyEscrowCertificates(escrowRootType, otapkiref);
2161 CFRelease(otapkiref);
2162
2163 if (NULL == result) {
2164 SecError(errSecInternal, error, CFSTR("Could not get escrow certificates from the current OTAPKIRef"));
2165 }
2166 return result;
2167 }
2168
2169 uint64_t SecOTAPKIGetCurrentTrustStoreVersion(CFErrorRef* error){
2170 SecOTAPKIRef otapkiref = SecOTAPKICopyCurrentOTAPKIRef();
2171 if (NULL == otapkiref) {
2172 SecError(errSecInternal, error, CFSTR("Unable to get the current OTAPKIRef"));
2173 return 0;
2174 }
2175
2176 return otapkiref->_trustStoreVersion;
2177 }
2178
2179 uint64_t SecOTAPKIGetCurrentAssetVersion(CFErrorRef* error) {
2180 SecOTAPKIRef otapkiref = SecOTAPKICopyCurrentOTAPKIRef();
2181 if (NULL == otapkiref) {
2182 SecError(errSecInternal, error, CFSTR("Unable to get the current OTAPKIRef"));
2183 return 0;
2184 }
2185
2186 return otapkiref->_assetVersion;
2187 }
2188
2189 uint64_t SecOTAPKIResetCurrentAssetVersion(CFErrorRef* error) {
2190 uint64_t system_version = GetSystemVersion((__bridge CFStringRef)kOTATrustContentVersionKey);
2191
2192 dispatch_sync(kOTAQueue, ^{
2193 kCurrentOTAPKIRef->_assetVersion = system_version;
2194 CFReleaseNull(kCurrentOTAPKIRef->_lastAssetCheckIn);
2195 kCurrentOTAPKIRef->_lastAssetCheckIn = NULL;
2196 });
2197
2198 #if !TARGET_OS_BRIDGE
2199 DeleteAssetFromDisk();
2200 #endif
2201 return system_version;
2202 }
2203
2204 uint64_t SecOTAPKISignalNewAsset(CFErrorRef* error) {
2205 NSError *nserror = nil;
2206 uint64_t version = 0;
2207 #if !TARGET_OS_BRIDGE
2208 if (SecOTAPKIIsSystemTrustd()) {
2209 if (!DownloadOTATrustAsset(NO, YES, &nserror) && error) {
2210 *error = CFRetainSafe((__bridge CFErrorRef)nserror);
2211 }
2212 } else {
2213 SecError(errSecServiceNotAvailable, error, CFSTR("This function may ony be performed by the system trustd."));
2214 }
2215 version = GetAssetVersion(nil);
2216 #else
2217 SecError(errSecUnsupportedService, error, CFSTR("This function is not available on this platform"));
2218 version = GetAssetVersion(error);
2219 #endif
2220 return version;
2221 }