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

coheigea pushed a commit to branch 3.6.x-fixes
in repository https://gitbox.apache.org/repos/asf/cxf.git


The following commit(s) were added to refs/heads/3.6.x-fixes by this push:
     new 5bd10fc3937 Adding LDAP XKMS tests (#3086)
5bd10fc3937 is described below

commit 5bd10fc39379aede42d8fa8d215482e91234a906
Author: Colm O hEigeartaigh <[email protected]>
AuthorDate: Fri May 8 08:01:19 2026 +0100

    Adding LDAP XKMS tests (#3086)
    
    (cherry picked from commit 7bc885e44d55fa0ca6f4fd48ba63b89bd0c3ad88)
---
 .../xkms/x509/repo/ldap/LdapCertificateRepo.java   |  90 ++++++++-
 systests/ldap/pom.xml                              |   6 +
 .../systest/ldap/xkms/LDAPCertificateRepoTest.java | 215 +++++++++++++++++++++
 3 files changed, 304 insertions(+), 7 deletions(-)

diff --git 
a/services/xkms/xkms-x509-repo-ldap/src/main/java/org/apache/cxf/xkms/x509/repo/ldap/LdapCertificateRepo.java
 
b/services/xkms/xkms-x509-repo-ldap/src/main/java/org/apache/cxf/xkms/x509/repo/ldap/LdapCertificateRepo.java
index a588e5cb9bd..a586d8d153e 100644
--- 
a/services/xkms/xkms-x509-repo-ldap/src/main/java/org/apache/cxf/xkms/x509/repo/ldap/LdapCertificateRepo.java
+++ 
b/services/xkms/xkms-x509-repo-ldap/src/main/java/org/apache/cxf/xkms/x509/repo/ldap/LdapCertificateRepo.java
@@ -30,7 +30,6 @@ import java.util.List;
 import java.util.Map;
 import java.util.logging.Level;
 import java.util.logging.Logger;
-import java.util.regex.Matcher;
 
 import javax.naming.NamingEnumeration;
 import javax.naming.NamingException;
@@ -200,7 +199,7 @@ public class LdapCertificateRepo implements CertificateRepo 
{
         if (cert == null) {
             // Try to find certificate by search for uid attribute
             try {
-                String filter = 
String.format(ldapConfig.getServiceCertUIDTemplate(), serviceName);
+                String filter = 
String.format(ldapConfig.getServiceCertUIDTemplate(), 
escapeFilterValue(serviceName));
                 Attribute attr = ldapSearch.findAttribute(rootDN, filter, 
ldapConfig.getAttrCrtBinary());
                 return getCert(attr);
             } catch (NamingException e) {
@@ -213,7 +212,7 @@ public class LdapCertificateRepo implements CertificateRepo 
{
     @Override
     public X509Certificate findByEndpoint(String endpoint) {
         X509Certificate cert = null;
-        String filter = String.format("(%s=%s)", ldapConfig.getAttrEndpoint(), 
endpoint);
+        String filter = String.format("(%s=%s)", ldapConfig.getAttrEndpoint(), 
escapeFilterValue(endpoint));
         try {
             Attribute attr = ldapSearch.findAttribute(rootDN, filter, 
ldapConfig.getAttrCrtBinary());
             cert = getCert(attr);
@@ -225,8 +224,50 @@ public class LdapCertificateRepo implements 
CertificateRepo {
 
 
     protected String getDnForIdentifier(String id) {
-        String escapedIdentifier = id.replaceAll("\\/", 
Matcher.quoteReplacement("\\/"));
-        return String.format(ldapConfig.getServiceCertRDNTemplate(), 
escapedIdentifier) + "," + rootDN;
+        return String.format(ldapConfig.getServiceCertRDNTemplate(), 
escapeDnValue(id)) + "," + rootDN;
+    }
+
+    /**
+     * Escapes RFC4514 special characters in an LDAP DN attribute value, plus
+     * the JNDI composite-name separator '/' to prevent namespace traversal.
+     */
+    protected String escapeDnValue(String value) {
+        if (value == null) {
+            return null;
+        }
+        StringBuilder escaped = new StringBuilder(value.length());
+        for (int i = 0; i < value.length(); i++) {
+            char ch = value.charAt(i);
+            // Leading space or '#' must be escaped per RFC4514
+            if (i == 0 && (ch == ' ' || ch == '#')) {
+                escaped.append('\\').append(ch);
+                continue;
+            }
+            switch (ch) {
+            case '\\':
+            case ',':
+            case '+':
+            case '"':
+            case '<':
+            case '>':
+            case ';':
+            case '/':
+                escaped.append('\\').append(ch);
+                break;
+            case '\0':
+                escaped.append("\\00");
+                break;
+            default:
+                escaped.append(ch);
+                break;
+            }
+        }
+        // Trailing space must be escaped per RFC4514
+        int len = escaped.length();
+        if (len > 1 && escaped.charAt(len - 1) == ' ' && escaped.charAt(len - 
2) != '\\') {
+            escaped.insert(len - 1, '\\');
+        }
+        return escaped.toString();
     }
 
     protected X509Certificate getCertificateForDn(String dn) throws 
NamingException {
@@ -235,7 +276,7 @@ public class LdapCertificateRepo implements CertificateRepo 
{
     }
 
     protected X509Certificate getCertificateForUIDAttr(String uid) throws 
NamingException {
-        String filter = String.format(filterUIDTemplate, uid);
+        String filter = String.format(filterUIDTemplate, 
escapeFilterValue(uid));
         Attribute attr = ldapSearch.findAttribute(rootDN, filter, 
ldapConfig.getAttrCrtBinary());
         return getCert(attr);
     }
@@ -245,7 +286,8 @@ public class LdapCertificateRepo implements CertificateRepo 
{
         if (issuer == null || serial == null) {
             throw new IllegalArgumentException("Issuer and serial applications 
are expected in request");
         }
-        String filter = String.format(filterIssuerSerialTemplate, issuer, 
serial);
+        String filter = String.format(filterIssuerSerialTemplate, 
escapeFilterValue(issuer),
+                                      escapeFilterValue(serial));
         try {
             Attribute attr = ldapSearch.findAttribute(rootDN, filter, 
ldapConfig.getAttrCrtBinary());
             return getCert(attr);
@@ -254,6 +296,40 @@ public class LdapCertificateRepo implements 
CertificateRepo {
         }
     }
 
+    /**
+     * Escapes RFC4515 special characters in an LDAP search filter assertion 
value.
+     */
+    protected String escapeFilterValue(String value) {
+        if (value == null) {
+            return null;
+        }
+        StringBuilder escaped = new StringBuilder(value.length());
+        for (int i = 0; i < value.length(); i++) {
+            char ch = value.charAt(i);
+            switch (ch) {
+            case '\\':
+                escaped.append("\\5c");
+                break;
+            case '*':
+                escaped.append("\\2a");
+                break;
+            case '(':
+                escaped.append("\\28");
+                break;
+            case ')':
+                escaped.append("\\29");
+                break;
+            case '\0':
+                escaped.append("\\00");
+                break;
+            default:
+                escaped.append(ch);
+                break;
+            }
+        }
+        return escaped.toString();
+    }
+
     protected X509Certificate getCert(Attribute attr) {
         if (attr == null) {
             return null;
diff --git a/systests/ldap/pom.xml b/systests/ldap/pom.xml
index a1c9859ea67..08800c7a0b4 100644
--- a/systests/ldap/pom.xml
+++ b/systests/ldap/pom.xml
@@ -162,6 +162,12 @@
             <version>${project.version}</version>
             <scope>test</scope>
         </dependency>
+        <dependency>
+            <groupId>org.apache.cxf.services.xkms</groupId>
+            <artifactId>cxf-services-xkms-x509-handlers</artifactId>
+            <version>${project.version}</version>
+            <scope>test</scope>
+        </dependency>
         <dependency>
             <groupId>org.apache.cxf.services.xkms</groupId>
             <artifactId>cxf-services-xkms-x509-repo-ldap</artifactId>
diff --git 
a/systests/ldap/src/test/java/org/apache/cxf/systest/ldap/xkms/LDAPCertificateRepoTest.java
 
b/systests/ldap/src/test/java/org/apache/cxf/systest/ldap/xkms/LDAPCertificateRepoTest.java
index 9b0d0d381de..a38085c000d 100644
--- 
a/systests/ldap/src/test/java/org/apache/cxf/systest/ldap/xkms/LDAPCertificateRepoTest.java
+++ 
b/systests/ldap/src/test/java/org/apache/cxf/systest/ldap/xkms/LDAPCertificateRepoTest.java
@@ -24,12 +24,18 @@ import java.net.URL;
 import java.security.cert.CertificateException;
 import java.security.cert.CertificateFactory;
 import java.security.cert.X509Certificate;
+import java.util.UUID;
 
 import javax.naming.NamingException;
 
 import org.apache.cxf.testutil.common.AbstractClientServerTestBase;
 import org.apache.cxf.xkms.handlers.Applications;
+import org.apache.cxf.xkms.handlers.XKMSConstants;
+import org.apache.cxf.xkms.model.xkms.LocateRequestType;
+import org.apache.cxf.xkms.model.xkms.QueryKeyBindingType;
+import org.apache.cxf.xkms.model.xkms.UnverifiedKeyBindingType;
 import org.apache.cxf.xkms.model.xkms.UseKeyWithType;
+import org.apache.cxf.xkms.x509.handlers.X509Locator;
 import org.apache.cxf.xkms.x509.repo.CertificateRepo;
 import org.apache.cxf.xkms.x509.repo.ldap.LdapCertificateRepo;
 import org.apache.cxf.xkms.x509.repo.ldap.LdapSchemaConfig;
@@ -61,6 +67,8 @@ public class LDAPCertificateRepoTest {
     private static final String ROOT_DN = "dc=example,dc=com";
     private static final String EXPECTED_SUBJECT_DN2 = "cn=newuser,ou=users";
     private static final String EXPECTED_SERVICE_URI = 
"http://myservice.apache.org/MyServiceName";;
+    private static final String LOCATOR_SERVICE_URI = 
"http://x509locator.test/MyServiceName";;
+    private static final String LOCATOR_ENDPOINT_URI = 
"http://x509locator.test/endpoint";;
 
     @org.junit.AfterClass
     public static void cleanup() throws Exception {
@@ -134,6 +142,213 @@ public class LDAPCertificateRepoTest {
         assertNotNull(foundCert);
     }
 
+    @Test
+    public void testX509LocatorFindBySubjectDn() throws Exception {
+        CertificateRepo persistenceManager = createLdapCertificateRepo();
+        X509Locator locator = new X509Locator(persistenceManager);
+
+        LocateRequestType request = createLocateRequest(Applications.PKIX, 
EXPECTED_SUBJECT_DN);
+        UnverifiedKeyBindingType result = locator.locate(request);
+
+        assertNotNull(result);
+        assertNotNull(result.getKeyInfo());
+    }
+
+    @Test
+    public void testX509LocatorFindBySubjectDnLDAPInjection() throws Exception 
{
+        CertificateRepo persistenceManager = createLdapCertificateRepo();
+        X509Locator locator = new X509Locator(persistenceManager);
+
+        LocateRequestType request = createLocateRequest(Applications.PKIX, 
"cn=*");
+        UnverifiedKeyBindingType result = locator.locate(request);
+
+        assertNull(result);
+    }
+
+    @Test
+    public void testX509LocatorReturnsNullForUnknownSubjectDn() throws 
Exception {
+        CertificateRepo persistenceManager = createLdapCertificateRepo();
+        X509Locator locator = new X509Locator(persistenceManager);
+
+        LocateRequestType request = createLocateRequest(Applications.PKIX, 
"cn=nobody,ou=users");
+        UnverifiedKeyBindingType result = locator.locate(request);
+
+        assertNull(result);
+    }
+
+    @Test
+    public void testX509LocatorFindByServiceName() throws Exception {
+        CertificateRepo persistenceManager = createLdapCertificateRepo();
+
+        // Save a cert under a service name first
+        URL url = this.getClass().getResource("cert1.cer");
+        CertificateFactory factory = CertificateFactory.getInstance("X.509");
+        X509Certificate cert = (X509Certificate) 
factory.generateCertificate(url.openStream());
+        UseKeyWithType key = new UseKeyWithType();
+        key.setApplication(Applications.SERVICE_NAME.getUri());
+        key.setIdentifier(LOCATOR_SERVICE_URI);
+        persistenceManager.saveCertificate(cert, key);
+
+        X509Locator locator = new X509Locator(persistenceManager);
+        LocateRequestType request = 
createLocateRequest(Applications.SERVICE_NAME, LOCATOR_SERVICE_URI);
+        UnverifiedKeyBindingType result = locator.locate(request);
+
+        assertNotNull(result);
+        assertNotNull(result.getKeyInfo());
+    }
+
+    @Test
+    public void testX509LocatorFindByServiceNameLDAPInjection() throws 
Exception {
+        CertificateRepo persistenceManager = createLdapCertificateRepo();
+
+        // Save a cert under a service name first
+        URL url = this.getClass().getResource("cert1.cer");
+        CertificateFactory factory = CertificateFactory.getInstance("X.509");
+        X509Certificate cert = (X509Certificate) 
factory.generateCertificate(url.openStream());
+        UseKeyWithType key = new UseKeyWithType();
+        key.setApplication(Applications.SERVICE_NAME.getUri());
+        // Ensure this test can be rerun in the same JVM without DN collisions.
+        key.setIdentifier(LOCATOR_SERVICE_URI + "/" + UUID.randomUUID());
+        persistenceManager.saveCertificate(cert, key);
+
+        X509Locator locator = new X509Locator(persistenceManager);
+        LocateRequestType request = 
createLocateRequest(Applications.SERVICE_NAME, "cn=*");
+        UnverifiedKeyBindingType result = locator.locate(request);
+
+        assertNull(result);
+    }
+
+    @Test
+    public void testX509LocatorFindByEndpoint() throws Exception {
+        CertificateRepo persistenceManager = createLdapCertificateRepo();
+
+        // Save a cert with a service endpoint attribute first
+        URL url = this.getClass().getResource("cert1.cer");
+        CertificateFactory factory = CertificateFactory.getInstance("X.509");
+        X509Certificate cert = (X509Certificate) 
factory.generateCertificate(url.openStream());
+        UseKeyWithType key = new UseKeyWithType();
+        key.setApplication(Applications.SERVICE_ENDPOINT.getUri());
+        String identifier = LOCATOR_ENDPOINT_URI + UUID.randomUUID();
+        key.setIdentifier(identifier);
+        persistenceManager.saveCertificate(cert, key);
+
+        X509Locator locator = new X509Locator(persistenceManager);
+        LocateRequestType request = 
createLocateRequest(Applications.SERVICE_ENDPOINT, identifier);
+        UnverifiedKeyBindingType result = locator.locate(request);
+
+        assertNotNull(result);
+        assertNotNull(result.getKeyInfo());
+    }
+
+    @Test
+    public void testX509LocatorFindByEndpointLDAPInjection() throws Exception {
+        CertificateRepo persistenceManager = createLdapCertificateRepo();
+
+        // Save a cert with a service endpoint attribute first
+        URL url = this.getClass().getResource("cert1.cer");
+        CertificateFactory factory = CertificateFactory.getInstance("X.509");
+        X509Certificate cert = (X509Certificate) 
factory.generateCertificate(url.openStream());
+        UseKeyWithType key = new UseKeyWithType();
+        key.setApplication(Applications.SERVICE_ENDPOINT.getUri());
+        String identifier = LOCATOR_ENDPOINT_URI + UUID.randomUUID();
+        key.setIdentifier(identifier);
+        persistenceManager.saveCertificate(cert, key);
+
+        X509Locator locator = new X509Locator(persistenceManager);
+        LocateRequestType request = 
createLocateRequest(Applications.SERVICE_ENDPOINT, "*");
+        UnverifiedKeyBindingType result = locator.locate(request);
+
+        assertNull(result);
+    }
+
+    @Test
+    public void testX509LocatorFindByIssuerSerial() throws Exception {
+        CertificateRepo persistenceManager = createLdapCertificateRepo();
+
+        // Save a cert so its issuer/serial attributes are written to LDAP
+        URL url = this.getClass().getResource("cert1.cer");
+        CertificateFactory factory = CertificateFactory.getInstance("X.509");
+        X509Certificate cert = (X509Certificate) 
factory.generateCertificate(url.openStream());
+        UseKeyWithType key = new UseKeyWithType();
+        key.setApplication(Applications.PKIX.getUri());
+        key.setIdentifier("cn=issuerserialtest,ou=users");
+        persistenceManager.saveCertificate(cert, key);
+
+        String issuer = cert.getIssuerX500Principal().getName();
+        String serial = cert.getSerialNumber().toString(16);
+
+        X509Locator locator = new X509Locator(persistenceManager);
+        LocateRequestType request = createIssuerSerialLocateRequest(issuer, 
serial);
+        UnverifiedKeyBindingType result = locator.locate(request);
+
+        assertNotNull(result);
+        assertNotNull(result.getKeyInfo());
+    }
+
+    @Test
+    public void testX509LocatorFindByIssuerSerialLDAPInjection() throws 
Exception {
+        CertificateRepo persistenceManager = createLdapCertificateRepo();
+
+        // Save a cert so its issuer/serial attributes are written to LDAP
+        URL url = this.getClass().getResource("cert1.cer");
+        CertificateFactory factory = CertificateFactory.getInstance("X.509");
+        X509Certificate cert = (X509Certificate) 
factory.generateCertificate(url.openStream());
+        UseKeyWithType key = new UseKeyWithType();
+        key.setApplication(Applications.PKIX.getUri());
+        // Ensure this test does not collide with the non-injection 
issuer/serial test entry.
+        key.setIdentifier("cn=issuerserialtest-" + UUID.randomUUID() + 
",ou=users");
+        persistenceManager.saveCertificate(cert, key);
+
+        String serial = cert.getSerialNumber().toString(16);
+
+        X509Locator locator = new X509Locator(persistenceManager);
+        LocateRequestType request = createIssuerSerialLocateRequest("*", 
serial);
+        UnverifiedKeyBindingType result = locator.locate(request);
+
+        assertNull(result);
+    }
+
+    private LocateRequestType createIssuerSerialLocateRequest(String issuer, 
String serial) {
+        org.apache.cxf.xkms.model.xkms.ObjectFactory xkmsOf =
+            new org.apache.cxf.xkms.model.xkms.ObjectFactory();
+
+        UseKeyWithType issuerKey = xkmsOf.createUseKeyWithType();
+        issuerKey.setApplication(Applications.ISSUER.getUri());
+        issuerKey.setIdentifier(issuer);
+
+        UseKeyWithType serialKey = xkmsOf.createUseKeyWithType();
+        serialKey.setApplication(Applications.SERIAL.getUri());
+        serialKey.setIdentifier(serial);
+
+        QueryKeyBindingType queryKeyBinding = 
xkmsOf.createQueryKeyBindingType();
+        queryKeyBinding.getUseKeyWith().add(issuerKey);
+        queryKeyBinding.getUseKeyWith().add(serialKey);
+
+        LocateRequestType request = xkmsOf.createLocateRequestType();
+        request.setQueryKeyBinding(queryKeyBinding);
+        request.setService(XKMSConstants.XKMS_ENDPOINT_NAME);
+        request.setId(UUID.randomUUID().toString());
+        return request;
+    }
+
+    private LocateRequestType createLocateRequest(Applications application, 
String identifier) {
+        org.apache.cxf.xkms.model.xkms.ObjectFactory xkmsOf =
+            new org.apache.cxf.xkms.model.xkms.ObjectFactory();
+
+        UseKeyWithType useKeyWithType = xkmsOf.createUseKeyWithType();
+        useKeyWithType.setApplication(application.getUri());
+        useKeyWithType.setIdentifier(identifier);
+
+        QueryKeyBindingType queryKeyBinding = 
xkmsOf.createQueryKeyBindingType();
+        queryKeyBinding.getUseKeyWith().add(useKeyWithType);
+
+        LocateRequestType request = xkmsOf.createLocateRequestType();
+        request.setQueryKeyBinding(queryKeyBinding);
+        request.setService(XKMSConstants.XKMS_ENDPOINT_NAME);
+        request.setId(UUID.randomUUID().toString());
+        return request;
+    }
+
     private CertificateRepo createLdapCertificateRepo() throws 
CertificateException {
         LdapSearch ldapSearch = new LdapSearch("ldap://localhost:"; + 
embeddedLdapRule.embeddedServerPort(),
             "UID=admin,DC=example,DC=com", "ldap_su", 2);

Reply via email to