This is an automated email from the ASF dual-hosted git repository. rmaucher pushed a commit to branch 9.0.x in repository https://gitbox.apache.org/repos/asf/tomcat.git
commit 2db32e5b28c6d6ef9d4684b29f8a3272376f5899 Author: opencode <[email protected]> AuthorDate: Wed Oct 7 15:10:56 2026 +0200 Track StartTlsResponse per connection in JNDIRealm JNDIRealm stored the StartTlsResponse of the most recently negotiated TLS session in a single realm-wide, non-volatile field. With connectionPoolSize greater than one and useStartTls enabled, every open() overwrote the reference and close() of any connection closed whatever session the field happened to reference. Closing one pooled connection - pool overflow in release(), failover paths, or stop - therefore terminated the TLS session of a different, still active pooled connection, causing spurious authentication failures, while superseded sessions leaked. Concurrent open() calls also raced on the non-volatile field. Store the StartTlsResponse on the JNDIConnection that negotiated it and close only the connection's own response when that connection is closed. The response is now recorded only after negotiate() succeeds, so a failed negotiation cannot leave a stale reference behind. Include a changelog entry. --- java/org/apache/catalina/realm/JNDIRealm.java | 48 ++++++++++++++++----------- webapps/docs/changelog.xml | 7 ++++ 2 files changed, 36 insertions(+), 19 deletions(-) diff --git a/java/org/apache/catalina/realm/JNDIRealm.java b/java/org/apache/catalina/realm/JNDIRealm.java index e664bf6e03..e79a56ec8f 100644 --- a/java/org/apache/catalina/realm/JNDIRealm.java +++ b/java/org/apache/catalina/realm/JNDIRealm.java @@ -343,8 +343,6 @@ public class JNDIRealm extends RealmBase { */ private boolean useStartTls = false; - private StartTlsResponse tls = null; - /** * The list of enabled cipher suites used for establishing tls connections. <code>null</code> means to use the * default cipher suites. @@ -2386,11 +2384,13 @@ public class JNDIRealm extends RealmBase { } // Close tls startResponse if used - if (tls != null) { + if (connection.tls != null) { try { - tls.close(); + connection.tls.close(); } catch (IOException ioe) { containerLog.error(sm.getString("jndiRealm.tlsClose"), ioe); + } finally { + connection.tls = null; } } // Close our opened connection @@ -2711,7 +2711,7 @@ public class JNDIRealm extends RealmBase { protected void open(JNDIConnection connection) throws NamingException { try { // Ensure that we have a directory context available - connection.context = createDirContext(getDirectoryContextEnvironment()); + connection.context = createDirContext(connection, getDirectoryContextEnvironment()); } catch (Exception e) { if (alternateURL == null || alternateURL.isEmpty()) { // No alternate URL. Re-throw the exception. @@ -2721,7 +2721,7 @@ public class JNDIRealm extends RealmBase { // log the first exception. containerLog.info(sm.getString("jndiRealm.exception.retry"), e); // Try connecting to the alternate url. - connection.context = createDirContext(getDirectoryContextEnvironment()); + connection.context = createDirContext(connection, getDirectoryContextEnvironment()); } finally { // reset it in case the connection times out. // the primary may come back. @@ -2737,9 +2737,10 @@ public class JNDIRealm extends RealmBase { } - private DirContext createDirContext(Hashtable<String,String> env) throws NamingException { + private DirContext createDirContext(JNDIConnection connection, Hashtable<String,String> env) + throws NamingException { if (useStartTls) { - return createTlsDirContext(env); + return createTlsDirContext(connection, env); } else { return new InitialDirContext(env); } @@ -2794,15 +2795,17 @@ public class JNDIRealm extends RealmBase { /** - * Create a tls enabled LdapContext and set the StartTlsResponse tls instance variable. + * Create a tls enabled LdapContext and set the StartTlsResponse tls instance field of the connection. * - * @param env Environment to use for context creation + * @param connection The directory server connection wrapper + * @param env Environment to use for context creation * * @return configured {@link LdapContext} * * @throws NamingException when something goes wrong while negotiating the connection */ - private DirContext createTlsDirContext(Hashtable<String,String> env) throws NamingException { + private DirContext createTlsDirContext(JNDIConnection connection, Hashtable<String,String> env) + throws NamingException { Map<String,Object> savedEnv = new HashMap<>(); for (String key : Arrays.asList(Context.SECURITY_AUTHENTICATION, Context.SECURITY_CREDENTIALS, Context.SECURITY_PRINCIPAL, Context.SECURITY_PROTOCOL)) { @@ -2814,16 +2817,17 @@ public class JNDIRealm extends RealmBase { LdapContext result = null; try { result = new InitialLdapContext(env, null); - tls = (StartTlsResponse) result.extendedOperation(new StartTlsRequest()); - if (getHostnameVerifier() != null) { - tls.setHostnameVerifier(getHostnameVerifier()); - } - if (getCipherSuitesArray() != null) { - tls.setEnabledCipherSuites(getCipherSuitesArray()); - } + StartTlsResponse tls = (StartTlsResponse) result.extendedOperation(new StartTlsRequest()); try { + if (getHostnameVerifier() != null) { + tls.setHostnameVerifier(getHostnameVerifier()); + } + if (getCipherSuitesArray() != null) { + tls.setEnabledCipherSuites(getCipherSuitesArray()); + } SSLSession negotiate = tls.negotiate(getSSLSocketFactory()); containerLog.debug(sm.getString("jndiRealm.negotiatedTls", negotiate.getProtocol())); + connection.tls = tls; } catch (IOException ioe) { NamingException ne = new NamingException(ioe.getMessage()); ne.initCause(ioe); @@ -3386,6 +3390,12 @@ public class JNDIRealm extends RealmBase { /** * The directory context linking us to our directory server. */ - public DirContext context = null; + public volatile DirContext context = null; + + /** + * The {@link StartTlsResponse} negotiated for this connection, if any. + */ + public volatile StartTlsResponse tls = null; + } } diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml index 3f62d69765..e4dc641b61 100644 --- a/webapps/docs/changelog.xml +++ b/webapps/docs/changelog.xml @@ -1886,6 +1886,13 @@ Ensure URL encoding errors in the Rewrite Valve trigger an exception rather than silently using a replacement character. (markt) </fix> + <fix> + Track the <code>StartTlsResponse</code> used by <code>JNDIRealm</code> + per connection rather than in a single realm-wide field, so that + closing one pooled connection when <code>useStartTls</code> is enabled + no longer terminates the TLS session of another, still active pooled + connection. (remm) + </fix> </changelog> </subsection> <subsection name="Coyote"> --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
