#import "apple_client_platform_darwin.h" #import #import #import #import #import #import #import #import #import #import #import #import #import typedef nw_connection_t _Nullable (*box_nw_connection_create_with_connected_socket_and_parameters_f)(int connected_socket, nw_parameters_t parameters); typedef const char * _Nullable (*box_sec_protocol_metadata_string_accessor_f)(sec_protocol_metadata_t metadata); typedef struct box_apple_tls_client { void *connection; void *queue; void *ready_semaphore; void *anchors; atomic_int ref_count; atomic_bool ready; atomic_bool ready_done; char *ready_error; box_apple_tls_state_t state; } box_apple_tls_client_t; struct box_apple_tls_read_result { void *content; bool eof; char *error; }; extern void box_apple_tls_read_callback(uintptr_t callback_handle, box_apple_tls_read_result_t *result); static nw_connection_t box_apple_tls_connection(box_apple_tls_client_t *client) { if (client == NULL || client->connection == NULL) { return nil; } return (__bridge nw_connection_t)client->connection; } static dispatch_queue_t box_apple_tls_client_queue(box_apple_tls_client_t *client) { if (client == NULL || client->queue == NULL) { return nil; } return (__bridge dispatch_queue_t)client->queue; } static dispatch_semaphore_t box_apple_tls_ready_semaphore(box_apple_tls_client_t *client) { if (client == NULL || client->ready_semaphore == NULL) { return nil; } return (__bridge dispatch_semaphore_t)client->ready_semaphore; } static NSArray *box_apple_tls_client_anchors(box_apple_tls_client_t *client) { if (client == NULL || client->anchors == NULL) { return nil; } return (__bridge NSArray *)client->anchors; } static dispatch_data_t box_apple_tls_read_result_content(box_apple_tls_read_result_t *result) { if (result == NULL || result->content == NULL) { return nil; } return (__bridge dispatch_data_t)result->content; } static void box_apple_tls_state_reset(box_apple_tls_state_t *state) { if (state == NULL) { return; } free(state->alpn); free(state->server_name); free(state->peer_cert_chain); memset(state, 0, sizeof(box_apple_tls_state_t)); } static void box_apple_tls_client_destroy(box_apple_tls_client_t *client) { free(client->ready_error); box_apple_tls_state_reset(&client->state); if (client->anchors != NULL) { CFRelease((CFTypeRef)client->anchors); } if (client->ready_semaphore != NULL) { CFBridgingRelease(client->ready_semaphore); } if (client->connection != NULL) { CFBridgingRelease(client->connection); } if (client->queue != NULL) { CFBridgingRelease(client->queue); } free(client); } static void box_apple_tls_client_release(box_apple_tls_client_t *client) { if (client == NULL) { return; } if (atomic_fetch_sub(&client->ref_count, 1) == 1) { box_apple_tls_client_destroy(client); } } static void box_set_error_string(char **error_out, NSString *message) { if (error_out == NULL || *error_out != NULL) { return; } const char *utf8 = [message UTF8String]; *error_out = strdup(utf8 != NULL ? utf8 : "unknown error"); } static void box_set_error_message(char **error_out, const char *message) { if (error_out == NULL || *error_out != NULL) { return; } *error_out = strdup(message != NULL ? message : "unknown error"); } static void box_set_error_from_nw_error(char **error_out, nw_error_t error) { if (error == NULL) { box_set_error_message(error_out, "unknown network error"); return; } CFErrorRef cfError = nw_error_copy_cf_error(error); if (cfError == NULL) { box_set_error_message(error_out, "unknown network error"); return; } NSString *description = [(__bridge NSError *)cfError description]; box_set_error_string(error_out, description); CFRelease(cfError); } static ssize_t box_apple_tls_dispatch_data_copy(dispatch_data_t content, void *buffer, size_t buffer_len, char **error_out) { if (content == nil) { return 0; } size_t content_size = dispatch_data_get_size(content); if (content_size == 0) { return 0; } if (buffer == NULL) { box_set_error_message(error_out, "apple TLS: read buffer unavailable"); return -1; } __block size_t copied = 0; __block bool overflow = false; bool complete = dispatch_data_apply(content, ^bool(dispatch_data_t region, size_t offset, const void *region_buffer, size_t region_size) { (void)region; (void)offset; if (region_size == 0) { return true; } if (region_buffer == NULL || region_size > buffer_len - copied) { overflow = true; return false; } memcpy((uint8_t *)buffer + copied, region_buffer, region_size); copied += region_size; return true; }); if (!complete || overflow) { box_set_error_message(error_out, "apple TLS: read buffer too small"); return -1; } return (ssize_t)copied; } ssize_t box_apple_tls_copy_dispatch_data_for_test(const void *first, size_t first_len, const void *second, size_t second_len, void *buffer, size_t buffer_len, char **error_out) { @autoreleasepool { dispatch_queue_t queue = dispatch_get_global_queue(QOS_CLASS_DEFAULT, 0); dispatch_data_t first_data = first_len > 0 ? dispatch_data_create(first, first_len, queue, DISPATCH_DATA_DESTRUCTOR_DEFAULT) : dispatch_data_empty; dispatch_data_t second_data = second_len > 0 ? dispatch_data_create(second, second_len, queue, DISPATCH_DATA_DESTRUCTOR_DEFAULT) : dispatch_data_empty; dispatch_data_t content = dispatch_data_create_concat(first_data, second_data); return box_apple_tls_dispatch_data_copy(content, buffer, buffer_len, error_out); } } static char *box_apple_tls_metadata_copy_negotiated_protocol(sec_protocol_metadata_t metadata) { static box_sec_protocol_metadata_string_accessor_f copy_fn; static box_sec_protocol_metadata_string_accessor_f get_fn; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ copy_fn = (box_sec_protocol_metadata_string_accessor_f)dlsym(RTLD_DEFAULT, "sec_protocol_metadata_copy_negotiated_protocol"); get_fn = (box_sec_protocol_metadata_string_accessor_f)dlsym(RTLD_DEFAULT, "sec_protocol_metadata_get_negotiated_protocol"); }); if (copy_fn != NULL) { return (char *)copy_fn(metadata); } if (get_fn != NULL) { const char *protocol = get_fn(metadata); if (protocol != NULL) { return strdup(protocol); } } return NULL; } static char *box_apple_tls_metadata_copy_server_name(sec_protocol_metadata_t metadata) { static box_sec_protocol_metadata_string_accessor_f copy_fn; static box_sec_protocol_metadata_string_accessor_f get_fn; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ copy_fn = (box_sec_protocol_metadata_string_accessor_f)dlsym(RTLD_DEFAULT, "sec_protocol_metadata_copy_server_name"); get_fn = (box_sec_protocol_metadata_string_accessor_f)dlsym(RTLD_DEFAULT, "sec_protocol_metadata_get_server_name"); }); if (copy_fn != NULL) { return (char *)copy_fn(metadata); } if (get_fn != NULL) { const char *server_name = get_fn(metadata); if (server_name != NULL) { return strdup(server_name); } } return NULL; } static NSArray *box_split_lines(const char *content, size_t content_len) { if (content == NULL || content_len == 0) { return @[]; } NSString *string = [[NSString alloc] initWithBytes:content length:content_len encoding:NSUTF8StringEncoding]; if (string == nil) { return @[]; } NSMutableArray *lines = [NSMutableArray array]; [string enumerateLinesUsingBlock:^(NSString *line, BOOL *stop) { if (line.length > 0) { [lines addObject:line]; } }]; return lines; } static bool box_evaluate_trust(sec_trust_t trust, NSArray *anchors, bool anchor_only, NSDate *verify_date) { bool result = false; SecTrustRef trustRef = sec_trust_copy_ref(trust); if (trustRef == NULL) { return false; } if (verify_date != nil && SecTrustSetVerifyDate(trustRef, (__bridge CFDateRef)verify_date) != errSecSuccess) { CFRelease(trustRef); return false; } if (anchors.count > 0 || anchor_only) { CFMutableArrayRef anchorArray = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); for (id certificate in anchors) { CFArrayAppendValue(anchorArray, (__bridge const void *)certificate); } SecTrustSetAnchorCertificates(trustRef, anchorArray); SecTrustSetAnchorCertificatesOnly(trustRef, anchor_only); CFRelease(anchorArray); } CFErrorRef error = NULL; result = SecTrustEvaluateWithError(trustRef, &error); if (error != NULL) { CFRelease(error); } CFRelease(trustRef); return result; } static nw_connection_t box_apple_tls_create_connection(int connected_socket, nw_parameters_t parameters) { static box_nw_connection_create_with_connected_socket_and_parameters_f create_fn; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ char name[] = "sretemarap_dna_tekcos_detcennoc_htiw_etaerc_noitcennoc_wn"; for (size_t i = 0, j = sizeof(name) - 2; i < j; i++, j--) { char t = name[i]; name[i] = name[j]; name[j] = t; } create_fn = (box_nw_connection_create_with_connected_socket_and_parameters_f)dlsym(RTLD_DEFAULT, name); }); if (create_fn == NULL) { return nil; } return create_fn(connected_socket, parameters); } static bool box_apple_tls_state_copy(const box_apple_tls_state_t *source, box_apple_tls_state_t *destination) { memset(destination, 0, sizeof(box_apple_tls_state_t)); destination->version = source->version; destination->cipher_suite = source->cipher_suite; if (source->alpn != NULL) { destination->alpn = strdup(source->alpn); if (destination->alpn == NULL) { goto oom; } } if (source->server_name != NULL) { destination->server_name = strdup(source->server_name); if (destination->server_name == NULL) { goto oom; } } if (source->peer_cert_chain_len > 0) { destination->peer_cert_chain = malloc(source->peer_cert_chain_len); if (destination->peer_cert_chain == NULL) { goto oom; } memcpy(destination->peer_cert_chain, source->peer_cert_chain, source->peer_cert_chain_len); destination->peer_cert_chain_len = source->peer_cert_chain_len; } return true; oom: box_apple_tls_state_reset(destination); return false; } // Captures TLS negotiation results from the verify block. The sec_metadata // exposed here is live for the duration of the handshake; the one retrieved // after nw_connection_state_ready may return stale ALPN/server_name buffers. static void box_apple_tls_state_load(sec_protocol_metadata_t sec_metadata, box_apple_tls_state_t *state) { state->version = (uint16_t)sec_protocol_metadata_get_negotiated_tls_protocol_version(sec_metadata); state->cipher_suite = (uint16_t)sec_protocol_metadata_get_negotiated_tls_ciphersuite(sec_metadata); state->alpn = box_apple_tls_metadata_copy_negotiated_protocol(sec_metadata); state->server_name = box_apple_tls_metadata_copy_server_name(sec_metadata); NSMutableData *chain_data = [NSMutableData data]; sec_protocol_metadata_access_peer_certificate_chain(sec_metadata, ^(sec_certificate_t certificate) { SecCertificateRef certificate_ref = sec_certificate_copy_ref(certificate); if (certificate_ref == NULL) { return; } CFDataRef certificate_data = SecCertificateCopyData(certificate_ref); CFRelease(certificate_ref); if (certificate_data == NULL) { return; } uint32_t certificate_len = (uint32_t)CFDataGetLength(certificate_data); uint32_t network_len = htonl(certificate_len); [chain_data appendBytes:&network_len length:sizeof(network_len)]; [chain_data appendBytes:CFDataGetBytePtr(certificate_data) length:certificate_len]; CFRelease(certificate_data); }); if (chain_data.length > 0) { state->peer_cert_chain = malloc(chain_data.length); if (state->peer_cert_chain != NULL) { memcpy(state->peer_cert_chain, chain_data.bytes, chain_data.length); state->peer_cert_chain_len = chain_data.length; } } } box_apple_tls_client_t *box_apple_tls_client_create( int connected_socket, const char *server_name, const char *alpn, size_t alpn_len, uint16_t min_version, uint16_t max_version, bool insecure, void *anchors_cf, bool anchor_only, bool has_verify_time, int64_t verify_time_unix_millis, char **error_out ) { box_apple_tls_client_t *client = calloc(1, sizeof(box_apple_tls_client_t)); if (client == NULL) { close(connected_socket); box_set_error_message(error_out, "apple TLS: out of memory"); return NULL; } client->queue = (__bridge_retained void *)dispatch_queue_create("sing-box.apple-private-tls", DISPATCH_QUEUE_SERIAL); client->ready_semaphore = (__bridge_retained void *)dispatch_semaphore_create(0); atomic_init(&client->ref_count, 1); atomic_init(&client->ready, false); atomic_init(&client->ready_done, false); if (anchors_cf != NULL) { client->anchors = (void *)CFRetain(anchors_cf); } NSArray *alpnList = box_split_lines(alpn, alpn_len); NSDate *verifyDate = nil; if (has_verify_time) { verifyDate = [NSDate dateWithTimeIntervalSince1970:(NSTimeInterval)verify_time_unix_millis / 1000.0]; } nw_parameters_t parameters = nw_parameters_create_secure_tcp(^(nw_protocol_options_t tls_options) { sec_protocol_options_t sec_options = nw_tls_copy_sec_protocol_options(tls_options); if (min_version != 0) { sec_protocol_options_set_min_tls_protocol_version(sec_options, (tls_protocol_version_t)min_version); } if (max_version != 0) { sec_protocol_options_set_max_tls_protocol_version(sec_options, (tls_protocol_version_t)max_version); } if (server_name != NULL && server_name[0] != '\0') { sec_protocol_options_set_tls_server_name(sec_options, server_name); } for (NSString *protocol in alpnList) { sec_protocol_options_add_tls_application_protocol(sec_options, protocol.UTF8String); } sec_protocol_options_set_peer_authentication_required(sec_options, !insecure); sec_protocol_options_set_verify_block(sec_options, ^(sec_protocol_metadata_t metadata, sec_trust_t trust, sec_protocol_verify_complete_t complete) { if (client->state.version == 0) { box_apple_tls_state_load(metadata, &client->state); } complete(insecure || box_evaluate_trust(trust, box_apple_tls_client_anchors(client), anchor_only, verifyDate)); }, box_apple_tls_client_queue(client)); }, NW_PARAMETERS_DEFAULT_CONFIGURATION); nw_connection_t connection = box_apple_tls_create_connection(connected_socket, parameters); if (connection == NULL) { close(connected_socket); if (client->anchors != NULL) { CFRelease((CFTypeRef)client->anchors); } if (client->ready_semaphore != NULL) { CFBridgingRelease(client->ready_semaphore); } if (client->queue != NULL) { CFBridgingRelease(client->queue); } free(client); box_set_error_message(error_out, "apple TLS: failed to create connection"); return NULL; } client->connection = (__bridge_retained void *)connection; atomic_fetch_add(&client->ref_count, 1); nw_connection_set_state_changed_handler(connection, ^(nw_connection_state_t state, nw_error_t error) { switch (state) { case nw_connection_state_ready: if (!atomic_load(&client->ready_done)) { if (client->state.version == 0) { box_set_error_message(&client->ready_error, "apple TLS: metadata unavailable"); } else { atomic_store(&client->ready, true); } atomic_store(&client->ready_done, true); dispatch_semaphore_signal(box_apple_tls_ready_semaphore(client)); } break; case nw_connection_state_failed: if (!atomic_load(&client->ready_done)) { box_set_error_from_nw_error(&client->ready_error, error); atomic_store(&client->ready_done, true); dispatch_semaphore_signal(box_apple_tls_ready_semaphore(client)); } break; case nw_connection_state_cancelled: if (!atomic_load(&client->ready_done)) { box_set_error_from_nw_error(&client->ready_error, error); atomic_store(&client->ready_done, true); dispatch_semaphore_signal(box_apple_tls_ready_semaphore(client)); } box_apple_tls_client_release(client); break; default: break; } }); nw_connection_set_queue(connection, box_apple_tls_client_queue(client)); nw_connection_start(connection); return client; } int box_apple_tls_client_wait_ready(box_apple_tls_client_t *client, int timeout_msec, char **error_out) { dispatch_semaphore_t ready_semaphore = box_apple_tls_ready_semaphore(client); if (ready_semaphore == nil) { box_set_error_message(error_out, "apple TLS: invalid client"); return 0; } if (!atomic_load(&client->ready_done)) { dispatch_time_t timeout = DISPATCH_TIME_FOREVER; if (timeout_msec >= 0) { timeout = dispatch_time(DISPATCH_TIME_NOW, (int64_t)timeout_msec * NSEC_PER_MSEC); } long wait_result = dispatch_semaphore_wait(ready_semaphore, timeout); if (wait_result != 0) { return -2; } } if (atomic_load(&client->ready)) { return 1; } if (client->ready_error != NULL) { if (error_out != NULL) { *error_out = client->ready_error; client->ready_error = NULL; } else { free(client->ready_error); client->ready_error = NULL; } } else { box_set_error_message(error_out, "apple TLS: handshake failed"); } return 0; } void box_apple_tls_client_cancel(box_apple_tls_client_t *client) { if (client == NULL) { return; } nw_connection_t connection = box_apple_tls_connection(client); if (connection != nil) { nw_connection_cancel(connection); } } void box_apple_tls_client_free(box_apple_tls_client_t *client) { if (client == NULL) { return; } nw_connection_t connection = box_apple_tls_connection(client); if (connection != nil) { nw_connection_cancel(connection); } box_apple_tls_client_release(client); } ssize_t box_apple_tls_client_read(box_apple_tls_client_t *client, void *buffer, size_t buffer_len, int timeout_msec, bool *eof_out, char **error_out) { @autoreleasepool { nw_connection_t connection = box_apple_tls_connection(client); if (connection == nil) { box_set_error_message(error_out, "apple TLS: invalid client"); return -1; } dispatch_semaphore_t read_semaphore = dispatch_semaphore_create(0); __block size_t content_len = 0; __block bool read_eof = false; __block char *local_error = NULL; nw_connection_receive(connection, 1, (uint32_t)buffer_len, ^(dispatch_data_t content, nw_content_context_t context, bool is_complete, nw_error_t error) { @autoreleasepool { if (content != NULL) { ssize_t copied = box_apple_tls_dispatch_data_copy(content, buffer, buffer_len, &local_error); if (copied >= 0) { content_len = (size_t)copied; } } if (error != NULL && content_len == 0 && local_error == NULL) { box_set_error_from_nw_error(&local_error, error); } if (is_complete && (context == NULL || nw_content_context_get_is_final(context))) { read_eof = true; } dispatch_semaphore_signal(read_semaphore); } }); dispatch_time_t wait_deadline = DISPATCH_TIME_FOREVER; if (timeout_msec >= 0) { wait_deadline = dispatch_time(DISPATCH_TIME_NOW, (int64_t)timeout_msec * NSEC_PER_MSEC); } long wait_result = dispatch_semaphore_wait(read_semaphore, wait_deadline); if (wait_result != 0) { nw_connection_cancel(connection); dispatch_semaphore_wait(read_semaphore, DISPATCH_TIME_FOREVER); if (local_error != NULL) { free(local_error); local_error = NULL; } return -2; } if (local_error != NULL) { if (error_out != NULL) { *error_out = local_error; } else { free(local_error); } return -1; } if (eof_out != NULL) { *eof_out = read_eof; } return (ssize_t)content_len; } } ssize_t box_apple_tls_client_write(box_apple_tls_client_t *client, const void *buffer, size_t buffer_len, int timeout_msec, char **error_out) { @autoreleasepool { nw_connection_t connection = box_apple_tls_connection(client); if (connection == nil) { box_set_error_message(error_out, "apple TLS: invalid client"); return -1; } if (buffer_len == 0) { return 0; } void *content_copy = malloc(buffer_len); if (content_copy == NULL) { box_set_error_message(error_out, "apple TLS: out of memory"); return -1; } dispatch_queue_t queue = box_apple_tls_client_queue(client); if (queue == nil) { free(content_copy); box_set_error_message(error_out, "apple TLS: invalid client"); return -1; } memcpy(content_copy, buffer, buffer_len); dispatch_data_t content = dispatch_data_create(content_copy, buffer_len, queue, ^{ free(content_copy); }); dispatch_semaphore_t write_semaphore = dispatch_semaphore_create(0); __block char *local_error = NULL; nw_connection_send(connection, content, NW_CONNECTION_DEFAULT_STREAM_CONTEXT, false, ^(nw_error_t error) { @autoreleasepool { if (error != NULL) { box_set_error_from_nw_error(&local_error, error); } dispatch_semaphore_signal(write_semaphore); } }); dispatch_time_t wait_deadline = DISPATCH_TIME_FOREVER; if (timeout_msec >= 0) { wait_deadline = dispatch_time(DISPATCH_TIME_NOW, (int64_t)timeout_msec * NSEC_PER_MSEC); } long wait_result = dispatch_semaphore_wait(write_semaphore, wait_deadline); if (wait_result != 0) { nw_connection_cancel(connection); dispatch_semaphore_wait(write_semaphore, DISPATCH_TIME_FOREVER); if (local_error != NULL) { free(local_error); local_error = NULL; } return -2; } if (local_error != NULL) { if (error_out != NULL) { *error_out = local_error; } else { free(local_error); } return -1; } return (ssize_t)buffer_len; } } bool box_apple_tls_client_read_async(box_apple_tls_client_t *client, size_t maximum_len, uintptr_t callback_handle, char **error_out) { @autoreleasepool { nw_connection_t connection = box_apple_tls_connection(client); if (connection == nil) { box_set_error_message(error_out, "apple TLS: invalid client"); return false; } if (maximum_len == 0) { box_set_error_message(error_out, "apple TLS: empty read buffer"); return false; } uint32_t receive_len = maximum_len > UINT32_MAX ? UINT32_MAX : (uint32_t)maximum_len; nw_connection_receive(connection, 1, receive_len, ^(dispatch_data_t content, nw_content_context_t context, bool is_complete, nw_error_t error) { @autoreleasepool { box_apple_tls_read_result_t *result = calloc(1, sizeof(box_apple_tls_read_result_t)); if (result == NULL) { box_apple_tls_read_callback(callback_handle, NULL); return; } size_t content_size = content != NULL ? dispatch_data_get_size(content) : 0; if (content_size > 0) { result->content = (__bridge_retained void *)content; } if (error != NULL && content_size == 0) { box_set_error_from_nw_error(&result->error, error); } if (is_complete && (context == NULL || nw_content_context_get_is_final(context))) { result->eof = true; } box_apple_tls_read_callback(callback_handle, result); } }); return true; } } ssize_t box_apple_tls_read_result_copy(box_apple_tls_read_result_t *result, void *buffer, size_t buffer_len, bool *eof_out, char **error_out) { @autoreleasepool { if (result == NULL) { box_set_error_message(error_out, "apple TLS: read result unavailable"); return -1; } if (result->error != NULL) { if (error_out != NULL) { *error_out = result->error; result->error = NULL; } else { free(result->error); result->error = NULL; } return -1; } if (eof_out != NULL) { *eof_out = result->eof; } dispatch_data_t content = box_apple_tls_read_result_content(result); if (content == nil) { return 0; } return box_apple_tls_dispatch_data_copy(content, buffer, buffer_len, error_out); } } void box_apple_tls_read_result_free(box_apple_tls_read_result_t *result) { if (result == NULL) { return; } free(result->error); if (result->content != NULL) { CFBridgingRelease(result->content); } free(result); } bool box_apple_tls_client_copy_state(box_apple_tls_client_t *client, box_apple_tls_state_t *state, char **error_out) { dispatch_queue_t queue = box_apple_tls_client_queue(client); if (queue == nil || state == NULL) { box_set_error_message(error_out, "apple TLS: invalid client"); return false; } memset(state, 0, sizeof(box_apple_tls_state_t)); __block bool copied = false; __block char *local_error = NULL; dispatch_sync(queue, ^{ if (!atomic_load(&client->ready)) { box_set_error_message(&local_error, "apple TLS: metadata unavailable"); return; } if (!box_apple_tls_state_copy(&client->state, state)) { box_set_error_message(&local_error, "apple TLS: out of memory"); return; } copied = true; }); if (copied) { return true; } if (local_error != NULL) { if (error_out != NULL) { *error_out = local_error; } else { free(local_error); } } box_apple_tls_state_reset(state); return false; } void box_apple_tls_state_free(box_apple_tls_state_t *state) { box_apple_tls_state_reset(state); }