]> git.saurik.com Git - apple/security.git/blob - protocol/SecProtocolConfigurationTest.m
Security-59306.80.4.tar.gz
[apple/security.git] / protocol / SecProtocolConfigurationTest.m
1 //
2 // SecProtocolConfigurationTest.m
3 // SecureTransportTests
4 //
5
6 #import <XCTest/XCTest.h>
7
8 #include <os/log.h>
9 #include <dlfcn.h>
10 #include <sys/param.h>
11
12 #import "SecProtocolConfiguration.h"
13 #import "SecProtocolPriv.h"
14 #import "SecProtocolInternal.h"
15
16 #import <nw/private.h> // Needed for the mock protocol
17
18 #define SEC_PROTOCOL_METADATA_VALIDATE(m, r) \
19 if (((void *)(m) == NULL) || ((size_t)(m) == 0)) { \
20 return (r); \
21 }
22
23 typedef struct mock_protocol {
24 struct nw_protocol protocol;
25 char *name;
26 } *mock_protocol_t;
27
28 static nw_protocol_t
29 _mock_protocol_create_extended(nw_protocol_identifier_const_t identifier,
30 nw_endpoint_t endpoint,
31 nw_parameters_t parameters)
32 {
33 mock_protocol_t handle = (mock_protocol_t)calloc(1, sizeof(struct mock_protocol));
34 if (handle == NULL) {
35 return NULL;
36 }
37
38 struct nw_protocol_callbacks *callbacks = (struct nw_protocol_callbacks *)malloc(sizeof(struct nw_protocol_callbacks));
39 memset(callbacks, 0, sizeof(struct nw_protocol_callbacks));
40
41 handle->protocol.callbacks = callbacks;
42 handle->protocol.handle = (void *)handle;
43
44 return &handle->protocol;
45 }
46
47 static bool
48 mock_protocol_register_extended(nw_protocol_identifier_const_t identifier,
49 nw_protocol_create_extended_f create_extended_function)
50 {
51 static void *libnetworkImage = NULL;
52 static dispatch_once_t onceToken;
53 static bool (*_nw_protocol_register_extended)(nw_protocol_identifier_const_t, nw_protocol_create_extended_f) = NULL;
54
55 dispatch_once(&onceToken, ^{
56 libnetworkImage = dlopen("/usr/lib/libnetwork.dylib", RTLD_LAZY | RTLD_LOCAL);
57 if (NULL != libnetworkImage) {
58 _nw_protocol_register_extended = (__typeof(_nw_protocol_register_extended))dlsym(libnetworkImage, "nw_protocol_register_extended");
59 if (NULL == _nw_protocol_register_extended) {
60 os_log_error(OS_LOG_DEFAULT, "dlsym libnetwork nw_protocol_register_extended");
61 }
62 } else {
63 os_log_error(OS_LOG_DEFAULT, "dlopen libnetwork");
64 }
65 });
66
67 if (_nw_protocol_register_extended == NULL) {
68 return false;
69 }
70
71 return _nw_protocol_register_extended(identifier, create_extended_function);
72 }
73
74 static nw_protocol_identifier_t
75 _mock_protocol_identifier(const char *name, size_t name_len)
76 {
77 static struct nw_protocol_identifier mock_identifier = {};
78 static dispatch_once_t onceToken;
79
80 dispatch_once(&onceToken, ^{
81 memset(&mock_identifier, 0, sizeof(mock_identifier));
82
83 strlcpy((char *)mock_identifier.name, name, name_len);
84
85 mock_identifier.level = nw_protocol_level_application;
86 mock_identifier.mapping = nw_protocol_mapping_one_to_one;
87
88 mock_protocol_register_extended(&mock_identifier, _mock_protocol_create_extended);
89 });
90
91 return &mock_identifier;
92 }
93
94 static void * _Nullable
95 mock_protocol_allocate_metadata(__unused nw_protocol_definition_t definition)
96 {
97 return calloc(1, sizeof(struct sec_protocol_metadata_content));
98 }
99
100 static void
101 mock_protocol_deallocate_metadata(__unused nw_protocol_definition_t definition, void *metadata)
102 {
103 sec_protocol_metadata_content_t content = (sec_protocol_metadata_content_t)metadata;
104 if (content) {
105 // pass
106 }
107 free(content);
108 }
109
110 static void
111 mock_protocol_set_metadata_allocator(nw_protocol_definition_t definition, nw_protocol_definition_allocate_f allocator, nw_protocol_definition_deallocate_f deallocator)
112 {
113 static void *libnetworkImage = NULL;
114 static dispatch_once_t onceToken;
115 static void (*_nw_protocol_definition_set_metadata_allocator)(nw_protocol_definition_t, nw_protocol_definition_allocate_f, nw_protocol_definition_deallocate_f) = NULL;
116
117 dispatch_once(&onceToken, ^{
118 libnetworkImage = dlopen("/usr/lib/libnetwork.dylib", RTLD_LAZY | RTLD_LOCAL);
119 if (NULL != libnetworkImage) {
120 _nw_protocol_definition_set_metadata_allocator = (__typeof(_nw_protocol_definition_set_metadata_allocator))dlsym(libnetworkImage, "nw_protocol_definition_set_metadata_allocator");
121 if (NULL == _nw_protocol_definition_set_metadata_allocator) {
122 os_log_error(OS_LOG_DEFAULT, "dlsym libnetwork nw_protocol_definition_set_metadata_allocator");
123 }
124 } else {
125 os_log_error(OS_LOG_DEFAULT, "dlopen libnetwork");
126 }
127 });
128
129 if (_nw_protocol_definition_set_metadata_allocator == NULL) {
130 return;
131 }
132
133 _nw_protocol_definition_set_metadata_allocator(definition, allocator, deallocator);
134 }
135
136 static void * _Nullable
137 mock_protocol_copy_options(__unused nw_protocol_definition_t definition, void *options)
138 {
139 void *new_options = calloc(1, sizeof(struct sec_protocol_options_content));
140
141 sec_protocol_options_content_t copy = (sec_protocol_options_content_t)new_options;
142 sec_protocol_options_content_t original = (sec_protocol_options_content_t)options;
143
144 copy->min_version = original->min_version;
145 copy->max_version = original->max_version;
146 copy->disable_sni = original->disable_sni;
147 copy->enable_fallback_attempt = original->enable_fallback_attempt;
148 copy->enable_false_start = original->enable_false_start;
149 copy->enable_tickets = original->enable_tickets;
150 copy->enable_sct = original->enable_sct;
151 copy->enable_ocsp = original->enable_ocsp;
152 copy->enable_resumption = original->enable_resumption;
153 copy->enable_renegotiation = original->enable_renegotiation;
154 copy->enable_early_data = original->enable_early_data;
155
156 if (original->server_name) {
157 copy->server_name = strdup(original->server_name);
158 }
159 if (original->identity) {
160 copy->identity = original->identity;
161 }
162 if (original->application_protocols) {
163 copy->application_protocols = xpc_copy(original->application_protocols);
164 }
165 if (original->ciphersuites) {
166 copy->ciphersuites = xpc_copy(original->ciphersuites);
167 }
168 if (original->dh_params) {
169 copy->dh_params = original->dh_params;
170 }
171 if (original->key_update_block) {
172 copy->key_update_block = original->key_update_block;
173 copy->key_update_queue = original->key_update_queue;
174 }
175 if (original->challenge_block) {
176 copy->challenge_block = original->challenge_block;
177 copy->challenge_queue = original->challenge_queue;
178 }
179 if (original->verify_block) {
180 copy->verify_block = original->verify_block;
181 copy->verify_queue = original->verify_queue;
182 }
183 if (original->session_state) {
184 copy->session_state = original->session_state;
185 }
186 if (original->session_update_block) {
187 copy->session_update_block = original->session_update_block;
188 copy->session_update_queue = original->session_update_queue;
189 }
190 if (original->pre_shared_keys) {
191 copy->pre_shared_keys = xpc_copy(original->pre_shared_keys);
192 }
193
194 return new_options;
195 }
196
197 static void * _Nullable
198 mock_protocol_allocate_options(__unused nw_protocol_definition_t definition)
199 {
200 return calloc(1, sizeof(struct sec_protocol_options_content));
201 }
202
203 static void
204 mock_protocol_deallocate_options(__unused nw_protocol_definition_t definition, void *options)
205 {
206 sec_protocol_options_content_t content = (sec_protocol_options_content_t)options;
207 if (content) {
208 // pass
209 }
210 free(content);
211 }
212
213 static void
214 mock_protocol_set_options_allocator(nw_protocol_definition_t definition,
215 nw_protocol_definition_allocate_f allocate_function,
216 nw_protocol_definition_copy_f copy_function,
217 nw_protocol_definition_deallocate_f deallocate_function)
218 {
219 static void *libnetworkImage = NULL;
220 static dispatch_once_t onceToken;
221 static void (*_nw_protocol_definition_set_options_allocator)(nw_protocol_definition_t, nw_protocol_definition_allocate_f, nw_protocol_definition_copy_f, nw_protocol_definition_deallocate_f) = NULL;
222
223 dispatch_once(&onceToken, ^{
224 libnetworkImage = dlopen("/usr/lib/libnetwork.dylib", RTLD_LAZY | RTLD_LOCAL);
225 if (NULL != libnetworkImage) {
226 _nw_protocol_definition_set_options_allocator = (__typeof(_nw_protocol_definition_set_options_allocator))dlsym(libnetworkImage, "nw_protocol_definition_set_options_allocator");
227 if (NULL == _nw_protocol_definition_set_options_allocator) {
228 os_log_error(OS_LOG_DEFAULT, "dlsym libnetwork nw_protocol_definition_set_options_allocator");
229 }
230 } else {
231 os_log_error(OS_LOG_DEFAULT, "dlopen libnetwork");
232 }
233 });
234
235 if (_nw_protocol_definition_set_options_allocator == NULL) {
236 return;
237 }
238
239 _nw_protocol_definition_set_options_allocator(definition, allocate_function, copy_function, deallocate_function);
240 }
241
242 static nw_protocol_definition_t
243 mock_protocol_definition_create_with_identifier(nw_protocol_identifier_const_t identifier)
244 {
245 static void *libnetworkImage = NULL;
246 static dispatch_once_t onceToken;
247 static nw_protocol_definition_t (*_nw_protocol_definition_create_with_identifier)(nw_protocol_identifier_const_t) = NULL;
248
249 dispatch_once(&onceToken, ^{
250 libnetworkImage = dlopen("/usr/lib/libnetwork.dylib", RTLD_LAZY | RTLD_LOCAL);
251 if (NULL != libnetworkImage) {
252 _nw_protocol_definition_create_with_identifier = (__typeof(_nw_protocol_definition_create_with_identifier))dlsym(libnetworkImage, "nw_protocol_definition_create_with_identifier");
253 if (NULL == _nw_protocol_definition_create_with_identifier) {
254 os_log_error(OS_LOG_DEFAULT, "dlsym libnetwork nw_protocol_definition_create_with_identifier");
255 }
256 } else {
257 os_log_error(OS_LOG_DEFAULT, "dlopen libnetwork");
258 }
259 });
260
261 if (_nw_protocol_definition_create_with_identifier == NULL) {
262 return NULL;
263 }
264
265 return _nw_protocol_definition_create_with_identifier(identifier);
266 }
267
268 static nw_protocol_definition_t
269 mock_protocol_copy_definition(void)
270 {
271 static nw_protocol_definition_t definition = NULL;
272 static dispatch_once_t onceToken;
273 dispatch_once(&onceToken, ^{
274 const char *mock_protocol_name = "SecProtocolConfigTestMock";
275 definition = mock_protocol_definition_create_with_identifier(_mock_protocol_identifier(mock_protocol_name, strlen(mock_protocol_name)));
276 mock_protocol_set_options_allocator(definition,
277 mock_protocol_allocate_options,
278 mock_protocol_copy_options,
279 mock_protocol_deallocate_options);
280 mock_protocol_set_metadata_allocator(definition,
281 mock_protocol_allocate_metadata,
282 mock_protocol_deallocate_metadata);
283
284 });
285
286 return definition;
287 }
288
289 #pragma clang diagnostic push
290 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
291 static SSLProtocol
292 protocol_string_to_version(const char *protocol)
293 {
294 if (protocol == NULL) {
295 return kSSLProtocolUnknown;
296 }
297
298 const char *tlsv10 = "TLSv1.0";
299 const char *tlsv11 = "TLSv1.1";
300 const char *tlsv12 = "TLSv1.2";
301 const char *tlsv13 = "TLSv1.3";
302
303 if (strlen(protocol) == strlen(tlsv10) && strncmp(protocol, tlsv10, strlen(protocol)) == 0) {
304 return kTLSProtocol1;
305 } else if (strlen(protocol) == strlen(tlsv11) && strncmp(protocol, tlsv11, strlen(protocol)) == 0) {
306 return kTLSProtocol11;
307 } else if (strlen(protocol) == strlen(tlsv12) && strncmp(protocol, tlsv12, strlen(protocol)) == 0) {
308 return kTLSProtocol12;
309 } else if (strlen(protocol) == strlen(tlsv13) && strncmp(protocol, tlsv13, strlen(protocol)) == 0) {
310 return kTLSProtocol13;
311 }
312
313 return kSSLProtocolUnknown;
314 }
315 #pragma clang diagnostic pop
316
317 @interface SecProtocolConfigurationTest : XCTestCase
318 @end
319
320 @implementation SecProtocolConfigurationTest
321
322 - (void)setUp {
323 }
324
325 - (void)tearDown {
326 }
327
328 - (sec_protocol_options_t)create_sec_protocol_options {
329 static void *libnetworkImage = NULL;
330 static dispatch_once_t onceToken;
331
332 static sec_protocol_options_t (*_nw_protocol_create_options)(nw_protocol_definition_t) = NULL;
333
334 dispatch_once(&onceToken, ^{
335 libnetworkImage = dlopen("/usr/lib/libnetwork.dylib", RTLD_LAZY | RTLD_LOCAL);
336 if (NULL != libnetworkImage) {
337 _nw_protocol_create_options = (__typeof(_nw_protocol_create_options))dlsym(libnetworkImage, "nw_protocol_create_options");
338 if (NULL == _nw_protocol_create_options) {
339 os_log_error(OS_LOG_DEFAULT, "dlsym libnetwork _nw_protocol_create_options");
340 }
341 } else {
342 os_log_error(OS_LOG_DEFAULT, "dlopen libnetwork");
343 }
344 });
345
346 if (_nw_protocol_create_options == NULL) {
347 return nil;
348 }
349
350 return (sec_protocol_options_t)_nw_protocol_create_options(mock_protocol_copy_definition());
351 }
352
353 - (void)testExampleFile:(NSURL *)path
354 {
355 NSDictionary *dictionary = [[NSDictionary alloc] init];
356 sec_protocol_configuration_builder_t builder = sec_protocol_configuration_builder_create((__bridge CFDictionaryRef)dictionary, true);
357 sec_protocol_configuration_t configuration = sec_protocol_configuration_create_with_builder(builder);
358 XCTAssertTrue(configuration != nil, @"failed to build configuration");
359 if (!configuration) {
360 return;
361 }
362
363 NSData *exampleData = [[NSData alloc] initWithContentsOfURL:path];
364 NSDictionary *exampleATS = [NSJSONSerialization JSONObjectWithData:exampleData options:kNilOptions error:nil];
365 XCTAssertNotNil(exampleATS, @"Loading %@ failed", path);
366 if (!exampleATS) {
367 return;
368 }
369
370 [exampleATS enumerateKeysAndObjectsUsingBlock:^(id _key, id _obj, BOOL *stop) {
371 NSString *key = (NSString *)_key;
372 if ([key isEqualToString:@"NSExceptionDomains"]) {
373 NSDictionary *domain_map = (NSDictionary *)_obj;
374 [domain_map enumerateKeysAndObjectsUsingBlock:^(id _domain, id _domain_entry, BOOL *_domain_stop) {
375 NSString *domain = (NSString *)_domain;
376 NSDictionary *entry = (NSDictionary *)_domain_entry;
377
378 #define BOOLEAN_FOR_KEY(key, value, default) \
379 bool value = default; \
380 { \
381 NSNumber *nsValue = [entry valueForKey:key]; \
382 if (nsValue) { \
383 value = [nsValue boolValue]; \
384 } \
385 }
386 #define STRING_FOR_KEY(key, value, default) \
387 NSString *value = default; \
388 { \
389 NSString *nsValue = [entry valueForKey:key]; \
390 if (nsValue) { \
391 value = nsValue; \
392 } \
393 }
394 BOOLEAN_FOR_KEY(@"NSExceptionAllowsInsecureHTTPLoads", allows_http, false);
395 BOOLEAN_FOR_KEY(@"NSIncludesSubdomains", includes_subdomains, false);
396 BOOLEAN_FOR_KEY(@"NSExceptionRequiresForwardSecrecy", requires_pfs, false);
397 STRING_FOR_KEY(@"NSExceptionMinimumTLSVersion", minimum_tls, @"TLSv1.2");
398 #undef STRING_FOR_KEY
399 #undef BOOLEAN_FOR_KEY
400
401 SSLProtocol minimum_protocol_version = protocol_string_to_version([minimum_tls cStringUsingEncoding:NSUTF8StringEncoding]);
402
403 sec_protocol_options_t options = [self create_sec_protocol_options];
404 sec_protocol_options_t transformed = sec_protocol_configuration_copy_transformed_options_for_host(configuration, options, [domain cStringUsingEncoding:NSUTF8StringEncoding]);
405 sec_protocol_options_access_handle(transformed, ^bool(void *handle) {
406 sec_protocol_options_content_t content = (sec_protocol_options_content_t)handle;
407 SEC_PROTOCOL_METADATA_VALIDATE(content, false);
408
409 XCTAssertTrue(content->ats_required == true);
410 XCTAssertTrue(content->min_version == minimum_protocol_version);
411 if (requires_pfs) {
412 XCTAssertTrue(content->ciphersuites != nil);
413 } else {
414 XCTAssertTrue(content->ciphersuites == nil);
415 }
416 });
417
418 XCTAssertTrue(allows_http != sec_protocol_configuration_tls_required_for_host(configuration, [domain cStringUsingEncoding:NSUTF8StringEncoding]));
419 }];
420 }
421 }];
422 }
423
424 - (void)testExampleATSDictionaries {
425 NSArray <NSURL *>* testFiles = [[NSBundle bundleForClass:[self class]]URLsForResourcesWithExtension:@".json" subdirectory:@"."];
426 [testFiles enumerateObjectsUsingBlock:^(NSURL* _Nonnull path, __unused NSUInteger idx, BOOL * _Nonnull stop) {
427 [self testExampleFile:path];
428 }];
429 }
430
431 @end