This is an automated email from the ASF dual-hosted git repository.

rmaucher pushed a commit to branch 11.0.x
in repository https://gitbox.apache.org/repos/asf/tomcat.git

commit 87c76e552e8210187a8b7ac26836684225e5c6df
Author: opencode <[email protected]>
AuthorDate: Fri Oct 9 10:22:46 2026 +0200

    Do not store the runtime placeholder UNDEFINED Certificate element
    
    When an SSL host configuration contains no Certificate element, the
    endpoint registers a bare UNDEFINED-type placeholder certificate when it
    creates the SSL context. SSLHostConfigSF only removed UNDEFINED
    certificates when more than one was present, a case that can never occur
    since addCertificate() rejects mixing UNDEFINED with typed certificates,
    so the single placeholder was stored as a <Certificate
    type="UNDEFINED"/> element. Adding a typed certificate to the saved file
    then made the next start fail because only one certificate may use the
    undefined type.
    
    Expose the placeholder through SSLHostConfig.getDefaultCertificate() and
    skip that exact instance when storing. An explicitly configured,
    type-less certificate remains stored since it is a distinct instance
    that
    the administrator wrote.
---
 .../catalina/storeconfig/SSLHostConfigSF.java      |  9 ++---
 java/org/apache/tomcat/util/net/SSLHostConfig.java | 10 ++++++
 .../catalina/storeconfig/TestStoreConfig.java      | 39 ++++++++++++++++++++++
 3 files changed, 54 insertions(+), 4 deletions(-)

diff --git a/java/org/apache/catalina/storeconfig/SSLHostConfigSF.java 
b/java/org/apache/catalina/storeconfig/SSLHostConfigSF.java
index 9676ec4d7f..3bc936c3e7 100644
--- a/java/org/apache/catalina/storeconfig/SSLHostConfigSF.java
+++ b/java/org/apache/catalina/storeconfig/SSLHostConfigSF.java
@@ -23,7 +23,6 @@ import org.apache.juli.logging.Log;
 import org.apache.juli.logging.LogFactory;
 import org.apache.tomcat.util.net.SSLHostConfig;
 import org.apache.tomcat.util.net.SSLHostConfigCertificate;
-import org.apache.tomcat.util.net.SSLHostConfigCertificate.Type;
 import org.apache.tomcat.util.net.openssl.OpenSSLConf;
 import org.apache.tomcat.util.net.openssl.OpenSSLConfCmd;
 
@@ -87,11 +86,13 @@ public class SSLHostConfigSF extends StoreFactoryBase {
             // Store nested <SSLHostConfigCertificate> elements
             SSLHostConfigCertificate[] hostConfigsCertificates =
                     sslHostConfig.getCertificates().toArray(new 
SSLHostConfigCertificate[0]);
-            // Remove a possible default UNDEFINED certificate
-            if (hostConfigsCertificates.length > 1) {
+            // Remove the runtime placeholder certificate that was created when
+            // no certificate was configured
+            SSLHostConfigCertificate defaultCertificate = 
sslHostConfig.getDefaultCertificate();
+            if (defaultCertificate != null) {
                 ArrayList<SSLHostConfigCertificate> certificates = new 
ArrayList<>();
                 for (SSLHostConfigCertificate certificate : 
hostConfigsCertificates) {
-                    if (Type.UNDEFINED != certificate.getType()) {
+                    if (certificate != defaultCertificate) {
                         certificates.add(certificate);
                     }
                 }
diff --git a/java/org/apache/tomcat/util/net/SSLHostConfig.java 
b/java/org/apache/tomcat/util/net/SSLHostConfig.java
index 16dc62e783..0db535b8bf 100644
--- a/java/org/apache/tomcat/util/net/SSLHostConfig.java
+++ b/java/org/apache/tomcat/util/net/SSLHostConfig.java
@@ -577,6 +577,16 @@ public class SSLHostConfig implements Serializable {
     }
 
 
+    /**
+     * Returns the placeholder certificate that was created at runtime because 
no certificate was configured, if any.
+     *
+     * @return the runtime placeholder certificate or {@code null} if none was 
created
+     */
+    public SSLHostConfigCertificate getDefaultCertificate() {
+        return defaultCertificate;
+    }
+
+
     // ----------------------------------------- Common configuration 
properties
 
     /**
diff --git a/test/org/apache/catalina/storeconfig/TestStoreConfig.java 
b/test/org/apache/catalina/storeconfig/TestStoreConfig.java
index faba0f337d..cfb0de62ee 100644
--- a/test/org/apache/catalina/storeconfig/TestStoreConfig.java
+++ b/test/org/apache/catalina/storeconfig/TestStoreConfig.java
@@ -43,6 +43,7 @@ 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.SSLHostConfigCertificate;
 import org.apache.tomcat.util.net.openssl.OpenSSLConf;
 import org.apache.tomcat.util.net.openssl.OpenSSLConfCmd;
 import org.xml.sax.InputSource;
@@ -605,6 +606,44 @@ public class TestStoreConfig extends TomcatBaseTest {
         Assert.assertTrue(dump, dump.contains("name=\"groups\""));
     }
 
+    /**
+     * Verify that the placeholder certificate of type UNDEFINED that is 
created at runtime when no certificate is
+     * configured is not stored. The placeholder would fail the start of a 
server when a typed certificate is added to
+     * the stored configuration later. An explicitly configured certificate of 
type UNDEFINED must be kept since the
+     * filter matches the runtime registered instance.
+     *
+     * @throws Exception if the test experiences an unexpected error
+     */
+    @Test
+    public void testDefaultCertificatePlaceholderNotStored() throws Exception {
+        StoreLoader loader = new StoreLoader();
+        loader.load(null);
+        StoreRegistry registry = loader.getRegistry();
+        StoreDescription desc = registry.findDescription(SSLHostConfig.class);
+        Assert.assertNotNull(desc);
+
+        // The placeholder registered at runtime when the endpoint creates the 
SSL context
+        SSLHostConfig placeholder = new SSLHostConfig();
+        placeholder.getCertificates(true);
+        String dump = storeToString(desc, placeholder);
+        Assert.assertTrue(dump, dump.contains("<SSLHostConfig"));
+        Assert.assertFalse(dump, dump.contains("<Certificate"));
+
+        // An explicitly configured, type-less certificate must be kept
+        SSLHostConfig explicit = new SSLHostConfig();
+        explicit.addCertificate(new SSLHostConfigCertificate(explicit, 
SSLHostConfigCertificate.Type.UNDEFINED));
+        dump = storeToString(desc, explicit);
+        Assert.assertTrue(dump, dump.contains("<Certificate"));
+        Assert.assertTrue(dump, dump.contains("type=\"UNDEFINED\""));
+
+        // Typed certificates are unaffected
+        SSLHostConfig typed = new SSLHostConfig();
+        typed.addCertificate(new SSLHostConfigCertificate(typed, 
SSLHostConfigCertificate.Type.EC));
+        dump = storeToString(desc, typed);
+        Assert.assertTrue(dump, dump.contains("<Certificate"));
+        Assert.assertTrue(dump, dump.contains("type=\"EC\""));
+    }
+
     private static String storeToString(StoreDescription desc, Object element) 
throws Exception {
         StringWriter buffer = new StringWriter();
         desc.getStoreFactory().store(new PrintWriter(buffer), -2, element);


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to