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

markt-asf pushed a commit to branch 9.0.x
in repository https://gitbox.apache.org/repos/asf/tomcat.git


The following commit(s) were added to refs/heads/9.0.x by this push:
     new a5507a0006 Review "Match OpenSSL semantics for AND cipher expressions 
with..."
a5507a0006 is described below

commit a5507a0006f3230708173bc3d71da070a8a09da4
Author: Mark Thomas <[email protected]>
AuthorDate: Wed Nov 4 09:13:03 2026 +0000

    Review "Match OpenSSL semantics for AND cipher expressions with..."
    
    Additional logging and tests for unknown terms
---
 .../ciphers/OpenSSLCipherConfigurationParser.java  |  8 ++
 .../TestOpenSSLCipherConfigurationParser.java      | 95 ++++++++++++++++++++++
 2 files changed, 103 insertions(+)

diff --git 
a/java/org/apache/tomcat/util/net/openssl/ciphers/OpenSSLCipherConfigurationParser.java
 
b/java/org/apache/tomcat/util/net/openssl/ciphers/OpenSSLCipherConfigurationParser.java
index cfeb7bced3..f98ef6c6a4 100644
--- 
a/java/org/apache/tomcat/util/net/openssl/ciphers/OpenSSLCipherConfigurationParser.java
+++ 
b/java/org/apache/tomcat/util/net/openssl/ciphers/OpenSSLCipherConfigurationParser.java
@@ -789,6 +789,8 @@ public class OpenSSLCipherConfigurationParser {
                 String alias = element.substring(1);
                 if (aliases.containsKey(alias)) {
                     remove(ciphers, alias);
+                } else {
+                    
log.warn(sm.getString("opensslCipherConfigurationParser.unknownElement", 
alias));
                 }
             } else if (element.startsWith(EXCLUDE)) {
                 String alias = element.substring(1);
@@ -802,6 +804,8 @@ public class OpenSSLCipherConfigurationParser {
                 if (aliases.containsKey(alias)) {
                     moveToEnd(ciphers, alias);
                     moveToEnd(orderedAllAddedCiphers, alias);
+                } else {
+                    
log.warn(sm.getString("opensslCipherConfigurationParser.unknownElement", 
alias));
                 }
             } else if ("@STRENGTH".equals(element)) {
                 ciphers = strengthSort(ciphers);
@@ -828,6 +832,10 @@ public class OpenSSLCipherConfigurationParser {
                 if (result != null) {
                     add(ciphers, result, orderedAllAddedCiphers);
                 }
+            } else if (!element.isEmpty() && !element.startsWith("PROFILE=") 
&& !element.startsWith("@SECLEVEL=")) {
+                // OpenSSL ignores unknown elements. Do the same but, since it 
is most likely a typo, warn about it.
+                // PROFILE= is reported separately above and @SECLEVEL= is 
valid for OpenSSL but is not used here.
+                
log.warn(sm.getString("opensslCipherConfigurationParser.unknownElement", 
element));
             }
         }
         ciphers.removeAll(excludedCiphers);
diff --git 
a/test/org/apache/tomcat/util/net/openssl/ciphers/TestOpenSSLCipherConfigurationParser.java
 
b/test/org/apache/tomcat/util/net/openssl/ciphers/TestOpenSSLCipherConfigurationParser.java
index 7f8cf44f65..52867c8064 100644
--- 
a/test/org/apache/tomcat/util/net/openssl/ciphers/TestOpenSSLCipherConfigurationParser.java
+++ 
b/test/org/apache/tomcat/util/net/openssl/ciphers/TestOpenSSLCipherConfigurationParser.java
@@ -16,8 +16,13 @@
  */
 package org.apache.tomcat.util.net.openssl.ciphers;
 
+import java.util.ArrayList;
 import java.util.List;
 import java.util.TreeSet;
+import java.util.logging.Handler;
+import java.util.logging.Level;
+import java.util.logging.LogRecord;
+import java.util.logging.Logger;
 
 import org.junit.Assert;
 import org.junit.Test;
@@ -547,6 +552,96 @@ public class TestOpenSSLCipherConfigurationParser {
     }
 
 
+    @Test
+    public void testIntersectionWithUnknownTermIsDiscarded() throws Exception {
+        // OpenSSL discards the whole AND element if any term is unknown, 
wherever the unknown term is. The other
+        // elements are not affected.
+        testSpecification("AES128:SHA1+Nonsense");
+        testSpecification("AES128:Nonsense+SHA1");
+        testSpecification("AES128:SHA1+Nonsense+AES128");
+
+        List<String> expected = 
OpenSSLCipherConfigurationParser.parseExpression("AES128");
+        Assert.assertEquals(expected, 
OpenSSLCipherConfigurationParser.parseExpression("AES128:SHA1+Nonsense"));
+        Assert.assertEquals(expected, 
OpenSSLCipherConfigurationParser.parseExpression("AES128:Nonsense+SHA1"));
+        Assert.assertEquals(expected,
+                
OpenSSLCipherConfigurationParser.parseExpression("AES128:SHA1+Nonsense+AES128"));
+    }
+
+
+    @Test
+    public void testUnknownElementsAreIgnored() throws Exception {
+        // OpenSSL ignores unknown elements, whatever the operator
+        testSpecification("AES128:Nonsense");
+        testSpecification("AES128:!Nonsense");
+
+        List<String> expected = 
OpenSSLCipherConfigurationParser.parseExpression("AES128");
+        for (String unknown : new String[] { "Nonsense", "!Nonsense", 
"-Nonsense", "+Nonsense" }) {
+            Assert.assertEquals(unknown, expected,
+                    OpenSSLCipherConfigurationParser.parseExpression("AES128:" 
+ unknown));
+        }
+    }
+
+
+    @Test
+    public void testUnknownElementsAreLogged() {
+        String[] specifications = { "AES128:Nonsense", "AES128:!Nonsense", 
"AES128:-Nonsense", "AES128:+Nonsense",
+                "AES128:SHA1+Nonsense", "AES128:Nonsense+SHA1" };
+
+        for (String specification : specifications) {
+            List<String> warnings = parseAndCaptureWarnings(specification);
+            Assert.assertEquals(specification + " logged " + warnings, 1, 
warnings.size());
+            Assert.assertTrue(specification + " logged " + warnings, 
warnings.get(0).contains("Nonsense"));
+        }
+    }
+
+
+    @Test
+    public void testKnownAndOtherValidElementsAreNotLogged() {
+        // Known elements, an empty element and the valid OpenSSL @SECLEVEL 
element must not trigger a warning
+        String[] specifications = { "AES128:SHA1+AES128", 
"AES128:!aNULL:-AES256:+SHA1:@STRENGTH", "AES128::SHA1",
+                "AES128:@SECLEVEL=1" };
+
+        for (String specification : specifications) {
+            List<String> warnings = parseAndCaptureWarnings(specification);
+            Assert.assertTrue(specification + " logged " + warnings, 
warnings.isEmpty());
+        }
+    }
+
+
+    private List<String> parseAndCaptureWarnings(String specification) {
+        List<String> warnings = new ArrayList<>();
+        Logger logger = 
Logger.getLogger(OpenSSLCipherConfigurationParser.class.getName());
+        Handler handler = new Handler() {
+            @Override
+            public void publish(LogRecord record) {
+                if (record.getLevel().intValue() >= Level.WARNING.intValue()) {
+                    warnings.add(record.getMessage() + " " + String.join(" ",
+                            record.getParameters() == null ? new String[0] :
+                                    
java.util.Arrays.stream(record.getParameters()).map(String::valueOf)
+                                            .toArray(String[]::new)));
+                }
+            }
+
+            @Override
+            public void flush() {
+                // NO-OP
+            }
+
+            @Override
+            public void close() {
+                // NO-OP
+            }
+        };
+        logger.addHandler(handler);
+        try {
+            OpenSSLCipherConfigurationParser.parseExpression(specification);
+        } finally {
+            logger.removeHandler(handler);
+        }
+        return warnings;
+    }
+
+
     private void testSpecificationIsEmpty(String specification) throws 
Exception {
         String openSSLCipherList = 
TesterOpenSSL.getOpenSSLCiphersAsExpression(specification);
         Assert.assertEquals(TesterOpenSSL.VERSION_STRING + " with 
specification [" + specification + "] returned [" +


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

Reply via email to