Skip to content

Commit

Permalink
move TLS test from aws-c-http -> aws-c-io (#141)
Browse files Browse the repository at this point in the history
* move TLS test from aws-c-http to aws-c-io
* minor touchups to tls/socket shutdown logic
  • Loading branch information
graebm authored Jun 6, 2019
1 parent 9235b65 commit bd6c7f2
Show file tree
Hide file tree
Showing 7 changed files with 182 additions and 70 deletions.
8 changes: 5 additions & 3 deletions source/darwin/secure_transport_tls_channel_handler.c
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
4 changes: 4 additions & 0 deletions source/event_loop.c
Original file line number Diff line number Diff line change
Expand Up @@ -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));

Expand Down
12 changes: 7 additions & 5 deletions source/s2n/s2n_tls_channel_handler.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
4 changes: 4 additions & 0 deletions source/socket_channel_handler.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
116 changes: 59 additions & 57 deletions source/windows/secure_channel_tls_handler.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}
}
}
Expand Down
5 changes: 2 additions & 3 deletions tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
103 changes: 101 additions & 2 deletions tests/tls_handler_test.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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(
Expand All @@ -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);
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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);

Expand Down

0 comments on commit bd6c7f2

Please sign in to comment.