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