]> git.saurik.com Git - apple/security.git/blob - OSX/sec/securityd/OTATrustUtilities.m
Security-58286.51.6.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 <sys/syslimits.h>
37 #include <sys/mman.h>
38 #include <sys/stat.h>
39 #include <CoreFoundation/CoreFoundation.h>
40 #include <ftw.h>
41 #include "SecFramework.h"
42 #include <pthread.h>
43 #include <sys/param.h>
44 #include <stdlib.h>
45 #include <utilities/SecCFRelease.h>
46 #include <utilities/SecCFError.h>
47 #include <utilities/SecCFWrappers.h>
48 #include <Security/SecBasePriv.h>
49 #include <Security/SecCertificatePriv.h>
50 #include <Security/SecFramework.h>
51 #include <dispatch/dispatch.h>
52 #include <CommonCrypto/CommonDigest.h>
53 #include <securityd/SecPinningDb.h>
54
55 #if !TARGET_OS_BRIDGE
56 #import <MobileAsset/MAAsset.h>
57 #import <MobileAsset/MAAssetQuery.h>
58 #include <notify.h>
59 #include <utilities/sec_action.h>
60 #include <utilities/SecFileLocations.h>
61 #import <securityd/SecTrustLoggingServer.h>
62 #endif
63
64 #if TARGET_OS_OSX
65 #import <MobileAsset/MobileAsset.h>
66 #endif
67
68 static inline bool isNSNumber(id nsType) {
69 return nsType && [nsType isKindOfClass:[NSNumber class]];
70 }
71
72 static inline bool isNSDictionary(id nsType) {
73 return nsType && [nsType isKindOfClass:[NSDictionary class]];
74 }
75
76 static inline bool isNSArray(id nsType) {
77 return nsType && [nsType isKindOfClass:[NSArray class]];
78 }
79
80 #define SECURITYD_ROLE_ACCOUNT 64
81 #define ROOT_ACCOUNT 0
82
83 bool SecOTAPKIIsSystemTrustd() {
84 static bool result = false;
85 static dispatch_once_t onceToken;
86 dispatch_once(&onceToken, ^{
87 #ifdef NO_SERVER
88 // Test app running as securityd
89 #elif TARGET_OS_IPHONE
90 if (getuid() == SECURITYD_ROLE_ACCOUNT)
91 #else
92 if (getuid() == ROOT_ACCOUNT)
93 #endif
94 {
95 result = true;
96 }
97 });
98 return result;
99 }
100
101 /* MARK: - */
102 /* MARK: System Trust Store */
103 static CFStringRef kSecSystemTrustStoreBundlePath = CFSTR("/System/Library/Security/Certificates.bundle");
104
105 CFGiblisGetSingleton(CFBundleRef, SecSystemTrustStoreGetBundle, bundle, ^{
106 CFStringRef bundlePath = NULL;
107 #if TARGET_OS_SIMULATOR
108 char *simulatorRoot = getenv("SIMULATOR_ROOT");
109 if (simulatorRoot)
110 bundlePath = CFStringCreateWithFormat(NULL, NULL, CFSTR("%s%@"), simulatorRoot, kSecSystemTrustStoreBundlePath);
111 #endif
112 if (!bundlePath)
113 bundlePath = CFRetainSafe(kSecSystemTrustStoreBundlePath);
114 CFURLRef url = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, bundlePath, kCFURLPOSIXPathStyle, true);
115 *bundle = (url) ? CFBundleCreate(kCFAllocatorDefault, url) : NULL;
116 CFReleaseSafe(url);
117 CFReleaseSafe(bundlePath);
118 })
119
120 static CFURLRef SecSystemTrustStoreCopyResourceURL(CFStringRef resourceName,
121 CFStringRef resourceType, CFStringRef subDirName) {
122 CFURLRef url = NULL;
123 CFBundleRef bundle = SecSystemTrustStoreGetBundle();
124 if (bundle) {
125 url = CFBundleCopyResourceURL(bundle, resourceName,
126 resourceType, subDirName);
127 }
128 if (!url) {
129 secwarning("resource: %@.%@ in %@ not found", resourceName,
130 resourceType, subDirName);
131 }
132 return url;
133 }
134
135 static NSURL *SecSystemTrustStoreCopyResourceNSURL(NSString *resourceFileName) {
136 CFBundleRef bundle = SecSystemTrustStoreGetBundle();
137 if (!bundle) {
138 return NULL;
139 }
140 NSURL *resourceDir = CFBridgingRelease(CFBundleCopyResourcesDirectoryURL(bundle));
141 if (!resourceDir) {
142 return NULL;
143 }
144 NSURL *fileURL = [NSURL URLWithString:resourceFileName
145 relativeToURL:resourceDir];
146 if (!fileURL) {
147 secwarning("resource: %@ not found", resourceFileName);
148 }
149 return fileURL;
150 }
151
152 static CFDataRef SecSystemTrustStoreCopyResourceContents(CFStringRef resourceName,
153 CFStringRef resourceType, CFStringRef subDirName) {
154 CFURLRef url = SecSystemTrustStoreCopyResourceURL(resourceName, resourceType, subDirName);
155 CFDataRef data = NULL;
156 if (url) {
157 SInt32 error;
158 if (!CFURLCreateDataAndPropertiesFromResource(kCFAllocatorDefault,
159 url, &data, NULL, NULL, &error)) {
160 secwarning("read: %ld", (long) error);
161 }
162 CFRelease(url);
163 }
164 return data;
165 }
166
167 /* MARK: - */
168 /* MARK: MobileAsset Updates */
169 // MARK: Forward Declarations
170 static uint64_t GetAssetVersion(void);
171 #if !TARGET_OS_BRIDGE
172 static BOOL UpdateFromAsset(NSURL *localURL, NSNumber *asset_version);
173 #endif
174
175 // MARK: Constants
176 NSString *kOTATrustContentVersionKey = @"MobileAssetContentVersion";
177 NSString *kOTATrustContextFilename = @"OTAPKIContext.plist";
178 NSString *kOTATrustTrustedCTLogsFilename = @"TrustedCTLogs.plist";
179 NSString *kOTATrustAnalyticsSamplingRatesFilename = @"AnalyticsSamplingRates.plist";
180 NSString *kOTATrustAppleCertifcateAuthoritiesFilename = @"AppleCertificateAuthorities.plist";
181
182 #if !TARGET_OS_BRIDGE
183 const NSString *OTATrustMobileAssetType = @"com.apple.MobileAsset.PKITrustSupplementals";
184 #define kOTATrustMobileAssetNotification "com.apple.MobileAsset.PKITrustSupplementals.cached-metadata-updated"
185 #define kOTATrustOnDiskAssetNotification "com.apple.trustd.asset-updated"
186 const NSUInteger OTATrustMobileAssetCompatibilityVersion = 1;
187 #define kOTATrustDefaultUpdatePeriod 60*60*12 // 12 hours
188 #define kOTATrustMinimumUpdatePeriod 60*5 // 5 min
189
190 #if TARGET_OS_OSX
191 const CFStringRef kSecSUPrefDomain = CFSTR("com.apple.SoftwareUpdate");
192 const CFStringRef kSecSUScanPrefConfigDataInstallKey = CFSTR("ConfigDataInstall");
193 #endif
194
195 // MARK: Helper functions
196 typedef enum {
197 OTATrustLogLevelDebug,
198 OTATrustLogLevelInfo,
199 OTATrustLogLevelNotice,
200 OTATrustLogLevelError,
201 } OTATrustLogLevel;
202
203 static void MakeOTATrustError(NSError **error, OTATrustLogLevel level, OSStatus errCode, NSString *format,...) NS_FORMAT_FUNCTION(4,5);
204
205 static void LogLocally(OTATrustLogLevel level, NSString *errorString) {
206 switch (level) {
207 case OTATrustLogLevelDebug:
208 secdebug("OTATrust", "%@", errorString);
209 break;
210 case OTATrustLogLevelInfo:
211 secinfo("OTATrust", "%@", errorString);
212 break;
213 case OTATrustLogLevelNotice:
214 secnotice("OTATrust", "%@", errorString);
215 break;
216 case OTATrustLogLevelError:
217 secerror("OTATrust: %@", errorString);
218 break;
219 }
220 }
221
222 static void LogRemotely(OTATrustLogLevel level, NSError **error) {
223 #if ENABLE_TRUSTD_ANALYTICS
224 /* only report errors and notices */
225 if (error && level == OTATrustLogLevelError) {
226 [[TrustdHealthAnalytics logger] logResultForEvent:TrustdHealthAnalyticsEventOTAPKIEvent hardFailure:YES result:*error];
227 } else if (error && level == OTATrustLogLevelNotice) {
228 [[TrustdHealthAnalytics logger] logResultForEvent:TrustdHealthAnalyticsEventOTAPKIEvent hardFailure:NO result:*error];
229 }
230 #endif // ENABLE_TRUSTD_ANALYTICS
231 }
232
233 static void MakeOTATrustError(NSError **error, OTATrustLogLevel level, OSStatus errCode, NSString *format,...) {
234 va_list args;
235 va_start(args, format);
236 NSString *formattedString = nil;
237 if (format) {
238 formattedString = [[NSString alloc] initWithFormat:format arguments:args];
239 }
240 if (error) {
241 NSMutableDictionary *userInfo = [[NSMutableDictionary alloc] init];
242 if (format) {
243 [userInfo setObject:formattedString forKey:NSLocalizedDescriptionKey];
244 }
245
246 *error = [NSError errorWithDomain:NSOSStatusErrorDomain
247 code:errCode
248 userInfo:userInfo];
249 }
250
251 LogLocally(level, formattedString);
252 LogRemotely(level, error);
253 va_end(args);
254 }
255
256 static BOOL CanCheckMobileAsset(void) {
257 BOOL result = YES;
258 #if TARGET_OS_OSX
259 /* Check the user's SU preferences to determine if "Install system data files" is off */
260 if (!CFPreferencesSynchronize(kSecSUPrefDomain, kCFPreferencesAnyUser, kCFPreferencesCurrentHost)) {
261 secerror("OTATrust: unable to synchronize SoftwareUpdate prefs");
262 return NO;
263 }
264
265 id value = nil;
266 if (CFPreferencesAppValueIsForced(kSecSUScanPrefConfigDataInstallKey, kSecSUPrefDomain)) {
267 value = CFBridgingRelease(CFPreferencesCopyAppValue(kSecSUScanPrefConfigDataInstallKey, kSecSUPrefDomain));
268 } else {
269 value = CFBridgingRelease(CFPreferencesCopyValue(kSecSUScanPrefConfigDataInstallKey, kSecSUPrefDomain,
270 kCFPreferencesAnyUser, kCFPreferencesCurrentHost));
271 }
272 if (isNSNumber(value)) {
273 result = [value boolValue];
274 }
275
276 if (!result) { secnotice("OTATrust", "User has disabled system data installation."); }
277
278 /* MobileAsset.framework isn't mastered into the BaseSystem. Check that the MA classes are linked. */
279 if (![ASAssetQuery class] || ![ASAsset class] || ![MAAssetQuery class] || ![MAAsset class]) {
280 secnotice("OTATrust", "Weak-linked MobileAsset framework missing.");
281 result = NO;
282 }
283 #endif
284 return result;
285 }
286
287 static BOOL ShouldUpdateWithAsset(NSNumber *asset_version) {
288 if (![asset_version isKindOfClass:[NSNumber class]]) {
289 return NO;
290 }
291 CFErrorRef error = nil;
292 uint64_t current_version = SecOTAPKIGetCurrentAssetVersion(&error);
293 if (error) {
294 CFReleaseNull(error);
295 return NO;
296 }
297 if ([asset_version compare:[NSNumber numberWithUnsignedLongLong:current_version]] == NSOrderedDescending) {
298 return YES;
299 }
300 return NO;
301 }
302
303 static bool verify_create_path(const char *path) {
304 int ret = mkpath_np(path, 0755);
305 if (!(ret == 0 || ret == EEXIST)) {
306 secerror("could not create path: %s (%s)", path, strerror(ret));
307 return false;
308 }
309 return true;
310 }
311
312 // MARK: File management functions
313 static NSURL *GetAssetFileURL(NSString *filename) {
314 /* Make sure the /Library/Keychains directory is there */
315 #if TARGET_OS_IPHONE
316 NSURL *keychainsDirectory = CFBridgingRelease(SecCopyURLForFileInKeychainDirectory(nil));
317 #else
318 NSURL *keychainsDirectory = [NSURL fileURLWithFileSystemRepresentation:"/Library/Keychains/" isDirectory:YES relativeToURL:nil];
319 #endif
320 NSURL *directory = [keychainsDirectory URLByAppendingPathComponent:@"SupplementalsAssets/" isDirectory:YES];
321 if (!verify_create_path([directory fileSystemRepresentation])) {
322 return nil;
323 }
324
325 if (filename) {
326 return [directory URLByAppendingPathComponent:filename];
327 } else {
328 return directory;
329 }
330 }
331
332 static void DeleteFileWithName(NSString *filename) {
333 NSFileManager *fileManager = [NSFileManager defaultManager];
334 NSError *error = nil;
335 [fileManager removeItemAtURL:GetAssetFileURL(filename) error:&error];
336 if (error) {
337 secerror("OTATrust: failed to remove %@: %@", filename, error);
338 }
339 }
340
341 static void DeleteAssetFromDisk(void) {
342 if (SecOTAPKIIsSystemTrustd()) {
343 DeleteFileWithName(kOTATrustContextFilename);
344 DeleteFileWithName(kOTATrustTrustedCTLogsFilename);
345 DeleteFileWithName(kOTATrustAnalyticsSamplingRatesFilename);
346 DeleteFileWithName(kOTATrustAppleCertifcateAuthoritiesFilename);
347 }
348 }
349
350 static BOOL WriteAssetVersionToDisk(NSNumber *asset_version) {
351 if (SecOTAPKIIsSystemTrustd()) {
352 NSError *error = nil;
353 NSDictionary *version_dict = @{ kOTATrustContentVersionKey : asset_version };
354 [version_dict writeToURL:GetAssetFileURL(kOTATrustContextFilename) error:&error];
355 if (error) {
356 secerror("OTATrust: unable to write asset version to disk: %@", error);
357 LogRemotely(OTATrustLogLevelError, &error);
358 return NO;
359 }
360 return YES;
361 }
362 return NO;
363 }
364
365
366 static BOOL CopyFileToDisk(NSString *filename, NSURL *localURL) {
367 if (SecOTAPKIIsSystemTrustd()) {
368 NSFileManager * fileManager = [NSFileManager defaultManager];
369 NSError *error = nil;
370 [fileManager copyItemAtURL:localURL toURL:GetAssetFileURL(filename) error:&error];
371 if (error) {
372 secerror("OTATrust: unable to write CT logs to disk: %@", error);
373 LogRemotely(OTATrustLogLevelError, &error);
374 return NO;
375 }
376 return YES;
377 }
378 return NO;
379 }
380
381 // MARK: Fetch and Update Functions
382 #if TARGET_OS_IPHONE
383 static NSNumber *UpdateAndPurgeAsset(MAAsset *asset, NSNumber *asset_version, NSError **error) {
384 if (SecPinningDbUpdateFromURL((__bridge CFURLRef)[asset getLocalFileUrl]) &&
385 UpdateFromAsset([asset getLocalFileUrl], asset_version)) {
386 secnotice("OTATrust", "finished update to version %@ from installed asset. purging asset.", asset_version);
387 #if ENABLE_TRUSTD_ANALYTICS
388 [[TrustdHealthAnalytics logger] logSuccessForEventNamed:TrustdHealthAnalyticsEventOTAPKIEvent];
389 #endif // ENABLE_TRUSTD_ANALYTICS
390 [asset purge:^(MAPurgeResult purge_result) {
391 if (purge_result != MAPurgeSucceeded) {
392 secerror("OTATrust: purge failed: %ld", (long)purge_result);
393 }
394 }];
395 return asset_version;
396 } else {
397 MakeOTATrustError(error, OTATrustLogLevelError, errSecCallbackFailed,
398 @"Failed to install new asset version %@ from %@", asset_version, [asset getLocalFileUrl]);
399 return nil;
400 }
401 }
402
403 static BOOL DownloadOTATrustAsset(BOOL isLocalOnly, BOOL wait, NSError **error) {
404 if (!CanCheckMobileAsset()) {
405 MakeOTATrustError(error, OTATrustLogLevelNotice, errSecServiceNotAvailable,
406 @"MobileAsset disabled, skipping check.");
407 return NO;
408 }
409
410 __block NSNumber *updated_version = nil;
411 __block dispatch_semaphore_t done = wait ? dispatch_semaphore_create(0) : nil;
412 __block NSError *ma_error = nil;
413 secnotice("OTATrust", "begin MobileAsset query for catalog");
414 [MAAsset startCatalogDownload:(NSString *)OTATrustMobileAssetType then:^(MADownLoadResult result) {
415 if (result != MADownloadSucceesful) {
416 MakeOTATrustError(&ma_error, OTATrustLogLevelError, errSecInternal,
417 @"failed to download catalog: %ld", (long)result);
418 return;
419 }
420 MAAssetQuery *query = [[MAAssetQuery alloc] initWithType:(NSString *)OTATrustMobileAssetType];
421 [query augmentResultsWithState:true];
422
423 secnotice("OTATrust", "begin MobileAsset metadata sync request");
424 MAQueryResult queryResult = [query queryMetaDataSync];
425 if (queryResult != MAQuerySucceesful) {
426 MakeOTATrustError(&ma_error, OTATrustLogLevelError, errSecInternal,
427 @"failed to query MobileAsset metadata: %ld", (long)queryResult);
428 return;
429 }
430
431 if (!query.results) {
432 MakeOTATrustError(&ma_error, OTATrustLogLevelError, errSecInternal,
433 @"no results in MobileAsset query");
434 return;
435 }
436
437 bool began_async_job = false;
438 for (MAAsset *asset in query.results) {
439 /* Check Compatibility Version against this software version */
440 NSNumber *compatibilityVersion = [asset assetProperty:@"_CompatibilityVersion"];
441 if (!isNSNumber(compatibilityVersion) ||
442 [compatibilityVersion unsignedIntegerValue] != OTATrustMobileAssetCompatibilityVersion) {
443 MakeOTATrustError(&ma_error, OTATrustLogLevelNotice, errSecIncompatibleVersion,
444 @"skipping asset because Compatibility Version doesn't match %@", compatibilityVersion);
445 continue;
446 }
447 /* Check Content Version agains the current content version */
448 NSNumber *asset_version = [asset assetProperty:@"_ContentVersion"];
449 if (!ShouldUpdateWithAsset(asset_version)) {
450 MakeOTATrustError(&ma_error, OTATrustLogLevelNotice, errSecDuplicateItem,
451 @"skipping asset because we already have _ContentVersion %@ (or newer)", asset_version);
452 continue;
453 }
454
455 switch (asset.state) {
456 default:
457 MakeOTATrustError(&ma_error, OTATrustLogLevelError, errSecInternal,
458 @"unknown asset state %ld", (long)asset.state);
459 continue;
460 case MAInstalled:
461 /* The asset is already in the cache, get it from disk. */
462 secdebug("OTATrust", "OTATrust asset already installed");
463 updated_version = UpdateAndPurgeAsset(asset, asset_version, &ma_error);
464 break;
465 case MAUnknown:
466 MakeOTATrustError(&ma_error, OTATrustLogLevelError, errSecInternal,
467 @"asset is unknown");
468 continue;
469 case MADownloading:
470 secnotice("OTATrust", "asset is downloading");
471 /* fall through */
472 case MANotPresent:
473 secnotice("OTATrust", "begin download of OTATrust asset");
474 began_async_job = true;
475 [asset startDownload:^(MADownLoadResult downloadResult) {
476 if (downloadResult != MADownloadSucceesful) {
477 MakeOTATrustError(&ma_error, OTATrustLogLevelError, errSecInternal,
478 @"failed to download asset: %ld", (long)downloadResult);
479 return;
480 }
481 updated_version = UpdateAndPurgeAsset(asset, asset_version, &ma_error);
482 if (wait) {
483 dispatch_semaphore_signal(done);
484 }
485 }];
486 break;
487 } /* switch (asset.state) */
488 } /* for (MAAsset.. */
489 if (wait && !began_async_job) {
490 dispatch_semaphore_signal(done);
491 }
492 }]; /* [MAAsset startCatalogDownload: ] */
493
494 /* If the caller is waiting for a response, wait up to one minute for the update to complete.
495 * If the MAAsset callback does not complete in that time, report a timeout.
496 * If the MAAsset callback completes and did not successfully update, it should report an error;
497 * forward that error to the caller.
498 * If the MAAsset callback completes and did not update and did not provide an error; report
499 * an unknown error. */
500 BOOL result = NO;
501 if (wait) {
502 if (dispatch_semaphore_wait(done, dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC * 60)) != 0) {
503 MakeOTATrustError(error, OTATrustLogLevelError, errSecNetworkFailure,
504 @"Failed to get asset metadata within 1 minute.");
505 } else {
506 result = (updated_version != nil);
507 if (error && ma_error) {
508 *error = ma_error;
509 } else if (!result) {
510 MakeOTATrustError(error, OTATrustLogLevelError, errSecInternalComponent,
511 @"Unknown error occurred.");
512 }
513 }
514 }
515 return result;
516 }
517 #else /* !TARGET_OS_IPHONE */
518 /* <rdar://problem/30879827> MobileAssetV2 fails on macOS, so use V1 */
519 static NSNumber *UpdateAndPurgeAsset(ASAsset *asset, NSNumber *asset_version, NSError ** error) {
520 if (SecPinningDbUpdateFromURL((__bridge CFURLRef)[asset localURL]) &&
521 UpdateFromAsset([asset localURL], asset_version)) {
522 secnotice("OTATrust", "finished update to version %@ from installed asset. purging asset.", asset_version);
523 #if ENABLE_TRUSTD_ANALYTICS
524 [[TrustdHealthAnalytics logger] logSuccessForEventNamed:TrustdHealthAnalyticsEventOTAPKIEvent];
525 #endif // ENABLE_TRUSTD_ANALYTICS
526 [asset purge:^(NSError *ma_error) {
527 if (error) {
528 secerror("OTATrust: purge failed %@", ma_error);
529 }
530 }];
531 return asset_version;
532 } else {
533 MakeOTATrustError(error, OTATrustLogLevelError, errSecCallbackFailed,
534 @"Failed to install new asset version %@ from %@", asset_version, [asset localURL]);
535 return nil;
536 }
537 }
538
539 static BOOL DownloadOTATrustAsset(BOOL isLocalOnly, BOOL wait, NSError **error) {
540 if (!CanCheckMobileAsset()) {
541 MakeOTATrustError(error, OTATrustLogLevelNotice, errSecServiceNotAvailable,
542 @"MobileAsset disabled, skipping check.");
543 return NO;
544 }
545
546 ASAssetQuery *query = [[ASAssetQuery alloc] initWithAssetType:(NSString *)OTATrustMobileAssetType];
547 [query setQueriesLocalAssetInformationOnly:isLocalOnly]; // Omitting this leads to a notifcation loop.
548 NSArray<ASAsset *>*query_results = [query runQueryAndReturnError:error];
549 if (!query_results) {
550 if (error) {
551 secerror("OTATrust: asset query failed: %@", *error);
552 LogRemotely(OTATrustLogLevelError, error);
553 }
554 return NO;
555 }
556
557 __block NSNumber *updated_version = nil;
558 __block NSError *handler_error = nil;
559 __block dispatch_semaphore_t done = wait ? dispatch_semaphore_create(0) : nil;
560 bool began_async_job = false;
561 for (ASAsset *asset in query_results) {
562 NSDictionary *attributes = [asset attributes];
563
564 NSNumber *compatibilityVersion = [attributes objectForKey:ASAttributeCompatibilityVersion];
565 if (!isNSNumber(compatibilityVersion) ||
566 [compatibilityVersion unsignedIntegerValue] != OTATrustMobileAssetCompatibilityVersion) {
567 MakeOTATrustError(error, OTATrustLogLevelNotice, errSecIncompatibleVersion,
568 @"skipping asset because Compatibility Version doesn't match %@", compatibilityVersion);
569 continue;
570 }
571
572 NSNumber *contentVersion = [attributes objectForKey:ASAttributeContentVersion];
573 if (!ShouldUpdateWithAsset(contentVersion)) {
574 MakeOTATrustError(error, OTATrustLogLevelNotice, errSecDuplicateItem,
575 @"skipping asset because we already have _ContentVersion %@ (or newer)", contentVersion);
576 continue;
577 }
578
579 ASProgressHandler OTATrustHandler = ^(NSDictionary *state, NSError *progressError){
580 NSString *operationState = nil;
581 if (progressError) {
582 MakeOTATrustError(&handler_error, OTATrustLogLevelError, errSecInternal,
583 @"OTATrust: asset download error: %@", progressError);
584 if (wait) {
585 dispatch_semaphore_signal(done);
586 }
587 return;
588 }
589
590 if (!state) {
591 MakeOTATrustError(&handler_error, OTATrustLogLevelError, errSecInternal,
592 @"OTATrust: no asset state in progress handler");
593 if (wait) {
594 dispatch_semaphore_signal(done);
595 }
596 return;
597 }
598
599 operationState = [state objectForKey:ASStateOperation];
600 secdebug("OTATrust", "Asset state is %@", operationState);
601
602 if (operationState && [operationState isEqualToString:ASOperationCompleted]) {
603 updated_version = UpdateAndPurgeAsset(asset, contentVersion, &handler_error);
604 if (wait) {
605 dispatch_semaphore_signal(done);
606 }
607 }
608 /* Other states keep calling our progress handler until so don't signal */
609 };
610
611 switch ([asset state]) {
612 case ASAssetStateNotPresent:
613 secdebug("OTATrust", "OTATrust asset needs to be downloaded");
614 asset.progressHandler= OTATrustHandler;
615 asset.userInitiatedDownload = YES;
616 [asset beginDownloadWithOptions:@{ASDownloadOptionPriority : ASDownloadPriorityNormal}];
617 began_async_job = true;
618 break;
619 case ASAssetStateInstalled:
620 /* The asset is already in the cache, get it from disk. */
621 secdebug("OTATrust", "OTATrust asset already installed");
622 updated_version = UpdateAndPurgeAsset(asset, contentVersion, error);
623 break;
624 case ASAssetStatePaused:
625 secdebug("OTATrust", "OTATrust asset download paused");
626 asset.progressHandler = OTATrustHandler;
627 asset.userInitiatedDownload = YES;
628 if (![asset resumeDownloadAndReturnError:error]) {
629 if (error) {
630 secerror("OTATrust: failed to resume download of asset: %@", *error);
631 LogRemotely(OTATrustLogLevelError, error);
632 }
633 } else {
634 began_async_job = true;
635 }
636 break;
637 case ASAssetStateDownloading:
638 secdebug("OTATrust", "OTATrust asset downloading");
639 asset.progressHandler = OTATrustHandler;
640 asset.userInitiatedDownload = YES;
641 began_async_job = true;
642 break;
643 default:
644 MakeOTATrustError(error, OTATrustLogLevelError, errSecInternal,
645 @"unhandled asset state %ld", (long)asset.state);
646 continue;
647 }
648 }
649
650 /* If the caller is waiting for a response, wait up to one minute for the update to complete.
651 * If the OTATrustHandler does not complete in the time, report a timeout.
652 * If the OTATrustHandler completes and did not successfully update and it reported an error;
653 * forward that error to the caller. */
654 BOOL result = (updated_version != nil);
655 if (wait && began_async_job) {
656 if (dispatch_semaphore_wait(done, dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC * 60)) != 0) {
657 MakeOTATrustError(error, OTATrustLogLevelError, errSecNetworkFailure,
658 @"Failed to get asset metadata within 1 minute.");
659 } else {
660 /* finished an async job, update the result */
661 result = (updated_version != nil);
662 if (error && handler_error) {
663 *error = handler_error;
664 }
665 }
666 }
667
668 /* If we failed and don't know why, report an unknown error */
669 if (!result && error && (*error == NULL)) {
670 MakeOTATrustError(error, OTATrustLogLevelError, errSecInternalComponent,
671 @"Unknown error occurred.");
672 }
673 return result;
674 }
675 #endif /* !TARGET_OS_IPHONE */
676
677 static void InitializeOTATrustAsset(dispatch_queue_t queue) {
678 /* Only the "system" trustd does updates */
679 if (SecOTAPKIIsSystemTrustd()) {
680 /* Asynchronously ask MobileAsset for most recent asset. */
681 dispatch_async(queue, ^{
682 secnotice("OTATrust", "Initial check with MobileAsset for newer PKITrustSupplementals asset");
683 (void)DownloadOTATrustAsset(NO, NO, nil);
684 });
685
686 /* Register for changes in our asset */
687 if (CanCheckMobileAsset()) {
688 int out_token = 0;
689 notify_register_dispatch(kOTATrustMobileAssetNotification, &out_token, queue, ^(int __unused token) {
690 secnotice("OTATrust", "Got notification about a new PKITrustSupplementals asset from mobileassetd.");
691 (void)DownloadOTATrustAsset(YES, NO, nil);
692 });
693 }
694 } else {
695 /* Register for changes signaled by the system trustd */
696 secnotice("OTATrust", "Intializing listener for Asset changes from system trustd.");
697 int out_token = 0;
698 notify_register_dispatch(kOTATrustOnDiskAssetNotification, &out_token, queue, ^(int __unused token) {
699 secnotice("OTATrust", "Got notification about a new PKITrustSupplementals asset from system trustd.");
700 UpdateFromAsset(GetAssetFileURL(nil), [NSNumber numberWithUnsignedLongLong:GetAssetVersion()]);
701 });
702 }
703 }
704
705 static void TriggerPeriodicOTATrustAssetChecks(dispatch_queue_t queue) {
706 if (SecOTAPKIIsSystemTrustd()) {
707 static sec_action_t action;
708 static dispatch_once_t onceToken;
709 dispatch_once(&onceToken, ^{
710 NSUserDefaults *defaults = [[NSUserDefaults alloc] initWithSuiteName:@"com.apple.security"];
711 NSNumber *updateDeltas = [defaults valueForKey:@"PKITrustSupplementalsUpdatePeriod"];
712 int delta = kOTATrustDefaultUpdatePeriod;
713 if (isNSNumber(updateDeltas)) {
714 delta = [updateDeltas intValue];
715 if (delta < kOTATrustMinimumUpdatePeriod) {
716 delta = kOTATrustMinimumUpdatePeriod;
717 }
718 }
719 secnotice("OTATrust", "Setting periodic update delta to %d seconds", delta);
720 action = sec_action_create_with_queue(queue,"OTATrust", delta);
721 sec_action_set_handler(action, ^{
722 (void)DownloadOTATrustAsset(NO, NO, nil);
723 });
724 });
725 sec_action_perform(action);
726 }
727 }
728 #endif /* !TARGET_OS_BRIDGE */
729
730 /* MARK: - */
731 /* MARK: Initialization functions */
732 static CFPropertyListRef CFPropertyListCopyFromSystem(CFStringRef asset) {
733 CFPropertyListRef plist = NULL;
734 CFDataRef xmlData = SecSystemTrustStoreCopyResourceContents(asset, CFSTR("plist"), NULL);
735
736 if (xmlData) {
737 plist = CFPropertyListCreateWithData(kCFAllocatorDefault, xmlData, kCFPropertyListImmutable, NULL, NULL);
738 CFRelease(xmlData);
739 }
740
741 return plist;
742 }
743
744 static uint64_t GetSystemVersion(CFStringRef key) {
745 uint64_t system_version = 0;
746 int64_t asset_number = 0;
747
748 CFDataRef assetVersionData = SecSystemTrustStoreCopyResourceContents(CFSTR("AssetVersion"), CFSTR("plist"), NULL);
749 if (NULL != assetVersionData) {
750 CFPropertyListFormat propFormat;
751 CFDictionaryRef versionPlist = CFPropertyListCreateWithData(kCFAllocatorDefault, assetVersionData, 0, &propFormat, NULL);
752 if (NULL != versionPlist && CFDictionaryGetTypeID() == CFGetTypeID(versionPlist)) {
753 CFNumberRef versionNumber = (CFNumberRef)CFDictionaryGetValue(versionPlist, (const void *)key);
754 if (NULL != versionNumber){
755 CFNumberGetValue(versionNumber, kCFNumberSInt64Type, &asset_number);
756 if (asset_number < 0) { // Not valid
757 asset_number = 0;
758 }
759 system_version = (uint64_t)asset_number;
760 }
761 }
762 CFReleaseSafe(versionPlist);
763 CFReleaseSafe(assetVersionData);
764 }
765
766 return system_version;
767 }
768
769 static bool ShouldInitializeWithAsset(void) {
770 uint64_t system_version = GetSystemVersion((__bridge CFStringRef)kOTATrustContentVersionKey);
771 uint64_t asset_version = GetAssetVersion();
772
773 if (asset_version > system_version) {
774 secnotice("OTATrust", "Using asset v%llu instead of system v%llu", asset_version, system_version);
775 return true;
776 }
777 return false;
778 }
779
780 static CFSetRef CFSetCreateFromPropertyList(CFPropertyListRef plist) {
781 CFSetRef result = NULL;
782
783 if (plist) {
784 CFMutableSetRef tempSet = NULL;
785 if (CFGetTypeID(plist) == CFArrayGetTypeID()) {
786 tempSet = CFSetCreateMutable(kCFAllocatorDefault, 0, &kCFTypeSetCallBacks);
787 if (NULL == tempSet) {
788 return result;
789 }
790 CFArrayRef array = (CFArrayRef)plist;
791 CFIndex num_keys = CFArrayGetCount(array);
792 for (CFIndex idx = 0; idx < num_keys; idx++) {
793 CFDataRef data = (CFDataRef)CFArrayGetValueAtIndex(array, idx);
794 CFSetAddValue(tempSet, data);
795 }
796 } else {
797 return result;
798 }
799
800 result = tempSet;
801 }
802 return result;
803 }
804
805 static CF_RETURNS_RETAINED CFSetRef InitializeBlackList() {
806 CFPropertyListRef plist = CFPropertyListCopyFromSystem(CFSTR("Blocked"));
807 CFSetRef result = CFSetCreateFromPropertyList(plist);
808 CFReleaseSafe(plist);
809
810 return result;
811 }
812
813 static CF_RETURNS_RETAINED CFSetRef InitializeGrayList() {
814 CFPropertyListRef plist = CFPropertyListCopyFromSystem(CFSTR("GrayListedKeys"));
815 CFSetRef result = CFSetCreateFromPropertyList(plist);
816 CFReleaseSafe(plist);
817
818 return result;
819 }
820
821 static CF_RETURNS_RETAINED CFURLRef InitializePinningList() {
822 return SecSystemTrustStoreCopyResourceURL(CFSTR("CertificatePinning"), CFSTR("plist"), NULL);
823 }
824
825 static CF_RETURNS_RETAINED CFDictionaryRef InitializeAllowList() {
826 CFPropertyListRef allowList = CFPropertyListCopyFromSystem(CFSTR("Allowed"));
827
828 if (allowList && (CFGetTypeID(allowList) == CFDictionaryGetTypeID())) {
829 return allowList;
830 } else {
831 CFReleaseNull(allowList);
832 return NULL;
833 }
834 }
835
836 static CF_RETURNS_RETAINED CFArrayRef InitializeTrustedCTLogs() {
837 NSArray *trustedCTLogs = nil;
838 #if !TARGET_OS_BRIDGE
839 if (ShouldInitializeWithAsset()) {
840 trustedCTLogs = [NSArray arrayWithContentsOfURL:GetAssetFileURL(kOTATrustTrustedCTLogsFilename)];
841 if (!isNSArray(trustedCTLogs)) {
842 DeleteAssetFromDisk();
843 }
844 }
845 #endif
846 if (!isNSArray(trustedCTLogs)) {
847 trustedCTLogs = [NSArray arrayWithContentsOfURL:SecSystemTrustStoreCopyResourceNSURL(kOTATrustTrustedCTLogsFilename)];
848 }
849 if (isNSArray(trustedCTLogs)) {
850 return CFBridgingRetain(trustedCTLogs);
851 }
852 return NULL;
853 }
854
855 static CF_RETURNS_RETAINED CFDictionaryRef InitializeEVPolicyToAnchorDigestsTable() {
856 CFDictionaryRef result = NULL;
857 CFPropertyListRef evroots = CFPropertyListCopyFromSystem(CFSTR("EVRoots"));
858
859 if (evroots) {
860 if (CFGetTypeID(evroots) == CFDictionaryGetTypeID()) {
861 /* @@@ Ensure that each dictionary key is a dotted list of digits,
862 each value is an NSArrayRef and each element in the array is a
863 20 byte digest. */
864 result = (CFDictionaryRef)evroots;
865 }
866 else {
867 secwarning("EVRoot.plist is wrong type.");
868 CFRelease(evroots);
869 }
870 }
871
872 return result;
873 }
874
875 static CFIndex InitializeValidSnapshotVersion(CFIndex *outFormat) {
876 CFIndex validVersion = 0;
877 CFIndex validFormat = 0;
878 CFDataRef validVersionData = SecSystemTrustStoreCopyResourceContents(CFSTR("ValidUpdate"), CFSTR("plist"), NULL);
879 if (NULL != validVersionData)
880 {
881 CFPropertyListFormat propFormat;
882 CFDictionaryRef versionPlist = CFPropertyListCreateWithData(kCFAllocatorDefault, validVersionData, 0, &propFormat, NULL);
883 if (NULL != versionPlist && CFDictionaryGetTypeID() == CFGetTypeID(versionPlist))
884 {
885 CFNumberRef versionNumber = (CFNumberRef)CFDictionaryGetValue(versionPlist, (const void *)CFSTR("Version"));
886 if (NULL != versionNumber)
887 {
888 CFNumberGetValue(versionNumber, kCFNumberCFIndexType, &validVersion);
889 }
890 CFNumberRef formatNumber = (CFNumberRef)CFDictionaryGetValue(versionPlist, (const void *)CFSTR("Format"));
891 if (NULL != formatNumber)
892 {
893 CFNumberGetValue(formatNumber, kCFNumberCFIndexType, &validFormat);
894 }
895 }
896 CFReleaseSafe(versionPlist);
897 CFReleaseSafe(validVersionData);
898 }
899 if (outFormat) {
900 *outFormat = validFormat;
901 }
902 return validVersion;
903 }
904
905 static Boolean PathExists(const char* path, size_t* pFileSize) {
906 const char *checked_path = (path) ? path : "";
907 Boolean result = false;
908 struct stat sb;
909
910 if (NULL != pFileSize) {
911 *pFileSize = 0;
912 }
913
914 int stat_result = stat(checked_path, &sb);
915 result = (stat_result == 0);
916
917 if (result && !S_ISDIR(sb.st_mode)) {
918 // It is a file
919 if (NULL != pFileSize) {
920 *pFileSize = (size_t)sb.st_size;
921 }
922 }
923
924 return result;
925 }
926
927 static const char* InitializeValidSnapshotData(CFStringRef filename_str) {
928 char *result = NULL;
929 const char *base_error_str = "could not get valid snapshot";
930
931 CFURLRef valid_url = SecSystemTrustStoreCopyResourceURL(filename_str, CFSTR("sqlite3"), NULL);
932 if (NULL == valid_url) {
933 secerror("%s", base_error_str);
934 } else {
935 CFStringRef valid_str = CFURLCopyFileSystemPath(valid_url, kCFURLPOSIXPathStyle);
936 char file_path_buffer[PATH_MAX];
937 memset(file_path_buffer, 0, PATH_MAX);
938 if (NULL == valid_str) {
939 secerror("%s path", base_error_str);
940 } else {
941 const char *valid_cstr = CFStringGetCStringPtr(valid_str, kCFStringEncodingUTF8);
942 if (NULL == valid_cstr) {
943 if (CFStringGetCString(valid_str, file_path_buffer, PATH_MAX, kCFStringEncodingUTF8)) {
944 valid_cstr = file_path_buffer;
945 }
946 }
947 if (NULL == valid_cstr) {
948 secerror("%s path as UTF8 string", base_error_str);
949 } else {
950 asprintf(&result, "%s", valid_cstr);
951 }
952 }
953 CFReleaseSafe(valid_str);
954 }
955 CFReleaseSafe(valid_url);
956 if (result && !PathExists(result, NULL)) {
957 free(result);
958 result = NULL;
959 }
960 return (const char*)result;
961 }
962
963 static const char* InitializeValidDatabaseSnapshot() {
964 return InitializeValidSnapshotData(CFSTR("valid"));
965 }
966
967 static const uint8_t* MapFile(const char* path, size_t* out_file_size) {
968 int rtn, fd;
969 const uint8_t *buf = NULL;
970 struct stat sb;
971 size_t size = 0;
972
973 if (NULL == path || NULL == out_file_size) {
974 return NULL;
975 }
976
977 *out_file_size = 0;
978
979 fd = open(path, O_RDONLY);
980 if (fd < 0) { return NULL; }
981 rtn = fstat(fd, &sb);
982 if (rtn || (sb.st_size > (off_t) ((UINT32_MAX >> 1)-1))) {
983 close(fd);
984 return NULL;
985 }
986 size = (size_t)sb.st_size;
987
988 buf = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
989 if (!buf || buf == MAP_FAILED) {
990 secerror("unable to map %s (errno %d)", path, errno);
991 close(fd);
992 return NULL;
993 }
994
995 close(fd);
996 *out_file_size = size;
997 return buf;
998 }
999
1000 static void UnMapFile(void* mapped_data, size_t data_size) {
1001 if (!mapped_data) {
1002 return;
1003 }
1004 int rtn = munmap(mapped_data, data_size);
1005 if (rtn != 0) {
1006 secerror("unable to unmap %ld bytes at %p (error %d)", data_size, mapped_data, rtn);
1007 }
1008 }
1009
1010 struct index_record {
1011 unsigned char hash[CC_SHA1_DIGEST_LENGTH];
1012 uint32_t offset;
1013 };
1014 typedef struct index_record index_record;
1015
1016 static bool InitializeAnchorTable(CFDictionaryRef* pLookupTable, const char** ppAnchorTable) {
1017
1018 bool result = false;
1019
1020 if (NULL == pLookupTable || NULL == ppAnchorTable) {
1021 return result;
1022 }
1023
1024 *pLookupTable = NULL;
1025 *ppAnchorTable = NULL;;
1026
1027 CFDataRef cert_index_file_data = NULL;
1028 char file_path_buffer[PATH_MAX];
1029 CFURLRef table_data_url = NULL;
1030 CFStringRef table_data_cstr_path = NULL;
1031 const char* table_data_path = NULL;
1032 const index_record* pIndex = NULL;
1033 size_t index_offset = 0;
1034 size_t index_data_size = 0;
1035 CFMutableDictionaryRef anchorLookupTable = NULL;
1036 uint32_t offset_int_value = 0;
1037 CFNumberRef index_offset_value = NULL;
1038 CFDataRef index_hash = NULL;
1039 CFMutableArrayRef offsets = NULL;
1040 Boolean release_offset = false;
1041
1042 char* local_anchorTable = NULL;
1043 size_t local_anchorTableSize = 0;
1044
1045 // local_anchorTable is still NULL so the asset in the system trust store bundle needs to be used.
1046 CFReleaseSafe(cert_index_file_data);
1047 cert_index_file_data = SecSystemTrustStoreCopyResourceContents(CFSTR("certsIndex"), CFSTR("data"), NULL);
1048 if (!cert_index_file_data) {
1049 secerror("could not find certsIndex");
1050 }
1051 table_data_url = SecSystemTrustStoreCopyResourceURL(CFSTR("certsTable"), CFSTR("data"), NULL);
1052 if (!table_data_url) {
1053 secerror("could not find certsTable");
1054 }
1055
1056 if (NULL != table_data_url) {
1057 table_data_cstr_path = CFURLCopyFileSystemPath(table_data_url, kCFURLPOSIXPathStyle);
1058 if (NULL != table_data_cstr_path) {
1059 memset(file_path_buffer, 0, PATH_MAX);
1060 table_data_path = CFStringGetCStringPtr(table_data_cstr_path, kCFStringEncodingUTF8);
1061 if (NULL == table_data_path) {
1062 if (CFStringGetCString(table_data_cstr_path, file_path_buffer, PATH_MAX, kCFStringEncodingUTF8)) {
1063 table_data_path = file_path_buffer;
1064 }
1065 }
1066 local_anchorTable = (char *)MapFile(table_data_path, &local_anchorTableSize);
1067 CFReleaseSafe(table_data_cstr_path);
1068 }
1069 }
1070 CFReleaseSafe(table_data_url);
1071
1072 if (NULL == local_anchorTable || NULL == cert_index_file_data) {
1073 // we are in trouble
1074 if (NULL != local_anchorTable) {
1075 UnMapFile(local_anchorTable, local_anchorTableSize);
1076 local_anchorTable = NULL;
1077 local_anchorTableSize = 0;
1078 }
1079 CFReleaseSafe(cert_index_file_data);
1080 return result;
1081 }
1082
1083 // ------------------------------------------------------------------------
1084 // Now that the locations of the files are known and the table file has
1085 // been mapped into memory, create a dictionary that maps the SHA1 hash of
1086 // normalized issuer to the offset in the mapped anchor table file which
1087 // contains a index_record to the correct certificate
1088 // ------------------------------------------------------------------------
1089 pIndex = (const index_record*)CFDataGetBytePtr(cert_index_file_data);
1090 index_data_size = CFDataGetLength(cert_index_file_data);
1091
1092 anchorLookupTable = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
1093 &kCFTypeDictionaryKeyCallBacks,
1094 &kCFTypeDictionaryValueCallBacks);
1095
1096 for (index_offset = index_data_size; index_offset > 0; index_offset -= sizeof(index_record), pIndex++) {
1097 offset_int_value = pIndex->offset;
1098
1099 index_offset_value = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &offset_int_value);
1100 index_hash = CFDataCreate(kCFAllocatorDefault, pIndex->hash, CC_SHA1_DIGEST_LENGTH);
1101
1102 // see if the dictionary already has this key
1103 release_offset = false;
1104 offsets = (CFMutableArrayRef)CFDictionaryGetValue(anchorLookupTable, index_hash);
1105 if (NULL == offsets) {
1106 offsets = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
1107 release_offset = true;
1108 }
1109
1110 // Add the offset
1111 CFArrayAppendValue(offsets, index_offset_value);
1112
1113 // set the key value pair in the dictionary
1114 CFDictionarySetValue(anchorLookupTable, index_hash, offsets);
1115
1116 CFRelease(index_offset_value);
1117 CFRelease(index_hash);
1118 if (release_offset) {
1119 CFRelease(offsets);
1120 }
1121 }
1122
1123 CFRelease(cert_index_file_data);
1124
1125 if (NULL != anchorLookupTable && NULL != local_anchorTable) {
1126 *pLookupTable = anchorLookupTable;
1127 *ppAnchorTable = local_anchorTable;
1128 result = true;
1129 } else {
1130 CFReleaseSafe(anchorLookupTable);
1131 if (NULL != local_anchorTable) {
1132 UnMapFile(local_anchorTable, local_anchorTableSize);
1133 local_anchorTable = NULL;
1134 local_anchorTableSize = 0;
1135 }
1136 }
1137
1138 return result;
1139 }
1140
1141 static void InitializeEscrowCertificates(CFArrayRef *escrowRoots, CFArrayRef *escrowPCSRoots) {
1142 CFDataRef file_data = SecSystemTrustStoreCopyResourceContents(CFSTR("AppleESCertificates"), CFSTR("plist"), NULL);
1143
1144 if (NULL == file_data) {
1145 return;
1146 }
1147
1148 CFPropertyListFormat propFormat;
1149 CFDictionaryRef certsDictionary = CFPropertyListCreateWithData(kCFAllocatorDefault, file_data, 0, &propFormat, NULL);
1150 if (NULL != certsDictionary && CFDictionaryGetTypeID() == CFGetTypeID((CFTypeRef)certsDictionary)) {
1151 CFArrayRef certs = (CFArrayRef)CFDictionaryGetValue(certsDictionary, CFSTR("ProductionEscrowKey"));
1152 if (NULL != certs && CFArrayGetTypeID() == CFGetTypeID((CFTypeRef)certs) && CFArrayGetCount(certs) > 0) {
1153 *escrowRoots = CFArrayCreateCopy(kCFAllocatorDefault, certs);
1154 }
1155 CFArrayRef pcs_certs = (CFArrayRef)CFDictionaryGetValue(certsDictionary, CFSTR("ProductionPCSEscrowKey"));
1156 if (NULL != pcs_certs && CFArrayGetTypeID() == CFGetTypeID((CFTypeRef)pcs_certs) && CFArrayGetCount(pcs_certs) > 0) {
1157 *escrowPCSRoots = CFArrayCreateCopy(kCFAllocatorDefault, pcs_certs);
1158 }
1159 }
1160 CFReleaseSafe(certsDictionary);
1161 CFRelease(file_data);
1162 }
1163
1164 static CF_RETURNS_RETAINED CFDictionaryRef InitializeEventSamplingRates() {
1165 NSDictionary *analyticsSamplingRates = nil;
1166 NSDictionary *eventSamplingRates = nil;
1167 #if !TARGET_OS_BRIDGE
1168 if (ShouldInitializeWithAsset()) {
1169 analyticsSamplingRates = [NSDictionary dictionaryWithContentsOfURL:GetAssetFileURL(kOTATrustAnalyticsSamplingRatesFilename)];
1170 if (!isNSDictionary(analyticsSamplingRates)) {
1171 DeleteAssetFromDisk();
1172 }
1173 eventSamplingRates = analyticsSamplingRates[@"Events"];
1174 }
1175 #endif
1176 if (!isNSDictionary(eventSamplingRates)) {
1177 analyticsSamplingRates = [NSDictionary dictionaryWithContentsOfURL:SecSystemTrustStoreCopyResourceNSURL(kOTATrustAnalyticsSamplingRatesFilename)];
1178 }
1179 if (isNSDictionary(analyticsSamplingRates)) {
1180 eventSamplingRates = analyticsSamplingRates[@"Events"];
1181 if (isNSDictionary(eventSamplingRates)) {
1182 return CFBridgingRetain(eventSamplingRates);
1183 }
1184 }
1185 return NULL;
1186 }
1187
1188 static CF_RETURNS_RETAINED CFArrayRef InitializeAppleCertificateAuthorities() {
1189 NSArray *appleCAs = nil;
1190 #if !TARGET_OS_BRIDGE
1191 if (ShouldInitializeWithAsset()) {
1192 appleCAs = [NSArray arrayWithContentsOfURL:GetAssetFileURL(kOTATrustAppleCertifcateAuthoritiesFilename)];
1193 if (!isNSArray(appleCAs)) {
1194 DeleteAssetFromDisk();
1195 }
1196 }
1197 #endif
1198 if (!isNSArray(appleCAs)) {
1199 appleCAs = [NSArray arrayWithContentsOfURL:SecSystemTrustStoreCopyResourceNSURL(kOTATrustAppleCertifcateAuthoritiesFilename)];
1200 }
1201 if (isNSArray(appleCAs)) {
1202 return CFBridgingRetain(appleCAs);
1203 }
1204 return NULL;
1205 }
1206
1207 /* MARK: - */
1208 /* MARK: SecOTA */
1209
1210 /* We keep track of one OTAPKI reference */
1211 static SecOTAPKIRef kCurrentOTAPKIRef = NULL;
1212 /* This queue is for making changes to the OTAPKI reference */
1213 static dispatch_queue_t kOTAQueue = NULL;
1214 /* This queue is for fetching changes to the OTAPKI reference or otherwise doing maintenance activities */
1215 static dispatch_queue_t kOTABackgroundQueue = NULL;
1216
1217 struct _OpaqueSecOTAPKI {
1218 CFRuntimeBase _base;
1219 CFSetRef _blackListSet;
1220 CFSetRef _grayListSet;
1221 CFDictionaryRef _allowList;
1222 CFArrayRef _trustedCTLogs;
1223 CFURLRef _pinningList;
1224 CFArrayRef _escrowCertificates;
1225 CFArrayRef _escrowPCSCertificates;
1226 CFDictionaryRef _evPolicyToAnchorMapping;
1227 CFDictionaryRef _anchorLookupTable;
1228 const char* _anchorTable;
1229 uint64_t _trustStoreVersion;
1230 const char* _validDatabaseSnapshot;
1231 CFIndex _validSnapshotVersion;
1232 CFIndex _validSnapshotFormat;
1233 uint64_t _assetVersion;
1234 CFDictionaryRef _eventSamplingRates;
1235 CFArrayRef _appleCAs;
1236 };
1237
1238 CFGiblisFor(SecOTAPKI)
1239
1240 static CF_RETURNS_RETAINED CFStringRef SecOTAPKICopyFormatDescription(CFTypeRef cf, CFDictionaryRef formatOptions) {
1241 SecOTAPKIRef otapkiRef = (SecOTAPKIRef)cf;
1242 return CFStringCreateWithFormat(kCFAllocatorDefault,NULL,CFSTR("<SecOTAPKIRef: version %llu/%llu>"),
1243 otapkiRef->_trustStoreVersion, otapkiRef->_assetVersion);
1244 }
1245
1246 static void SecOTAPKIDestroy(CFTypeRef cf) {
1247 SecOTAPKIRef otapkiref = (SecOTAPKIRef)cf;
1248
1249 CFReleaseNull(otapkiref->_blackListSet);
1250 CFReleaseNull(otapkiref->_grayListSet);
1251 CFReleaseNull(otapkiref->_escrowCertificates);
1252 CFReleaseNull(otapkiref->_escrowPCSCertificates);
1253
1254 CFReleaseNull(otapkiref->_evPolicyToAnchorMapping);
1255 CFReleaseNull(otapkiref->_anchorLookupTable);
1256
1257 CFReleaseNull(otapkiref->_trustedCTLogs);
1258 CFReleaseNull(otapkiref->_pinningList);
1259 CFReleaseNull(otapkiref->_eventSamplingRates);
1260 CFReleaseNull(otapkiref->_appleCAs);
1261
1262 if (otapkiref->_anchorTable) {
1263 free((void *)otapkiref->_anchorTable);
1264 otapkiref->_anchorTable = NULL;
1265 }
1266 if (otapkiref->_validDatabaseSnapshot) {
1267 free((void *)otapkiref->_validDatabaseSnapshot);
1268 otapkiref->_validDatabaseSnapshot = NULL;
1269 }
1270 }
1271
1272 static uint64_t GetSystemTrustStoreVersion(void) {
1273 return GetSystemVersion(CFSTR("VersionNumber"));
1274 }
1275
1276 static uint64_t GetAssetVersion(void) {
1277 @autoreleasepool {
1278 /* Get system asset version */
1279 uint64_t version = GetSystemVersion((__bridge CFStringRef)kOTATrustContentVersionKey);
1280
1281 #if !TARGET_OS_BRIDGE
1282 uint64_t asset_version = 0;
1283 NSDictionary *OTAPKIContext = [NSDictionary dictionaryWithContentsOfURL:GetAssetFileURL(kOTATrustContextFilename)];
1284 if (isNSDictionary(OTAPKIContext)) {
1285 NSNumber *tmpNumber = OTAPKIContext[kOTATrustContentVersionKey];
1286 if (isNSNumber(tmpNumber)) {
1287 asset_version = [tmpNumber unsignedLongLongValue];
1288 }
1289 }
1290
1291 if (asset_version > version) {
1292 return asset_version;
1293 } else {
1294 /* Delete old data */
1295 DeleteAssetFromDisk();
1296 }
1297 #endif
1298 return version;
1299 }
1300 }
1301
1302 static SecOTAPKIRef SecOTACreate() {
1303
1304 SecOTAPKIRef otapkiref = NULL;
1305
1306 otapkiref = CFTypeAllocate(SecOTAPKI, struct _OpaqueSecOTAPKI , kCFAllocatorDefault);
1307
1308 if (NULL == otapkiref) {
1309 return otapkiref;
1310 }
1311
1312 // Make sure that if this routine has to bail that the clean up
1313 // will do the right thing
1314 memset(otapkiref, 0, sizeof(*otapkiref));
1315
1316 // Start off by getting the trust store version
1317 otapkiref->_trustStoreVersion = GetSystemTrustStoreVersion();
1318
1319 // Get the set of black listed keys
1320 CFSetRef blackKeysSet = InitializeBlackList();
1321 if (NULL == blackKeysSet) {
1322 CFReleaseNull(otapkiref);
1323 return otapkiref;
1324 }
1325 otapkiref->_blackListSet = blackKeysSet;
1326
1327 // Get the set of gray listed keys
1328 CFSetRef grayKeysSet = InitializeGrayList();
1329 if (NULL == grayKeysSet) {
1330 CFReleaseNull(otapkiref);
1331 return otapkiref;
1332 }
1333 otapkiref->_grayListSet = grayKeysSet;
1334
1335 // Get the allow list dictionary
1336 // (now loaded lazily in SecOTAPKICopyAllowList)
1337
1338 // Get the trusted Certificate Transparency Logs
1339 otapkiref->_trustedCTLogs = InitializeTrustedCTLogs();
1340
1341 // Get the pinning list
1342 otapkiref->_pinningList = InitializePinningList();
1343
1344 // Get the Event Sampling Rates
1345 otapkiref->_eventSamplingRates = InitializeEventSamplingRates();
1346
1347 // Get the list of CAs used by Apple
1348 otapkiref->_appleCAs = InitializeAppleCertificateAuthorities();
1349
1350 // Get the asset version (after possible reset due to missing asset date)
1351 otapkiref->_assetVersion = GetAssetVersion();
1352
1353 // Get the valid update snapshot version and format
1354 CFIndex update_format = 0;
1355 otapkiref->_validSnapshotVersion = InitializeValidSnapshotVersion(&update_format);
1356 otapkiref->_validSnapshotFormat = update_format;
1357
1358 // Get the valid database snapshot path (if it exists, NULL otherwise)
1359 otapkiref->_validDatabaseSnapshot = InitializeValidDatabaseSnapshot();
1360
1361 CFArrayRef escrowCerts = NULL;
1362 CFArrayRef escrowPCSCerts = NULL;
1363 InitializeEscrowCertificates(&escrowCerts, &escrowPCSCerts);
1364 if (NULL == escrowCerts || NULL == escrowPCSCerts) {
1365 CFReleaseNull(escrowCerts);
1366 CFReleaseNull(escrowPCSCerts);
1367 CFReleaseNull(otapkiref);
1368 return otapkiref;
1369 }
1370 otapkiref->_escrowCertificates = escrowCerts;
1371 otapkiref->_escrowPCSCertificates = escrowPCSCerts;
1372
1373 // Get the mapping of EV Policy OIDs to Anchor digest
1374 CFDictionaryRef evOidToAnchorDigestMap = InitializeEVPolicyToAnchorDigestsTable();
1375 if (NULL == evOidToAnchorDigestMap) {
1376 CFReleaseNull(otapkiref);
1377 return otapkiref;
1378 }
1379 otapkiref->_evPolicyToAnchorMapping = evOidToAnchorDigestMap;
1380
1381 CFDictionaryRef anchorLookupTable = NULL;
1382 const char* anchorTablePtr = NULL;
1383
1384 if (!InitializeAnchorTable(&anchorLookupTable, &anchorTablePtr)) {
1385 CFReleaseSafe(anchorLookupTable);
1386 if (anchorTablePtr) {
1387 free((void *)anchorTablePtr);
1388 }
1389 CFReleaseNull(otapkiref);
1390 return otapkiref;
1391 }
1392 otapkiref->_anchorLookupTable = anchorLookupTable;
1393 otapkiref->_anchorTable = anchorTablePtr;
1394
1395 #if !TARGET_OS_BRIDGE
1396 /* Initialize our update handling */
1397 InitializeOTATrustAsset(kOTABackgroundQueue);
1398 #endif
1399
1400 return otapkiref;
1401 }
1402
1403 SecOTAPKIRef SecOTAPKICopyCurrentOTAPKIRef() {
1404 __block SecOTAPKIRef result = NULL;
1405 static dispatch_once_t kInitializeOTAPKI = 0;
1406 dispatch_once(&kInitializeOTAPKI, ^{
1407 @autoreleasepool {
1408 kOTAQueue = dispatch_queue_create("com.apple.security.OTAPKIQueue", NULL);
1409 dispatch_queue_attr_t attr = dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_SERIAL,
1410 QOS_CLASS_BACKGROUND, 0);
1411 attr = dispatch_queue_attr_make_with_autorelease_frequency(attr, DISPATCH_AUTORELEASE_FREQUENCY_WORK_ITEM);
1412 kOTABackgroundQueue = dispatch_queue_create("com.apple.security.OTAPKIBackgroundQueue", attr);
1413 kCurrentOTAPKIRef = SecOTACreate();
1414 if (!kOTAQueue || !kOTABackgroundQueue) {
1415 secerror("Failed to create OTAPKI Queues. May crash later.");
1416 }
1417 }
1418 });
1419
1420 dispatch_sync(kOTAQueue, ^{
1421 result = kCurrentOTAPKIRef;
1422 CFRetainSafe(result);
1423 });
1424 return result;
1425 }
1426
1427 #if !TARGET_OS_BRIDGE
1428 static BOOL UpdateFromAsset(NSURL *localURL, NSNumber *asset_version) {
1429 if (!localURL || !asset_version) {
1430 secerror("OTATrust: missing url and version for downloaded asset");
1431 return NO;
1432 }
1433 __block NSArray *newTrustedCTLogs = NULL;
1434 __block uint64_t version = [asset_version unsignedLongLongValue];
1435 __block NSDictionary *newAnalyticsSamplingRates = NULL;
1436 __block NSArray *newAppleCAs = NULL;
1437
1438 NSURL *TrustedCTLogsFileLoc = [NSURL URLWithString:kOTATrustTrustedCTLogsFilename
1439 relativeToURL:localURL];
1440 newTrustedCTLogs = [NSArray arrayWithContentsOfURL:TrustedCTLogsFileLoc];
1441 if (!newTrustedCTLogs) {
1442 secerror("OTATrust: unable to create TrustedCTLogs from asset file: %@", TrustedCTLogsFileLoc);
1443 return NO;
1444 }
1445
1446 NSURL *AnalyticsSamplingRatesFileLoc = [NSURL URLWithString:kOTATrustAnalyticsSamplingRatesFilename
1447 relativeToURL:localURL];
1448 newAnalyticsSamplingRates = [NSDictionary dictionaryWithContentsOfURL:AnalyticsSamplingRatesFileLoc];
1449 if (!newAnalyticsSamplingRates) {
1450 secerror("OTATrust: unable to create AnalyticsSamplingRates from asset file: %@", AnalyticsSamplingRatesFileLoc);
1451 return NO;
1452 }
1453
1454 NSURL *AppleCAsFileLoc = [NSURL URLWithString:kOTATrustAppleCertifcateAuthoritiesFilename
1455 relativeToURL:localURL];
1456 newAppleCAs = [NSArray arrayWithContentsOfURL:AppleCAsFileLoc];
1457 if (!newAppleCAs) {
1458 secerror("OTATrust: unable to create AppleCAs from asset file: %@", AppleCAsFileLoc);
1459 return NO;
1460 }
1461
1462 /* Update the Current OTAPKIRef with the new data */
1463 dispatch_sync(kOTAQueue, ^{
1464 secnotice("OTATrust", "updating asset version from %llu to %llu", kCurrentOTAPKIRef->_assetVersion, version);
1465 CFRetainAssign(kCurrentOTAPKIRef->_trustedCTLogs, (__bridge CFArrayRef)newTrustedCTLogs);
1466 CFRetainAssign(kCurrentOTAPKIRef->_eventSamplingRates, (__bridge CFDictionaryRef)newAnalyticsSamplingRates);
1467 CFRetainAssign(kCurrentOTAPKIRef->_appleCAs, (__bridge CFArrayRef)newAppleCAs);
1468 kCurrentOTAPKIRef->_assetVersion = version;
1469 });
1470
1471 /* Write the data to disk (so that we don't have to re-download the asset on re-launch) */
1472 DeleteAssetFromDisk();
1473 if (CopyFileToDisk(kOTATrustTrustedCTLogsFilename, TrustedCTLogsFileLoc) &&
1474 CopyFileToDisk(kOTATrustAnalyticsSamplingRatesFilename, AnalyticsSamplingRatesFileLoc) &&
1475 CopyFileToDisk(kOTATrustAppleCertifcateAuthoritiesFilename, AppleCAsFileLoc) &&
1476 WriteAssetVersionToDisk(asset_version)) {
1477 /* If we successfully updated the "asset" on disk, signal the other trustds to pick up the changes */
1478 notify_post(kOTATrustOnDiskAssetNotification);
1479 }
1480
1481 return YES;
1482 }
1483 #endif // !TARGET_OS_BRIDGE
1484
1485
1486 CFSetRef SecOTAPKICopyBlackListSet(SecOTAPKIRef otapkiRef) {
1487 if (NULL == otapkiRef) {
1488 return NULL;
1489 }
1490
1491 return CFRetainSafe(otapkiRef->_blackListSet);
1492 }
1493
1494
1495 CFSetRef SecOTAPKICopyGrayList(SecOTAPKIRef otapkiRef) {
1496 if (NULL == otapkiRef) {
1497 return NULL;
1498 }
1499
1500 return CFRetainSafe(otapkiRef->_grayListSet);
1501 }
1502
1503 CFDictionaryRef SecOTAPKICopyAllowList(SecOTAPKIRef otapkiRef) {
1504 if (NULL == otapkiRef) {
1505 return NULL;
1506 }
1507
1508 CFDictionaryRef result = otapkiRef->_allowList;
1509 if (!result) {
1510 result = InitializeAllowList();
1511 otapkiRef->_allowList = result;
1512 }
1513
1514 return CFRetainSafe(result);
1515 }
1516
1517 CFArrayRef SecOTAPKICopyAllowListForAuthKeyID(SecOTAPKIRef otapkiRef, CFStringRef authKeyID) {
1518 // %%% temporary performance optimization:
1519 // only load dictionary if we know an allow list exists for this key
1520 const CFStringRef keyIDs[3] = {
1521 CFSTR("7C724B39C7C0DB62A54F9BAA183492A2CA838259"),
1522 CFSTR("65F231AD2AF7F7DD52960AC702C10EEFA6D53B11"),
1523 CFSTR("D2A716207CAFD9959EEB430A19F2E0B9740EA8C7")
1524 };
1525 CFArrayRef result = NULL;
1526 bool hasAllowList = false;
1527 CFIndex count = (sizeof(keyIDs) / sizeof(keyIDs[0]));
1528 for (CFIndex ix=0; ix<count && authKeyID; ix++) {
1529 if (kCFCompareEqualTo == CFStringCompare(authKeyID, keyIDs[ix], 0)) {
1530 hasAllowList = true;
1531 break;
1532 }
1533 }
1534 if (!hasAllowList || !otapkiRef) {
1535 return result;
1536 }
1537
1538 CFDictionaryRef allowListDict = SecOTAPKICopyAllowList(otapkiRef);
1539 if (!allowListDict) {
1540 return result;
1541 }
1542
1543 // return a retained copy of the allow list array (or NULL)
1544 result = CFDictionaryGetValue(allowListDict, authKeyID);
1545 CFRetainSafe(result);
1546 CFReleaseSafe(allowListDict);
1547 return result;
1548 }
1549
1550 CFArrayRef SecOTAPKICopyTrustedCTLogs(SecOTAPKIRef otapkiRef) {
1551 CFArrayRef result = NULL;
1552 if (NULL == otapkiRef)
1553 {
1554 return result;
1555 }
1556
1557 #if !TARGET_OS_BRIDGE
1558 /* Trigger periodic background MA checks in system trustd
1559 * We also check on trustd launch and listen for notifications. */
1560 TriggerPeriodicOTATrustAssetChecks(kOTABackgroundQueue);
1561 #endif
1562
1563 result = otapkiRef->_trustedCTLogs;
1564 CFRetainSafe(result);
1565 return result;
1566 }
1567
1568 CFURLRef SecOTAPKICopyPinningList(SecOTAPKIRef otapkiRef) {
1569 if (NULL == otapkiRef) {
1570 return NULL;
1571 }
1572
1573 return CFRetainSafe(otapkiRef->_pinningList);
1574 }
1575
1576
1577 /* Returns an array of certificate data (CFDataRef) */
1578 CFArrayRef SecOTAPKICopyEscrowCertificates(uint32_t escrowRootType, SecOTAPKIRef otapkiRef) {
1579 CFMutableArrayRef result = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
1580 if (NULL == otapkiRef) {
1581 return result;
1582 }
1583
1584 switch (escrowRootType) {
1585 // Note: we shouldn't be getting called to return baseline roots,
1586 // since this function vends production roots by definition.
1587 case kSecCertificateBaselineEscrowRoot:
1588 case kSecCertificateProductionEscrowRoot:
1589 case kSecCertificateBaselineEscrowBackupRoot:
1590 case kSecCertificateProductionEscrowBackupRoot:
1591 if (otapkiRef->_escrowCertificates) {
1592 CFArrayRef escrowCerts = otapkiRef->_escrowCertificates;
1593 CFArrayAppendArray(result, escrowCerts, CFRangeMake(0, CFArrayGetCount(escrowCerts)));
1594 }
1595 break;
1596 case kSecCertificateBaselineEscrowEnrollmentRoot:
1597 case kSecCertificateProductionEscrowEnrollmentRoot:
1598 if (otapkiRef->_escrowCertificates) {
1599 // for enrollment purposes, exclude the v100 root
1600 static const unsigned char V100EscrowRoot[] = {
1601 0x65,0x5C,0xB0,0x3C,0x39,0x3A,0x32,0xA6,0x0B,0x96,
1602 0x40,0xC0,0xCA,0x73,0x41,0xFD,0xC3,0x9E,0x96,0xB3
1603 };
1604 CFArrayRef escrowCerts = otapkiRef->_escrowCertificates;
1605 CFIndex idx, count = CFArrayGetCount(escrowCerts);
1606 for (idx=0; idx < count; idx++) {
1607 CFDataRef tmpData = (CFDataRef) CFArrayGetValueAtIndex(escrowCerts, idx);
1608 SecCertificateRef tmpCert = (tmpData) ? SecCertificateCreateWithData(NULL, tmpData) : NULL;
1609 CFDataRef sha1Hash = (tmpCert) ? SecCertificateGetSHA1Digest(tmpCert) : NULL;
1610 const uint8_t *dp = (sha1Hash) ? CFDataGetBytePtr(sha1Hash) : NULL;
1611 if (!(dp && !memcmp(V100EscrowRoot, dp, sizeof(V100EscrowRoot))) && tmpData) {
1612 CFArrayAppendValue(result, tmpData);
1613 }
1614 CFReleaseSafe(tmpCert);
1615 }
1616 }
1617 break;
1618 case kSecCertificateBaselinePCSEscrowRoot:
1619 case kSecCertificateProductionPCSEscrowRoot:
1620 if (otapkiRef->_escrowPCSCertificates) {
1621 CFArrayRef escrowPCSCerts = otapkiRef->_escrowPCSCertificates;
1622 CFArrayAppendArray(result, escrowPCSCerts, CFRangeMake(0, CFArrayGetCount(escrowPCSCerts)));
1623 }
1624 break;
1625 default:
1626 break;
1627 }
1628
1629 return result;
1630 }
1631
1632
1633 CFDictionaryRef SecOTAPKICopyEVPolicyToAnchorMapping(SecOTAPKIRef otapkiRef) {
1634 if (NULL == otapkiRef) {
1635 return NULL;
1636 }
1637
1638 return CFRetainSafe(otapkiRef->_evPolicyToAnchorMapping);
1639 }
1640
1641
1642 CFDictionaryRef SecOTAPKICopyAnchorLookupTable(SecOTAPKIRef otapkiRef) {
1643 if (NULL == otapkiRef) {
1644 return NULL;
1645 }
1646
1647 return CFRetainSafe(otapkiRef->_anchorLookupTable);
1648 }
1649
1650 const char* SecOTAPKIGetAnchorTable(SecOTAPKIRef otapkiRef) {
1651 if (NULL == otapkiRef) {
1652 return NULL;
1653 }
1654
1655 return otapkiRef->_anchorTable;
1656 }
1657
1658 const char* SecOTAPKIGetValidDatabaseSnapshot(SecOTAPKIRef otapkiRef) {
1659 if (NULL == otapkiRef) {
1660 return NULL;
1661 }
1662
1663 return otapkiRef->_validDatabaseSnapshot;
1664 }
1665
1666 CFIndex SecOTAPKIGetValidSnapshotVersion(SecOTAPKIRef otapkiRef) {
1667 if (NULL == otapkiRef) {
1668 return 0;
1669 }
1670
1671 return otapkiRef->_validSnapshotVersion;
1672 }
1673
1674 CFIndex SecOTAPKIGetValidSnapshotFormat(SecOTAPKIRef otapkiRef) {
1675 if (NULL == otapkiRef) {
1676 return 0;
1677 }
1678
1679 return otapkiRef->_validSnapshotFormat;
1680 }
1681
1682 uint64_t SecOTAPKIGetTrustStoreVersion(SecOTAPKIRef otapkiRef) {
1683 if (NULL == otapkiRef) {
1684 return 0;
1685 }
1686
1687 return otapkiRef->_trustStoreVersion;
1688 }
1689
1690 uint64_t SecOTAPKIGetAssetVersion(SecOTAPKIRef otapkiRef) {
1691 if (NULL == otapkiRef) {
1692 return 0;
1693 }
1694
1695 return otapkiRef->_assetVersion;
1696 }
1697
1698 NSNumber *SecOTAPKIGetSamplingRateForEvent(SecOTAPKIRef otapkiRef, NSString *eventName) {
1699 if (NULL == otapkiRef) {
1700 return nil;
1701 }
1702
1703 #if !TARGET_OS_BRIDGE
1704 /* Trigger periodic background MA checks in system trustd
1705 * We also check on trustd launch and listen for notifications. */
1706 TriggerPeriodicOTATrustAssetChecks(kOTABackgroundQueue);
1707 #endif
1708
1709 if (otapkiRef->_eventSamplingRates) {
1710 CFTypeRef value = CFDictionaryGetValue(otapkiRef->_eventSamplingRates, (__bridge CFStringRef)eventName);
1711 if (isNumberOfType(value, kCFNumberSInt64Type)) {
1712 return (__bridge NSNumber *)value;
1713 }
1714 }
1715 return nil;
1716 }
1717
1718 CFArrayRef SecOTAPKICopyAppleCertificateAuthorities(SecOTAPKIRef otapkiRef) {
1719 if (NULL == otapkiRef) {
1720 return NULL;
1721 }
1722
1723 #if !TARGET_OS_BRIDGE
1724 /* Trigger periodic background MA checks in system trustd
1725 * We also check on trustd launch and listen for notifications. */
1726 TriggerPeriodicOTATrustAssetChecks(kOTABackgroundQueue);
1727 #endif
1728
1729 return CFRetainSafe(otapkiRef->_appleCAs);
1730 }
1731
1732 /* Returns an array of certificate data (CFDataRef) */
1733 CFArrayRef SecOTAPKICopyCurrentEscrowCertificates(uint32_t escrowRootType, CFErrorRef* error) {
1734 SecOTAPKIRef otapkiref = SecOTAPKICopyCurrentOTAPKIRef();
1735 if (NULL == otapkiref) {
1736 SecError(errSecInternal, error, CFSTR("Unable to get the current OTAPKIRef"));
1737 return NULL;
1738 }
1739
1740 CFArrayRef result = SecOTAPKICopyEscrowCertificates(escrowRootType, otapkiref);
1741 CFRelease(otapkiref);
1742
1743 if (NULL == result) {
1744 SecError(errSecInternal, error, CFSTR("Could not get escrow certificates from the current OTAPKIRef"));
1745 }
1746 return result;
1747 }
1748
1749 uint64_t SecOTAPKIGetCurrentTrustStoreVersion(CFErrorRef* error){
1750 SecOTAPKIRef otapkiref = SecOTAPKICopyCurrentOTAPKIRef();
1751 if (NULL == otapkiref) {
1752 SecError(errSecInternal, error, CFSTR("Unable to get the current OTAPKIRef"));
1753 return 0;
1754 }
1755
1756 return otapkiref->_trustStoreVersion;
1757 }
1758
1759 uint64_t SecOTAPKIGetCurrentAssetVersion(CFErrorRef* error) {
1760 SecOTAPKIRef otapkiref = SecOTAPKICopyCurrentOTAPKIRef();
1761 if (NULL == otapkiref) {
1762 SecError(errSecInternal, error, CFSTR("Unable to get the current OTAPKIRef"));
1763 return 0;
1764 }
1765
1766 return otapkiref->_assetVersion;
1767 }
1768
1769 uint64_t SecOTAPKIResetCurrentAssetVersion(CFErrorRef* error) {
1770 uint64_t system_version = GetSystemVersion((__bridge CFStringRef)kOTATrustContentVersionKey);
1771
1772 dispatch_sync(kOTAQueue, ^{
1773 kCurrentOTAPKIRef->_assetVersion = system_version;
1774 });
1775
1776 #if !TARGET_OS_BRIDGE
1777 DeleteAssetFromDisk();
1778 #endif
1779 return system_version;
1780 }
1781
1782 uint64_t SecOTAPKISignalNewAsset(CFErrorRef* error) {
1783 #if !TARGET_OS_BRIDGE
1784 if (SecOTAPKIIsSystemTrustd()) {
1785 NSError *nserror = nil;
1786 if (!DownloadOTATrustAsset(NO, YES, &nserror) && error) {
1787 *error = CFRetainSafe((__bridge CFErrorRef)nserror);
1788 }
1789 } else {
1790 SecError(errSecServiceNotAvailable, error, CFSTR("This function may ony be performed by the system trustd."));
1791 }
1792 return GetAssetVersion();
1793 #else
1794 SecError(errSecUnsupportedService, error, CFSTR("This function is not available on this platform"));
1795 return GetAssetVersion();
1796 #endif
1797 }