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/PlainSASLMechanismHandler.java |   42 ++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 42 insertions(+), 0 deletions(-)

diff --git a/opendj-server-legacy/src/main/java/org/opends/server/extensions/PlainSASLMechanismHandler.java b/opendj-server-legacy/src/main/java/org/opends/server/extensions/PlainSASLMechanismHandler.java
index eff15b6..9a9b478 100644
--- a/opendj-server-legacy/src/main/java/org/opends/server/extensions/PlainSASLMechanismHandler.java
+++ b/opendj-server-legacy/src/main/java/org/opends/server/extensions/PlainSASLMechanismHandler.java
@@ -13,6 +13,7 @@
  *
  * Copyright 2006-2009 Sun Microsystems, Inc.
  * Portions Copyright 2011-2016 ForgeRock AS.
+ * Portions Copyrighted 2026 3A Systems, LLC.
  */
 package org.opends.server.extensions;
 
@@ -330,6 +331,11 @@
               return;
             }
           }
+
+          if (! checkProxyAccess(bindOperation, userEntry, authZEntry))
+          {
+            return;
+          }
         }
       }
       else
@@ -391,6 +397,11 @@
             bindOperation.setAuthFailureReason(message);
             return;
           }
+
+          if (! checkProxyAccess(bindOperation, userEntry, authZEntry))
+          {
+            return;
+          }
         }
       }
     }
@@ -444,6 +455,37 @@
     return;
   }
 
+  /**
+   * Checks whether the authenticated user is allowed by the access control
+   * subsystem to assume the given authorization identity (i.e. holds the
+   * "proxy" access control right for the target entry), and if not sets the
+   * bind failure result. This delegates to {@link SASLContext#hasProxyAccess}
+   * so that SASL PLAIN enforces the same ACI scope check as the proxied
+   * authorization controls and the DIGEST-MD5 / GSSAPI {@code authzid}, in
+   * addition to the {@code PROXIED_AUTH} privilege check. The denial uses the
+   * same {@code INVALID_CREDENTIALS} result and message as DIGEST-MD5 / GSSAPI
+   * so a client cannot distinguish a missing privilege from a missing proxy
+   * grant.
+   *
+   * @param  bindOperation  The bind operation being processed.
+   * @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.
+   * @return  {@code true} if the access control configuration permits the
+   *          authenticated user to assume the authorization identity.
+   */
+  private boolean checkProxyAccess(BindOperation bindOperation, Entry authEntry, Entry authZEntry)
+  {
+    if (! SASLContext.hasProxyAccess(authEntry, authZEntry, bindOperation))
+    {
+      bindOperation.setResultCode(ResultCode.INVALID_CREDENTIALS);
+      bindOperation.setAuthFailureReason(
+          ERR_SASL_AUTHZID_INSUFFICIENT_ACCESS.get(authEntry.getName()));
+      return false;
+    }
+    return true;
+  }
+
   @Override
   public boolean isPasswordBased(String mechanism)
   {

--
Gitblit v1.10.0