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

api: add method to append protocol preferences #2534

Merged
merged 9 commits into from
Feb 2, 2021
Merged
Show file tree
Hide file tree
Changes from 6 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
26 changes: 26 additions & 0 deletions api/s2n.h
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,19 @@ S2N_API
extern int s2n_config_add_dhparams(struct s2n_config *config, const char *dhparams_pem);
S2N_API
extern int s2n_config_set_cipher_preferences(struct s2n_config *config, const char *version);

/**
* Appends the provided application protocol to the preference list
*
* The data provided in `protocol` parameter will be copied into an internal buffer
*
* @param config The configuration object being updated
* @param protocol A pointer to a byte array value
* @param protocol_len The length of bytes that should be read from `protocol`. Note: this value cannot be 0, otherwise an error will be returned.
*/
S2N_API
extern int s2n_config_append_protocol_preference(struct s2n_config *config, const uint8_t *protocol, uint8_t protocol_len);

S2N_API
extern int s2n_config_set_protocol_preferences(struct s2n_config *config, const char * const *protocols, int protocol_count);
typedef enum { S2N_STATUS_REQUEST_NONE = 0, S2N_STATUS_REQUEST_OCSP = 1 } s2n_status_request_type;
Expand Down Expand Up @@ -328,6 +341,19 @@ extern uint64_t s2n_connection_get_delay(struct s2n_connection *conn);

S2N_API
extern int s2n_connection_set_cipher_preferences(struct s2n_connection *conn, const char *version);

/**
* Appends the provided application protocol to the preference list
*
* The data provided in `protocol` parameter will be copied into an internal buffer
*
* @param conn The connection object being updated
* @param protocol A pointer to a slice of bytes
* @param protocol_len The length of bytes that should be read from `protocol`. Note: this value cannot be 0, otherwise an error will be returned.
*/
S2N_API
extern int s2n_connection_append_protocol_preference(struct s2n_connection *conn, const uint8_t *protocol, uint8_t protocol_len);

S2N_API
extern int s2n_connection_set_protocol_preferences(struct s2n_connection *conn, const char * const *protocols, int protocol_count);
S2N_API
Expand Down
2 changes: 1 addition & 1 deletion error/s2n_errno.c
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@ static const char *no_such_error = "Internal s2n error";
ERR_ENTRY(S2N_ERR_NUM_DEFAULT_CERTIFICATES, "exceeded max default certificates or provided no default") \
ERR_ENTRY(S2N_ERR_MULTIPLE_DEFAULT_CERTIFICATES_PER_AUTH_TYPE, "setting multiple default certificates per auth type is not allowed") \
ERR_ENTRY(S2N_ERR_INVALID_CIPHER_PREFERENCES, "Invalid Cipher Preferences version") \
ERR_ENTRY(S2N_ERR_APPLICATION_PROTOCOL_TOO_LONG, "Application protocol name is too long") \
ERR_ENTRY(S2N_ERR_INVALID_APPLICATION_PROTOCOL, "The supplied application protocol name is invalid") \
ERR_ENTRY(S2N_ERR_KEY_MISMATCH, "public and private key do not match") \
ERR_ENTRY(S2N_ERR_SEND_SIZE, "Retried s2n_send() size is invalid") \
ERR_ENTRY(S2N_ERR_CORK_SET_ON_UNMANAGED, "Attempt to set connection cork management on unmanaged IO") \
Expand Down
2 changes: 1 addition & 1 deletion error/s2n_errno.h
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,7 @@ typedef enum {
S2N_ERR_NUM_DEFAULT_CERTIFICATES,
S2N_ERR_MULTIPLE_DEFAULT_CERTIFICATES_PER_AUTH_TYPE,
S2N_ERR_INVALID_CIPHER_PREFERENCES,
S2N_ERR_APPLICATION_PROTOCOL_TOO_LONG,
S2N_ERR_INVALID_APPLICATION_PROTOCOL,
S2N_ERR_KEY_MISMATCH,
S2N_ERR_SEND_SIZE,
S2N_ERR_CORK_SET_ON_UNMANAGED,
Expand Down
170 changes: 170 additions & 0 deletions tests/unit/s2n_protocol_preferences_test.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
/*
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/

#include "s2n_test.h"
#include "tls/s2n_connection.h"

#include <s2n.h>

int main(int argc, char **argv)
{
BEGIN_TEST();

/* Test config append */
{
struct s2n_config *config;
EXPECT_NOT_NULL(config = s2n_config_new());
EXPECT_EQUAL(config->application_protocols.size, 0);
size_t prev_size = 0;

/* should grow the blob with the provided value */
EXPECT_SUCCESS(s2n_config_append_protocol_preference(config, (const uint8_t *)"protocol1", 9));
EXPECT_EQUAL(config->application_protocols.size, 1 /* len prefix */ + 9);
camshaft marked this conversation as resolved.
Show resolved Hide resolved
prev_size = config->application_protocols.size;

/* should grow the blob even more */
EXPECT_SUCCESS(s2n_config_append_protocol_preference(config, (const uint8_t *)"protocol2", 9));
EXPECT_EQUAL(config->application_protocols.size, prev_size + 1 /* len prefix */ + 9);
prev_size = config->application_protocols.size;

/* should allow null byte values */
const uint8_t null_value[9] = { 0 };
EXPECT_SUCCESS(s2n_config_append_protocol_preference(config, (const uint8_t *)null_value, 9));
EXPECT_EQUAL(config->application_protocols.size, prev_size + 1 /* len prefix */ + 9);
prev_size = config->application_protocols.size;

/* should reallocate the blob */
const uint8_t large_value[255] = { 0 };
EXPECT_SUCCESS(s2n_config_append_protocol_preference(config, (const uint8_t *)large_value, 255));
EXPECT_EQUAL(config->application_protocols.size, prev_size + 1 /* len prefix */ + 255);
camshaft marked this conversation as resolved.
Show resolved Hide resolved
prev_size = config->application_protocols.size;

/* should not allow empty protocol values */
EXPECT_FAILURE(s2n_config_append_protocol_preference(config, (const uint8_t *)large_value, 0));
EXPECT_EQUAL(config->application_protocols.size, prev_size);

EXPECT_SUCCESS(s2n_config_free(config));
}

/* Test connection append */
{
struct s2n_connection *conn;
EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT));
EXPECT_EQUAL(conn->application_protocols_overridden.size, 0);
size_t prev_size = 0;

