2 * Copyright (c) 2015-2017 Apple Inc. All Rights Reserved.
5 #include <AssertMacros.h>
6 #import <Foundation/Foundation.h>
9 #import <archive_entry.h>
11 #include <Security/SecCertificate.h>
12 #include <Security/SecCertificatePriv.h>
13 #include <Security/SecPolicyPriv.h>
14 #include <Security/SecTrustPriv.h>
15 #include <Security/SecItem.h>
16 #include <utilities/SecCFWrappers.h>
18 #include "shared_regressions.h"
20 #include "si-87-sectrust-name-constraints.h"
23 static void tests(void) {
24 SecCertificateRef root = NULL, subca = NULL, leaf1 = NULL, leaf2 = NULL;
25 NSArray *certs1 = nil, *certs2, *anchors = nil;
26 SecPolicyRef policy = SecPolicyCreateBasicX509();
27 SecTrustRef trust = NULL;
28 SecTrustResultType trustResult = kSecTrustResultInvalid;
29 NSDate *date = [NSDate dateWithTimeIntervalSinceReferenceDate:517282600.0]; // 23 May 2017
31 require_action(root = SecCertificateCreateWithBytes(NULL, _test_root, sizeof(_test_root)), errOut,
32 fail("Failed to create root cert"));
33 require_action(subca = SecCertificateCreateWithBytes(NULL, _test_intermediate, sizeof(_test_intermediate)), errOut,
34 fail("Failed to create subca cert"));
35 require_action(leaf1 = SecCertificateCreateWithBytes(NULL, _test_leaf1, sizeof(_test_leaf1)), errOut,
36 fail("Failed to create leaf cert 1"));
37 require_action(leaf2 = SecCertificateCreateWithBytes(NULL, _test_leaf2, sizeof(_test_leaf2)), errOut,
38 fail("Failed to create leaf cert 2"));
40 certs1 = @[(__bridge id)leaf1, (__bridge id)subca];
41 certs2 = @[(__bridge id)leaf2, (__bridge id)subca];
42 anchors = @[(__bridge id)root];
44 require_noerr_action(SecTrustCreateWithCertificates((__bridge CFArrayRef)certs1,
45 policy, &trust), errOut,
46 fail("Failed to create trust for leaf 1"));
47 require_noerr_action(SecTrustSetVerifyDate(trust, (__bridge CFDateRef)date), errOut,
48 fail("Failed to set verify date"));
49 require_noerr_action(SecTrustSetAnchorCertificates(trust, (__bridge CFArrayRef)anchors), errOut,
50 fail("Failed to set anchors"));
51 require_noerr_action(SecTrustEvaluate(trust, &trustResult), errOut,
52 fail("Failed to evaluate trust"));
53 is(trustResult, kSecTrustResultUnspecified, "Got wrong trust result for leaf 1");
56 trustResult = kSecTrustResultInvalid;
58 require_noerr_action(SecTrustCreateWithCertificates((__bridge CFArrayRef)certs2,
59 policy, &trust), errOut,
60 fail("Failed to create trust for leaf 1"));
61 require_noerr_action(SecTrustSetVerifyDate(trust, (__bridge CFDateRef)date), errOut,
62 fail("Failed to set verify date"));
63 require_noerr_action(SecTrustSetAnchorCertificates(trust, (__bridge CFArrayRef)anchors), errOut,
64 fail("Failed to set anchors"));
65 require_noerr_action(SecTrustEvaluate(trust, &trustResult), errOut,
66 fail("Failed to evaluate trust"));
67 is(trustResult, kSecTrustResultUnspecified, "Got wrong trust result for leaf 1");
74 CFReleaseNull(policy);
78 /* MARK: BetterTLS tests */
79 NSString *kSecTrustTestNameConstraintsResources = @"si-87-sectrust-name-constraints";
80 NSString *kSecTrustTestCertificates = @"TestCertificates";
81 NSString *kSecTrustTestIPAddress = @"52.20.118.238";
82 NSString *kSecTrustTestDNSAddress = @"test.nameconstraints.bettertls.com";
83 NSString *kSecTrustTestID = @"id";
84 NSString *kSecTrustTestDNSResult = @"dns";
85 NSString *kSecTrustTestIPResult = @"ip";
86 NSString *kSecTrustTestExpect = @"expect";
87 NSString *kSecTrustTestExpectFailure = @"ERROR";
88 NSString *kSecTrustTestExpectSuccess = @"OK";
89 NSString *kSecTrustTestExpectMaybeSuccess = @"WEAK-OK";
91 static NSArray *anchors = nil;
92 static NSURL *tmpCertsDir = nil;
94 static NSArray *getTestsArray(void) {
95 NSURL *testPlist = nil;
96 NSDictionary *testsDict = nil;
97 NSArray *testsArray = nil;
99 testPlist = [[NSBundle mainBundle] URLForResource:@"debugging" withExtension:@"plist"
100 subdirectory:kSecTrustTestNameConstraintsResources];
102 testPlist = [[NSBundle mainBundle] URLForResource:@"expects" withExtension:@"plist"
103 subdirectory:kSecTrustTestNameConstraintsResources];
105 require_action_quiet(testPlist, exit,
106 fail("Failed to get tests plist from %@", kSecTrustTestNameConstraintsResources));
107 testsDict = [NSDictionary dictionaryWithContentsOfURL:testPlist];
108 require_action_quiet(testsDict, exit, fail("Failed to decode tests plist into dictionary"));
110 testsArray = testsDict[@"expects"];
111 require_action_quiet(testsArray, exit, fail("Failed to get expects array from test dictionary"));
112 require_action_quiet([testsArray isKindOfClass:[NSArray class]], exit, fail("expected array of tests"));
118 static NSFileHandle *openFileForWriting(const char *filename) {
119 NSFileHandle *fileHandle = NULL;
120 NSURL *file = [NSURL URLWithString:[NSString stringWithCString:filename encoding:NSUTF8StringEncoding] relativeToURL:tmpCertsDir];
123 fd = open([file fileSystemRepresentation], O_RDWR | O_CREAT | O_TRUNC, 0644);
124 if (fd < 0 || (off = lseek(fd, 0, SEEK_SET)) < 0) {
125 fail("unable to open file for archive");
132 fileHandle = [NSFileHandle fileHandleForWritingToURL:file error:&error];
134 fail("unable to get file handle for %@\n\terror:%@", file, error);
141 extract(NSURL *archive) {
144 struct archive_entry *entry;
146 struct archive *a = archive_read_new();
147 archive_read_support_compression_all(a);
148 archive_read_support_format_tar(a);
149 r = archive_read_open_filename(a, [archive fileSystemRepresentation], 16384);
150 if (r != ARCHIVE_OK) {
151 fail("unable to open archive");
155 while((r = archive_read_next_header(a, &entry)) == ARCHIVE_OK) {
157 const char *filename = archive_entry_pathname(entry);
158 NSFileHandle *fh = openFileForWriting(filename);
160 size_t bufsize = 4192;
161 uint8_t *buf = calloc(bufsize, 1);
163 size = archive_read_data(a, buf, bufsize);
165 fail("failed to read %s from archive", filename);
172 [fh writeData:[NSData dataWithBytes:buf length:size]];
178 if (r != ARCHIVE_EOF) {
179 fail("unable to read archive header");
185 archive_read_finish(a);
189 static BOOL untar_test_certs(void) {
190 NSError *error = nil;
191 tmpCertsDir = [[NSURL fileURLWithPath:NSTemporaryDirectory() isDirectory:YES] URLByAppendingPathComponent:kSecTrustTestNameConstraintsResources isDirectory:YES];
193 if (![[NSFileManager defaultManager] createDirectoryAtURL:tmpCertsDir
194 withIntermediateDirectories:NO
197 fail("unable to create temporary cert directory: %@", error);
201 NSURL *certs_tar = [[NSBundle mainBundle] URLForResource:kSecTrustTestCertificates withExtension:nil
202 subdirectory:kSecTrustTestNameConstraintsResources];
203 if(!extract(certs_tar)) {
210 static BOOL extractLeaf(NSString *filename, NSMutableArray *certs) {
211 NSString *fullFilename = [NSString stringWithFormat:@"%@.cer", filename];
212 NSURL *leafURL = [tmpCertsDir URLByAppendingPathComponent:fullFilename];
214 fail("Failed to get leaf certificate for test id %@", filename);
217 NSData *leafData = [NSData dataWithContentsOfURL:leafURL];
219 fail("Failed to get leaf certificate data for URL %@", leafURL);
222 SecCertificateRef cert = SecCertificateCreateWithData(NULL, (__bridge CFDataRef)leafData);
224 fail("Failed to create leaf cert for %@", leafURL);
227 [certs addObject:(__bridge id)cert];
232 static BOOL extractChain(NSString *filename, NSMutableArray *certs) {
233 NSString *fullFilename = [NSString stringWithFormat:@"%@.chain", filename];
234 NSURL *chainURL = [tmpCertsDir URLByAppendingPathComponent:fullFilename];
236 fail("Failed to get chain URL for %@", filename);
239 NSString *chain = [NSString stringWithContentsOfURL:chainURL encoding:NSUTF8StringEncoding error:nil];
241 fail("Failed to get chain for %@", chainURL);
245 NSString *pattern = @"-----BEGIN CERTIFICATE-----.+?-----END CERTIFICATE-----\n";
246 NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:pattern
247 options:NSRegularExpressionDotMatchesLineSeparators|NSRegularExpressionUseUnixLineSeparators
249 [regex enumerateMatchesInString:chain options:0 range:NSMakeRange(0, [chain length])
250 usingBlock:^(NSTextCheckingResult * _Nullable result, NSMatchingFlags flags, BOOL * _Nonnull stop) {
251 NSString *certPEMString = [chain substringWithRange:[result range]];
252 NSData *certPEMData = [certPEMString dataUsingEncoding:NSUTF8StringEncoding];
253 SecCertificateRef cert = SecCertificateCreateWithPEM(NULL, (__bridge CFDataRef)certPEMData);
254 [certs addObject:(__bridge id)cert];
260 static BOOL getAnchor(void) {
261 NSURL *rootURL = [[NSBundle mainBundle] URLForResource:@"root" withExtension:@"cer"
262 subdirectory:kSecTrustTestNameConstraintsResources];
264 fail("Failed to get root cert");
267 NSData *rootData = [NSData dataWithContentsOfURL:rootURL];
268 SecCertificateRef root = SecCertificateCreateWithData(NULL, (__bridge CFDataRef)rootData);
270 fail("failed to create root cert");
273 anchors = [NSArray arrayWithObject:(__bridge id)root];
278 static BOOL testTrust(NSArray *certs, NSString *hostname) {
279 if (!anchors && !getAnchor()) {
283 SecPolicyRef policy = SecPolicyCreateSSL(true, (__bridge CFStringRef)hostname);
284 SecTrustRef trust = NULL;
285 NSDate *date = [NSDate dateWithTimeIntervalSinceReferenceDate:531900000.0]; /* November 8, 2017 at 10:00:00 PM PST */
286 require_noerr_action(SecTrustCreateWithCertificates((__bridge CFArrayRef)certs, policy, &trust), exit,
287 fail("Failed to create trust ref"));
288 require_noerr_action(SecTrustSetAnchorCertificates(trust, (__bridge CFArrayRef)anchors), exit,
289 fail("Failed to add anchor"));
290 require_noerr_action(SecTrustSetVerifyDate(trust, (__bridge CFDateRef)date), exit,
291 fail("Failed to set verify date"));
292 #pragma clang diagnostic push
293 #pragma clang diagnostic ignored "-Wunguarded-availability-new"
294 result = SecTrustEvaluateWithError(trust, nil);
295 #pragma clang diagnostic pop
298 CFReleaseNull(policy);
299 CFReleaseNull(trust);
303 void (^runNameConstraintsTestForObject)(id, NSUInteger, BOOL *) =
304 ^(NSDictionary *testDict, NSUInteger idx, BOOL *stop) {
306 /* Get the certificates */
307 NSNumber *testNum = testDict[kSecTrustTestID];
308 NSString *fileName = [NSString stringWithFormat:@"%@",testNum];
309 NSMutableArray *certificates = [NSMutableArray array];
310 if (!extractLeaf(fileName, certificates) || !extractChain(fileName, certificates)) {
314 /* Test DNS address */
315 NSDictionary *dnsDict = testDict[kSecTrustTestDNSResult];
316 BOOL result = testTrust(certificates, kSecTrustTestDNSAddress);
317 NSString *dnsExpectedResult = dnsDict[kSecTrustTestExpect];
318 if ([dnsExpectedResult isEqualToString:kSecTrustTestExpectFailure]) {
320 "Test DNS id: %@. Expected %@. Got %d", testNum, dnsExpectedResult, result);
321 } else if ([dnsExpectedResult isEqualToString:kSecTrustTestExpectSuccess]) {
323 "Test DNS id: %@. Expected %@. Got %d", testNum, dnsExpectedResult, result);
324 } else if ([dnsExpectedResult isEqualToString:kSecTrustTestExpectMaybeSuccess]) {
325 /* These are "OK" but it's acceptable to reject them */
329 /* Test IP address */
330 NSDictionary *ipDict = testDict[kSecTrustTestIPResult];
331 result = testTrust(certificates, kSecTrustTestIPAddress);
332 NSString *ipExpectedResult = ipDict[kSecTrustTestExpect];
333 if ([ipExpectedResult isEqualToString:kSecTrustTestExpectFailure]) {
335 "Test IP id: %@. Expected %@. Got %d", testNum, ipExpectedResult, result);
336 } else if ([ipExpectedResult isEqualToString:kSecTrustTestExpectSuccess]) {
338 "Test IP id: %@. Expected %@. Got %d", testNum, ipExpectedResult, result);
339 } else if ([ipExpectedResult isEqualToString:kSecTrustTestExpectMaybeSuccess]) {
340 /* These are "OK" but it's acceptable to reject them */
346 static void cleanup(NSURL *tmpDir) {
347 [[NSFileManager defaultManager] removeItemAtURL:tmpDir error:nil];
350 int si_87_sectrust_name_constraints(int argc, char *const *argv)
352 NSArray *testsArray = getTestsArray();
353 plan_tests(2 + (int)(2 * [testsArray count]));
356 if(untar_test_certs()) {
357 [testsArray enumerateObjectsUsingBlock:runNameConstraintsTestForObject];
359 cleanup(tmpCertsDir);