static int s_drive_negotiation()

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;
}