/* should grow the blob with the provided value */
EXPECT_SUCCESS(s2n_connection_append_protocol_preference(conn, (const uint8_t *)"protocol1", 9));
EXPECT_EQUAL(conn->application_protocols_overridden.size, 1 /* len prefix */ + 9);
prev_size = conn->application_protocols_overridden.size;

/* should grow the blob even more */
EXPECT_SUCCESS(s2n_connection_append_protocol_preference(conn, (const uint8_t *)"protocol2", 9));
EXPECT_EQUAL(conn->application_protocols_overridden.size, prev_size + 1 /* len prefix */ + 9);
prev_size = conn->application_protocols_overridden.size;

/* should allow null byte values */
const uint8_t null_value[9] = { 0 };
EXPECT_SUCCESS(s2n_connection_append_protocol_preference(conn, (const uint8_t *)null_value, 9));
EXPECT_EQUAL(conn->application_protocols_overridden.size, prev_size + 1 /* len prefix */ + 9);
prev_size = conn->application_protocols_overridden.size;

/* should reallocate the blob */
const uint8_t large_value[255] = { 0 };
EXPECT_SUCCESS(s2n_connection_append_protocol_preference(conn, (const uint8_t *)large_value, 255));
EXPECT_EQUAL(conn->application_protocols_overridden.size, prev_size + 1 /* len prefix */ + 255);
prev_size = conn->application_protocols_overridden.size;

/* should not allow empty protocol values */
EXPECT_FAILURE(s2n_connection_append_protocol_preference(conn, (const uint8_t *)large_value, 0));
EXPECT_EQUAL(conn->application_protocols_overridden.size, prev_size);

EXPECT_SUCCESS(s2n_connection_free(conn));
}

const char *protocols[] = { "protocol1", "protocol2", "protocol3" };
const uint8_t protocols_count = s2n_array_len(protocols);

