diff --git a/opendj-server-legacy/src/main/java/org/opends/server/core/BindOperationBasis.java b/opendj-server-legacy/src/main/java/org/opends/server/core/BindOperationBasis.java index 957dddb213..22669d4026 100644 --- a/opendj-server-legacy/src/main/java/org/opends/server/core/BindOperationBasis.java +++ b/opendj-server-legacy/src/main/java/org/opends/server/core/BindOperationBasis.java @@ -13,6 +13,7 @@ * * Copyright 2007-2010 Sun Microsystems, Inc. * Portions Copyright 2013-2016 ForgeRock AS. + * Portions Copyright 2026 3A Systems, LLC */ package org.opends.server.core; @@ -52,6 +53,15 @@ public class BindOperationBasis { private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass(); + /** + * The cancel request sent to the other operations in progress when a bind + * starts. CancelRequest and LocalizableMessage are both immutable (the + * message is rendered per locale when used), so a single shared instance + * avoids building both objects on every bind. + */ + private static final CancelRequest CANCEL_ALL_BY_BIND_REQUEST = + new CancelRequest(true, INFO_CANCELED_BY_BIND_REQUEST.get()); + /** The credentials used for SASL authentication. */ private ByteString saslCredentials; @@ -483,9 +493,7 @@ public final void run() clientConnection.setUnauthenticated(); // Abandon any operations that may be in progress for the client. - LocalizableMessage cancelReason = INFO_CANCELED_BY_BIND_REQUEST.get(); - CancelRequest cancelRequest = new CancelRequest(true, cancelReason); - clientConnection.cancelAllOperationsExcept(cancelRequest, getMessageID()); + clientConnection.cancelAllOperationsExcept(CANCEL_ALL_BY_BIND_REQUEST, getMessageID()); // This flag is set to true as soon as a workflow has been executed. boolean workflowExecuted = false; diff --git a/opendj-server-legacy/src/main/java/org/opends/server/core/PasswordPolicyState.java b/opendj-server-legacy/src/main/java/org/opends/server/core/PasswordPolicyState.java index 250bc6ff02..0463a38b37 100644 --- a/opendj-server-legacy/src/main/java/org/opends/server/core/PasswordPolicyState.java +++ b/opendj-server-legacy/src/main/java/org/opends/server/core/PasswordPolicyState.java @@ -13,6 +13,7 @@ * * Copyright 2006-2010 Sun Microsystems, Inc. * Portions Copyright 2011-2016 ForgeRock AS. + * Portions Copyright 2026 3A Systems, LLC */ package org.opends.server.core; @@ -1963,27 +1964,50 @@ public boolean passwordMatches(ByteString password) return false; } + boolean isAuthPassword = passwordPolicy.isAuthPasswordSyntax(); for (Attribute a : attrList) { for (ByteString v : a) { try { - String[] pwComponents = getPwComponents(v); - String schemeName = pwComponents[0]; - PasswordStorageScheme scheme = getPasswordStorageScheme(schemeName); - if (scheme == null) + String schemeName; + boolean matches; + if (isAuthPassword) { - if (logger.isTraceEnabled()) + String[] pwComponents = getPwComponents(v); + schemeName = pwComponents[0]; + PasswordStorageScheme scheme = getPasswordStorageScheme(schemeName); + if (scheme == null) { - logger.trace("User entry %s contains a password with scheme %s that is not defined in the server.", - userDNString, schemeName); + traceUndefinedScheme(schemeName); + continue; } - - continue; + matches = passwordMatches(password, pwComponents, scheme); + } + else + { + // This method runs for every bind: parse the {scheme} prefix at + // the byte level instead of materializing the whole stored value + // as a String only to convert its payload back to bytes. + int closePos = userPasswordSchemeEnd(v); + if (closePos < 0) + { + // Malformed value: raise the same errors as the String decoder. + getPwComponents(v); + continue; + } + schemeName = toLowerCase(v.subSequence(1, closePos).toString()); + PasswordStorageScheme scheme = DirectoryServer.getPasswordStorageScheme(schemeName); + if (scheme == null) + { + traceUndefinedScheme(schemeName); + continue; + } + matches = scheme.passwordMatches(password, v.subSequence(closePos + 1, v.length())); } - if (passwordMatches(password, pwComponents, scheme)) + if (matches) { if (logger.isTraceEnabled()) { @@ -2024,6 +2048,37 @@ private String[] getPwComponents(ByteString v) throws DirectoryException : UserPasswordSyntax.decodeUserPassword(v.toString()); } + /** + * Returns the index of the closing brace of the {scheme} prefix of the + * provided user password value, or -1 if the value is not well-formed + * (also when the scheme name is empty, mirroring + * {@link UserPasswordSyntax#decodeUserPassword(String)}). + */ + private static int userPasswordSchemeEnd(ByteString v) + { + if (v.length() == 0 || v.byteAt(0) != '{') + { + return -1; + } + for (int i = 1; i < v.length(); i++) + { + if (v.byteAt(i) == '}') + { + return i > 1 ? i : -1; + } + } + return -1; + } + + private void traceUndefinedScheme(String schemeName) + { + if (logger.isTraceEnabled()) + { + logger.trace("User entry %s contains a password with scheme %s that is not defined in the server.", + userDNString, schemeName); + } + } + /** * Indicates whether the provided password value is pre-encoded. *