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