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

Fix bug where early_data does not work if no SNI callback is present #4519

Closed
wants to merge 2 commits into from

Conversation

mattcaswell
Copy link
Member

Fixes #4496

Checklist
  • documentation is added or updated
  • tests are added or updated

…data

Test for the bug where early_data is not accepted by the server when it
does not have an SNI callback set up, but the client sent a servername in
the initial ClientHello establishing the session.
@mattcaswell
Copy link
Member Author

Pushed. Thanks.

levitte pushed a commit that referenced this pull request Oct 12, 2017
Fixes #4496

Reviewed-by: Rich Salz <rsalz@openssl.org>
Reviewed-by: Ben Kaduk <kaduk@mit.edu>
(Merged from #4519)
levitte pushed a commit that referenced this pull request Oct 12, 2017
…data

Test for the bug where early_data is not accepted by the server when it
does not have an SNI callback set up, but the client sent a servername in
the initial ClientHello establishing the session.

Reviewed-by: Rich Salz <rsalz@openssl.org>
Reviewed-by: Ben Kaduk <kaduk@mit.edu>
(Merged from #4519)
@@ -834,6 +834,11 @@ static int final_server_name(SSL *s, unsigned int context, int sent,
ret = s->session_ctx->ext.servername_cb(s, &altmp,
s->session_ctx->ext.servername_arg);

if (!sent) {
OPENSSL_free(s->session->ext.hostname);
s->session->ext.hostname = NULL;
Copy link
Contributor

Choose a reason for hiding this comment

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

This change is intended to fix a bug with early data, a TLS 1.3-only feature.
However, if I am reading things correctly, we can also run this code for pre-1.3 handshakes, and since we do not send SNI in the ServerHello in some cases (e.g., when resuming), we end up freeing a field in the SSL_SESSION object for a resumed session, i.e., one that is still in cache (for non-ticket cases). This is, or should be, forbidden, because cached sessions can be in use by multiple threads simultaneously -- for the TLS 1.3 case we fix this by always creating a new SSL_SESSION object for each connection, but TLS 1.2 and older still have the old behavior.

One could perhaps argue that we should not be storing the "intermediate-state" (i.e., during handshake processing) hostname in the SSL_SESSION at all, and should instead be using the SSL's ext.hostname field. We would then only populate the session once we know that we have negotiated an SNI value and are establishing a successful session, so the SSL_SESSION would remain read-only after (logical) creation.

Copy link
Contributor

Choose a reason for hiding this comment

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

We are getting a double-free condition at this particular condition. We can get it to happen fairly readily, but don't know the exact cause (i.e. modifying a cached session).

Copy link

Choose a reason for hiding this comment

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

Hi,
https://tools.ietf.org/html/draft-ietf-tls-tls13-28#section-4.2.11 says :
"
In TLS 1.3, the SNI value is always explicitly specified in
the resumption handshake, and there is no need for the server to
associate an SNI value with the ticket.
"
So it seems that there is no need to zero ext.hostname.
Instead there is a need to check the existence of ext.hostname.

I also think that the draft should mention that SNI is required also at initial handshake.

Copy link
Contributor

Choose a reason for hiding this comment

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

We are getting a double-free condition at this particular condition. We can get it to happen fairly readily, but don't know the exact cause (i.e. modifying a cached session).

Is this a multi-threaded server? This function does indeed appear to violate the threading rules around s->session, so that's probably what's going on. With the caveat that I'm not familiar with the 1.1.0 state machine, this function is run in both resumption and full handshakes, right? s->session is immutable in resumption handshakes.

We split s->session into a bunch of different session pointers on our end, largely to help avoid this sort of mixup. This is especially helpful starting TLS 1.3, where, at least in our implementation, we found it was simplest to say that 1.3 resumption stamps out a new SSL_SESSION using the previous one as a template. Having the mutability encoded in which field you're accessing rather than runtime state makes it easier to get things right.

Copy link
Contributor

Choose a reason for hiding this comment

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

It is multi-threaded, and the current leading hypothesis is that this code's violation of the threading rules for s->session is causing the problems.
It is perhaps debatable whether we should be writing a hostname into the session during handshake processing but before we have decided to accept SNI, though, which would be a somewhat more invasive patch.

Copy link
Contributor

Choose a reason for hiding this comment

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

Is this a multi-threaded server?

Yes, this is the path we are currently going down; and agree with the SSL_SESSION write-access violation.

Copy link
Contributor

Choose a reason for hiding this comment

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

Oops, somehow missed that @kaduk already said basically the same thing. :-)

One could perhaps argue that we should not be storing the "intermediate-state" (i.e., during handshake processing) hostname in the SSL_SESSION at all, and should instead be using the SSL's ext.hostname field. We would then only populate the session once we know that we have negotiated an SNI value and are establishing a successful session, so the SSL_SESSION would remain read-only after (logical) creation.

This is what we do. Or rather, it's what we did when we stored the hostname in the SSL_SESSION at all. We've since stopped doing that. But note there is some weirdness here around being consistent whether, in a TLS 1.2 resumption, SSL_get_servername returns the value stored in the session or the value received on the connection, if you don't enforce that they match. That API is sometimes queried during and sometimes after the handshake.

Copy link
Contributor

Choose a reason for hiding this comment

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

Indeed, there'd probably need to be some awful hack where SSL_get_servername can return the hostname from the SSL during the handshake and the one from the session otherwise. Maybe that's more messiness than it's worth, and we should just go for the minimal change of only doing this free for non-cached session objects (i.e., non-resumptions and TLS 1.3 connections).

Copy link
Contributor

Choose a reason for hiding this comment

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

Okay, I made #6378 to show what this approach looks like.

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

Successfully merging this pull request may close these issues.

TLSv1.3 early data and server name callback
6 participants