Skip to content

Commit e53e018

Browse files
authored
[#673] Fix ArrayIndexOutOfBoundsException on truncated percent-encoding in LDAP URLs (#676)
1 parent 770c47d commit e53e018

5 files changed

Lines changed: 130 additions & 1 deletion

File tree

opendj-core/src/main/java/org/forgerock/opendj/ldap/LDAPUrl.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
*
1414
* Copyright 2010 Sun Microsystems, Inc.
1515
* Portions copyright 2012-2016 ForgeRock AS.
16+
* Portions Copyright 2026 3A Systems, LLC
1617
*/
1718
package org.forgerock.opendj.ldap;
1819

@@ -246,6 +247,12 @@ private static void percentDecoder(final String urlString, final int index, fina
246247
dstPos++;
247248
continue;
248249
}
250+
if (srcPos + 2 >= decoded.length()) {
251+
// A percent sign must be followed by two hexadecimal digits.
252+
final LocalizableMessage msg =
253+
ERR_LDAPURL_INVALID_HEX_BYTE.get(urlString, index + srcPos + 1);
254+
throw new LocalizedIllegalArgumentException(msg);
255+
}
249256
int i = decodeHex(urlString, index + srcPos + 1, decoded.charAt(srcPos + 1)) << 4;
250257
int j = decodeHex(urlString, index + srcPos + 2, decoded.charAt(srcPos + 2));
251258
decoded.setCharAt(dstPos, (char) (i | j));

opendj-core/src/test/java/org/forgerock/opendj/ldap/LDAPUrlTestCase.java

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,15 @@
1212
* information: "Portions Copyright [year] [name of copyright owner]".
1313
*
1414
* Copyright 2010 Sun Microsystems, Inc.
15+
* Portions Copyright 2026 3A Systems, LLC
1516
*/
1617

1718
package org.forgerock.opendj.ldap;
1819

1920
import static org.testng.Assert.assertEquals;
2021
import static org.testng.Assert.assertTrue;
2122

23+
import org.forgerock.i18n.LocalizedIllegalArgumentException;
2224
import org.testng.annotations.DataProvider;
2325
import org.testng.annotations.Test;
2426

@@ -181,4 +183,32 @@ public void testURLEncoding(final String toEncode, final String encoded, final b
181183
assertTrue(url1.equals(url2));
182184
}
183185
}
186+
187+
/**
188+
* URLs with a percent sign followed by fewer than two hexadecimal digits.
189+
*
190+
* @return The malformed URLs.
191+
*/
192+
@DataProvider
193+
public Object[][] truncatedPercentUrls() {
194+
return new Object[][] {
195+
{ "ldap:///cn=name%B" },
196+
{ "ldap:///cn=name%" },
197+
};
198+
}
199+
200+
/**
201+
* A truncated percent-encoded sequence must be rejected with a clean decode error
202+
* instead of a StringIndexOutOfBoundsException - see issue #673.
203+
*
204+
* @param url
205+
* The URL to decode.
206+
* @throws Exception
207+
* If the test failed unexpectedly.
208+
*/
209+
@Test(dataProvider = "truncatedPercentUrls",
210+
expectedExceptions = LocalizedIllegalArgumentException.class)
211+
public void testTruncatedPercentEncoding(final String url) throws Exception {
212+
LDAPUrl.valueOf(url);
213+
}
184214
}

opendj-server-legacy/src/main/java/org/opends/server/types/LDAPURL.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
*
1414
* Copyright 2006-2008 Sun Microsystems, Inc.
1515
* Portions Copyright 2012-2016 ForgeRock AS.
16+
* Portions Copyright 2026 3A Systems, LLC
1617
*/
1718
package org.opends.server.types;
1819

