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