This is an automated email from the ASF dual-hosted git repository. rmaucher pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/tomcat.git
commit b582095efc5cdbc0c82be8c70472eba8e1cbd314 Author: opencode <[email protected]> AuthorDate: Fri Oct 9 10:13:40 2026 +0200 Do not store the runtime-derived OpenSSLConfCmd groups command When using the JNI OpenSSL implementation, creating the SSL context adds a "groups" OpenSSLConfCmd derived from the SSLHostConfig "groups" attribute directly into the configuration's command list. StoreConfig did not skip it (only the four internal OCSP commands were skipped), so saving the running configuration wrote a command that duplicated the persisted attribute. On the next start the stored command suppressed the derivation from the attribute, silently ignoring any later change of the attribute. SSLHostConfigSF now stores a copy of the OpenSSLConf without groups commands whose value is derived from the attribute. A user-written command with a different value is kept, and no filtering occurs when the attribute is not set, since the runtime cannot have injected a command then. --- .../catalina/storeconfig/SSLHostConfigSF.java | 30 ++++++++++++ .../catalina/storeconfig/TestStoreConfig.java | 53 ++++++++++++++++++++++ 2 files changed, 83 insertions(+) diff --git a/java/org/apache/catalina/storeconfig/SSLHostConfigSF.java b/java/org/apache/catalina/storeconfig/SSLHostConfigSF.java index ab8722a6b4..f1269555b3 100644 --- a/java/org/apache/catalina/storeconfig/SSLHostConfigSF.java +++ b/java/org/apache/catalina/storeconfig/SSLHostConfigSF.java @@ -26,6 +26,7 @@ import org.apache.tomcat.util.net.SSLHostConfigCertificate; import org.apache.tomcat.util.net.SSLHostConfigCertificate.Type; import org.apache.tomcat.util.net.SSLHostConfigPreSharedKey; import org.apache.tomcat.util.net.openssl.OpenSSLConf; +import org.apache.tomcat.util.net.openssl.OpenSSLConfCmd; /** * Store SSLHostConfig @@ -109,8 +110,37 @@ public class SSLHostConfigSF extends StoreFactoryBase { storeElementArray(aWriter, indent, preSharedKeys); // Store nested <OpenSSLConf> element OpenSSLConf openSslConf = sslHostConfig.getOpenSslConf(); + openSslConf = removeRuntimeCommands(openSslConf, sslHostConfig); storeElement(aWriter, indent, openSslConf); } } + /* + * When the SSL context is created, the OpenSSL implementation adds a runtime "groups" command derived from the + * SSLHostConfig "groups" attribute. Store a copy of the configuration without such a command, since it duplicates + * the attribute and a stored command would shadow later changes of the attribute. A command whose value differs + * from the derived one was written by the user and is kept. + */ + private OpenSSLConf removeRuntimeCommands(OpenSSLConf openSslConf, SSLHostConfig sslHostConfig) { + if (openSslConf == null || sslHostConfig.getGroups() == null) { + return openSslConf; + } + String derivedValue = sslHostConfig.getGroups().replace(',', ':'); + ArrayList<OpenSSLConfCmd> retainedCommands = new ArrayList<>(); + for (OpenSSLConfCmd command : openSslConf.getCommands()) { + if (OpenSSLConfCmd.GROUPS.equals(command.getName()) && derivedValue.equals(command.getValue())) { + continue; + } + retainedCommands.add(command); + } + if (retainedCommands.size() == openSslConf.getCommands().size()) { + return openSslConf; + } + OpenSSLConf filteredCommands = new OpenSSLConf(); + for (OpenSSLConfCmd command : retainedCommands) { + filteredCommands.addCmd(command); + } + return filteredCommands; + } + } diff --git a/test/org/apache/catalina/storeconfig/TestStoreConfig.java b/test/org/apache/catalina/storeconfig/TestStoreConfig.java index 9d97398bed..9c4a32b86d 100644 --- a/test/org/apache/catalina/storeconfig/TestStoreConfig.java +++ b/test/org/apache/catalina/storeconfig/TestStoreConfig.java @@ -42,7 +42,10 @@ import org.apache.catalina.startup.TomcatBaseTest; import org.apache.catalina.util.IOTools; import org.apache.catalina.util.SessionIdGeneratorBase; import org.apache.catalina.valves.AccessLogValve; +import org.apache.tomcat.util.net.SSLHostConfig; import org.apache.tomcat.util.net.SSLHostConfigPreSharedKey; +import org.apache.tomcat.util.net.openssl.OpenSSLConf; +import org.apache.tomcat.util.net.openssl.OpenSSLConfCmd; import org.xml.sax.InputSource; public class TestStoreConfig extends TomcatBaseTest { @@ -573,6 +576,56 @@ public class TestStoreConfig extends TomcatBaseTest { Assert.assertTrue(serverXmlDump, serverXmlDump.contains("throwOnFailure=\"false\"")); } + /** + * Verify that the "groups" OpenSSLConfCmd that the OpenSSL implementation derives at runtime from the + * SSLHostConfig "groups" attribute is not stored, since a stored command would shadow later changes of the + * attribute. A user-written groups command must be kept. + * + * @throws Exception if the test experiences an unexpected error + */ + @Test + public void testOpenSSLConfGroupsCommandHandling() throws Exception { + StoreLoader loader = new StoreLoader(); + loader.load(null); + StoreRegistry registry = loader.getRegistry(); + StoreDescription desc = registry.findDescription(SSLHostConfig.class); + Assert.assertNotNull(desc); + + // The runtime command that the OpenSSL implementation adds from the attribute must not be stored + SSLHostConfig derived = new SSLHostConfig(); + derived.setGroups("secp256r1,secp384r1"); + derived.setOpenSslConf(new OpenSSLConf()); + derived.getOpenSslConf().addCmd(new OpenSSLConfCmd(OpenSSLConfCmd.GROUPS, "secp256r1:secp384r1")); + String dump = storeToString(desc, derived); + Assert.assertTrue(dump, dump.contains("groups=\"secp256r1,secp384r1\"")); + Assert.assertFalse(dump, dump.contains("OpenSSLConfCmd")); + + // A user-written command with a value different from the derived one must be kept + SSLHostConfig userCommand = new SSLHostConfig(); + userCommand.setGroups("secp256r1,secp384r1"); + userCommand.setOpenSslConf(new OpenSSLConf()); + userCommand.getOpenSslConf().addCmd(new OpenSSLConfCmd(OpenSSLConfCmd.GROUPS, "X25519")); + dump = storeToString(desc, userCommand); + Assert.assertTrue(dump, dump.contains("OpenSSLConfCmd")); + Assert.assertTrue(dump, dump.contains("name=\"groups\"")); + Assert.assertTrue(dump, dump.contains("value=\"X25519\"")); + + // Without the attribute, a groups command can only be user-written and must be kept + SSLHostConfig noAttribute = new SSLHostConfig(); + noAttribute.setGroups(null); + noAttribute.setOpenSslConf(new OpenSSLConf()); + noAttribute.getOpenSslConf().addCmd(new OpenSSLConfCmd(OpenSSLConfCmd.GROUPS, "secp256r1")); + dump = storeToString(desc, noAttribute); + Assert.assertTrue(dump, dump.contains("OpenSSLConfCmd")); + Assert.assertTrue(dump, dump.contains("name=\"groups\"")); + } + + private static String storeToString(StoreDescription desc, Object element) throws Exception { + StringWriter buffer = new StringWriter(); + desc.getStoreFactory().store(new PrintWriter(buffer), -2, element); + return buffer.toString(); + } + public static class CustomTestManager extends StandardManager { public CustomTestManager() { --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