@@ -621,7 +622,7 @@ static String urlDecode(String s) throws DirectoryException
621622
{
622623
// There must be at least two bytes left. If not, then that's
623624
// a problem.
624-
if (i+2 > length)
625+
if (i+2 >= length)
625626
{
626627
LocalizableMessage message = ERR_LDAPURL_PERCENT_TOO_CLOSE_TO_END.get(s, i);
627628
throw new DirectoryException(
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
/*
2+
* The contents of this file are subject to the terms of the Common Development and
3+
* Distribution License (the License). You may not use this file except in compliance with the
4+
* License.
5+
*
6+
* You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
7+
* specific language governing permission and limitations under the License.
8+
*
9+
* When distributing Covered Software, include this CDDL Header Notice in each file and include
10+
* the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
11+
* Header, with the fields enclosed by brackets [] replaced by your own identifying
12+
* information: "Portions Copyright [year] [name of copyright owner]".
13+
*
14+
* Copyright 2026 3A Systems, LLC
15+
*/
16+
package org.opends.server.authorization.dseecompat;
17+
18+
import org.forgerock.opendj.ldap.ByteString;
19+
import org.forgerock.opendj.ldap.DN;
20+
import org.testng.annotations.DataProvider;
21+
import org.testng.annotations.Test;
22+
23+
/** Tests for decoding of the userdn bind rule. */
24+
public class UserDNTestCase extends AciTestCase
25+
{
26+
/**
27+
* ACIs whose userdn LDAP URL contains a percent sign followed by fewer than
28+
* two hexadecimal digits - see issue #673.
29+
*
30+
* @return The malformed ACIs.
31+
*/
32+
@DataProvider
33+
public Object[][] truncatedPercentAcis()
34+
{
35+
return new Object[][] {
36+
{ "(version 3.0; acl \":\"; allow (search) userdn=\"ldap://cn=name%B\"; )" },
37+
{ "(version 3.0; acl \":\"; allow (search) userdn=\"ldap://cn=name%\"; )" },
38+
};
39+
}
40+
41+
/**
42+
* A truncated percent-encoded sequence in the userdn URL must be rejected
43+
* with a clean AciException instead of an ArrayIndexOutOfBoundsException -
44+
* see issue #673.
45+
*
46+
* @param aciString
47+
* The ACI to decode.
48+
* @throws Exception
49+
* If an unexpected exception occurred.
50+
*/
51+
@Test(dataProvider = "truncatedPercentAcis", expectedExceptions = AciException.class)
52+
public void testTruncatedPercentInUserDN(String aciString) throws Exception
53+
{
54+
Aci.decode(ByteString.valueOfUtf8(aciString), DN.rootDN());
55+
}
56+
}

opendj-server-legacy/src/test/java/org/opends/server/types/LDAPURLTestCase.java

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
* information: "Portions Copyright [year] [name of copyright owner]".
1313
*
1414
* Portions Copyright 2012-2016 ForgeRock AS.
15+
* Portions Copyright 2026 3A Systems, LLC
1516
*/
1617
package org.opends.server.types;
1718

@@ -98,4 +99,38 @@ public void testURLEncoding(String urlString, String dnString,
9899
assertEquals(url.getRawFilter(), filterString);
99100
}
100101

102+
103+
104+
/**
105+
* Test data for testTruncatedPercentEncoding.
106+
*
107+
* @return URLs with a percent sign followed by fewer than two hexadecimal digits.
108+
*/
109+
@DataProvider
110+
public Object[][] truncatedPercentData()
111+
{
112+
return new Object[][] {
113+
{ "ldap:///cn=name%B" },
114+
{ "ldap:///cn=name%" },
115+
};
116+
}
117+
118+
119+
120+
/**
121+
* A percent sign followed by fewer than two hexadecimal digits must be rejected with a
122+
* clean decode error instead of an ArrayIndexOutOfBoundsException - see issue #673.
123+
*
124+
* @param urlString
125+
* The URL to decode.
126+
* @throws Exception
127+
* If an unexpected exception occurred.
128+
*/
129+
@Test(dataProvider = "truncatedPercentData",
130+
expectedExceptions = DirectoryException.class)
131+
public void testTruncatedPercentEncoding(String urlString) throws Exception
132+
{
133+
LDAPURL.decode(urlString, true);
134+
}
135+
101136
}

0 commit comments

Comments
 (0)