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