Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add protocol version check for outgoing DATA messages #97

Merged
merged 52 commits into from
Aug 24, 2023
Merged
Show file tree
Hide file tree
Changes from 20 commits
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
39db7b1
Add protocol version check for outgoing DATA messages
sfodagain Aug 15, 2023
cf9b787
Fix logging when protocols mismatch
sfodagain Aug 15, 2023
8f07b40
Use s_aws_secure_tunnel_protocol_version_match_check in aws_secure_tu…
sfodagain Aug 15, 2023
7bd8829
Add protocol match tests for outbound message
sfodagain Aug 17, 2023
423ff36
Fix constness and formatting
sfodagain Aug 17, 2023
31cf647
Add comments to the tests
sfodagain Aug 17, 2023
9c11ea4
Add more validations for outgoing DATA messages
sfodagain Aug 17, 2023
2f0046c
Fix naming
sfodagain Aug 17, 2023
c21052d
Return error on sending DATA message with incorrect parameters
sfodagain Aug 18, 2023
0124473
Fix comments
sfodagain Aug 18, 2023
2438ee1
Rearrange tests
sfodagain Aug 18, 2023
38484fe
Clean tests
sfodagain Aug 18, 2023
bab5632
Fix formatting
sfodagain Aug 18, 2023
4b5b31c
Add new errors, add test
sfodagain Aug 18, 2023
041e937
Fix formatting
sfodagain Aug 18, 2023
4d8e6e5
Fix typo
sfodagain Aug 18, 2023
a561c40
Fix tests naming
sfodagain Aug 18, 2023
267ab22
Fix order of enum errors
sfodagain Aug 18, 2023
424b24c
Fix enum erros
sfodagain Aug 18, 2023
5061e4a
Remove extra variable
sfodagain Aug 18, 2023
cdd6d72
Add test with data race
sfodagain Aug 22, 2023
8bdf6e7
Fix
sfodagain Aug 22, 2023
39137aa
Reproducible data race
sfodagain Aug 22, 2023
3eefe56
Working tests
sfodagain Aug 23, 2023
0618b39
Remove todo
sfodagain Aug 23, 2023
b30e584
Remove unused function
sfodagain Aug 23, 2023
e6e79f8
Remove todo
sfodagain Aug 23, 2023
1ed9483
Fix logging format
sfodagain Aug 23, 2023
388ff13
Rename goto label
sfodagain Aug 23, 2023
04c463a
Remove unused include
sfodagain Aug 23, 2023
59b863f
Remove stream start test
sfodagain Aug 23, 2023
662e32e
Fix tests naming
sfodagain Aug 23, 2023
fb2c9f5
Receive messages from mocked server in the event loop
sfodagain Aug 23, 2023
7757a8e
Add missing static
sfodagain Aug 23, 2023
fc70e82
Fix naming
sfodagain Aug 23, 2023
3cdf140
Remove unnecessary unused-casts
sfodagain Aug 23, 2023
dbbdbf2
Remove unused function
sfodagain Aug 23, 2023
5eea789
Use vtable for fixing connection id in outbound message
sfodagain Aug 24, 2023
1c1d97e
Add doxygen comments
sfodagain Aug 24, 2023
5a92cd9
Move STREAM_START validation to event loop
sfodagain Aug 24, 2023
1e7af76
Add test for STREM_START in SOURCE mode
sfodagain Aug 24, 2023
3b017c0
Revert "Move STREAM_START validation to event loop"
sfodagain Aug 24, 2023
9a77ade
Fix stream_start test
sfodagain Aug 24, 2023
05878fa
Add CONNECTION_START test for source mode
sfodagain Aug 24, 2023
2cbe3e4
Revert "Revert "Move STREAM_START validation to event loop""
sfodagain Aug 24, 2023
85da045
Revert "Move STREAM_START validation to event loop"
sfodagain Aug 24, 2023
0ab2997
Add test for outbound STREAM_START message causing reset
sfodagain Aug 24, 2023
5d1d183
Revert "Revert "Move STREAM_START validation to event loop""
sfodagain Aug 24, 2023
5521fd1
Move validation of CONNECTION_START to event loop
sfodagain Aug 24, 2023
254293a
Disable failing test
sfodagain Aug 24, 2023
78bc7fa
Fix stream_start test with reset
sfodagain Aug 24, 2023
1ee3b31
Fix windows build
sfodagain Aug 24, 2023
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
5 changes: 5 additions & 0 deletions include/aws/iotdevice/iotdevice.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,14 @@ enum aws_iotdevice_error {
AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_OPERATION_FAILED_DUE_TO_OFFLINE_QUEUE_POLICY,
AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_UNEXPECTED_HANGUP,
AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_USER_REQUESTED_STOP,
/* NOTE Leave the old name for compatibility. */
AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_PROTOCOL_VERSION_MISSMATCH,
AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_PROTOCOL_VERSION_MISMATCH =
AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_PROTOCOL_VERSION_MISSMATCH,
AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_TERMINATED,
AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_DECODE_FAILURE,
AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_DATA_NO_ACTIVE_CONNECTION,
AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_DATA_PROTOCOL_VERSION_MISMATCH,

AWS_ERROR_END_IOTDEVICE_RANGE = AWS_ERROR_ENUM_END_RANGE(AWS_C_IOTDEVICE_PACKAGE_ID),
};
Expand Down
3 changes: 2 additions & 1 deletion include/aws/iotdevice/private/secure_tunneling_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -158,12 +158,13 @@ struct aws_secure_tunnel_connections {

/* Used for streams not using multiplexing (service ids) */
int32_t stream_id;
/* TODO This field should be removed. */
struct aws_hash_table connection_ids;

/* Table containing streams using multiplexing (service ids) */
struct aws_hash_table service_ids;

/* Message used for initializing a stream upon a reconnect due to a protocol version missmatch */
/* Message used for initializing a stream upon a reconnect due to a protocol version mismatch */
struct aws_secure_tunnel_message_storage *restore_stream_message_view;
struct aws_secure_tunnel_message_storage restore_stream_message;
};
Expand Down
18 changes: 12 additions & 6 deletions source/iotdevice.c
Original file line number Diff line number Diff line change
Expand Up @@ -77,22 +77,28 @@ static struct aws_error_info s_errors[] = {
"Error while processing secure tunnel operational state."),
AWS_DEFINE_ERROR_INFO_IOTDEVICE(
AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_OPERATION_FAILED_DUE_TO_OFFLINE_QUEUE_POLICY,
"Error while processing secure tunnel operational state."),
"Secure Tunnel operation failed due to offline queue policy."),
AWS_DEFINE_ERROR_INFO_IOTDEVICE(
AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_UNEXPECTED_HANGUP,
"The connection was closed unexpectedly."),
AWS_DEFINE_ERROR_INFO_IOTDEVICE(
AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_USER_REQUESTED_STOP,
"Secure Tunnel connection interrupted by user request."),
AWS_DEFINE_ERROR_INFO_IOTDEVICE(
AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_PROTOCOL_VERSION_MISSMATCH,
"Secure Tunnel connection interrupted due to a protocol version missmatch."),
AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_PROTOCOL_VERSION_MISMATCH,
"Secure Tunnel connection interrupted due to a protocol version mismatch."),
AWS_DEFINE_ERROR_INFO_IOTDEVICE(
AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_TERMINATED,
"Secure Tunnel terminated by user request."),
AWS_DEFINE_ERROR_INFO_IOTDEVICE(
AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_DECODE_FAILURE,
"Error occured while decoding an incoming message." ),
AWS_DEFINE_ERROR_INFO_IOTDEVICE(
AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_DECODE_FAILURE,
"Error occured while decoding an incoming message." ),
AWS_DEFINE_ERROR_INFO_IOTDEVICE(
AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_DATA_NO_ACTIVE_CONNECTION,
"DATA message processing failed due to no active connection found." ),
AWS_DEFINE_ERROR_INFO_IOTDEVICE(
AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_DATA_PROTOCOL_VERSION_MISMATCH,
"DATA message processing failed due to a protocol version mismatch." ),
};
/* clang-format on */
#undef AWS_DEFINE_ERROR_INFO_IOTDEVICE
Expand Down
116 changes: 87 additions & 29 deletions source/secure_tunneling.c
Original file line number Diff line number Diff line change
Expand Up @@ -205,13 +205,14 @@ static uint8_t s_aws_secure_tunnel_message_min_protocol_check(const struct aws_s
}

