mirror of https://github.com/OpenIdentityPlatform/OpenDJ.git

Gaetan Boismal
16.41.2016 ff0653af9a57afaf256008ce4d39e7d6fc899446
OPENDJ-2937 Fix SDK SASL bind requests implementations

* For multi-stage (CRAM-MD5, DIGEST-MD5, GSSAPI and EXTERNAL) mechanisms
* Rely on the result of saslClient.evaluateChallenge(...) instead of saslClient.isComplete() to know if there is a need to send additinal data to the server (i.e if we are in a multi-stage SASL bind)
* Factorize code in parent class to prevent duplication.

* As SASL Plain is not multi-stage we do not need to override evaluateResult() in the bind request implementations, return true is enought.

* Also adapts test ldap server to not systematically attach a challenge to a bind result.
8 files modified
144 ■■■■■ changed files
opendj-core/src/main/java/org/forgerock/opendj/ldap/requests/CRAMMD5SASLBindRequestImpl.java 19 ●●●● patch | view | raw | blame | history
opendj-core/src/main/java/org/forgerock/opendj/ldap/requests/DigestMD5SASLBindRequestImpl.java 18 ●●●● patch | view | raw | blame | history
opendj-core/src/main/java/org/forgerock/opendj/ldap/requests/ExternalSASLBindRequestImpl.java 20 ●●●● patch | view | raw | blame | history
opendj-core/src/main/java/org/forgerock/opendj/ldap/requests/GSSAPISASLBindRequestImpl.java 20 ●●●● patch | view | raw | blame | history
opendj-core/src/main/java/org/forgerock/opendj/ldap/requests/PlainSASLBindRequestImpl.java 8 ●●●● patch | view | raw | blame | history
opendj-core/src/main/java/org/forgerock/opendj/ldap/requests/SASLBindClientImpl.java 46 ●●●●● patch | view | raw | blame | history
opendj-core/src/main/resources/com/forgerock/opendj/ldap/core.properties 3 ●●●● patch | view | raw | blame | history
opendj-core/src/test/java/org/forgerock/opendj/ldap/LDAPServer.java 10 ●●●● patch | view | raw | blame | history
opendj-core/src/main/java/org/forgerock/opendj/ldap/requests/CRAMMD5SASLBindRequestImpl.java
@@ -12,7 +12,7 @@
 * information: "Portions Copyright [year] [name of copyright owner]".
 *
 * Copyright 2010 Sun Microsystems, Inc.
 * Portions Copyright 2011-2014 ForgeRock AS.
 * Portions Copyright 2011-2016 ForgeRock AS.
 */
package org.forgerock.opendj.ldap.requests;
@@ -20,7 +20,6 @@
import static com.forgerock.opendj.util.StaticUtils.copyOfBytes;
import static org.forgerock.opendj.ldap.LdapException.newLdapException;
import static org.forgerock.opendj.ldap.responses.Responses.*;
import javax.security.auth.callback.NameCallback;
import javax.security.auth.callback.PasswordCallback;
@@ -79,21 +78,7 @@
        @Override
        public boolean evaluateResult(final BindResult result) throws LdapException {
            if (saslClient.isComplete()) {
                return true;
            }
            try {
                setNextSASLCredentials(saslClient.evaluateChallenge(result
                        .getServerSASLCredentials() == null ? new byte[0] : result
                        .getServerSASLCredentials().toByteArray()));
                return saslClient.isComplete();
            } catch (final SaslException e) {
                // FIXME: I18N need to have a better error message.
                // FIXME: Is this the best result code?
                throw newLdapException(newResult(ResultCode.CLIENT_SIDE_LOCAL_ERROR).setDiagnosticMessage(
                        "An error occurred during multi-stage authentication").setCause(e));
            }
            return evaluateSaslBindResult(saslClient, result);
        }
        @Override
opendj-core/src/main/java/org/forgerock/opendj/ldap/requests/DigestMD5SASLBindRequestImpl.java
@@ -12,7 +12,7 @@
 * information: "Portions Copyright [year] [name of copyright owner]".
 *
 * Copyright 2010 Sun Microsystems, Inc.
 * Portions Copyright 2011-2015 ForgeRock AS.
 * Portions Copyright 2011-2016 ForgeRock AS.
 */
package org.forgerock.opendj.ldap.requests;
@@ -138,21 +138,7 @@
        @Override
        public boolean evaluateResult(final BindResult result) throws LdapException {
            if (saslClient.isComplete()) {
                return true;
            }
            try {
                setNextSASLCredentials(saslClient.evaluateChallenge(result
                        .getServerSASLCredentials() == null ? new byte[0] : result
                        .getServerSASLCredentials().toByteArray()));
                return saslClient.isComplete();
            } catch (final SaslException e) {
                // FIXME: I18N need to have a better error message.
                // FIXME: Is this the best result code?
                throw newLdapException(ResultCode.CLIENT_SIDE_LOCAL_ERROR,
                        "An error occurred during multi-stage authentication", e);
            }
            return evaluateSaslBindResult(saslClient, result);
        }
        @Override
