From e53e018771bf9e30558e21950a8cbcd4c567e103 Mon Sep 17 00:00:00 2001
From: Valery Kharseko <vharseko@3a-systems.ru>
Date: Wed, 08 Jul 2026 16:08:15 +0000
Subject: [PATCH] [#673] Fix ArrayIndexOutOfBoundsException on truncated percent-encoding in LDAP URLs (#676)
---
opendj-server-legacy/src/test/java/org/opends/server/authorization/dseecompat/UserDNTestCase.java | 56 ++++++++++++++++++
opendj-core/src/test/java/org/forgerock/opendj/ldap/LDAPUrlTestCase.java | 30 ++++++++++
opendj-server-legacy/src/main/java/org/opends/server/types/LDAPURL.java | 3
opendj-server-legacy/src/test/java/org/opends/server/types/LDAPURLTestCase.java | 35 +++++++++++
opendj-core/src/main/java/org/forgerock/opendj/ldap/LDAPUrl.java | 7 ++
5 files changed, 130 insertions(+), 1 deletions(-)
diff --git a/opendj-core/src/main/java/org/forgerock/opendj/ldap/LDAPUrl.java b/opendj-core/src/main/java/org/forgerock/opendj/ldap/LDAPUrl.java
index f30d9a0..9bc4947 100644
--- a/opendj-core/src/main/java/org/forgerock/opendj/ldap/LDAPUrl.java
+++ b/opendj-core/src/main/java/org/forgerock/opendj/ldap/LDAPUrl.java
@@ -13,6 +13,7 @@
*
* Copyright 2010 Sun Microsystems, Inc.
* Portions copyright 2012-2016 ForgeRock AS.
+ * Portions Copyright 2026 3A Systems, LLC
*/
package org.forgerock.opendj.ldap;
@@ -246,6 +247,12 @@
dstPos++;
continue;
}
+ if (srcPos + 2 >= decoded.length()) {
+ // A percent sign must be followed by two hexadecimal digits.
+ final LocalizableMessage msg =
+ ERR_LDAPURL_INVALID_HEX_BYTE.get(urlString, index + srcPos + 1);
+ throw new LocalizedIllegalArgumentException(msg);
+ }
int i = decodeHex(urlString, index + srcPos + 1, decoded.charAt(srcPos + 1)) << 4;
int j = decodeHex(urlString, index + srcPos + 2, decoded.charAt(srcPos + 2));
decoded.setCharAt(dstPos, (char) (i | j));
diff --git a/opendj-core/src/test/java/org/forgerock/opendj/ldap/LDAPUrlTestCase.java b/opendj-core/src/test/java/org/forgerock/opendj/ldap/LDAPUrlTestCase.java
index a316d9b..1c3020e 100644
--- a/opendj-core/src/test/java/org/forgerock/opendj/ldap/LDAPUrlTestCase.java
+++ b/opendj-core/src/test/java/org/forgerock/opendj/ldap/LDAPUrlTestCase.java
@@ -12,6 +12,7 @@
* information: "Portions Copyright [year] [name of copyright owner]".
*
* Copyright 2010 Sun Microsystems, Inc.
+ * Portions Copyright 2026 3A Systems, LLC
*/
package org.forgerock.opendj.ldap;
@@ -19,6 +20,7 @@
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertTrue;
+import org.forgerock.i18n.LocalizedIllegalArgumentException;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
@@ -181,4 +183,32 @@
assertTrue(url1.equals(url2));
}
}
+
+ /**
+ * URLs with a percent sign followed by fewer than two hexadecimal digits.
+ *
+ * @return The malformed URLs.
+ */
+ @DataProvider
+ public Object[][] truncatedPercentUrls() {
+ return new Object[][] {
+ { "ldap:///cn=name%B" },
+ { "ldap:///cn=name%" },
+ };
+ }
+
+ /**
+ * A truncated percent-encoded sequence must be rejected with a clean decode error
+ * instead of a StringIndexOutOfBoundsException - see issue #673.
+ *
+ * @param url
+ * The URL to decode.
+ * @throws Exception
+ * If the test failed unexpectedly.
+ */
+ @Test(dataProvider = "truncatedPercentUrls",
+ expectedExceptions = LocalizedIllegalArgumentException.class)
+ public void testTruncatedPercentEncoding(final String url) throws Exception {
+ LDAPUrl.valueOf(url);
+ }
}
diff --git a/opendj-server-legacy/src/main/java/org/opends/server/types/LDAPURL.java b/opendj-server-legacy/src/main/java/org/opends/server/types/LDAPURL.java
index 8b4c50a..c16e437 100644
--- a/opendj-server-legacy/src/main/java/org/opends/server/types/LDAPURL.java
+++ b/opendj-server-legacy/src/main/java/org/opends/server/types/LDAPURL.java
@@ -13,6 +13,7 @@
*
* Copyright 2006-2008 Sun Microsystems, Inc.
* Portions Copyright 2012-2016 ForgeRock AS.
+ * Portions Copyright 2026 3A Systems, LLC
*/
package org.opends.server.types;
@@ -621,7 +622,7 @@
{
// There must be at least two bytes left. If not, then that's
// a problem.
- if (i+2 > length)
+ if (i+2 >= length)
{
LocalizableMessage message = ERR_LDAPURL_PERCENT_TOO_CLOSE_TO_END.get(s, i);
throw new DirectoryException(
diff --git a/opendj-server-legacy/src/test/java/org/opends/server/authorization/dseecompat/UserDNTestCase.java b/opendj-server-legacy/src/test/java/org/opends/server/authorization/dseecompat/UserDNTestCase.java
new file mode 100644
index 0000000..fac597f
--- /dev/null
+++ b/opendj-server-legacy/src/test/java/org/opends/server/authorization/dseecompat/UserDNTestCase.java
@@ -0,0 +1,56 @@
+/*
+ * The contents of this file are subject to the terms of the Common Development and
+ * Distribution License (the License). You may not use this file except in compliance with the
+ * License.
+ *
+ * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
+ * specific language governing permission and limitations under the License.
+ *
+ * When distributing Covered Software, include this CDDL Header Notice in each file and include
+ * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
+ * Header, with the fields enclosed by brackets [] replaced by your own identifying
+ * information: "Portions Copyright [year] [name of copyright owner]".
+ *
+ * Copyright 2026 3A Systems, LLC
+ */
+package org.opends.server.authorization.dseecompat;
+
+import org.forgerock.opendj.ldap.ByteString;
+import org.forgerock.opendj.ldap.DN;
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+/** Tests for decoding of the userdn bind rule. */
+public class UserDNTestCase extends AciTestCase
+{
+ /**
+ * ACIs whose userdn LDAP URL contains a percent sign followed by fewer than
+ * two hexadecimal digits - see issue #673.
+ *
+ * @return The malformed ACIs.
+ */
+ @DataProvider
+ public Object[][] truncatedPercentAcis()
+ {
+ return new Object[][] {
+ { "(version 3.0; acl \":\"; allow (search) userdn=\"ldap://cn=name%B\"; )" },
+ { "(version 3.0; acl \":\"; allow (search) userdn=\"ldap://cn=name%\"; )" },
+ };
+ }
+
+ /**
+ * A truncated percent-encoded sequence in the userdn URL must be rejected
+ * with a clean AciException instead of an ArrayIndexOutOfBoundsException -
+ * see issue #673.
+ *
+ * @param aciString
+ * The ACI to decode.
+ * @throws Exception
+ * If an unexpected exception occurred.
+ */
+ @Test(dataProvider = "truncatedPercentAcis", expectedExceptions = AciException.class)
+ public void testTruncatedPercentInUserDN(String aciString) throws Exception
+ {
+ Aci.decode(ByteString.valueOfUtf8(aciString), DN.rootDN());
+ }
+}
diff --git a/opendj-server-legacy/src/test/java/org/opends/server/types/LDAPURLTestCase.java b/opendj-server-legacy/src/test/java/org/opends/server/types/LDAPURLTestCase.java
index 7e82bbb..223ca31 100644
--- a/opendj-server-legacy/src/test/java/org/opends/server/types/LDAPURLTestCase.java
+++ b/opendj-server-legacy/src/test/java/org/opends/server/types/LDAPURLTestCase.java
@@ -12,6 +12,7 @@
* information: "Portions Copyright [year] [name of copyright owner]".
*
* Portions Copyright 2012-2016 ForgeRock AS.
+ * Portions Copyright 2026 3A Systems, LLC
*/
package org.opends.server.types;
@@ -98,4 +99,38 @@
assertEquals(url.getRawFilter(), filterString);
}
+
+
+ /**
+ * Test data for testTruncatedPercentEncoding.
+ *
+ * @return URLs with a percent sign followed by fewer than two hexadecimal digits.
+ */
+ @DataProvider
+ public Object[][] truncatedPercentData()
+ {
+ return new Object[][] {
+ { "ldap:///cn=name%B" },
+ { "ldap:///cn=name%" },
+ };
+ }
+
+
+
+ /**
+ * A percent sign followed by fewer than two hexadecimal digits must be rejected with a
+ * clean decode error instead of an ArrayIndexOutOfBoundsException - see issue #673.
+ *
+ * @param urlString
+ * The URL to decode.
+ * @throws Exception
+ * If an unexpected exception occurred.
+ */
+ @Test(dataProvider = "truncatedPercentData",
+ expectedExceptions = DirectoryException.class)
+ public void testTruncatedPercentEncoding(String urlString) throws Exception
+ {
+ LDAPURL.decode(urlString, true);
+ }
+
}
--
Gitblit v1.10.0