]> git.saurik.com Git - apple/security.git/blob - OSX/sec/Security/Regressions/secitem/si-87-sectrust-name-constraints.m
Security-58286.60.28.tar.gz
[apple/security.git] / OSX / sec / Security / Regressions / secitem / si-87-sectrust-name-constraints.m
1 /*
2 * Copyright (c) 2015-2017 Apple Inc. All Rights Reserved.
3 */
4
5 #include <AssertMacros.h>
6 #import <Foundation/Foundation.h>
7
8 #import <archive.h>
9 #import <archive_entry.h>
10
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>
17
18 #include "shared_regressions.h"
19
20 #include "si-87-sectrust-name-constraints.h"
21
22
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
30
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"));
39
40 certs1 = @[(__bridge id)leaf1, (__bridge id)subca];
41 certs2 = @[(__bridge id)leaf2, (__bridge id)subca];
42 anchors = @[(__bridge id)root];
43
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");
54
55 CFReleaseNull(trust);
56 trustResult = kSecTrustResultInvalid;
57
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");
68
69 errOut:
70 CFReleaseNull(root);
71 CFReleaseNull(subca);
72 CFReleaseNull(leaf1);
73 CFReleaseNull(leaf2);
74 CFReleaseNull(policy);
75 CFReleaseNull(trust);
76 }
77
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";
90
91 static NSArray *anchors = nil;
92 static NSURL *tmpCertsDir = nil;
93
94 static NSArray *getTestsArray(void) {
95 NSURL *testPlist = nil;
96 NSDictionary *testsDict = nil;
97 NSArray *testsArray = nil;
98
99 testPlist = [[NSBundle mainBundle] URLForResource:@"debugging" withExtension:@"plist"
100 subdirectory:kSecTrustTestNameConstraintsResources];
101 if (!testPlist) {
102 testPlist = [[NSBundle mainBundle] URLForResource:@"expects" withExtension:@"plist"
103 subdirectory:kSecTrustTestNameConstraintsResources];
104 }
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"));
109
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"));
113
114 exit:
115 return testsArray;
116 }
117
118 static NSFileHandle *openFileForWriting(const char *filename) {
119 NSFileHandle *fileHandle = NULL;
120 NSURL *file = [NSURL URLWithString:[NSString stringWithCString:filename encoding:NSUTF8StringEncoding] relativeToURL:tmpCertsDir];
121 int fd;
122 off_t off;
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");
126 }
127 if (fd >= 0) {
128 close(fd);
129 }
130
131 NSError *error;
132 fileHandle = [NSFileHandle fileHandleForWritingToURL:file error:&error];
133 if (!fileHandle) {
134 fail("unable to get file handle for %@\n\terror:%@", file, error);
135 }
136
137 return fileHandle;
138 }
139
140 static BOOL
141 extract(NSURL *archive) {
142 BOOL result = NO;
143 int r;
144 struct archive_entry *entry;
145
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");
152 goto exit;
153 }
154
155 while((r = archive_read_next_header(a, &entry)) == ARCHIVE_OK) {
156 @autoreleasepool {
157 const char *filename = archive_entry_pathname(entry);
158 NSFileHandle *fh = openFileForWriting(filename);
159 ssize_t size = 0;
160 size_t bufsize = 4192;
161 uint8_t *buf = calloc(bufsize, 1);
162 for (;;) {
163 size = archive_read_data(a, buf, bufsize);
164 if (size < 0) {
165 fail("failed to read %s from archive", filename);
166 [fh closeFile];
167 goto exit;
168 }
169 if (size == 0) {
170 break;
171 }
172 [fh writeData:[NSData dataWithBytes:buf length:size]];
173 }
174 free(buf);
175 [fh closeFile];
176 }
177 }
178 if (r != ARCHIVE_EOF) {
179 fail("unable to read archive header");
180 } else {
181 result = YES;
182 }
183
184 exit:
185 archive_read_finish(a);
186 return result;
187 }
188
189 static BOOL untar_test_certs(void) {
190 NSError *error = nil;
191 tmpCertsDir = [[NSURL fileURLWithPath:NSTemporaryDirectory() isDirectory:YES] URLByAppendingPathComponent:kSecTrustTestNameConstraintsResources isDirectory:YES];
192
193 if (![[NSFileManager defaultManager] createDirectoryAtURL:tmpCertsDir
194 withIntermediateDirectories:NO
195 attributes:NULL
196 error:&error]) {
197 fail("unable to create temporary cert directory: %@", error);
198 return NO;
199 }
200
201 NSURL *certs_tar = [[NSBundle mainBundle] URLForResource:kSecTrustTestCertificates withExtension:nil
202 subdirectory:kSecTrustTestNameConstraintsResources];
203 if(!extract(certs_tar)) {
204 return NO;
205 }
206
207 return YES;
208 }
209
210 static BOOL extractLeaf(NSString *filename, NSMutableArray *certs) {
211 NSString *fullFilename = [NSString stringWithFormat:@"%@.cer", filename];
212 NSURL *leafURL = [tmpCertsDir URLByAppendingPathComponent:fullFilename];
213 if (!leafURL) {
214 fail("Failed to get leaf certificate for test id %@", filename);
215 return NO;
216 }
217 NSData *leafData = [NSData dataWithContentsOfURL:leafURL];
218 if (!leafData) {
219 fail("Failed to get leaf certificate data for URL %@", leafURL);
220 return NO;
221 }
222 SecCertificateRef cert = SecCertificateCreateWithData(NULL, (__bridge CFDataRef)leafData);
223 if (!leafData) {
224 fail("Failed to create leaf cert for %@", leafURL);
225 return NO;
226 }
227 [certs addObject:(__bridge id)cert];
228 CFReleaseNull(cert);
229 return YES;
230 }
231
232 static BOOL extractChain(NSString *filename, NSMutableArray *certs) {
233 NSString *fullFilename = [NSString stringWithFormat:@"%@.chain", filename];
234 NSURL *chainURL = [tmpCertsDir URLByAppendingPathComponent:fullFilename];
235 if (!chainURL) {
236 fail("Failed to get chain URL for %@", filename);
237 return NO;
238 }
239 NSString *chain = [NSString stringWithContentsOfURL:chainURL encoding:NSUTF8StringEncoding error:nil];
240 if (!chain) {
241 fail("Failed to get chain for %@", chainURL);
242 return NO;
243 }
244
245 NSString *pattern = @"-----BEGIN CERTIFICATE-----.+?-----END CERTIFICATE-----\n";
246 NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:pattern
247 options:NSRegularExpressionDotMatchesLineSeparators|NSRegularExpressionUseUnixLineSeparators
248 error:nil];
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];
255 CFReleaseNull(cert);
256 }];
257 return YES;
258 }
259
260 static BOOL getAnchor(void) {
261 NSURL *rootURL = [[NSBundle mainBundle] URLForResource:@"root" withExtension:@"cer"
262 subdirectory:kSecTrustTestNameConstraintsResources];
263 if (!rootURL) {
264 fail("Failed to get root cert");
265 return NO;
266 }
267 NSData *rootData = [NSData dataWithContentsOfURL:rootURL];
268 SecCertificateRef root = SecCertificateCreateWithData(NULL, (__bridge CFDataRef)rootData);
269 if (!root) {
270 fail("failed to create root cert");
271 return NO;
272 }
273 anchors = [NSArray arrayWithObject:(__bridge id)root];
274 CFReleaseNull(root);
275 return YES;
276 }
277
278 static BOOL testTrust(NSArray *certs, NSString *hostname) {
279 if (!anchors && !getAnchor()) {
280 return NO;
281 }
282 BOOL result = NO;
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
296
297 exit:
298 CFReleaseNull(policy);
299 CFReleaseNull(trust);
300 return result;
301 }
302
303 void (^runNameConstraintsTestForObject)(id, NSUInteger, BOOL *) =
304 ^(NSDictionary *testDict, NSUInteger idx, BOOL *stop) {
305 @autoreleasepool {
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)) {
311 return;
312 }
313
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]) {
319 is(result, NO,
320 "Test DNS id: %@. Expected %@. Got %d", testNum, dnsExpectedResult, result);
321 } else if ([dnsExpectedResult isEqualToString:kSecTrustTestExpectSuccess]) {
322 is(result, YES,
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 */
326 pass();
327 }
328
329 /* Test IP address */
330 NSDictionary *ipDict = testDict[kSecTrustTestIPResult];
331 result = testTrust(certificates, kSecTrustTestIPAddress);
332 NSString *ipExpectedResult = ipDict[kSecTrustTestExpect];
333 if ([ipExpectedResult isEqualToString:kSecTrustTestExpectFailure]) {
334 is(result, NO,
335 "Test IP id: %@. Expected %@. Got %d", testNum, ipExpectedResult, result);
336 } else if ([ipExpectedResult isEqualToString:kSecTrustTestExpectSuccess]) {
337 is(result, YES,
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 */
341 pass();
342 }
343 }
344 };
345
346 static void cleanup(NSURL *tmpDir) {
347 [[NSFileManager defaultManager] removeItemAtURL:tmpDir error:nil];
348 }
349
350 int si_87_sectrust_name_constraints(int argc, char *const *argv)
351 {
352 NSArray *testsArray = getTestsArray();
353 plan_tests(2 + (int)(2 * [testsArray count]));
354 tests();
355
356 if(untar_test_certs()) {
357 [testsArray enumerateObjectsUsingBlock:runNameConstraintsTestForObject];
358 }
359 cleanup(tmpCertsDir);
360
361 return 0;
362 }