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);