]> git.saurik.com Git - apple/security.git/blob - tests/TrustTests/EvaluationTests/TrustEvaluationTestCase.m
Security-59306.120.7.tar.gz
[apple/security.git] / tests / TrustTests / EvaluationTests / TrustEvaluationTestCase.m
1 /*
2 * Copyright (c) 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 */
24 #import <XCTest/XCTest.h>
25 #include "trust/trustd/trustd_spi.h"
26 #include "trust/trustd/SecRevocationDb.h"
27 #include <Security/SecCertificatePriv.h>
28 #include <Security/SecTrustPriv.h>
29 #include <Security/SecPolicy.h>
30 #include <Security/SecTrustSettings.h>
31 #include <Security/SecTrustSettingsPriv.h>
32 #include "OSX/utilities/SecCFWrappers.h"
33 #include <dispatch/dispatch.h>
34 #include <dlfcn.h>
35
36 #if TARGET_OS_IPHONE
37 #include <Security/SecTrustStore.h>
38 #else
39 #define kSystemLoginKeychainPath "/Library/Keychains/System.keychain"
40 #include <Security/SecKeychain.h>
41 #endif
42
43 #import "../TestMacroConversions.h"
44 #import "TrustEvaluationTestCase.h"
45
46 @implementation TrustEvaluationTestCase
47
48 static int current_dir = -1;
49 static char *home_var = NULL;
50
51 /* Build in trustd functionality to the tests */
52 + (void) setUp {
53 /* Set up TMP directory for trustd's files */
54 int ok = 0;
55 NSError* error = nil;
56 NSString* pid = [NSString stringWithFormat: @"tst-%d", [[NSProcessInfo processInfo] processIdentifier]];
57 NSURL* tmpDirURL = [[NSURL fileURLWithPath:NSTemporaryDirectory() isDirectory:YES] URLByAppendingPathComponent:pid];
58 ok = (bool)tmpDirURL;
59
60 if (current_dir == -1 && home_var == NULL) {
61 ok = ok && [[NSFileManager defaultManager] createDirectoryAtURL:tmpDirURL
62 withIntermediateDirectories:NO
63 attributes:NULL
64 error:&error];
65
66 NSURL* libraryURL = [tmpDirURL URLByAppendingPathComponent:@"Library"];
67 NSURL* preferencesURL = [tmpDirURL URLByAppendingPathComponent:@"Preferences"];
68
69 ok = (ok && (current_dir = open(".", O_RDONLY) >= 0)
70 && (chdir([tmpDirURL fileSystemRepresentation]) >= 0)
71 && (setenv("HOME", [tmpDirURL fileSystemRepresentation], 1) >= 0)
72 && (bool)(home_var = getenv("HOME")));
73
74 ok = ok && [[NSFileManager defaultManager] createDirectoryAtURL:libraryURL
75 withIntermediateDirectories:NO
76 attributes:NULL
77 error:&error];
78
79 ok = ok && [[NSFileManager defaultManager] createDirectoryAtURL:preferencesURL
80 withIntermediateDirectories:NO
81 attributes:NULL
82 error:&error];
83 }
84
85 /* Use the production Valid DB by default (so we'll have data to test against) */
86 CFPreferencesSetAppValue(CFSTR("ValidUpdateServer"), kValidUpdateProdServer, kSecurityPreferencesDomain);
87 CFPreferencesAppSynchronize(kSecurityPreferencesDomain);
88
89 if (ok > 0) {
90 /* Be trustd */
91 trustd_init((__bridge CFURLRef) tmpDirURL);
92 }
93 }
94
95 - (id)addTrustSettingsForCert:(SecCertificateRef)cert trustSettings:(id)trustSettings
96 {
97 #if TARGET_OS_IPHONE
98 SecTrustStoreRef defaultStore = SecTrustStoreForDomain(kSecTrustStoreDomainUser);
99 OSStatus status = SecTrustStoreSetTrustSettings(defaultStore, cert, (__bridge CFTypeRef)trustSettings);
100 XCTAssert(errSecSuccess == status, "failed to set trust settings: %d", (int)status);
101 return nil;
102 #else
103 /* Since we're putting trust settings in the admin domain,
104 * we need to add the certs to the system keychain. */
105 SecKeychainRef kcRef = NULL;
106 CFArrayRef certRef = NULL;
107 NSDictionary *attrs = nil;
108
109 SecKeychainOpen(kSystemLoginKeychainPath, &kcRef);
110 if (!kcRef) {
111 return nil;
112 }
113
114 /* Since we're interacting with the keychain we need a framework cert */
115 SecCertificateRef frameworkCert = SecFrameworkCertificateCreateFromTestCert(cert);
116 attrs = @{(__bridge NSString*)kSecValueRef: (__bridge id)frameworkCert,
117 (__bridge NSString*)kSecUseKeychain: (__bridge id)kcRef,
118 (__bridge NSString*)kSecReturnPersistentRef: @YES};
119 OSStatus status = SecItemAdd((CFDictionaryRef)attrs, (void *)&certRef);
120 XCTAssert(errSecSuccess == status, "failed to add cert to keychain: %d", status);
121 id result = ((__bridge NSArray*)certRef)[0];
122 CFReleaseNull(kcRef);
123 CFReleaseNull(certRef);
124
125 status = SecTrustSettingsSetTrustSettings(frameworkCert, kSecTrustSettingsDomainAdmin,
126 (__bridge CFTypeRef)trustSettings);
127 XCTAssert(errSecSuccess == status, "failed to set trust settings: %d", status);
128 usleep(20000);
129
130 CFReleaseNull(frameworkCert);
131 return result;
132 #endif
133 }
134
135
136 - (id)addTrustSettingsForCert:(SecCertificateRef)cert
137 {
138 NSDictionary *trustSettings = @{ (__bridge NSString*)kSecTrustSettingsResult: @(kSecTrustSettingsResultTrustRoot)};
139 Boolean isSelfSigned = false;
140 XCTAssert(errSecSuccess == SecCertificateIsSelfSigned(cert, &isSelfSigned));
141 if (!isSelfSigned) {
142 trustSettings = @{ (__bridge NSString*)kSecTrustSettingsResult: @(kSecTrustSettingsResultTrustAsRoot)};
143 }
144
145 return [self addTrustSettingsForCert:cert trustSettings:trustSettings];
146 }
147
148 - (void)removeTrustSettingsForCert:(SecCertificateRef)cert persistentRef:(id)persistentRef
149 {
150 #if TARGET_OS_IPHONE
151 SecTrustStoreRef defaultStore = SecTrustStoreForDomain(kSecTrustStoreDomainUser);
152 XCTAssert(errSecSuccess == SecTrustStoreRemoveCertificate(defaultStore, cert), "failed to remove trust settings");
153 #else
154 SecCertificateRef frameworkCert = SecFrameworkCertificateCreateFromTestCert(cert);
155 XCTAssert(errSecSuccess == SecTrustSettingsRemoveTrustSettings(frameworkCert, kSecTrustSettingsDomainAdmin),
156 "failed to remove trust settings");
157 XCTAssert(errSecSuccess == SecItemDelete((CFDictionaryRef)@{ (__bridge NSString*)kSecValuePersistentRef: persistentRef}),
158 "failed to remove item from keychain");
159 CFReleaseNull(frameworkCert);
160 #endif
161 }
162
163 const CFStringRef kSecurityPreferencesDomain = CFSTR("com.apple.security");
164 const CFStringRef kTestSystemRootKey = CFSTR("TestSystemRoot");
165
166 - (void)setTestRootAsSystem:(const uint8_t *)sha256hash
167 {
168 NSData *rootHash = [NSData dataWithBytes:sha256hash length:32];
169 CFPreferencesSetAppValue(kTestSystemRootKey, (__bridge CFDataRef)rootHash, kSecurityPreferencesDomain);
170 CFPreferencesAppSynchronize(kSecurityPreferencesDomain);
171 }
172
173 - (void)removeTestRootAsSystem
174 {
175 CFPreferencesSetAppValue(kTestSystemRootKey, NULL, kSecurityPreferencesDomain);
176 CFPreferencesAppSynchronize(kSecurityPreferencesDomain);
177 }
178
179 - (id _Nullable) CF_RETURNS_RETAINED SecCertificateCreateFromResource:(NSString *)name
180 subdirectory:(NSString *)dir
181 {
182 NSURL *url = [[NSBundle bundleForClass:[self class]] URLForResource:name withExtension:@".cer"
183 subdirectory:dir];
184 NSData *certData = [NSData dataWithContentsOfURL:url];
185 if (!certData) {
186 return nil;
187 }
188 SecCertificateRef cert = SecCertificateCreateWithData(kCFAllocatorDefault, (__bridge CFDataRef)certData);
189 return (__bridge id)cert;
190 }
191
192 - (id _Nullable) CF_RETURNS_RETAINED SecCertificateCreateFromPEMResource:(NSString *)name
193 subdirectory:(NSString *)dir
194 {
195 NSURL *url = [[NSBundle bundleForClass:[self class]] URLForResource:name withExtension:@".pem"
196 subdirectory:dir];
197 NSData *certData = [NSData dataWithContentsOfURL:url];
198 if (!certData) {
199 return nil;
200 }
201
202 SecCertificateRef cert = SecCertificateCreateWithPEM(kCFAllocatorDefault, (__bridge CFDataRef)certData);
203 return (__bridge id)cert;
204 }
205
206 /* MARK: run test methods from regressionBase */
207 - (void)runOneLeafTest:(SecPolicyRef)policy
208 anchors:(NSArray *)anchors
209 intermediates:(NSArray *)intermediates
210 leafPath:(NSString *)path
211 expectedResult:(bool)expectedResult
212 expectations:(NSObject *)expectations
213 verifyDate:(NSDate *)date
214 {
215 NSString* fileName = [path lastPathComponent];
216 NSString *reason = NULL;
217 SecTrustRef trustRef = NULL;
218 NSMutableArray* certArray = NULL;
219 SecCertificateRef certRef = NULL;
220 CFErrorRef error = NULL;
221
222 if (expectations) {
223 if ([expectations isKindOfClass: [NSString class]]) {
224 reason = (NSString *)expectations;
225 } else if ([expectations isKindOfClass: [NSDictionary class]]) {
226 NSDictionary *dict = (NSDictionary *)expectations;
227 NSObject *value = [dict valueForKey:@"valid"];
228 if (value) {
229 if ([value isKindOfClass: [NSNumber class]]) {
230 expectedResult = [(NSNumber *)value boolValue];
231 } else {
232 NSLog(@"Unexpected valid value %@ in dict for key %@", value, fileName);
233 }
234 }
235 value = [dict valueForKey:@"reason"];
236 if (value) {
237 if ([value isKindOfClass: [NSString class]]) {
238 reason = (NSString *)value;
239 } else {
240 NSLog(@"Unexpected reason value %@ in dict for key %@", value, fileName);
241 }
242 }
243 } else if ([expectations isKindOfClass: [NSNumber class]]) {
244 expectedResult = [(NSNumber *)expectations boolValue];
245 } else {
246 NSLog(@"Unexpected class %@ value %@ for key %@", [expectations class], expectations, fileName);
247 }
248 }
249
250 certRef = SecCertificateCreateWithData(NULL, (CFDataRef)[NSData dataWithContentsOfFile:path]);
251 if (!certRef) {
252 if (reason) {
253 fail("TODO test: %@ unable to create certificate, %@", fileName, reason);
254 } else {
255 fail("PARSE %@ unable to create certificate", fileName);
256 }
257 goto exit;
258 }
259
260 certArray = [NSMutableArray arrayWithArray:intermediates];
261 [certArray insertObject:(__bridge id)certRef atIndex:0]; //The certificate to be verified must be the first in the array.
262
263 OSStatus err;
264 err = SecTrustCreateWithCertificates((__bridge CFTypeRef _Nonnull)(certArray), policy, &trustRef);
265 if (err) {
266 ok_status(err, "SecTrustCreateWithCertificates");
267 goto exit;
268 }
269 if ([anchors count])
270 SecTrustSetAnchorCertificates(trustRef, (CFArrayRef)anchors);
271
272 SecTrustSetVerifyDate(trustRef, (__bridge CFDateRef)date);
273
274 BOOL isValid = SecTrustEvaluateWithError(trustRef, &error);
275 if (reason) {
276 XCTAssertFalse(isValid == expectedResult, "TODO test: %@%@", fileName, error);
277 } else {
278 ok(isValid == expectedResult, "%s %@%@", expectedResult ? "REGRESSION" : "SECURITY", fileName, error);
279 }
280
281 exit:
282 CFReleaseSafe(trustRef);
283 CFReleaseSafe(certRef);
284 CFReleaseSafe(error);
285 }
286
287 - (void)runCertificateTestFor:(SecPolicyRef)policy
288 anchors:(NSArray *)anchors
289 intermediates:(NSArray *)intermediates
290 leafPaths:(NSMutableArray *)leafPaths
291 expectations:(NSDictionary *)expect
292 verifyDate:(NSDate *)date
293 {
294 /* Sort the tests by name. */
295 [leafPaths sortUsingSelector:@selector(compare:)];
296
297 for (NSString* path in leafPaths) {
298 NSString* fileName = [path lastPathComponent];
299 [self runOneLeafTest:policy anchors:anchors intermediates:intermediates leafPath:path expectedResult:![fileName hasPrefix:@"Invalid"] expectations:[expect objectForKey:fileName] verifyDate:date];
300 }
301 }
302
303
304 - (void)runCertificateTestForDirectory:(SecPolicyRef)policy subDirectory:(NSString *)resourceSubDirectory verifyDate:(NSDate*)date
305 {
306 NSMutableArray* allRoots = [NSMutableArray array];
307 NSMutableArray* allCAs = [NSMutableArray array];
308 NSMutableArray* certTests = [NSMutableArray array];
309 NSDictionary* expect = NULL;
310
311 NSURL* filesDirectory = [[[NSBundle bundleForClass:[self class]] resourceURL] URLByAppendingPathComponent:resourceSubDirectory];
312 for (NSURL* fileURL in [[NSFileManager defaultManager] contentsOfDirectoryAtURL:filesDirectory includingPropertiesForKeys:[NSArray array] options:NSDirectoryEnumerationSkipsSubdirectoryDescendants error:nil]) {
313 NSString* path = [fileURL path];
314 if ([path hasSuffix:@"Cert.crt"]) {
315 SecCertificateRef certRef = SecCertificateCreateWithData(NULL, (CFDataRef)[NSData dataWithContentsOfFile:path]);
316 [allCAs addObject:(__bridge id)certRef];
317 CFReleaseNull(certRef);
318 } else if ([path hasSuffix:@"RootCertificate.crt"]) {
319 SecCertificateRef certRef = SecCertificateCreateWithData(NULL, (CFDataRef)[NSData dataWithContentsOfFile:path]);
320 [allRoots addObject:(__bridge id)certRef];
321 CFReleaseNull(certRef);
322 } else if ([path hasSuffix:@".crt"]) {
323 [certTests addObject:path];
324 } else if ([path hasSuffix:@".plist"]) {
325 if (expect) {
326 fail("Multiple .plist files found in %@", filesDirectory);
327 } else {
328 expect = [NSDictionary dictionaryWithContentsOfFile:path];
329 }
330 }
331 }
332
333 [self runCertificateTestFor:policy anchors:allRoots intermediates:allCAs leafPaths:certTests expectations:expect verifyDate:date];
334 }
335
336 @end
337
338 typedef SecCertificateRef (*create_f)(CFAllocatorRef allocator,
339 const UInt8 *der_bytes, CFIndex der_length);
340 CF_RETURNS_RETAINED _Nullable
341 SecCertificateRef SecFrameworkCertificateCreate(const uint8_t * _Nonnull der_bytes, CFIndex der_length) {
342 static create_f FrameworkCertCreateFunctionPtr = NULL;
343 static dispatch_once_t onceToken;
344
345 dispatch_once(&onceToken, ^{
346 void *framework = dlopen("/System/Library/Frameworks/Security.framework/Security", RTLD_LAZY);
347 if (framework) {
348 FrameworkCertCreateFunctionPtr = dlsym(framework, "SecCertificateCreateWithBytes");
349 }
350 });
351
352 if (FrameworkCertCreateFunctionPtr) {
353 return FrameworkCertCreateFunctionPtr(NULL, der_bytes, der_length);
354 } else {
355 NSLog(@"WARNING: not using Security framework certificate");
356 return SecCertificateCreateWithBytes(NULL, der_bytes, der_length);
357 }
358 }
359
360 SecCertificateRef SecFrameworkCertificateCreateFromTestCert(SecCertificateRef cert) {
361 return SecFrameworkCertificateCreate(SecCertificateGetBytePtr(cert), SecCertificateGetLength(cert));
362 }