From 07bad95016f2cfafaf217114f9b0b50429af7d2f Mon Sep 17 00:00:00 2001
From: Valery Kharseko <vharseko@3a-systems.ru>
Date: Sun, 12 Jul 2026 12:06:03 +0000
Subject: [PATCH] [#726] Reject malformed bracketed IPv6 hosts in HostPort (#732)
---
opendj-server-legacy/src/test/java/org/opends/server/types/HostPortTest.java | 66 ++++++++++++++++++++++
opendj-server-legacy/src/test/java/org/opends/server/authorization/dseecompat/UserDNTestCase.java | 45 +++++++++++++++
opendj-server-legacy/src/main/java/org/opends/server/types/HostPort.java | 37 +++++++++---
3 files changed, 138 insertions(+), 10 deletions(-)
diff --git a/opendj-server-legacy/src/main/java/org/opends/server/types/HostPort.java b/opendj-server-legacy/src/main/java/org/opends/server/types/HostPort.java
index fabb3df..bff0143 100644
--- a/opendj-server-legacy/src/main/java/org/opends/server/types/HostPort.java
+++ b/opendj-server-legacy/src/main/java/org/opends/server/types/HostPort.java
@@ -13,7 +13,7 @@
*
* Copyright 2006-2008 Sun Microsystems, Inc.
* Portions Copyright 2013-2016 ForgeRock AS.
- * Portions Copyright 2025 3A Systems LLC.
+ * Portions Copyright 2025-2026 3A Systems LLC.
*/
package org.opends.server.types;
@@ -229,8 +229,9 @@
* @throws NumberFormatException
* If the "port" in the supplied string cannot be converted to an int
* @throws IllegalArgumentException
- * if no port could be found in the supplied string, or if the port
- * is not a valid port number
+ * if no port could be found in the supplied string, if the port
+ * is not a valid port number, or if the host is a malformed
+ * bracketed IPv6 address
*/
public static HostPort valueOf(String hostPort) throws NumberFormatException,
IllegalArgumentException
@@ -251,12 +252,18 @@
* @throws NumberFormatException
* If the "port" in the supplied string cannot be converted to an int
* @throws IllegalArgumentException
- * if no port could be found in the supplied string, or if the port
- * is not a valid port number
+ * if no port could be found in the supplied string, if the port
+ * is not a valid port number, or if the host is a malformed
+ * bracketed IPv6 address
*/
public static HostPort valueOf(String hostPort, Integer defaultPort) throws NumberFormatException,
IllegalArgumentException
{
+ if (hostPort.isEmpty())
+ {
+ throw new IllegalArgumentException(
+ "Invalid host/port string: no host name was provided in ''");
+ }
final int sepIndex = hostPort.lastIndexOf(':');
if ((hostPort.charAt(0) == '['
&& hostPort.charAt(hostPort.length() - 1) == ']')
@@ -276,8 +283,9 @@
"Invalid host/port string: no host name was provided in '" + hostPort
+ "'");
}
- else if (hostPort.lastIndexOf(':', sepIndex - 1) != -1
- && (hostPort.charAt(0) != '[' || hostPort.charAt(sepIndex - 1) != ']'))
+ else if (hostPort.charAt(0) == '['
+ ? hostPort.charAt(sepIndex - 1) != ']'
+ : hostPort.lastIndexOf(':', sepIndex - 1) != -1)
{
if (defaultPort != null)
{
@@ -300,15 +308,24 @@
* @param host
* the host name to clean
* @return the cleaned up host name
+ * @throws IllegalArgumentException
+ * if the host name starts or ends with a square bracket, but is
+ * not a well-formed "[bracketed IPv6 address]"
*/
private String removeExtraChars(String host)
{
- final int startsWith = host.indexOf("[");
- if (startsWith == -1)
+ final boolean startsWithBracket = host.startsWith("[");
+ final boolean endsWithBracket = host.endsWith("]");
+ if (!startsWithBracket && !endsWithBracket)
{
return host;
}
- return host.substring(1, host.length() - 1);
+ if (startsWithBracket && endsWithBracket && host.length() > 2)
+ {
+ return host.substring(1, host.length() - 1);
+ }
+ throw new IllegalArgumentException(
+ "Invalid host name: expected \"[bracketed IPv6 address]\", but got '" + host + "'");
}
/**
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
index fac597f..9fbc728 100644
--- 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
@@ -53,4 +53,49 @@
{
Aci.decode(ByteString.valueOfUtf8(aciString), DN.rootDN());
}
+
+ /**
+ * ACIs whose userdn LDAP URL contains a malformed bracketed IPv6 host - see
+ * issue #726.
+ *
+ * @return The malformed ACIs.
+ */
+ @DataProvider
+ public Object[][] malformedIPv6HostAcis()
+ {
+ return new Object[][] {
+ { "(version 3.0; acl \"f\"; allow (search) userdn=\"q://[:1\"; )" },
+ { "(version 3.0; acl \"f\"; allow (search) userdn=\"ldap://[::1:389/dc=example,dc=com\"; )" },
+ };
+ }
+
+ /**
+ * A malformed bracketed IPv6 host in the userdn URL must be rejected with a
+ * clean AciException instead of a StringIndexOutOfBoundsException - see
+ * issue #726.
+ *
+ * @param aciString
+ * The ACI to decode.
+ * @throws Exception
+ * If an unexpected exception occurred.
+ */
+ @Test(dataProvider = "malformedIPv6HostAcis", expectedExceptions = AciException.class)
+ public void testMalformedIPv6HostInUserDN(String aciString) throws Exception
+ {
+ Aci.decode(ByteString.valueOfUtf8(aciString), DN.rootDN());
+ }
+
+ /**
+ * A well-formed bracketed IPv6 host in the userdn URL must still decode.
+ *
+ * @throws Exception
+ * If an unexpected exception occurred.
+ */
+ @Test
+ public void testValidIPv6HostInUserDN() throws Exception
+ {
+ String aciString = "(version 3.0; acl \"f\"; allow (search) "
+ + "userdn=\"ldap://[::1]:389/uid=user,dc=example,dc=com\"; )";
+ Aci.decode(ByteString.valueOfUtf8(aciString), DN.rootDN());
+ }
}
diff --git a/opendj-server-legacy/src/test/java/org/opends/server/types/HostPortTest.java b/opendj-server-legacy/src/test/java/org/opends/server/types/HostPortTest.java
index fe60d4b..87be1aa 100644
--- a/opendj-server-legacy/src/test/java/org/opends/server/types/HostPortTest.java
+++ b/opendj-server-legacy/src/test/java/org/opends/server/types/HostPortTest.java
@@ -12,6 +12,7 @@
* information: "Portions Copyright [year] [name of copyright owner]".
*
* Copyright 2013-2016 ForgeRock AS.
+ * Portions Copyright 2026 3A Systems LLC.
*/
package org.opends.server.types;
@@ -210,6 +211,71 @@
HostPort.valueOf("host:99999999");
}
+ /** Issue #726: used to throw StringIndexOutOfBoundsException. */
+ @Test(expectedExceptions = IllegalArgumentException.class)
+ public void valueOfOpeningBracketOnlyHost()
+ {
+ HostPort.valueOf("[:1");
+ }
+
+ /** Issue #726: used to throw StringIndexOutOfBoundsException. */
+ @Test(expectedExceptions = IllegalArgumentException.class)
+ public void valueOfOpeningBracketOnlyHostDefaultPort()
+ {
+ HostPort.valueOf("[", 1);
+ }
+
+ @Test(expectedExceptions = IllegalArgumentException.class)
+ public void valueOfIPv6MissingClosingBracket()
+ {
+ HostPort.valueOf("[" + IPV6_ADDRESS + ":389");
+ }
+
+ @Test(expectedExceptions = IllegalArgumentException.class)
+ public void valueOfIPv6MissingOpeningBracket()
+ {
+ HostPort.valueOf(IPV6_ADDRESS + "]:389");
+ }
+
+ @Test(expectedExceptions = IllegalArgumentException.class)
+ public void valueOfEmptyBracketsHost()
+ {
+ HostPort.valueOf("[]:389");
+ }
+
+ @Test(expectedExceptions = IllegalArgumentException.class)
+ public void valueOfEmptyString()
+ {
+ HostPort.valueOf("");
+ }
+
+ /** Issue #726: used to throw StringIndexOutOfBoundsException. */
+ @Test(expectedExceptions = IllegalArgumentException.class)
+ public void constructorOpeningBracketOnlyHost()
+ {
+ new HostPort("[", 1);
+ }
+
+ @Test(expectedExceptions = IllegalArgumentException.class)
+ public void constructorMissingClosingBracket()
+ {
+ new HostPort("[" + IPV6_ADDRESS, 389);
+ }
+
+ @Test(expectedExceptions = IllegalArgumentException.class)
+ public void constructorMissingOpeningBracket()
+ {
+ new HostPort(IPV6_ADDRESS + "]", 389);
+ }
+
+ @Test
+ public void constructorBracketedIPv6()
+ {
+ final HostPort hp = new HostPort("[" + IPV6_ADDRESS + "]", 389);
+ assertThat(hp.getHost()).isEqualTo(IPV6_ADDRESS);
+ assertThat(hp.getPort()).isEqualTo(389);
+ }
+
@Test
public void valueOfIPv6NoPort()
{
--
Gitblit v1.10.0