/* Test config set */
{
struct s2n_config *config;
EXPECT_NOT_NULL(config = s2n_config_new());
EXPECT_EQUAL(config->application_protocols.size, 0);

/* should copy the preference list */
EXPECT_SUCCESS(s2n_config_set_protocol_preferences(config, protocols, protocols_count));
EXPECT_EQUAL(config->application_protocols.size, (1 /* len prefix */ + 9) * protocols_count);

/* should correctly free the old list list */
EXPECT_SUCCESS(s2n_config_set_protocol_preferences(config, protocols, 1));
EXPECT_EQUAL(config->application_protocols.size, 1 /* len prefix */ + 9);

/* should clear the preference list */
EXPECT_SUCCESS(s2n_config_set_protocol_preferences(config, NULL, protocols_count));
EXPECT_EQUAL(config->application_protocols.size, 0);

EXPECT_SUCCESS(s2n_config_set_protocol_preferences(config, protocols, protocols_count));
EXPECT_EQUAL(config->application_protocols.size, (1 /* len prefix */ + 9) * protocols_count);
EXPECT_SUCCESS(s2n_config_set_protocol_preferences(config, protocols, 0));
EXPECT_EQUAL(config->application_protocols.size, 0);

/* should limit the length of the protocol value */
char oversized[257] = { 0 };
memset(&oversized, 1, 256);
EXPECT_EQUAL(strlen(oversized), 256);
const char *oversized_p[] = { (const char *)&oversized };
EXPECT_FAILURE(s2n_config_set_protocol_preferences(config, (const char * const *)oversized_p, 1));
EXPECT_EQUAL(config->application_protocols.size, 0);

EXPECT_SUCCESS(s2n_config_free(config));
}

/* Test connection set */
{
struct s2n_connection *conn;
EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT));
EXPECT_EQUAL(conn->application_protocols_overridden.size, 0);

/* should copy the preference list */
EXPECT_SUCCESS(s2n_connection_set_protocol_preferences(conn, protocols, protocols_count));
EXPECT_EQUAL(conn->application_protocols_overridden.size, (1 /* len prefix */ + 9) * protocols_count);

/* should correctly free the old list */
EXPECT_SUCCESS(s2n_connection_set_protocol_preferences(conn, protocols, 1));
EXPECT_EQUAL(conn->application_protocols_overridden.size, 1 /* len prefix */ + 9);

/* should clear the preference list */
EXPECT_SUCCESS(s2n_connection_set_protocol_preferences(conn, NULL, protocols_count));
EXPECT_EQUAL(conn->application_protocols_overridden.size, 0);

EXPECT_SUCCESS(s2n_connection_set_protocol_preferences(conn, protocols, protocols_count));
EXPECT_EQUAL(conn->application_protocols_overridden.size, (1 /* len prefix */ + 9) * protocols_count);
EXPECT_SUCCESS(s2n_connection_set_protocol_preferences(conn, protocols, 0));
EXPECT_EQUAL(conn->application_protocols_overridden.size, 0);

/* should limit the length of the protocol value */
char oversized[257] = { 0 };
memset(&oversized, 1, 256);
EXPECT_EQUAL(strlen(oversized), 256);
const char *oversized_p[] = { (const char *)&oversized };
EXPECT_FAILURE(s2n_connection_set_protocol_preferences(conn, (const char * const *)oversized_p, 1));
EXPECT_EQUAL(conn->application_protocols_overridden.size, 0);

EXPECT_SUCCESS(s2n_connection_free(conn));
}

END_TEST();
return 0;
}
2 changes: 2 additions & 0 deletions tls/s2n_connection.c
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,8 @@ struct s2n_connection *s2n_connection_new(s2n_mode mode)
* is received, the stuffer will be resized according to the cookie length */
GUARD_PTR(s2n_stuffer_growable_alloc(&conn->cookie_stuffer, 0));

conn->application_protocols_overridden = (struct s2n_blob){ 0 };
camshaft marked this conversation as resolved.
Show resolved Hide resolved
camshaft marked this conversation as resolved.
Show resolved Hide resolved

return conn;
}

Expand Down
81 changes: 65 additions & 16 deletions tls/s2n_protocol_preferences.c
Original file line number Diff line number Diff line change
Expand Up @@ -17,39 +17,88 @@
#include "error/s2n_errno.h"
#include "utils/s2n_safety.h"

int s2n_blob_set_protocol_preferences(struct s2n_blob *application_protocols, const char *const *protocols, int protocol_count)
S2N_RESULT s2n_protocol_preferences_append(struct s2n_blob *application_protocols, const uint8_t *protocol, uint8_t protocol_len)
{
ENSURE_MUT(application_protocols);
ENSURE_REF(protocol);

/**
*= https://tools.ietf.org/rfc/rfc7301#section-3.1
*# Empty strings
*# MUST NOT be included and byte strings MUST NOT be truncated.
*/
ENSURE(protocol_len != 0, S2N_ERR_INVALID_APPLICATION_PROTOCOL);

uint32_t prev_len = application_protocols->size;
uint32_t new_len = prev_len + /* len prefix */ 1 + protocol_len;
ENSURE(new_len <= UINT16_MAX, S2N_ERR_INVALID_APPLICATION_PROTOCOL);

GUARD_AS_RESULT(s2n_realloc(application_protocols, new_len));

struct s2n_stuffer protocol_stuffer = {0};
GUARD_AS_RESULT(s2n_stuffer_init(&protocol_stuffer, application_protocols));
GUARD_AS_RESULT(s2n_stuffer_skip_write(&protocol_stuffer, prev_len));
GUARD_AS_RESULT(s2n_stuffer_write_uint8(&protocol_stuffer, protocol_len));
GUARD_AS_RESULT(s2n_stuffer_write_bytes(&protocol_stuffer, protocol, protocol_len));

GUARD(s2n_free(application_protocols));
return S2N_RESULT_OK;
}

