in source/darwin/secure_transport_tls_channel_handler.c [321:488]
static int s_drive_negotiation(struct aws_channel_handler *handler) {
struct secure_transport_handler *secure_transport_handler = handler->impl;
aws_on_drive_tls_negotiation(&secure_transport_handler->shared_state);
OSStatus status = SSLHandshake(secure_transport_handler->ctx);
/* yay!!!! negotiation finished successfully. */
if (status == noErr) {
AWS_LOGF_DEBUG(AWS_LS_IO_TLS, "id=%p: negotiation succeeded", (void *)handler);
secure_transport_handler->negotiation_finished = true;
CFStringRef protocol = s_get_protocol(secure_transport_handler);
if (protocol) {
if (aws_byte_buf_init(
&secure_transport_handler->protocol, handler->alloc, (size_t)CFStringGetLength(protocol) + 1)) {
CFRelease(protocol);
s_invoke_negotiation_callback(handler, AWS_IO_TLS_ERROR_NEGOTIATION_FAILURE);
return AWS_OP_ERR;
}
memset(secure_transport_handler->protocol.buffer, 0, secure_transport_handler->protocol.capacity);
CFRange byte_range = CFRangeMake(0, CFStringGetLength(protocol));
CFStringGetBytes(
protocol,
byte_range,
kCFStringEncodingASCII,
0,
false,
secure_transport_handler->protocol.buffer,
secure_transport_handler->protocol.capacity,
NULL);
secure_transport_handler->protocol.len = secure_transport_handler->protocol.capacity - 1;
CFRelease(protocol);
AWS_LOGF_DEBUG(
AWS_LS_IO_TLS,
"id=%p: negotiated protocol: %s",
(void *)handler,
secure_transport_handler->protocol.buffer);
}
if (secure_transport_handler->server_name) {
/* Log server name to be consistent with other tls_channel_handler implementations,
* but this is just a copy of the EXPECTED server name,
* the Secure Transport API doesn't seem to expose actual server name. */
AWS_LOGF_DEBUG(
AWS_LS_IO_TLS,
"id=%p: Remote Server Name: %s",
(void *)handler,
aws_string_c_str(secure_transport_handler->server_name));
}
if (secure_transport_handler->parent_slot->adj_right && secure_transport_handler->advertise_alpn_message &&
protocol) {
struct aws_io_message *message = aws_channel_acquire_message_from_pool(
secure_transport_handler->parent_slot->channel,
AWS_IO_MESSAGE_APPLICATION_DATA,
sizeof(struct aws_tls_negotiated_protocol_message));
message->message_tag = AWS_TLS_NEGOTIATED_PROTOCOL_MESSAGE;
struct aws_tls_negotiated_protocol_message *protocol_message =
(struct aws_tls_negotiated_protocol_message *)message->message_data.buffer;
protocol_message->protocol = secure_transport_handler->protocol;
message->message_data.len = sizeof(struct aws_tls_negotiated_protocol_message);
if (aws_channel_slot_send_message(secure_transport_handler->parent_slot, message, AWS_CHANNEL_DIR_READ)) {
aws_mem_release(message->allocator, message);
aws_channel_shutdown(secure_transport_handler->parent_slot->channel, aws_last_error());
return AWS_OP_SUCCESS;
}
}
s_invoke_negotiation_callback(handler, AWS_ERROR_SUCCESS);
} else if (status == errSSLPeerAuthCompleted) {
/* this branch gets hit only when verification is disabled,
* or a custom CA bundle is being used. */
if (secure_transport_handler->verify_peer) {
if (!secure_transport_handler->ca_certs) {
s_invoke_negotiation_callback(handler, AWS_IO_TLS_ERROR_NEGOTIATION_FAILURE);
return AWS_OP_ERR;
}
SecTrustRef trust;
status = SSLCopyPeerTrust(secure_transport_handler->ctx, &trust);
if (status != errSecSuccess) {
s_invoke_negotiation_callback(handler, AWS_IO_TLS_ERROR_NEGOTIATION_FAILURE);
return AWS_OP_ERR;
}
SecPolicyRef policy;
if (secure_transport_handler->server_name) {
CFStringRef server_name = CFStringCreateWithCString(
secure_transport_handler->wrapped_allocator,
aws_string_c_str(secure_transport_handler->server_name),
kCFStringEncodingUTF8);
policy = SecPolicyCreateSSL(true, server_name);
CFRelease(server_name);
} else {
policy = SecPolicyCreateBasicX509();
}
status = SecTrustSetPolicies(trust, policy);
CFRelease(policy);
if (status != errSecSuccess) {
AWS_LOGF_ERROR(AWS_LS_IO_TLS, "id=%p: Failed to set trust policy %d\n", (void *)handler, (int)status);
CFRelease(trust);
s_invoke_negotiation_callback(handler, AWS_IO_TLS_ERROR_NEGOTIATION_FAILURE);
return AWS_OP_ERR;
}
status = SecTrustSetAnchorCertificates(trust, secure_transport_handler->ca_certs);
if (status != errSecSuccess) {
AWS_LOGF_ERROR(
AWS_LS_IO_TLS,
"id=%p: Failed to set anchor certificate with OSStatus %d\n",
(void *)handler,
(int)status);
CFRelease(trust);
s_invoke_negotiation_callback(handler, AWS_IO_TLS_ERROR_NEGOTIATION_FAILURE);
return AWS_OP_ERR;
}
/* Use ONLY the custom CA bundle (ignoring system anchors) */
status = SecTrustSetAnchorCertificatesOnly(trust, true);
if (status != errSecSuccess) {
AWS_LOGF_ERROR(
AWS_LS_IO_TLS,
"id=%p: Failed to ignore system anchors with OSStatus %d\n",
(void *)handler,
(int)status);
CFRelease(trust);
s_invoke_negotiation_callback(handler, AWS_IO_TLS_ERROR_NEGOTIATION_FAILURE);
return AWS_OP_ERR;
}
SecTrustResultType trust_eval = 0;
status = SecTrustEvaluate(trust, &trust_eval);
CFRelease(trust);
if (status == errSecSuccess &&
(trust_eval == kSecTrustResultProceed || trust_eval == kSecTrustResultUnspecified)) {
return s_drive_negotiation(handler);
}
AWS_LOGF_WARN(
AWS_LS_IO_TLS,
"id=%p: Using custom CA, certificate validation failed with OSStatus %d and Trust Eval %d.",
(void *)handler,
(int)status,
(int)trust_eval)
return AWS_OP_ERR;
}
return s_drive_negotiation(handler);
/* if this is here, everything went wrong. */
} else if (status != errSSLWouldBlock) {
secure_transport_handler->negotiation_finished = false;
AWS_LOGF_WARN(AWS_LS_IO_TLS, "id=%p: negotiation failed with OSStatus %d.", (void *)handler, (int)status);
aws_raise_error(AWS_IO_TLS_ERROR_NEGOTIATION_FAILURE);
s_invoke_negotiation_callback(handler, AWS_IO_TLS_ERROR_NEGOTIATION_FAILURE);
return AWS_OP_ERR;
}
return AWS_OP_SUCCESS;
}