libssl: New API for client applications to control session resumption

NEW
Unassigned

Status

NSS
Libraries
P1
enhancement
7 years ago
4 years ago

People

(Reporter: briansmith, Unassigned)

Tracking

Firefox Tracking Flags

(Not tracked)

Details

For my upcoming work in Gecko, I need a to cache some information, including an "isEV" bit for each SSL session, so that when it is resumed, I can determine whether the connection was EV without revalidating it. I cannot use the same technique that Chromium is using. I can get most of the way without any changes to libssl, however there are some race conditions regarding SSL cache clearing vs. new SSL connections that seem to require a new API.

I propose we have a new API that works as follows:

When building the ClientHello message and we find a session in the session cache that can be resumed, we call a new application-provided callback function, the "session resumption callback". If the session resumption callback returns SECSuccess then we carry on with the session resumption; if it returns SECWouldBlock then we wait for a new SSL_RestartSessionResumption function to be called; otherwise, we delete the session from the cache and repeat the process for any other sessions that can be resumed for this connection.

In the session resumption callback, the client can pin any cached state that it would need for the resumed session to the connection (stashing things in the fd->secret or the callback arg) and return PR_TRUE. Or, if the information is not available any more, it can return SECFailure. Or, if the session is too old and the certificate just needs to be re-validated, then it can start a revalidation and return SECWouldBlock.

Such a change might be useful for the server side too. However, I am proposing this only for the client side. We can file a follow-up bug for doing something similar on the server side.
Whiteboard: [mozilla-SPDY]
Severity: normal → enhancement

Comment 1

7 years ago
isEV is a property of the server certificate.  I don't think it
belongs in the SSL session state.  It seems that you are asking
NSS to provide this service to avoid the need to implement a
hashtable that maps a certificate to the certificate's status
in Gecko.  Did I understand this correctly?

Or do you need a way to check the certificate before sending
application data to the server?  You can do that in the
handshake completion callback.  Does that not work?
I am already implementing that hashtable in Gecko. But, there are some places in Gecko where we explicitly try to forget all session state and certificate auth state (e.g. when the user changes certificate trust bits in the cert manager, or during profile change, or during private browsing transitions).

There is a potential race between such cache-clearing operations and SSL handshakes:

0. libssl finds a session in the session cache for the host we are connecting to.
1. libssl sends the client hello with a session ID indicating that it wants to resume the session.
2. Certificate authentication state gets cleared by the user.
3. libssl calls our handshake callback, which needs to know the EV state of the certificate used in the resumed session, but that EV state was deleted in step #2, so we either have to re-validate the certificate or fail the connection. We will not be able to re-validate in the handshake callback after we remove the SSL thread, because that would cause a deadlock. And, we don't want to fail the connection in this case either.

If we had the callback that I am proposing, we could do the following:

0. libssl finds a session in the session cache for the host we are connecting to.

1. libssl calls our session resumption callback, and our session resumption callback stores the EV status (retrieved from the hashtable you mentioned above) in the nsNSSSocketInfo structure for the connection.

2. Certificate authentication state gets cleared by the user.

3. libssl calls our handshake callback, which needs to know the EV state of the certificate used in the resumed session. Now, it can use the EV state it stashed in the nsNSSSocketInfo structure and carry on safely.

or, if the race happens a different way:

0. libssl finds a session in the session cache for the host we are connecting to.

1. Certificate authentication state gets cleared by the user.

2. libssl calls our session resumption callback, and our session resumption callback realizes it has forgotten the EV status for the cert in step #1, so it refuses to allow the resumption by returning SECFailure.

3. libssl deletes the stale session and starts a new one, so that our auth certificate callback gets called, and we store the new EV status in the hashtable for future use.
Also, this API would allow the client to control session lifetime in a way that (AFAICT) isn't possible with current libssl APIs: if the cert validation was done too long ago (as determined by the application), it can return SECFailure to force a new session or it can re-validate the certificate (returning SECWouldBlock).

In the local version of the patch for this, I do not allow the session resumption callback to return SECWouldBlock, only SECSuccess and SECFailure. I might split the SECWouldBlock case into a separate bug and/or refuse to allow that case.

Comment 4

7 years ago
What you are really looking for is a way to clear the session cache for a specific SSL connection/server/port/client.

I think we've need that for a while.

Your latter case is simply giving application control for the session timeout value. The default value for SSL3 is 24hours, but the value stored in the sid is the time when the session becomes invalid. Having an interface which tweaks with the side timeout time will give you everything you need much more simply.

bob
(In reply to Robert Relyea from comment #4)
> What you are really looking for is a way to clear the
> session cache for a specific SSL connection/server/port/client.

I don't see how that would help. Even completely clearing the whole session cache doesn't prevent these races.

> Your latter case is simply giving application control for the session
> timeout value. The default value for SSL3 is 24hours, but the value stored
> in the sid is the time when the session becomes invalid. Having an interface
> which tweaks with the side timeout time will give you everything you need
> much more simply.

We might want to re-validate certs every hour, but resume sessions for that (host,port,cert) across those re-validations as long as they succeed. So, it isn't the case that entries in the session cache are just valid or invalid; there would also be a new "needs revalidation" state, and we would have to provide some way for the application to do the revalidation and allowing the session to be resumed again. That is what this function would allow in the "return SECWouldBlock" case.

Yesterday, Wan-Teh said that they always use SSL_ForceHandshake in Chromium, before they read/write any data. This allows them to do a re-validation for resumed sessions after the handshake is over, if they need to. I guess that, if we change our read/write/recv/send PRIOMethods to block until after the handshake callback is complete and until certificate validation is complete, we might be able to do something similar, instead of what I propose here. I will investigate it.
This is no longer blocking the remove-the-SSL-thread project, and thus is not blocking SPDY in Gecko any more.
No longer blocks: 674147
Whiteboard: [mozilla-SPDY]
No longer blocks: 660749
Assignee: brian → nobody
You need to log in before you can comment on or make changes to this bug.