static bool s_aws_secure_tunnel_protocol_version_match_check(
struct aws_secure_tunnel *secure_tunnel,
const struct aws_secure_tunnel *secure_tunnel,
const struct aws_secure_tunnel_message_view *message) {
uint8_t message_protocol_version = s_aws_secure_tunnel_message_min_protocol_check(message);
if (secure_tunnel->connections->protocol_version != message_protocol_version) {
AWS_LOGF_WARN(
AWS_LS_IOTDEVICE_SECURE_TUNNELING,
"id=%p: Secure Tunnel is currently using Protocol V%d and received a message using Protocol V%d",
"id=%p: Protocol Version mismatch: Secure Tunnel is currently using Protocol V%d and a message "
"is using Protocol V%d",
(void *)secure_tunnel,
(int)secure_tunnel->connections->protocol_version,
message_protocol_version);
Expand All @@ -221,7 +222,7 @@ static bool s_aws_secure_tunnel_protocol_version_match_check(
}

static bool s_aws_secure_tunnel_stream_id_match_check(
struct aws_secure_tunnel *secure_tunnel,
const struct aws_secure_tunnel *secure_tunnel,
const struct aws_byte_cursor *service_id,
int32_t stream_id) {
/*
Expand All @@ -242,7 +243,7 @@ static bool s_aws_secure_tunnel_stream_id_match_check(
}

static bool s_aws_secure_tunnel_active_stream_check(
struct aws_secure_tunnel *secure_tunnel,
const struct aws_secure_tunnel *secure_tunnel,
const struct aws_secure_tunnel_message_view *message_view) {
/*
* No service id means either V1 protocol is being used or V3 protocol is being used on a tunnel without service ids
Expand Down Expand Up @@ -299,6 +300,58 @@ static bool s_aws_secure_tunnel_active_stream_check(
return true;
}

/**
* \internal
* Check if DATA message can be sent to one of active connections.
* \endinternal
*/
static bool s_aws_secure_tunnel_is_data_message_valid_for_connections(
const struct aws_secure_tunnel *secure_tunnel,
const struct aws_secure_tunnel_message_view *message_view) {

if (secure_tunnel->connections->protocol_version == 0) {
aws_raise_error(AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_DATA_NO_ACTIVE_CONNECTION);
return false;
}

if (!s_aws_secure_tunnel_protocol_version_match_check(secure_tunnel, message_view)) {
aws_raise_error(AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_DATA_PROTOCOL_VERSION_MISMATCH);
return false;
}

/* The V1 message will be assigned a stream ID that is currently in use, so it's always valid. */
if (secure_tunnel->connections->protocol_version == 1) {
sfod marked this conversation as resolved.
Show resolved Hide resolved
return true;
}

/* The message is at least V2 version, check if the requested service ID is available in the secure tunnel. */
struct aws_hash_element *elem = NULL;
aws_hash_table_find(&secure_tunnel->connections->service_ids, message_view->service_id, &elem);
if (elem == NULL) {
aws_raise_error(AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_INVALID_SERVICE_ID);
return false;
}

/* Check if the requested service ID is active. */
sfod marked this conversation as resolved.
Show resolved Hide resolved
struct aws_service_id_element *service_id_elem = elem->value;
if (service_id_elem->stream_id == INVALID_STREAM_ID) {
aws_raise_error(AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_INVALID_SERVICE_ID);
return false;
}

/* The message uses V3 protocol, check connection ID. */
if (message_view->connection_id != 0) {
struct aws_hash_element *connection_id_elem = NULL;
aws_hash_table_find(&service_id_elem->connection_ids, &message_view->connection_id, &connection_id_elem);
if (connection_id_elem == NULL) {
aws_raise_error(AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_INVALID_CONNECTION_ID);
return false;
}
}

return true;
}

static int s_aws_secure_tunnel_set_stream(
struct aws_secure_tunnel *secure_tunnel,
const struct aws_byte_cursor *service_id,
Expand All @@ -308,6 +361,7 @@ static int s_aws_secure_tunnel_set_stream(
if (service_id == NULL || service_id->len == 0) {
secure_tunnel->connections->stream_id = stream_id;
aws_hash_table_clear(&secure_tunnel->connections->connection_ids);
/* TODO This case should be treated as error. */
sfod marked this conversation as resolved.
Show resolved Hide resolved
if (connection_id > 0) {
struct aws_connection_id_element *connection_id_elem =
aws_connection_id_element_new(secure_tunnel->allocator, connection_id);
Expand Down Expand Up @@ -510,12 +564,12 @@ static void s_aws_secure_tunnel_on_data_received(

if (!s_aws_secure_tunnel_protocol_version_match_check(secure_tunnel, message_view)) {
/*
* Protocol missmatch results in a full disconnect/reconnect to the Secure Tunnel Service followed by
* initializing the stream that caused the missmatch
* Protocol mismatch results in a full disconnect/reconnect to the Secure Tunnel Service followed by
* initializing the stream that caused the mismatch
*/
AWS_LOGF_INFO(
AWS_LS_IOTDEVICE_SECURE_TUNNELING,
"id=%p: Secure Tunnel will be reset due to Protocol Version missmatch between previously established "
"id=%p: Secure Tunnel will be reset due to Protocol Version mismatch between previously established "
"Protocol Version and Protocol Version used by incoming STREAM START message.",
(void *)secure_tunnel);
reset_secure_tunnel_connection(secure_tunnel);
Expand Down Expand Up @@ -572,12 +626,12 @@ static void s_aws_secure_tunnel_on_stream_start_received(
secure_tunnel->connections->protocol_version);
} else if (!s_aws_secure_tunnel_protocol_version_match_check(secure_tunnel, message_view)) {
/*
* Protocol missmatch results in a full disconnect/reconnect to the Secure Tunnel Service followed by
* initializing the stream that caused the missmatch
* Protocol mismatch results in a full disconnect/reconnect to the Secure Tunnel Service followed by
* initializing the stream that caused the mismatch
*/
AWS_LOGF_INFO(
AWS_LS_IOTDEVICE_SECURE_TUNNELING,
"id=%p: Secure Tunnel will be reset due to Protocol Version missmatch between previously established "
"id=%p: Secure Tunnel will be reset due to Protocol Version mismatch between previously established "
"Protocol Version and Protocol Version used by incoming STREAM START message.",
(void *)secure_tunnel);
reset_secure_tunnel_connection(secure_tunnel);
Expand Down Expand Up @@ -615,7 +669,7 @@ static void s_aws_secure_tunnel_on_stream_reset_received(
!s_aws_secure_tunnel_protocol_version_match_check(secure_tunnel, message_view)) {
AWS_LOGF_INFO(
AWS_LS_IOTDEVICE_SECURE_TUNNELING,
"id=%p: Secure Tunnel will be reset due to Protocol Version missmatch between previously established "
"id=%p: Secure Tunnel will be reset due to Protocol Version mismatch between previously established "
"Protocol Version and Protocol Version used by incoming STREAM RESET message.",
(void *)secure_tunnel);
reset_secure_tunnel_connection(secure_tunnel);
Expand Down Expand Up @@ -728,7 +782,7 @@ static void s_aws_secure_tunnel_on_connection_start_received(
if (secure_tunnel->connections->protocol_version != 3) {
AWS_LOGF_INFO(
AWS_LS_IOTDEVICE_SECURE_TUNNELING,
"id=%p: Secure Tunnel will be reset due to Protocol Version missmatch between previously established "
"id=%p: Secure Tunnel will be reset due to Protocol Version mismatch between previously established "
"Protocol Version and Protocol Version used by incoming CONNECTION START message.",
(void *)secure_tunnel);
reset_secure_tunnel_connection(secure_tunnel);
Expand Down Expand Up @@ -771,7 +825,7 @@ static void s_aws_secure_tunnel_on_connection_reset_received(
if (secure_tunnel->connections->protocol_version != 3) {
AWS_LOGF_INFO(
AWS_LS_IOTDEVICE_SECURE_TUNNELING,
"id=%p: Secure Tunnel will be reset due to Protocol Version missmatch between previously established "
"id=%p: Secure Tunnel will be reset due to Protocol Version mismatch between previously established "
"Protocol Version and Protocol Version used by incoming CONNECTION RESET message.",
(void *)secure_tunnel);
reset_secure_tunnel_connection(secure_tunnel);
Expand Down Expand Up @@ -2310,7 +2364,7 @@ static void s_service_state_clean_disconnect(struct aws_secure_tunnel *secure_tu
if (aws_linked_list_empty(&secure_tunnel->queued_operations)) {
s_reset_secure_tunnel_streams(secure_tunnel);
s_secure_tunnel_shutdown_websocket(
secure_tunnel, AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_PROTOCOL_VERSION_MISSMATCH);
secure_tunnel, AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_PROTOCOL_VERSION_MISMATCH);
return;
}

Expand Down Expand Up @@ -2535,7 +2589,7 @@ int aws_secure_tunnel_send_message(
secure_tunnel->allocator, secure_tunnel, message_options, AWS_STOT_MESSAGE);

if (message_op == NULL) {
return aws_last_error();
goto error;
sfod marked this conversation as resolved.
Show resolved Hide resolved
}

/*
Expand All @@ -2547,20 +2601,28 @@ int aws_secure_tunnel_send_message(
message_op->options_storage.storage_view.connection_id = 0;
}

if (!s_aws_secure_tunnel_is_data_message_valid_for_connections(
secure_tunnel, &message_op->options_storage.storage_view)) {
AWS_LOGF_WARN(
AWS_LS_IOTDEVICE_SECURE_TUNNELING, "id=%p: Failed to send outbound DATA message", (void *)secure_tunnel);
goto destroy_message_op;
}

AWS_LOGF_DEBUG(
AWS_LS_IOTDEVICE_SECURE_TUNNELING,
"id=%p: Submitting MESSAGE operation (%p)",
(void *)secure_tunnel,
(void *)message_op);

if (s_submit_operation(secure_tunnel, &message_op->base)) {
goto error;
goto destroy_message_op;
}

return AWS_OP_SUCCESS;

error:
destroy_message_op:
aws_secure_tunnel_operation_release(&message_op->base);
error:
return aws_last_error();
}

Expand All @@ -2575,25 +2637,21 @@ int aws_secure_tunnel_stream_start(
return AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_INCORRECT_MODE;
}

uint8_t message_protocol_version = s_aws_secure_tunnel_message_min_protocol_check(message_options);
if (secure_tunnel->connections->protocol_version != 0 &&
message_protocol_version != secure_tunnel->connections->protocol_version) {
if (!s_aws_secure_tunnel_protocol_version_match_check(secure_tunnel, message_options)) {
sfod marked this conversation as resolved.
Show resolved Hide resolved
/*
* Protocol missmatch results in a full disconnect/reconnect to the Secure Tunnel Service followed by
* sending the STREAM START request that caused the missmatch.
* Protocol mismatch results in a full disconnect/reconnect to the Secure Tunnel Service followed by
* sending the STREAM START request that caused the mismatch.
*/
AWS_LOGF_INFO(
AWS_LS_IOTDEVICE_SECURE_TUNNELING,
"id=%p: Secure Tunnel will be reset due to Protocol Version missmatch between previously established "
"Protocol Version (%d) and Protocol Version used by outbound STREAM START message (%d).",
(void *)secure_tunnel,
(int)secure_tunnel->connections->protocol_version,
(int)message_protocol_version);
"id=%p: Secure Tunnel will be reset due to Protocol Version mismatch between previously established "
"Protocol Version and Protocol Version used by outbound STREAM START message.",
(void *)secure_tunnel);
reset_secure_tunnel_connection(secure_tunnel);
}

if (secure_tunnel->connections->protocol_version == 0) {
secure_tunnel->connections->protocol_version = message_protocol_version;
secure_tunnel->connections->protocol_version = s_aws_secure_tunnel_message_min_protocol_check(message_options);
AWS_LOGF_INFO(
AWS_LS_IOTDEVICE_SECURE_TUNNELING,
"id=%p: Secure tunnel client Protocol set to V%d based on outbound STREAM START",
Expand Down Expand Up @@ -2639,7 +2697,7 @@ int aws_secure_tunnel_connection_start(
if (secure_tunnel->connections->protocol_version != 3) {
AWS_LOGF_WARN(
AWS_LS_IOTDEVICE_SECURE_TUNNELING, "Connection Start may only be used with a Protocol V3 stream.");
return AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_PROTOCOL_VERSION_MISSMATCH;
return AWS_ERROR_IOTDEVICE_SECURE_TUNNELING_PROTOCOL_VERSION_MISMATCH;
}

if (message_options->connection_id == 0) {
Expand Down
10 changes: 9 additions & 1 deletion tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,17 @@ add_net_test_case(secure_tunneling_v2_to_v1_stream_start_test)
add_net_test_case(secure_tunneling_v3_to_v1_stream_start_test)
add_net_test_case(secure_tunneling_v1_stream_start_v3_message_reset_test)
add_net_test_case(secure_tunneling_v2_stream_start_connection_start_reset_test)
add_net_test_case(secure_tunneling_ignore_outbound_inactive_connection_id_message_sending_test)
add_net_test_case(secure_tunneling_close_stream_on_connection_reset_test)
add_net_test_case(secure_tunneling_existing_connection_start_send_reset_test)
add_net_test_case(secure_tunneling_send_v2_data_message_on_v1_connection)
add_net_test_case(secure_tunneling_send_v3_data_message_on_v1_connection)
add_net_test_case(secure_tunneling_send_v1_data_message_on_v2_connection)
add_net_test_case(secure_tunneling_send_v3_data_message_on_v2_connection)
add_net_test_case(secure_tunneling_send_v1_data_message_on_v3_connection)
add_net_test_case(secure_tunneling_send_v2_data_message_on_v3_connection)
add_net_test_case(secure_tunneling_send_v2_data_message_on_incorrect_v2_connection)
add_net_test_case(secure_tunneling_send_v3_data_message_on_incorrect_v3_connection)
add_net_test_case(secure_tunneling_send_v1_data_message_with_no_active_connection)

generate_test_driver(${PROJECT_NAME}-tests)

Expand Down
Loading