S2N_RESULT s2n_protocol_preferences_set(struct s2n_blob *application_protocols, const char *const *protocols, int protocol_count)
{
ENSURE_MUT(application_protocols);

/* NULL value indicates no preference so free the previous blob */
if (protocols == NULL || protocol_count == 0) {
/* NULL value indicates no preference, so nothing to do */
return 0;
lrstewart marked this conversation as resolved.
Show resolved Hide resolved
GUARD_AS_RESULT(s2n_free(application_protocols));
return S2N_RESULT_OK;
}

GUARD(s2n_stuffer_growable_alloc(&protocol_stuffer, 256));
for (int i = 0; i < protocol_count; i++) {
DEFER_CLEANUP(struct s2n_blob new_protocols = { 0 }, s2n_free);

for (size_t i = 0; i < protocol_count; i++) {
const uint8_t * protocol = (const uint8_t *)protocols[i];
size_t length = strlen(protocols[i]);
uint8_t protocol[255];

S2N_ERROR_IF(length > 255 || (s2n_stuffer_data_available(&protocol_stuffer) + length + 1) > 65535, S2N_ERR_APPLICATION_PROTOCOL_TOO_LONG);
memcpy_check(protocol, protocols[i], length);
GUARD(s2n_stuffer_write_uint8(&protocol_stuffer, length));
GUARD(s2n_stuffer_write_bytes(&protocol_stuffer, protocol, length));
/**
*= https://tools.ietf.org/rfc/rfc7301#section-3.1
*# Empty strings
*# MUST NOT be included and byte strings MUST NOT be truncated.
*/
ENSURE(length < 256, S2N_ERR_INVALID_APPLICATION_PROTOCOL);
camshaft marked this conversation as resolved.
Show resolved Hide resolved

GUARD_RESULT(s2n_protocol_preferences_append(&new_protocols, protocol, (uint8_t)length));
camshaft marked this conversation as resolved.
Show resolved Hide resolved
}

GUARD(s2n_stuffer_extract_blob(&protocol_stuffer, application_protocols));
GUARD(s2n_stuffer_free(&protocol_stuffer));
return 0;
/* now we can free the previous list since we've validated all new input */
GUARD_AS_RESULT(s2n_free(application_protocols));

/* update the connection/config application_protocols with the newly allocated blob */
*application_protocols = new_protocols;

/* zero out new_protocols so we no longer refer to what we just allocated */
camshaft marked this conversation as resolved.
Show resolved Hide resolved
new_protocols = (struct s2n_blob){ 0 };
GUARD_AS_RESULT(s2n_free(&new_protocols));
camshaft marked this conversation as resolved.
Show resolved Hide resolved

return S2N_RESULT_OK;
}

int s2n_config_set_protocol_preferences(struct s2n_config *config, const char *const *protocols, int protocol_count)
{
return s2n_blob_set_protocol_preferences(&config->application_protocols, protocols, protocol_count);
return S2N_RESULT_TO_POSIX(s2n_protocol_preferences_set(&config->application_protocols, protocols, protocol_count));
}

int s2n_config_append_protocol_preference(struct s2n_config *config, const uint8_t *protocol, uint8_t protocol_len)
{
return S2N_RESULT_TO_POSIX(s2n_protocol_preferences_append(&config->application_protocols, protocol, protocol_len));
}

int s2n_connection_set_protocol_preferences(struct s2n_connection *conn, const char * const *protocols, int protocol_count)
{
return s2n_blob_set_protocol_preferences(&conn->application_protocols_overridden, protocols, protocol_count);
return S2N_RESULT_TO_POSIX(s2n_protocol_preferences_set(&conn->application_protocols_overridden, protocols, protocol_count));
}

int s2n_connection_append_protocol_preference(struct s2n_connection *conn, const uint8_t *protocol, uint8_t protocol_len)
{
return S2N_RESULT_TO_POSIX(s2n_protocol_preferences_append(&conn->application_protocols_overridden, protocol, protocol_len));
}