diff options
author | robot-contrib <robot-contrib@yandex-team.com> | 2022-07-21 15:48:40 +0300 |
---|---|---|
committer | robot-contrib <robot-contrib@yandex-team.com> | 2022-07-21 15:48:40 +0300 |
commit | d7bcd6c1debc3c73be1d9b096c22a4a7a8bd75d9 (patch) | |
tree | 00b0a76a27ea296b562bf8d1aad63ea59df02a88 | |
parent | 4fc945f9177d94ae69c94276cc6f3277a0284f1f (diff) | |
download | ydb-d7bcd6c1debc3c73be1d9b096c22a4a7a8bd75d9.tar.gz |
Update contrib/restricted/aws/s2n to 1.3.17
16 files changed, 282 insertions, 129 deletions
diff --git a/contrib/restricted/aws/s2n/api/s2n.h b/contrib/restricted/aws/s2n/api/s2n.h index f1395151db..11df0c5f99 100644 --- a/contrib/restricted/aws/s2n/api/s2n.h +++ b/contrib/restricted/aws/s2n/api/s2n.h @@ -286,10 +286,8 @@ extern int s2n_config_free_cert_chain_and_key(struct s2n_config *config); /** * Callback function type used to get the system time. * - * Takes two arguments. A pointer to arbitrary data for use within the callback and a pointer to a 64 bit int. - * the callback and a pointer to a 64 bit int. The 64 bit pointer should be set to the - * number of nanoseconds since the Unix epoch. - * + * @param void* A pointer to arbitrary data for use within the callback + * @param uint64_t* A pointer that the callback will set to the time in nanoseconds * The function should return 0 on success and -1 on failure. */ typedef int (*s2n_clock_time_nanoseconds) (void *, uint64_t *); @@ -345,7 +343,11 @@ typedef int (*s2n_cache_delete_callback) (struct s2n_connection *conn, void *, /** * Allows the caller to set a callback function that will be used to get the - * system time. + * system time. The time returned should be the number of nanoseconds since the + * Unix epoch (Midnight, January 1st, 1970). + * + * s2n-tls uses this clock for timestamps. + * * @param config The configuration object being updated * @param clock_fn The wall clock time callback function * @param ctx An opaque pointer that the callback will be invoked with @@ -356,7 +358,11 @@ extern int s2n_config_set_wall_clock(struct s2n_config *config, s2n_clock_time_n /** * Allows the caller to set a callback function that will be used to get - * monotonic time. + * monotonic time. The monotonic time is the time since an arbitrary, unspecified + * point. Unlike wall clock time, it MUST never move backwards. + * + * s2n-tls uses this clock for timers. + * * @param config The configuration object being updated * @param clock_fn The monotonic time callback function * @param ctx An opaque pointer that the callback will be invoked with @@ -712,6 +718,29 @@ S2N_API extern s2n_cert_private_key *s2n_cert_chain_and_key_get_private_key(struct s2n_cert_chain_and_key *cert_and_key); /** + * Set the raw OCSP stapling data for a certificate chain. + * + * @param chain_and_key The certificate chain handle + * @param data A pointer to the raw OCSP stapling data bytes. The data will be copied. + * @param length The length of the data bytes. + * @returns S2N_SUCCESS on success. S2N_FAILURE on failure + */ +S2N_API +extern int s2n_cert_chain_and_key_set_ocsp_data(struct s2n_cert_chain_and_key *chain_and_key, const uint8_t *data, uint32_t length); + +/** + * Set the signed certificate timestamp (SCT) for a certificate chain. + * This is used for Certificate Transparency. + * + * @param chain_and_key The certificate chain handle + * @param data A pointer to the SCT data. The data will be copied. + * @param length The length of the data bytes. + * @returns S2N_SUCCESS on success. S2N_FAILURE on failure + */ +S2N_API +extern int s2n_cert_chain_and_key_set_sct_list(struct s2n_cert_chain_and_key *chain_and_key, const uint8_t *data, uint32_t length); + +/** * A callback function that is invoked if s2n-tls cannot resolve a conflict between * two certificates with the same domain name. This function is invoked while certificates * are added to an `s2n_config`. @@ -738,16 +767,18 @@ S2N_API extern int s2n_config_set_cert_tiebreak_callback(struct s2n_config *config, s2n_cert_tiebreak_callback cert_tiebreak_cb); /** - * Associates a certificate chain and a private key, with an `s2n_config` object. - * At present, only one certificate-chain/key pair may be associated with a config. + * Associates a certificate chain and private key with an `s2n_config` object. + * Using this API, only one cert chain of each type (like ECDSA or RSA) may be associated with a config. * `cert_chain_pem` should be a PEM encoded certificate chain, with the first certificate - * in the chain being your servers certificate. `private_key_pem` should be a + * in the chain being your server's certificate. `private_key_pem` should be a * PEM encoded private key corresponding to the server certificate. * + * @deprecated Use s2n_config_add_cert_chain_and_key_to_store instead. + * * @param config The configuration object being updated * @param cert_chain_pem A byte array of a PEM encoded certificate chain. * @param private_key_pem A byte array of a PEM encoded key. - * @returns S2N_SUCCESS on success. S2N_FAILURE on failure + * @returns S2N_SUCCESS on success. S2N_FAILURE on failure. */ S2N_API extern int s2n_config_add_cert_chain_and_key(struct s2n_config *config, const char *cert_chain_pem, const char *private_key_pem); @@ -1023,6 +1054,8 @@ extern int s2n_config_set_alert_behavior(struct s2n_config *config, s2n_alert_be * parameters are set to NULL, no new data is set in the `s2n_config` object, * effectively clearing existing data. * + * @deprecated Use s2n_cert_chain_and_key_set_ocsp_data and s2n_cert_chain_and_key_set_sct_list instead. + * * @param config The configuration object being updated * @param type The extension type * @param data Data for the extension @@ -1672,10 +1705,12 @@ S2N_API extern int s2n_connection_set_protocol_preferences(struct s2n_connection *conn, const char * const *protocols, int protocol_count); /** - * Sets the server name for the connection. + * Sets the server name for the connection. * - * @note In the future, this can be used by clients who wish to use the TLS "Server Name indicator" - * extension. At present, client functionality is disabled. + * It may be desirable for clients + * to provide this information to facilitate secure connections to + * servers that host multiple 'virtual' servers at a single underlying + * network address. * * @param conn The connection object being queried * @param server_name A pointer to a string containing the desired server name @@ -1707,7 +1742,7 @@ S2N_API extern const char *s2n_get_application_protocol(struct s2n_connection *conn); /** - * Query the connection for a buffer containing the OCSP reponse. + * Query the connection for a buffer containing the OCSP response. * * @param conn The connection object being queried * @param length A pointer that is set to the certificate transparency response buffer's size diff --git a/contrib/restricted/aws/s2n/crypto/s2n_certificate.c b/contrib/restricted/aws/s2n/crypto/s2n_certificate.c index f1a71a0332..9e3f4bc551 100644 --- a/contrib/restricted/aws/s2n/crypto/s2n_certificate.c +++ b/contrib/restricted/aws/s2n/crypto/s2n_certificate.c @@ -185,47 +185,38 @@ int s2n_cert_chain_and_key_set_sct_list(struct s2n_cert_chain_and_key *chain_and struct s2n_cert_chain_and_key *s2n_cert_chain_and_key_new(void) { - struct s2n_cert_chain_and_key *chain_and_key; - struct s2n_blob chain_and_key_mem, cert_chain_mem, pkey_mem; - + DEFER_CLEANUP(struct s2n_blob chain_and_key_mem = { 0 }, s2n_free); PTR_GUARD_POSIX(s2n_alloc(&chain_and_key_mem, sizeof(struct s2n_cert_chain_and_key))); - chain_and_key = (struct s2n_cert_chain_and_key *)(void *)chain_and_key_mem.data; + PTR_GUARD_POSIX(s2n_blob_zero(&chain_and_key_mem)); - /* Allocate the memory for the chain and key */ - if (s2n_alloc(&cert_chain_mem, sizeof(struct s2n_cert_chain)) != S2N_SUCCESS) { - goto cleanup; - } - chain_and_key->cert_chain = (struct s2n_cert_chain *)(void *)cert_chain_mem.data; + DEFER_CLEANUP(struct s2n_blob cert_chain_mem = { 0 }, s2n_free); + PTR_GUARD_POSIX(s2n_alloc(&cert_chain_mem, sizeof(struct s2n_cert_chain))); + PTR_GUARD_POSIX(s2n_blob_zero(&cert_chain_mem)); - if (s2n_alloc(&pkey_mem, sizeof(s2n_cert_private_key)) != S2N_SUCCESS) { - goto cleanup; - } - chain_and_key->private_key = (s2n_cert_private_key *)(void *)pkey_mem.data; + DEFER_CLEANUP(struct s2n_blob pkey_mem = { 0 }, s2n_free); + PTR_GUARD_POSIX(s2n_alloc(&pkey_mem, sizeof(s2n_cert_private_key))); + PTR_GUARD_POSIX(s2n_blob_zero(&pkey_mem)); - chain_and_key->cert_chain->head = NULL; - if (s2n_pkey_zero_init(chain_and_key->private_key) != S2N_SUCCESS) { - goto cleanup; - } - memset(&chain_and_key->ocsp_status, 0, sizeof(chain_and_key->ocsp_status)); - memset(&chain_and_key->sct_list, 0, sizeof(chain_and_key->sct_list)); - chain_and_key->cn_names = s2n_array_new(sizeof(struct s2n_blob)); - if (!chain_and_key->cn_names) { - goto cleanup; - } - - chain_and_key->san_names = s2n_array_new(sizeof(struct s2n_blob)); - if (!chain_and_key->san_names) { - goto cleanup; - } + DEFER_CLEANUP(struct s2n_array *cn_names = NULL, s2n_array_free_p); + cn_names = s2n_array_new(sizeof(struct s2n_blob)); + PTR_ENSURE_REF(cn_names); - chain_and_key->context = NULL; + DEFER_CLEANUP(struct s2n_array *san_names = NULL, s2n_array_free_p); + san_names = s2n_array_new(sizeof(struct s2n_blob)); + PTR_ENSURE_REF(san_names); + struct s2n_cert_chain_and_key *chain_and_key = (struct s2n_cert_chain_and_key *)(void *)chain_and_key_mem.data; + chain_and_key->cert_chain = (struct s2n_cert_chain *)(void *)cert_chain_mem.data; + chain_and_key->private_key = (s2n_cert_private_key *)(void *)pkey_mem.data; + chain_and_key->cn_names = cn_names; + chain_and_key->san_names = san_names; + + ZERO_TO_DISABLE_DEFER_CLEANUP(chain_and_key_mem); + ZERO_TO_DISABLE_DEFER_CLEANUP(cert_chain_mem); + ZERO_TO_DISABLE_DEFER_CLEANUP(pkey_mem); + ZERO_TO_DISABLE_DEFER_CLEANUP(cn_names); + ZERO_TO_DISABLE_DEFER_CLEANUP(san_names); return chain_and_key; - cleanup: - s2n_free(&pkey_mem); - s2n_free(&cert_chain_mem); - s2n_free(&chain_and_key_mem); - return NULL; } DEFINE_POINTER_CLEANUP_FUNC(GENERAL_NAMES *, GENERAL_NAMES_free); diff --git a/contrib/restricted/aws/s2n/crypto/s2n_certificate.h b/contrib/restricted/aws/s2n/crypto/s2n_certificate.h index e78601c1ae..c0cfad98da 100644 --- a/contrib/restricted/aws/s2n/crypto/s2n_certificate.h +++ b/contrib/restricted/aws/s2n/crypto/s2n_certificate.h @@ -60,8 +60,6 @@ struct certs_by_type { struct s2n_cert_chain_and_key *certs[S2N_CERT_TYPE_COUNT]; }; -int s2n_cert_chain_and_key_set_ocsp_data(struct s2n_cert_chain_and_key *chain_and_key, const uint8_t *data, uint32_t length); -int s2n_cert_chain_and_key_set_sct_list(struct s2n_cert_chain_and_key *chain_and_key, const uint8_t *data, uint32_t length); /* Exposed for fuzzing */ int s2n_cert_chain_and_key_load_cns(struct s2n_cert_chain_and_key *chain_and_key, X509 *x509_cert); int s2n_cert_chain_and_key_load_sans(struct s2n_cert_chain_and_key *chain_and_key, X509 *x509_cert); diff --git a/contrib/restricted/aws/s2n/error/s2n_errno.c b/contrib/restricted/aws/s2n/error/s2n_errno.c index 1ddc11b6e7..3ade2ea4ee 100644 --- a/contrib/restricted/aws/s2n/error/s2n_errno.c +++ b/contrib/restricted/aws/s2n/error/s2n_errno.c @@ -270,6 +270,7 @@ static const char *no_such_error = "Internal s2n error"; ERR_ENTRY(S2N_ERR_SECRET_SCHEDULE_STATE, "Correct inputs to secret calculation not available") \ ERR_ENTRY(S2N_ERR_LIBCRYPTO_VERSION_NUMBER_MISMATCH, "The libcrypto major version number seen at compile-time is different from the major version number seen at run-time") \ ERR_ENTRY(S2N_ERR_LIBCRYPTO_VERSION_NAME_MISMATCH, "The libcrypto major version name seen at compile-time is different from the major version name seen at run-time") \ + ERR_ENTRY(S2N_ERR_CERT_OWNERSHIP, "The ownership of the certificate chain is incompatible with the operation") /* clang-format on */ #define ERR_STR_CASE(ERR, str) case ERR: return str; diff --git a/contrib/restricted/aws/s2n/error/s2n_errno.h b/contrib/restricted/aws/s2n/error/s2n_errno.h index a02c7d5acc..a257500ef4 100644 --- a/contrib/restricted/aws/s2n/error/s2n_errno.h +++ b/contrib/restricted/aws/s2n/error/s2n_errno.h @@ -287,6 +287,7 @@ typedef enum { S2N_ERR_INSUFFICIENT_MEM_SIZE, S2N_ERR_KEYING_MATERIAL_EXPIRED, S2N_ERR_SECRET_SCHEDULE_STATE, + S2N_ERR_CERT_OWNERSHIP, S2N_ERR_T_USAGE_END, } s2n_error; diff --git a/contrib/restricted/aws/s2n/tls/extensions/s2n_client_key_share.c b/contrib/restricted/aws/s2n/tls/extensions/s2n_client_key_share.c index 6073263014..7e0ec41a82 100644 --- a/contrib/restricted/aws/s2n/tls/extensions/s2n_client_key_share.c +++ b/contrib/restricted/aws/s2n/tls/extensions/s2n_client_key_share.c @@ -84,6 +84,13 @@ static int s2n_generate_default_ecc_key_share(struct s2n_connection *conn, struc POSIX_GUARD(s2n_ecc_evp_params_free(client_params)); } + /** + *= https://tools.ietf.org/rfc/rfc8446#4.2.8 + *# Otherwise, when sending the new ClientHello, the client MUST + *# replace the original "key_share" extension with one containing only a + *# new KeyShareEntry for the group indicated in the selected_group field + *# of the triggering HelloRetryRequest. + **/ client_params->negotiated_curve = server_curve; } else { client_params->negotiated_curve = ecc_pref->ecc_curves[0]; @@ -164,6 +171,13 @@ static int s2n_generate_default_pq_hybrid_key_share(struct s2n_connection *conn, POSIX_GUARD(s2n_kem_group_free(client_params)); } + /** + *= https://tools.ietf.org/rfc/rfc8446#4.2.8 + *# Otherwise, when sending the new ClientHello, the client MUST + *# replace the original "key_share" extension with one containing only a + *# new KeyShareEntry for the group indicated in the selected_group field + *# of the triggering HelloRetryRequest. + **/ client_params->kem_group = server_group; } else { client_params->kem_group = kem_pref->tls13_kem_groups[0]; @@ -175,6 +189,16 @@ static int s2n_generate_default_pq_hybrid_key_share(struct s2n_connection *conn, static int s2n_client_key_share_send(struct s2n_connection *conn, struct s2n_stuffer *out) { + if (s2n_is_hello_retry_handshake(conn)) { + const struct s2n_ecc_named_curve *server_curve = conn->kex_params.server_ecc_evp_params.negotiated_curve; + const struct s2n_ecc_named_curve *client_curve = conn->kex_params.client_ecc_evp_params.negotiated_curve; + const struct s2n_kem_group *server_group = conn->kex_params.server_kem_group_params.kem_group; + const struct s2n_kem_group *client_group = conn->kex_params.client_kem_group_params.kem_group; + + /* Ensure a new key share will be sent after a hello retry request */ + POSIX_ENSURE(server_curve != client_curve || server_group != client_group, S2N_ERR_BAD_KEY_SHARE); + } + struct s2n_stuffer_reservation shares_size = {0}; POSIX_GUARD(s2n_stuffer_reserve_uint16(out, &shares_size)); POSIX_GUARD(s2n_generate_default_pq_hybrid_key_share(conn, out)); @@ -397,8 +421,15 @@ static int s2n_client_key_share_recv(struct s2n_connection *conn, struct s2n_stu /* During a retry, the client should only have sent one keyshare */ POSIX_ENSURE(!s2n_is_hello_retry_handshake(conn) || keyshare_count == 1, S2N_ERR_BAD_MESSAGE); - /* If there were no matching key shares, then we received an empty key share extension - * or we didn't match a key share with a supported group. We should send a retry. */ + /** + * If there were no matching key shares, then we received an empty key share extension + * or we didn't match a key share with a supported group. We should send a retry. + * + *= https://tools.ietf.org/rfc/rfc8446#4.1.1 + *# If the server selects an (EC)DHE group and the client did not offer a + *# compatible "key_share" extension in the initial ClientHello, the + *# server MUST respond with a HelloRetryRequest (Section 4.1.4) message. + **/ struct s2n_ecc_evp_params *client_ecc_params = &conn->kex_params.client_ecc_evp_params; struct s2n_kem_group_params *client_pq_params = &conn->kex_params.client_kem_group_params; if (!client_pq_params->kem_group && !client_ecc_params->negotiated_curve) { diff --git a/contrib/restricted/aws/s2n/tls/extensions/s2n_extension_list.c b/contrib/restricted/aws/s2n/tls/extensions/s2n_extension_list.c index 25000e56a9..64b691abc3 100644 --- a/contrib/restricted/aws/s2n/tls/extensions/s2n_extension_list.c +++ b/contrib/restricted/aws/s2n/tls/extensions/s2n_extension_list.c @@ -24,8 +24,6 @@ #define s2n_parsed_extension_is_empty(parsed_extension) ((parsed_extension)->extension.data == NULL) -static const s2n_parsed_extension empty_parsed_extensions[S2N_PARSED_EXTENSIONS_COUNT] = { 0 }; - int s2n_extension_list_send(s2n_extension_list_id list_type, struct s2n_connection *conn, struct s2n_stuffer *out) { s2n_extension_type_list *extension_type_list; @@ -50,23 +48,27 @@ int s2n_extension_list_recv(s2n_extension_list_id list_type, struct s2n_connecti return S2N_SUCCESS; } -static int s2n_extension_process_impl(const s2n_extension_type *extension_type, s2n_extension_type_id extension_id, - struct s2n_connection *conn, s2n_parsed_extension *parsed_extensions) +static int s2n_extension_process_impl(const s2n_extension_type *extension_type, + struct s2n_connection *conn, s2n_parsed_extension *parsed_extension) { POSIX_ENSURE_REF(extension_type); - POSIX_ENSURE_REF(parsed_extensions); + POSIX_ENSURE_REF(parsed_extension); + + if (parsed_extension->processed) { + return S2N_SUCCESS; + } - if (s2n_parsed_extension_is_empty(&parsed_extensions[extension_id])) { + if (s2n_parsed_extension_is_empty(parsed_extension)) { POSIX_GUARD(s2n_extension_is_missing(extension_type, conn)); return S2N_SUCCESS; } - POSIX_ENSURE(parsed_extensions[extension_id].extension_type == extension_type->iana_value, + POSIX_ENSURE(parsed_extension->extension_type == extension_type->iana_value, S2N_ERR_INVALID_PARSED_EXTENSIONS); - struct s2n_stuffer extension_stuffer; - POSIX_GUARD(s2n_stuffer_init(&extension_stuffer, &parsed_extensions[extension_id].extension)); - POSIX_GUARD(s2n_stuffer_skip_write(&extension_stuffer, parsed_extensions[extension_id].extension.size)); + struct s2n_stuffer extension_stuffer = { 0 }; + POSIX_GUARD(s2n_stuffer_init(&extension_stuffer, &parsed_extension->extension)); + POSIX_GUARD(s2n_stuffer_skip_write(&extension_stuffer, parsed_extension->extension.size)); POSIX_GUARD(s2n_extension_recv(extension_type, conn, &extension_stuffer)); @@ -82,13 +84,10 @@ int s2n_extension_process(const s2n_extension_type *extension_type, struct s2n_c s2n_extension_type_id extension_id; POSIX_GUARD(s2n_extension_supported_iana_value_to_id(extension_type->iana_value, &extension_id)); - int result = s2n_extension_process_impl(extension_type, extension_id, conn, parsed_extension_list->parsed_extensions); - - /* Wipe parsed_extension. - * We can check for unprocessed extensions later by checking for non-blank parsed_extensions. */ - parsed_extension_list->parsed_extensions[extension_id] = empty_parsed_extensions[0]; - - return result; + s2n_parsed_extension *parsed_extension = &parsed_extension_list->parsed_extensions[extension_id]; + POSIX_GUARD(s2n_extension_process_impl(extension_type, conn, parsed_extension)); + parsed_extension->processed = true; + return S2N_SUCCESS; } int s2n_extension_list_process(s2n_extension_list_id list_type, struct s2n_connection *conn, @@ -104,17 +103,17 @@ int s2n_extension_list_process(s2n_extension_list_id list_type, struct s2n_conne conn, parsed_extension_list)); } - /* If parsed_extension_list.parsed_extensions is not completely wiped at this point, - * then we have received an extension not allowed on this message type. - * - * According to the RFC, we should alert and close the connection. - * From https://tools.ietf.org/html/rfc8446#section-4.2: - * If an implementation receives an extension which it recognizes and which is not - * specified for the message in which it appears, it MUST abort the handshake with an - * "illegal_parameter" alert. + /** + *= https://tools.ietf.org/rfc/rfc8446#section-4.2 + *= type=exception + *= reason=Incorrect implementations exist in the wild. Ignoring instead. + *# If an implementation receives an extension + *# which it recognizes and which is not specified for the message in + *# which it appears, it MUST abort the handshake with an + *# "illegal_parameter" alert. * - * However, to be more tolerant of non-compliant peers, we will just ignore and not - * process the illegal extensions, treating them as if they are unsupported. + * If we want to enforce this restriction in the future, we can verify + * that no parsed extensions exist without the "processed" flag set. */ return S2N_SUCCESS; diff --git a/contrib/restricted/aws/s2n/tls/extensions/s2n_extension_list.h b/contrib/restricted/aws/s2n/tls/extensions/s2n_extension_list.h index a26eb3a89c..00245dacec 100644 --- a/contrib/restricted/aws/s2n/tls/extensions/s2n_extension_list.h +++ b/contrib/restricted/aws/s2n/tls/extensions/s2n_extension_list.h @@ -24,6 +24,7 @@ typedef struct { uint16_t extension_type; struct s2n_blob extension; uint16_t wire_index; + unsigned processed:1; } s2n_parsed_extension; typedef struct { diff --git a/contrib/restricted/aws/s2n/tls/extensions/s2n_server_key_share.c b/contrib/restricted/aws/s2n/tls/extensions/s2n_server_key_share.c index 02fa59ac3e..2df2ccd171 100644 --- a/contrib/restricted/aws/s2n/tls/extensions/s2n_server_key_share.c +++ b/contrib/restricted/aws/s2n/tls/extensions/s2n_server_key_share.c @@ -238,7 +238,24 @@ static int s2n_server_key_share_recv_ecc(struct s2n_connection *conn, uint16_t n } struct s2n_ecc_evp_params *server_ecc_evp_params = &conn->kex_params.server_ecc_evp_params; - server_ecc_evp_params->negotiated_curve = ecc_pref->ecc_curves[supported_curve_index]; + const struct s2n_ecc_named_curve *negotiated_curve = ecc_pref->ecc_curves[supported_curve_index]; + + /** + *= https://tools.ietf.org/rfc/rfc8446#4.2.8 + *# If using (EC)DHE key establishment and a HelloRetryRequest containing a + *# "key_share" extension was received by the client, the client MUST + *# verify that the selected NamedGroup in the ServerHello is the same as + *# that in the HelloRetryRequest. If this check fails, the client MUST + *# abort the handshake with an "illegal_parameter" alert. + **/ + if (s2n_is_hello_retry_handshake(conn) && !s2n_is_hello_retry_message(conn)) { + POSIX_ENSURE_REF(server_ecc_evp_params->negotiated_curve); + const struct s2n_ecc_named_curve *previous_negotiated_curve = server_ecc_evp_params->negotiated_curve; + POSIX_ENSURE(negotiated_curve == previous_negotiated_curve, + S2N_ERR_BAD_MESSAGE); + } + + server_ecc_evp_params->negotiated_curve = negotiated_curve; /* If this is a HelloRetryRequest, we won't have a key share. We just have the selected group. * Set the server negotiated curve and exit early so a proper keyshare can be generated. */ diff --git a/contrib/restricted/aws/s2n/tls/s2n_client_hello.c b/contrib/restricted/aws/s2n/tls/s2n_client_hello.c index 0553d1fe14..32506f495d 100644 --- a/contrib/restricted/aws/s2n/tls/s2n_client_hello.c +++ b/contrib/restricted/aws/s2n/tls/s2n_client_hello.c @@ -405,11 +405,7 @@ int s2n_process_client_hello(struct s2n_connection *conn) conn->actual_protocol_version = MIN(conn->server_protocol_version, S2N_TLS12); } - /* s2n_extension_list_process clears the parsed extensions as it processes them. - * To keep the version in client_hello intact for the extension retrieval APIs, process a copy instead. - */ - s2n_parsed_extensions_list copy_of_parsed_extensions = conn->client_hello.extensions; - POSIX_GUARD(s2n_extension_list_process(S2N_EXTENSION_LIST_CLIENT_HELLO, conn, ©_of_parsed_extensions)); + POSIX_GUARD(s2n_extension_list_process(S2N_EXTENSION_LIST_CLIENT_HELLO, conn, &conn->client_hello.extensions)); /* After parsing extensions, select a curve and corresponding keyshare to use */ if (conn->actual_protocol_version >= S2N_TLS13) { diff --git a/contrib/restricted/aws/s2n/tls/s2n_config.c b/contrib/restricted/aws/s2n/tls/s2n_config.c index 7c455bb642..dd8e06b0ad 100644 --- a/contrib/restricted/aws/s2n/tls/s2n_config.c +++ b/contrib/restricted/aws/s2n/tls/s2n_config.c @@ -314,15 +314,27 @@ int s2n_config_free_session_ticket_keys(struct s2n_config *config) int s2n_config_free_cert_chain_and_key(struct s2n_config *config) { + /* We track certificate ownership on the config itself because a config + * CANNOT use a combination of owned and unowned chains. + * If it does, and the unowned chains are freed before the config is, + * then iterating over the chains to determine which are owned and need to be freed + * will mean also reading the invalid, freed memory of any unowned certificates. + * As of now, some tests free chains before the config, so that pattern may also + * appear in application code. + */ + if (config->cert_ownership != S2N_LIB_OWNED) { + return S2N_SUCCESS; + } + /* Free the cert_chain_and_key since the application has no reference * to it. This is necessary until s2n_config_add_cert_chain_and_key is deprecated. */ - if (config->cert_allocated) { - for (int i = 0; i < S2N_CERT_TYPE_COUNT; i++) { - s2n_cert_chain_and_key_free(config->default_certs_by_type.certs[i]); - } + for (int i = 0; i < S2N_CERT_TYPE_COUNT; i++) { + s2n_cert_chain_and_key_free(config->default_certs_by_type.certs[i]); + config->default_certs_by_type.certs[i] = NULL; } - return 0; + config->cert_ownership = S2N_NOT_OWNED; + return S2N_SUCCESS; } int s2n_config_free_dhparams(struct s2n_config *config) @@ -466,19 +478,7 @@ int s2n_config_set_verification_ca_location(struct s2n_config *config, const cha return err_code; } -/* Deprecated. Superseded by s2n_config_add_cert_chain_and_key_to_store */ -int s2n_config_add_cert_chain_and_key(struct s2n_config *config, const char *cert_chain_pem, const char *private_key_pem) -{ - struct s2n_cert_chain_and_key *chain_and_key; - POSIX_ENSURE_REF(chain_and_key = s2n_cert_chain_and_key_new()); - POSIX_GUARD(s2n_cert_chain_and_key_load_pem(chain_and_key, cert_chain_pem, private_key_pem)); - POSIX_GUARD(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); - config->cert_allocated = 1; - - return 0; -} - -int s2n_config_add_cert_chain_and_key_to_store(struct s2n_config *config, struct s2n_cert_chain_and_key *cert_key_pair) +static int s2n_config_add_cert_chain_and_key_impl(struct s2n_config *config, struct s2n_cert_chain_and_key *cert_key_pair) { POSIX_ENSURE_REF(config->domain_name_to_cert_map); POSIX_ENSURE_REF(cert_key_pair); @@ -491,6 +491,13 @@ int s2n_config_add_cert_chain_and_key_to_store(struct s2n_config *config, struct * default, etc. */ if (config->default_certs_by_type.certs[cert_type] == NULL) { config->default_certs_by_type.certs[cert_type] = cert_key_pair; + } else { + /* Because library-owned certificates are tracked and cleaned up via the + * default_certs_by_type mapping, library-owned chains MUST be set as the default + * to avoid a memory leak. If they're not the default, they're not freed. + */ + POSIX_ENSURE(config->cert_ownership != S2N_LIB_OWNED, + S2N_ERR_MULTIPLE_DEFAULT_CERTIFICATES_PER_AUTH_TYPE); } } @@ -501,6 +508,35 @@ int s2n_config_add_cert_chain_and_key_to_store(struct s2n_config *config, struct return S2N_SUCCESS; } +/* Deprecated. Superseded by s2n_config_add_cert_chain_and_key_to_store */ +int s2n_config_add_cert_chain_and_key(struct s2n_config *config, const char *cert_chain_pem, const char *private_key_pem) +{ + POSIX_ENSURE_REF(config); + POSIX_ENSURE(config->cert_ownership != S2N_APP_OWNED, S2N_ERR_CERT_OWNERSHIP); + + DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = s2n_cert_chain_and_key_new(), + s2n_cert_chain_and_key_ptr_free); + POSIX_ENSURE_REF(chain_and_key); + POSIX_GUARD(s2n_cert_chain_and_key_load_pem(chain_and_key, cert_chain_pem, private_key_pem)); + POSIX_GUARD(s2n_config_add_cert_chain_and_key_impl(config, chain_and_key)); + config->cert_ownership = S2N_LIB_OWNED; + + ZERO_TO_DISABLE_DEFER_CLEANUP(chain_and_key); + return S2N_SUCCESS; +} + +int s2n_config_add_cert_chain_and_key_to_store(struct s2n_config *config, struct s2n_cert_chain_and_key *cert_key_pair) +{ + POSIX_ENSURE_REF(config); + POSIX_ENSURE(config->cert_ownership != S2N_LIB_OWNED, S2N_ERR_CERT_OWNERSHIP); + + POSIX_ENSURE_REF(cert_key_pair); + POSIX_GUARD(s2n_config_add_cert_chain_and_key_impl(config, cert_key_pair)); + config->cert_ownership = S2N_APP_OWNED; + + return S2N_SUCCESS; +} + int s2n_config_set_async_pkey_callback(struct s2n_config *config, s2n_async_pkey_fn fn) { POSIX_ENSURE_REF(config); @@ -510,12 +546,19 @@ int s2n_config_set_async_pkey_callback(struct s2n_config *config, s2n_async_pkey return S2N_SUCCESS; } -int s2n_config_clear_default_certificates(struct s2n_config *config) +static int s2n_config_clear_default_certificates(struct s2n_config *config) { POSIX_ENSURE_REF(config); + + /* Clearing library-owned chains would lead to a memory leak. + * See s2n_config_free_cert_chain_and_key. + */ + POSIX_ENSURE(config->cert_ownership != S2N_LIB_OWNED, S2N_ERR_CERT_OWNERSHIP); + for (int i = 0; i < S2N_CERT_TYPE_COUNT; i++) { config->default_certs_by_type.certs[i] = NULL; } + config->cert_ownership = S2N_NOT_OWNED; return 0; } @@ -528,6 +571,11 @@ int s2n_config_set_cert_chain_and_key_defaults(struct s2n_config *config, S2N_ERROR_IF(num_cert_key_pairs < 1 || num_cert_key_pairs > S2N_CERT_TYPE_COUNT, S2N_ERR_NUM_DEFAULT_CERTIFICATES); + /* This method will set application-owned chains, so we must not already be using + * any library owned chains. See s2n_config_free_cert_chain_and_key. + */ + POSIX_ENSURE(config->cert_ownership != S2N_LIB_OWNED, S2N_ERR_CERT_OWNERSHIP); + /* Validate certs being set before clearing auto-chosen defaults or previously set defaults */ struct certs_by_type new_defaults = {{ 0 }}; for (uint32_t i = 0; i < num_cert_key_pairs; i++) { @@ -545,6 +593,7 @@ int s2n_config_set_cert_chain_and_key_defaults(struct s2n_config *config, } config->default_certs_are_explicit = 1; + config->cert_ownership = S2N_APP_OWNED; return 0; } @@ -639,16 +688,15 @@ int s2n_config_set_extension_data(struct s2n_config *config, s2n_tls_extension_t } struct s2n_cert_chain_and_key *config_chain_and_key = s2n_config_get_single_default_cert(config); POSIX_ENSURE_REF(config_chain_and_key); + POSIX_ENSURE(config->cert_ownership == S2N_LIB_OWNED, S2N_ERR_CERT_OWNERSHIP); switch (type) { case S2N_EXTENSION_CERTIFICATE_TRANSPARENCY: - { - POSIX_GUARD(s2n_cert_chain_and_key_set_sct_list(config_chain_and_key, data, length)); - } break; + POSIX_GUARD(s2n_cert_chain_and_key_set_sct_list(config_chain_and_key, data, length)); + break; case S2N_EXTENSION_OCSP_STAPLING: - { - POSIX_GUARD(s2n_cert_chain_and_key_set_ocsp_data(config_chain_and_key, data, length)); - } break; + POSIX_GUARD(s2n_cert_chain_and_key_set_ocsp_data(config_chain_and_key, data, length)); + break; default: POSIX_BAIL(S2N_ERR_UNRECOGNIZED_EXTENSION); } diff --git a/contrib/restricted/aws/s2n/tls/s2n_config.h b/contrib/restricted/aws/s2n/tls/s2n_config.h index c0068ce133..cfc7474c04 100644 --- a/contrib/restricted/aws/s2n/tls/s2n_config.h +++ b/contrib/restricted/aws/s2n/tls/s2n_config.h @@ -29,6 +29,12 @@ struct s2n_cipher_preferences; +typedef enum { + S2N_NOT_OWNED = 0, + S2N_APP_OWNED, + S2N_LIB_OWNED, +} s2n_cert_ownership; + struct s2n_config { unsigned use_tickets:1; @@ -36,7 +42,6 @@ struct s2n_config { * See s2n_quic_support.h */ unsigned quic_enabled:1; - unsigned cert_allocated:1; unsigned default_certs_are_explicit:1; unsigned use_session_cache:1; /* if this is FALSE, server will ignore client's Maximum Fragment Length request */ @@ -142,6 +147,8 @@ struct s2n_config { /* The user defined context associated with config */ void *context; + + s2n_cert_ownership cert_ownership; }; S2N_CLEANUP_RESULT s2n_config_ptr_free(struct s2n_config **config); diff --git a/contrib/restricted/aws/s2n/tls/s2n_key_update.c b/contrib/restricted/aws/s2n/tls/s2n_key_update.c index 86e933a1ac..3c862eb98e 100644 --- a/contrib/restricted/aws/s2n/tls/s2n_key_update.c +++ b/contrib/restricted/aws/s2n/tls/s2n_key_update.c @@ -107,10 +107,20 @@ int s2n_check_record_limit(struct s2n_connection *conn, struct s2n_blob *sequenc POSIX_ENSURE_REF(conn->secure.cipher_suite); POSIX_ENSURE_REF(conn->secure.cipher_suite->record_alg); - uint64_t output = 0; - POSIX_GUARD(s2n_sequence_number_to_uint64(sequence_number, &output)); - - if (output + 1 > conn->secure.cipher_suite->record_alg->encryption_limit) { + /* + * This is the sequence number that will be used for the next record, + * because we incremented the sequence number after sending the last record. + */ + uint64_t next_seq_num = 0; + POSIX_GUARD(s2n_sequence_number_to_uint64(sequence_number, &next_seq_num)); + + /* + * If the next record is the last record we can send, then the next record needs + * to contain a KeyUpdate message. + * + * This should always trigger on "==", but we use ">=" just in case. + */ + if (next_seq_num >= conn->secure.cipher_suite->record_alg->encryption_limit) { conn->key_update_pending = true; } diff --git a/contrib/restricted/aws/s2n/tls/s2n_server_hello_retry.c b/contrib/restricted/aws/s2n/tls/s2n_server_hello_retry.c index ed80865998..4fbf2ca6a6 100644 --- a/contrib/restricted/aws/s2n/tls/s2n_server_hello_retry.c +++ b/contrib/restricted/aws/s2n/tls/s2n_server_hello_retry.c @@ -68,20 +68,33 @@ int s2n_server_hello_retry_recv(struct s2n_connection *conn) POSIX_GUARD(s2n_connection_get_kem_preferences(conn, &kem_pref)); POSIX_ENSURE_REF(kem_pref); - /* Upon receipt of the HelloRetryRequest, the client MUST verify that: - * (1) the selected_group field corresponds to a group - * which was provided in the "supported_groups" extension in the - * original ClientHello and - * (2) the selected_group field does not correspond to a group which was provided - * in the "key_share" extension in the original ClientHello. - * If either of these checks fails, then the client MUST abort the handshake. */ - const struct s2n_ecc_named_curve *named_curve = conn->kex_params.server_ecc_evp_params.negotiated_curve; const struct s2n_kem_group *kem_group = conn->kex_params.server_kem_group_params.kem_group; /* Boolean XOR check: exactly one of {named_curve, kem_group} should be non-null. */ POSIX_ENSURE( (named_curve != NULL) != (kem_group != NULL), S2N_ERR_INVALID_HELLO_RETRY); + /** + *= https://tools.ietf.org/rfc/rfc8446#4.2.8 + *# Upon receipt of this extension in a HelloRetryRequest, the client + *# MUST verify that (1) the selected_group field corresponds to a group + *# which was provided in the "supported_groups" extension in the + *# original ClientHello + **/ + bool selected_group_in_supported_groups = false; + if (named_curve != NULL && s2n_ecc_preferences_includes_curve(ecc_pref, named_curve->iana_id)) { + selected_group_in_supported_groups = true; + } + if (kem_group != NULL && s2n_kem_preferences_includes_tls13_kem_group(kem_pref, kem_group->iana_id)) { + selected_group_in_supported_groups = true; + } + + /** + *= https://tools.ietf.org/rfc/rfc8446#4.2.8 + *# and (2) the selected_group field does not + *# correspond to a group which was provided in the "key_share" extension + *# in the original ClientHello. + **/ bool new_key_share_requested = false; if (named_curve != NULL) { new_key_share_requested = (named_curve != conn->kex_params.client_ecc_evp_params.negotiated_curve); @@ -93,14 +106,19 @@ int s2n_server_hello_retry_recv(struct s2n_connection *conn) new_key_share_requested = (kem_group != conn->kex_params.client_kem_group_params.kem_group); } - /* + /** + *= https://tools.ietf.org/rfc/rfc8446#4.2.8 + *# If either of these checks fails, then + *# the client MUST abort the handshake with an "illegal_parameter" + *# alert. + * *= https://tools.ietf.org/rfc/rfc8446#section-4.1.4 *# Clients MUST abort the handshake with an *# "illegal_parameter" alert if the HelloRetryRequest would not result *# in any change in the ClientHello. - */ - POSIX_ENSURE((conn->early_data_state == S2N_EARLY_DATA_REJECTED) || new_key_share_requested, - S2N_ERR_INVALID_HELLO_RETRY); + **/ + POSIX_ENSURE(new_key_share_requested, S2N_ERR_INVALID_HELLO_RETRY); + POSIX_ENSURE(selected_group_in_supported_groups, S2N_ERR_INVALID_HELLO_RETRY); /* Update transcript hash */ POSIX_GUARD(s2n_server_hello_retry_recreate_transcript(conn)); diff --git a/contrib/restricted/aws/s2n/utils/s2n_array.c b/contrib/restricted/aws/s2n/utils/s2n_array.c index 0ac2207e9a..0a7f185fc2 100644 --- a/contrib/restricted/aws/s2n/utils/s2n_array.c +++ b/contrib/restricted/aws/s2n/utils/s2n_array.c @@ -175,7 +175,7 @@ S2N_RESULT s2n_array_capacity(struct s2n_array *array, uint32_t *capacity) return S2N_RESULT_OK; } -S2N_RESULT s2n_array_free_p(struct s2n_array **parray) +S2N_CLEANUP_RESULT s2n_array_free_p(struct s2n_array **parray) { RESULT_ENSURE_REF(parray); struct s2n_array *array = *parray; diff --git a/contrib/restricted/aws/s2n/utils/s2n_array.h b/contrib/restricted/aws/s2n/utils/s2n_array.h index 3e846ce4fd..a8f81325ee 100644 --- a/contrib/restricted/aws/s2n/utils/s2n_array.h +++ b/contrib/restricted/aws/s2n/utils/s2n_array.h @@ -41,5 +41,5 @@ extern S2N_RESULT s2n_array_insert_and_copy(struct s2n_array *array, uint32_t id extern S2N_RESULT s2n_array_num_elements(struct s2n_array *array, uint32_t *len); extern S2N_RESULT s2n_array_capacity(struct s2n_array *array, uint32_t *capacity); extern S2N_RESULT s2n_array_remove(struct s2n_array *array, uint32_t idx); -extern S2N_RESULT s2n_array_free_p(struct s2n_array **parray); +extern S2N_CLEANUP_RESULT s2n_array_free_p(struct s2n_array **parray); extern S2N_RESULT s2n_array_free(struct s2n_array *array); |