From ef075525c85be644ed4fc8ff712b14c85ed14ab7 Mon Sep 17 00:00:00 2001 From: Martin Stockhammer Date: Sun, 10 May 2020 11:02:54 +0200 Subject: [PATCH] Improving LDAP filters by escaping characters --- .../ldap/LdapBindAuthenticator.java | 3 +- .../ldap/LdapBindAuthenticatorTest.java | 11 ++++ .../redback/common/ldap/LdapUtils.java | 56 +++++++++++++++++++ .../redback/users/ldap/LdapUserQuery.java | 7 ++- .../users/ldap/LdapUserManagerTest.java | 15 +++++ 5 files changed, 88 insertions(+), 4 deletions(-) diff --git a/redback-authentication/redback-authentication-providers/redback-authentication-ldap/src/main/java/org/apache/archiva/redback/authentication/ldap/LdapBindAuthenticator.java b/redback-authentication/redback-authentication-providers/redback-authentication-ldap/src/main/java/org/apache/archiva/redback/authentication/ldap/LdapBindAuthenticator.java index 9c33ffdd..3c46c78a 100644 --- a/redback-authentication/redback-authentication-providers/redback-authentication-ldap/src/main/java/org/apache/archiva/redback/authentication/ldap/LdapBindAuthenticator.java +++ b/redback-authentication/redback-authentication-providers/redback-authentication-ldap/src/main/java/org/apache/archiva/redback/authentication/ldap/LdapBindAuthenticator.java @@ -20,6 +20,7 @@ package org.apache.archiva.redback.authentication.ldap; */ import org.apache.archiva.redback.authentication.AbstractAuthenticator; +import org.apache.archiva.redback.common.ldap.LdapUtils; import org.apache.archiva.redback.common.ldap.connection.DefaultLdapConnection; import org.apache.archiva.redback.common.ldap.connection.LdapConnection; import org.apache.archiva.redback.common.ldap.user.UserMapper; @@ -100,7 +101,7 @@ public class LdapBindAuthenticator String filter = "(&(objectClass=" + mapper.getUserObjectClass() + ")" + ( mapper.getUserFilter() != null ? mapper.getUserFilter() - : "" ) + "(" + mapper.getUserIdAttribute() + "=" + source.getUsername() + "))"; + : "" ) + "(" + mapper.getUserIdAttribute() + "=" + LdapUtils.encodeFilterValue( source.getUsername() ) + "))"; log.debug( "Searching for users with filter: '{}' from base dn: {}", filter, mapper.getUserBaseDn() ); diff --git a/redback-authentication/redback-authentication-providers/redback-authentication-ldap/src/test/java/org/apache/archiva/redback/authentication/ldap/LdapBindAuthenticatorTest.java b/redback-authentication/redback-authentication-providers/redback-authentication-ldap/src/test/java/org/apache/archiva/redback/authentication/ldap/LdapBindAuthenticatorTest.java index 84ee8bef..e668f841 100644 --- a/redback-authentication/redback-authentication-providers/redback-authentication-ldap/src/test/java/org/apache/archiva/redback/authentication/ldap/LdapBindAuthenticatorTest.java +++ b/redback-authentication/redback-authentication-providers/redback-authentication-ldap/src/test/java/org/apache/archiva/redback/authentication/ldap/LdapBindAuthenticatorTest.java @@ -137,6 +137,17 @@ public class LdapBindAuthenticatorTest assertTrue( result.isAuthenticated() ); } + @Test + public void testAuthenticationWithInvalidChar() + throws Exception + { + PasswordBasedAuthenticationDataSource authDs = new PasswordBasedAuthenticationDataSource(); + authDs.setPrincipal( "jesse)(mail=foo" ); + authDs.setPassword( passwordEncoder.encodePassword( "foo" ) ); + AuthenticationResult result = authnr.authenticate( authDs ); + assertFalse( result.isAuthenticated() ); + } + // REDBACK-289/MRM-1488 @Test public void testAuthenticationFromCache() diff --git a/redback-common/redback-common-ldap/src/main/java/org/apache/archiva/redback/common/ldap/LdapUtils.java b/redback-common/redback-common-ldap/src/main/java/org/apache/archiva/redback/common/ldap/LdapUtils.java index 529a2d9d..b1c45cb0 100644 --- a/redback-common/redback-common-ldap/src/main/java/org/apache/archiva/redback/common/ldap/LdapUtils.java +++ b/redback-common/redback-common-ldap/src/main/java/org/apache/archiva/redback/common/ldap/LdapUtils.java @@ -34,6 +34,28 @@ import javax.naming.ldap.Rdn; */ public final class LdapUtils { + + private static String[] FILTER_ESCAPE_TABLE = new String['\\' + 1]; + + + // Characters that must be escaped in a user filter + static { + + // Filter encoding table ------------------------------------- + // fill with char itself + for (char c = 0; c < FILTER_ESCAPE_TABLE.length; c++) { + FILTER_ESCAPE_TABLE[c] = String.valueOf(c); + } + + // escapes (RFC2254) + FILTER_ESCAPE_TABLE['*'] = "\\2a"; + FILTER_ESCAPE_TABLE['('] = "\\28"; + FILTER_ESCAPE_TABLE[')'] = "\\29"; + FILTER_ESCAPE_TABLE['\\'] = "\\5c"; + FILTER_ESCAPE_TABLE[0] = "\\00"; + } + + private LdapUtils() { // no op @@ -172,4 +194,38 @@ public final class LdapUtils } return ""; } + + /** + * Escape a value for use in a filter. + * This method is copied from the spring framework class org.springframework.security.ldap.authentication.LdapEncoder + * + * @param value the value to escape. + * @return a properly escaped representation of the supplied value. + */ + public static String encodeFilterValue(String value) { + + if (value == null) { + return null; + } + + // make buffer roomy + StringBuilder encodedValue = new StringBuilder(value.length() * 2); + + int length = value.length(); + + for (int i = 0; i < length; i++) { + + char c = value.charAt(i); + + if (c < FILTER_ESCAPE_TABLE.length) { + encodedValue.append(FILTER_ESCAPE_TABLE[c]); + } + else { + // default: add the char + encodedValue.append(c); + } + } + + return encodedValue.toString(); + } } diff --git a/redback-users/redback-users-providers/redback-users-ldap/src/main/java/org/apache/archiva/redback/users/ldap/LdapUserQuery.java b/redback-users/redback-users-providers/redback-users-ldap/src/main/java/org/apache/archiva/redback/users/ldap/LdapUserQuery.java index 6f097944..d20063a8 100644 --- a/redback-users/redback-users-providers/redback-users-ldap/src/main/java/org/apache/archiva/redback/users/ldap/LdapUserQuery.java +++ b/redback-users/redback-users-providers/redback-users-ldap/src/main/java/org/apache/archiva/redback/users/ldap/LdapUserQuery.java @@ -19,6 +19,7 @@ package org.apache.archiva.redback.users.ldap; * under the License. */ +import org.apache.archiva.redback.common.ldap.LdapUtils; import org.apache.archiva.redback.common.ldap.user.UserMapper; import org.apache.archiva.redback.users.AbstractUserQuery; @@ -49,13 +50,13 @@ public class LdapUserQuery String filter = ""; if (this.getEmail() != null ) { - filter += "(" + mapper.getEmailAddressAttribute() + "=" + this.getEmail() + ")"; + filter += "(" + mapper.getEmailAddressAttribute() + "=" + LdapUtils.encodeFilterValue( this.getEmail() ) + ")"; } if ( this.getFullName() != null ) { - filter += "(" + mapper.getUserFullNameAttribute() + "=" + this.getFullName() + ")"; + filter += "(" + mapper.getUserFullNameAttribute() + "=" + LdapUtils.encodeFilterValue( this.getFullName() ) + ")"; } - filter += "(" + mapper.getUserIdAttribute() + "=" + ( this.getUsername() != null ? this.getUsername() : "*" ) + ")"; + filter += "(" + mapper.getUserIdAttribute() + "=" + ( this.getUsername() != null ? LdapUtils.encodeFilterValue( this.getUsername() ) : "*" ) + ")"; return filter; } diff --git a/redback-users/redback-users-providers/redback-users-ldap/src/test/java/org/apache/archiva/redback/users/ldap/LdapUserManagerTest.java b/redback-users/redback-users-providers/redback-users-ldap/src/test/java/org/apache/archiva/redback/users/ldap/LdapUserManagerTest.java index 88ea9228..ca061189 100644 --- a/redback-users/redback-users-providers/redback-users-ldap/src/test/java/org/apache/archiva/redback/users/ldap/LdapUserManagerTest.java +++ b/redback-users/redback-users-providers/redback-users-ldap/src/test/java/org/apache/archiva/redback/users/ldap/LdapUserManagerTest.java @@ -233,6 +233,21 @@ public class LdapUserManagerTest } } + @Test + public void testUserWithInvalidChars() + throws Exception + { + try + { + userManager.findUser( "jesse)(mail=jesse@apache.org" ); + fail( "UserNotFoundException should be thrown, if invalid filter chars are in the username" ); + } + catch ( UserNotFoundException e ) + { + // cool it works ! + } + } + @Test public void testWithManyUsers() throws Exception