From ff0653af9a57afaf256008ce4d39e7d6fc899446 Mon Sep 17 00:00:00 2001
From: Gaetan Boismal <gaetan.boismal@forgerock.com>
Date: Mon, 31 Oct 2016 14:00:38 +0000
Subject: [PATCH] OPENDJ-2937 Fix SDK SASL bind requests implementations

---
 opendj-core/src/main/java/org/forgerock/opendj/ldap/requests/ExternalSASLBindRequestImpl.java  |   20 ------
 opendj-core/src/main/java/org/forgerock/opendj/ldap/requests/CRAMMD5SASLBindRequestImpl.java   |   19 -----
 opendj-core/src/main/java/org/forgerock/opendj/ldap/requests/DigestMD5SASLBindRequestImpl.java |   18 -----
 opendj-core/src/main/java/org/forgerock/opendj/ldap/requests/PlainSASLBindRequestImpl.java     |    8 --
 opendj-core/src/main/java/org/forgerock/opendj/ldap/requests/SASLBindClientImpl.java           |   46 +++++++++++++++
 opendj-core/src/main/resources/com/forgerock/opendj/ldap/core.properties                       |    3 
 opendj-core/src/main/java/org/forgerock/opendj/ldap/requests/GSSAPISASLBindRequestImpl.java    |   20 ------
 opendj-core/src/test/java/org/forgerock/opendj/ldap/LDAPServer.java                            |   10 ++-
 8 files changed, 63 insertions(+), 81 deletions(-)

diff --git a/opendj-core/src/main/java/org/forgerock/opendj/ldap/requests/CRAMMD5SASLBindRequestImpl.java b/opendj-core/src/main/java/org/forgerock/opendj/ldap/requests/CRAMMD5SASLBindRequestImpl.java
index 568c795..ef56753 100644
--- a/opendj-core/src/main/java/org/forgerock/opendj/ldap/requests/CRAMMD5SASLBindRequestImpl.java
+++ b/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
diff --git a/opendj-core/src/main/java/org/forgerock/opendj/ldap/requests/DigestMD5SASLBindRequestImpl.java b/opendj-core/src/main/java/org/forgerock/opendj/ldap/requests/DigestMD5SASLBindRequestImpl.java
index 5ad1118..2261c0a 100644
--- a/opendj-core/src/main/java/org/forgerock/opendj/ldap/requests/DigestMD5SASLBindRequestImpl.java
+++ b/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
diff --git a/opendj-core/src/main/java/org/forgerock/opendj/ldap/requests/ExternalSASLBindRequestImpl.java b/opendj-core/src/main/java/org/forgerock/opendj/ldap/requests/ExternalSASLBindRequestImpl.java
index af07105..20d29fc 100644
--- a/opendj-core/src/main/java/org/forgerock/opendj/ldap/requests/ExternalSASLBindRequestImpl.java
+++ b/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);
         }
     }
 
diff --git a/opendj-core/src/main/java/org/forgerock/opendj/ldap/requests/GSSAPISASLBindRequestImpl.java b/opendj-core/src/main/java/org/forgerock/opendj/ldap/requests/GSSAPISASLBindRequestImpl.java
index 0b5e057..1e39ed2 100644
--- a/opendj-core/src/main/java/org/forgerock/opendj/ldap/requests/GSSAPISASLBindRequestImpl.java
+++ b/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;
diff --git a/opendj-core/src/main/java/org/forgerock/opendj/ldap/requests/PlainSASLBindRequestImpl.java b/opendj-core/src/main/java/org/forgerock/opendj/ldap/requests/PlainSASLBindRequestImpl.java
index 2c8bcb2..fa374d3 100644
--- a/opendj-core/src/main/java/org/forgerock/opendj/ldap/requests/PlainSASLBindRequestImpl.java
+++ b/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);
         }
diff --git a/opendj-core/src/main/java/org/forgerock/opendj/ldap/requests/SASLBindClientImpl.java b/opendj-core/src/main/java/org/forgerock/opendj/ldap/requests/SASLBindClientImpl.java
index a82efcf..6987eb9 100644
--- a/opendj-core/src/main/java/org/forgerock/opendj/ldap/requests/SASLBindClientImpl.java
+++ b/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);
+        }
+    }
 }
diff --git a/opendj-core/src/main/resources/com/forgerock/opendj/ldap/core.properties b/opendj-core/src/main/resources/com/forgerock/opendj/ldap/core.properties
index 92c89d6..d3d8375 100644
--- a/opendj-core/src/main/resources/com/forgerock/opendj/ldap/core.properties
+++ b/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
\ No newline at end of file
+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'
diff --git a/opendj-core/src/test/java/org/forgerock/opendj/ldap/LDAPServer.java b/opendj-core/src/test/java/org/forgerock/opendj/ldap/LDAPServer.java
index 25abde3..be59f39 100644
--- a/opendj-core/src/test/java/org/forgerock/opendj/ldap/LDAPServer.java
+++ b/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)) {

--
Gitblit v1.10.0