Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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())
{
Expand Down Expand Up @@ -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.
*
Expand Down