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