aboutsummaryrefslogtreecommitdiffstats
path: root/contrib/restricted/aws/s2n/tls
diff options
context:
space:
mode:
authorrobot-contrib <robot-contrib@yandex-team.com>2023-03-02 10:05:41 +0300
committerrobot-contrib <robot-contrib@yandex-team.com>2023-03-02 10:05:41 +0300
commitc8b3fb9dab5150585defd576d83647a12a84b990 (patch)
tree918b7d8f1a373e70555914fd15aa4e0a8cc09940 /contrib/restricted/aws/s2n/tls
parent093b04166b8440db82cf0590a4beb6c5e8cd5668 (diff)
downloadydb-c8b3fb9dab5150585defd576d83647a12a84b990.tar.gz
Update contrib/restricted/aws/s2n to 1.3.37
Diffstat (limited to 'contrib/restricted/aws/s2n/tls')
-rw-r--r--contrib/restricted/aws/s2n/tls/s2n_client_hello.c18
-rw-r--r--contrib/restricted/aws/s2n/tls/s2n_client_hello.h10
-rw-r--r--contrib/restricted/aws/s2n/tls/s2n_fingerprint.c322
-rw-r--r--contrib/restricted/aws/s2n/tls/s2n_record_read.c30
4 files changed, 376 insertions, 4 deletions
diff --git a/contrib/restricted/aws/s2n/tls/s2n_client_hello.c b/contrib/restricted/aws/s2n/tls/s2n_client_hello.c
index c4b67f85eb..f185292ea0 100644
--- a/contrib/restricted/aws/s2n/tls/s2n_client_hello.c
+++ b/contrib/restricted/aws/s2n/tls/s2n_client_hello.c
@@ -652,10 +652,24 @@ int s2n_client_hello_send(struct s2n_connection *conn)
return S2N_SUCCESS;
}
-/* See http://www-archive.mozilla.org/projects/security/pki/nss/ssl/draft02.html 2.5 */
+/*
+ * s2n-tls does NOT support SSLv2. However, it does support SSLv2 ClientHellos.
+ * Clients may send SSLv2 ClientHellos advertising higher protocol versions for
+ * backwards compatibility reasons. See https://tools.ietf.org/rfc/rfc2246 Appendix E.
+ *
+ * In this case, conn->client_hello_version will be SSLv2, but conn->client_protocol_version
+ * will likely be higher.
+ *
+ * See http://www-archive.mozilla.org/projects/security/pki/nss/ssl/draft02.html Section 2.5
+ * for a description of the expected SSLv2 format.
+ * Alternatively, the TLS1.0 RFC includes a more modern description of the format:
+ * https://tools.ietf.org/rfc/rfc2246 Appendix E.1
+ */
int s2n_sslv2_client_hello_recv(struct s2n_connection *conn)
{
struct s2n_client_hello *client_hello = &conn->client_hello;
+ client_hello->sslv2 = true;
+
struct s2n_stuffer in_stuffer = { 0 };
POSIX_GUARD(s2n_stuffer_init(&in_stuffer, &client_hello->raw_message));
POSIX_GUARD(s2n_stuffer_skip_write(&in_stuffer, client_hello->raw_message.size));
@@ -713,7 +727,7 @@ int s2n_sslv2_client_hello_recv(struct s2n_connection *conn)
return 0;
}
-static int s2n_client_hello_get_parsed_extension(s2n_tls_extension_type extension_type,
+int s2n_client_hello_get_parsed_extension(s2n_tls_extension_type extension_type,
s2n_parsed_extensions_list *parsed_extension_list, s2n_parsed_extension **parsed_extension)
{
POSIX_ENSURE_REF(parsed_extension_list);
diff --git a/contrib/restricted/aws/s2n/tls/s2n_client_hello.h b/contrib/restricted/aws/s2n/tls/s2n_client_hello.h
index cef7f8175d..d7eaa749b8 100644
--- a/contrib/restricted/aws/s2n/tls/s2n_client_hello.h
+++ b/contrib/restricted/aws/s2n/tls/s2n_client_hello.h
@@ -43,6 +43,14 @@ struct s2n_client_hello {
* issues a hello retry.
*/
unsigned int parsed : 1;
+
+ /*
+ * SSLv2 ClientHellos have a different format.
+ * Cipher suites are each three bytes instead of two.
+ * And due to how s2n-tls parses the record,
+ * the raw_message will not contain the protocol version.
+ */
+ unsigned int sslv2 : 1;
};
int s2n_client_hello_free(struct s2n_client_hello *client_hello);
@@ -55,5 +63,7 @@ ssize_t s2n_client_hello_get_raw_message(struct s2n_client_hello *ch, uint8_t *o
ssize_t s2n_client_hello_get_cipher_suites_length(struct s2n_client_hello *ch);
ssize_t s2n_client_hello_get_cipher_suites(struct s2n_client_hello *ch, uint8_t *out, uint32_t max_length);
+int s2n_client_hello_get_parsed_extension(s2n_tls_extension_type extension_type,
+ s2n_parsed_extensions_list *parsed_extension_list, s2n_parsed_extension **parsed_extension);
ssize_t s2n_client_hello_get_extensions_length(struct s2n_client_hello *ch);
ssize_t s2n_client_hello_get_extensions(struct s2n_client_hello *ch, uint8_t *out, uint32_t max_length);
diff --git a/contrib/restricted/aws/s2n/tls/s2n_fingerprint.c b/contrib/restricted/aws/s2n/tls/s2n_fingerprint.c
new file mode 100644
index 0000000000..db21d6ca6f
--- /dev/null
+++ b/contrib/restricted/aws/s2n/tls/s2n_fingerprint.c
@@ -0,0 +1,322 @@
+/*
+ * 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 "api/unstable/fingerprint.h"
+#include "crypto/s2n_fips.h"
+#include "crypto/s2n_hash.h"
+#include "stuffer/s2n_stuffer.h"
+#include "tls/extensions/s2n_extension_list.h"
+#include "tls/s2n_client_hello.h"
+#include "tls/s2n_crypto_constants.h"
+#include "utils/s2n_blob.h"
+#include "utils/s2n_result.h"
+#include "utils/s2n_safety.h"
+
+#define S2N_JA3_FIELD_DIV ','
+#define S2N_JA3_LIST_DIV '-'
+
+/* UINT16_MAX == 65535 */
+#define S2N_UINT16_STR_MAX_SIZE 5
+
+/* See https://datatracker.ietf.org/doc/html/rfc8701
+ * for an explanation of GREASE and lists of the GREASE values.
+ */
+static S2N_RESULT s2n_assert_grease_value(uint16_t val)
+{
+ uint8_t byte1 = val >> 8;
+ uint8_t byte2 = val & 0x00FF;
+ /* Both bytes of the GREASE values are identical */
+ RESULT_ENSURE_EQ(byte1, byte2);
+ /* The GREASE value bytes all follow the format 0x[0-F]A.
+ * So 0x0A, 0x1A, 0x2A etc, up to 0xFA. */
+ RESULT_ENSURE_EQ((byte1 | 0xF0), 0xFA);
+ return S2N_RESULT_OK;
+}
+
+static bool s2n_is_grease_value(uint16_t val)
+{
+ return s2n_result_is_ok(s2n_assert_grease_value(val));
+}
+
+static S2N_RESULT s2n_fingerprint_hash_flush(struct s2n_hash_state *hash, struct s2n_stuffer *in)
+{
+ if (hash == NULL) {
+ /* If the buffer is full and needs to be flushed, but no hash was provided,
+ * then we have insufficient memory to complete the fingerprint.
+ *
+ * The application will need to provide a larger buffer.
+ */
+ RESULT_BAIL(S2N_ERR_INSUFFICIENT_MEM_SIZE);
+ }
+
+ uint32_t hash_data_len = s2n_stuffer_data_available(in);
+ uint8_t *hash_data = s2n_stuffer_raw_read(in, hash_data_len);
+ RESULT_ENSURE_REF(hash_data);
+ RESULT_GUARD_POSIX(s2n_hash_update(hash, hash_data, hash_data_len));
+ RESULT_GUARD_POSIX(s2n_stuffer_wipe(in));
+ return S2N_RESULT_OK;
+}
+
+static S2N_RESULT s2n_fingerprint_write_char(struct s2n_stuffer *stuffer,
+ char c, struct s2n_hash_state *hash)
+{
+ if (s2n_stuffer_space_remaining(stuffer) < 1) {
+ RESULT_GUARD(s2n_fingerprint_hash_flush(hash, stuffer));
+ }
+ RESULT_GUARD_POSIX(s2n_stuffer_write_char(stuffer, c));
+ return S2N_RESULT_OK;
+}
+
+static S2N_RESULT s2n_fingerprint_write_entry(struct s2n_stuffer *stuffer,
+ bool *is_list, uint16_t value, struct s2n_hash_state *hash)
+{
+ /* If we have already written at least one value for this field,
+ * then we are writing a list and need to prepend a list divider before
+ * writing the next value.
+ */
+ RESULT_ENSURE_REF(is_list);
+ if (*is_list) {
+ RESULT_GUARD(s2n_fingerprint_write_char(stuffer, S2N_JA3_LIST_DIV, hash));
+ }
+ *is_list = true;
+
+ /* snprintf always appends a '\0' to the output,
+ * but that extra '\0' is not included in the return value */
+ uint8_t entry[S2N_UINT16_STR_MAX_SIZE + 1] = { 0 };
+ int written = snprintf((char *) entry, sizeof(entry), "%u", value);
+ RESULT_ENSURE_GT(written, 0);
+ RESULT_ENSURE_LTE(written, S2N_UINT16_STR_MAX_SIZE);
+
+ if (s2n_stuffer_space_remaining(stuffer) < written) {
+ RESULT_GUARD(s2n_fingerprint_hash_flush(hash, stuffer));
+ }
+ RESULT_GUARD_POSIX(s2n_stuffer_write_bytes(stuffer, entry, written));
+
+ return S2N_RESULT_OK;
+}
+
+static S2N_RESULT s2n_fingerprint_write_version(struct s2n_client_hello *ch,
+ struct s2n_stuffer *output, struct s2n_hash_state *hash)
+{
+ RESULT_ENSURE_REF(ch);
+ bool is_list = false;
+ uint16_t version = 0;
+ struct s2n_stuffer message = { 0 };
+ RESULT_GUARD_POSIX(s2n_stuffer_init_written(&message, &ch->raw_message));
+ RESULT_GUARD_POSIX(s2n_stuffer_read_uint16(&message, &version));
+ RESULT_GUARD(s2n_fingerprint_write_entry(output, &is_list, version, hash));
+ return S2N_RESULT_OK;
+}
+
+static S2N_RESULT s2n_fingerprint_write_ciphers(struct s2n_client_hello *ch,
+ struct s2n_stuffer *output, struct s2n_hash_state *hash)
+{
+ RESULT_ENSURE_REF(ch);
+
+ bool cipher_found = false;
+ struct s2n_stuffer ciphers = { 0 };
+ RESULT_GUARD_POSIX(s2n_stuffer_init_written(&ciphers, &ch->cipher_suites));
+ while (s2n_stuffer_data_available(&ciphers)) {
+ uint16_t cipher = 0;
+ RESULT_GUARD_POSIX(s2n_stuffer_read_uint16(&ciphers, &cipher));
+ if (s2n_is_grease_value(cipher)) {
+ continue;
+ }
+ RESULT_GUARD(s2n_fingerprint_write_entry(output, &cipher_found, cipher, hash));
+ }
+ return S2N_RESULT_OK;
+}
+
+static S2N_RESULT s2n_fingerprint_write_extensions(struct s2n_client_hello *ch,
+ struct s2n_stuffer *output, struct s2n_hash_state *hash)
+{
+ RESULT_ENSURE_REF(ch);
+
+ /* We have to use the raw extensions instead of the parsed extensions
+ * because s2n-tls both intentionally ignores any unknown extensions
+ * and reorders the extensions when parsing the list.
+ */
+ struct s2n_stuffer extensions = { 0 };
+ RESULT_GUARD_POSIX(s2n_stuffer_init_written(&extensions, &ch->extensions.raw));
+
+ bool extension_found = false;
+ while (s2n_stuffer_data_available(&extensions)) {
+ uint16_t extension = 0, extension_size = 0;
+ RESULT_GUARD_POSIX(s2n_stuffer_read_uint16(&extensions, &extension));
+ RESULT_GUARD_POSIX(s2n_stuffer_read_uint16(&extensions, &extension_size));
+ RESULT_GUARD_POSIX(s2n_stuffer_skip_read(&extensions, extension_size));
+ if (s2n_is_grease_value(extension)) {
+ continue;
+ }
+ RESULT_GUARD(s2n_fingerprint_write_entry(output, &extension_found, extension, hash));
+ }
+ return S2N_RESULT_OK;
+}
+
+static S2N_RESULT s2n_fingerprint_write_elliptic_curves(struct s2n_client_hello *ch,
+ struct s2n_stuffer *output, struct s2n_hash_state *hash)
+{
+ RESULT_ENSURE_REF(ch);
+
+ s2n_parsed_extension *elliptic_curves_extension = NULL;
+ int result = s2n_client_hello_get_parsed_extension(S2N_EXTENSION_SUPPORTED_GROUPS,
+ &ch->extensions, &elliptic_curves_extension);
+ if (result != S2N_SUCCESS) {
+ return S2N_RESULT_OK;
+ }
+
+ struct s2n_stuffer elliptic_curves = { 0 };
+ RESULT_GUARD_POSIX(s2n_stuffer_init_written(&elliptic_curves,
+ &elliptic_curves_extension->extension));
+
+ uint16_t count = 0;
+ RESULT_GUARD_POSIX(s2n_stuffer_read_uint16(&elliptic_curves, &count));
+
+ bool curve_found = false;
+ while (s2n_stuffer_data_available(&elliptic_curves)) {
+ uint16_t curve = 0;
+ RESULT_GUARD_POSIX(s2n_stuffer_read_uint16(&elliptic_curves, &curve));
+ if (s2n_is_grease_value(curve)) {
+ continue;
+ }
+ RESULT_GUARD(s2n_fingerprint_write_entry(output, &curve_found, curve, hash));
+ }
+ return S2N_RESULT_OK;
+}
+
+static S2N_RESULT s2n_fingerprint_write_point_formats(struct s2n_client_hello *ch,
+ struct s2n_stuffer *output, struct s2n_hash_state *hash)
+{
+ RESULT_ENSURE_REF(ch);
+
+ s2n_parsed_extension *point_formats_extension = NULL;
+ int result = s2n_client_hello_get_parsed_extension(S2N_EXTENSION_EC_POINT_FORMATS,
+ &ch->extensions, &point_formats_extension);
+ if (result != S2N_SUCCESS) {
+ return S2N_RESULT_OK;
+ }
+
+ struct s2n_stuffer point_formats = { 0 };
+ RESULT_GUARD_POSIX(s2n_stuffer_init_written(&point_formats,
+ &point_formats_extension->extension));
+
+ uint8_t count = 0;
+ RESULT_GUARD_POSIX(s2n_stuffer_read_uint8(&point_formats, &count));
+
+ bool format_found = false;
+ while (s2n_stuffer_data_available(&point_formats)) {
+ uint8_t format = 0;
+ RESULT_GUARD_POSIX(s2n_stuffer_read_uint8(&point_formats, &format));
+ RESULT_GUARD(s2n_fingerprint_write_entry(output, &format_found, format, hash));
+ }
+ return S2N_RESULT_OK;
+}
+
+/* JA3 involves concatenating a set of fields from the ClientHello:
+ * SSLVersion,Cipher,SSLExtension,EllipticCurve,EllipticCurvePointFormat
+ * For example:
+ * "769,47-53-5-10-49161-49162-49171-49172-50-56-19-4,0-10-11,23-24-25,0"
+ * See https://github.com/salesforce/ja3
+ */
+static S2N_RESULT s2n_fingerprint_ja3(struct s2n_client_hello *ch,
+ struct s2n_stuffer *output, uint32_t *output_size, struct s2n_hash_state *hash)
+{
+ RESULT_ENSURE_REF(ch);
+ RESULT_ENSURE(!ch->sslv2, S2N_ERR_PROTOCOL_VERSION_UNSUPPORTED);
+
+ RESULT_GUARD(s2n_fingerprint_write_version(ch, output, hash));
+ RESULT_GUARD(s2n_fingerprint_write_char(output, S2N_JA3_FIELD_DIV, hash));
+ RESULT_GUARD(s2n_fingerprint_write_ciphers(ch, output, hash));
+ RESULT_GUARD(s2n_fingerprint_write_char(output, S2N_JA3_FIELD_DIV, hash));
+ RESULT_GUARD(s2n_fingerprint_write_extensions(ch, output, hash));
+ RESULT_GUARD(s2n_fingerprint_write_char(output, S2N_JA3_FIELD_DIV, hash));
+ RESULT_GUARD(s2n_fingerprint_write_elliptic_curves(ch, output, hash));
+ RESULT_GUARD(s2n_fingerprint_write_char(output, S2N_JA3_FIELD_DIV, hash));
+ RESULT_GUARD(s2n_fingerprint_write_point_formats(ch, output, hash));
+
+ return S2N_RESULT_OK;
+}
+
+int s2n_client_hello_get_fingerprint_hash(struct s2n_client_hello *ch, s2n_fingerprint_type type,
+ uint32_t max_hash_size, uint8_t *hash, uint32_t *hash_size, uint32_t *str_size)
+{
+ POSIX_ENSURE(type == S2N_FINGERPRINT_JA3, S2N_ERR_INVALID_ARGUMENT);
+ POSIX_ENSURE(max_hash_size >= MD5_DIGEST_LENGTH, S2N_ERR_INSUFFICIENT_MEM_SIZE);
+ POSIX_ENSURE_REF(hash);
+ POSIX_ENSURE_REF(hash_size);
+ POSIX_ENSURE_REF(str_size);
+ *hash_size = 0;
+ *str_size = 0;
+
+ /* The maximum size of the JA3 string is variable and could theoretically
+ * be extremely large. However, we don't need enough memory to hold the full
+ * string when calculating a hash. We can calculate and add the JA3 string
+ * to the hash in chunks, similarly to how the TLS transcript hash is
+ * calculated by adding handshake messages to the hash as they become
+ * available. After a chunk is added to the hash, the string buffer can be
+ * wiped and reused for the next chunk.
+ *
+ * The size of this buffer was chosen fairly arbitrarily.
+ */
+ uint8_t string_mem[50] = { 0 };
+ struct s2n_blob string_blob = { 0 };
+ struct s2n_stuffer string_stuffer = { 0 };
+ POSIX_GUARD(s2n_blob_init(&string_blob, string_mem, sizeof(string_mem)));
+ POSIX_GUARD(s2n_stuffer_init(&string_stuffer, &string_blob));
+
+ /* JA3 uses an MD5 hash.
+ * The hash doesn't have to be cryptographically secure,
+ * so the weakness of MD5 shouldn't be a problem.
+ */
+ DEFER_CLEANUP(struct s2n_hash_state md5_hash = { 0 }, s2n_hash_free);
+ POSIX_GUARD(s2n_hash_new(&md5_hash));
+ if (s2n_is_in_fips_mode()) {
+ /* This hash is unrelated to TLS and does not affect FIPS */
+ POSIX_GUARD(s2n_hash_allow_md5_for_fips(&md5_hash));
+ }
+ POSIX_GUARD(s2n_hash_init(&md5_hash, S2N_HASH_MD5));
+
+ POSIX_GUARD_RESULT(s2n_fingerprint_ja3(ch, &string_stuffer, hash_size, &md5_hash));
+ POSIX_GUARD_RESULT(s2n_fingerprint_hash_flush(&md5_hash, &string_stuffer));
+
+ uint64_t in_hash = 0;
+ POSIX_GUARD(s2n_hash_get_currently_in_hash_total(&md5_hash, &in_hash));
+ POSIX_ENSURE_LTE(in_hash, UINT32_MAX);
+ *str_size = in_hash;
+
+ POSIX_GUARD(s2n_hash_digest(&md5_hash, hash, MD5_DIGEST_LENGTH));
+ *hash_size = MD5_DIGEST_LENGTH;
+ return S2N_SUCCESS;
+}
+
+int s2n_client_hello_get_fingerprint_string(struct s2n_client_hello *ch, s2n_fingerprint_type type,
+ uint32_t max_size, uint8_t *output, uint32_t *output_size)
+{
+ POSIX_ENSURE(type == S2N_FINGERPRINT_JA3, S2N_ERR_INVALID_ARGUMENT);
+ POSIX_ENSURE(max_size > 0, S2N_ERR_INSUFFICIENT_MEM_SIZE);
+ POSIX_ENSURE_REF(output);
+ POSIX_ENSURE_REF(output_size);
+ *output_size = 0;
+
+ struct s2n_blob output_blob = { 0 };
+ struct s2n_stuffer output_stuffer = { 0 };
+ POSIX_GUARD(s2n_blob_init(&output_blob, output, max_size));
+ POSIX_GUARD(s2n_stuffer_init(&output_stuffer, &output_blob));
+
+ POSIX_GUARD_RESULT(s2n_fingerprint_ja3(ch, &output_stuffer, output_size, NULL));
+ *output_size = s2n_stuffer_data_available(&output_stuffer);
+
+ return S2N_SUCCESS;
+}
diff --git a/contrib/restricted/aws/s2n/tls/s2n_record_read.c b/contrib/restricted/aws/s2n/tls/s2n_record_read.c
index ef5811847f..552b6b2aae 100644
--- a/contrib/restricted/aws/s2n/tls/s2n_record_read.c
+++ b/contrib/restricted/aws/s2n/tls/s2n_record_read.c
@@ -40,13 +40,39 @@ int s2n_sslv2_record_header_parse(
POSIX_GUARD(s2n_stuffer_read_uint16(in, fragment_length));
- /* Adjust to account for the 3 bytes of payload data we consumed in the header */
+ /* The SSLv2 header is only a 2 byte record length (technically 3 bytes if
+ * padding is included, but s2n-tls assumes no padding).
+ * See https://www.ietf.org/archive/id/draft-hickman-netscape-ssl-00.txt.
+ *
+ * So by reading 5 bytes for a standard header we have also read the first
+ * 3 bytes of the record payload. s2n-tls only supports SSLv2 ClientHellos,
+ * so we assume that those 3 bytes are the first two fields of the
+ * SSLv2 ClientHello.
+ */
+
+ /* Because we already read 3 bytes of the record payload while trying to
+ * read a standard header, we need to adjust the length so that we only
+ * try to read the remainder of the record payload.
+ */
POSIX_ENSURE_GTE(*fragment_length, 3);
*fragment_length -= 3;
+ /*
+ * The first field of an SSLv2 ClientHello is the msg_type.
+ *
+ * This is always '1', matching the ClientHello msg_type used by later
+ * handshake messages.
+ */
POSIX_GUARD(s2n_stuffer_read_uint8(in, record_type));
- uint8_t protocol_version[S2N_TLS_PROTOCOL_VERSION_LEN];
+ /*
+ * The second field of an SSLv2 ClientHello is the version.
+ *
+ * The protocol version read here will likely not be SSLv2, since we only
+ * accept SSLv2 ClientHellos offering higher protocol versions.
+ * See s2n_sslv2_client_hello_recv.
+ */
+ uint8_t protocol_version[S2N_TLS_PROTOCOL_VERSION_LEN] = { 0 };
POSIX_GUARD(s2n_stuffer_read_bytes(in, protocol_version, S2N_TLS_PROTOCOL_VERSION_LEN));
*client_protocol_version = (protocol_version[0] * 10) + protocol_version[1];