]> git.saurik.com Git - apple/security.git/blob - tests/TrustTests/TrustEvaluationTestHelpers.m
Security-59306.80.4.tar.gz
[apple/security.git] / tests / TrustTests / TrustEvaluationTestHelpers.m
1 //
2 // TrustEvaluationTestHelpers.m
3 // Security
4 //
5 //
6
7 #include <AssertMacros.h>
8 #import <Foundation/Foundation.h>
9 #import <Security/Security.h>
10
11 #include <utilities/SecInternalReleasePriv.h>
12 #include <utilities/SecCFRelease.h>
13 #include <Security/SecCertificate.h>
14 #include <Security/SecCertificatePriv.h>
15 #include <Security/SecPolicyPriv.h>
16 #include <Security/SecTrust.h>
17 #include <Security/SecTrustPriv.h>
18 #include <stdlib.h>
19 #include <sys/socket.h>
20 #include <sys/types.h>
21 #include <netinet/in.h>
22 #include <arpa/inet.h>
23 #include <netdb.h>
24 #include <unistd.h>
25 #include <string.h>
26
27 #include "TrustEvaluationTestHelpers.h"
28
29 // Want a class for running trust evaluations
30 // Want a dictionary-driven test with callback
31
32 @interface TestTrustEvaluation ()
33 @property NSString *directory;
34 @property NSMutableArray *certificates;
35 @property NSMutableArray *policies;
36 @property BOOL enableTestCertificates;
37 @end
38
39 @implementation TestTrustEvaluation
40 @synthesize ocspResponses = _ocspResponses;
41 @synthesize presentedSCTs = _presentedSCTs;
42 @synthesize trustedCTLogs = _trustedCTLogs;
43
44
45 - (instancetype)initWithCertificates:(NSArray *)certs policies:(NSArray *)policies {
46 if (self = [super init]) {
47 if (errSecSuccess != SecTrustCreateWithCertificates((__bridge CFArrayRef)certs, (__bridge CFArrayRef)policies, &_trust)) {
48 return NULL;
49 }
50 }
51 return self;
52 }
53
54 - (void)addAnchor:(SecCertificateRef)certificate
55 {
56 CFArrayRef anchors = NULL;
57 SecTrustCopyCustomAnchorCertificates(_trust, &anchors);
58
59 NSMutableArray *newAnchors = [NSMutableArray array];
60 if (anchors) {
61 [newAnchors addObjectsFromArray:CFBridgingRelease(anchors)];
62 }
63 [newAnchors addObject:(__bridge id)certificate];
64 (void)SecTrustSetAnchorCertificates(_trust, (__bridge CFArrayRef)newAnchors);
65 }
66
67 - (void)setAnchors:(NSArray *)anchorArray {
68 (void)SecTrustSetAnchorCertificates(_trust, (__bridge CFArrayRef)anchorArray);
69 }
70
71 - (NSArray *)anchors {
72 CFArrayRef anchors = NULL;
73 SecTrustCopyCustomAnchorCertificates(_trust, &anchors);
74 return CFBridgingRelease(anchors);
75 }
76
77 - (void)setOcspResponses:(NSArray *)ocspResponsesArray {
78 if (ocspResponsesArray != _ocspResponses) {
79 _ocspResponses = nil;
80 _ocspResponses = ocspResponsesArray;
81 (void)SecTrustSetOCSPResponse(_trust, (__bridge CFArrayRef)ocspResponsesArray);
82 }
83 }
84
85 - (void)setPresentedSCTs:(NSArray *)presentedSCTsArray {
86 if (presentedSCTsArray != _presentedSCTs) {
87 _presentedSCTs = nil;
88 _presentedSCTs = presentedSCTsArray;
89 (void)SecTrustSetSignedCertificateTimestamps(_trust, (__bridge CFArrayRef)presentedSCTsArray);
90 }
91 }
92
93 - (void)setTrustedCTLogs:(NSArray *)trustedCTLogsArray {
94 if (trustedCTLogsArray != _trustedCTLogs) {
95 _trustedCTLogs = nil;
96 _trustedCTLogs = trustedCTLogsArray;
97 (void)SecTrustSetTrustedLogs(_trust, (__bridge CFArrayRef)trustedCTLogsArray);
98 }
99 }
100
101 - (void)setVerifyDate:(NSDate *)aVerifyDate {
102 if (aVerifyDate != _verifyDate) {
103 _verifyDate = nil;
104 _verifyDate = aVerifyDate;
105 (void)SecTrustSetVerifyDate(_trust, (__bridge CFDateRef)aVerifyDate);
106 }
107 }
108
109 - (bool)evaluate:(out NSError * _Nullable __autoreleasing *)outError {
110 CFErrorRef localError = nil;
111 _trustResult = kSecTrustResultInvalid;
112 _resultDictionary = nil;
113 bool result = SecTrustEvaluateWithError(_trust, &localError);
114 if (outError && localError) {
115 *outError = CFBridgingRelease(localError);
116 }
117 (void)SecTrustGetTrustResult(_trust, &_trustResult);
118 _resultDictionary = CFBridgingRelease(SecTrustCopyResult(_trust));
119 return result;
120 }
121
122 - (void) dealloc {
123 CFReleaseNull(_trust);
124 }
125
126 /* MARK: -
127 * MARK: Dictionary-driven trust objects
128 */
129
130 /* INSTRUCTIONS FOR ADDING NEW DICTIONARY-DRIVEN TRUSTS:
131 * 1. Add the certificates, as DER-encoded files with the 'cer' extension to a directory included in the test Resources
132 * (e.g. OSX/shared_regressions/si-20-sectrust-policies-data/)
133 * 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
134 * 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
135 * works.
136 * 2. The input dictionary must include: (see constants below)
137 * MajorTestName
138 * MinorTestName
139 * Policies
140 * Leaf
141 * ExpectedResult
142 * CertDirectory
143 * It is strongly recommended that all test dictionaries include the Anchors and VerifyDate keys.
144 * Addtional optional keys are defined below.
145 */
146
147 /* Key Constants for Test Dictionaries */
148 const NSString *kSecTrustTestMajorTestName = @"MajorTestName"; /* Required; value: string */
149 const NSString *kSecTrustTestMinorTestName = @"MinorTestName"; /* Required; value: string */
150 const NSString *kSecTrustTestPolicies = @"Policies"; /* Required; value: dictionary or array of dictionaries */
151 const NSString *kSecTrustTestLeaf = @"Leaf"; /* Required; value: string */
152 const NSString *kSecTrustTestIntermediates = @"Intermediates"; /* Optional; value: string or array of strings */
153 const NSString *kSecTrustTestAnchors = @"Anchors"; /* Recommended; value: string or array of strings */
154 const NSString *kSecTrustTestVerifyDate = @"VerifyDate"; /* Recommended; value: date */
155 const NSString *kSecTrustTestExpectedResult = @"ExpectedResult"; /* Required; value: number */
156 const NSString *kSecTrustTestChainLength = @"ChainLength"; /* Optional; value: number */
157 const NSString *kSecTrustTestEnableTestCerts= @"EnableTestCertificates"; /* Optional; value: string */
158 const NSString *kSecTrustTestDisableBridgeOS= @"BridgeOSDisable"; /* Optional; value: boolean */
159 const NSString *kSecTrustTestDirectory = @"CertDirectory"; /* Required; value: string */
160
161 /* Key Constants for Policies Dictionaries */
162 const NSString *kSecTrustTestPolicyOID = @"PolicyIdentifier"; /* Required; value: string */
163 const NSString *kSecTrustTestPolicyProperties = @"Properties"; /* Optional; value: dictionary, see Policy Value Constants, SecPolicy.h */
164
165 - (void)setMajorTestName:(NSString *)majorTestName minorTestName:(NSString *)minorTestName {
166 self.fullTestName = [[majorTestName stringByAppendingString:@"-"] stringByAppendingString:minorTestName];
167 }
168
169 #if TARGET_OS_TV
170 /* Mastering removes all files named i[Pp]hone, so dynamically replace any i[Pp]hone's with
171 * iPh0ne. We have two copies in the resources directory. */
172 - (NSString *)replaceiPhoneNamedFiles:(NSString *)filename {
173 NSRegularExpression *regularExpression = [NSRegularExpression regularExpressionWithPattern:@"iphone"
174 options:NSRegularExpressionCaseInsensitive
175 error:nil];
176 NSString *newfilename = [regularExpression stringByReplacingMatchesInString:filename
177 options:0
178 range:NSMakeRange(0, [filename length])
179 withTemplate:@"iPh0ne"];
180 return newfilename;
181 }
182 #endif
183
184 - (bool)addLeafToCertificates:(NSString *)leafName {
185 SecCertificateRef cert;
186 NSString *path = nil, *filename = nil;
187 require_string(leafName, errOut, "%@: failed to get leaf for test");
188 #if TARGET_OS_TV
189 filename = [self replaceiPhoneNamedFiles:leafName];
190 #else
191 filename = leafName;
192 #endif
193
194 path = [[NSBundle bundleForClass:[self class]]
195 pathForResource:filename
196 ofType:@"cer"
197 inDirectory:self.directory];
198 require_string(path, errOut, "failed to get path for leaf");
199 cert = SecCertificateCreateWithData(NULL, (CFDataRef)[NSData dataWithContentsOfFile:path]);
200 require_string(cert, errOut, "failed to create leaf certificate from path");
201 self.certificates = [[NSMutableArray alloc] initWithObjects:(__bridge id)cert, nil];
202 CFReleaseNull(cert);
203 require_string(self.certificates, errOut, "failed to initialize certificates array");
204 return true;
205
206 errOut:
207 return false;
208 }
209
210 - (bool)addCertsToArray:(id)pathsObj outputArray:(NSMutableArray *)outArray {
211 __block SecCertificateRef cert = NULL;
212 __block NSString* path = nil, *filename = nil;
213 require_string(pathsObj, errOut,
214 "failed to get certificate paths for test");
215
216 if ([pathsObj isKindOfClass:[NSString class]]) {
217 /* Only one cert path */
218 #if TARGET_OS_TV
219 filename = [self replaceiPhoneNamedFiles:pathsObj];
220 #else
221 filename = pathsObj;
222 #endif
223 path = [[NSBundle bundleForClass:[self class]]
224 pathForResource:filename
225 ofType:@"cer"
226 inDirectory:self.directory];
227 require_string(path, errOut, "failed to get path for cert");
228 cert = SecCertificateCreateWithData(NULL, (CFDataRef)[NSData dataWithContentsOfFile:path]);
229 require_string(cert, errOut, "failed to create certificate from path");
230 [outArray addObject:(__bridge id)cert];
231 CFReleaseNull(cert);
232 } else if ([pathsObj isKindOfClass:[NSArray class]]) {
233 /* Test has more than one intermediate */
234 [(NSArray *)pathsObj enumerateObjectsUsingBlock:^(NSString *resource, NSUInteger idx, BOOL *stop) {
235 #if TARGET_OS_TV
236 filename = [self replaceiPhoneNamedFiles:resource];
237 #else
238 filename = resource;
239 #endif
240 path = [[NSBundle bundleForClass:[self class]]
241 pathForResource:filename
242 ofType:@"cer"
243 inDirectory:self.directory];
244 require_string(path, blockOut, "failed to get path for cert");
245 cert = SecCertificateCreateWithData(NULL, (CFDataRef)[NSData dataWithContentsOfFile:path]);
246 require_string(cert, blockOut, "failed to create certificate %ld from path %@");
247 [outArray addObject:(__bridge id)cert];
248
249 CFReleaseNull(cert);
250 return;
251
252 blockOut:
253 CFReleaseNull(cert);
254 *stop = YES;
255 }];
256 } else {
257 require_string(false, errOut, "unexpected type for intermediates or anchors value");
258 }
259
260 return true;
261
262 errOut:
263 CFReleaseNull(cert);
264 return false;
265 }
266
267 - (bool)addIntermediatesToCertificates:(id)intermediatesObj {
268 require_string(intermediatesObj, errOut, "failed to get intermediates for test");
269
270 require_string([self addCertsToArray:intermediatesObj outputArray:self.certificates], errOut,
271 "failed to add intermediates to certificates array");
272
273 if ([intermediatesObj isKindOfClass:[NSString class]]) {
274 require_string([self.certificates count] == 2, errOut,
275 "failed to add all intermediates");
276 } else if ([intermediatesObj isKindOfClass:[NSArray class]]) {
277 require_string([self.certificates count] == [(NSArray *)intermediatesObj count] + 1, errOut,
278 "failed to add all intermediates");
279 }
280
281 return true;
282
283 errOut:
284 return false;
285 }
286
287 - (bool)addPolicy:(NSDictionary *)policyDict
288 {
289 SecPolicyRef policy = NULL;
290 NSString *policyIdentifier = [(NSDictionary *)policyDict objectForKey:kSecTrustTestPolicyOID];
291 NSDictionary *policyProperties = [(NSDictionary *)policyDict objectForKey:kSecTrustTestPolicyProperties];
292 require_string(policyIdentifier, errOut, "failed to get policy OID");
293
294 policy = SecPolicyCreateWithProperties((__bridge CFStringRef)policyIdentifier,
295 (__bridge CFDictionaryRef)policyProperties);
296 require_string(policy, errOut, "failed to create properties for policy OID");
297 [self.policies addObject:(__bridge id)policy];
298 CFReleaseNull(policy);
299
300 return true;
301 errOut:
302 return false;
303 }
304
305 - (bool)addPolicies:(id)policiesObj {
306 require_string(policiesObj, errOut,
307 "failed to get policies for test");
308
309 self.policies = [[NSMutableArray alloc] init];
310 require_string(self.policies, errOut,
311 "failed to initialize policies array");
312 if ([policiesObj isKindOfClass:[NSDictionary class]]) {
313 /* Test has only one policy */
314 require_string([self addPolicy:policiesObj], errOut, "failed to add policy");
315 } else if ([policiesObj isKindOfClass:[NSArray class]]) {
316 /* Test more than one policy */
317 [(NSArray *)policiesObj enumerateObjectsUsingBlock:^(NSDictionary *policyDict, NSUInteger idx, BOOL *stop) {
318 if (![self addPolicy:policyDict]) {
319 *stop = YES;
320 }
321 }];
322
323 require_string([(NSArray *)policiesObj count] == [self.policies count], errOut, "failed to add all policies");
324 } else {
325 require_string(false, errOut, "unexpected type for Policies value");
326 }
327
328 return true;
329
330 errOut:
331 return false;
332 }
333
334 - (bool)setAnchorsFromPlist:(id)anchorsObj {
335 NSMutableArray *anchors = [NSMutableArray array];
336 require_string(anchorsObj, errOut, "failed to get anchors for test");
337 require_string([self addCertsToArray:anchorsObj outputArray:anchors], errOut, "failed to add anchors to anchors array");
338
339 if ([anchorsObj isKindOfClass:[NSString class]]) {
340 require_string([anchors count] == 1, errOut, "failed to add all anchors");
341 } else if ([anchorsObj isKindOfClass:[NSArray class]]) {
342 require_string([anchors count] == [(NSArray *)anchorsObj count], errOut, "failed to add all anchors");
343 }
344
345 // set the anchors in the SecTrustRef
346 self.anchors = anchors;
347 return true;
348
349 errOut:
350 return false;
351 }
352
353 - (instancetype _Nullable) initWithTrustDictionary:(NSDictionary *)testDict
354 {
355 if (!(self = [super init])) {
356 return self;
357 }
358
359 NSString *majorTestName = nil, *minorTestName = nil;
360 SecTrustRef trust = NULL;
361
362 #if TARGET_OS_BRIDGE
363 /* Some of the tests don't work on bridgeOS because there is no Certificates bundle. Skip them. */
364 if([testDict[kSecTrustTestDisableBridgeOS] boolValue]) {
365 self.bridgeOSDisabled = YES;
366 }
367 #endif
368
369 /* Test certificates work by default on internal builds. We still need this to
370 * determine whether to expect failure for production devices. */
371 self.enableTestCertificates = [testDict[kSecTrustTestEnableTestCerts] boolValue];
372
373 /* Test name, for documentation purposes */
374 majorTestName = testDict[kSecTrustTestMajorTestName];
375 minorTestName = testDict[kSecTrustTestMinorTestName];
376 require_string(majorTestName && minorTestName, errOut, "Failed to create test names for test");
377 [self setMajorTestName:majorTestName minorTestName:minorTestName];
378
379 #if DEBUG
380 fprintf(stderr, "BEGIN trust creation for %s", [self.fullTestName cStringUsingEncoding:NSUTF8StringEncoding]);
381 #endif
382
383 /* Cert directory */
384 self.directory = testDict[kSecTrustTestDirectory];
385 require_string(self.directory, errOut, "No directory for test!");
386
387 /* Populate the certificates array */
388 require_quiet([self addLeafToCertificates:testDict[kSecTrustTestLeaf]], errOut);
389
390 /* Add optional intermediates to certificates array */
391 if (testDict[kSecTrustTestIntermediates]) {
392 require_quiet([self addIntermediatesToCertificates:testDict[kSecTrustTestIntermediates]], errOut);
393 }
394
395 /* Create the policies */
396 #if !TARGET_OS_BRIDGE
397 require_quiet([self addPolicies:testDict[kSecTrustTestPolicies]], errOut);
398 #else // TARGET_OS_BRIDGE
399 if (![self addPolicies:testDict[kSecTrustTestPolicies]]) {
400 /* Some policies aren't available on bridgeOS (because there is no Certificates bundle on bridgeOS).
401 * If we fail to add the policies for a disabled test, let SecTrustCreate fall back to the Basic policy.
402 * We'll skip the evaluation and other tests by honoring bridgeOSDisabled, but we need to return a
403 * TestTrustEvaluation object so that the test continues. */
404 if (self.bridgeOSDisabled) {
405 self.policies = nil;
406 } else {
407 goto errOut;
408 }
409 }
410 #endif // TARGET_OS_BRIDGE
411
412
413 /* Create the trust object */
414 require_noerr_string(SecTrustCreateWithCertificates((__bridge CFArrayRef)self.certificates,
415 (__bridge CFArrayRef)self.policies,
416 &trust),
417 errOut,
418 "failed to create trust ref");
419 self.trust = trust;
420
421 /* Optionally set anchors in trust object */
422 if (testDict[kSecTrustTestAnchors]) {
423 require_quiet([self setAnchorsFromPlist:testDict[kSecTrustTestAnchors]], errOut);
424 }
425
426 /* Set optional date in trust object */
427 if (testDict[kSecTrustTestVerifyDate]) {
428 self.verifyDate = testDict[kSecTrustTestVerifyDate];
429 }
430
431 /* Set expected results */
432 self.expectedResult = testDict[kSecTrustTestExpectedResult];
433 self.expectedChainLength = testDict[kSecTrustTestChainLength];
434
435 #if DEBUG
436 fprintf(stderr, "END trust creation for %s", [self.fullTestName cStringUsingEncoding:NSUTF8StringEncoding]);
437 #endif
438
439 return self;
440
441 errOut:
442 return nil;
443 }
444
445 - (bool)evaluateForExpectedResults:(out NSError * _Nullable __autoreleasing *)outError
446 {
447 #if TARGET_OS_BRIDGE
448 // Artificially skip tests for bridgeOS. To prevent test errors these need to be reported as a pass.
449 if (self.bridgeOSDisabled) {
450 return true;
451 }
452 #endif
453
454 if (!self.expectedResult) {
455 if (outError) {
456 NSString *errorDescription = [NSString stringWithFormat:@"Test %@: no expected results set",
457 self.fullTestName];
458 *outError = [NSError errorWithDomain:@"TrustTestsError" code:(-1)
459 userInfo:@{ NSLocalizedFailureReasonErrorKey : errorDescription}];
460 }
461 return false;
462 }
463
464 SecTrustResultType trustResult = kSecTrustResultInvalid;
465 if (errSecSuccess != SecTrustGetTrustResult(self.trust, &trustResult)) {
466 if (outError) {
467 NSString *errorDescription = [NSString stringWithFormat:@"Test %@: Failed to get trust result",
468 self.fullTestName];
469 *outError = [NSError errorWithDomain:@"TrustTestsError" code:(-2)
470 userInfo:@{ NSLocalizedFailureReasonErrorKey : errorDescription}];
471 }
472 return false;
473 }
474
475 bool result = false;
476
477 /* If we enabled test certificates on a non-internal device, expect a failure instead of success. */
478 if (self.enableTestCertificates && !SecIsInternalRelease() && ([self.expectedResult unsignedIntValue] == 4)) {
479 if (trustResult == kSecTrustResultRecoverableTrustFailure) {
480 result = true;
481 }
482 } else if (trustResult == [self.expectedResult unsignedIntValue]) {
483 result = true;
484 }
485
486 if (!result) {
487 if (outError) {
488 NSString *errorDescription = [NSString stringWithFormat:@"Test %@: Expected result %@ %s does not match actual result %u %s",
489 self.fullTestName, self.expectedResult,
490 (self.enableTestCertificates ? "for test cert" : ""),
491 trustResult,
492 SecIsInternalRelease() ? "" : "on prod device"];
493 *outError = [NSError errorWithDomain:@"TrustTestsError" code:(-3)
494 userInfo:@{ NSLocalizedFailureReasonErrorKey : errorDescription}];
495 }
496 return result;
497 }
498
499 /* expected chain length is optional, but if we have it, verify */
500 if (self.expectedChainLength && (SecTrustGetCertificateCount(self.trust) != [self.expectedChainLength longValue])) {
501 if (outError) {
502 NSString *errorDescription = [NSString stringWithFormat:@"Test %@: Expected chain length %@ does not match actual chain length %ld",
503 self.fullTestName, self.expectedChainLength, SecTrustGetCertificateCount(self.trust)];
504 *outError = [NSError errorWithDomain:@"TrustTestsError" code:(-4)
505 userInfo:@{ NSLocalizedFailureReasonErrorKey : errorDescription}];
506 }
507 return false;
508 }
509 return true;
510 }
511
512 @end
513
514 int ping_host(char *host_name)
515 {
516 struct sockaddr_in pin;
517 struct hostent *nlp_host;
518 struct in_addr addr;
519 int sd = 0;
520 int port = 80;
521 int retries = 5; // we try 5 times, then give up
522 char **h_addr_list = NULL;
523
524 while ((nlp_host=gethostbyname(host_name)) == 0 && retries--) {
525 printf("Resolve Error! (%s) %d\n", host_name, h_errno);
526 sleep(1);
527 }
528 if (nlp_host == 0) {
529 return 0;
530 }
531
532 bzero(&pin,sizeof(pin));
533 pin.sin_family=AF_INET;
534 pin.sin_addr.s_addr=htonl(INADDR_ANY);
535 h_addr_list = malloc(nlp_host->h_length * sizeof(char *));
536 memcpy(h_addr_list, nlp_host->h_addr_list, nlp_host->h_length * sizeof(char *));
537 memcpy(&addr, h_addr_list[0], sizeof(struct in_addr));
538 pin.sin_addr.s_addr=addr.s_addr;
539 pin.sin_port=htons(port);
540
541 sd=socket(AF_INET,SOCK_STREAM,0);
542
543 if (connect(sd,(struct sockaddr*)&pin,sizeof(pin)) == -1) {
544 printf("connect error! (%s) %d\n", host_name, errno);
545 close(sd);
546 free(h_addr_list);
547 return 0;
548 }
549 close(sd);
550 free(h_addr_list);
551 return 1;
552 }