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