Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 35 additions & 15 deletions .builder/actions/tls_server_setup.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
"""
Setup local TLS server for tests
Setup local TLS servers for tests
"""

import Builder

import os
from pathlib import Path
import sys
import subprocess
import atexit
import time


class TlsServerSetup(Builder.Action):
Expand All @@ -18,26 +17,47 @@ class TlsServerSetup(Builder.Action):
This action should be run in the 'pre_build_steps' or 'build_steps' stage.
"""

@staticmethod
def cleanup_tls_server(tls_server_process):
tls_server_process.terminate()
out, err = tls_server_process.communicate()
print("TLS server stdout:")
for line in out.splitlines():
print(f" = {line.decode('utf-8')}")
print("TLS server stderr:")
for line in err.splitlines():
print(f" = {line.decode('utf-8')}")

def run(self, env):
if not env.project.needs_tests(env):
print("Skipping TLS server setup because tests disabled for project")
return

self.env = env

base_dir = os.path.dirname(os.path.realpath(__file__))
dir = os.path.join(base_dir, "..", "..", "tests", "tls_server")
root_dir = Path(__file__).resolve().parent / '..' / '..'
tls_server_dir = root_dir / 'tests' / 'tls_server'
resource_dir = root_dir / 'tests' / 'resources'

print("Running TLS servers")

python_path = env.config['variables']['python']

print("Running openssl TLS server")
tls12_server_process = subprocess.Popen(
[python_path, tls_server_dir / 'tls_server.py', '--port', '58443', '--resource-dir', resource_dir,
'--min-tls', '1.2',
'--max-tls', '1.2'],
stdout=subprocess.PIPE, stderr=subprocess.PIPE)

python_path = sys.executable
p = subprocess.Popen([python_path, "tls_server.py",
], cwd=dir, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
tls13_server_process = subprocess.Popen(
[python_path, tls_server_dir / 'tls_server.py', '--port', '59443', '--resource-dir', resource_dir,
'--min-tls', '1.3',
'--max-tls', '1.3'],
stdout=subprocess.PIPE, stderr=subprocess.PIPE)

@atexit.register
def close_tls_server():
print("Terminating openssl TLS server")
p.terminate()
out, err = p.communicate()
print("TLS server stdout:\n{}".format(out))
print("TLS server stderr:\n{}".format(err))
def close_tls_servers():
print('Terminating TLS 1.2 server')
TlsServerSetup.cleanup_tls_server(tls12_server_process)
print('Terminating TLS 1.3 server')
TlsServerSetup.cleanup_tls_server(tls13_server_process)
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ on:
- 'main'

env:
BUILDER_VERSION: v0.9.72
BUILDER_SOURCE: releases
BUILDER_VERSION: ubuntu-update
BUILDER_SOURCE: channels
BUILDER_HOST: https://d19elf31gohf1l.cloudfront.net
PACKAGE_NAME: aws-c-io
LINUX_BASE_IMAGE: ubuntu-18-x64
Expand Down
5 changes: 1 addition & 4 deletions builder.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,11 @@
{ "name": "aws-c-mqtt" },
{ "name": "aws-c-event-stream" }
],
"pre_build_steps": ["tls-server-setup"],
"targets": {
"linux": {
"_comment": "set up SoftHSM2 for PKCS#11 tests (see: ./builder/actions/pkcs11_test_setup.py)",
"+pre_build_steps": ["pkcs11-test-setup"]
},
"windows": {
"+pre_build_steps": ["tls-server-setup"]

}
},
"build_env": {
Expand Down
1 change: 1 addition & 0 deletions tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,7 @@ if(NOT BYO_CRYPTO)
add_net_test_case(tls_client_channel_negotiation_error_broken_crypto_dh480)
add_net_test_case(tls_client_channel_negotiation_error_broken_crypto_dh512)
add_net_test_case(tls_client_channel_negotiation_error_broken_crypto_null)
add_net_test_case(tls_client_channel_negotiation_error_tls1_3_to_tls1_2_server)

# Badssl - Legacy crypto suite, includes both negative and positive tests, with override checks where appropriate
# Our current baseline/default is platform-specific, whereas badssl expects a baseline of 1.2
Expand Down
157 changes: 140 additions & 17 deletions tests/tls_handler_test.c
Original file line number Diff line number Diff line change
Expand Up @@ -1225,6 +1225,10 @@ static void s_raise_tls_version_to_12(struct aws_tls_ctx_options *options) {
aws_tls_ctx_options_set_minimum_tls_version(options, AWS_IO_TLSv1_2);
}

static void s_raise_tls_version_to_13(struct aws_tls_ctx_options *options) {
aws_tls_ctx_options_set_minimum_tls_version(options, AWS_IO_TLSv1_3);
}

static int s_tls_client_channel_negotiation_error_override_legacy_crypto_tls11_fn(
struct aws_allocator *allocator,
void *ctx) {
Expand Down Expand Up @@ -1444,7 +1448,7 @@ static int s_verify_good_host(
return AWS_OP_SUCCESS;
}

static int s_verify_good_host_mqtt_connect(
static int s_verify_good_host_connect(
struct aws_allocator *allocator,
const struct aws_string *host_name,
uint32_t port,
Expand Down Expand Up @@ -1494,7 +1498,6 @@ static int s_verify_good_host_mqtt_connect(

/* tls13_server_root_ca.pem.crt is self-signed, so peer verification fails without additional OS configuration. */
aws_tls_ctx_options_set_verify_peer(&tls_options, false);
aws_tls_ctx_options_set_alpn_list(&tls_options, "x-amzn-mqtt-ca");

if (override_tls_options_fn) {
(*override_tls_options_fn)(&tls_options);
Expand All @@ -1511,7 +1514,6 @@ static int s_verify_good_host_mqtt_connect(

struct aws_byte_cursor host_name_cur = aws_byte_cursor_from_string(host_name);
aws_tls_connection_options_set_server_name(&tls_client_conn_options, allocator, &host_name_cur);
aws_tls_connection_options_set_alpn_list(&tls_client_conn_options, allocator, "x-amzn-mqtt-ca");

struct aws_socket_options options;
AWS_ZERO_STRUCT(options);
Expand Down Expand Up @@ -1549,16 +1551,8 @@ static int s_verify_good_host_mqtt_connect(
ASSERT_SUCCESS(aws_mutex_unlock(&c_tester.mutex));

ASSERT_FALSE(outgoing_args.error_invoked);
struct aws_byte_buf expected_protocol = aws_byte_buf_from_c_str("x-amzn-mqtt-ca");
/* check ALPN and SNI was properly negotiated */
if (aws_tls_is_alpn_available() && tls_options.verify_peer) {
ASSERT_BIN_ARRAYS_EQUALS(
expected_protocol.buffer,
expected_protocol.len,
outgoing_args.negotiated_protocol.buffer,
outgoing_args.negotiated_protocol.len);
}

/* check SNI was properly negotiated */
ASSERT_BIN_ARRAYS_EQUALS(
host_name->bytes, host_name->len, outgoing_args.server_name.buffer, outgoing_args.server_name.len);

Expand All @@ -1580,6 +1574,125 @@ static int s_verify_good_host_mqtt_connect(
return AWS_OP_SUCCESS;
}

static int s_verify_negotiation_fails_connect(
struct aws_allocator *allocator,
const struct aws_string *host_name,
uint32_t port,
void (*override_tls_options_fn)(struct aws_tls_ctx_options *)) {

struct aws_byte_buf cert_buf = {0};
struct aws_byte_buf key_buf = {0};
struct aws_byte_buf ca_buf = {0};

ASSERT_SUCCESS(aws_byte_buf_init_from_file(&cert_buf, allocator, "tls13_device.pem.crt"));
ASSERT_SUCCESS(aws_byte_buf_init_from_file(&key_buf, allocator, "tls13_device.key"));
ASSERT_SUCCESS(aws_byte_buf_init_from_file(&ca_buf, allocator, "tls13_server_root_ca.pem.crt"));

struct aws_byte_cursor cert_cur = aws_byte_cursor_from_buf(&cert_buf);
struct aws_byte_cursor key_cur = aws_byte_cursor_from_buf(&key_buf);
struct aws_byte_cursor ca_cur = aws_byte_cursor_from_buf(&ca_buf);

aws_io_library_init(allocator);

ASSERT_SUCCESS(s_tls_common_tester_init(allocator, &c_tester));

uint8_t outgoing_received_message[128] = {0};

struct tls_test_rw_args outgoing_rw_args;
ASSERT_SUCCESS(s_tls_rw_args_init(
&outgoing_rw_args,
&c_tester,
aws_byte_buf_from_empty_array(outgoing_received_message, sizeof(outgoing_received_message))));

struct tls_test_args outgoing_args = {
.mutex = &c_tester.mutex,
.allocator = allocator,
.condition_variable = &c_tester.condition_variable,
.error_invoked = 0,
.expects_error = true,
.rw_handler = NULL,
.server = false,
.tls_levels_negotiated = 0,
.desired_tls_levels = 1,
.shutdown_finished = false,
};

struct aws_tls_ctx_options tls_options = {0};
AWS_ZERO_STRUCT(tls_options);

AWS_FATAL_ASSERT(
AWS_OP_SUCCESS == aws_tls_ctx_options_init_client_mtls(&tls_options, allocator, &cert_cur, &key_cur));

/* tls13_server_root_ca.pem.crt is self-signed, so peer verification fails without additional OS configuration. */
aws_tls_ctx_options_set_verify_peer(&tls_options, false);

if (override_tls_options_fn) {
(*override_tls_options_fn)(&tls_options);
}

struct aws_tls_ctx *tls_context = aws_tls_client_ctx_new(allocator, &tls_options);
ASSERT_NOT_NULL(tls_context);

struct aws_tls_connection_options tls_client_conn_options;
aws_tls_connection_options_init_from_ctx(&tls_client_conn_options, tls_context);
aws_tls_connection_options_set_callbacks(&tls_client_conn_options, s_tls_on_negotiated, NULL, NULL, &outgoing_args);

aws_tls_ctx_options_override_default_trust_store(&tls_options, &ca_cur);

struct aws_byte_cursor host_name_cur = aws_byte_cursor_from_string(host_name);
aws_tls_connection_options_set_server_name(&tls_client_conn_options, allocator, &host_name_cur);

struct aws_socket_options options;
AWS_ZERO_STRUCT(options);
options.connect_timeout_ms = 10000;
options.type = AWS_SOCKET_STREAM;
options.domain = AWS_SOCKET_IPV4;

struct aws_client_bootstrap_options bootstrap_options = {
.event_loop_group = c_tester.el_group,
.host_resolver = c_tester.resolver,
};
struct aws_client_bootstrap *client_bootstrap = aws_client_bootstrap_new(allocator, &bootstrap_options);
ASSERT_NOT_NULL(client_bootstrap);

struct aws_socket_channel_bootstrap_options channel_options;
AWS_ZERO_STRUCT(channel_options);
channel_options.bootstrap = client_bootstrap;
channel_options.host_name = aws_string_c_str(host_name);
channel_options.port = port;
channel_options.socket_options = &options;
channel_options.tls_options = &tls_client_conn_options;
channel_options.setup_callback = s_tls_handler_test_client_setup_callback;
channel_options.shutdown_callback = s_tls_handler_test_client_shutdown_callback;
channel_options.user_data = &outgoing_args;

ASSERT_SUCCESS(aws_client_bootstrap_new_socket_channel(&channel_options));

/* put this here to verify ownership semantics are correct. This should NOT cause a segfault. If it does, ya
* done messed up. */
aws_tls_connection_options_clean_up(&tls_client_conn_options);

ASSERT_SUCCESS(aws_mutex_lock(&c_tester.mutex));
ASSERT_SUCCESS(aws_condition_variable_wait_pred(
&c_tester.condition_variable, &c_tester.mutex, s_tls_channel_shutdown_predicate, &outgoing_args));
ASSERT_SUCCESS(aws_mutex_unlock(&c_tester.mutex));

ASSERT_TRUE(outgoing_args.error_invoked);

ASSERT_TRUE(aws_error_code_is_tls(outgoing_args.last_error_code));

/* cleanups */
aws_byte_buf_clean_up(&cert_buf);
aws_byte_buf_clean_up(&key_buf);
aws_byte_buf_clean_up(&ca_buf);
aws_tls_ctx_release(tls_context);
aws_tls_ctx_options_clean_up(&tls_options);
aws_client_bootstrap_release(client_bootstrap);
ASSERT_SUCCESS(s_tls_common_tester_clean_up(&c_tester));

return AWS_OP_SUCCESS;
}

static int s_tls_client_channel_negotiation_success_fn(struct aws_allocator *allocator, void *ctx) {
(void)ctx;
return s_verify_good_host(allocator, s_amazon_host_name, 443, NULL);
Expand Down Expand Up @@ -1623,20 +1736,30 @@ AWS_TEST_CASE(
s_tls_client_channel_negotiation_success_ecc384_SCHANNEL_CREDS_fn)
# endif

static void s_raise_tls_version_to_13(struct aws_tls_ctx_options *options) {
aws_tls_ctx_options_set_minimum_tls_version(options, AWS_IO_TLSv1_3);
}

AWS_STATIC_STRING_FROM_LITERAL(s_aws_ecc384_host_name, "127.0.0.1");
static int s_tls_client_channel_negotiation_success_mtls_tls1_3_fn(struct aws_allocator *allocator, void *ctx) {
(void)ctx;
return s_verify_good_host_mqtt_connect(allocator, s_aws_ecc384_host_name, 59443, s_raise_tls_version_to_13);
uint32_t server_tls1_3_port = 59443;
return s_verify_good_host_connect(allocator, s_aws_ecc384_host_name, server_tls1_3_port, s_raise_tls_version_to_13);
}

AWS_TEST_CASE(
tls_client_channel_negotiation_success_mtls_tls1_3,
s_tls_client_channel_negotiation_success_mtls_tls1_3_fn)

static int s_tls_client_channel_negotiation_error_tls1_3_to_tls1_2_server_fn(
struct aws_allocator *allocator,
void *ctx) {
(void)ctx;
uint32_t server_tls1_2_port = 58443;
return s_verify_negotiation_fails_connect(
allocator, s_aws_ecc384_host_name, server_tls1_2_port, s_raise_tls_version_to_13);
}

AWS_TEST_CASE(
tls_client_channel_negotiation_error_tls1_3_to_tls1_2_server,
s_tls_client_channel_negotiation_error_tls1_3_to_tls1_2_server_fn)

AWS_STATIC_STRING_FROM_LITERAL(s3_host_name, "s3.amazonaws.com");

static void s_disable_verify_peer(struct aws_tls_ctx_options *options) {
Expand Down
Loading
Loading