From 08aee4724608e4a32baa3c7d7499ec913a275aaf Mon Sep 17 00:00:00 2001
From: Maxim Thomas <maxim.thomas@gmail.com>
Date: Mon, 03 Mar 2025 17:27:49 +0000
Subject: [PATCH] CVE-2025-27497 Fix Denial of Service (Dos) using alias loop
---
opendj-server-legacy/src/test/java/org/openidentityplatform/opendj/AliasTestCase.java | 101 +++++++++++++++++++++++++++++++++++++++++++++++++-
1 files changed, 98 insertions(+), 3 deletions(-)
diff --git a/opendj-server-legacy/src/test/java/org/openidentityplatform/opendj/AliasTestCase.java b/opendj-server-legacy/src/test/java/org/openidentityplatform/opendj/AliasTestCase.java
index ee248b1..99376f8 100644
--- a/opendj-server-legacy/src/test/java/org/openidentityplatform/opendj/AliasTestCase.java
+++ b/opendj-server-legacy/src/test/java/org/openidentityplatform/opendj/AliasTestCase.java
@@ -11,7 +11,7 @@
* Header, with the fields enclosed by brackets [] replaced by your own identifying
* information: "Portions Copyright [year] [name of copyright owner]".
*
- * Copyright 2024 3A Systems, LLC.
+ * Copyright 2024-2025 3A Systems, LLC.
*/
package org.openidentityplatform.opendj;
@@ -23,6 +23,7 @@
import org.opends.server.DirectoryServerTestCase;
import org.opends.server.TestCaseUtils;
+import org.opends.server.types.Entry;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
@@ -60,6 +61,61 @@
"objectclass: extensibleobject",
"cn: President",
"aliasedobjectname: cn=John Doe, o=MyCompany, o=test",
+ "",
+
+ "dn: ou=employees,o=test",
+ "objectClass: top",
+ "objectClass: organizationalUnit",
+ "ou: employees",
+ "description: All employees",
+ "",
+ "dn: uid=jdoe,ou=employees,o=test",
+ "objectClass: alias",
+ "objectClass: top",
+ "objectClass: extensibleObject",
+ "aliasedObjectName: uid=jdoe,ou=researchers,o=test",
+ "uid: jdoe",
+ "",
+ "dn: ou=researchers,o=test",
+ "objectClass: top",
+ "objectClass: organizationalUnit",
+ "ou: researchers",
+ "description: All reasearchers",
+ "",
+ "dn: uid=jdoe,ou=researchers,o=test",
+ "objectClass: alias",
+ "objectClass: top",
+ "objectClass: extensibleObject",
+ "aliasedObjectName: uid=jdoe,ou=employees,o=test",
+ "uid: jdoe",
+
+ "",
+ "dn: ou=students,o=test",
+ "objectClass: top",
+ "objectClass: organizationalUnit",
+ "ou: students",
+ "description: All students",
+ "",
+ "dn: uid=janedoe,ou=students,o=test",
+ "objectClass: alias",
+ "objectClass: top",
+ "objectClass: extensibleObject",
+ "aliasedObjectName: uid=janedoe,ou=researchers,o=test",
+ "uid: janedoe",
+ "",
+ "dn: uid=janedoe,ou=researchers,o=test",
+ "objectClass: alias",
+ "objectClass: top",
+ "objectClass: extensibleObject",
+ "aliasedObjectName: uid=janedoe,ou=employees,o=test",
+ "uid: janedoe",
+ "",
+ "dn: uid=janedoe,ou=employees,o=test",
+ "objectClass: alias",
+ "objectClass: top",
+ "objectClass: extensibleObject",
+ "aliasedObjectName: uid=janedoe,ou=students,o=test",
+ "uid: janedoe",
""
);
@@ -70,7 +126,11 @@
}
public HashMap<String,SearchResultEntry> search(SearchScope scope,DereferenceAliasesPolicy policy) throws SearchResultReferenceIOException, LdapException {
- final SearchRequest request =Requests.newSearchRequest("ou=Area1,o=test", scope,"(objectclass=*)")
+ return search("ou=Area1,o=test", scope, policy);
+ }
+
+ public HashMap<String,SearchResultEntry> search(String dn, SearchScope scope,DereferenceAliasesPolicy policy) throws SearchResultReferenceIOException, LdapException {
+ final SearchRequest request =Requests.newSearchRequest(dn, scope,"(objectclass=*)")
.setDereferenceAliasesPolicy(policy);
System.out.println("---------------------------------------------------------------------------------------");
System.out.println(request);
@@ -125,7 +185,7 @@
// It returns ou=Area1,o=test.
@Test
public void test_base_search() throws SearchResultReferenceIOException, LdapException {
- HashMap<String,SearchResultEntry> res=search(SearchScope.BASE_OBJECT,DereferenceAliasesPolicy.IN_SEARCHING);
+ HashMap<String,SearchResultEntry> res=search(SearchScope.BASE_OBJECT, DereferenceAliasesPolicy.IN_SEARCHING);
assertThat(res.containsKey("ou=Area1,o=test")).isTrue();
assertThat(res.containsKey("o=MyCompany,o=test")).isFalse();
@@ -308,4 +368,39 @@
assertThat(res.containsKey("cn=John Doe,o=MyCompany,o=test")).isTrue();
}
+ // Dereferencing recursion avoidance test.
+ @Test
+ public void test_alias_recursive() throws LdapException, SearchResultReferenceIOException {
+ HashMap<String, SearchResultEntry> res = search("uid=jdoe,ou=employees,o=test", SearchScope.WHOLE_SUBTREE, DereferenceAliasesPolicy.ALWAYS);
+
+ assertThat(res.containsKey("uid=jdoe,ou=employees,o=test")).isTrue();
+ assertThat(res.containsKey("uid=jdoe,ou=researchers,o=test")).isFalse();
+ }
+
+ @Test
+ public void test_alias_recursive_loop() throws LdapException, SearchResultReferenceIOException {
+ HashMap<String, SearchResultEntry> res = search("uid=janedoe,ou=students,o=test", SearchScope.WHOLE_SUBTREE, DereferenceAliasesPolicy.ALWAYS);
+
+ assertThat(res.containsKey("uid=janedoe,ou=students,o=test")).isTrue();
+ assertThat(res.containsKey("uid=janedoe,ou=researches,o=test")).isFalse();
+ assertThat(res.containsKey("uid=janedoe,ou=employees,o=test")).isFalse();
+ }
+
+ @Test(expectedExceptions = LdapException.class)
+ public void test_stackoverflow() throws Exception {
+
+ String entryTemplate = "dn: uid={uid},ou=employees,o=test\n" +
+ "objectClass: alias\n" +
+ "objectClass: top\n" +
+ "objectClass: extensibleObject\n" +
+ "aliasedObjectName: uid={alias},ou=employees,o=test \n" +
+ "uid: {uid}\n";
+ final String firstDn = "uid=jdoe0,ou=employees,o=test";
+ for(int i = 0; i < 10000; i++) {
+ String entryStr = entryTemplate.replace("{uid}", "jdoe" + i).replace("{alias}", "jdoe" + (i + 1));
+ Entry entry = TestCaseUtils.makeEntry(entryStr);
+ TestCaseUtils.addEntry(entry);
+ }
+ search(firstDn, SearchScope.WHOLE_SUBTREE, DereferenceAliasesPolicy.ALWAYS);
+ }
}
--
Gitblit v1.10.0