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

jbonofre pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/karaf.git


The following commit(s) were added to refs/heads/main by this push:
     new d2cff4af22 Escape %u/%dn/%fqdn substitutions in LDAP search filters 
(CWE-90) (#2880)
d2cff4af22 is described below

commit d2cff4af220e23cbc9066b393bdb0f03931d0b68
Author: JB Onofré <[email protected]>
AuthorDate: Fri Sep 11 16:07:54 2026 +0200

    Escape %u/%dn/%fqdn substitutions in LDAP search filters (CWE-90) (#2880)
    
    LDAPCache and LDAPBackingEngine.lookupUser built search filters by
    substituting %u/%dn/%fqdn and only doubling backslashes, which does not
    escape *, (, ), or NUL. LDAPLoginModule and LDAPPubkeyLoginModule masked
    this by pre-escaping the username themselves, but GSSAPILdapLoginModule
    and LDAPBackingEngine.lookupUser did not, so filter metacharacters in a
    login name could widen a search (e.g. a username of "*" matches any
    entry) and potentially over-grant roles.
    
    Centralize proper RFC 4515 filter escaping in LDAPCache/LDAPBackingEngine
    using the existing Util.doRFC2254Encoding helper, and drop the redundant
    pre-escaping in LDAPLoginModule/LDAPPubkeyLoginModule so escaping happens
    exactly once, at the point filters are built.
---
 .../karaf/jaas/modules/ldap/LDAPBackingEngine.java      |  4 ++--
 .../org/apache/karaf/jaas/modules/ldap/LDAPCache.java   | 10 ++++------
 .../apache/karaf/jaas/modules/ldap/LDAPLoginModule.java |  2 +-
 .../karaf/jaas/modules/ldap/LDAPPubkeyLoginModule.java  |  2 +-
 .../apache/karaf/jaas/modules/ldap/LdapCacheTest.java   | 17 +++++++++++++++++
 5 files changed, 25 insertions(+), 10 deletions(-)

diff --git 
a/jaas/modules/src/main/java/org/apache/karaf/jaas/modules/ldap/LDAPBackingEngine.java
 
b/jaas/modules/src/main/java/org/apache/karaf/jaas/modules/ldap/LDAPBackingEngine.java
index 1039a60ee8..9a4528bdf2 100644
--- 
a/jaas/modules/src/main/java/org/apache/karaf/jaas/modules/ldap/LDAPBackingEngine.java
+++ 
b/jaas/modules/src/main/java/org/apache/karaf/jaas/modules/ldap/LDAPBackingEngine.java
@@ -31,6 +31,7 @@ import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
 import java.util.Map;
+import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
 /**
@@ -74,8 +75,7 @@ public class LDAPBackingEngine implements BackingEngine {
             }
 
             String filter = options.getUserFilter();
-            filter = filter.replaceAll(Pattern.quote("%u"), username);
-            filter = filter.replace("\\", "\\\\");
+            filter = filter.replaceAll(Pattern.quote("%u"), 
Matcher.quoteReplacement(Util.doRFC2254Encoding(username)));
 
             LOGGER.debug("Looking for user {} in LDAP with", username);
             LOGGER.debug("   base DN: {}", options.getUserBaseDn());
diff --git 
a/jaas/modules/src/main/java/org/apache/karaf/jaas/modules/ldap/LDAPCache.java 
b/jaas/modules/src/main/java/org/apache/karaf/jaas/modules/ldap/LDAPCache.java
index 20759e8ee5..d459f6d61b 100644
--- 
a/jaas/modules/src/main/java/org/apache/karaf/jaas/modules/ldap/LDAPCache.java
+++ 
b/jaas/modules/src/main/java/org/apache/karaf/jaas/modules/ldap/LDAPCache.java
@@ -196,8 +196,7 @@ public class LDAPCache implements Closeable, 
NamespaceChangeListener, ObjectChan
         }
 
         String filter = options.getUserFilter();
-        filter = filter.replaceAll(Pattern.quote("%u"), 
Matcher.quoteReplacement(user));
-        filter = filter.replace("\\", "\\\\");
+        filter = filter.replaceAll(Pattern.quote("%u"), 
Matcher.quoteReplacement(Util.doRFC2254Encoding(user)));
 
         LOGGER.debug("Looking for the user in LDAP with ");
         LOGGER.debug("  base DN: " + options.getUserBaseDn());
@@ -297,10 +296,9 @@ public class LDAPCache implements Closeable, 
NamespaceChangeListener, ObjectChan
 
         String filter = options.getRoleFilter();
         if (filter != null) {
-            filter = filter.replaceAll(Pattern.quote("%u"), 
Matcher.quoteReplacement(user));
-            filter = filter.replaceAll(Pattern.quote("%dn"), 
Matcher.quoteReplacement(userDn));
-            filter = filter.replaceAll(Pattern.quote("%fqdn"), 
Matcher.quoteReplacement(userDnNamespace));
-            filter = filter.replace("\\", "\\\\");
+            filter = filter.replaceAll(Pattern.quote("%u"), 
Matcher.quoteReplacement(Util.doRFC2254Encoding(user)));
+            filter = filter.replaceAll(Pattern.quote("%dn"), 
Matcher.quoteReplacement(Util.doRFC2254Encoding(userDn)));
+            filter = filter.replaceAll(Pattern.quote("%fqdn"), 
Matcher.quoteReplacement(Util.doRFC2254Encoding(userDnNamespace)));
 
             LOGGER.debug("Looking for the user roles in LDAP with ");
             LOGGER.debug("  base DN: {}", options.getRoleBaseDn());
diff --git 
a/jaas/modules/src/main/java/org/apache/karaf/jaas/modules/ldap/LDAPLoginModule.java
 
b/jaas/modules/src/main/java/org/apache/karaf/jaas/modules/ldap/LDAPLoginModule.java
index e64e36e3f9..c979e41cec 100644
--- 
a/jaas/modules/src/main/java/org/apache/karaf/jaas/modules/ldap/LDAPLoginModule.java
+++ 
b/jaas/modules/src/main/java/org/apache/karaf/jaas/modules/ldap/LDAPLoginModule.java
@@ -70,7 +70,7 @@ public class LDAPLoginModule extends AbstractKarafLoginModule 
{
             throw new LoginException(unsupportedCallbackException.getMessage() 
+ " not available to obtain information from user.");
         }
 
-        user = Util.doRFC2254Encoding(((NameCallback) callbacks[0]).getName());
+        user = ((NameCallback) callbacks[0]).getName();
 
         char[] tmpPassword = ((PasswordCallback) callbacks[1]).getPassword();
 
diff --git 
a/jaas/modules/src/main/java/org/apache/karaf/jaas/modules/ldap/LDAPPubkeyLoginModule.java
 
b/jaas/modules/src/main/java/org/apache/karaf/jaas/modules/ldap/LDAPPubkeyLoginModule.java
index 189efbb958..226b2d86da 100644
--- 
a/jaas/modules/src/main/java/org/apache/karaf/jaas/modules/ldap/LDAPPubkeyLoginModule.java
+++ 
b/jaas/modules/src/main/java/org/apache/karaf/jaas/modules/ldap/LDAPPubkeyLoginModule.java
@@ -69,7 +69,7 @@ public class LDAPPubkeyLoginModule extends 
AbstractKarafLoginModule {
             throw new LoginException(unsupportedCallbackException.getMessage() 
+ " not available to obtain information from user.");
         }
 
-        user = Util.doRFC2254Encoding(((NameCallback) callbacks[0]).getName());
+        user = ((NameCallback) callbacks[0]).getName();
 
         PublicKey remotePubkey = ((PublickeyCallback) 
callbacks[1]).getPublicKey();
 
diff --git 
a/jaas/modules/src/test/java/org/apache/karaf/jaas/modules/ldap/LdapCacheTest.java
 
b/jaas/modules/src/test/java/org/apache/karaf/jaas/modules/ldap/LdapCacheTest.java
index 9d26d1dd5e..5f86f574aa 100644
--- 
a/jaas/modules/src/test/java/org/apache/karaf/jaas/modules/ldap/LdapCacheTest.java
+++ 
b/jaas/modules/src/test/java/org/apache/karaf/jaas/modules/ldap/LdapCacheTest.java
@@ -19,6 +19,7 @@ import static 
org.apache.karaf.jaas.modules.PrincipalHelper.names;
 import static org.apache.karaf.jaas.modules.ldap.LdapPropsUpdater.ldapProps;
 import static org.hamcrest.Matchers.containsInAnyOrder;
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertThat;
 import static org.junit.Assert.assertTrue;
 
@@ -120,6 +121,22 @@ public class LdapCacheTest extends AbstractLdapTestUnit {
         }
     }
 
+    @Test
+    public void testUserFilterInjectionDoesNotWidenSearch() throws Exception {
+        // "*" would widen (uid=%u) to (uid=*), matching any existing user, 
unless
+        // the substituted value is properly filter-escaped (CWE-90).
+        Properties options = ldapLoginModuleOptions();
+        LDAPCache cache = LDAPCache.getCache(new LDAPOptions(options));
+        assertNull(cache.getUserDnAndNamespace("*"));
+    }
+
+    @Test
+    public void testBackingEngineUserFilterInjectionDoesNotWidenSearch() 
throws Exception {
+        Properties options = ldapLoginModuleOptions();
+        LDAPBackingEngine engine = new LDAPBackingEngine(options);
+        assertNull(engine.lookupUser("*"));
+    }
+
     @Test
     public void testLDAPCacheHashCode() throws Exception {
         Properties options = ldapLoginModuleOptions();

Reply via email to