opendj-core/src/main/java/org/forgerock/opendj/ldap/requests/ExternalSASLBindRequestImpl.java
@@ -12,7 +12,7 @@
 * information: "Portions Copyright [year] [name of copyright owner]".
 *
 * Copyright 2010 Sun Microsystems, Inc.
 * Portions Copyright 2011-2014 ForgeRock AS.
 * Portions Copyright 2011-2016 ForgeRock AS.
 */
package org.forgerock.opendj.ldap.requests;
@@ -27,7 +27,6 @@
import org.forgerock.opendj.ldap.LdapException;
import org.forgerock.opendj.ldap.ResultCode;
import org.forgerock.opendj.ldap.responses.BindResult;
import org.forgerock.opendj.ldap.responses.Responses;
/**
 * External SASL bind request implementation.
@@ -67,22 +66,7 @@
        @Override
        public boolean evaluateResult(final BindResult result) throws LdapException {
            if (saslClient.isComplete()) {
                return true;
            }
            try {
                setNextSASLCredentials(saslClient.evaluateChallenge(result
                        .getServerSASLCredentials() == null ? new byte[0] : result
                        .getServerSASLCredentials().toByteArray()));
                return saslClient.isComplete();
            } catch (final SaslException e) {
                // FIXME: I18N need to have a better error message.
                // FIXME: Is this the best result code?
                throw newLdapException(Responses.newResult(
                        ResultCode.CLIENT_SIDE_LOCAL_ERROR).setDiagnosticMessage(
                        "An error occurred during multi-stage authentication").setCause(e));
            }
            return evaluateSaslBindResult(saslClient, result);
        }
    }
opendj-core/src/main/java/org/forgerock/opendj/ldap/requests/GSSAPISASLBindRequestImpl.java
@@ -12,7 +12,7 @@
 * information: "Portions Copyright [year] [name of copyright owner]".
 *
 * Copyright 2010 Sun Microsystems, Inc.
 * Portions Copyright 2011-2015 ForgeRock AS.
 * Portions Copyright 2011-2016 ForgeRock AS.
 */
package org.forgerock.opendj.ldap.requests;
@@ -115,23 +115,7 @@
                new PrivilegedExceptionAction<Boolean>() {
                    @Override
                    public Boolean run() throws LdapException {
                        if (saslClient.isComplete()) {
                            return true;
                        }
                        try {
                            setNextSASLCredentials(saslClient.evaluateChallenge(lastResult
                                    .getServerSASLCredentials() == null ? new byte[0] : lastResult
                                    .getServerSASLCredentials().toByteArray()));
                            return saslClient.isComplete();
                        } catch (final SaslException e) {
                            // FIXME: I18N need to have a better error message.
                            // FIXME: Is this the best result code?
                            throw newLdapException(Responses.newResult(
                                    ResultCode.CLIENT_SIDE_LOCAL_ERROR).setDiagnosticMessage(
                                    "An error occurred during multi-stage authentication")
                                    .setCause(e));
                        }
                        return evaluateSaslBindResult(saslClient, lastResult);
                    }
                };
        private BindResult lastResult;
opendj-core/src/main/java/org/forgerock/opendj/ldap/requests/PlainSASLBindRequestImpl.java
@@ -12,7 +12,7 @@
 * information: "Portions Copyright [year] [name of copyright owner]".
 *
 * Copyright 2010 Sun Microsystems, Inc.
 * Portions Copyright 2011-2014 ForgeRock AS.
 * Portions Copyright 2011-2016 ForgeRock AS.
 */
package org.forgerock.opendj.ldap.requests;
@@ -29,7 +29,6 @@
import org.forgerock.opendj.ldap.ByteString;
import org.forgerock.opendj.ldap.LdapException;
import org.forgerock.opendj.ldap.ResultCode;
import org.forgerock.opendj.ldap.responses.BindResult;
import org.forgerock.util.Reject;
import com.forgerock.opendj.util.StaticUtils;
@@ -77,11 +76,6 @@
        }
        @Override
        public boolean evaluateResult(final BindResult result) {
            return saslClient.isComplete();
        }
        @Override
        void handle(final NameCallback callback) throws UnsupportedCallbackException {
            callback.setName(authenticationID);
        }
