]> git.saurik.com Git - apple/security.git/blob - OSX/shared_regressions/si-20-sectrust-policies.m
Security-57740.20.22.tar.gz
[apple/security.git] / OSX / shared_regressions / si-20-sectrust-policies.m
1 /*
2 * Copyright (c) 2016 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
25 /* INSTRUCTIONS FOR ADDING NEW SUBTESTS:
26 * 1. Add the certificates, as DER-encoded files with the 'cer' extension, to OSX/shared_regressions/si-20-sectrust-policies-data/
27 * 2. Add a new dictionary to the test plist (OSX/shared_regressions/si-20-sectrust-policies-data/PinningPolicyTrustTest.plist).
28 * This dictionary must include: (see constants below)
29 * MajorTestName
30 * MinorTestName
31 * Policies
32 * Leaf
33 * Intermediates
34 * ExpectedResult
35 * It is strongly recommended that all test dictionaries include the Anchors and VerifyDate keys.
36 * Addtional optional keys are defined below.
37 */
38
39 /* INSTRUCTIONS FOR DEBUGGING SUBTESTS:
40 * Add a debugging.plist to OSX/shared_regressions/si-20-sectrust-policies-data/ containing only those subtest dictionaries
41 * you want to debug. Git will ignore this file, so you don't accidentally commit it.
42 */
43
44 #include "shared_regressions.h"
45
46 #include <AssertMacros.h>
47 #import <Foundation/Foundation.h>
48
49 #include <utilities/SecInternalReleasePriv.h>
50 #include <utilities/SecCFRelease.h>
51 #include <Security/SecCertificate.h>
52 #include <Security/SecCertificatePriv.h>
53 #include <Security/SecPolicyPriv.h>
54 #include <Security/SecTrust.h>
55
56 /* Key Constants for Test Dictionaries */
57 const NSString *kSecTrustTestMajorTestName = @"MajorTestName"; /* Required; value: string */
58 const NSString *kSecTrustTestMinorTestName = @"MinorTestName"; /* Required; value: string */
59 const NSString *kSecTrustTestPolicies = @"Policies"; /* Required; value: dictionary or array of dictionaries */
60 const NSString *kSecTrustTestLeaf = @"Leaf"; /* Required; value: string */
61 const NSString *kSecTrustTestIntermediates = @"Intermediates"; /* Required; value: string or array of strings */
62 const NSString *kSecTrustTestAnchors = @"Anchors"; /* Recommended; value: string or array of strings */
63 const NSString *kSecTrustTestVerifyDate = @"VerifyDate"; /* Recommended; value: date */
64 const NSString *kSecTrustTestExpectedResult = @"ExpectedResult"; /* Required; value: number */
65 const NSString *kSecTrustTestChainLength = @"ChainLength"; /* Optional; value: number */
66 const NSString *kSecTrustTestEnableTestCerts= @"EnableTestCertificates"; /* Optiona; value: string */
67
68 /* Key Constants for Policies Dictionaries */
69 const NSString *kSecTrustTestPolicyOID = @"PolicyIdentifier"; /* Required; value: string */
70 const NSString *kSecTrustTestPolicyProperties = @"Properties"; /* Optional; value: dictionary, see Policy Value Constants, SecPolicy.h */
71
72 const NSString *kSecTrustTestPinningPolicyResources = @"si-20-sectrust-policies-data";
73
74 @interface TestObject : NSObject
75 @property (readonly) NSMutableArray *certificates;
76 @property (readonly) NSMutableArray *policies;
77 @property (readonly) NSMutableArray *anchors;
78 @property (readonly) NSString *fullTestName;
79
80 - (id)initWithMajorTestName:(NSString *)majorTestName minorTestName:(NSString *)minorTestName;
81 - (bool)addLeafToCertificates:(NSString *)leafName;
82 - (bool)addCertsToArray:(id)pathsObj outputArray:(NSMutableArray *)outArray;
83 - (bool)addIntermediatesToCertificates:(id)intermediatesObj;
84 - (bool)addPolicies:(id)policiesObj;
85 - (bool)addAnchors:(id)anchorsObj;
86 @end
87
88 @implementation TestObject
89
90 - (id)init {
91 self = [super init];
92 return self;
93 }
94
95 - (id)initWithMajorTestName:(NSString *)majorTestName minorTestName:(NSString *)minorTestName {
96 if ((self = [super init])) {
97 _fullTestName = [[majorTestName stringByAppendingString:@"-"] stringByAppendingString:minorTestName];
98 }
99 return self;
100 }
101
102 - (bool)addLeafToCertificates:(NSString *)leafName {
103 SecCertificateRef cert;
104 NSString *path = nil;
105 require_action_quiet(leafName, errOut,
106 fail("%@: failed to get leaf for test", _fullTestName));
107
108 path = [[NSBundle mainBundle]
109 pathForResource:leafName
110 ofType:@"cer"
111 inDirectory:(NSString *)kSecTrustTestPinningPolicyResources];
112 require_action_quiet(path, errOut, fail("%@: failed to get path for leaf", _fullTestName));
113 cert = SecCertificateCreateWithData(NULL, (CFDataRef)[NSData dataWithContentsOfFile:path]);
114 require_action_quiet(cert, errOut,
115 fail("%@: failed to create leaf certificate from path %@",
116 _fullTestName, path));
117 _certificates = [[NSMutableArray alloc] initWithObjects:(__bridge id)cert, nil];
118 CFReleaseNull(cert);
119 require_action_quiet(_certificates, errOut,
120 fail("%@: failed to initialize certificates array",
121 _fullTestName));
122 return true;
123
124 errOut:
125 return false;
126 }
127
128 - (bool)addCertsToArray:(id)pathsObj outputArray:(NSMutableArray *)outArray {
129 __block SecCertificateRef cert = NULL;
130 __block NSString* path = nil;
131 require_action_quiet(pathsObj, errOut,
132 fail("%@: failed to get certificate paths for test", _fullTestName));
133
134 if ([pathsObj isKindOfClass:[NSString class]]) {
135 /* Only one cert path */
136 path = [[NSBundle mainBundle]
137 pathForResource:pathsObj
138 ofType:@"cer"
139 inDirectory:(NSString *)kSecTrustTestPinningPolicyResources];
140 require_action_quiet(path, errOut, fail("%@: failed to get path for cert",
141 _fullTestName));
142 cert = SecCertificateCreateWithData(NULL, (CFDataRef)[NSData dataWithContentsOfFile:path]);
143 require_action_quiet(cert, errOut,
144 fail("%@: failed to create certificate from path %@",
145 _fullTestName, path));
146 [outArray addObject:(__bridge id)cert];
147 CFReleaseNull(cert);
148 }
149
150 else if ([pathsObj isKindOfClass:[NSArray class]]) {
151 /* Test has more than one intermediate */
152 [(NSArray *)pathsObj enumerateObjectsUsingBlock:^(NSString *resource, NSUInteger idx, BOOL *stop) {
153 path = [[NSBundle mainBundle]
154 pathForResource:resource
155 ofType:@"cer"
156 inDirectory:(NSString *)kSecTrustTestPinningPolicyResources];
157 require_action_quiet(path, blockOut,
158 fail("%@: failed to get path for cert %ld",
159 _fullTestName, (unsigned long)idx));
160 cert = SecCertificateCreateWithData(NULL, (CFDataRef)[NSData dataWithContentsOfFile:path]);
161 require_action_quiet(cert, blockOut,
162 fail("%@: failed to create certificate %ld from path %@",
163 _fullTestName, (unsigned long) idx, path));
164 [outArray addObject:(__bridge id)cert];
165
166 CFReleaseNull(cert);
167 return;
168
169 blockOut:
170 CFReleaseNull(cert);
171 *stop = YES;
172 }];
173 }
174
175 else {
176 fail("%@: unexpected type for intermediates or anchors value", _fullTestName);
177 goto errOut;
178 }
179
180 return true;
181
182 errOut:
183 CFReleaseNull(cert);
184 return false;
185
186 }
187
188 - (bool)addIntermediatesToCertificates:(id)intermediatesObj {
189 require_action_quiet(intermediatesObj, errOut,
190 fail("%@: failed to get intermediates for test", _fullTestName));
191
192 require_action_quiet([self addCertsToArray:intermediatesObj outputArray:_certificates], errOut,
193 fail("%@: failed to add intermediates to certificates array", _fullTestName));
194
195 if ([intermediatesObj isKindOfClass:[NSString class]]) {
196 require_action_quiet([_certificates count] == 2, errOut,
197 fail("%@: failed to add all intermediates", _fullTestName));
198 } else if ([intermediatesObj isKindOfClass:[NSArray class]]) {
199 require_action_quiet([_certificates count] == [(NSArray *)intermediatesObj count] + 1, errOut,
200 fail("%@: failed to add all intermediates", _fullTestName));
201 }
202
203 return true;
204
205 errOut:
206 return false;
207 }
208
209 - (bool)addPolicies:(id)policiesObj {
210 __block SecPolicyRef policy = NULL;
211 require_action_quiet(policiesObj, errOut,
212 fail("%@: failed to get policies for test", _fullTestName));
213
214 _policies = [[NSMutableArray alloc] init];
215 require_action_quiet(_policies, errOut,
216 fail("%@: failed to initialize policies array", _fullTestName));
217 if ([policiesObj isKindOfClass:[NSDictionary class]]) {
218 /* Test has only one policy */
219 NSString *policyIdentifier = [(NSDictionary *)policiesObj objectForKey:kSecTrustTestPolicyOID];
220 NSDictionary *policyProperties = [(NSDictionary *)policiesObj objectForKey:kSecTrustTestPolicyProperties];
221 require_action_quiet(policyIdentifier, errOut, fail("%@: failed to get policy OID", _fullTestName));
222
223 policy = SecPolicyCreateWithProperties((__bridge CFStringRef)policyIdentifier,
224 (__bridge CFDictionaryRef)policyProperties);
225 require_action_quiet(policy, errOut,
226 fail("%@: failed to create properties for policy OID %@",
227 _fullTestName, policyIdentifier));
228 [_policies addObject:(__bridge id)policy];
229 CFReleaseNull(policy);
230 }
231
232 else if ([policiesObj isKindOfClass:[NSArray class]]) {
233 /* Test more than one intermediate */
234 [(NSArray *)policiesObj enumerateObjectsUsingBlock:^(NSDictionary *policyDict, NSUInteger idx, BOOL *stop) {
235 NSString *policyIdentifier = [(NSDictionary *)policyDict objectForKey:kSecTrustTestPolicyOID];
236 NSDictionary *policyProperties = [(NSDictionary *)policyDict objectForKey:kSecTrustTestPolicyProperties];
237 require_action_quiet(policyIdentifier, blockOut, fail("%@: failed to get policy OID", _fullTestName));
238
239 policy = SecPolicyCreateWithProperties((__bridge CFStringRef)policyIdentifier,
240 (__bridge CFDictionaryRef)policyProperties);
241 require_action_quiet(policy, blockOut,
242 fail("%@: failed to create properties for policy OID %@",
243 _fullTestName, policyIdentifier));
244 [_policies addObject:(__bridge id)policy];
245
246 CFReleaseNull(policy);
247 return;
248
249 blockOut:
250 CFReleaseNull(policy);
251 *stop = YES;
252 }];
253
254 require_action_quiet([(NSArray *)policiesObj count] == [_policies count], errOut,
255 fail("%@: failed to add all policies", _fullTestName));
256 }
257
258 else {
259 fail("%@: unexpected type for %@ value", _fullTestName, kSecTrustTestPolicies);
260 goto errOut;
261 }
262
263 return true;
264
265 errOut:
266 CFReleaseNull(policy);
267 return false;
268 }
269
270 - (bool)addAnchors:(id)anchorsObj {
271 require_action_quiet(anchorsObj, errOut,
272 fail("%@: failed to get anchors for test", _fullTestName));
273
274 _anchors = [[NSMutableArray alloc] init];
275 require_action_quiet(_anchors, errOut,
276 fail("%@: failed to initialize anchors array", _fullTestName));
277 require_action_quiet([self addCertsToArray:anchorsObj outputArray:_anchors], errOut,
278 fail("%@: failed to add anchors to anchors array", _fullTestName));
279
280 if ([anchorsObj isKindOfClass:[NSString class]]) {
281 require_action_quiet([_anchors count] == 1, errOut,
282 fail("%@: failed to add all anchors", _fullTestName));
283 } else if ([anchorsObj isKindOfClass:[NSArray class]]) {
284 require_action_quiet([_anchors count] == [(NSArray *)anchorsObj count], errOut,
285 fail("%@: failed to add all anchors", _fullTestName));
286 }
287
288 return true;
289
290 errOut:
291 return false;
292 }
293
294 @end
295
296 void (^runTestForObject)(id, NSUInteger, BOOL *) =
297 ^(NSDictionary *testDict, NSUInteger idx, BOOL *stop) {
298 NSString *majorTestName = nil, *minorTestName = nil;
299 TestObject *test = nil;
300 SecTrustRef trust = NULL;
301 SecTrustResultType trustResult = kSecTrustResultInvalid;
302 NSDate *verifyDate = nil;
303 NSNumber *expectedResult = nil, *chainLen = nil;
304
305 bool enableTestCertificates = (bool)[testDict objectForKey:kSecTrustTestEnableTestCerts];
306
307 /* Test name, for documentation purposes */
308 majorTestName = [testDict objectForKey:kSecTrustTestMajorTestName];
309 minorTestName = [testDict objectForKey:kSecTrustTestMinorTestName];
310 require_action_quiet(majorTestName && minorTestName, testOut,
311 fail("Failed to create test names for test %lu",(unsigned long)idx));
312 test = [[TestObject alloc] initWithMajorTestName:majorTestName minorTestName:minorTestName];
313 require_action_quiet((test), testOut, fail("%@-%@: failed to create test object", majorTestName, minorTestName));
314
315 /* Populate the certificates array */
316 require_quiet([test addLeafToCertificates:[testDict objectForKey:kSecTrustTestLeaf]], testOut);
317 require_quiet([test addIntermediatesToCertificates:[testDict objectForKey:kSecTrustTestIntermediates]], testOut);
318
319 /* Optionally: enable test certificates for the policy */
320 if (enableTestCertificates) {
321 /* Note: Some of the policies use defaults writes with the "com.apple.Security" domain;
322 * others use "com.apple.security". Set both since we don't know which one this is. */
323 CFPreferencesSetAppValue((__bridge CFStringRef)[testDict objectForKey:kSecTrustTestEnableTestCerts],
324 kCFBooleanTrue, CFSTR("com.apple.Security"));
325 CFPreferencesSetAppValue((__bridge CFStringRef)[testDict objectForKey:kSecTrustTestEnableTestCerts],
326 kCFBooleanTrue, CFSTR("com.apple.security"));
327 }
328
329 /* Create the policies */
330 require_quiet([test addPolicies:[testDict objectForKey:kSecTrustTestPolicies]], testOut);
331
332 /* Create the trust object */
333 require_noerr_action_quiet(SecTrustCreateWithCertificates((__bridge CFArrayRef)test.certificates,
334 (__bridge CFArrayRef)test.policies,
335 &trust),
336 testOut,
337 fail("%@: failed to create trust ref", test.fullTestName));
338
339 /* Optionally set anchors in trust object */
340 if ([testDict objectForKey:kSecTrustTestAnchors]) {
341 require_quiet([test addAnchors:[testDict objectForKey:kSecTrustTestAnchors]], testOut);
342 require_noerr_action_quiet(SecTrustSetAnchorCertificates(trust, (__bridge CFArrayRef)test.anchors),
343 testOut,
344 fail("%@: failed to add anchors to trust ref", test.fullTestName));
345 }
346
347 /* Set optional date in trust object */
348 verifyDate = [testDict objectForKey:kSecTrustTestVerifyDate];
349 if (verifyDate) {
350 require_noerr_action_quiet(SecTrustSetVerifyDate(trust, (__bridge CFDateRef)verifyDate), testOut,
351 fail("%@: failed to set verify date, %@, in trust ref", test.fullTestName,
352 verifyDate));
353 }
354
355 /* Evaluate */
356 require_noerr_action_quiet(SecTrustEvaluate(trust, &trustResult), testOut,
357 fail("%@: failed to evaluate trust", test.fullTestName));
358
359 /* Check results */
360 require_action_quiet(expectedResult = [testDict objectForKey:kSecTrustTestExpectedResult],
361 testOut, fail("%@: failed to get expected result for test", test.fullTestName));
362
363 /* If we enabled test certificates on a non-internal device, expect a failure instead of succees. */
364 if (enableTestCertificates && !SecIsInternalRelease() && ([expectedResult unsignedIntValue] == 4)) {
365 ok(trustResult == 5,
366 "%@: actual trust result %u did not match expected trust result %u",
367 test.fullTestName, trustResult, 5);
368 } else {
369 ok(trustResult == [expectedResult unsignedIntValue],
370 "%@: actual trust result %u did not match expected trust result %u",
371 test.fullTestName, trustResult, [expectedResult unsignedIntValue]);
372 }
373 require_quiet(trustResult == [expectedResult unsignedIntValue], testOut);
374
375 require_quiet(chainLen = [testDict objectForKey:kSecTrustTestChainLength], testOut);
376 require_action_quiet(SecTrustGetCertificateCount(trust) == [chainLen longValue], testOut,
377 fail("%@: actual chain length %ld did not match expected chain length %ld",
378 test.fullTestName, SecTrustGetCertificateCount(trust), [chainLen longValue]));
379
380 testOut:
381 // Unset preferences to prevent contamination
382 if (enableTestCertificates) {
383 CFPreferencesSetAppValue((__bridge CFStringRef)[testDict objectForKey:kSecTrustTestEnableTestCerts],
384 kCFBooleanFalse, CFSTR("com.apple.security"));
385 CFPreferencesSetAppValue((__bridge CFStringRef)[testDict objectForKey:kSecTrustTestEnableTestCerts],
386 kCFBooleanFalse, CFSTR("com.apple.Security"));
387 }
388 CFReleaseNull(trust);
389 };
390
391 static void tests(void)
392 {
393 NSURL *testPlist = nil;
394 NSArray *testsArray = nil;
395
396 testPlist = [[NSBundle mainBundle] URLForResource:@"debugging" withExtension:@"plist"
397 subdirectory:(NSString *)kSecTrustTestPinningPolicyResources ];
398 if (!testPlist) {
399 testPlist = [[NSBundle mainBundle] URLForResource:nil withExtension:@"plist"
400 subdirectory:(NSString *)kSecTrustTestPinningPolicyResources ];
401 }
402 require_action_quiet(testPlist, exit,
403 fail("Failed to get tests plist from %@", kSecTrustTestPinningPolicyResources));
404
405 testsArray = [NSArray arrayWithContentsOfURL: testPlist];
406 require_action_quiet(testsArray, exit,
407 fail("Failed to create array from plist"));
408
409 plan_tests((int)[testsArray count]);
410
411 [testsArray enumerateObjectsUsingBlock:runTestForObject];
412
413 exit:
414 return;
415 }
416
417 int si_20_sectrust_policies(int argc, char *const *argv)
418 {
419
420 @autoreleasepool {
421 tests();
422 }
423
424 return 0;
425 }