From 5c326850f1ab945cfca7ac9c5aaf77d1052c6bed Mon Sep 17 00:00:00 2001
From: Valera V Harseko <vharseko@3a-systems.ru>
Date: Fri, 17 Jul 2026 11:19:46 +0000
Subject: [PATCH] GHSA-p279-2cqp-84jg SASL PLAIN authzid bypassing the proxy ACI scope check

---
 opendj-server-legacy/src/main/java/org/opends/server/extensions/SASLContext.java |   56 +++++++++++++++++++++++++++++++++++++++++---------------
 1 files changed, 41 insertions(+), 15 deletions(-)

diff --git a/opendj-server-legacy/src/main/java/org/opends/server/extensions/SASLContext.java b/opendj-server-legacy/src/main/java/org/opends/server/extensions/SASLContext.java
index 7b24cd5..6f62e51 100644
--- a/opendj-server-legacy/src/main/java/org/opends/server/extensions/SASLContext.java
+++ b/opendj-server-legacy/src/main/java/org/opends/server/extensions/SASLContext.java
@@ -13,6 +13,7 @@
  *
  * Copyright 2008-2009 Sun Microsystems, Inc.
  * Portions Copyright 2011-2016 ForgeRock AS.
+ * Portions Copyrighted 2026 3A Systems, LLC.
  */
 package org.opends.server.extensions;
 
@@ -58,6 +59,7 @@
 import org.opends.server.types.AuthenticationInfo;
 import org.opends.server.types.DirectoryException;
 import org.opends.server.types.Entry;
+import org.opends.server.types.Operation;
 import org.opends.server.types.Privilege;
 
 /**
@@ -927,30 +929,54 @@
    */
   private boolean hasPermission(final AuthenticationInfo authInfo)
   {
-    boolean ret = true;
-    Entry e = authzEntry;
-
-    // If the authz entry is null, use the entry associated with the NULL DN.
-    if (e == null)
+    if (!hasProxyAccess(authInfo.getAuthenticationEntry(), authzEntry, bindOp))
     {
+      setCallbackMsg(ERR_SASL_AUTHZID_INSUFFICIENT_ACCESS.get(authEntry.getName()));
+      return false;
+    }
+    return true;
+  }
+
+
+
+  /**
+   * Determines whether the access control configuration permits the
+   * authenticated user to assume the given authorization identity, i.e. holds
+   * the "proxy" access control right for the target entry. A {@code null}
+   * authorization entry maps to the entry associated with the NULL DN (the
+   * anonymous / root authorization identity). This is the shared SASL
+   * proxy-scope check used by DIGEST-MD5 / GSSAPI and by SASL PLAIN.
+   *
+   * @param authEntry
+   *          The authenticated user entry (the proxy user).
+   * @param authzEntry
+   *          The entry to be assumed, or {@code null} for the anonymous / root
+   *          authorization identity.
+   * @param operation
+   *          The operation being processed.
+   * @return {@code true} if the authenticated user may assume the authorization
+   *         identity.
+   */
+  static boolean hasProxyAccess(final Entry authEntry, final Entry authzEntry,
+      final Operation operation)
+  {
+    Entry proxiedEntry = authzEntry;
+    if (proxiedEntry == null)
+    {
+      // A null authorization entry maps to the entry associated with the NULL DN.
       try
       {
-        e = DirectoryServer.getEntry(DN.rootDN());
+        proxiedEntry = DirectoryServer.getEntry(DN.rootDN());
       }
-      catch (final DirectoryException ex)
+      catch (final DirectoryException e)
       {
+        logger.traceException(e);
         return false;
       }
     }
 
-    if (!AccessControlConfigManager.getInstance().getAccessControlHandler()
-        .mayProxy(authInfo.getAuthenticationEntry(), e, bindOp))
-    {
-      setCallbackMsg(ERR_SASL_AUTHZID_INSUFFICIENT_ACCESS.get(authEntry.getName()));
-      ret = false;
-    }
-
-    return ret;
+    return AccessControlConfigManager.getInstance().getAccessControlHandler()
+        .mayProxy(authEntry, proxiedEntry, operation);
   }
 
 

--
Gitblit v1.10.0