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]

Reply via email to