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

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

commit c4df8d44a959a937d507d15e5b1ca35c3dbc41eb
Author: Mark Thomas <ma...@apache.org>
AuthorDate: Tue Apr 13 11:12:02 2021 +0100

    Add attribute value escaping to support user names containing ';'
---
 java/org/apache/catalina/realm/JNDIRealm.java      | 79 +++++++++++++++++++-
 .../realm/TestJNDIRealmAttributeValueEscape.java   | 86 ++++++++++++++++++++++
 .../catalina/realm/TestJNDIRealmIntegration.java   | 15 +++-
 3 files changed, 177 insertions(+), 3 deletions(-)

diff --git a/java/org/apache/catalina/realm/JNDIRealm.java 
b/java/org/apache/catalina/realm/JNDIRealm.java
index d94ed7f..1e81d89 100644
--- a/java/org/apache/catalina/realm/JNDIRealm.java
+++ b/java/org/apache/catalina/realm/JNDIRealm.java
@@ -1541,8 +1541,11 @@ public class JNDIRealm extends RealmBase {
             return null;
         }
 
-        // Form the dn from the user pattern
-        String dn = 
connection.userPatternFormatArray[curUserPattern].format(new String[] { 
username });
+        // Form the DistinguishedName from the user pattern.
+        // Escape in case username contains a character with special meaning in
+        // an attribute value.
+        String dn = connection.userPatternFormatArray[curUserPattern].format(
+                new String[] { doAttributeValueEscaping(username) });
 
         try {
             user = getUserByPattern(connection.context, username, attrIds, dn);
@@ -2824,6 +2827,78 @@ public class JNDIRealm extends RealmBase {
     }
 
 
+    /**
+     * Implements the necessary escaping to represent an attribute value as a
+     * String as per RFC 4514.
+     *
+     * @param input The original attribute value
+     * @return      The string representation of the attribute value
+     */
+    protected String doAttributeValueEscaping(String input) {
+        int len = input.length();
+        StringBuilder result = new StringBuilder();
+
+        for (int i = 0; i < len; i++) {
+            char c = input.charAt(i);
+            switch (c) {
+                case ' ': {
+                    if (i == 0 || i == (len -1)) {
+                        result.append("\\20");
+                    } else {
+                        result.append(c);
+                    }
+                    break;
+                }
+                case '#': {
+                    if (i == 0 ) {
+                        result.append("\\23");
+                    } else {
+                        result.append(c);
+                    }
+                    break;
+                }
+                case '\"': {
+                    result.append("\\22");
+                    break;
+                }
+                case '+': {
+                    result.append("\\2B");
+                    break;
+                }
+                case ',': {
+                    result.append("\\2C");
+                    break;
+                }
+                case ';': {
+                    result.append("\\3B");
+                    break;
+                }
+                case '<': {
+                    result.append("\\3C");
+                    break;
+                }
+                case '>': {
+                    result.append("\\3E");
+                    break;
+                }
+                case '\\': {
+                    result.append("\\5C");
+                    break;
+                }
+                case '\u0000': {
+                    result.append("\\00");
+                    break;
+                }
+                default:
+                    result.append(c);
+            }
+
+        }
+
+        return result.toString();
+    }
+
+
     protected static String convertToHexEscape(String input) {
         if (input.indexOf('\\') == -1) {
             // No escaping present. Return original.
diff --git 
a/test/org/apache/catalina/realm/TestJNDIRealmAttributeValueEscape.java 
b/test/org/apache/catalina/realm/TestJNDIRealmAttributeValueEscape.java
new file mode 100644
index 0000000..677bcc5
--- /dev/null
+++ b/test/org/apache/catalina/realm/TestJNDIRealmAttributeValueEscape.java
@@ -0,0 +1,86 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.catalina.realm;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+
+@RunWith(Parameterized.class)
+public class TestJNDIRealmAttributeValueEscape {
+
+    @Parameterized.Parameters(name = "{index}: in[{0}], out[{1}]")
+    public static Collection<Object[]> parameters() {
+        List<Object[]> parameterSets = new ArrayList<>();
+
+        // No escaping required
+        parameterSets.add(new String[] { "none", "none" });
+        // Simple cases (same order as RFC 4512 section 2)
+        // Each appearing at the beginning, middle and ent
+        parameterSets.add(new String[] { " test", "\\20test" });
+        parameterSets.add(new String[] { "te st", "te st" });
+        parameterSets.add(new String[] { "test ", "test\\20" });
+        parameterSets.add(new String[] { "#test", "\\23test" });
+        parameterSets.add(new String[] { "te#st", "te#st" });
+        parameterSets.add(new String[] { "test#", "test#" });
+        parameterSets.add(new String[] { "\"test", "\\22test" });
+        parameterSets.add(new String[] { "te\"st", "te\\22st" });
+        parameterSets.add(new String[] { "test\"", "test\\22" });
+        parameterSets.add(new String[] { "+test", "\\2Btest" });
+        parameterSets.add(new String[] { "te+st", "te\\2Bst" });
+        parameterSets.add(new String[] { "test+", "test\\2B" });
+        parameterSets.add(new String[] { ",test", "\\2Ctest" });
+        parameterSets.add(new String[] { "te,st", "te\\2Cst" });
+        parameterSets.add(new String[] { "test,", "test\\2C" });
+        parameterSets.add(new String[] { ";test", "\\3Btest" });
+        parameterSets.add(new String[] { "te;st", "te\\3Bst" });
+        parameterSets.add(new String[] { "test;", "test\\3B" });
+        parameterSets.add(new String[] { "<test", "\\3Ctest" });
+        parameterSets.add(new String[] { "te<st", "te\\3Cst" });
+        parameterSets.add(new String[] { "test<", "test\\3C" });
+        parameterSets.add(new String[] { ">test", "\\3Etest" });
+        parameterSets.add(new String[] { "te>st", "te\\3Est" });
+        parameterSets.add(new String[] { "test>", "test\\3E" });
+        parameterSets.add(new String[] { "\\test", "\\5Ctest" });
+        parameterSets.add(new String[] { "te\\st", "te\\5Cst" });
+        parameterSets.add(new String[] { "test\\", "test\\5C" });
+        parameterSets.add(new String[] { "\u0000test", "\\00test" });
+        parameterSets.add(new String[] { "te\u0000st", "te\\00st" });
+        parameterSets.add(new String[] { "test\u0000", "test\\00" });
+        return parameterSets;
+    }
+
+
+    @Parameter(0)
+    public String in;
+    @Parameter(1)
+    public String out;
+
+    private JNDIRealm realm = new JNDIRealm();
+
+    @Test
+    public void testConvertToHexEscape() throws Exception {
+        String result = realm.doAttributeValueEscaping(in);
+        Assert.assertEquals(out, result);
+    }
+}
\ No newline at end of file
diff --git a/test/org/apache/catalina/realm/TestJNDIRealmIntegration.java 
b/test/org/apache/catalina/realm/TestJNDIRealmIntegration.java
index 03e1655..ca45053 100644
--- a/test/org/apache/catalina/realm/TestJNDIRealmIntegration.java
+++ b/test/org/apache/catalina/realm/TestJNDIRealmIntegration.java
@@ -50,6 +50,7 @@ public class TestJNDIRealmIntegration {
         List<Object[]> parameterSets = new ArrayList<>();
 
         parameterSets.add(new Object[] { "test", "test", new String[] 
{"TestGroup"} });
+        parameterSets.add(new Object[] { "t;", "test", new String[] 
{"TestGroup"} });
 
         return parameterSets;
     }
@@ -125,12 +126,24 @@ public class TestJNDIRealmIntegration {
             result = conn.processOperation(addUserTest);
             Assert.assertEquals(ResultCode.SUCCESS, result.getResultCode());
 
+            AddRequest addUserTestSemicolon = new AddRequest(
+                    "dn: cn=t\\;,ou=people,dc=example,dc=com",
+                    "objectClass: top",
+                    "objectClass: person",
+                    "objectClass: organizationalPerson",
+                    "cn: test",
+                    "sn: Test",
+                    "userPassword: test");
+            result = conn.processOperation(addUserTestSemicolon);
+            Assert.assertEquals(ResultCode.SUCCESS, result.getResultCode());
+
             AddRequest addGroupTest = new AddRequest(
                     "dn: cn=TestGroup,ou=people,dc=example,dc=com",
                     "objectClass: top",
                     "objectClass: groupOfNames",
                     "cn: TestGroup",
-                    "member: cn=test,ou=people,dc=example,dc=com");
+                    "member: cn=test,ou=people,dc=example,dc=com",
+                    "member: cn=t\\;,ou=people,dc=example,dc=com");
             result = conn.processOperation(addGroupTest);
             Assert.assertEquals(ResultCode.SUCCESS, result.getResultCode());
         }

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org
For additional commands, e-mail: dev-h...@tomcat.apache.org

Reply via email to