Skip to content

Commit 07bad95

Browse files
authored
[#726] Reject malformed bracketed IPv6 hosts in HostPort (#732)
1 parent e8c0d1b commit 07bad95

3 files changed

Lines changed: 138 additions & 10 deletions

File tree

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

Lines changed: 27 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
*
1414
* Copyright 2006-2008 Sun Microsystems, Inc.
1515
* Portions Copyright 2013-2016 ForgeRock AS.
16-
* Portions Copyright 2025 3A Systems LLC.
16+
* Portions Copyright 2025-2026 3A Systems LLC.
1717
*/
1818
package org.opends.server.types;
1919

@@ -229,8 +229,9 @@ public HostPort(String host, int port)
229229
* @throws NumberFormatException
230230
* If the "port" in the supplied string cannot be converted to an int
231231
* @throws IllegalArgumentException
232-
* if no port could be found in the supplied string, or if the port
233-
* is not a valid port number
232+
* if no port could be found in the supplied string, if the port
233+
* is not a valid port number, or if the host is a malformed
234+
* bracketed IPv6 address
234235
*/
235236
public static HostPort valueOf(String hostPort) throws NumberFormatException,
236237
IllegalArgumentException
@@ -251,12 +252,18 @@ public static HostPort valueOf(String hostPort) throws NumberFormatException,
251252
* @throws NumberFormatException
252253
* If the "port" in the supplied string cannot be converted to an int
253254
* @throws IllegalArgumentException
254-
* if no port could be found in the supplied string, or if the port
255-
* is not a valid port number
255+
* if no port could be found in the supplied string, if the port
256+
* is not a valid port number, or if the host is a malformed
257+
* bracketed IPv6 address
256258
*/
257259
public static HostPort valueOf(String hostPort, Integer defaultPort) throws NumberFormatException,
258260
IllegalArgumentException
259261
{
262+
if (hostPort.isEmpty())
263+
{
264+
throw new IllegalArgumentException(
265+
"Invalid host/port string: no host name was provided in ''");
266+
}
260267
final int sepIndex = hostPort.lastIndexOf(':');
261268
if ((hostPort.charAt(0) == '['
262269
&& hostPort.charAt(hostPort.length() - 1) == ']')
@@ -276,8 +283,9 @@ else if (sepIndex == 0)
276283
"Invalid host/port string: no host name was provided in '" + hostPort
277284
+ "'");
278285
}
279-
else if (hostPort.lastIndexOf(':', sepIndex - 1) != -1
280-
&& (hostPort.charAt(0) != '[' || hostPort.charAt(sepIndex - 1) != ']'))
286+
else if (hostPort.charAt(0) == '['
287+
? hostPort.charAt(sepIndex - 1) != ']'
288+
: hostPort.lastIndexOf(':', sepIndex - 1) != -1)
281289
{
282290
if (defaultPort != null)
283291
{
@@ -300,15 +308,24 @@ else if (hostPort.lastIndexOf(':', sepIndex - 1) != -1
300308
* @param host
301309
* the host name to clean
302310
* @return the cleaned up host name
311+
* @throws IllegalArgumentException
312+
* if the host name starts or ends with a square bracket, but is
313+
* not a well-formed "[bracketed IPv6 address]"
303314
*/
304315
private String removeExtraChars(String host)
305316
{
306-
final int startsWith = host.indexOf("[");
307-
if (startsWith == -1)
317+
final boolean startsWithBracket = host.startsWith("[");
318+
final boolean endsWithBracket = host.endsWith("]");
319+
if (!startsWithBracket && !endsWithBracket)
308320
{
309321
return host;
310322
}
311-
return host.substring(1, host.length() - 1);
323+
if (startsWithBracket && endsWithBracket && host.length() > 2)
324+
{
325+
return host.substring(1, host.length() - 1);
326+
}
327+
throw new IllegalArgumentException(
328+
"Invalid host name: expected \"[bracketed IPv6 address]\", but got '" + host + "'");
312329
}
313330

314331
/**

opendj-server-legacy/src/test/java/org/opends/server/authorization/dseecompat/UserDNTestCase.java

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,4 +53,49 @@ public void testTruncatedPercentInUserDN(String aciString) throws Exception
5353
{
5454
Aci.decode(ByteString.valueOfUtf8(aciString), DN.rootDN());
5555
}
56+
57+
/**
58+
* ACIs whose userdn LDAP URL contains a malformed bracketed IPv6 host - see
59+
* issue #726.
60+
*
61+
* @return The malformed ACIs.
62+
*/
63+
@DataProvider
64+
public Object[][] malformedIPv6HostAcis()
65+
{
66+
return new Object[][] {
67+
{ "(version 3.0; acl \"f\"; allow (search) userdn=\"q://[:1\"; )" },
68+
{ "(version 3.0; acl \"f\"; allow (search) userdn=\"ldap://[::1:389/dc=example,dc=com\"; )" },
69+
};
70+
}
71+
72+
/**
73+
* A malformed bracketed IPv6 host in the userdn URL must be rejected with a
74+
* clean AciException instead of a StringIndexOutOfBoundsException - see
75+
* issue #726.
76+
*
77+
* @param aciString
78+
* The ACI to decode.
79+
* @throws Exception
80+
* If an unexpected exception occurred.
81+
*/
82+
@Test(dataProvider = "malformedIPv6HostAcis", expectedExceptions = AciException.class)
83+
public void testMalformedIPv6HostInUserDN(String aciString) throws Exception
84+
{
85+
Aci.decode(ByteString.valueOfUtf8(aciString), DN.rootDN());
86+
}
87+
88+
/**
89+
* A well-formed bracketed IPv6 host in the userdn URL must still decode.
90+
*
91+
* @throws Exception
92+
* If an unexpected exception occurred.
93+
*/
94+
@Test
95+
public void testValidIPv6HostInUserDN() throws Exception
96+
{
97+
String aciString = "(version 3.0; acl \"f\"; allow (search) "
98+
+ "userdn=\"ldap://[::1]:389/uid=user,dc=example,dc=com\"; )";
99+
Aci.decode(ByteString.valueOfUtf8(aciString), DN.rootDN());
100+
}
56101
}

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

Lines changed: 66 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
* Copyright 2013-2016 ForgeRock AS.
15+
* Portions Copyright 2026 3A Systems LLC.
1516
*/
1617
package org.opends.server.types;
1718

@@ -210,6 +211,71 @@ public void valueOfPortNumberTooBig()
210211
HostPort.valueOf("host:99999999");
211212
}
212213

214+
/** Issue #726: used to throw StringIndexOutOfBoundsException. */
215+
@Test(expectedExceptions = IllegalArgumentException.class)
216+
public void valueOfOpeningBracketOnlyHost()
217+
{
218+
HostPort.valueOf("[:1");
219+
}
220+
221+
/** Issue #726: used to throw StringIndexOutOfBoundsException. */
222+
@Test(expectedExceptions = IllegalArgumentException.class)
223+
public void valueOfOpeningBracketOnlyHostDefaultPort()
224+
{
225+
HostPort.valueOf("[", 1);
226+
}
227+
228+
@Test(expectedExceptions = IllegalArgumentException.class)
229+
public void valueOfIPv6MissingClosingBracket()
230+
{
231+
HostPort.valueOf("[" + IPV6_ADDRESS + ":389");
232+
}
233+
234+
@Test(expectedExceptions = IllegalArgumentException.class)
235+
public void valueOfIPv6MissingOpeningBracket()
236+
{
237+
HostPort.valueOf(IPV6_ADDRESS + "]:389");
238+
}
239+
240+
@Test(expectedExceptions = IllegalArgumentException.class)
241+
public void valueOfEmptyBracketsHost()
242+
{
243+
HostPort.valueOf("[]:389");
244+
}
245+
246+
@Test(expectedExceptions = IllegalArgumentException.class)
247+
public void valueOfEmptyString()
248+
{
249+
HostPort.valueOf("");
250+
}
251+
252+
/** Issue #726: used to throw StringIndexOutOfBoundsException. */
253+
@Test(expectedExceptions = IllegalArgumentException.class)
254+
public void constructorOpeningBracketOnlyHost()
255+
{
256+
new HostPort("[", 1);
257+
}
258+
259+
@Test(expectedExceptions = IllegalArgumentException.class)
260+
public void constructorMissingClosingBracket()
261+
{
262+
new HostPort("[" + IPV6_ADDRESS, 389);
263+
}
264+
265+
@Test(expectedExceptions = IllegalArgumentException.class)
266+
public void constructorMissingOpeningBracket()
267+
{
268+
new HostPort(IPV6_ADDRESS + "]", 389);
269+
}
270+
271+
@Test
272+
public void constructorBracketedIPv6()
273+
{
274+
final HostPort hp = new HostPort("[" + IPV6_ADDRESS + "]", 389);
275+
assertThat(hp.getHost()).isEqualTo(IPV6_ADDRESS);
276+
assertThat(hp.getPort()).isEqualTo(389);
277+
}
278+
213279
@Test
214280
public void valueOfIPv6NoPort()
215281
{

0 commit comments

Comments
 (0)