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