Skip to content

Move client_hello_version from s2n_connection to s2n_client_hello #3819

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

Open
wants to merge 4 commits into
base: main
Choose a base branch
from

Conversation

lrstewart
Copy link
Contributor

Resolved issues:

resolves #3817 (comment)

Description of changes:

I'm moving s2n_connection.client_hello_version to s2n_client_hello.version. This will make it accessible from s2n_client_hello APIs, like the new fingerprinting APIs. It also removes the need for s2n_client_hello.sslv2.

I double-checked that we don't unexpectedly wipe s2n_client_hello anywhere, and found one location in s2n_parse_client_hello in the case of a retry. The specific test for that retry version check continued to pass after my change, but was no longer testing the right thing (it was now checking 0 vs TLS1.2 instead of SSLv2 vs TLS1.2). The check itself was deceptive / not quite right due to the behavior of SSLv2 vs other versions, so I rewrote it to be clearer and handle the wipe properly.

Testing:

Basically just a name change. Existing tests pass.

By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.

@github-actions github-actions bot added the s2n-core team label Feb 10, 2023
@lrstewart lrstewart changed the title Ja3 ch Move client_hello_version from s2n_connection to s2n_client_hello Feb 10, 2023
@lrstewart lrstewart marked this pull request as ready for review February 10, 2023 01:26
Copy link
Contributor

@harrisonkaiser harrisonkaiser left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I understand correctly, the SSLv2 check needs to come before we parse because of the different format format of the client hello for SSLv2.

@lrstewart
Copy link
Contributor Author

If I understand correctly, the SSLv2 check needs to come before we parse because of the different format format of the client hello for SSLv2.

Yes. The SSLv2 check can/should come before we parse because of the different format / parsing of SSLv2. But we can't check for any other versions at that point.

Comment on lines +116 to -118
uint8_t high = (ch->version / 10);
uint8_t low = (ch->version % 10);
uint16_t version = (high << 8) | low;

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));
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm torn on whether this is an improvement :\ Math is definitely faster, but the stuffer operations were probably more readable.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, true. The stuffers do seem more readable... Although we do this conversion in other places, so maybe it makes sense here. I could see it either way I guess.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm in favor of the more efficient math. But we may be able to maintain readability: If we do the conversion in other places does it make sense to turn it into a macro/function to make it more readable?

Copy link
Contributor Author

@lrstewart lrstewart Feb 23, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think part of my hesitation to make this change is that it actually still wouldn't work for sslv2. In sslv2, we'd need to know that the ClientHello was an sslv2 ClientHello, but the version we'd include in the fingerprint wouldn't be sslv2. It'd be conn->client_protocol_version / the version listed in the ClientHello: basically the version we'd read from the stuffer, if s2n-tls didn't weirdly drop the first few bytes of the sslv2 ClientHello.

Comment on lines +116 to -118
uint8_t high = (ch->version / 10);
uint8_t low = (ch->version % 10);
uint16_t version = (high << 8) | low;

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));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, true. The stuffers do seem more readable... Although we do this conversion in other places, so maybe it makes sense here. I could see it either way I guess.

@@ -201,7 +201,7 @@ int main(int argc, char **argv)

struct s2n_client_hello *client_hello = s2n_connection_get_client_hello(server);
EXPECT_NOT_NULL(client_hello);
EXPECT_FALSE(client_hello->sslv2);
EXPECT_EQUAL(client_hello->version, S2N_TLS12);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is now a more specific check. EXPECT_NOT_EQUAL(client_hello->version, S2N_SSLv2) would be the direct translation. Just checking to make sure that is intentional.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It doesn't matter. We already expect TLS1.2 on line 212, so this test already has to do TLS1.2.

@@ -32,7 +32,7 @@
#include "utils/s2n_random.h"
#include "utils/s2n_safety.h"

#define get_client_hello_protocol_version(conn) (conn->client_hello_version == S2N_SSLv2 ? conn->client_protocol_version : conn->client_hello_version)
#define get_client_hello_protocol_version(conn) (conn->client_hello.version == S2N_SSLv2 ? conn->client_protocol_version : conn->client_hello.version)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I found this macro confusing. My understanding after thinking about it for a bit is as follows:

It's not possible to negotiate the SSLv2 so if the client_hello says we are using SSLv2 we need to check the client_protocol_version instead.

But I'm confused why we don't just use client_protocol_version? Is it not in sync with client_hello.version at some moment?

Perhaps this comment is relevant:

/* client_hello.version is set when parsing the record header for
     * SSLv2 ClientHellos, not when parsing the ClientHello itself.
     * Therefore, client_hello.version is meaningful ONLY for SSLv2
     * before we parse the ClientHello.
     */

Comment on lines +116 to -118
uint8_t high = (ch->version / 10);
uint8_t low = (ch->version % 10);
uint16_t version = (high << 8) | low;

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));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm in favor of the more efficient math. But we may be able to maintain readability: If we do the conversion in other places does it make sense to turn it into a macro/function to make it more readable?

@@ -235,7 +236,7 @@ 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_ENSURE(ch->version > S2N_SSLv2, S2N_ERR_PROTOCOL_VERSION_UNSUPPORTED);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I assume we are intentionally checking more here (i.e. ch->version isn't an unknown protocol version).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A client hello without a version would be invalid and we wouldn't know how to parse it, so yes we are intentionally checking more. We have more to validate than just a flag now.

@github-actions
Copy link

This PR has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants