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