JDK-8316703 : SNIServerName settings are persisted across individual client socket connections when reuseSSLContext=true
  • Type: Bug
  • Component: security-libs
  • Sub-Component: javax.net.ssl
  • Affected Version: 17,21,22
  • Priority: P4
  • Status: Open
  • Resolution: Unresolved
  • OS: generic
  • CPU: generic
  • Submitted: 2023-09-20
  • Updated: 2024-04-10
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
Other
tbdUnresolved
Description
ADDITIONAL SYSTEM INFORMATION :
It looks like any Java VM I tested was affected (versions 8-22 on macOS, Linux, Windows, IBM i, z/OS).


A DESCRIPTION OF THE PROBLEM :
It appears that SNIServerName settings are persisted across individual client socket connections created with the same SSLContext.

The first connection made from the client determines whether/which SNI server names are being sent to the server, regardless of what server names are set later on via the socket-specific SSLParameters

SSLContexts are supposed to be reusable, and great efforts were made to not erroneously reuse settings, as can be seen by the many cloned/duplicated objects in the SSL code, e.g., in SSLParameters and SNIServerName, for example.

This bug can lead to situations where client requests fail unexpectedly due to race conditions, etc.

Note that for the server-side, SNIMatchers appear to not be reused across calls, indicating not only an unintuitive but also an inconsistent behavior.


STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Use code from https://github.com/kohlschuetter/snisslbug
(from source or run java -jar snissl-1.0-SNAPSHOT.jar )

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
**** START DEMO (reuseSSLContext=true) ****

Connecting to server; setServerName=snihostName
Setting SNI Matchers: true
Current server names: null
Setting server names: [type=host_name (0), value=snihostName]
Received SNI server name: type=host_name (0), value=snihostName

Connecting to server; setServerName=anotherSnihostName
Current server names: null
Setting server names: [type=host_name (0), value=anotherSnihostName]
Setting SNI Matchers: true
Received SNI server name: type=host_name (0), value=anotherSnihostName

Connecting to server; setServerName=null
Setting SNI Matchers: true
Current server names: null
Not setting server names
Did not receive SNI server name


**** START DEMO (reuseSSLContext=false) ****

Connecting to server; setServerName=snihostName
Setting SNI Matchers: true
Current server names: null
Setting server names: [type=host_name (0), value=snihostName]
Received SNI server name: type=host_name (0), value=snihostName

Connecting to server; setServerName=anotherSnihostName
Setting SNI Matchers: true
Current server names: null
Setting server names: [type=host_name (0), value=anotherSnihostName]
Received SNI server name: type=host_name (0), value=anotherSnihostName

Connecting to server; setServerName=null
Setting SNI Matchers: true
Current server names: null
Not setting server names
Did not receive SNI server name
ACTUAL -
**** START DEMO (reuseSSLContext=true) ****

Connecting to server; setServerName=snihostName
Setting SNI Matchers: true
Current server names: null
Setting server names: [type=host_name (0), value=snihostName]
Received SNI server name: type=host_name (0), value=snihostName

Connecting to server; setServerName=anotherSnihostName
Current server names: null
Setting server names: [type=host_name (0), value=anotherSnihostName]
Setting SNI Matchers: true
Received SNI server name: type=host_name (0), value=snihostName

Connecting to server; setServerName=null
Setting SNI Matchers: true
Current server names: null
Not setting server names
Received SNI server name: type=host_name (0), value=snihostName


**** START DEMO (reuseSSLContext=false) ****

Connecting to server; setServerName=snihostName
Setting SNI Matchers: true
Current server names: null
Setting server names: [type=host_name (0), value=snihostName]
Received SNI server name: type=host_name (0), value=snihostName

Connecting to server; setServerName=anotherSnihostName
Setting SNI Matchers: true
Current server names: null
Setting server names: [type=host_name (0), value=anotherSnihostName]
Received SNI server name: type=host_name (0), value=anotherSnihostName

Connecting to server; setServerName=null
Setting SNI Matchers: true
Current server names: null
Not setting server names
Did not receive SNI server name

---------- BEGIN SOURCE ----------
https://github.com/kohlschuetter/snisslbug
---------- END SOURCE ----------

CUSTOMER SUBMITTED WORKAROUND :
The only workaround I've found so far is to not reuse SSLContext for these configurations from the client-side. 


FREQUENCY : always



Comments
This is because when resuming a session, we use the SNI names from the original session: https://github.com/openjdk/jdk/blob/c5150c7b81e2b7b8c9e13c228d3b7bcb9dfe5024/src/java.base/share/classes/sun/security/ssl/ServerNameExtension.java#L228-L233 We are resuming a session, because both connections use the same serverAddress/serverPort in createSocket, and the host/port pair is used to select the appropriate session to resume. The situation where the host name (serverAddress) is different from the SNI name is unusual, so it's not clear what the right behavior would be. We definitely cannot resume a session using a different SNI name (this is forbidden by the TLS spec), so our options are limited to (1) using the original SNI name, which we do currently, or (2) performing a full handshake if the SNI name does not match the one from the remembered session. Both have their merits. On the user side, the user could use layered sockets to ensure that the hostname in createSocket is the same as the SNI name.
10-04-2024

The observations on Window 11: JDK 11: The reproducer threw IOException: Invalid keystore format JDK 17: Failed, "Received SNI server name: type=host_name (0), value=snihostName" observed in all client socket connections when reuseSSLContext=true JDK 21: Failed. JDK 22ea+16: Failed.
22-09-2023