2 //  SecProtocolConfigurationTest.m
 
   3 //  SecureTransportTests
 
   6 #import <XCTest/XCTest.h>
 
  10 #include <sys/param.h>
 
  12 #import "SecProtocolConfiguration.h"
 
  13 #import "SecProtocolPriv.h"
 
  14 #import "SecProtocolInternal.h"
 
  16 #import <nw/private.h> // Needed for the mock protocol
 
  18 #define SEC_PROTOCOL_METADATA_VALIDATE(m, r) \
 
  19     if (((void *)(m) == NULL) || ((size_t)(m) == 0)) { \
 
  23 typedef struct mock_protocol {
 
  24     struct nw_protocol protocol;
 
  29 _mock_protocol_create_extended(nw_protocol_identifier_const_t identifier,
 
  30                                nw_endpoint_t endpoint,
 
  31                                nw_parameters_t parameters)
 
  33     mock_protocol_t handle = (mock_protocol_t)calloc(1, sizeof(struct mock_protocol));
 
  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));
 
  41     handle->protocol.callbacks = callbacks;
 
  42     handle->protocol.handle = (void *)handle;
 
  44     return &handle->protocol;
 
  48 mock_protocol_register_extended(nw_protocol_identifier_const_t identifier,
 
  49                                 nw_protocol_create_extended_f create_extended_function)
 
  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;
 
  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");
 
  63             os_log_error(OS_LOG_DEFAULT, "dlopen libnetwork");
 
  67     if (_nw_protocol_register_extended == NULL) {
 
  71     return _nw_protocol_register_extended(identifier, create_extended_function);
 
  74 static nw_protocol_identifier_t
 
  75 _mock_protocol_identifier(const char *name, size_t name_len)
 
  77     static struct nw_protocol_identifier mock_identifier = {};
 
  78     static dispatch_once_t onceToken;
 
  80     dispatch_once(&onceToken, ^{
 
  81         memset(&mock_identifier, 0, sizeof(mock_identifier));
 
  83         strlcpy((char *)mock_identifier.name, name, name_len);
 
  85         mock_identifier.level = nw_protocol_level_application;
 
  86         mock_identifier.mapping = nw_protocol_mapping_one_to_one;
 
  88         mock_protocol_register_extended(&mock_identifier, _mock_protocol_create_extended);
 
  91     return &mock_identifier;
 
  94 static void * _Nullable
 
  95 mock_protocol_allocate_metadata(__unused nw_protocol_definition_t definition)
 
  97     return calloc(1, sizeof(struct sec_protocol_metadata_content));
 
 101 mock_protocol_deallocate_metadata(__unused nw_protocol_definition_t definition, void *metadata)
 
 103     sec_protocol_metadata_content_t content = (sec_protocol_metadata_content_t)metadata;
 
 111 mock_protocol_set_metadata_allocator(nw_protocol_definition_t definition, nw_protocol_definition_allocate_f allocator, nw_protocol_definition_deallocate_f deallocator)
 
 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;
 
 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");
 
 125             os_log_error(OS_LOG_DEFAULT, "dlopen libnetwork");
 
 129     if (_nw_protocol_definition_set_metadata_allocator == NULL) {
 
 133     _nw_protocol_definition_set_metadata_allocator(definition, allocator, deallocator);
 
 136 static void * _Nullable
 
 137 mock_protocol_copy_options(__unused nw_protocol_definition_t definition, void *options)
 
 139     void *new_options = calloc(1, sizeof(struct sec_protocol_options_content));
 
 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;
 
 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;
 
 156     if (original->server_name) {
 
 157         copy->server_name = strdup(original->server_name);
 
 159     if (original->identity) {
 
 160         copy->identity = original->identity;
 
 162     if (original->application_protocols) {
 
 163         copy->application_protocols = xpc_copy(original->application_protocols);
 
 165     if (original->ciphersuites) {
 
 166         copy->ciphersuites = xpc_copy(original->ciphersuites);
 
 168     if (original->dh_params) {
 
 169         copy->dh_params = original->dh_params;
 
 171     if (original->key_update_block) {
 
 172         copy->key_update_block = original->key_update_block;
 
 173         copy->key_update_queue = original->key_update_queue;
 
 175     if (original->challenge_block) {
 
 176         copy->challenge_block = original->challenge_block;
 
 177         copy->challenge_queue = original->challenge_queue;
 
 179     if (original->verify_block) {
 
 180         copy->verify_block = original->verify_block;
 
 181         copy->verify_queue = original->verify_queue;
 
 183     if (original->session_state) {
 
 184         copy->session_state = original->session_state;
 
 186     if (original->session_update_block) {
 
 187         copy->session_update_block = original->session_update_block;
 
 188         copy->session_update_queue = original->session_update_queue;
 
 190     if (original->pre_shared_keys) {
 
 191         copy->pre_shared_keys = xpc_copy(original->pre_shared_keys);
 
 197 static void * _Nullable
 
 198 mock_protocol_allocate_options(__unused nw_protocol_definition_t definition)
 
 200     return calloc(1, sizeof(struct sec_protocol_options_content));
 
 204 mock_protocol_deallocate_options(__unused nw_protocol_definition_t definition, void *options)
 
 206     sec_protocol_options_content_t content = (sec_protocol_options_content_t)options;
 
 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)
 
 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;
 
 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");
 
 231             os_log_error(OS_LOG_DEFAULT, "dlopen libnetwork");
 
 235     if (_nw_protocol_definition_set_options_allocator == NULL) {
 
 239     _nw_protocol_definition_set_options_allocator(definition, allocate_function, copy_function, deallocate_function);
 
 242 static nw_protocol_definition_t
 
 243 mock_protocol_definition_create_with_identifier(nw_protocol_identifier_const_t identifier)
 
 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;
 
 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");
 
 257             os_log_error(OS_LOG_DEFAULT, "dlopen libnetwork");
 
 261     if (_nw_protocol_definition_create_with_identifier == NULL) {
 
 265     return _nw_protocol_definition_create_with_identifier(identifier);
 
 268 static nw_protocol_definition_t
 
 269 mock_protocol_copy_definition(void)
 
 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);
 
 289 #pragma clang diagnostic push
 
 290 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
 
 292 protocol_string_to_version(const char *protocol)
 
 294     if (protocol == NULL) {
 
 295         return kSSLProtocolUnknown;
 
 298     const char *tlsv10 = "TLSv1.0";
 
 299     const char *tlsv11 = "TLSv1.1";
 
 300     const char *tlsv12 = "TLSv1.2";
 
 301     const char *tlsv13 = "TLSv1.3";
 
 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;
 
 313     return kSSLProtocolUnknown;
 
 315 #pragma clang diagnostic pop
 
 317 @interface SecProtocolConfigurationTest : XCTestCase
 
 320 @implementation SecProtocolConfigurationTest
 
 328 - (sec_protocol_options_t)create_sec_protocol_options {
 
 329     static void *libnetworkImage = NULL;
 
 330     static dispatch_once_t onceToken;
 
 332     static sec_protocol_options_t (*_nw_protocol_create_options)(nw_protocol_definition_t) = NULL;
 
 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");
 
 342             os_log_error(OS_LOG_DEFAULT, "dlopen libnetwork");
 
 346     if (_nw_protocol_create_options == NULL) {
 
 350     return (sec_protocol_options_t)_nw_protocol_create_options(mock_protocol_copy_definition());
 
 353 - (void)testExampleFile:(NSURL *)path
 
 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) {
 
 363     NSData *exampleData = [[NSData alloc] initWithContentsOfURL:path];
 
 364     NSDictionary *exampleATS = [NSJSONSerialization JSONObjectWithData:exampleData options:kNilOptions error:nil];
 
 365     XCTAssertNotNil(exampleATS, @"Loading %@ failed", path);
 
 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;
 
 378 #define BOOLEAN_FOR_KEY(key, value, default) \
 
 379     bool value = default; \
 
 381         NSNumber *nsValue = [entry valueForKey:key]; \
 
 383             value = [nsValue boolValue]; \
 
 386 #define STRING_FOR_KEY(key, value, default) \
 
 387     NSString *value = default; \
 
 389         NSString *nsValue = [entry valueForKey:key]; \
 
 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
 
 401                 SSLProtocol minimum_protocol_version = protocol_string_to_version([minimum_tls cStringUsingEncoding:NSUTF8StringEncoding]);
 
 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);
 
 409                     XCTAssertTrue(content->ats_required == true);
 
 410                     XCTAssertTrue(content->min_version == minimum_protocol_version);
 
 412                         XCTAssertTrue(content->ciphersuites != nil);
 
 414                         XCTAssertTrue(content->ciphersuites == nil);
 
 418                 XCTAssertTrue(allows_http != sec_protocol_configuration_tls_required_for_host(configuration, [domain cStringUsingEncoding:NSUTF8StringEncoding]));
 
 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];