opendj-core/src/main/java/org/forgerock/opendj/ldap/requests/SASLBindClientImpl.java
@@ -12,12 +12,14 @@
 * information: "Portions Copyright [year] [name of copyright owner]".
 *
 * Copyright 2009 Sun Microsystems, Inc.
 * Portions copyright 2011-2013 ForgeRock AS.
 * Portions copyright 2011-2016 ForgeRock AS.
 */
package org.forgerock.opendj.ldap.requests;
import static com.forgerock.opendj.ldap.CoreMessages.ERR_SASL_BIND_MULTI_STAGE;
import static com.forgerock.opendj.ldap.CoreMessages.INFO_SASL_UNSUPPORTED_CALLBACK;
import static org.forgerock.opendj.ldap.LdapException.newLdapException;
import java.io.IOException;
@@ -34,11 +36,16 @@
import javax.security.sasl.AuthorizeCallback;
import javax.security.sasl.RealmCallback;
import javax.security.sasl.RealmChoiceCallback;
import javax.security.sasl.SaslClient;
import javax.security.sasl.SaslException;
import org.forgerock.opendj.io.ASN1;
import org.forgerock.opendj.io.ASN1Writer;
import org.forgerock.opendj.ldap.ByteString;
import org.forgerock.opendj.ldap.ByteStringBuilder;
import org.forgerock.opendj.ldap.LdapException;
import org.forgerock.opendj.ldap.ResultCode;
import org.forgerock.opendj.ldap.responses.BindResult;
/**
 * SASL bind client implementation.
@@ -191,4 +198,41 @@
        return setNextAuthenticationValue(builder.toByteString().toByteArray());
    }
    /**
     * Evaluates the {@link BindResult} returned by the server and returns {@code true} iff the server
     * has sent a challenge and the client needs to send additional data.
     * <p>
     * If the server has sent a challenge, this method evaluates it and prepare data to send in the next request.
     *
     * @param saslClient
     *          The {@link SaslClient} to use to evaluate the challenge.
     * @param result
     *          The last {@link BindResult} returned by the server.
     * @return {@code true} iff the server has sent a challenge and the client needs to send additional data.
     * @throws LdapException
     *          If an error occurred when the {@link SaslClient} evaluates the challenge.
     */
    boolean evaluateSaslBindResult(final SaslClient saslClient, final BindResult result) throws LdapException {
        if (saslClient.isComplete()) {
            return true;
        }
        try {
            final ByteString serverSASLCredentials = result.getServerSASLCredentials();
            final byte[] nextResponse = saslClient.evaluateChallenge(
                    serverSASLCredentials == null ? new byte[0]
                                                  : serverSASLCredentials.toByteArray());
            if (nextResponse == null) {
                return true;
            }
            setNextSASLCredentials(nextResponse);
            return false;
        } catch (final SaslException e) {
            throw newLdapException(ResultCode.CLIENT_SIDE_LOCAL_ERROR,
                                   ERR_SASL_BIND_MULTI_STAGE.get(e.getLocalizedMessage()),
                                   e);
        }
    }
}
opendj-core/src/main/resources/com/forgerock/opendj/ldap/core.properties
@@ -1723,4 +1723,5 @@
 element as a connection affinity control because it did not have a value, when \
 a value must always be provided
ERR_UNKNOWN_REQUEST_TYPE=The following request has a unknown request type: %s
WARN_DECODING_AFFINITY_CONTROL=An error occurred while decoding an affinity control: %s
WARN_DECODING_AFFINITY_CONTROL=An error occurred while decoding an affinity control: %s
ERR_SASL_BIND_MULTI_STAGE=An error occurred during multi-stage authentication: '%s'
opendj-core/src/test/java/org/forgerock/opendj/ldap/LDAPServer.java
@@ -12,7 +12,7 @@
 * information: "Portions Copyright [year] [name of copyright owner]".
 *
 * Copyright 2010 Sun Microsystems, Inc.
 * Portions Copyright 2011-2015 ForgeRock AS.
 * Portions Copyright 2011-2016 ForgeRock AS.
 */
package org.forgerock.opendj.ldap;
@@ -255,8 +255,12 @@
                    byte[] challenge = saslServer.evaluateResponse(saslCred.toByteArray());
                    if (saslServer.isComplete()) {
                        resultHandler.handleResult(Responses.newBindResult(ResultCode.SUCCESS)
                                .setServerSASLCredentials(ByteString.wrap(challenge)));
                        final BindResult result = Responses.newBindResult(ResultCode.SUCCESS);
                        if (challenge != null) {
                            // If there was a SASL bind in progress, challenge has already been provided
                            result.setServerSASLCredentials(ByteString.wrap(challenge));
                        }
                        resultHandler.handleResult(result);
                        String qop = (String) saslServer.getNegotiatedProperty(Sasl.QOP);
                        if ("auth-int".equalsIgnoreCase(qop) || "auth-conf".equalsIgnoreCase(qop)) {