Skip to content

Commit d3d86c9

Browse files
maximthomasvharseko
authored andcommitted
CVE-2026-41573 OpenAM LDAP Injection via Parameter
1 parent 203e549 commit d3d86c9

8 files changed

Lines changed: 47 additions & 13 deletions

File tree

openam-core-rest/src/main/java/org/forgerock/openam/core/rest/IdentityResourceV1.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
* information: "Portions copyright [year] [name of copyright owner]".
1313
*
1414
* Copyright 2012-2016 ForgeRock AS.
15-
* Portions copyright 2022-2025 3A Systems, LLC.
15+
* Portions copyright 2022-2026 3A Systems, LLC.
1616
*/
1717
package org.forgerock.openam.core.rest;
1818

@@ -1209,7 +1209,7 @@ public Promise<QueryResponse, ResourceException> queryCollection(final Context c
12091209
if (queryId == null || queryId.isEmpty()) {
12101210
queryId = "*";
12111211
}
1212-
List<String> users = identityServices.search(new CrestQuery(queryId, null, null, false),
1212+
List<String> users = identityServices.search(new CrestQuery(queryId),
12131213
getIdentityServicesAttributes(realm), admin);
12141214
String principalName = PrincipalRestUtils.getPrincipalNameFromServerContext(context);
12151215
debug.message("IdentityResource.queryCollection :: QUERY performed on realm={} by principalName={}", realm,

openam-core/src/main/java/com/sun/identity/delegation/plugins/DelegationPolicyImpl.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
* $Id: DelegationPolicyImpl.java,v 1.12 2010/01/16 06:35:25 dillidorai Exp $
2626
*
2727
* Portions Copyrighted 2011-2016 ForgeRock AS.
28+
* Portions Copyrighted 2022-2026 3A Systems LLC.
2829
*/
2930

3031
package com.sun.identity.delegation.plugins;
@@ -437,7 +438,7 @@ public Set getSubjects(SSOToken token, String orgName, Set types,
437438
ctrl.setRecursive(true);
438439
ctrl.setMaxResults(-1);
439440
ctrl.setTimeOut(-1);
440-
CrestQuery crestQuery = new CrestQuery(pattern, null, null, false);
441+
CrestQuery crestQuery = new CrestQuery(pattern);
441442
IdSearchResults idsr = idRepo.searchIdentities(
442443
idType, crestQuery, ctrl);
443444
if (idsr != null) {

openam-core/src/main/java/com/sun/identity/idm/plugins/internal/SpecialRepo.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
* $Id: SpecialRepo.java,v 1.19 2010/01/06 17:41:00 veiming Exp $
2626
*
2727
* Portions Copyrighted 2012-2016 ForgeRock AS.
28+
* Portions Copyrighted 2021-2026 3A Systems LLC.
2829
*/
2930

3031
package com.sun.identity.idm.plugins.internal;
@@ -565,7 +566,7 @@ public RepoSearchResults search(SSOToken token, IdType type, CrestQuery crestQue
565566
if (uidVals != null && !uidVals.isEmpty()) {
566567
pattern = (String) uidVals.iterator().next();
567568
if (crestQuery.isEscapeQueryId()) {
568-
pattern = Filter.escapeAssertionValue(pattern);
569+
pattern = crestQuery.getEscapedQueryId();
569570
}
570571
} else {
571572
// pattern is "*" and avPairs is not empty, so return
@@ -579,7 +580,7 @@ public RepoSearchResults search(SSOToken token, IdType type, CrestQuery crestQue
579580
// If wild card is used for pattern, do a search else a lookup
580581
if (pattern.indexOf('*') != -1) {
581582
if (crestQuery.isEscapeQueryId()) {
582-
pattern = Filter.escapeAssertionValue(pattern);
583+
pattern = crestQuery.getEscapedQueryId();
583584
}
584585
userRes = userConfig.getSubConfigNames(pattern);
585586
} else {

openam-core/src/main/java/com/sun/identity/idm/server/IdServicesImpl.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
* $Id: IdServicesImpl.java,v 1.61 2010/01/20 01:08:36 goodearth Exp $
2626
*
2727
* Portions Copyrighted 2011-2016 ForgeRock AS.
28-
* Portions Copyrighted 2021 Open Identity Platform Community.
28+
* Portions Copyrighted 2021-2026 3A Systems LLC.
2929
*/
3030

3131
package com.sun.identity.idm.server;
@@ -1667,7 +1667,7 @@ protected boolean isSpecialIdentity(SSOToken token, String name,
16671667
for (Iterator items = repos.iterator(); items.hasNext();) {
16681668
IdRepo repo = (IdRepo) items.next();
16691669
if (repo instanceof SpecialRepo) {
1670-
CrestQuery crestQuery = new CrestQuery("*", null, null, false);
1670+
CrestQuery crestQuery = new CrestQuery("*");
16711671
RepoSearchResults res = repo.search(token, type, crestQuery,
16721672
0, 0, Collections.EMPTY_SET, false,
16731673
0, Collections.EMPTY_MAP, false);

openam-datastore/src/main/java/org/forgerock/openam/idrepo/ldap/DJLDAPv3Repo.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
*
1414
* Copyright 2013-2016 ForgeRock AS.
1515
* Portions Copyright 2016 Nomura Research Institute, Ltd.
16+
* Portions copyright 2019-2026 3A Systems LLC.
1617
*/
1718
package org.forgerock.openam.idrepo.ldap;
1819

@@ -1249,7 +1250,7 @@ protected Filter getFilter(IdType type, CrestQuery crestQuery, int filterOp, Map
12491250

12501251
String pattern = crestQuery.getQueryId();
12511252
if (crestQuery.isEscapeQueryId()) {
1252-
pattern = Filter.escapeAssertionValue(pattern);
1253+
pattern = crestQuery.getEscapedQueryId();
12531254
}
12541255
first = Filter.valueOf(searchAttr + "=" + pattern);
12551256
}

openam-datastore/src/test/java/org/forgerock/openam/idrepo/ldap/GenericRepoTest.java

Lines changed: 5 additions & 4 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 2018-2026 3A Systems LLC.
1516
*/
1617
package org.forgerock.openam.idrepo.ldap;
1718

@@ -350,7 +351,7 @@ public void searchReturnsEmptyResultsIfNoMatch() throws Exception {
350351

351352
@Test
352353
public void searchReturnsMatchesForSearchAttribute() throws Exception {
353-
CrestQuery crestQuery = new CrestQuery("searchTester*", null, null, false);
354+
CrestQuery crestQuery = new CrestQuery("searchTester*");
354355
RepoSearchResults results =
355356
idrepo.search(null, IdType.USER, crestQuery, 0, 0, null, true, IdRepo.AND_MOD, null, true);
356357
assertThat(results.getErrorCode()).isEqualTo(ResultCode.SUCCESS.intValue());
@@ -380,7 +381,7 @@ public void searchReturnsMatchesForComplexFilters() throws Exception {
380381
Map<String, Set<String>> avPairs = new HashMap<String, Set<String>>();
381382
avPairs.put("objectclass", asSet("inetorgperson"));
382383
avPairs.put("sn", asSet("hellNo"));
383-
CrestQuery crestQuery = new CrestQuery("*", null, null, false);
384+
CrestQuery crestQuery = new CrestQuery("*");
384385
RepoSearchResults results =
385386
idrepo.search(null, IdType.USER, crestQuery, 0, 0, null, true, IdRepo.AND_MOD, avPairs, true);
386387
assertThat(results.getErrorCode()).isEqualTo(ResultCode.SUCCESS.intValue());
@@ -400,10 +401,10 @@ public void getSearchFilter() {
400401
avPairs.put("sn", asSet("hellNo"));
401402
CrestQuery crestQuery = new CrestQuery("*");
402403
Filter filter = idrepo.getFilter(IdType.USER, crestQuery, IdRepo.AND_MOD, avPairs);
403-
assertThat(filter.toString()).isEqualTo("(&(&(sn=hellNo)(objectclass=inetorgperson))(&(uid=\\2A)(objectclass=inetorgperson)))");
404+
assertThat(filter.toString()).isEqualTo("(&(&(sn=hellNo)(objectclass=inetorgperson))(&(uid=*)(objectclass=inetorgperson)))");
404405

405406
avPairs = new HashMap<>();
406-
crestQuery = new CrestQuery("*", null, null, false);
407+
crestQuery = new CrestQuery("*");
407408
filter = idrepo.getFilter(IdType.USER, crestQuery, IdRepo.AND_MOD, avPairs);
408409
assertThat(filter.toString()).isEqualTo("(&(uid=*)(objectclass=inetorgperson))");
409410

openam-federation/OpenFM/src/main/java/com/sun/identity/plugin/datastore/impl/IdRepoDataStoreProvider.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
* $Id: IdRepoDataStoreProvider.java,v 1.6 2008/08/06 17:29:26 exu Exp $
2626
*
2727
* Portions Copyrighted 2013-2015 ForgeRock AS.
28+
* Portions Copyrighted 2026 3A Systems LLC.
2829
*/
2930

3031
package com.sun.identity.plugin.datastore.impl;
@@ -275,7 +276,7 @@ public String getUserID(String orgDN, Map<String, Set<String>> avPairs)
275276
try {
276277
IdSearchControl searchControl = getIdSearchControl(avPairs, IdSearchOpModifier.AND);
277278
AMIdentityRepository idRepo = getAMIdentityRepository(orgDN);
278-
CrestQuery pattern = new CrestQuery("*", null, null, false);
279+
CrestQuery pattern = new CrestQuery("*");
279280
IdSearchResults searchResults = idRepo.searchIdentities(IdType.USER, pattern, searchControl);
280281
amIdSet = searchResults.getSearchResults();
281282
} catch (IdRepoException ame) {

openam-shared/src/main/java/org/forgerock/openam/utils/CrestQuery.java

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,19 @@
1212
* information: "Portions copyright [year] [name of copyright owner]".
1313
*
1414
* Copyright 2015-2016 ForgeRock AS.
15+
* Portions copyright 2021-2026 3A Systems LLC.
1516
*/
1617

1718
package org.forgerock.openam.utils;
1819

1920
import java.util.List;
2021

2122
import org.forgerock.json.JsonPointer;
23+
import org.forgerock.opendj.ldap.ByteString;
2224
import org.forgerock.util.query.QueryFilter;
2325

26+
import static com.forgerock.opendj.util.StaticUtils.byteToHex;
27+
2428
/**
2529
* This class was created to handle queries made via CREST that can provide either a _queryID (String) or a
2630
* _queryFilter (QueryFilter&lt;JsonPointer&gt;) to search for. Obviously the query filter cannot be converted
@@ -136,6 +140,31 @@ public boolean hasQueryFilter() {
136140
public boolean isEscapeQueryId() {
137141
return escapeQueryId;
138142
}
143+
144+
/** The backslash character. */
145+
private static final byte BACKSLASH = 0x5C;
146+
147+
public String getEscapedQueryId() {
148+
149+
final ByteString bytes = ByteString.valueOfObject(queryId);
150+
final StringBuilder builder = new StringBuilder(bytes.length());
151+
for (int i = 0; i < bytes.length(); i++) {
152+
final byte b = bytes.byteAt(i);
153+
if (((b & 0x7F) != b) // Not 7-bit clean
154+
|| b <= 0x1F // Below the printable character range
155+
|| b == 0x28 // Open parenthesis
156+
|| b == 0x29 // Close parenthesis
157+
|| b == BACKSLASH
158+
|| b == 0x7F /* Delete character */) {
159+
builder.append('\\');
160+
builder.append(byteToHex(b));
161+
} else {
162+
builder.append((char) b);
163+
}
164+
}
165+
return builder.toString();
166+
}
167+
139168
/**
140169
* This is mainly for debugging purposes so you can say "this is a rough idea of the CrestQuery object I've
141170
* been handed".

0 commit comments

Comments
 (0)