From bd6c7f29e8813f60571c1fb4b8bd53ae0bb7a506 Mon Sep 17 00:00:00 2001 From: graebm Date: Thu, 6 Jun 2019 15:16:40 -0700 Subject: [PATCH] move TLS test from aws-c-http -> aws-c-io (#141) * move TLS test from aws-c-http to aws-c-io * minor touchups to tls/socket shutdown logic --- .../secure_transport_tls_channel_handler.c | 8 +- source/event_loop.c | 4 + source/s2n/s2n_tls_channel_handler.c | 12 +- source/socket_channel_handler.c | 4 + source/windows/secure_channel_tls_handler.c | 116 +++++++++--------- tests/CMakeLists.txt | 5 +- tests/tls_handler_test.c | 103 +++++++++++++++- 7 files changed, 182 insertions(+), 70 deletions(-) diff --git a/source/darwin/secure_transport_tls_channel_handler.c b/source/darwin/secure_transport_tls_channel_handler.c index 9669229c7..b23eaf838 100644 --- a/source/darwin/secure_transport_tls_channel_handler.c +++ b/source/darwin/secure_transport_tls_channel_handler.c @@ -515,9 +515,11 @@ static int s_handle_shutdown( bool abort_immediately) { struct secure_transport_handler *secure_transport_handler = handler->impl; - if (dir == AWS_CHANNEL_DIR_WRITE && !error_code) { - AWS_LOGF_TRACE(AWS_LS_IO_TLS, "id=%p: shutting down write direction.", (void *)handler); - SSLClose(secure_transport_handler->ctx); + if (dir == AWS_CHANNEL_DIR_WRITE) { + if (!error_code) { + AWS_LOGF_TRACE(AWS_LS_IO_TLS, "id=%p: shutting down write direction.", (void *)handler); + SSLClose(secure_transport_handler->ctx); + } } else { AWS_LOGF_DEBUG( AWS_LS_IO_TLS, diff --git a/source/event_loop.c b/source/event_loop.c index ae6dc3ee6..0543d2d82 100644 --- a/source/event_loop.c +++ b/source/event_loop.c @@ -159,6 +159,10 @@ void aws_event_loop_clean_up_base(struct aws_event_loop *event_loop) { } void aws_event_loop_destroy(struct aws_event_loop *event_loop) { + if (!event_loop) { + return; + } + AWS_ASSERT(event_loop->vtable && event_loop->vtable->destroy); AWS_ASSERT(!aws_event_loop_thread_is_callers_thread(event_loop)); diff --git a/source/s2n/s2n_tls_channel_handler.c b/source/s2n/s2n_tls_channel_handler.c index 2764c36ce..fe0e7ded3 100644 --- a/source/s2n/s2n_tls_channel_handler.c +++ b/source/s2n/s2n_tls_channel_handler.c @@ -559,11 +559,13 @@ static int s_s2n_handler_shutdown( bool abort_immediately) { struct s2n_handler *s2n_handler = (struct s2n_handler *)handler->impl; - if (dir == AWS_CHANNEL_DIR_WRITE && !error_code) { - AWS_LOGF_DEBUG(AWS_LS_IO_TLS, "id=%p: Shutting down write direction", (void *)handler) - s2n_blocked_status blocked; - /* make a best effort, but the channel is going away after this run, so.... you only get one shot anyways */ - s2n_shutdown(s2n_handler->connection, &blocked); + if (dir == AWS_CHANNEL_DIR_WRITE) { + if (!error_code) { + AWS_LOGF_DEBUG(AWS_LS_IO_TLS, "id=%p: Shutting down write direction", (void *)handler) + s2n_blocked_status blocked; + /* make a best effort, but the channel is going away after this run, so.... you only get one shot anyways */ + s2n_shutdown(s2n_handler->connection, &blocked); + } } else { AWS_LOGF_DEBUG( AWS_LS_IO_TLS, "id=%p: Shutting down read direction with error code %d", (void *)handler, error_code); diff --git a/source/socket_channel_handler.c b/source/socket_channel_handler.c index f5eee0c51..78f93d09c 100644 --- a/source/socket_channel_handler.c +++ b/source/socket_channel_handler.c @@ -98,6 +98,10 @@ static int s_socket_process_write_message( (void *)handler, (unsigned long long)message->message_data.len); + if (!aws_socket_is_open(socket_handler->socket)) { + return AWS_OP_ERR; + } + struct aws_byte_cursor cursor = aws_byte_cursor_from_buf(&message->message_data); if (aws_socket_write(socket_handler->socket, &cursor, s_on_socket_write_complete, message)) { return AWS_OP_ERR; diff --git a/source/windows/secure_channel_tls_handler.c b/source/windows/secure_channel_tls_handler.c index 4982fdb19..3e18b4d87 100644 --- a/source/windows/secure_channel_tls_handler.c +++ b/source/windows/secure_channel_tls_handler.c @@ -1388,73 +1388,75 @@ static int s_handler_shutdown( bool abort_immediately) { struct secure_channel_handler *sc_handler = handler->impl; - if (dir == AWS_CHANNEL_DIR_WRITE && !error_code) { - AWS_LOGF_DEBUG(AWS_LS_IO_TLS, "id=%p: Shutting down both the read and write direction", (void *)handler) + if (dir == AWS_CHANNEL_DIR_WRITE) { + if (!error_code) { + AWS_LOGF_DEBUG(AWS_LS_IO_TLS, "id=%p: Shutting down the write direction", (void *)handler) - /* send a TLS alert. */ - SECURITY_STATUS status; + /* send a TLS alert. */ + SECURITY_STATUS status; - DWORD shutdown_code = SCHANNEL_SHUTDOWN; - SecBuffer shutdown_buffer = { - .pvBuffer = &shutdown_code, - .cbBuffer = sizeof(shutdown_code), - .BufferType = SECBUFFER_TOKEN, - }; + DWORD shutdown_code = SCHANNEL_SHUTDOWN; + SecBuffer shutdown_buffer = { + .pvBuffer = &shutdown_code, + .cbBuffer = sizeof(shutdown_code), + .BufferType = SECBUFFER_TOKEN, + }; - SecBufferDesc shutdown_buffer_desc = { - .ulVersion = SECBUFFER_VERSION, - .cBuffers = 1, - .pBuffers = &shutdown_buffer, - }; + SecBufferDesc shutdown_buffer_desc = { + .ulVersion = SECBUFFER_VERSION, + .cBuffers = 1, + .pBuffers = &shutdown_buffer, + }; - /* this updates the SSPI internal state machine. */ - status = ApplyControlToken(&sc_handler->sec_handle, &shutdown_buffer_desc); + /* this updates the SSPI internal state machine. */ + status = ApplyControlToken(&sc_handler->sec_handle, &shutdown_buffer_desc); - if (status != SEC_E_OK) { - aws_raise_error(AWS_IO_SYS_CALL_FAILURE); - return aws_channel_slot_on_handler_shutdown_complete(slot, dir, AWS_IO_SYS_CALL_FAILURE, true); - } - - SecBuffer output_buffer = { - .pvBuffer = NULL, - .cbBuffer = 0, - .BufferType = SECBUFFER_EMPTY, - }; + if (status != SEC_E_OK) { + aws_raise_error(AWS_IO_SYS_CALL_FAILURE); + return aws_channel_slot_on_handler_shutdown_complete(slot, dir, AWS_IO_SYS_CALL_FAILURE, true); + } - SecBufferDesc output_buffer_desc = { - .ulVersion = SECBUFFER_VERSION, - .cBuffers = 1, - .pBuffers = &output_buffer, - }; + SecBuffer output_buffer = { + .pvBuffer = NULL, + .cbBuffer = 0, + .BufferType = SECBUFFER_EMPTY, + }; - struct aws_byte_buf server_name = aws_tls_handler_server_name(handler); - /* this acutally gives us an Alert record to send. */ - status = InitializeSecurityContextA( - &sc_handler->creds, - &sc_handler->sec_handle, - (SEC_CHAR *)server_name.buffer, - sc_handler->ctx_req, - 0, - 0, - NULL, - 0, - NULL, - &output_buffer_desc, - &sc_handler->ctx_ret_flags, - NULL); + SecBufferDesc output_buffer_desc = { + .ulVersion = SECBUFFER_VERSION, + .cBuffers = 1, + .pBuffers = &output_buffer, + }; - if (status == SEC_E_OK || status == SEC_I_CONTEXT_EXPIRED) { - struct aws_io_message *outgoing_message = aws_channel_acquire_message_from_pool( - slot->channel, AWS_IO_MESSAGE_APPLICATION_DATA, output_buffer.cbBuffer); + struct aws_byte_buf server_name = aws_tls_handler_server_name(handler); + /* this acutally gives us an Alert record to send. */ + status = InitializeSecurityContextA( + &sc_handler->creds, + &sc_handler->sec_handle, + (SEC_CHAR *)server_name.buffer, + sc_handler->ctx_req, + 0, + 0, + NULL, + 0, + NULL, + &output_buffer_desc, + &sc_handler->ctx_ret_flags, + NULL); + + if (status == SEC_E_OK || status == SEC_I_CONTEXT_EXPIRED) { + struct aws_io_message *outgoing_message = aws_channel_acquire_message_from_pool( + slot->channel, AWS_IO_MESSAGE_APPLICATION_DATA, output_buffer.cbBuffer); - if (!outgoing_message || outgoing_message->message_data.capacity < output_buffer.cbBuffer) { - return aws_channel_slot_on_handler_shutdown_complete(slot, dir, aws_last_error(), true); - } - memcpy(outgoing_message->message_data.buffer, output_buffer.pvBuffer, output_buffer.cbBuffer); + if (!outgoing_message || outgoing_message->message_data.capacity < output_buffer.cbBuffer) { + return aws_channel_slot_on_handler_shutdown_complete(slot, dir, aws_last_error(), true); + } + memcpy(outgoing_message->message_data.buffer, output_buffer.pvBuffer, output_buffer.cbBuffer); - /* we don't really care if this succeeds or not, it's just sending the TLS alert. */ - if (aws_channel_slot_send_message(slot, outgoing_message, AWS_CHANNEL_DIR_WRITE)) { - aws_mem_release(outgoing_message->allocator, outgoing_message); + /* we don't really care if this succeeds or not, it's just sending the TLS alert. */ + if (aws_channel_slot_send_message(slot, outgoing_message, AWS_CHANNEL_DIR_WRITE)) { + aws_mem_release(outgoing_message->allocator, outgoing_message); + } } } } diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index cb38b219c..28cb4f0d2 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -103,12 +103,11 @@ add_net_test_case(tls_client_channel_negotiation_error_expired) add_net_test_case(tls_client_channel_negotiation_error_wrong_host) add_net_test_case(tls_client_channel_negotiation_error_self_signed) add_net_test_case(tls_client_channel_negotiation_error_untrusted_root) -add_net_test_case(tls_client_channel_negotiation_success) #track these down in s2n and find out why that aren't failing. #add_net_test_case(tls_client_channel_negotiation_error_revoked) #add_net_test_case(tls_client_channel_negotiation_error_pinning) -#add_net_test_case(tls_client_channel_negotiation_success) -#add_net_test_case(tls_channel_negotiation_error) +add_net_test_case(tls_client_channel_negotiation_error_socket_closed) +add_net_test_case(tls_client_channel_negotiation_success) add_test_case(alpn_successfully_negotiates) add_test_case(alpn_no_protocol_message) diff --git a/tests/tls_handler_test.c b/tests/tls_handler_test.c index 3ebdc9c71..4e4a932c3 100644 --- a/tests/tls_handler_test.c +++ b/tests/tls_handler_test.c @@ -72,6 +72,7 @@ static void s_tls_handler_test_client_setup_callback( (void)bootstrap; struct tls_test_args *setup_test_args = user_data; + aws_mutex_lock(setup_test_args->mutex); if (!error_code) { setup_test_args->channel = channel; @@ -88,6 +89,7 @@ static void s_tls_handler_test_client_setup_callback( } aws_condition_variable_notify_one(setup_test_args->condition_variable); + aws_mutex_unlock(setup_test_args->mutex); } static void s_tls_handler_test_server_setup_callback( @@ -113,6 +115,7 @@ static void s_tls_handler_test_server_setup_callback( setup_test_args->tls_negotiated = true; } else { setup_test_args->error_invoked = true; + setup_test_args->last_error_code = error_code; } aws_condition_variable_notify_one(setup_test_args->condition_variable); @@ -197,12 +200,14 @@ static struct aws_byte_buf s_tls_test_handle_read( (void)slot; struct tls_test_rw_args *rw_args = (struct tls_test_rw_args *)user_data; + aws_mutex_lock(rw_args->mutex); - memcpy(rw_args->received_message.buffer + rw_args->received_message.len, data_read->buffer, data_read->len); - rw_args->received_message.len += data_read->len; + aws_byte_buf_write_from_whole_buffer(&rw_args->received_message, *data_read); rw_args->read_invocations += 1; rw_args->invocation_happened = true; + aws_condition_variable_notify_one(rw_args->condition_variable); + aws_mutex_unlock(rw_args->mutex); return rw_args->received_message; } @@ -634,6 +639,100 @@ static int s_tls_client_channel_negotiation_error_pinning_fn(struct aws_allocato AWS_TEST_CASE(tls_client_channel_negotiation_error_pinning, s_tls_client_channel_negotiation_error_pinning_fn) +/* Test that, if the channel shuts down unexpectedly during tls negotiation, that the user code is still notified. + * We make this happen by connecting to port 80 on s3 or amazon.com and attempting TLS, + * which gets you hung up on after a few seconds */ +static int s_tls_client_channel_negotiation_error_socket_closed_fn(struct aws_allocator *allocator, void *ctx) { + (void)ctx; + + const char *host_name = "aws-crt-test-stuff.s3.amazonaws.com"; + uint16_t port = 80; /* Note: intentionally wrong and not 443 */ + + aws_tls_init_static_state(allocator); + + struct aws_event_loop_group el_group; + ASSERT_SUCCESS(aws_event_loop_group_default_init(&el_group, allocator, 0)); + + struct aws_host_resolver resolver; + ASSERT_SUCCESS(aws_host_resolver_init_default(&resolver, allocator, 1, &el_group)); + + struct aws_mutex mutex; + ASSERT_SUCCESS(aws_mutex_init(&mutex)); + struct aws_condition_variable condition_variable; + ASSERT_SUCCESS(aws_condition_variable_init(&condition_variable)); + + struct aws_tls_ctx_options client_ctx_options; + aws_tls_ctx_options_init_default_client(&client_ctx_options, allocator); + + struct aws_tls_ctx *client_ctx = aws_tls_client_ctx_new(allocator, &client_ctx_options); + ASSERT_NOT_NULL(client_ctx); + + struct aws_tls_connection_options tls_client_conn_options; + aws_tls_connection_options_init_from_ctx(&tls_client_conn_options, client_ctx); + aws_tls_connection_options_set_callbacks(&tls_client_conn_options, s_tls_on_negotiated, NULL, NULL, NULL); + struct aws_byte_cursor host_name_cursor = aws_byte_cursor_from_c_str(host_name); + aws_tls_connection_options_set_server_name(&tls_client_conn_options, allocator, &host_name_cursor); + + struct tls_test_args outgoing_args = { + .mutex = &mutex, + .allocator = allocator, + .condition_variable = &condition_variable, + .rw_handler = NULL, + .server = false, + .tls_negotiated = false, + .shutdown_finished = false, + }; + + struct aws_socket_options options = { + .connect_timeout_ms = 10000, .type = AWS_SOCKET_STREAM, .domain = AWS_SOCKET_IPV4}; + + aws_mutex_lock(&mutex); + + struct aws_client_bootstrap *client_bootstrap = aws_client_bootstrap_new(allocator, &el_group, &resolver, NULL); + ASSERT_NOT_NULL(client_bootstrap); + + ASSERT_SUCCESS(aws_client_bootstrap_new_tls_socket_channel( + client_bootstrap, + host_name, + port, + &options, + &tls_client_conn_options, + s_tls_handler_test_client_setup_callback, + s_tls_handler_test_client_shutdown_callback, + &outgoing_args)); + + /* Wait for setup to complete */ + ASSERT_SUCCESS( + aws_condition_variable_wait_pred(&condition_variable, &mutex, s_tls_channel_setup_predicate, &outgoing_args)); + + /* Assert that setup failed, and that it failed for reasons unrelated to the tls-handler. */ + ASSERT_FALSE(outgoing_args.tls_negotiated); + ASSERT_TRUE(outgoing_args.error_invoked); + ASSERT_INT_EQUALS(AWS_IO_SOCKET_CLOSED, outgoing_args.last_error_code); + + aws_mutex_unlock(&mutex); + + /* Clean up */ + aws_client_bootstrap_release(client_bootstrap); + + aws_tls_ctx_destroy(client_ctx); + aws_tls_ctx_options_clean_up(&client_ctx_options); + aws_tls_connection_options_clean_up(&tls_client_conn_options); + aws_host_resolver_clean_up(&resolver); + + aws_mutex_clean_up(&mutex); + aws_condition_variable_clean_up(&condition_variable); + + aws_event_loop_group_clean_up(&el_group); + aws_tls_clean_up_static_state(); + + return AWS_OP_SUCCESS; +} + +AWS_TEST_CASE( + tls_client_channel_negotiation_error_socket_closed, + s_tls_client_channel_negotiation_error_socket_closed_fn); + static int s_verify_good_host(struct aws_allocator *allocator, const struct aws_string *host_name) { aws_tls_init_static_state(allocator);