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