]> git.saurik.com Git - apple/security.git/blob - OSX/sec/Security/Regressions/secitem/si-87-sectrust-name-constraints.m
Security-58286.251.4.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, leaf3 = NULL;
25 NSArray *certs1 = nil, *certs2 = nil, *certs3 = nil, *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 require_action(leaf3 = SecCertificateCreateWithBytes(NULL, _test_leaf3, sizeof(_test_leaf3)), errOut,
40 fail("Failed to create leaf cert 3"));;
41
42 certs1 = @[(__bridge id)leaf1, (__bridge id)subca];
43 certs2 = @[(__bridge id)leaf2, (__bridge id)subca];
44 certs3 = @[(__bridge id)leaf3, (__bridge id)subca];
45 anchors = @[(__bridge id)root];
46
47 /* Test multiple pre-pended labels and intersecting name constraints in the subCA */
48 require_noerr_action(SecTrustCreateWithCertificates((__bridge CFArrayRef)certs1,
49 policy, &trust), errOut,
50 fail("Failed to create trust for leaf 1"));
51 require_noerr_action(SecTrustSetVerifyDate(trust, (__bridge CFDateRef)date), errOut,
52 fail("Failed to set verify date"));
53 require_noerr_action(SecTrustSetAnchorCertificates(trust, (__bridge CFArrayRef)anchors), errOut,
54 fail("Failed to set anchors"));
55 require_noerr_action(SecTrustEvaluate(trust, &trustResult), errOut,
56 fail("Failed to evaluate trust"));
57 is(trustResult, kSecTrustResultUnspecified, "Got wrong trust result for leaf 1");
58
59 CFReleaseNull(trust);
60 trustResult = kSecTrustResultInvalid;
61
62 /* Test no pre-pended labels */
63 require_noerr_action(SecTrustCreateWithCertificates((__bridge CFArrayRef)certs2,
64 policy, &trust), errOut,
65 fail("Failed to create trust for leaf 2"));
66 require_noerr_action(SecTrustSetVerifyDate(trust, (__bridge CFDateRef)date), errOut,
67 fail("Failed to set verify date"));
68 require_noerr_action(SecTrustSetAnchorCertificates(trust, (__bridge CFArrayRef)anchors), errOut,
69 fail("Failed to set anchors"));
70 require_noerr_action(SecTrustEvaluate(trust, &trustResult), errOut,
71 fail("Failed to evaluate trust"));
72 is(trustResult, kSecTrustResultUnspecified, "Got wrong trust result for leaf 2");
73
74 CFReleaseNull(trust);
75 trustResult = kSecTrustResultInvalid;
76
77 /* Test DNS-looking Common Name */
78 date = [NSDate dateWithTimeIntervalSinceReferenceDate:548800000.0]; // 23 May 2018
79 require_noerr_action(SecTrustCreateWithCertificates((__bridge CFArrayRef)certs3,
80 policy, &trust), errOut,
81 fail("Failed to create trust for leaf 3"));
82 require_noerr_action(SecTrustSetVerifyDate(trust, (__bridge CFDateRef)date), errOut,
83 fail("Failed to set verify date"));
84 require_noerr_action(SecTrustSetAnchorCertificates(trust, (__bridge CFArrayRef)anchors), errOut,
85 fail("Failed to set anchors"));
86 require_noerr_action(SecTrustEvaluate(trust, &trustResult), errOut,
87 fail("Failed to evaluate trust"));
88 is(trustResult, kSecTrustResultUnspecified, "Got wrong trust result for leaf 3");
89
90 errOut:
91 CFReleaseNull(root);
92 CFReleaseNull(subca);
93 CFReleaseNull(leaf1);
94 CFReleaseNull(leaf2);
95 CFReleaseNull(policy);
96 CFReleaseNull(trust);
97 }
98
99 /* MARK: BetterTLS tests */
100 NSString *kSecTrustTestNameConstraintsResources = @"si-87-sectrust-name-constraints";
101 NSString *kSecTrustTestCertificates = @"TestCertificates";
102 NSString *kSecTrustTestIPAddress = @"52.20.118.238";
103 NSString *kSecTrustTestDNSAddress = @"test.nameconstraints.bettertls.com";
104 NSString *kSecTrustTestID = @"id";
105 NSString *kSecTrustTestDNSResult = @"dns";
106 NSString *kSecTrustTestIPResult = @"ip";
107 NSString *kSecTrustTestExpect = @"expect";
108 NSString *kSecTrustTestExpectFailure = @"ERROR";
109 NSString *kSecTrustTestExpectSuccess = @"OK";
110 NSString *kSecTrustTestExpectMaybeSuccess = @"WEAK-OK";
111
112 static NSArray *anchors = nil;
113 static NSURL *tmpCertsDir = nil;
114
115 static NSArray *getTestsArray(void) {
116 NSURL *testPlist = nil;
117 NSDictionary *testsDict = nil;
118 NSArray *testsArray = nil;
119
120 testPlist = [[NSBundle mainBundle] URLForResource:@"debugging" withExtension:@"plist"
121 subdirectory:kSecTrustTestNameConstraintsResources];
122 if (!testPlist) {
123 testPlist = [[NSBundle mainBundle] URLForResource:@"expects" withExtension:@"plist"
124 subdirectory:kSecTrustTestNameConstraintsResources];
125 }
126 require_action_quiet(testPlist, exit,
127 fail("Failed to get tests plist from %@", kSecTrustTestNameConstraintsResources));
128 testsDict = [NSDictionary dictionaryWithContentsOfURL:testPlist];
129 require_action_quiet(testsDict, exit, fail("Failed to decode tests plist into dictionary"));
130
131 testsArray = testsDict[@"expects"];
132 require_action_quiet(testsArray, exit, fail("Failed to get expects array from test dictionary"));
133 require_action_quiet([testsArray isKindOfClass:[NSArray class]], exit, fail("expected array of tests"));
134
135 exit:
136 return testsArray;
137 }
138
139 static NSFileHandle *openFileForWriting(const char *filename) {
140 NSFileHandle *fileHandle = NULL;
141 NSURL *file = [NSURL URLWithString:[NSString stringWithCString:filename encoding:NSUTF8StringEncoding] relativeToURL:tmpCertsDir];
142 int fd;
143 off_t off;
144 fd = open([file fileSystemRepresentation], O_RDWR | O_CREAT | O_TRUNC, 0644);
145 if (fd < 0 || (off = lseek(fd, 0, SEEK_SET)) < 0) {
146 fail("unable to open file for archive");
147 }
148 if (fd >= 0) {
149 close(fd);
150 }
151
152 NSError *error;
153 fileHandle = [NSFileHandle fileHandleForWritingToURL:file error:&error];
154 if (!fileHandle) {
155 fail("unable to get file handle for %@\n\terror:%@", file, error);
156 }
157
158 return fileHandle;
159 }
160
161 static BOOL
162 extract(NSURL *archive) {
163 BOOL result = NO;
164 int r;
165 struct archive_entry *entry;
166
167 struct archive *a = archive_read_new();
168 archive_read_support_compression_all(a);
169 archive_read_support_format_tar(a);
170 r = archive_read_open_filename(a, [archive fileSystemRepresentation], 16384);
171 if (r != ARCHIVE_OK) {
172 fail("unable to open archive");
173 goto exit;
174 }
175
176 while((r = archive_read_next_header(a, &entry)) == ARCHIVE_OK) {
177 @autoreleasepool {
178 const char *filename = archive_entry_pathname(entry);
179 NSFileHandle *fh = openFileForWriting(filename);
180 ssize_t size = 0;
181 size_t bufsize = 4192;
182 uint8_t *buf = calloc(bufsize, 1);
183 for (;;) {
184 size = archive_read_data(a, buf, bufsize);
185 if (size < 0) {
186 fail("failed to read %s from archive", filename);
187 [fh closeFile];
188 goto exit;
189 }
190 if (size == 0) {
191 break;
192 }
193 [fh writeData:[NSData dataWithBytes:buf length:size]];
194 }
195 free(buf);
196 [fh closeFile];
197 }
198 }
199 if (r != ARCHIVE_EOF) {
200 fail("unable to read archive header");
201 } else {
202 result = YES;
203 }
204
205 exit:
206 archive_read_finish(a);
207 return result;
208 }
209
210 static BOOL untar_test_certs(void) {
211 NSError *error = nil;
212 tmpCertsDir = [[NSURL fileURLWithPath:NSTemporaryDirectory() isDirectory:YES] URLByAppendingPathComponent:kSecTrustTestNameConstraintsResources isDirectory:YES];
213
214 if (![[NSFileManager defaultManager] createDirectoryAtURL:tmpCertsDir
215 withIntermediateDirectories:NO
216 attributes:NULL
217 error:&error]) {
218 fail("unable to create temporary cert directory: %@", error);
219 return NO;
220 }
221
222 NSURL *certs_tar = [[NSBundle mainBundle] URLForResource:kSecTrustTestCertificates withExtension:nil
223 subdirectory:kSecTrustTestNameConstraintsResources];
224 if(!extract(certs_tar)) {
225 return NO;
226 }
227
228 return YES;
229 }
230
231 static BOOL extractLeaf(NSString *filename, NSMutableArray *certs) {
232 NSString *fullFilename = [NSString stringWithFormat:@"%@.cer", filename];
233 NSURL *leafURL = [tmpCertsDir URLByAppendingPathComponent:fullFilename];
234 if (!leafURL) {
235 fail("Failed to get leaf certificate for test id %@", filename);
236 return NO;
237 }
238 NSData *leafData = [NSData dataWithContentsOfURL:leafURL];
239 if (!leafData) {
240 fail("Failed to get leaf certificate data for URL %@", leafURL);
241 return NO;
242 }
243 SecCertificateRef cert = SecCertificateCreateWithData(NULL, (__bridge CFDataRef)leafData);
244 if (!leafData) {
245 fail("Failed to create leaf cert for %@", leafURL);
246 return NO;
247 }
248 [certs addObject:(__bridge id)cert];
249 CFReleaseNull(cert);
250 return YES;
251 }
252
253 static BOOL extractChain(NSString *filename, NSMutableArray *certs) {
254 NSString *fullFilename = [NSString stringWithFormat:@"%@.chain", filename];
255 NSURL *chainURL = [tmpCertsDir URLByAppendingPathComponent:fullFilename];
256 if (!chainURL) {
257 fail("Failed to get chain URL for %@", filename);
258 return NO;
259 }
260 NSString *chain = [NSString stringWithContentsOfURL:chainURL encoding:NSUTF8StringEncoding error:nil];
261 if (!chain) {
262 fail("Failed to get chain for %@", chainURL);
263 return NO;
264 }
265
266 NSString *pattern = @"-----BEGIN CERTIFICATE-----.+?-----END CERTIFICATE-----\n";
267 NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:pattern
268 options:NSRegularExpressionDotMatchesLineSeparators|NSRegularExpressionUseUnixLineSeparators
269 error:nil];
270 [regex enumerateMatchesInString:chain options:0 range:NSMakeRange(0, [chain length])
271 usingBlock:^(NSTextCheckingResult * _Nullable result, NSMatchingFlags flags, BOOL * _Nonnull stop) {
272 NSString *certPEMString = [chain substringWithRange:[result range]];
273 NSData *certPEMData = [certPEMString dataUsingEncoding:NSUTF8StringEncoding];
274 SecCertificateRef cert = SecCertificateCreateWithPEM(NULL, (__bridge CFDataRef)certPEMData);
275 [certs addObject:(__bridge id)cert];
276 CFReleaseNull(cert);
277 }];
278 return YES;
279 }
280
281 static BOOL getAnchor(void) {
282 NSURL *rootURL = [[NSBundle mainBundle] URLForResource:@"root" withExtension:@"cer"
283 subdirectory:kSecTrustTestNameConstraintsResources];
284 if (!rootURL) {
285 fail("Failed to get root cert");
286 return NO;
287 }
288 NSData *rootData = [NSData dataWithContentsOfURL:rootURL];
289 SecCertificateRef root = SecCertificateCreateWithData(NULL, (__bridge CFDataRef)rootData);
290 if (!root) {
291 fail("failed to create root cert");
292 return NO;
293 }
294 anchors = [NSArray arrayWithObject:(__bridge id)root];
295 CFReleaseNull(root);
296 return YES;
297 }
298
299 static BOOL testTrust(NSArray *certs, NSString *hostname) {
300 if (!anchors && !getAnchor()) {
301 return NO;
302 }
303 BOOL result = NO;
304 SecPolicyRef policy = SecPolicyCreateSSL(true, (__bridge CFStringRef)hostname);
305 SecTrustRef trust = NULL;
306 NSDate *date = [NSDate dateWithTimeIntervalSinceReferenceDate:531900000.0]; /* November 8, 2017 at 10:00:00 PM PST */
307 require_noerr_action(SecTrustCreateWithCertificates((__bridge CFArrayRef)certs, policy, &trust), exit,
308 fail("Failed to create trust ref"));
309 require_noerr_action(SecTrustSetAnchorCertificates(trust, (__bridge CFArrayRef)anchors), exit,
310 fail("Failed to add anchor"));
311 require_noerr_action(SecTrustSetVerifyDate(trust, (__bridge CFDateRef)date), exit,
312 fail("Failed to set verify date"));
313 #pragma clang diagnostic push
314 #pragma clang diagnostic ignored "-Wunguarded-availability-new"
315 result = SecTrustEvaluateWithError(trust, nil);
316 #pragma clang diagnostic pop
317
318 exit:
319 CFReleaseNull(policy);
320 CFReleaseNull(trust);
321 return result;
322 }
323
324 void (^runNameConstraintsTestForObject)(id, NSUInteger, BOOL *) =
325 ^(NSDictionary *testDict, NSUInteger idx, BOOL *stop) {
326 @autoreleasepool {
327 /* Get the certificates */
328 NSNumber *testNum = testDict[kSecTrustTestID];
329 NSString *fileName = [NSString stringWithFormat:@"%@",testNum];
330 NSMutableArray *certificates = [NSMutableArray array];
331 if (!extractLeaf(fileName, certificates) || !extractChain(fileName, certificates)) {
332 return;
333 }
334
335 /* Test DNS address */
336 NSDictionary *dnsDict = testDict[kSecTrustTestDNSResult];
337 BOOL result = testTrust(certificates, kSecTrustTestDNSAddress);
338 NSString *dnsExpectedResult = dnsDict[kSecTrustTestExpect];
339 if ([dnsExpectedResult isEqualToString:kSecTrustTestExpectFailure]) {
340 is(result, NO,
341 "Test DNS id: %@. Expected %@. Got %d", testNum, dnsExpectedResult, result);
342 } else if ([dnsExpectedResult isEqualToString:kSecTrustTestExpectSuccess]) {
343 is(result, YES,
344 "Test DNS id: %@. Expected %@. Got %d", testNum, dnsExpectedResult, result);
345 } else if ([dnsExpectedResult isEqualToString:kSecTrustTestExpectMaybeSuccess]) {
346 /* These are "OK" but it's acceptable to reject them */
347 pass();
348 }
349
350 /* Test IP address */
351 NSDictionary *ipDict = testDict[kSecTrustTestIPResult];
352 result = testTrust(certificates, kSecTrustTestIPAddress);
353 NSString *ipExpectedResult = ipDict[kSecTrustTestExpect];
354 if ([ipExpectedResult isEqualToString:kSecTrustTestExpectFailure]) {
355 is(result, NO,
356 "Test IP id: %@. Expected %@. Got %d", testNum, ipExpectedResult, result);
357 } else if ([ipExpectedResult isEqualToString:kSecTrustTestExpectSuccess]) {
358 is(result, YES,
359 "Test IP id: %@. Expected %@. Got %d", testNum, ipExpectedResult, result);
360 } else if ([ipExpectedResult isEqualToString:kSecTrustTestExpectMaybeSuccess]) {
361 /* These are "OK" but it's acceptable to reject them */
362 pass();
363 }
364 }
365 };
366
367 static void cleanup(NSURL *tmpDir) {
368 [[NSFileManager defaultManager] removeItemAtURL:tmpDir error:nil];
369 }
370
371 int si_87_sectrust_name_constraints(int argc, char *const *argv)
372 {
373 NSArray *testsArray = getTestsArray();
374 plan_tests(3 + (int)(2 * [testsArray count]));
375 tests();
376
377 if(untar_test_certs()) {
378 [testsArray enumerateObjectsUsingBlock:runNameConstraintsTestForObject];
379 }
380 cleanup(tmpCertsDir);
381
382 return 0;
383 }