From 139c40de1bc595ccd4b8ca952da9e2a37bc8a18e Mon Sep 17 00:00:00 2001
From: dugan <dugan@localhost>
Date: Wed, 05 Nov 2008 13:22:43 +0000
Subject: [PATCH] These fixes add confidentiality/integrity to the SASL GSSAPI and DIGEST-MD5 mechanisms. The issue links:

---
 opends/src/admin/defn/org/opends/server/admin/std/DigestMD5SASLMechanismHandlerConfiguration.xml                  |  131 +
 opends/src/server/org/opends/server/core/AccessControlConfigManager.java                                          |    2 
 opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/DigestMD5SASLMechanismHandlerTestCase.java |   35 
 opends/src/server/org/opends/server/util/ServerConstants.java                                                     |   33 
 opends/src/server/org/opends/server/authorization/dseecompat/AciEvalContext.java                                  |    2 
 opends/src/admin/defn/org/opends/server/admin/std/GSSAPISASLMechanismHandlerConfiguration.xml                     |   81 
 opends/src/server/org/opends/server/extensions/GSSAPISASLMechanismHandler.java                                    |  534 ++---
 opends/src/server/org/opends/server/extensions/NullConnectionSecurityProvider.java                                |    9 
 opends/tests/unit-tests-testng/src/server/org/opends/server/tools/LDAPSearchTestCase.java                         |   17 
 opends/src/messages/messages/extension.properties                                                                 |  353 +---
 opends/src/server/org/opends/server/protocols/ldap/LDAPClientConnection.java                                      |   25 
 opends/resource/schema/02-config.ldif                                                                             |   19 
 opends/src/server/org/opends/server/authorization/dseecompat/Aci.java                                             |    2 
 opends/src/server/org/opends/server/extensions/SASLSecurityProvider.java                                          |  457 +++++
 opends/src/server/org/opends/server/extensions/SASLContext.java                                                   |  894 ++++++++++
 opends/src/server/org/opends/server/authorization/dseecompat/AciContainer.java                                    |   51 
 opends/src/server/org/opends/server/api/AccessControlHandler.java                                                 |   17 
 opends/src/server/org/opends/server/api/ConnectionSecurityProvider.java                                           |   10 
 opends/src/server/org/opends/server/authorization/dseecompat/AciHandler.java                                      |   36 
 opends/src/server/org/opends/server/extensions/DigestMD5SASLMechanismHandler.java                                 | 1631 ++-----------------
 opends/src/server/org/opends/server/extensions/TLSConnectionSecurityProvider.java                                 |    9 
 opends/tests/unit-tests-testng/src/server/org/opends/server/tools/LDAPCompareTestCase.java                        |   21 
 opends/tests/unit-tests-testng/src/server/org/opends/server/tools/LDAPAuthenticationHandlerTestCase.java          |   38 
 opends/tests/unit-tests-testng/src/server/org/opends/server/tools/LDAPModifyTestCase.java                         |   17 
 opends/src/server/org/opends/server/api/ClientConnection.java                                                     |   28 
 opends/src/server/org/opends/server/core/DefaultAccessControlHandler.java                                         |   10 
 /dev/null                                                                                                         |  531 ------
 opends/tests/unit-tests-testng/src/server/org/opends/server/types/PrivilegeTestCase.java                          |   24 
 opends/src/server/org/opends/server/authorization/dseecompat/AciLDAPOperationContainer.java                       |   20 
 29 files changed, 2,378 insertions(+), 2,659 deletions(-)

diff --git a/opends/resource/schema/02-config.ldif b/opends/resource/schema/02-config.ldif
index 00e4eeb..79bf5ae 100644
--- a/opends/resource/schema/02-config.ldif
+++ b/opends/resource/schema/02-config.ldif
@@ -2354,6 +2354,21 @@
   NAME 'ds-cfg-prohibited-subtrees'
   SYNTAX 1.3.6.1.4.1.1466.115.121.1.12
   X-ORIGIN 'OpenDS Directory Server' )
+attributeTypes: ( 1.3.6.1.4.1.26027.1.1.511
+  NAME 'ds-cfg-quality-of-protection'
+  SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
+  SINGLE-VALUE
+  X-ORIGIN 'OpenDS Directory Server' )
+attributeTypes: ( 1.3.6.1.4.1.26027.1.1.512
+  NAME 'ds-cfg-cipher-strength'
+  SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
+  SINGLE-VALUE
+  X-ORIGIN 'OpenDS Directory Server' )
+attributeTypes: ( 1.3.6.1.4.1.26027.1.1.513
+  NAME 'ds-cfg-principal-name'
+  SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
+  SINGLE-VALUE
+  X-ORIGIN 'OpenDS Directory Server' )
 objectClasses: ( 1.3.6.1.4.1.26027.1.2.1
   NAME 'ds-cfg-access-control-handler'
   SUP top
@@ -2801,6 +2816,8 @@
   STRUCTURAL
   MUST ds-cfg-identity-mapper
   MAY ( ds-cfg-realm $
+        ds-cfg-cipher-strength $
+        ds-cfg-quality-of-protection $
         ds-cfg-server-fqdn )
   X-ORIGIN 'OpenDS Directory Server' )
 objectClasses: ( 1.3.6.1.4.1.26027.1.2.47
@@ -2811,6 +2828,8 @@
         ds-cfg-realm $
         ds-cfg-kdc-address $
         ds-cfg-keytab $
+        ds-cfg-principal-name $
+        ds-cfg-quality-of-protection $
         ds-cfg-server-fqdn )
   X-ORIGIN 'OpenDS Directory Server' )
 objectClasses: ( 1.3.6.1.4.1.26027.1.2.48
diff --git a/opends/src/admin/defn/org/opends/server/admin/std/DigestMD5SASLMechanismHandlerConfiguration.xml b/opends/src/admin/defn/org/opends/server/admin/std/DigestMD5SASLMechanismHandlerConfiguration.xml
index e52079d..ceb4cc0 100644
--- a/opends/src/admin/defn/org/opends/server/admin/std/DigestMD5SASLMechanismHandlerConfiguration.xml
+++ b/opends/src/admin/defn/org/opends/server/admin/std/DigestMD5SASLMechanismHandlerConfiguration.xml
@@ -33,18 +33,18 @@
   <adm:synopsis>
     The DIGEST-MD5 SASL mechanism
     is used to perform all processing related to SASL DIGEST-MD5
-    authentication. 
+    authentication.
   </adm:synopsis>
   <adm:description>
-    The DIGEST-MD5 SASL mechanism is very similar 
-    to the CRAM-MD5 mechanism in that it allows for password-based 
-    authentication without exposing the password in the clear 
-    (although it does require that both the client and the server 
-    have access to the clear-text password). Like the CRAM-MD5 
-    mechanism, it uses data that is randomly generated by the server 
-    to make it resistant to replay attacks, but it also includes 
-    randomly-generated data from the client, which makes it also 
-    resistant to problems resulting from weak server-side random 
+    The DIGEST-MD5 SASL mechanism is very similar
+    to the CRAM-MD5 mechanism in that it allows for password-based
+    authentication without exposing the password in the clear
+    (although it does require that both the client and the server
+    have access to the clear-text password). Like the CRAM-MD5
+    mechanism, it uses data that is randomly generated by the server
+    to make it resistant to replay attacks, but it also includes
+    randomly-generated data from the client, which makes it also
+    resistant to problems resulting from weak server-side random
     number generation.
   </adm:description>
   <adm:profile name="ldap">
@@ -64,18 +64,18 @@
   </adm:property-override>
   <adm:property name="realm">
     <adm:synopsis>
-      Specifies the realm that is to be used by the server for
+      Specifies the realms that is to be used by the server for
       DIGEST-MD5 authentication.
     </adm:synopsis>
     <adm:description>
-      If this value is not provided, then the server defaults to use a
-      set of realm names that correspond to the defined suffixes.
+      If this value is not provided, then the server defaults to use the fully
+	  qualified hostname of the machine.
     </adm:description>
     <adm:default-behavior>
       <adm:alias>
         <adm:synopsis>
-          The server defaults to a set of realm names that
-          correspond to the defined suffixes.
+        If this value is not provided, then the server defaults to use the fully
+        qualified hostname of the machine.
         </adm:synopsis>
       </adm:alias>
     </adm:default-behavior>
@@ -85,8 +85,7 @@
           <adm:regex>.*</adm:regex>
           <adm:usage>STRING</adm:usage>
           <adm:synopsis>
-            Any realm string. As needed, it be a DN or matched 
-            to a realm already in use for another service.
+            Any realm string that does not contain a comma.
           </adm:synopsis>
         </adm:pattern>
       </adm:string>
@@ -96,7 +95,80 @@
         <ldap:name>ds-cfg-realm</ldap:name>
       </ldap:attribute>
     </adm:profile>
-  </adm:property> <adm:property name="identity-mapper" mandatory="true">
+    </adm:property>
+  <adm:property name="quality-of-protection">
+    <adm:synopsis>
+     The name of a property that specifies the quality of protection
+	 the server will support.
+    </adm:synopsis>
+    <adm:default-behavior>
+      <adm:defined>
+        <adm:value>none</adm:value>
+      </adm:defined>
+    </adm:default-behavior>
+    <adm:syntax>
+      <adm:enumeration>
+        <adm:value name="none">
+          <adm:synopsis>
+            QOP equals authentication only.
+          </adm:synopsis>
+        </adm:value>
+        <adm:value name="integrity">
+          <adm:synopsis>
+            Quality of protection equals authentication with integrity
+			protection.
+          </adm:synopsis>
+        </adm:value>
+        <adm:value name="confidentiality">
+          <adm:synopsis>
+            Quality of protection equals authentication with integrity and
+            confidentiality protection.
+          </adm:synopsis>
+        </adm:value>
+      </adm:enumeration>
+    </adm:syntax>
+    <adm:profile name="ldap">
+      <ldap:attribute>
+        <ldap:name>ds-cfg-quality-of-protection</ldap:name>
+      </ldap:attribute>
+    </adm:profile>
+  </adm:property>
+    <adm:property name="cipher-strength">
+    <adm:synopsis>
+     The name of a property that specifies the minimum cipher strength that the
+	 server will support.
+    </adm:synopsis>
+    <adm:default-behavior>
+      <adm:defined>
+        <adm:value>low</adm:value>
+      </adm:defined>
+    </adm:default-behavior>
+    <adm:syntax>
+      <adm:enumeration>
+        <adm:value name="low">
+          <adm:synopsis>
+            Cipher strength suported is high, medium or low.
+          </adm:synopsis>
+        </adm:value>
+        <adm:value name="medium">
+          <adm:synopsis>
+           Cipher strength suported is medium,high.
+          </adm:synopsis>
+          </adm:value>
+          <adm:value name="high">
+          <adm:synopsis>
+          Cipher strength suported is high only.
+          </adm:synopsis>
+        </adm:value>
+      </adm:enumeration>
+    </adm:syntax>
+    <adm:profile name="ldap">
+      <ldap:attribute>
+        <ldap:name>ds-cfg-cipher-strength</ldap:name>
+      </ldap:attribute>
+    </adm:profile>
+  </adm:property>
+ <adm:property name="identity-mapper" mandatory="true">
     <adm:synopsis>
       Specifies the name of the identity mapper that is to be used
       with this SASL mechanism handler to match the authentication
@@ -128,20 +200,21 @@
       </ldap:attribute>
     </adm:profile>
   </adm:property>
+
   <adm:property name="server-fqdn">
     <adm:synopsis>
       Specifies the DNS-resolvable fully-qualified domain name for the
-      server that is used when validating the digest-uri parameter during 
-      the authentication process. 
+      server that is used when validating the digest-uri parameter during
+      the authentication process.
     </adm:synopsis>
     <adm:description>
-      If this configuration attribute is 
-      present, then the server expects that clients use a digest-uri equal 
-      to "ldap/" followed by the value of this attribute. For example, if 
-      the attribute has a value of "directory.example.com", then the 
-      server expects clients to use a digest-uri of 
-      "ldap/directory.example.com". If no value is provided, then the 
-      server does not attempt to validate the digest-uri provided by the 
+      If this configuration attribute is
+      present, then the server expects that clients use a digest-uri equal
+      to "ldap/" followed by the value of this attribute. For example, if
+      the attribute has a value of "directory.example.com", then the
+      server expects clients to use a digest-uri of
+      "ldap/directory.example.com". If no value is provided, then the
+      server does not attempt to validate the digest-uri provided by the
       client and accepts any value.
     </adm:description>
     <adm:default-behavior>
@@ -158,12 +231,12 @@
           <adm:regex>.*</adm:regex>
           <adm:usage>STRING</adm:usage>
           <adm:synopsis>
-            The fully-qualified address that is expected for clients to use 
+            The fully-qualified address that is expected for clients to use
             when connecting to the server and authenticating via DIGEST-MD5.
           </adm:synopsis>
         </adm:pattern>
       </adm:string>
-    </adm:syntax> 
+    </adm:syntax>
     <adm:profile name="ldap">
       <ldap:attribute>
         <ldap:name>ds-cfg-server-fqdn</ldap:name>
diff --git a/opends/src/admin/defn/org/opends/server/admin/std/GSSAPISASLMechanismHandlerConfiguration.xml b/opends/src/admin/defn/org/opends/server/admin/std/GSSAPISASLMechanismHandlerConfiguration.xml
index b76f7fb..2579301 100644
--- a/opends/src/admin/defn/org/opends/server/admin/std/GSSAPISASLMechanismHandlerConfiguration.xml
+++ b/opends/src/admin/defn/org/opends/server/admin/std/GSSAPISASLMechanismHandlerConfiguration.xml
@@ -31,15 +31,15 @@
   xmlns:adm="http://www.opends.org/admin"
   xmlns:ldap="http://www.opends.org/admin-ldap">
   <adm:synopsis>
-    The GSSAPI SASL mechanism 
+    The GSSAPI SASL mechanism
     performs all processing related to SASL GSSAPI
     authentication using Kerberos V5.
   </adm:synopsis>
   <adm:description>
-    The GSSAPI SASL mechanism provides the ability for clients 
-    to authenticate themselves to the server using existing 
-    authentication in a Kerberos environment. This mechanism 
-    provides the ability to achieve single sign-on for 
+    The GSSAPI SASL mechanism provides the ability for clients
+    to authenticate themselves to the server using existing
+    authentication in a Kerberos environment. This mechanism
+    provides the ability to achieve single sign-on for
     Kerberos-based clients.
   </adm:description>
   <adm:profile name="ldap">
@@ -85,7 +85,7 @@
     </adm:synopsis>
     <adm:description>
       If provided, this property must be a fully-qualified DNS-resolvable name.
-      If this property is not provided, then the server attempts to determine it 
+      If this property is not provided, then the server attempts to determine it
       from the system-wide Kerberos configuration.
     </adm:description>
     <adm:default-behavior>
@@ -105,6 +105,71 @@
       </ldap:attribute>
     </adm:profile>
   </adm:property>
+  <adm:property name="quality-of-protection">
+    <adm:synopsis>
+     The name of a property that specifies the quality of protection
+     the server will support.
+    </adm:synopsis>
+    <adm:default-behavior>
+      <adm:defined>
+        <adm:value>none</adm:value>
+      </adm:defined>
+    </adm:default-behavior>
+    <adm:syntax>
+      <adm:enumeration>
+        <adm:value name="none">
+          <adm:synopsis>
+            QOP equals authentication only.
+          </adm:synopsis>
+        </adm:value>
+        <adm:value name="integrity">
+          <adm:synopsis>
+            Quality of protection equals authentication with integrity
+            protection.
+          </adm:synopsis>
+        </adm:value>
+        <adm:value name="confidentiality">
+          <adm:synopsis>
+            Quality of protection equals authentication with integrity and
+            confidentiality protection.
+          </adm:synopsis>
+        </adm:value>
+      </adm:enumeration>
+    </adm:syntax>
+    <adm:profile name="ldap">
+      <ldap:attribute>
+        <ldap:name>ds-cfg-quality-of-protection</ldap:name>
+      </ldap:attribute>
+    </adm:profile>
+  </adm:property>
+  <adm:property name="principal-name">
+    <adm:synopsis>
+      Specifies the principal name.
+    </adm:synopsis>
+    <adm:description>
+      It can either be a simple user name or a
+      service name such as host/example.com.
+      If this property is not provided, then the server attempts to build the
+      principal name by appending the fully qualified domain name to the string
+      "ldap/".
+    </adm:description>
+    <adm:default-behavior>
+      <adm:alias>
+        <adm:synopsis>
+          The server attempts to determine the principal name from the
+          underlying system configuration.
+        </adm:synopsis>
+      </adm:alias>
+    </adm:default-behavior>
+    <adm:syntax>
+      <adm:string />
+    </adm:syntax>
+    <adm:profile name="ldap">
+      <ldap:attribute>
+        <ldap:name>ds-cfg-principal-name</ldap:name>
+      </ldap:attribute>
+    </adm:profile>
+  </adm:property>
   <adm:property name="keytab">
     <adm:synopsis>
       Specifies the path to the keytab file that should be used for
@@ -155,8 +220,8 @@
   <adm:property name="identity-mapper" mandatory="true">
     <adm:synopsis>
       Specifies the name of the identity mapper that is to be used
-      with this SASL mechanism handler 
-      to match the Kerberos principal 
+      with this SASL mechanism handler
+      to match the Kerberos principal
       included in the SASL bind request to the corresponding
       user in the directory.
     </adm:synopsis>
diff --git a/opends/src/messages/messages/extension.properties b/opends/src/messages/messages/extension.properties
index ade0c5e..9811b59 100644
--- a/opends/src/messages/messages/extension.properties
+++ b/opends/src/messages/messages/extension.properties
@@ -400,7 +400,7 @@
  security provider is required for clients that wish to use SASL EXTERNAL \
  authentication
 MILD_ERR_SASLEXTERNAL_NO_CLIENT_CERT_126=The SASL EXTERNAL bind request could \
- not be processed because the client did not present a certificate chain \
+ not be processed because the client did not present an certificate chain \
  during SSL/TLS negotiation
 MILD_ERR_SASLEXTERNAL_NO_MAPPING_127=The SASL EXTERNAL bind request failed \
  because the certificate chain presented by the client during SSL/TLS \
@@ -614,271 +614,76 @@
 INFO_SASLCRAMMD5_UPDATED_USER_BASE_DN_191=Attribute ds-cfg-user-base-dn in \
  configuration entry %s has been updated.  The DN %s will now be used as the \
  search base when looking up user entries based on their username
-SEVERE_ERR_SASLDIGESTMD5_CANNOT_GET_MESSAGE_DIGEST_192=An unexpected error \
- occurred while attempting to obtain an MD5 digest engine for use by the \
- DIGEST-MD5 SASL handler:  %s
-INFO_SASLDIGESTMD5_DESCRIPTION_USERNAME_ATTRIBUTE_193=Name of \
- the attribute that will be used to identify user entries based on the \
- username provided during SASL DIGEST-MD5 authentication.  This must specify \
- the name of a valid attribute type defined in the server schema.  Changes to \
- this configuration attribute will take effect immediately
-SEVERE_ERR_SASLDIGESTMD5_CANNOT_GET_USERNAME_ATTR_194=An unexpected error \
- occurred while attempting to determine the value of the \
- ds-cfg-user-name-attribute attribute in configuration entry %s:  %s
-SEVERE_ERR_SASLDIGESTMD5_UNKNOWN_USERNAME_ATTR_195=The attribute %s \
- referenced in configuration attribute ds-cfg-user-name-attribute in \
- configuration entry %s does not exist in the Directory Server schema.  The \
- attribute that is to be used for username lookups during SASL DIGEST-MD5 \
- authentication must be defined in the server schema
-INFO_SASLDIGESTMD5_DESCRIPTION_USER_BASE_DN_196=Base DN that \
- should be used when searching for entries based on the username provided \
- during SASL DIGEST-MD5 authentication.  Changes to this configuration \
- attribute will take effect immediately
-SEVERE_ERR_SASLDIGESTMD5_CANNOT_GET_USER_BASE_DN_197=An unexpected error \
- occurred while attempting to determine the value of the ds-cfg-user-base-dn \
- attribute in configuration entry %s:  %s
-INFO_SASLDIGESTMD5_DESCRIPTION_REALM_198=Realm that should be \
- used by the server for DIGEST-MD5 authentication.  If this is not provided, \
- then the server will default to using a set of realm names that correspond to \
- the defined suffixes.  Changes to this configuration attribute will take \
- effect immediately
-SEVERE_ERR_SASLDIGESTMD5_CANNOT_GET_REALM_199=An unexpected error occurred \
- while attempting to determine the value of the ds-cfg-realm attribute in \
- configuration entry %s:  %s
-SEVERE_WARN_SASLDIGESTMD5_CHALLENGE_TOO_LONG_200=The initial DIGEST-MD5 must \
- be less than 2048 bytes, but the generated challenge was %d bytes
-MILD_ERR_SASLDIGESTMD5_NO_CREDENTIALS_201=The client connection included \
- DIGEST-MD5 state information, indicating that the client was in the process \
- of performing a DIGEST-MD5 bind, but the bind request did not include any \
+ INFO_SASL_UNSUPPORTED_CALLBACK_192=An unsupported or unexpected callback was \
+ provided to the SASL server for use during %s authentication:  %s
+MILD_ERR_SASL_NO_CREDENTIALS_193=The client connection included \
+ %s state information, indicating that the client was in the process \
+ of performing a %s bind, but the bind request did not include any \
  credentials
-MILD_ERR_SASLDIGESTMD5_INVALID_STORED_STATE_202=The SASL DIGEST-MD5 bind \
- request contained SASL credentials, but the stored SASL state information for \
- this client connection is not in an appropriate form for the challenge
-SEVERE_WARN_SASLDIGESTMD5_CANNOT_PARSE_ISO_CREDENTIALS_203=An error occurred \
- while attempting to parse the DIGEST-MD5 credentials as a string using the %s \
- character set:  %s.  The server will re-try using UTF-8
-SEVERE_WARN_SASLDIGESTMD5_CANNOT_PARSE_UTF8_CREDENTIALS_204=An error occurred \
- while attempting to parse the DIGEST-MD5 credentials as a string using the \
- UTF-8 character set:  %s
-MILD_ERR_SASLDIGESTMD5_INVALID_TOKEN_IN_CREDENTIALS_205=The DIGEST-MD5 \
- credentials provided by the client contained an invalid token of "%s" \
- starting at position %d
-MILD_ERR_SASLDIGESTMD5_INVALID_CHARSET_206=The DIGEST-MD5 credentials \
- provided by the client specified an invalid character set of %s.  Only a \
- value of 'utf-8' is acceptable for this parameter
-MILD_ERR_SASLDIGESTMD5_CANNOT_DECODE_REALM_AS_DN_207=An error occurred while \
- attempting to parse the provided response realm "%s" as a DN:  %s
-MILD_ERR_SASLDIGESTMD5_INVALID_REALM_208=The DIGEST-MD5 credentials provided \
- by the client included an invalid realm of "%s"
-SEVERE_ERR_SASLDIGESTMD5_INVALID_NONCE_209=The DIGEST-MD5 credentials \
- provided by the client included a nonce that was different from the nonce \
- supplied by the server.  This could indicate a replay attack or a chosen \
- plaintext attack, and as a result the client connection will be terminated
-MILD_ERR_SASLDIGESTMD5_CANNOT_DECODE_NONCE_COUNT_210=The DIGEST-MD5 \
- credentials provided by the client included a nonce count "%s" that could not \
- be decoded as a hex-encoded integer
-SEVERE_ERR_SASLDIGESTMD5_CANNOT_DECODE_STORED_NONCE_COUNT_211=An unexpected \
- error occurred while attempting to decode the nonce count stored by the \
- server for this client connection:  %s
-SEVERE_ERR_SASLDIGESTMD5_INVALID_NONCE_COUNT_212=The DIGEST-MD5 credentials \
- provided by the client included a nonce count that was different from the \
- count expected by the server.  This could indicate a replay attack, and as a \
- result the client connection will be terminated
-MILD_ERR_SASLDIGESTMD5_INTEGRITY_NOT_SUPPORTED_213=The client requested the \
- auth-int quality of protection but integrity protection is not currently \
- supported by the Directory Server
-MILD_ERR_SASLDIGESTMD5_CONFIDENTIALITY_NOT_SUPPORTED_214=The client requested \
- the auth-conf quality of protection but confidentiality protection is not \
- currently supported by the Directory Server
-MILD_ERR_SASLDIGESTMD5_INVALID_QOP_215=The DIGEST-MD5 credentials provided by \
- the client requested an invalid quality of protection mechanism of %s
-MILD_ERR_SASLDIGESTMD5_CANNOT_PARSE_RESPONSE_DIGEST_216=The DIGEST-MD5 \
- credentials provided by the client included a digest that could not be \
- decoded as a hex-encoded byte sequence:  %s
-MILD_ERR_SASLDIGESTMD5_INVALID_RESPONSE_TOKEN_217=The DIGEST-MD5 credentials \
- provided by the client included an invalid token named "%s"
-MILD_ERR_SASLDIGESTMD5_NO_USERNAME_IN_RESPONSE_218=The DIGEST-MD5 credentials \
- provided by the client did not contain the required "username" token
-MILD_ERR_SASLDIGESTMD5_NO_NONCE_IN_RESPONSE_219=The DIGEST-MD5 credentials \
- provided by the client did not contain the required "nonce" token
-MILD_ERR_SASLDIGESTMD5_NO_CNONCE_IN_RESPONSE_220=The DIGEST-MD5 credentials \
- provided by the client did not contain the required "cnonce" token
-MILD_ERR_SASLDIGESTMD5_NO_NONCE_COUNT_IN_RESPONSE_221=The DIGEST-MD5 \
- credentials provided by the client did not contain the required "nc" token
-MILD_ERR_SASLDIGESTMD5_NO_DIGEST_IN_RESPONSE_223=The DIGEST-MD5 credentials \
- provided by the client did not contain the required "response" token
-MILD_ERR_SASLDIGESTMD5_CANNOT_DECODE_USERNAME_AS_DN_224=An error occurred \
- while attempting to decode the SASL DIGEST-MD5 username "%s" because it \
- appeared to contain a DN but DN decoding failed:  %s
-MILD_ERR_SASLDIGESTMD5_USERNAME_IS_NULL_DN_225=The username in the SASL \
- DIGEST-MD5 bind request appears to be an empty DN.  This is not allowed
-INFO_SASLDIGESTMD5_CANNOT_LOCK_ENTRY_226=The Directory Server was unable to \
- obtain a read lock on user entry %s in order to retrieve that entry
-MILD_ERR_SASLDIGESTMD5_CANNOT_GET_ENTRY_BY_DN_227=An error occurred while \
- attempting to retrieve user entry %s as specified in the DN-based username of \
- a SASL DIGEST-MD5 bind request:  %s
-MILD_ERR_SASLDIGESTMD5_ZERO_LENGTH_USERNAME_228=The username contained in the \
- SASL DIGEST-MD5 bind request had a length of zero characters, which is not \
- allowed.  DIGEST-MD5 authentication does not allow an empty string for use as \
- the username
-MILD_ERR_SASLDIGESTMD5_CANNOT_PERFORM_INTERNAL_SEARCH_229=An error occurred \
- while trying to perform an internal search to retrieve the user entry \
- associated with the SASL DIGEST-MD5 username %s.  The result of that search \
- was %s with a message of %s
-MILD_ERR_SASLDIGESTMD5_MULTIPLE_MATCHING_ENTRIES_230=The internal search \
- attempting to resolve SASL DIGEST-MD5 username %s matched multiple entries. \
- Authentication cannot succeed unless the username is mapped to exactly one \
- user entry
-MILD_ERR_SASLDIGESTMD5_NO_MATCHING_ENTRIES_231=The server was not able to \
- find any user entries for the provided username of %s
-MILD_ERR_SASLDIGESTMD5_NO_PW_ATTR_232=The SASL DIGEST-MD5 authentication \
- failed because the mapped user entry did not contain any values for the %s \
- attribute
-MILD_ERR_SASLDIGESTMD5_UNKNOWN_STORAGE_SCHEME_233=A password in the target \
- user entry %s could not be processed via SASL DIGEST-MD5 because that \
- password has an unknown storage scheme of %s
-MILD_ERR_SASLDIGESTMD5_CANNOT_GET_CLEAR_PASSWORD_234=An error occurred while \
- attempting to obtain the clear-text password for user %s from the value with \
- storage scheme %s:  %s
-MILD_ERR_SASLDIGESTMD5_INVALID_CREDENTIALS_235=The DIGEST-MD5 credentials \
- provided by the client are not appropriate for any password in the associated \
- user account
-MILD_ERR_SASLDIGESTMD5_NO_REVERSIBLE_PASSWORDS_236=SASL DIGEST-MD5 \
- authentication is not possible for user %s because none of the passwords in \
- the user entry are stored in a reversible form
-SEVERE_WARN_SASLDIGESTMD5_CANNOT_GENERATE_RESPONSE_DIGEST_237=An error \
- occurred while attempting to generate a server-side digest to compare with \
- the client response:  %s
-SEVERE_ERR_SASLDIGESTMD5_CANNOT_GENERATE_RESPONSE_AUTH_DIGEST_238=An error \
- occurred while trying to generate the response auth digest to include in the \
- server SASL credentials:  %s
-MILD_ERR_SASLDIGESTMD5_INVALID_CLOSING_QUOTE_POS_239=The DIGEST-MD5 response \
- challenge could not be parsed because it had an invalid quotation mark at \
- position %d
-INFO_SASLDIGESTMD5_UPDATED_USERNAME_ATTR_240=Attribute \
- ds-cfg-user-name-attribute in configuration entry %s has been updated.  The \
- %s attribute will now be used when looking up user entries based on their \
- username
-INFO_SASLDIGESTMD5_UPDATED_USER_BASE_DN_241=Attribute ds-cfg-user-base-dn in \
- configuration entry %s has been updated.  The DN %s will now be used as the \
- search base when looking up user entries based on their username
-INFO_SASLDIGESTMD5_UPDATED_NEW_REALM_242=Attribute ds-cfg-realm in \
- configuration entry %s has been updated.  The realm "%s" will now be \
- advertised by the server in the challenge response
-INFO_SASLDIGESTMD5_UPDATED_NO_REALM_243=Attribute ds-cfg-realm in \
- configuration entry %s has been updated.  The realm(s) advertised by the \
- server in the challenge response will be the DNs of the server suffixes
-INFO_SASLGSSAPI_DESCRIPTION_USERNAME_ATTRIBUTE_244=Name of the \
- attribute that will be used to identify user entries based on the username \
- provided during SASL GSSAPI authentication.  This must specify the name of a \
- valid attribute type defined in the server schema.  Changes to this \
- configuration attribute will take effect immediately
-SEVERE_ERR_SASLGSSAPI_CANNOT_GET_USERNAME_ATTR_245=An unexpected error \
- occurred while attempting to determine the value of the \
- ds-cfg-user-name-attribute attribute in configuration entry %s:  %s
-SEVERE_ERR_SASLGSSAPI_UNKNOWN_USERNAME_ATTR_246=The attribute %s referenced \
- in configuration attribute ds-cfg-user-name-attribute in configuration entry \
- %s does not exist in the Directory Server schema.  The attribute that is to \
- be used for username lookups during SASL GSSAPI authentication must be \
- defined in the server schema
-INFO_SASLGSSAPI_DESCRIPTION_USER_BASE_DN_247=Base DN that \
- should be used when searching for entries based on the username provided \
- during SASL GSSAPI authentication.  Changes to this configuration attribute \
- will take effect immediately
-SEVERE_ERR_SASLGSSAPI_CANNOT_GET_USER_BASE_DN_248=An unexpected error \
- occurred while attempting to determine the value of the ds-cfg-user-base-dn \
- attribute in configuration entry %s:  %s
-INFO_SASLGSSAPI_DESCRIPTION_SERVER_FQDN_249=Fully-qualified \
- domain name that should be used for the server during SASL GSSAPI \
- authentication.  Changes to this configuration attribute will take effect \
- immediately
-SEVERE_ERR_SASLGSSAPI_CANNOT_GET_SERVER_FQDN_250=An unexpected error occurred \
+SEVERE_ERR_SASL_CANNOT_GET_SERVER_FQDN_194=An unexpected error occurred \
  while attempting to determine the value of the ds-cfg-server-fqdn attribute \
  in configuration entry %s:  %s
-INFO_SASLGSSAPI_UPDATED_USERNAME_ATTR_251=Attribute \
- ds-cfg-user-name-attribute in configuration entry %s has been updated.  The \
- %s attribute will now be used when looking up user entries based on their \
- username
-INFO_SASLGSSAPI_UPDATED_USER_BASE_DN_252=Attribute ds-cfg-user-base-dn in \
- configuration entry %s has been updated.  The DN %s will now be used as the \
- search base when looking up user entries based on their username
-INFO_SASLGSSAPI_UPDATED_NEW_SERVER_FQDN_253=Attribute ds-cfg-server-fqdn in \
- configuration entry %s has been updated.  The value "%s" will now be used as \
- the fully-qualified name of the Directory Server for GSSAPI authentication
-INFO_SASLGSSAPI_UPDATED_NO_SERVER_FQDN_254=Attribute ds-cfg-server-fqdn in \
- configuration entry %s has been updated.  The Directory Server will attempt \
- to determine its own FQDN for use in GSSAPI authentication
-INFO_SASLGSSAPI_UNEXPECTED_CALLBACK_255=An unexpected callback was provided \
- for the SASL server for use during GSSAPI authentication:  %s
-INFO_SASLGSSAPI_DESCRIPTION_KDC_ADDRESS_256=Address of the KDC \
- that should be used during SASL GSSAPI authentication.  If this is not \
- specified, then an attempt will be made to obtain it from the system-wide \
- Kerberos configuration.  Changes to this configuration attribute will take \
- effect immediately for subsequent GSSAPI bind attempts
-MILD_ERR_SASLGSSAPI_CANNOT_GET_KDC_ADDRESS_257=An unexpected error occurred \
- while attempting to determine the value of the ds-cfg-kdc-address attribute \
- in configuration entry %s:  %s
-INFO_SASLGSSAPI_DESCRIPTION_REALM_258=Default realm that should \
- be used during SASL GSSAPI authentication.  If this is not specified, then an \
- attempt will be made to obtain it from the system-wide Kerberos \
- configuration.  Changes to this configuration attribute will take effect \
- immediately for subsequent GSSAPI bind attempts
-MILD_ERR_SASLGSSAPI_CANNOT_GET_REALM_259=An unexpected error occurred while \
- attempting to determine the value of the ds-cfg-realm attribute in \
- configuration entry %s:  %s
-MILD_ERR_SASLGSSAPI_NO_CLIENT_CONNECTION_260=No client connection was \
- available for use in processing the GSSAPI bind request
-MILD_ERR_SASLGSSAPI_CANNOT_CREATE_SASL_SERVER_261=An error occurred while \
- attempting to create the SASL server instance to process the GSSAPI bind \
- request:  %s
-MILD_ERR_SASLGSSAPI_CANNOT_EVALUATE_RESPONSE_262=An error occurred while \
- attempting to evaluate the challenge response provided by the client in the \
- GSSAPI bind request:  %s
-MILD_ERR_SASLGSSAPI_NO_AUTHZ_ID_263=The GSSAPI authentication process appears \
- to have completed but no authorization ID is available for mapping to a \
- directory user
-MILD_ERR_SASLGSSAPI_CANNOT_PERFORM_INTERNAL_SEARCH_264=An error occurred \
- while attempting to perform an internal search to map the GSSAPI \
- authorization ID %s to a Directory Server user (result code %d, error message \
- "%s")
-MILD_ERR_SASLGSSAPI_MULTIPLE_MATCHING_ENTRIES_265=The GSSAPI authorization ID \
- %s appears to have multiple matches in the Directory Server
-MILD_ERR_SASLGSSAPI_CANNOT_MAP_AUTHZID_266=The GSSAPI authorization ID %s \
- could not be mapped to any user in the Directory Server
-INFO_SASLGSSAPI_UPDATED_KDC_267=Attribute ds-cfg-kdc-address in configuration \
- entry %s has been updated.  The value "%s" will now be used as the address of \
- the KDC for GSSAPI authentication
-INFO_SASLGSSAPI_UNSET_KDC_268=Attribute ds-cfg-kdc-address in configuration \
- entry %s has been un-set as a system property.  Any further GSSAPI \
- authentication attempts will rely on the Kerberos configuration in the \
- underlying operating system to determine the KDC address
-INFO_SASLGSSAPI_UPDATED_REALM_269=Attribute ds-cfg-realm in configuration \
- entry %s has been updated.  The value "%s" will now be used as the default \
- realm for GSSAPI authentication
-INFO_SASLGSSAPI_UNSET_REALM_270=Attribute ds-cfg-realm in configuration entry \
- %s has been un-set as a system property.  Any further GSSAPI authentication \
- attempts will rely on the Kerberos configuration in the underlying operating \
- system to determine the default realm
-MILD_ERR_SASLGSSAPI_CANNOT_CREATE_LOGIN_CONTEXT_271=An error occurred while \
- attempting to create the JAAS login context for GSSAPI authentication:  %s
-MILD_ERR_SASLGSSAPI_CANNOT_AUTHENTICATE_SERVER_272=An error occurred while \
- attempting to perform server-side Kerberos authentication to support a GSSAPI \
- bind operation:  %s
-INFO_SASLGSSAPI_DESCRIPTION_KEYTAB_FILE_273=Path to the keytab \
- file containing the secret key for the Kerberos principal to use when \
- processing GSSAPI authentication.  If this is not specified, then the \
- system-wide default keytab file will be used.  Changes to this configuration \
- attribute will not take effect until the GSSAPI SASL mechanism handler is \
- disabled and re-enabled or the Directory Server is restarted
-MILD_ERR_SASLGSSAPI_CANNOT_GET_KEYTAB_FILE_274=An unexpected error occurred \
- while attempting to determine the value of the ds-cfg-keytab attribute in \
- configuration entry %s:  %s
-SEVERE_ERR_SASLGSSAPI_CANNOT_CREATE_JAAS_CONFIG_275=An error occurred while \
+ SEVERE_ERR_SASL_CONTEXT_CREATE_ERROR_195=An unexpected error occurred while \
+ trying to create an %s context: %s
+MILD_ERR_SASL_CANNOT_DECODE_USERNAME_AS_DN_196=An error occurred \
+ while attempting to decode the SASL %s username "%s" because it \
+ appeared to contain a DN but DN decoding failed:  %s
+MILD_ERR_SASL_USERNAME_IS_NULL_DN_197=The username in the SASL \
+ %s bind request appears to be an empty DN.  This is not allowed
+INFO_SASL_CANNOT_LOCK_ENTRY_198=The Directory Server was unable to \
+ obtain a read lock on user entry %s in order to retrieve that entry
+MILD_ERR_SASL_CANNOT_GET_ENTRY_BY_DN_199=An error occurred while \
+ attempting to retrieve user entry %s as specified in the DN-based username of \
+ a SASL %s bind request:  %s
+MILD_ERR_SASL_ZERO_LENGTH_USERNAME_200=The username contained in the \
+ SASL %s bind request had a length of zero characters, which is not \
+ allowed.  %s authentication does not allow an empty string for use as \
+ the username
+MILD_ERR_SASL_NO_MATCHING_ENTRIES_201=The server was not able to \
+ find any user entries for the provided username of %s
+MILD_ERR_SASL_AUTHZID_INVALID_DN_202=The provided authorization ID \
+ %s contained an invalid DN:  %s
+ MILD_ERR_SASL_AUTHZID_NO_SUCH_ENTRY_203=The entry %s specified as \
+ the authorization identity does not exist
+MILD_ERR_SASL_AUTHZID_CANNOT_GET_ENTRY_204=The entry %s specified as \
+ the authorization identity could not be retrieved:  %s
+MILD_ERR_SASL_AUTHZID_NO_MAPPED_ENTRY_205=The server was unable to \
+ find any entry corresponding to authorization ID %s
+MILD_ERR_SASL_CANNOT_MAP_AUTHZID_206=An error occurred while \
+ attempting to map authorization ID %s to a user entry:  %s
+MILD_ERR_SASL_CANNOT_GET_REVERSIBLE_PASSWORDS_207=An error occurred \
+ while attempting to retrieve the clear-text password(s) for user %s in order \
+ to perform SASL %s authentication:  %s
+MILD_ERR_SASL_NO_REVERSIBLE_PASSWORDS_208=SASL %s \
+ authentication is not possible for user %s because none of the passwords in \
+ the user entry are stored in a reversible form
+SEVERE_ERR_SASL_PROTOCOL_ERROR_209=SASL %s protocol error: %s
+MILD_ERR_SASL_AUTHZID_INSUFFICIENT_PRIVILEGES_210=The authenticating \
+ user %s does not have sufficient privileges to assume a different \
+ authorization identity
+MILD_ERR_SASL_AUTHZID_INSUFFICIENT_ACCESS_211=The authenticating \
+ user %s does not have sufficient access to assume a different \
+ authorization identity
+MILD_ERR_SASL_AUTHENTRY_NO_MAPPED_ENTRY_212=The server was unable to \
+ find any entry corresponding to authentication ID %s
+SEVERE_ERR_SASLGSSAPI_KDC_REALM_NOT_DEFINED_213=The server was unable to \
+ because both the ds-cfg-kdc-address and ds-cfg-realm attributes must be \
+ defined or neither defined
+MILD_ERR_SASL_CANNOT_MAP_AUTHENTRY_214=An error occurred while \
+ attempting to map authorization ID %s to a user entry:  %s
+SEVERE_ERR_SASLGSSAPI_CANNOT_CREATE_JAAS_CONFIG_215=An error occurred while \
  attempting to write a temporary JAAS configuration file for use during GSSAPI \
  processing:  %s
-SEVERE_ERR_SASLGSSAPI_DIFFERENT_AUTHID_AND_AUTHZID_276=The authentication ID \
- %s was not equal to the authorization ID %s.  This is not supported for \
- GSSAPI authentication
+ SEVERE_ERR_SASLGSSAPI_CANNOT_CREATE_LOGIN_CONTEXT_216=An error occurred while \
+ attempting to create the JAAS login context for GSSAPI authentication:  %s
+ MILD_ERR_SASLGSSAPI_NO_CLIENT_CONNECTION_217=No client connection was \
+ available for use in processing the GSSAPI bind request
+ INFO_GSSAPI_PRINCIPAL_NAME_218=GSSAPI mechanism using a principal name of: %s
+ INFO_GSSAPI_SERVER_FQDN_219=GSSAPI SASL mechanism using a server fully \
+ qualified domain name of: %s
+ INFO_DIGEST_MD5_REALM_220=DIGEST-MD5 SASL mechanism using a realm of: %s
+ INFO_DIGEST_MD5_SERVER_FQDN_221=DIGEST-MD5 SASL mechanism using a server \
+ fully qualified domain name of: %s
 SEVERE_ERR_EXTOP_WHOAMI_PROXYAUTH_INSUFFICIENT_PRIVILEGES_277=You do not have \
  sufficient privileges to use the proxied authorization control
 INFO_EXACTMAP_DESCRIPTION_MATCH_ATTR_298=Name or OID of the \
@@ -1123,9 +928,11 @@
  from configuration entry %s:  %s
 NOTICE_ERRORLOG_ACCTNOTHANDLER_NOTIFICATION_375=Account-Status-Notification \
  type='%s' userdn='%s' id=%d msg='%s'
-MILD_ERR_SASLDIGESTMD5_CANNOT_GET_REVERSIBLE_PASSWORDS_376=An error occurred \
- while attempting to retrieve the clear-text password(s) for user %s in order \
- to perform SASL DIGEST-MD5 authentication:  %s
+
+
+
+
+
 MILD_ERR_SASLCRAMMD5_CANNOT_GET_REVERSIBLE_PASSWORDS_377=An error occurred \
  while attempting to retrieve the clear-text password(s) for user %s in order \
  to perform SASL CRAM-MD5 authentication:  %s
@@ -1169,13 +976,13 @@
  perform an internal modification to update the group:  %s
 MILD_ERR_EXTOP_PASSMOD_INSUFFICIENT_PRIVILEGES_392=You do not have sufficient \
  privileges to perform password reset operations
+
+
+
 MILD_ERR_SASLDIGESTMD5_EMPTY_AUTHZID_393=The provided authorization ID was \
  empty, which is not allowed for DIGEST-MD5 authentication
 MILD_ERR_SASLDIGESTMD5_AUTHZID_INVALID_DN_394=The provided authorization ID \
  %s contained an invalid DN:  %s
-MILD_ERR_SASLDIGESTMD5_AUTHZID_INSUFFICIENT_PRIVILEGES_395=The authenticating \
- user %s does not have sufficient privileges to assume a different \
- authorization identity
 MILD_ERR_SASLDIGESTMD5_AUTHZID_NO_SUCH_ENTRY_396=The entry %s specified as \
  the authorization identity does not exist
 MILD_ERR_SASLDIGESTMD5_AUTHZID_CANNOT_GET_ENTRY_397=The entry %s specified as \
@@ -1184,6 +991,14 @@
  find any entry corresponding to authorization ID %s
 MILD_ERR_SASLDIGESTMD5_CANNOT_MAP_AUTHZID_399=An error occurred while \
  attempting to map authorization ID %s to a user entry:  %s
+
+
+
+
+
+
+
+
 MILD_ERR_SASLPLAIN_AUTHZID_INVALID_DN_400=The provided authorization ID %s \
  contained an invalid DN:  %s
 MILD_ERR_SASLPLAIN_AUTHZID_INSUFFICIENT_PRIVILEGES_401=The authenticating \
@@ -1625,6 +1440,6 @@
 SEVERE_ERR_SDTUACM_ATTR_UNINDEXED_569=The subject DN to user attribute \
  certificate mapper defined in configuration entry %s references attribute \
  type %s which is does not have an equality index defined in backend %s
-INFO_LOG_EXTENSION_INFORMATION_570=Loaded extension from file '%s' (build %s, \
+SEVERE_ERR_SASLDIGESTMD5_PROTOCOL_ERROR_570=SASL DIGEST MD5 protocol error: %s
+INFO_LOG_EXTENSION_INFORMATION_571=Loaded extension from file '%s' (build %s, \
  revision %s)
-
diff --git a/opends/src/server/org/opends/server/api/AccessControlHandler.java b/opends/src/server/org/opends/server/api/AccessControlHandler.java
index b1a1d63..d0cb74e 100644
--- a/opends/src/server/org/opends/server/api/AccessControlHandler.java
+++ b/opends/src/server/org/opends/server/api/AccessControlHandler.java
@@ -331,5 +331,22 @@
                              SearchOperation searchOperation,
                              SearchResultReference searchReference);
 
+  /**
+   * Indicates if the specified proxy user entry can proxy, or act on
+   * the behalf of the specified proxied user entry. The operation
+   * parameter is used in the evaluation.
+   *
+   * @param proxyUser The entry to use as the proxy user.
+   * @param proxiedUser The entry to be proxied by the proxy user.
+   * @param operation The operation to use in the evaluation.
+   *
+   * @return  {@code true} if the access control configuration allows
+   *          the proxy user to proxy the proxied user, or
+   *          {@code false} if not.
+   */
+  public abstract boolean mayProxy(Entry proxyUser,
+                                   Entry proxiedUser,
+                                   Operation operation);
+
 }
 
diff --git a/opends/src/server/org/opends/server/api/ClientConnection.java b/opends/src/server/org/opends/server/api/ClientConnection.java
index a9f8b5a..c56d565 100644
--- a/opends/src/server/org/opends/server/api/ClientConnection.java
+++ b/opends/src/server/org/opends/server/api/ClientConnection.java
@@ -306,7 +306,7 @@
    * @return  The connection handler that accepted this client
    *          connection.
    */
-  public abstract ConnectionHandler getConnectionHandler();
+  public abstract ConnectionHandler<?> getConnectionHandler();
 
 
 
@@ -1113,6 +1113,26 @@
   }
 
 
+  /**
+   * Indicate whether the specified authorization entry parameter
+   * has the specified privilege. The method can be used to perform
+   * a "what-if" scenario.
+   *
+ * @param authorizationEntry The authentication entry to use.
+ * @param privilege The privilege to check for.
+   *
+   * @return  {@code true} if the authentication entry has the
+   *          specified privilege, or {@code false} if not.
+   */
+  public static boolean hasPrivilege(Entry authorizationEntry,
+                                   Privilege privilege) {
+      boolean isRoot =
+          DirectoryServer.isRootDN(authorizationEntry.getDN());
+      return getPrivileges(authorizationEntry,
+              isRoot).contains(privilege) ||
+              DirectoryServer.isDisabled(privilege);
+  }
+
 
   /**
    * Indicates whether the authenticated client has the specified
@@ -1298,7 +1318,7 @@
    *
    * @return  A set of the privileges that should be assigned.
    */
-  private HashSet<Privilege> getPrivileges(Entry entry,
+  private static HashSet<Privilege> getPrivileges(Entry entry,
                                            boolean isRoot)
   {
     if (entry == null)
@@ -1579,7 +1599,7 @@
    * @throws  DirectoryException  If a problem occurs while attempting
    *                             to make the determination.
    */
-  public boolean isMemberOf(Group group, Operation operation)
+  public boolean isMemberOf(Group<?> group, Operation operation)
          throws DirectoryException
   {
     if (operation == null)
@@ -1668,7 +1688,7 @@
    * Retrieves the DN of the key manager provider that should be used
    * for operations requiring access to a key manager.  The default
    * implementation returns {@code null} to indicate that no key
-   * manager provider is avaialble, but subclasses should override
+   * manager provider is available, but subclasses should override
    * this method to return a valid DN if they perform operations which
    * may need access to a key manager.
    *
diff --git a/opends/src/server/org/opends/server/api/ConnectionSecurityProvider.java b/opends/src/server/org/opends/server/api/ConnectionSecurityProvider.java
index 9ad8c1b..a2a510d 100644
--- a/opends/src/server/org/opends/server/api/ConnectionSecurityProvider.java
+++ b/opends/src/server/org/opends/server/api/ConnectionSecurityProvider.java
@@ -98,6 +98,16 @@
    */
   public abstract boolean isSecure();
 
+ /**
+  * Indicates whether the security provider is active or not. Some
+  * security providers (DIGEST-MD5, GSSAPI) perform
+  * confidentiality/integrity processing of messages and require
+  * several handshakes to setup.
+  *
+  * @return  {@code true} if the security provider is active, or,
+  *          {@code false} if not.
+  */
+  public abstract boolean isActive();
 
 
   /**
diff --git a/opends/src/server/org/opends/server/authorization/dseecompat/Aci.java b/opends/src/server/org/opends/server/authorization/dseecompat/Aci.java
index 3fafa70..96df8da 100644
--- a/opends/src/server/org/opends/server/authorization/dseecompat/Aci.java
+++ b/opends/src/server/org/opends/server/authorization/dseecompat/Aci.java
@@ -538,7 +538,7 @@
    * @param expr A string representing the OID expression.
    * @param msg  A message to be used if there is an exception.
    *
-   * @return  Return a hash set of verfied OID strings parsed from the OID
+   * @return  Return a hash set of verified OID strings parsed from the OID
    *          expression.
    *
    * @throws AciException If the specified expression string is invalid.
diff --git a/opends/src/server/org/opends/server/authorization/dseecompat/AciContainer.java b/opends/src/server/org/opends/server/authorization/dseecompat/AciContainer.java
index 7e301bc..b661486 100644
--- a/opends/src/server/org/opends/server/authorization/dseecompat/AciContainer.java
+++ b/opends/src/server/org/opends/server/authorization/dseecompat/AciContainer.java
@@ -93,9 +93,9 @@
     private boolean isAddOp=false;
 
     /*
-     * The rights to use in the evaluation of the LDAP operation.
+     * The right mask to use in the evaluation of the LDAP operation.
      */
-    private int rights;
+    private int rightsMask;
 
     /*
      * The entry being evaluated (resource entry).
@@ -246,8 +246,15 @@
     */
     private String extOpOID;
 
+    /*
+     * AuthenticationInfo class to use.
+     */
+    private AuthenticationInfo authInfo;
+
   /**
-     * This constructor is used by all currently supported LDAP operations.
+     * This constructor is used by all currently supported LDAP operations
+     * except the generic access control check that can be used by
+     * plugins.
      *
      * @param operation The Operation object being evaluated and target
      * matching.
@@ -262,6 +269,7 @@
       this.clientConnection=operation.getClientConnection();
       if(operation instanceof AddOperationBasis)
           this.isAddOp=true;
+      this.authInfo = clientConnection.getAuthenticationInfo();
 
       //If the proxied authorization control was processed, then the operation
       //will contain an attachment containing the original authorization entry.
@@ -313,9 +321,29 @@
       //if an access proxy check was performed.
       this.saveAuthorizationEntry=this.authorizationEntry;
       this.saveResourceEntry=this.resourceEntry;
-      this.rights = rights;
+      this.rightsMask = rights;
     }
 
+    /**
+     * This constructor is used by the generic access control check.
+     *
+     * @param operation The operation to use in the access evaluation.
+     * @param e The entry to check access for.
+     * @param authInfo The authentication information to use in the evaluation.
+     * @param rights The rights to check access of.
+     */
+    protected AciContainer(Operation operation, Entry e,
+                            AuthenticationInfo authInfo,
+                            int rights) {
+        this.resourceEntry=e;
+        this.operation=operation;
+        this.clientConnection=operation.getClientConnection();
+        this.authInfo = authInfo;
+        this.authorizationEntry = authInfo.getAuthorizationEntry();
+        this.saveAuthorizationEntry=this.authorizationEntry;
+        this.saveResourceEntry=this.resourceEntry;
+        this.rightsMask = rights;
+    }
   /**
    * Returns true if an entry has already been processed by an access proxy
    * check.
@@ -655,7 +683,7 @@
     * {@inheritDoc}
     */
     public boolean isAnonymousUser() {
-        return !clientConnection.getAuthenticationInfo().isAuthenticated();
+        return !authInfo.isAuthenticated();
     }
 
    /**
@@ -689,21 +717,21 @@
     * {@inheritDoc}
     */
     public boolean hasRights(int rights) {
-       return (this.rights & rights) != 0;
+       return (this.rightsMask & rights) != 0;
     }
 
    /**
     * {@inheritDoc}
     */
     public int getRights() {
-        return this.rights;
+        return this.rightsMask;
     }
 
    /**
     * {@inheritDoc}
     */
     public void setRights(int rights) {
-         this.rights=rights;
+         this.rightsMask=rights;
     }
 
    /**
@@ -791,7 +819,6 @@
         /*
          * Some kind of authentication is required.
          */
-        AuthenticationInfo authInfo=clientConnection.getAuthenticationInfo();
         if(authInfo.isAuthenticated()) {
           if(authMethod==EnumAuthMethod.AUTHMETHOD_SIMPLE) {
             if(authInfo.hasAuthenticationType(AuthenticationType.SIMPLE)) {
@@ -833,7 +860,7 @@
     /**
      * {@inheritDoc}
      */
-    public boolean isMemberOf(Group group) {
+    public boolean isMemberOf(Group<?> group) {
         boolean ret;
         try {
             if(useAuthzid) {
@@ -884,7 +911,7 @@
    * {@inheritDoc}
    */
   public  void setEvalUserAttributes(int v) {
-    if(operation instanceof SearchOperation && (rights == ACI_READ)) {
+    if(operation instanceof SearchOperation && (rightsMask == ACI_READ)) {
       if(v == ACI_FOUND_USER_ATTR_RULE) {
         evalAllAttributes |= ACI_FOUND_USER_ATTR_RULE;
         evalAllAttributes &= ~ACI_USER_ATTR_STAR_MATCHED;
@@ -897,7 +924,7 @@
    * {@inheritDoc}
    */
   public  void setEvalOpAttributes(int v) {
-    if(operation instanceof SearchOperation && (rights == ACI_READ)) {
+    if(operation instanceof SearchOperation && (rightsMask == ACI_READ)) {
       if(v == ACI_FOUND_OP_ATTR_RULE) {
         evalAllAttributes |= ACI_FOUND_OP_ATTR_RULE;
         evalAllAttributes &= ~ACI_OP_ATTR_PLUS_MATCHED;
diff --git a/opends/src/server/org/opends/server/authorization/dseecompat/AciEvalContext.java b/opends/src/server/org/opends/server/authorization/dseecompat/AciEvalContext.java
index 9eee358..e9b437c 100644
--- a/opends/src/server/org/opends/server/authorization/dseecompat/AciEvalContext.java
+++ b/opends/src/server/org/opends/server/authorization/dseecompat/AciEvalContext.java
@@ -146,7 +146,7 @@
      * @return True if the authorization DN of the operation is a
      * member of the specified group.
      */
-    public boolean isMemberOf(Group group);
+    public boolean isMemberOf(Group<?> group);
 
   /**
    * Returns true if the hashtable of ACIs that matched the targattrfilters
diff --git a/opends/src/server/org/opends/server/authorization/dseecompat/AciHandler.java b/opends/src/server/org/opends/server/authorization/dseecompat/AciHandler.java
index f2f197c..d505d25 100644
--- a/opends/src/server/org/opends/server/authorization/dseecompat/AciHandler.java
+++ b/opends/src/server/org/opends/server/authorization/dseecompat/AciHandler.java
@@ -28,8 +28,6 @@
 package org.opends.server.authorization.dseecompat;
 import org.opends.messages.Message;
 
-
-
 import static org.opends.server.authorization.dseecompat.Aci.*;
 import static org.opends.server.config.ConfigConstants.ATTR_AUTHZ_GLOBAL_ACI;
 import static org.opends.server.loggers.ErrorLogger.logError;
@@ -39,12 +37,11 @@
 import static org.opends.server.schema.SchemaConstants.SYNTAX_DN_OID;
 import static org.opends.server.util.ServerConstants.*;
 import static org.opends.server.util.StaticUtils.toLowerCase;
-
 import java.util.*;
 import java.util.concurrent.locks.Lock;
-
 import org.opends.server.admin.std.server.DseeCompatAccessControlHandlerCfg;
 import org.opends.server.api.AccessControlHandler;
+import org.opends.server.api.ClientConnection;
 import org.opends.server.config.ConfigException;
 import org.opends.server.core.*;
 import org.opends.server.loggers.debug.DebugTracer;
@@ -535,6 +532,17 @@
     }
 
     /**
+     * Check to see if the specified entry has the specified privilege.
+     *
+     * @param e The entry to check privileges on.
+     * @return  {@code true} if the entry has the
+     *          specified privilege, or {@code false} if not.
+     */
+    private boolean skipAccessCheck(Entry e) {
+        return ClientConnection.hasPrivilege(e, Privilege.BYPASS_ACL);
+    }
+
+    /**
      * Check access using the specified container. This container will have all
      * of the information to gather applicable ACIs and perform evaluation on
      * them.
@@ -1242,6 +1250,26 @@
    * {@inheritDoc}
    */
   @Override
+  public boolean
+  mayProxy(Entry proxyUser, Entry proxiedUser, Operation op) {
+      boolean ret;
+      if(!(ret=skipAccessCheck(proxyUser))) {
+          AuthenticationInfo authInfo =
+              new AuthenticationInfo(proxyUser,
+                     DirectoryServer.isRootDN(proxyUser.getDN()));
+          AciLDAPOperationContainer operationContainer =
+              new AciLDAPOperationContainer(op, proxiedUser,
+                                            authInfo, ACI_PROXY);
+          ret=accessAllowedEntry(operationContainer);
+      }
+      return ret;
+  }
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
   public boolean isAllowed(LocalBackendBindOperation bindOperation) {
       //Not planned to be implemented.
       return true;
diff --git a/opends/src/server/org/opends/server/authorization/dseecompat/AciLDAPOperationContainer.java b/opends/src/server/org/opends/server/authorization/dseecompat/AciLDAPOperationContainer.java
index 149ded5..8354477 100644
--- a/opends/src/server/org/opends/server/authorization/dseecompat/AciLDAPOperationContainer.java
+++ b/opends/src/server/org/opends/server/authorization/dseecompat/AciLDAPOperationContainer.java
@@ -28,7 +28,6 @@
 package org.opends.server.authorization.dseecompat;
 
 import java.util.List;
-
 import org.opends.server.core.*;
 import org.opends.server.types.*;
 import org.opends.server.workflowelement.localbackend.*;
@@ -62,6 +61,23 @@
         super(operation, rights, operation.getEntryToCompare());
     }
 
+
+    /**
+     * Constructor interface for evaluation general purpose Operation, entry and
+     * rights..
+     *
+     * @param operation The operation to use in the evaluation.
+     * @param e The entry for evaluation.
+     * @param authInfo The authentication information to use in the evaluation.
+     * @param rights The rights of the operation.
+     */
+    public AciLDAPOperationContainer(Operation operation, Entry e,
+                                     AuthenticationInfo authInfo,
+                                     int rights) {
+      super(operation, e, authInfo, rights);
+    }
+
+
     /**
      * Constructor interface for evaluation of a control.
      *
@@ -126,7 +142,7 @@
      * Constructor interface for the modify DN operation.
      * @param operation  The modify DN operation.
      * @param rights  The rights of the modify DN operation.
-     * @param entry  The entry to evalauted for this modify DN.
+     * @param entry  The entry to evaluated for this modify DN.
      */
     public AciLDAPOperationContainer(LocalBackendModifyDNOperation operation,
                                      int rights,
diff --git a/opends/src/server/org/opends/server/core/AccessControlConfigManager.java b/opends/src/server/org/opends/server/core/AccessControlConfigManager.java
index fc9e793..1cd92dc 100644
--- a/opends/src/server/org/opends/server/core/AccessControlConfigManager.java
+++ b/opends/src/server/org/opends/server/core/AccessControlConfigManager.java
@@ -143,7 +143,7 @@
    *
    * @return   The active access control handler (never {@code null}).
    */
-  public AccessControlHandler getAccessControlHandler()
+  public AccessControlHandler<?> getAccessControlHandler()
   {
     return accessControlHandler.get();
   }
diff --git a/opends/src/server/org/opends/server/core/DefaultAccessControlHandler.java b/opends/src/server/org/opends/server/core/DefaultAccessControlHandler.java
index 6655a60..c634bd2 100644
--- a/opends/src/server/org/opends/server/core/DefaultAccessControlHandler.java
+++ b/opends/src/server/org/opends/server/core/DefaultAccessControlHandler.java
@@ -222,5 +222,15 @@
     return true;
   }
 
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public  boolean mayProxy(Entry proxyUser, Entry proxiedUser,
+                           Operation operation) {
+      return true;
+  }
 }
 
diff --git a/opends/src/server/org/opends/server/extensions/DigestMD5SASLMechanismHandler.java b/opends/src/server/org/opends/server/extensions/DigestMD5SASLMechanismHandler.java
index c1e1b9e..56106d5 100644
--- a/opends/src/server/org/opends/server/extensions/DigestMD5SASLMechanismHandler.java
+++ b/opends/src/server/org/opends/server/extensions/DigestMD5SASLMechanismHandler.java
@@ -26,95 +26,65 @@
  */
 package org.opends.server.extensions;
 
-
-
-import java.io.UnsupportedEncodingException;
-import java.security.MessageDigest;
-import java.security.SecureRandom;
-import java.text.ParseException;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
 import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Iterator;
+import java.util.HashMap;
 import java.util.List;
-import java.util.Map;
-import java.util.concurrent.locks.Lock;
-
+import javax.security.sasl.*;
 import org.opends.messages.Message;
 import org.opends.server.admin.server.ConfigurationChangeListener;
+import org.opends.server.admin.std.meta.DigestMD5SASLMechanismHandlerCfgDefn.*;
 import org.opends.server.admin.std.server.DigestMD5SASLMechanismHandlerCfg;
 import org.opends.server.admin.std.server.SASLMechanismHandlerCfg;
-import org.opends.server.api.Backend;
 import org.opends.server.api.ClientConnection;
 import org.opends.server.api.IdentityMapper;
 import org.opends.server.api.SASLMechanismHandler;
 import org.opends.server.config.ConfigException;
 import org.opends.server.core.BindOperation;
 import org.opends.server.core.DirectoryServer;
-import org.opends.server.core.PasswordPolicyState;
-import org.opends.server.loggers.debug.DebugTracer;
-import org.opends.server.protocols.asn1.ASN1OctetString;
-import org.opends.server.protocols.internal.InternalClientConnection;
-import org.opends.server.types.AuthenticationInfo;
-import org.opends.server.types.ByteString;
+import org.opends.server.loggers.debug.*;
 import org.opends.server.types.ConfigChangeResult;
-import org.opends.server.types.DebugLogLevel;
-import org.opends.server.types.DirectoryException;
-import org.opends.server.types.DisconnectReason;
 import org.opends.server.types.DN;
-import org.opends.server.types.Entry;
+import org.opends.server.types.DebugLogLevel;
 import org.opends.server.types.InitializationException;
-import org.opends.server.types.LockManager;
-import org.opends.server.types.Privilege;
 import org.opends.server.types.ResultCode;
-import org.opends.server.util.Base64;
-
-import static org.opends.messages.ExtensionMessages.*;
-import static org.opends.server.loggers.ErrorLogger.*;
 import static org.opends.server.loggers.debug.DebugLogger.*;
+import static org.opends.server.loggers.ErrorLogger.logError;
+import static org.opends.messages.ExtensionMessages.*;
 import static org.opends.server.util.ServerConstants.*;
 import static org.opends.server.util.StaticUtils.*;
 
 
-
 /**
- * This class provides an implementation of a SASL mechanism that uses digest
- * authentication via DIGEST-MD5.  This is a password-based mechanism that does
- * not expose the password itself over the wire but rather uses an MD5 hash that
- * proves the client knows the password.  This is similar to the CRAM-MD5
- * mechanism, and the primary differences are that CRAM-MD5 only obtains random
- * data from the server whereas DIGEST-MD5 uses random data from both the
- * server and the client, CRAM-MD5 does not allow for an authorization ID in
- * addition to the authentication ID where DIGEST-MD5 does, and CRAM-MD5 does
- * not define any integrity and confidentiality mechanisms where DIGEST-MD5
- * does.  This implementation is based on the specification in RFC 2831 and
- * updates from draft-ietf-sasl-rfc2831bis-06.
+ * This class provides an implementation of a SASL mechanism that authenticates
+ * clients through DIGEST-MD5.
  */
 public class DigestMD5SASLMechanismHandler
-       extends SASLMechanismHandler<DigestMD5SASLMechanismHandlerCfg>
-       implements ConfigurationChangeListener<
-                       DigestMD5SASLMechanismHandlerCfg>
-{
-  /**
-   * The tracer object for the debug logger.
-   */
+      extends SASLMechanismHandler<DigestMD5SASLMechanismHandlerCfg>
+      implements ConfigurationChangeListener<DigestMD5SASLMechanismHandlerCfg> {
+
+  //The tracer object for the debug logger.
   private static final DebugTracer TRACER = getTracer();
 
   // The current configuration for this SASL mechanism handler.
-  private DigestMD5SASLMechanismHandlerCfg currentConfig;
+  private DigestMD5SASLMechanismHandlerCfg configuration;
 
   // The identity mapper that will be used to map ID strings to user entries.
   private IdentityMapper<?> identityMapper;
 
-  // The message digest engine that will be used to create the MD5 digests.
-  private MessageDigest md5Digest;
+  //Properties to use when creating a SASL server to process the authentication.
+  private HashMap<String,String> saslProps;
 
-  // The lock that will be used to provide threadsafe access to the message
-  // digest.
-  private Object digestLock;
+//The fully qualified domain name used when creating the SASL server.
+  private String serverFQDN;
 
-  // The random number generator that we will use to create the nonce.
-  private SecureRandom randomGenerator;
+  // The DN of the configuration entry for this SASL mechanism handler.
+  private DN configEntryDN;
 
+  //Property used to set the realm in the environment.
+  private static final String REALM_PROPERTY =
+                                          "com.sun.security.sasl.digest.realm";
 
 
   /**
@@ -128,1409 +98,95 @@
   }
 
 
-
   /**
    * {@inheritDoc}
    */
   @Override()
   public void initializeSASLMechanismHandler(
-                   DigestMD5SASLMechanismHandlerCfg configuration)
-         throws ConfigException, InitializationException
-  {
-    configuration.addDigestMD5ChangeListener(this);
-    currentConfig = configuration;
-
-
-    // Initialize the variables needed for the MD5 digest creation.
-    digestLock      = new Object();
-    randomGenerator = new SecureRandom();
-
-    try
-    {
-      md5Digest = MessageDigest.getInstance("MD5");
-    }
-    catch (Exception e)
-    {
-      if (debugEnabled())
-      {
-        TRACER.debugCaught(DebugLogLevel.ERROR, e);
+          DigestMD5SASLMechanismHandlerCfg configuration)
+  throws ConfigException, InitializationException {
+      configuration.addDigestMD5ChangeListener(this);
+      configEntryDN = configuration.dn();
+      try {
+         DN identityMapperDN = configuration.getIdentityMapperDN();
+         identityMapper = DirectoryServer.getIdentityMapper(identityMapperDN);
+         serverFQDN = getFQDN(configuration);
+         Message msg= INFO_DIGEST_MD5_SERVER_FQDN.get(serverFQDN);
+         logError(msg);
+         String QOP = getQOP(configuration);
+         saslProps = new HashMap<String,String>();
+         saslProps.put(Sasl.QOP, QOP);
+         if(QOP.equalsIgnoreCase(SASL_MECHANISM_CONFIDENTIALITY)) {
+             saslProps.put(Sasl.STRENGTH, getStrength(configuration));
+         }
+         String realm=getRealm(configuration);
+         if(realm != null) {
+           msg = INFO_DIGEST_MD5_REALM.get(realm);
+           logError(msg);
+           saslProps.put(REALM_PROPERTY, getRealm(configuration));
+         }
+         this.configuration = configuration;
+         DirectoryServer.registerSASLMechanismHandler(SASL_MECHANISM_DIGEST_MD5,
+                  this);
+      } catch (UnknownHostException unhe) {
+          if (debugEnabled()) {
+              TRACER.debugCaught(DebugLogLevel.ERROR, unhe);
+          }
+          Message message = ERR_SASL_CANNOT_GET_SERVER_FQDN.get(
+                  String.valueOf(configEntryDN), getExceptionMessage(unhe));
+          throw new InitializationException(message, unhe);
       }
-
-      Message message = ERR_SASLDIGESTMD5_CANNOT_GET_MESSAGE_DIGEST.get(
-          getExceptionMessage(e));
-      throw new InitializationException(message, e);
-    }
-
-
-    // Get the identity mapper that should be used to find users.
-    DN identityMapperDN = configuration.getIdentityMapperDN();
-    identityMapper = DirectoryServer.getIdentityMapper(identityMapperDN);
-
-
-    DirectoryServer.registerSASLMechanismHandler(SASL_MECHANISM_DIGEST_MD5,
-                                                 this);
   }
 
 
-
   /**
    * {@inheritDoc}
    */
   @Override()
-  public void finalizeSASLMechanismHandler()
-  {
-    currentConfig.removeDigestMD5ChangeListener(this);
+  public void finalizeSASLMechanismHandler() {
+    configuration.removeDigestMD5ChangeListener(this);
     DirectoryServer.deregisterSASLMechanismHandler(SASL_MECHANISM_DIGEST_MD5);
   }
 
 
-
-
   /**
    * {@inheritDoc}
    */
   @Override()
-  public void processSASLBind(BindOperation bindOperation)
-  {
-    DigestMD5SASLMechanismHandlerCfg config = currentConfig;
-    IdentityMapper<?> identityMapper = this.identityMapper;
-    String realm = config.getRealm();
-
-
-    // The DIGEST-MD5 bind process uses two stages.  See if we have any state
-    // information from the first stage to determine whether this is a
-    // continuation of an existing bind or an initial authentication.  Note that
-    // this implementation does not support subsequent authentication, so even
-    // if the client provided credentials for the bind, it will be treated as an
-    // initial authentication if there is no existing state.
-    boolean initialAuth = true;
-    ClientConnection clientConnection  = bindOperation.getClientConnection();
-    Object saslStateInfo = clientConnection.getSASLAuthStateInfo();
-    if ((saslStateInfo != null) &&
-        (saslStateInfo instanceof DigestMD5StateInfo))
-    {
-      initialAuth = false;
-    }
-
-    if (initialAuth)
-    {
-      // Create a buffer to hold the challenge.
-      StringBuilder challengeBuffer = new StringBuilder();
-
-
-      // Add the realm to the challenge.  If we have a configured realm, then
-      // use it.  Otherwise, add a realm for each suffix defined in the server.
-      if (realm == null)
-      {
-        Map<DN,Backend> suffixes = DirectoryServer.getPublicNamingContexts();
-        if (! suffixes.isEmpty())
-        {
-          Iterator<DN> iterator = suffixes.keySet().iterator();
-          challengeBuffer.append("realm=\"");
-          challengeBuffer.append(iterator.next().toNormalizedString());
-          challengeBuffer.append("\"");
-
-          while (iterator.hasNext())
-          {
-            challengeBuffer.append(",realm=\"");
-            challengeBuffer.append(iterator.next().toNormalizedString());
-            challengeBuffer.append("\"");
-          }
-        }
-      }
-      else
-      {
-        challengeBuffer.append("realm=\"");
-        challengeBuffer.append(realm);
-        challengeBuffer.append("\"");
-      }
-
-
-      // Generate the nonce.  Add it to the challenge and remember it for future
-      // use.
-      String nonce = generateNonce();
-      if (challengeBuffer.length() > 0)
-      {
-        challengeBuffer.append(",");
-      }
-      challengeBuffer.append("nonce=\"");
-      challengeBuffer.append(nonce);
-      challengeBuffer.append("\"");
-
-
-      // Generate the qop-list and add it to the challenge.
-      // FIXME -- Add support for integrity and confidentiality.  Once we do,
-      //          we'll also want to add the maxbuf and cipher options.
-      challengeBuffer.append(",qop=\"auth\"");
-
-
-      // Add the charset option to indicate that we support UTF-8 values.
-      challengeBuffer.append(",charset=utf-8");
-
-
-      // Add the algorithm, which will always be "md5-sess".
-      challengeBuffer.append(",algorithm=md5-sess");
-
-
-      // Encode the challenge as an ASN.1 element.  The total length of the
-      // encoded value must be less than 2048 bytes, which should not be a
-      // problem, but we'll add a safety check just in case....  In the event
-      // that it does happen, we'll also log an error so it is more noticeable.
-      ASN1OctetString challenge =
-           new ASN1OctetString(challengeBuffer.toString());
-      if (challenge.value().length >= 2048)
-      {
-        bindOperation.setResultCode(ResultCode.INVALID_CREDENTIALS);
-
-        Message message = WARN_SASLDIGESTMD5_CHALLENGE_TOO_LONG.get(
-                challenge.value().length);
-        bindOperation.setAuthFailureReason(message);
-
-        logError(message);
-        return;
-      }
-
-
-      // Store the state information with the client connection so we can use it
-      // for later validation.
-      DigestMD5StateInfo stateInfo = new DigestMD5StateInfo(nonce, "00000000");
-      clientConnection.setSASLAuthStateInfo(stateInfo);
-
-
-      // Prepare the response and return so it will be sent to the client.
-      bindOperation.setResultCode(ResultCode.SASL_BIND_IN_PROGRESS);
-      bindOperation.setServerSASLCredentials(challenge);
-      return;
-    }
-
-
-    // If we've gotten here, then we have existing SASL state information for
-    // this client.  Make sure that the client also provided credentials.
-    ASN1OctetString clientCredentials = bindOperation.getSASLCredentials();
-    if ((clientCredentials == null) || (clientCredentials.value().length == 0))
-    {
-      bindOperation.setResultCode(ResultCode.INVALID_CREDENTIALS);
-
-      Message message = ERR_SASLDIGESTMD5_NO_CREDENTIALS.get();
-      bindOperation.setAuthFailureReason(message);
-      return;
-    }
-
-
-    // Parse the SASL state information.  Also, since there are only ever two
-    // stages of a DIGEST-MD5 bind, clear the SASL state information stored in
-    // the client connection because it shouldn't be used anymore regardless of
-    // whether the bind succeeds or fails.  Note that if we do add support for
-    // subsequent authentication in the future, then we will probably need to
-    // keep state information in the client connection, but even then it will
-    // be different from what's already there.
-    DigestMD5StateInfo stateInfo = (DigestMD5StateInfo) saslStateInfo;
-    clientConnection.setSASLAuthStateInfo(null);
-
-
-    // Create variables to hold values stored in the client's response.  We'll
-    // also store the base DN because we might need to override it later.
-    String responseUserName      = null;
-    String responseRealm         = null;
-    String responseNonce         = null;
-    String responseCNonce        = null;
-    int    responseNonceCount    = -1;
-    String responseNonceCountStr = null;
-    String responseQoP           = "auth";
-    String responseDigestURI     = null;
-    byte[] responseDigest        = null;
-    String responseCharset       = "ISO-8859-1";
-    String responseAuthzID       = null;
-
-
-    // Get a temporary string representation of the SASL credentials using the
-    // ISO-8859-1 encoding and see if it contains "charset=utf-8".  If so, then
-    // re-parse the credentials using that character set.
-    byte[] credBytes  = clientCredentials.value();
-    String credString = null;
-    String lowerCreds = null;
-    try
-    {
-      credString = new String(credBytes, responseCharset);
-      lowerCreds = toLowerCase(credString);
-    }
-    catch (Exception e)
-    {
-      if (debugEnabled())
-      {
-        TRACER.debugCaught(DebugLogLevel.ERROR, e);
-      }
-
-      // This isn't necessarily fatal because we're going to retry using UTF-8,
-      // but we want to log it anyway.
-      logError(WARN_SASLDIGESTMD5_CANNOT_PARSE_ISO_CREDENTIALS.get(
-          responseCharset, getExceptionMessage(e)));
-    }
-
-    if ((credString == null) ||
-        (lowerCreds.indexOf("charset=utf-8") >= 0))
-    {
-      try
-      {
-        credString = new String(credBytes, "UTF-8");
-        lowerCreds = toLowerCase(credString);
-      }
-      catch (Exception e)
-      {
-        if (debugEnabled())
-        {
-          TRACER.debugCaught(DebugLogLevel.ERROR, e);
-        }
-
-        // This is fatal because either we can't parse the credentials as a
-        // string at all, or we know we need to do so using UTF-8 and can't.
-        bindOperation.setResultCode(ResultCode.INVALID_CREDENTIALS);
-
-        Message message = WARN_SASLDIGESTMD5_CANNOT_PARSE_UTF8_CREDENTIALS.get(
-                getExceptionMessage(e));
-        bindOperation.setAuthFailureReason(message);
-        return;
-      }
-    }
-
-
-    // Iterate through the credentials string, parsing the property names and
-    // their corresponding values.
-    int pos    = 0;
-    int length = credString.length();
-    while (pos < length)
-    {
-      int equalPos = credString.indexOf('=', pos+1);
-      if (equalPos < 0)
-      {
-        // This is bad because we're not at the end of the string but we don't
-        // have a name/value delimiter.
-        bindOperation.setResultCode(ResultCode.INVALID_CREDENTIALS);
-
-        Message message = ERR_SASLDIGESTMD5_INVALID_TOKEN_IN_CREDENTIALS.get(
-                credString, pos);
-        bindOperation.setAuthFailureReason(message);
-        return;
-      }
-
-
-      String tokenName  = lowerCreds.substring(pos, equalPos);
-
-      String tokenValue;
-      try
-      {
-        StringBuilder valueBuffer = new StringBuilder();
-        pos = readToken(credString, equalPos+1, length, valueBuffer);
-        tokenValue = valueBuffer.toString();
-      }
-      catch (DirectoryException de)
-      {
-        // We couldn't parse the token value, so it must be malformed.
-        bindOperation.setResultCode(ResultCode.INVALID_CREDENTIALS);
-        bindOperation.setAuthFailureReason(
-                de.getMessageObject());
-        return;
-      }
-
-      if (tokenName.equals("charset"))
-      {
-        // The value must be the string "utf-8".  If not, that's an error.
-        if (! tokenValue.equalsIgnoreCase("utf-8"))
-        {
-          bindOperation.setResultCode(ResultCode.INVALID_CREDENTIALS);
-
-          Message message = ERR_SASLDIGESTMD5_INVALID_CHARSET.get(tokenValue);
-          bindOperation.setAuthFailureReason(message);
+  public void processSASLBind(BindOperation bindOp) {
+      ClientConnection clientConnection = bindOp.getClientConnection();
+      if (clientConnection == null) {
+          Message message = ERR_SASLGSSAPI_NO_CLIENT_CONNECTION.get();
+          bindOp.setAuthFailureReason(message);
+          bindOp.setResultCode(ResultCode.INVALID_CREDENTIALS);
           return;
-        }
       }
-      else if (tokenName.equals("username"))
-      {
-        responseUserName = tokenValue;
-      }
-      else if (tokenName.equals("realm"))
-      {
-        responseRealm = tokenValue;
-        if (realm != null)
-        {
-          if (! responseRealm.equals(realm))
-          {
-            bindOperation.setResultCode(ResultCode.INVALID_CREDENTIALS);
-
-            Message message =
-                    ERR_SASLDIGESTMD5_INVALID_REALM.get(responseRealm);
-            bindOperation.setAuthFailureReason(message);
-            return;
-          }
-        }
-      }
-      else if (tokenName.equals("nonce"))
-      {
-        responseNonce = tokenValue;
-        String requestNonce = stateInfo.getNonce();
-        if (! responseNonce.equals(requestNonce))
-        {
-          // The nonce provided by the client is incorrect.  This could be an
-          // attempt at a replay or chosen plaintext attack, so we'll close the
-          // connection.  We will put a message in the log but will not send it
-          // to the client.
-          Message message = ERR_SASLDIGESTMD5_INVALID_NONCE.get();
-          clientConnection.disconnect(DisconnectReason.SECURITY_PROBLEM, false,
-                  message);
-          return;
-        }
-      }
-      else if (tokenName.equals("cnonce"))
-      {
-        responseCNonce = tokenValue;
-      }
-      else if (tokenName.equals("nc"))
-      {
-        try
-        {
-          responseNonceCountStr = tokenValue;
-          responseNonceCount    = Integer.parseInt(responseNonceCountStr, 16);
-        }
-        catch (Exception e)
-        {
-          if (debugEnabled())
-          {
-            TRACER.debugCaught(DebugLogLevel.ERROR, e);
-          }
-
-          bindOperation.setResultCode(ResultCode.INVALID_CREDENTIALS);
-
-          Message message = ERR_SASLDIGESTMD5_CANNOT_DECODE_NONCE_COUNT.get(
-                  tokenValue);
-          bindOperation.setAuthFailureReason(message);
-          return;
-        }
-
-        int storedNonce;
-        try
-        {
-          storedNonce = Integer.parseInt(stateInfo.getNonceCount(), 16);
-        }
-        catch (Exception e)
-        {
-          if (debugEnabled())
-          {
-            TRACER.debugCaught(DebugLogLevel.ERROR, e);
-          }
-
-          bindOperation.setResultCode(ResultCode.INVALID_CREDENTIALS);
-
-          Message message =
-                  ERR_SASLDIGESTMD5_CANNOT_DECODE_STORED_NONCE_COUNT.get(
-                          getExceptionMessage(e));
-          bindOperation.setAuthFailureReason(message);
-          return;
-        }
-
-        if (responseNonceCount != (storedNonce + 1))
-        {
-          // The nonce count provided by the client is incorrect.  This
-          // indicates a replay attack, so we'll close the connection.  We will
-          // put a message in the log but we will not send it to the client.
-          Message message = ERR_SASLDIGESTMD5_INVALID_NONCE_COUNT.get();
-          clientConnection.disconnect(DisconnectReason.SECURITY_PROBLEM, false,
-                  message);
-          return;
-        }
-      }
-      else if (tokenName.equals("qop"))
-      {
-        responseQoP = tokenValue;
-
-        if (responseQoP.equals("auth"))
-        {
-          // No action necessary.
-        }
-        else if (responseQoP.equals("auth-int"))
-        {
-          // FIXME -- Add support for integrity protection.
-          bindOperation.setResultCode(ResultCode.INVALID_CREDENTIALS);
-
-          Message message = ERR_SASLDIGESTMD5_INTEGRITY_NOT_SUPPORTED.get();
-          bindOperation.setAuthFailureReason(message);
-          return;
-        }
-        else if (responseQoP.equals("auth-conf"))
-        {
-          // FIXME -- Add support for confidentiality protection.
-          bindOperation.setResultCode(ResultCode.INVALID_CREDENTIALS);
-
-          Message message =
-                  ERR_SASLDIGESTMD5_CONFIDENTIALITY_NOT_SUPPORTED.get();
-          bindOperation.setAuthFailureReason(message);
-          return;
-        }
-        else
-        {
-          // This is an invalid QoP value.
-          bindOperation.setResultCode(ResultCode.INVALID_CREDENTIALS);
-
-          Message message = ERR_SASLDIGESTMD5_INVALID_QOP.get(responseQoP);
-          bindOperation.setAuthFailureReason(message);
-          return;
-        }
-      }
-      else if (tokenName.equals("digest-uri"))
-      {
-        responseDigestURI = tokenValue;
-
-        String serverFQDN = config.getServerFqdn();
-        if ((serverFQDN != null) && (serverFQDN.length() > 0))
-        {
-          // If a server FQDN is populated, then we'll use it to validate the
-          // digest-uri, which should be in the form "ldap/serverfqdn".
-          String expectedDigestURI = "ldap/" + serverFQDN;
-          if (! expectedDigestURI.equalsIgnoreCase(responseDigestURI))
-          {
-            bindOperation.setResultCode(ResultCode.INVALID_CREDENTIALS);
-
-            Message message = ERR_SASLDIGESTMD5_INVALID_DIGEST_URI.get(
-                    responseDigestURI, expectedDigestURI);
-            bindOperation.setAuthFailureReason(message);
-            return;
-          }
-        }
-      }
-      else if (tokenName.equals("response"))
-      {
-        try
-        {
-          responseDigest = hexStringToByteArray(tokenValue);
-        }
-        catch (ParseException pe)
-        {
-          if (debugEnabled())
-          {
-            TRACER.debugCaught(DebugLogLevel.ERROR, pe);
-          }
-
-          Message message =
-                  ERR_SASLDIGESTMD5_CANNOT_PARSE_RESPONSE_DIGEST.get(
-                          getExceptionMessage(pe));
-          bindOperation.setAuthFailureReason(message);
-          return;
-        }
-      }
-      else if (tokenName.equals("authzid"))
-      {
-        responseAuthzID = tokenValue;
-
-        // FIXME -- This must always be parsed in UTF-8 even if the charset for
-        // other elements is ISO 8859-1.
-      }
-      else if (tokenName.equals("maxbuf") || tokenName.equals("cipher"))
-      {
-        // FIXME -- Add support for confidentiality and integrity protection.
-      }
-      else
-      {
-        bindOperation.setResultCode(ResultCode.INVALID_CREDENTIALS);
-
-        Message message = ERR_SASLDIGESTMD5_INVALID_RESPONSE_TOKEN.get(
-                tokenName);
-        bindOperation.setAuthFailureReason(message);
-        return;
-      }
-    }
-
-
-    // Make sure that all required properties have been specified.
-    if ((responseUserName == null) || (responseUserName.length() == 0))
-    {
-      bindOperation.setResultCode(ResultCode.INVALID_CREDENTIALS);
-
-      Message message = ERR_SASLDIGESTMD5_NO_USERNAME_IN_RESPONSE.get();
-      bindOperation.setAuthFailureReason(message);
-      return;
-    }
-    else if (responseNonce == null)
-    {
-      bindOperation.setResultCode(ResultCode.INVALID_CREDENTIALS);
-
-      Message message = ERR_SASLDIGESTMD5_NO_NONCE_IN_RESPONSE.get();
-      bindOperation.setAuthFailureReason(message);
-      return;
-    }
-    else if (responseCNonce == null)
-    {
-      bindOperation.setResultCode(ResultCode.INVALID_CREDENTIALS);
-
-      Message message = ERR_SASLDIGESTMD5_NO_CNONCE_IN_RESPONSE.get();
-      bindOperation.setAuthFailureReason(message);
-      return;
-    }
-    else if (responseNonceCount < 0)
-    {
-      bindOperation.setResultCode(ResultCode.INVALID_CREDENTIALS);
-
-      Message message = ERR_SASLDIGESTMD5_NO_NONCE_COUNT_IN_RESPONSE.get();
-      bindOperation.setAuthFailureReason(message);
-      return;
-    }
-    else if (responseDigest == null)
-    {
-      bindOperation.setResultCode(ResultCode.INVALID_CREDENTIALS);
-
-      Message message = ERR_SASLDIGESTMD5_NO_DIGEST_IN_RESPONSE.get();
-      bindOperation.setAuthFailureReason(message);
-      return;
-    }
-
-
-    // Slight departure from draft-ietf-sasl-rfc2831bis-06 in order to
-    // support legacy/broken client implementations, such as Solaris
-    // Native LDAP Client, which omit digest-uri directive. the presence
-    // of digest-uri directive erroneously read "may" in the RFC and has
-    // been fixed later in the DRAFT to read "must". if the client does
-    // not include digest-uri directive use the empty string instead.
-    if (responseDigestURI == null)
-    {
-      responseDigestURI = "";
-    }
-
-
-    // If a realm has not been specified, then use the empty string.
-    // FIXME -- Should we reject this if a specific realm is defined?
-    if (responseRealm == null)
-    {
-      responseRealm = "";
-    }
-
-
-    // Get the user entry for the authentication ID.  Allow for an
-    // authentication ID that is just a username (as per the DIGEST-MD5 spec),
-    // but also allow a value in the authzid form specified in RFC 2829.
-    Entry  userEntry    = null;
-    String lowerUserName = toLowerCase(responseUserName);
-    if (lowerUserName.startsWith("dn:"))
-    {
-      // Try to decode the user DN and retrieve the corresponding entry.
-      DN userDN;
-      try
-      {
-        userDN = DN.decode(responseUserName.substring(3));
-      }
-      catch (DirectoryException de)
-      {
-        if (debugEnabled())
-        {
-          TRACER.debugCaught(DebugLogLevel.ERROR, de);
-        }
-
-        bindOperation.setResultCode(ResultCode.INVALID_CREDENTIALS);
-
-        Message message = ERR_SASLDIGESTMD5_CANNOT_DECODE_USERNAME_AS_DN.get(
-                responseUserName, de.getMessageObject());
-        bindOperation.setAuthFailureReason(message);
-        return;
-      }
-
-      if (userDN.isNullDN())
-      {
-        bindOperation.setResultCode(ResultCode.INVALID_CREDENTIALS);
-
-        Message message = ERR_SASLDIGESTMD5_USERNAME_IS_NULL_DN.get();
-        bindOperation.setAuthFailureReason(message);
-        return;
-      }
-
-      DN rootDN = DirectoryServer.getActualRootBindDN(userDN);
-      if (rootDN != null)
-      {
-        userDN = rootDN;
-      }
-
-      // Acquire a read lock on the user entry.  If this fails, then so will the
-      // authentication.
-      Lock readLock = null;
-      for (int i=0; i < 3; i++)
-      {
-        readLock = LockManager.lockRead(userDN);
-        if (readLock != null)
-        {
-          break;
-        }
-      }
-
-      if (readLock == null)
-      {
-        bindOperation.setResultCode(DirectoryServer.getServerErrorResultCode());
-
-        Message message = INFO_SASLDIGESTMD5_CANNOT_LOCK_ENTRY.get(
-                String.valueOf(userDN));
-        bindOperation.setAuthFailureReason(message);
-        return;
-      }
-
-      try
-      {
-        userEntry = DirectoryServer.getEntry(userDN);
-      }
-      catch (DirectoryException de)
-      {
-        if (debugEnabled())
-        {
-          TRACER.debugCaught(DebugLogLevel.ERROR, de);
-        }
-
-        bindOperation.setResultCode(ResultCode.INVALID_CREDENTIALS);
-
-        Message message = ERR_SASLDIGESTMD5_CANNOT_GET_ENTRY_BY_DN.get(
-                String.valueOf(userDN), de.getMessageObject());
-        bindOperation.setAuthFailureReason(message);
-        return;
-      }
-      finally
-      {
-        LockManager.unlock(userDN, readLock);
-      }
-    }
-    else
-    {
-      // Use the identity mapper to resolve the username to an entry.
-      String userName = responseUserName;
-      if (lowerUserName.startsWith("u:"))
-      {
-        if (lowerUserName.equals("u:"))
-        {
-          bindOperation.setResultCode(ResultCode.INVALID_CREDENTIALS);
-
-          Message message = ERR_SASLDIGESTMD5_ZERO_LENGTH_USERNAME.get();
-          bindOperation.setAuthFailureReason(message);
-          return;
-        }
-
-        userName = responseUserName.substring(2);
-      }
-
-
-      try
-      {
-        userEntry = identityMapper.getEntryForID(userName);
-      }
-      catch (DirectoryException de)
-      {
-        if (debugEnabled())
-        {
-          TRACER.debugCaught(DebugLogLevel.ERROR, de);
-        }
-
-        bindOperation.setResultCode(ResultCode.INVALID_CREDENTIALS);
-
-        Message message = ERR_SASLDIGESTMD5_CANNOT_MAP_USERNAME.get(
-                String.valueOf(responseUserName), de.getMessageObject());
-        bindOperation.setAuthFailureReason(message);
-        return;
-      }
-    }
-
-
-    // At this point, we should have a user entry.  If we don't then fail.
-    if (userEntry == null)
-    {
-      bindOperation.setResultCode(ResultCode.INVALID_CREDENTIALS);
-
-      Message message =
-              ERR_SASLDIGESTMD5_NO_MATCHING_ENTRIES.get(responseUserName);
-      bindOperation.setAuthFailureReason(message);
-      return;
-    }
-    else
-    {
-      bindOperation.setSASLAuthUserEntry(userEntry);
-    }
-
-
-    Entry authZEntry = userEntry;
-    if (responseAuthzID != null)
-    {
-      if (responseAuthzID.length() == 0)
-      {
-        // The authorization ID must not be an empty string.
-        bindOperation.setResultCode(ResultCode.INVALID_CREDENTIALS);
-
-        Message message = ERR_SASLDIGESTMD5_EMPTY_AUTHZID.get();
-        bindOperation.setAuthFailureReason(message);
-        return;
-      }
-      else if (! responseAuthzID.equals(responseUserName))
-      {
-        String lowerAuthzID = toLowerCase(responseAuthzID);
-
-        if (lowerAuthzID.startsWith("dn:"))
-        {
-          DN authzDN;
-          try
-          {
-            authzDN = DN.decode(responseAuthzID.substring(3));
-          }
-          catch (DirectoryException de)
-          {
-            if (debugEnabled())
-            {
-              TRACER.debugCaught(DebugLogLevel.ERROR, de);
-            }
-
-            bindOperation.setResultCode(ResultCode.INVALID_CREDENTIALS);
-
-            Message message = ERR_SASLDIGESTMD5_AUTHZID_INVALID_DN.get(
-                    responseAuthzID, de.getMessageObject());
-            bindOperation.setAuthFailureReason(message);
-            return;
-          }
-
-          DN actualAuthzDN = DirectoryServer.getActualRootBindDN(authzDN);
-          if (actualAuthzDN != null)
-          {
-            authzDN = actualAuthzDN;
-          }
-
-          if (! authzDN.equals(userEntry.getDN()))
-          {
-            AuthenticationInfo tempAuthInfo =
-              new AuthenticationInfo(userEntry,
-                       DirectoryServer.isRootDN(userEntry.getDN()));
-            InternalClientConnection tempConn =
-                 new InternalClientConnection(tempAuthInfo);
-            if (! tempConn.hasPrivilege(Privilege.PROXIED_AUTH, bindOperation))
-            {
-              bindOperation.setResultCode(ResultCode.INVALID_CREDENTIALS);
-
-              Message message =
-                      ERR_SASLDIGESTMD5_AUTHZID_INSUFFICIENT_PRIVILEGES.get(
-                              String.valueOf(userEntry.getDN()));
-              bindOperation.setAuthFailureReason(message);
+      ClientConnection clientConn  = bindOp.getClientConnection();
+      SASLContext saslContext =
+         (SASLContext) clientConn.getSASLAuthStateInfo();
+      if(saslContext == null) {
+          try {
+              saslContext = SASLContext.createSASLContext(saslProps, serverFQDN,
+                            SASL_MECHANISM_DIGEST_MD5, identityMapper);
+          } catch (SaslException ex) {
+              if (debugEnabled()) {
+                  TRACER.debugCaught(DebugLogLevel.ERROR, ex);
+              }
+              Message msg =
+                  ERR_SASL_CONTEXT_CREATE_ERROR.get(SASL_MECHANISM_DIGEST_MD5,
+                                                    getExceptionMessage(ex));
+              clientConn.setSASLAuthStateInfo(null);
+              bindOp.setAuthFailureReason(msg);
+              bindOp.setResultCode(ResultCode.INVALID_CREDENTIALS);
               return;
-            }
-
-            if (authzDN.isNullDN())
-            {
-              authZEntry = null;
-            }
-            else
-            {
-              try
-              {
-                authZEntry = DirectoryServer.getEntry(authzDN);
-                if (authZEntry == null)
-                {
-                  bindOperation.setResultCode(ResultCode.INVALID_CREDENTIALS);
-
-                  Message message = ERR_SASLDIGESTMD5_AUTHZID_NO_SUCH_ENTRY.get(
-                          String.valueOf(authzDN));
-                  bindOperation.setAuthFailureReason(message);
-                  return;
-                }
-              }
-              catch (DirectoryException de)
-              {
-                if (debugEnabled())
-                {
-                  TRACER.debugCaught(DebugLogLevel.ERROR, de);
-                }
-
-                bindOperation.setResultCode(ResultCode.INVALID_CREDENTIALS);
-
-                Message message = ERR_SASLDIGESTMD5_AUTHZID_CANNOT_GET_ENTRY
-                        .get(String.valueOf(authzDN), de.getMessageObject());
-                bindOperation.setAuthFailureReason(message);
-                return;
-              }
-            }
           }
-        }
-        else
-        {
-          String idStr;
-          if (lowerAuthzID.startsWith("u:"))
-          {
-            idStr = responseAuthzID.substring(2);
-          }
-          else
-          {
-            idStr = responseAuthzID;
-          }
-
-          if (idStr.length() == 0)
-          {
-            authZEntry = null;
-          }
-          else
-          {
-            try
-            {
-              authZEntry = identityMapper.getEntryForID(idStr);
-              if (authZEntry == null)
-              {
-                bindOperation.setResultCode(ResultCode.INVALID_CREDENTIALS);
-
-                Message message = ERR_SASLDIGESTMD5_AUTHZID_NO_MAPPED_ENTRY.get(
-                        responseAuthzID);
-                bindOperation.setAuthFailureReason(message);
-                return;
-              }
-            }
-            catch (DirectoryException de)
-            {
-              if (debugEnabled())
-              {
-                TRACER.debugCaught(DebugLogLevel.ERROR, de);
-              }
-
-              bindOperation.setResultCode(ResultCode.INVALID_CREDENTIALS);
-
-              Message message = ERR_SASLDIGESTMD5_CANNOT_MAP_AUTHZID.get(
-                      responseAuthzID, de.getMessageObject());
-              bindOperation.setAuthFailureReason(message);
-              return;
-            }
-          }
-
-          if ((authZEntry == null) ||
-              (! authZEntry.getDN().equals(userEntry.getDN())))
-          {
-            AuthenticationInfo tempAuthInfo =
-              new AuthenticationInfo(userEntry,
-                       DirectoryServer.isRootDN(userEntry.getDN()));
-            InternalClientConnection tempConn =
-                 new InternalClientConnection(tempAuthInfo);
-            if (! tempConn.hasPrivilege(Privilege.PROXIED_AUTH, bindOperation))
-            {
-              bindOperation.setResultCode(ResultCode.INVALID_CREDENTIALS);
-
-              Message message =
-                      ERR_SASLDIGESTMD5_AUTHZID_INSUFFICIENT_PRIVILEGES.get(
-                              String.valueOf(userEntry.getDN()));
-              bindOperation.setAuthFailureReason(message);
-              return;
-            }
-          }
-        }
+          saslContext.evaluateInitialStage(bindOp);
+      } else {
+          saslContext.evaluateFinalStage(bindOp);
       }
-    }
-
-
-    // Get the clear-text passwords from the user entry, if there are any.
-    List<ByteString> clearPasswords;
-    try
-    {
-      PasswordPolicyState pwPolicyState =
-           new PasswordPolicyState(userEntry, false);
-      clearPasswords = pwPolicyState.getClearPasswords();
-      if ((clearPasswords == null) || clearPasswords.isEmpty())
-      {
-        bindOperation.setResultCode(ResultCode.INVALID_CREDENTIALS);
-
-        Message message = ERR_SASLDIGESTMD5_NO_REVERSIBLE_PASSWORDS.get(
-                String.valueOf(userEntry.getDN()));
-        bindOperation.setAuthFailureReason(message);
-        return;
-      }
-    }
-    catch (Exception e)
-    {
-      bindOperation.setResultCode(ResultCode.INVALID_CREDENTIALS);
-
-      Message message = ERR_SASLDIGESTMD5_CANNOT_GET_REVERSIBLE_PASSWORDS.get(
-              String.valueOf(userEntry.getDN()),
-              String.valueOf(e));
-      bindOperation.setAuthFailureReason(message);
-      return;
-    }
-
-
-    // Iterate through the clear-text values and see if any of them can be used
-    // in conjunction with the challenge to construct the provided digest.
-    boolean matchFound    = false;
-    byte[]  passwordBytes = null;
-    for (ByteString clearPassword : clearPasswords)
-    {
-      byte[] generatedDigest;
-      try
-      {
-        generatedDigest =
-             generateResponseDigest(responseUserName, responseAuthzID,
-                                    clearPassword.value(), responseRealm,
-                                    responseNonce, responseCNonce,
-                                    responseNonceCountStr, responseDigestURI,
-                                    responseQoP, responseCharset);
-      }
-      catch (Exception e)
-      {
-        if (debugEnabled())
-        {
-          TRACER.debugCaught(DebugLogLevel.ERROR, e);
-        }
-
-        logError(WARN_SASLDIGESTMD5_CANNOT_GENERATE_RESPONSE_DIGEST.get(
-            getExceptionMessage(e)));
-        continue;
-      }
-
-      if (Arrays.equals(responseDigest, generatedDigest))
-      {
-        matchFound    = true;
-        passwordBytes = clearPassword.value();
-        break;
-      }
-    }
-
-    if (! matchFound)
-    {
-      bindOperation.setResultCode(ResultCode.INVALID_CREDENTIALS);
-
-      Message message = ERR_SASLDIGESTMD5_INVALID_CREDENTIALS.get();
-      bindOperation.setAuthFailureReason(message);
-      return;
-    }
-
-
-    // Generate the response auth element to include in the response to the
-    // client.
-    byte[] responseAuth;
-    try
-    {
-      responseAuth =
-           generateResponseAuthDigest(responseUserName, responseAuthzID,
-                                      passwordBytes, responseRealm,
-                                      responseNonce, responseCNonce,
-                                      responseNonceCountStr, responseDigestURI,
-                                      responseQoP, responseCharset);
-    }
-    catch (Exception e)
-    {
-      if (debugEnabled())
-      {
-        TRACER.debugCaught(DebugLogLevel.ERROR, e);
-      }
-
-      bindOperation.setResultCode(ResultCode.INVALID_CREDENTIALS);
-
-      Message message =
-              ERR_SASLDIGESTMD5_CANNOT_GENERATE_RESPONSE_AUTH_DIGEST.get(
-                      getExceptionMessage(e));
-      bindOperation.setAuthFailureReason(message);
-      return;
-    }
-
-    ASN1OctetString responseAuthStr =
-         new ASN1OctetString("rspauth=" + getHexString(responseAuth));
-
-
-    // Make sure to store the updated nonce count with the client connection to
-    // allow for correct subsequent authentication.
-    stateInfo.setNonceCount(responseNonceCountStr);
-
-
-    // If we've gotten here, then the authentication was successful.  We'll also
-    // need to include the response auth string in the server SASL credentials.
-    bindOperation.setResultCode(ResultCode.SUCCESS);
-    bindOperation.setServerSASLCredentials(responseAuthStr);
-
-
-    AuthenticationInfo authInfo =
-         new AuthenticationInfo(userEntry, authZEntry,
-                                SASL_MECHANISM_DIGEST_MD5,
-                                DirectoryServer.isRootDN(userEntry.getDN()));
-    bindOperation.setAuthenticationInfo(authInfo);
-    return;
   }
 
 
-
-  /**
-   * Generates a new nonce value to use during the DIGEST-MD5 authentication
-   * process.
-   *
-   * @return  The nonce that should be used for DIGEST-MD5 authentication.
-   */
-  private String generateNonce()
-  {
-    byte[] nonceBytes = new byte[16];
-    randomGenerator.nextBytes(nonceBytes);
-    return Base64.encode(nonceBytes);
-  }
-
-
-
-  /**
-   * Reads the next token from the provided credentials string using the
-   * provided information.  If the token is surrounded by quotation marks, then
-   * the token returned will not include those quotation marks.
-   *
-   * @param  credentials  The credentials string from which to read the token.
-   * @param  startPos     The position of the first character of the token to
-   *                      read.
-   * @param  length       The total number of characters in the credentials
-   *                      string.
-   * @param  token        The buffer into which the token is to be placed.
-   *
-   * @return  The position at which the next token should start, or a value
-   *          greater than or equal to the length of the string if there are no
-   *          more tokens.
-   *
-   * @throws  DirectoryException  If a problem occurs while attempting to read
-   *                              the token.
-   */
-  private int readToken(String credentials, int startPos, int length,
-                        StringBuilder token)
-          throws DirectoryException
-  {
-    // If the position is greater than or equal to the length, then we shouldn't
-    // do anything.
-    if (startPos >= length)
-    {
-      return startPos;
-    }
-
-
-    // Look at the first character to see if it's an empty string or the string
-    // is quoted.
-    boolean isEscaped = false;
-    boolean isQuoted  = false;
-    int     pos       = startPos;
-    char    c         = credentials.charAt(pos++);
-
-    if (c == ',')
-    {
-      // This must be a zero-length token, so we'll just return the next
-      // position.
-      return pos;
-    }
-    else if (c == '"')
-    {
-      // The string is quoted, so we'll ignore this character, and we'll keep
-      // reading until we find the unescaped closing quote followed by a comma
-      // or the end of the string.
-      isQuoted = true;
-    }
-    else if (c == '\\')
-    {
-      // The next character is escaped, so we'll take it no matter what.
-      isEscaped = true;
-    }
-    else
-    {
-      // The string is not quoted, and this is the first character.  Store this
-      // character and keep reading until we find a comma or the end of the
-      // string.
-      token.append(c);
-    }
-
-
-    // Enter a loop, reading until we find the appropriate criteria for the end
-    // of the token.
-    while (pos < length)
-    {
-      c = credentials.charAt(pos++);
-
-      if (isEscaped)
-      {
-        // The previous character was an escape, so we'll take this no matter
-        // what.
-        token.append(c);
-        isEscaped = false;
-      }
-      else if (c == ',')
-      {
-        // If this is a quoted string, then this comma is part of the token.
-        // Otherwise, it's the end of the token.
-        if (isQuoted)
-        {
-          token.append(c);
-        }
-        else
-        {
-          break;
-        }
-      }
-      else if (c == '"')
-      {
-        if (isQuoted)
-        {
-          // This should be the end of the token, but in order for it to be
-          // valid it must be followed by a comma or the end of the string.
-          if (pos >= length)
-          {
-            // We have hit the end of the string, so this is fine.
-            break;
-          }
-          else
-          {
-            char c2 = credentials.charAt(pos++);
-            if (c2 == ',')
-            {
-              // We have hit the end of the token, so this is fine.
-              break;
-            }
-            else
-            {
-              // We found the closing quote before the end of the token.  This
-              // is not fine.
-              Message message =
-                  ERR_SASLDIGESTMD5_INVALID_CLOSING_QUOTE_POS.get((pos-2));
-              throw new DirectoryException(ResultCode.INVALID_CREDENTIALS,
-                                           message);
-            }
-          }
-        }
-        else
-        {
-          // This must be part of the value, so we'll take it.
-          token.append(c);
-        }
-      }
-      else if (c == '\\')
-      {
-        // The next character is escaped.  We'll set a flag so we know to
-        // accept it, but will not include the backspace itself.
-        isEscaped = true;
-      }
-      else
-      {
-        token.append(c);
-      }
-    }
-
-
-    return pos;
-  }
-
-
-
-  /**
-   * Generates the appropriate DIGEST-MD5 response for the provided set of
-   * information.
-   *
-   * @param  userName    The username from the authentication request.
-   * @param  authzID     The authorization ID from the request, or
-   *                     <CODE>null</CODE> if there is none.
-   * @param  password    The clear-text password for the user.
-   * @param  realm       The realm for which the authentication is to be
-   *                     performed.
-   * @param  nonce       The random data generated by the server for use in the
-   *                     digest.
-   * @param  cnonce      The random data generated by the client for use in the
-   *                     digest.
-   * @param  nonceCount  The 8-digit hex string indicating the number of times
-   *                     the provided nonce has been used by the client.
-   * @param  digestURI   The digest URI that specifies the service and host for
-   *                     which the authentication is being performed.
-   * @param  qop         The quality of protection string for the
-   *                     authentication.
-   * @param  charset     The character set used to encode the information.
-   *
-   * @return  The DIGEST-MD5 response for the provided set of information.
-   *
-   * @throws  UnsupportedEncodingException  If the specified character set is
-   *                                        invalid for some reason.
-   */
-  public byte[] generateResponseDigest(String userName, String authzID,
-                                       byte[] password, String realm,
-                                       String nonce, String cnonce,
-                                       String nonceCount, String digestURI,
-                                       String qop, String charset)
-         throws UnsupportedEncodingException
-  {
-    synchronized (digestLock)
-    {
-      // First, get a hash of "username:realm:password".
-      StringBuilder a1String1 = new StringBuilder();
-      a1String1.append(userName);
-      a1String1.append(':');
-      a1String1.append(realm);
-      a1String1.append(':');
-
-      byte[] a1Bytes1a = a1String1.toString().getBytes(charset);
-      byte[] a1Bytes1  = new byte[a1Bytes1a.length + password.length];
-      System.arraycopy(a1Bytes1a, 0, a1Bytes1, 0, a1Bytes1a.length);
-      System.arraycopy(password, 0, a1Bytes1, a1Bytes1a.length,
-                       password.length);
-      byte[] urpHash = md5Digest.digest(a1Bytes1);
-
-
-      // Next, get a hash of "urpHash:nonce:cnonce[:authzid]".
-      StringBuilder a1String2 = new StringBuilder();
-      a1String2.append(':');
-      a1String2.append(nonce);
-      a1String2.append(':');
-      a1String2.append(cnonce);
-      if (authzID != null)
-      {
-        a1String2.append(':');
-        a1String2.append(authzID);
-      }
-      byte[] a1Bytes2a = a1String2.toString().getBytes(charset);
-      byte[] a1Bytes2  = new byte[urpHash.length + a1Bytes2a.length];
-      System.arraycopy(urpHash, 0, a1Bytes2, 0, urpHash.length);
-      System.arraycopy(a1Bytes2a, 0, a1Bytes2, urpHash.length,
-                       a1Bytes2a.length);
-      byte[] a1Hash = md5Digest.digest(a1Bytes2);
-
-
-      // Next, get a hash of "AUTHENTICATE:digesturi".
-      byte[] a2Bytes = ("AUTHENTICATE:" + digestURI).getBytes(charset);
-      byte[] a2Hash  = md5Digest.digest(a2Bytes);
-
-
-      // Get hex string representations of the last two hashes.
-      String a1HashHex = getHexString(a1Hash);
-      String a2HashHex = getHexString(a2Hash);
-
-
-      // Put together the final string to hash, consisting of
-      // "a1HashHex:nonce:nonceCount:cnonce:qop:a2HashHex" and get its digest.
-      StringBuilder kdString = new StringBuilder();
-      kdString.append(a1HashHex);
-      kdString.append(':');
-      kdString.append(nonce);
-      kdString.append(':');
-      kdString.append(nonceCount);
-      kdString.append(':');
-      kdString.append(cnonce);
-      kdString.append(':');
-      kdString.append(qop);
-      kdString.append(':');
-      kdString.append(a2HashHex);
-      return md5Digest.digest(kdString.toString().getBytes(charset));
-    }
-  }
-
-
-
-  /**
-   * Generates the appropriate DIGEST-MD5 rspauth digest using the provided
-   * information.
-   *
-   * @param  userName    The username from the authentication request.
-   * @param  authzID     The authorization ID from the request, or
-   *                     <CODE>null</CODE> if there is none.
-   * @param  password    The clear-text password for the user.
-   * @param  realm       The realm for which the authentication is to be
-   *                     performed.
-   * @param  nonce       The random data generated by the server for use in the
-   *                     digest.
-   * @param  cnonce      The random data generated by the client for use in the
-   *                     digest.
-   * @param  nonceCount  The 8-digit hex string indicating the number of times
-   *                     the provided nonce has been used by the client.
-   * @param  digestURI   The digest URI that specifies the service and host for
-   *                     which the authentication is being performed.
-   * @param  qop         The quality of protection string for the
-   *                     authentication.
-   * @param  charset     The character set used to encode the information.
-   *
-   * @return  The DIGEST-MD5 response for the provided set of information.
-   *
-   * @throws  UnsupportedEncodingException  If the specified character set is
-   *                                        invalid for some reason.
-   */
-  public byte[] generateResponseAuthDigest(String userName, String authzID,
-                                           byte[] password, String realm,
-                                           String nonce, String cnonce,
-                                           String nonceCount, String digestURI,
-                                           String qop, String charset)
-         throws UnsupportedEncodingException
-  {
-    synchronized (digestLock)
-    {
-      // First, get a hash of "username:realm:password".
-      StringBuilder a1String1 = new StringBuilder();
-      a1String1.append(userName);
-      a1String1.append(':');
-      a1String1.append(realm);
-      a1String1.append(':');
-
-      byte[] a1Bytes1a = a1String1.toString().getBytes(charset);
-      byte[] a1Bytes1  = new byte[a1Bytes1a.length + password.length];
-      System.arraycopy(a1Bytes1a, 0, a1Bytes1, 0, a1Bytes1a.length);
-      System.arraycopy(password, 0, a1Bytes1, a1Bytes1a.length,
-                       password.length);
-      byte[] urpHash = md5Digest.digest(a1Bytes1);
-
-
-      // Next, get a hash of "urpHash:nonce:cnonce[:authzid]".
-      StringBuilder a1String2 = new StringBuilder();
-      a1String2.append(':');
-      a1String2.append(nonce);
-      a1String2.append(':');
-      a1String2.append(cnonce);
-      if (authzID != null)
-      {
-        a1String2.append(':');
-        a1String2.append(authzID);
-      }
-      byte[] a1Bytes2a = a1String2.toString().getBytes(charset);
-      byte[] a1Bytes2  = new byte[urpHash.length + a1Bytes2a.length];
-      System.arraycopy(urpHash, 0, a1Bytes2, 0, urpHash.length);
-      System.arraycopy(a1Bytes2a, 0, a1Bytes2, urpHash.length,
-                       a1Bytes2a.length);
-      byte[] a1Hash = md5Digest.digest(a1Bytes2);
-
-
-      // Next, get a hash of "AUTHENTICATE:digesturi".
-      String a2String = ":" + digestURI;
-      if (qop.equals("auth-int") || qop.equals("auth-conf"))
-      {
-        a2String += ":00000000000000000000000000000000";
-      }
-      byte[] a2Bytes = a2String.getBytes(charset);
-      byte[] a2Hash  = md5Digest.digest(a2Bytes);
-
-
-      // Get hex string representations of the last two hashes.
-      String a1HashHex = getHexString(a1Hash);
-      String a2HashHex = getHexString(a2Hash);
-
-
-      // Put together the final string to hash, consisting of
-      // "a1HashHex:nonce:nonceCount:cnonce:qop:a2HashHex" and get its digest.
-      StringBuilder kdString = new StringBuilder();
-      kdString.append(a1HashHex);
-      kdString.append(':');
-      kdString.append(nonce);
-      kdString.append(':');
-      kdString.append(nonceCount);
-      kdString.append(':');
-      kdString.append(cnonce);
-      kdString.append(':');
-      kdString.append(qop);
-      kdString.append(':');
-      kdString.append(a2HashHex);
-      return md5Digest.digest(kdString.toString().getBytes(charset));
-    }
-  }
-
-
-
-  /**
-   * Retrieves a hexadecimal string representation of the contents of the
-   * provided byte array.
-   *
-   * @param  byteArray  The byte array for which to obtain the hexadecimal
-   *                    string representation.
-   *
-   * @return  The hexadecimal string representation of the contents of the
-   *          provided byte array.
-   */
-  private String getHexString(byte[] byteArray)
-  {
-    StringBuilder buffer = new StringBuilder(2*byteArray.length);
-    for (byte b : byteArray)
-    {
-      buffer.append(byteToLowerHex(b));
-    }
-
-    return buffer.toString();
-  }
-
-
-
   /**
    * {@inheritDoc}
    */
@@ -1554,7 +210,6 @@
   }
 
 
-
   /**
    * {@inheritDoc}
    */
@@ -1569,7 +224,6 @@
   }
 
 
-
   /**
    * {@inheritDoc}
    */
@@ -1581,23 +235,116 @@
   }
 
 
-
   /**
    * {@inheritDoc}
    */
   public ConfigChangeResult applyConfigurationChange(
-              DigestMD5SASLMechanismHandlerCfg configuration)
+          DigestMD5SASLMechanismHandlerCfg configuration)
   {
-    ResultCode        resultCode          = ResultCode.SUCCESS;
-    boolean           adminActionRequired = false;
-    ArrayList<Message> messages            = new ArrayList<Message>();
+      ResultCode        resultCode          = ResultCode.SUCCESS;
+      boolean           adminActionRequired = false;
 
-    // Get the identity mapper that should be used to find users.
-    DN identityMapperDN = configuration.getIdentityMapperDN();
-    identityMapper = DirectoryServer.getIdentityMapper(identityMapperDN);
-    currentConfig  = configuration;
+      ArrayList<Message> messages            = new ArrayList<Message>();
+      try {
+          DN identityMapperDN = configuration.getIdentityMapperDN();
+          identityMapper = DirectoryServer.getIdentityMapper(identityMapperDN);
+          serverFQDN = getFQDN(configuration);
+          Message msg = INFO_DIGEST_MD5_SERVER_FQDN.get(serverFQDN);
+          logError(msg);
+          String QOP = getQOP(configuration);
+          saslProps = new HashMap<String,String>();
+          saslProps.put(Sasl.QOP, QOP);
+          if(QOP.equalsIgnoreCase(SASL_MECHANISM_CONFIDENTIALITY)) {
+              saslProps.put(Sasl.STRENGTH, getStrength(configuration));
+          }
+          String realm=getRealm(configuration);
+          if(realm != null) {
+               msg = INFO_DIGEST_MD5_REALM.get(realm);
+              logError(msg);
+             saslProps.put(REALM_PROPERTY, getRealm(configuration));
+          }
+          this.configuration  = configuration;
+      } catch (UnknownHostException unhe) {
+          if (debugEnabled()) {
+              TRACER.debugCaught(DebugLogLevel.ERROR, unhe);
+          }
+          resultCode = ResultCode.OPERATIONS_ERROR;
+          messages.add(ERR_SASL_CANNOT_GET_SERVER_FQDN.get(
+                  String.valueOf(configEntryDN), getExceptionMessage(unhe)));
+          return new ConfigChangeResult(resultCode,adminActionRequired,
+                  messages);
+      }
+      return new ConfigChangeResult(resultCode, adminActionRequired, messages);
+  }
 
-    return new ConfigChangeResult(resultCode, adminActionRequired, messages);
+
+  /**
+   * Retrieves the cipher strength string to use if confidentiality is enforce.
+   * This determination is the lowest value that the server can use.
+   *
+   * @param configuration The configuration to examine.
+   * @return The cipher strength string.
+   */
+  private String
+  getStrength(DigestMD5SASLMechanismHandlerCfg configuration) {
+      CipherStrength strength = configuration.getCipherStrength();
+      if(strength.equals(CipherStrength.HIGH)) {
+          return "high";
+      } else if(strength.equals(CipherStrength.MEDIUM)) {
+          return "high,medium";
+      } else {
+          return "high,medium,low";
+      }
+  }
+
+
+  /**
+   * Retrieves the QOP (quality-of-protection) from the specified
+   * configuration.
+   *
+   * @param configuration The new configuration to use.
+   * @return A string representing the quality-of-protection.
+   */
+  private String
+  getQOP(DigestMD5SASLMechanismHandlerCfg configuration) {
+      QualityOfProtection QOP = configuration.getQualityOfProtection();
+      if(QOP.equals(QualityOfProtection.CONFIDENTIALITY))
+          return "auth-conf";
+      else if(QOP.equals(QualityOfProtection.INTEGRITY))
+          return "auth-int";
+      else
+          return "auth";
+  }
+
+
+  /**
+   * Returns the fully qualified name either defined in the configuration, or,
+   * determined by examining the system configuration.
+   *
+   * @param configuration The configuration to check.
+   * @return The fully qualified hostname of the server.
+   *
+   * @throws UnknownHostException If the name cannot be determined from the
+   *                              system configuration.
+   */
+  private String getFQDN(DigestMD5SASLMechanismHandlerCfg configuration)
+  throws UnknownHostException {
+      String serverName = configuration.getServerFqdn();
+      if (serverName == null) {
+              serverName = InetAddress.getLocalHost().getCanonicalHostName();
+      }
+      return serverName;
+  }
+
+
+  /**
+   * Retrieve the realm either defined in the specified configuration. If this
+   * isn't defined, the SaslServer internal code uses the server name.
+   *
+   * @param configuration The configuration to check.
+   * @return A string representing the realm.
+   */
+  private String getRealm(DigestMD5SASLMechanismHandlerCfg configuration) {
+    return configuration.getRealm();
   }
 }
-
diff --git a/opends/src/server/org/opends/server/extensions/DigestMD5StateInfo.java b/opends/src/server/org/opends/server/extensions/DigestMD5StateInfo.java
deleted file mode 100644
index b51f9d7..0000000
--- a/opends/src/server/org/opends/server/extensions/DigestMD5StateInfo.java
+++ /dev/null
@@ -1,104 +0,0 @@
-/*
- * CDDL HEADER START
- *
- * The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License").  You may not use this file except in compliance
- * with the License.
- *
- * You can obtain a copy of the license at
- * trunk/opends/resource/legal-notices/OpenDS.LICENSE
- * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
- * See the License for the specific language governing permissions
- * and limitations under the License.
- *
- * When distributing Covered Code, include this CDDL HEADER in each
- * file and include the License file at
- * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
- * add the following below this CDDL HEADER, with the fields enclosed
- * by brackets "[]" replaced with your own identifying information:
- *      Portions Copyright [yyyy] [name of copyright owner]
- *
- * CDDL HEADER END
- *
- *
- *      Copyright 2006-2008 Sun Microsystems, Inc.
- */
-package org.opends.server.extensions;
-
-
-/**
- * This class defines a data structure that will hold state information for use
- * in the DIGEST-MD5 authentication process.  This information will support both
- * the two-stage initial authentication as well as subsequent authentication.
- */
-public class DigestMD5StateInfo
-{
-
-
-
-  // The nonce generated by the server for this authentication session.
-  private String nonce;
-
-  // The hex string representation of the nonce count used by the last
-  // successful authentication.
-  private String nonceCount;
-
-
-
-  /**
-   * Creates a new instance of this DIGEST-MD5 state info structure.
-   *
-   * @param  nonce       The nonce generated by the server for this
-   *                     authentication session.
-   * @param  nonceCount  The hex string representation of the nonce count used
-   *                     by the last successful authentication.
-   */
-  public DigestMD5StateInfo(String nonce, String nonceCount)
-  {
-    this.nonce      = nonce;
-    this.nonceCount = nonceCount;
-  }
-
-
-
-  /**
-   * Retrieves the nonce generated by the server for this authentication
-   * session.
-   *
-   * @return  The nonce generated by the server for this authentication session.
-   */
-  public String getNonce()
-  {
-    return nonce;
-  }
-
-
-
-  /**
-   * Retrieves the hex string representation of the nonce count used by the last
-   * successful authentication.
-   *
-   * @return  The hex string representation of the nonce count used by the last
-   *          successful authentication.
-   */
-  public String getNonceCount()
-  {
-    return nonceCount;
-  }
-
-
-
-  /**
-   * Specifies the hex string representation of the nonce count used by the last
-   * successful authentication.
-   *
-   * @param  nonceCount  The hex string representation of the nonce count used
-   *                     by the last successful authentication.
-   */
-  public void setNonceCount(String nonceCount)
-  {
-    this.nonceCount = nonceCount;
-  }
-}
-
diff --git a/opends/src/server/org/opends/server/extensions/GSSAPISASLMechanismHandler.java b/opends/src/server/org/opends/server/extensions/GSSAPISASLMechanismHandler.java
index e45809b..6e3ff5f 100644
--- a/opends/src/server/org/opends/server/extensions/GSSAPISASLMechanismHandler.java
+++ b/opends/src/server/org/opends/server/extensions/GSSAPISASLMechanismHandler.java
@@ -32,11 +32,21 @@
 import java.io.BufferedWriter;
 import java.io.File;
 import java.io.FileWriter;
+import java.io.IOException;
 import java.net.InetAddress;
+import java.net.UnknownHostException;
 import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.List;
-
+import javax.security.auth.callback.Callback;
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.auth.callback.UnsupportedCallbackException;
+import javax.security.auth.login.LoginContext;
+import javax.security.auth.login.LoginException;
+import javax.security.sasl.Sasl;
+import javax.security.sasl.SaslException;
 import org.opends.server.admin.server.ConfigurationChangeListener;
+import org.opends.server.admin.std.meta.GSSAPISASLMechanismHandlerCfgDefn.*;
 import org.opends.server.admin.std.server.GSSAPISASLMechanismHandlerCfg;
 import org.opends.server.admin.std.server.SASLMechanismHandlerCfg;
 import org.opends.server.api.ClientConnection;
@@ -45,19 +55,17 @@
 import org.opends.server.config.ConfigException;
 import org.opends.server.core.BindOperation;
 import org.opends.server.core.DirectoryServer;
-import org.opends.server.types.AuthenticationInfo;
 import org.opends.server.types.ConfigChangeResult;
 import org.opends.server.types.DirectoryException;
 import org.opends.server.types.DN;
 import org.opends.server.types.Entry;
 import org.opends.server.types.InitializationException;
 import org.opends.server.types.ResultCode;
-
 import static org.opends.server.loggers.debug.DebugLogger.*;
 import org.opends.server.loggers.debug.DebugTracer;
 import org.opends.server.types.DebugLogLevel;
+import static org.opends.server.loggers.ErrorLogger.logError;
 import static org.opends.messages.ExtensionMessages.*;
-
 import static org.opends.server.util.ServerConstants.*;
 import static org.opends.server.util.StaticUtils.*;
 
@@ -69,28 +77,30 @@
  */
 public class GSSAPISASLMechanismHandler
        extends SASLMechanismHandler<GSSAPISASLMechanismHandlerCfg>
-       implements ConfigurationChangeListener<
-                       GSSAPISASLMechanismHandlerCfg>
-{
-  /**
-   * The tracer object for the debug logger.
-   */
+       implements ConfigurationChangeListener< GSSAPISASLMechanismHandlerCfg>,
+       CallbackHandler {
+
+  //The tracer object for the debug logger.
   private static final DebugTracer TRACER = getTracer();
 
   // The DN of the configuration entry for this SASL mechanism handler.
   private DN configEntryDN;
 
   // The current configuration for this SASL mechanism handler.
-  private GSSAPISASLMechanismHandlerCfg currentConfig;
+  private GSSAPISASLMechanismHandlerCfg configuration;
 
-  // The identity mapper that will be used to map the Kerberos principal to a
-  // directory user.
+  // The identity mapper that will be used to map identities.
   private IdentityMapper<?> identityMapper;
 
-  // The fully-qualified domain name for the server system.
+  //The properties to use when creating a SASL server to process the GSSAPI
+  //authentication.
+  private HashMap<String,String> saslProps;
+
+  //The fully qualified domain name used when creating the SASL server.
   private String serverFQDN;
 
-
+  //The login context used to perform server-side authentication.
+  private LoginContext loginContext;
 
   /**
    * Creates a new instance of this SASL mechanism handler.  No initialization
@@ -103,221 +113,237 @@
   }
 
 
-
   /**
    * {@inheritDoc}
    */
   @Override()
-  public void initializeSASLMechanismHandler(
-                   GSSAPISASLMechanismHandlerCfg configuration)
-         throws ConfigException, InitializationException
-  {
-    configuration.addGSSAPIChangeListener(this);
-
-    currentConfig = configuration;
-    configEntryDN = configuration.dn();
-
-
-    // Get the identity mapper that should be used to find users.
-    DN identityMapperDN = configuration.getIdentityMapperDN();
-    identityMapper = DirectoryServer.getIdentityMapper(identityMapperDN);
-
-
-    // Determine the fully-qualified hostname for this system.  It may be
-    // provided, but if not, then try to determine it programmatically.
-    serverFQDN = configuration.getServerFqdn();
-    if (serverFQDN == null)
-    {
-      try
-      {
-        serverFQDN = InetAddress.getLocalHost().getCanonicalHostName();
+  public void
+  initializeSASLMechanismHandler(GSSAPISASLMechanismHandlerCfg configuration)
+  throws ConfigException, InitializationException {
+      configuration.addGSSAPIChangeListener(this);
+      this.configuration = configuration;
+      configEntryDN = configuration.dn();
+      try {
+          DN identityMapperDN = configuration.getIdentityMapperDN();
+          identityMapper = DirectoryServer.getIdentityMapper(identityMapperDN);
+          serverFQDN = getFQDN(configuration);
+          Message msg= INFO_GSSAPI_SERVER_FQDN.get(serverFQDN);
+          logError(msg);
+          saslProps = new HashMap<String,String>();
+          saslProps.put(Sasl.QOP, getQOP(configuration));
+          saslProps.put(Sasl.REUSE, "false");
+          String configFileName=configureLoginConfFile(configuration);
+          System.setProperty(JAAS_PROPERTY_CONFIG_FILE, configFileName);
+          System.setProperty(JAAS_PROPERTY_SUBJECT_CREDS_ONLY, "false");
+          getKdcRealm(configuration);
+          DirectoryServer.registerSASLMechanismHandler(SASL_MECHANISM_GSSAPI,
+                  this);
+          login();
+      } catch (UnknownHostException unhe) {
+          if (debugEnabled()) {
+            TRACER.debugCaught(DebugLogLevel.ERROR, unhe);
+          }
+          Message message = ERR_SASL_CANNOT_GET_SERVER_FQDN.get(
+                  String.valueOf(configEntryDN), getExceptionMessage(unhe));
+          throw new InitializationException(message, unhe);
+      } catch(IOException ioe) {
+          if (debugEnabled()) {
+              TRACER.debugCaught(DebugLogLevel.ERROR, ioe);
+            }
+          Message message = ERR_SASLGSSAPI_CANNOT_CREATE_JAAS_CONFIG.get(
+                                                     getExceptionMessage(ioe));
+          throw new InitializationException(message, ioe);
+      } catch (LoginException le) {
+          if (debugEnabled()) {
+              TRACER.debugCaught(DebugLogLevel.ERROR, le);
+           }
+          Message message = ERR_SASLGSSAPI_CANNOT_CREATE_LOGIN_CONTEXT.get(
+                  getExceptionMessage(le));
+          throw new InitializationException(message, le);
       }
-      catch (Exception e)
-      {
-        if (debugEnabled())
-        {
-          TRACER.debugCaught(DebugLogLevel.ERROR, e);
-        }
+  }
 
-        Message message = ERR_SASLGSSAPI_CANNOT_GET_SERVER_FQDN.get(
-            String.valueOf(configEntryDN), getExceptionMessage(e));
-        throw new InitializationException(message, e);
+
+  /**
+   * Checks to make sure that the ds-cfg-kdc-address and dc-cfg-realm are
+   * both defined in the configuration. If only one is set, then that is an
+   * error. If both are defined, or, both are null that is fine.
+   *
+   * @param configuration The configuration to use.
+   * @throws InitializationException If the properties violate the requirements.
+   */
+  private void getKdcRealm(GSSAPISASLMechanismHandlerCfg configuration)
+  throws InitializationException {
+      String kdcAddress = configuration.getKdcAddress();
+      String realm = configuration.getRealm();
+      if((kdcAddress != null && realm == null) ||
+         (kdcAddress == null && realm != null)) {
+          Message message = ERR_SASLGSSAPI_KDC_REALM_NOT_DEFINED.get();
+          throw new InitializationException(message);
+      } else if(kdcAddress != null && realm != null) {
+          System.setProperty(KRBV_PROPERTY_KDC, kdcAddress);
+          System.setProperty(KRBV_PROPERTY_REALM, realm);
+
       }
-    }
+  }
 
 
-    // Since we're going to be using JAAS behind the scenes, we need to have a
-    // JAAS configuration.  Rather than always requiring the user to provide it,
-    // we'll write one to a temporary file that will be deleted when the JVM
-    // exits.
-    String configFileName;
-    try
-    {
+  /**
+   * During login, callbacks are usually used to prompt for passwords. All of
+   * the GSSAPI login information is provided in the properties and login.conf
+   * file, so callbacks are ignored.
+   *
+   * @param callbacks An array of callbacks to process.
+   * @throws UnsupportedCallbackException if an error occurs.
+   */
+  public void handle(Callback[] callbacks)
+  throws UnsupportedCallbackException {
+  }
+
+
+  /**
+   * Returns the fully qualified name either defined in the configuration, or,
+   * determined by examining the system configuration.
+   *
+   * @param configuration The configuration to check.
+   * @return The fully qualified hostname of the server.
+   *
+   * @throws UnknownHostException If the name cannot be determined from the
+   *                              system configuration.
+   */
+  private String getFQDN(GSSAPISASLMechanismHandlerCfg configuration)
+  throws UnknownHostException {
+      String serverName = configuration.getServerFqdn();
+      if (serverName == null) {
+              serverName = InetAddress.getLocalHost().getCanonicalHostName();
+      }
+      return serverName;
+  }
+
+
+  /**
+   * Create a login context or login using the principal and keytab information
+   * specified in the configuration.
+   *
+   * @throws LoginException If a login context cannot be created.
+   */
+  private void login() throws LoginException {
+      loginContext =
+          new LoginContext(GSSAPISASLMechanismHandler.class.getName(), this);
+      loginContext.login();
+  }
+
+
+  /**
+   * Logout of the current login context.
+   *
+   */
+  private void logout() {
+      try {
+          loginContext.logout();
+      } catch (LoginException e) {
+          if (debugEnabled()) {
+              TRACER.debugCaught(DebugLogLevel.ERROR, e);
+          }
+      }
+  }
+
+
+  /**
+   * Creates an login.conf file from information in the specified configuration.
+   * This file is used during the login phase.
+   *
+   * @param configuration The new configuration to use.
+   * @return The filename of the new configuration file.
+   *
+   * @throws IOException If the configuration file cannot be created.
+   */
+  private String
+  configureLoginConfFile(GSSAPISASLMechanismHandlerCfg configuration)
+  throws IOException {
+      String configFileName;
       File tempFile = File.createTempFile("login", "conf");
       configFileName = tempFile.getAbsolutePath();
       tempFile.deleteOnExit();
       BufferedWriter w = new BufferedWriter(new FileWriter(tempFile, false));
-
       w.write(getClass().getName() + " {");
       w.newLine();
-
       w.write("  com.sun.security.auth.module.Krb5LoginModule required " +
-              "storeKey=true useKeyTab=true ");
-
+      "storeKey=true useKeyTab=true ");
       String keyTabFile = configuration.getKeytab();
-      if (keyTabFile != null)
-      {
-        w.write("keyTab=\"" + keyTabFile + "\" ");
+      if (keyTabFile != null) {
+          w.write("keyTab=\"" + keyTabFile + "\" ");
       }
-
-      // FIXME -- Should we add the ability to include "debug=true"?
-
-      // FIXME -- Can we get away from hard-coding a protocol here?
-      w.write("principal=\"ldap/" + serverFQDN);
-
+      StringBuilder principal= new StringBuilder();
+      String principalName = configuration.getPrincipalName();
       String realm = configuration.getRealm();
-      if (realm != null)
-      {
-        w.write("@" + realm);
+      if(principalName != null) {
+          principal.append("principal=\"" + principalName);
+      } else {
+          principal.append("principal=\"ldap/" + serverFQDN);
       }
+      if (realm != null) {
+          principal.append("@" + realm);
+      }
+      w.write(principal.toString());
+      Message msg =  INFO_GSSAPI_PRINCIPAL_NAME.get(principal.toString());
+      logError(msg);
       w.write("\";");
-
       w.newLine();
-
       w.write("};");
       w.newLine();
-
       w.flush();
       w.close();
-    }
-    catch (Exception e)
-    {
-      if (debugEnabled())
-      {
-        TRACER.debugCaught(DebugLogLevel.ERROR, e);
-      }
-
-      Message message =
-          ERR_SASLGSSAPI_CANNOT_CREATE_JAAS_CONFIG.get(getExceptionMessage(e));
-      throw new InitializationException(message, e);
-    }
-
-    System.setProperty(JAAS_PROPERTY_CONFIG_FILE, configFileName);
-    System.setProperty(JAAS_PROPERTY_SUBJECT_CREDS_ONLY, "false");
-
-
-    DirectoryServer.registerSASLMechanismHandler(SASL_MECHANISM_GSSAPI, this);
+      return configFileName;
   }
 
 
-
   /**
    * {@inheritDoc}
    */
   @Override()
-  public void finalizeSASLMechanismHandler()
-  {
-    currentConfig.removeGSSAPIChangeListener(this);
-    DirectoryServer.deregisterSASLMechanismHandler(SASL_MECHANISM_GSSAPI);
+  public void finalizeSASLMechanismHandler() {
+      logout();
+      configuration.removeGSSAPIChangeListener(this);
+      DirectoryServer.deregisterSASLMechanismHandler(SASL_MECHANISM_GSSAPI);
   }
 
 
-
-
   /**
    * {@inheritDoc}
    */
   @Override()
-  public void processSASLBind(BindOperation bindOperation)
-  {
-    // GSSAPI binds use multiple stages, so we need to determine whether this is
-    // the first stage or a subsequent one.  To do that, see if we have SASL
-    // state information in the client connection.
-    ClientConnection clientConnection = bindOperation.getClientConnection();
-    if (clientConnection == null)
-    {
-      Message message = ERR_SASLGSSAPI_NO_CLIENT_CONNECTION.get();
-
-      bindOperation.setAuthFailureReason(message);
-      bindOperation.setResultCode(ResultCode.INVALID_CREDENTIALS);
-      return;
-    }
-
-    GSSAPIStateInfo stateInfo = null;
-    Object saslBindState = clientConnection.getSASLAuthStateInfo();
-    if ((saslBindState != null) && (saslBindState instanceof GSSAPIStateInfo))
-    {
-      stateInfo = (GSSAPIStateInfo) saslBindState;
-    }
-    else
-    {
-      try
-      {
-        stateInfo = new GSSAPIStateInfo(this, bindOperation, serverFQDN);
+  public void processSASLBind(BindOperation bindOp) {
+      ClientConnection clientConnection = bindOp.getClientConnection();
+      if (clientConnection == null) {
+          Message message = ERR_SASLGSSAPI_NO_CLIENT_CONNECTION.get();
+          bindOp.setAuthFailureReason(message);
+          bindOp.setResultCode(ResultCode.INVALID_CREDENTIALS);
+          return;
       }
-      catch (InitializationException ie)
-      {
-        if (debugEnabled())
-        {
-          TRACER.debugCaught(DebugLogLevel.ERROR, ie);
-        }
-
-        bindOperation.setAuthFailureReason(ie.getMessageObject());
-        bindOperation.setResultCode(ResultCode.INVALID_CREDENTIALS);
-        clientConnection.setSASLAuthStateInfo(null);
-        return;
+      ClientConnection clientConn  = bindOp.getClientConnection();
+      SASLContext saslContext = (SASLContext) clientConn.getSASLAuthStateInfo();
+      if(saslContext == null) {
+          try {
+              saslContext = SASLContext.createSASLContext(saslProps, serverFQDN,
+                                        SASL_MECHANISM_GSSAPI, identityMapper);
+          } catch (SaslException ex) {
+              if (debugEnabled()) {
+                  TRACER.debugCaught(DebugLogLevel.ERROR, ex);
+               }
+              Message msg =
+                  ERR_SASL_CONTEXT_CREATE_ERROR.get(SASL_MECHANISM_GSSAPI,
+                                                    getExceptionMessage(ex));
+              clientConn.setSASLAuthStateInfo(null);
+              bindOp.setAuthFailureReason(msg);
+              bindOp.setResultCode(ResultCode.INVALID_CREDENTIALS);
+              return;
+          }
       }
-    }
-
-    stateInfo.setBindOperation(bindOperation);
-    stateInfo.processAuthenticationStage();
-
-
-    if (bindOperation.getResultCode() == ResultCode.SUCCESS)
-    {
-      // The authentication was successful, so set the proper state information
-      // in the client connection and return success.
-      Entry userEntry = stateInfo.getUserEntry();
-      AuthenticationInfo authInfo =
-           new AuthenticationInfo(userEntry, SASL_MECHANISM_GSSAPI,
-                                  DirectoryServer.isRootDN(userEntry.getDN()));
-      bindOperation.setAuthenticationInfo(authInfo);
-      bindOperation.setResultCode(ResultCode.SUCCESS);
-
-      // FIXME -- If we're using integrity or confidentiality, then we can't do
-      // this.
-      clientConnection.setSASLAuthStateInfo(null);
-
-      try
-      {
-        stateInfo.dispose();
-      }
-      catch (Exception e)
-      {
-        if (debugEnabled())
-        {
-          TRACER.debugCaught(DebugLogLevel.ERROR, e);
-        }
-      }
-    }
-    else if (bindOperation.getResultCode() == ResultCode.SASL_BIND_IN_PROGRESS)
-    {
-      // We need to store the SASL auth state with the client connection so we
-      // can resume authentication the next time around.
-      clientConnection.setSASLAuthStateInfo(stateInfo);
-    }
-    else
-    {
-      // The authentication failed.  We don't want to keep the SASL state
-      // around.
-      // FIXME -- Are there other result codes that we need to check for and
-      //          preserve the auth state?
-      clientConnection.setSASLAuthStateInfo(null);
-    }
+      saslContext.performAuthentication(loginContext, bindOp);
   }
 
 
-
   /**
    * Retrieves the user account for the user associated with the provided
    * authorization ID.
@@ -328,7 +354,7 @@
    *                        associated user.
    *
    * @return  The user entry for the user with the specified authorization ID,
-   *          or <CODE>null</CODE> if none is identified.
+   *          or {@code null} if none is identified.
    *
    * @throws  DirectoryException  If a problem occurs while searching the
    *                              directory for the associated user, or if
@@ -392,119 +418,41 @@
   }
 
 
-
   /**
    * {@inheritDoc}
    */
   public ConfigChangeResult applyConfigurationChange(
-              GSSAPISASLMechanismHandlerCfg configuration)
-  {
-    ResultCode        resultCode          = ResultCode.SUCCESS;
-    boolean           adminActionRequired = false;
-    ArrayList<Message> messages            = new ArrayList<Message>();
-
-
-    // Get the identity mapper that should be used to find users.
-    DN identityMapperDN = configuration.getIdentityMapperDN();
-    IdentityMapper<?> newIdentityMapper =
-         DirectoryServer.getIdentityMapper(identityMapperDN);
-
-
-    // Determine the fully-qualified hostname for this system.  It may be
-    // provided, but if not, then try to determine it programmatically.
-    String newFQDN = configuration.getServerFqdn();
-    if (newFQDN == null)
-    {
-      try
-      {
-        newFQDN = InetAddress.getLocalHost().getCanonicalHostName();
-      }
-      catch (Exception e)
-      {
-        if (debugEnabled())
-        {
-          TRACER.debugCaught(DebugLogLevel.ERROR, e);
-        }
-
-        if (resultCode == ResultCode.SUCCESS)
-        {
-          resultCode = DirectoryServer.getServerErrorResultCode();
-        }
-
-
-        messages.add(ERR_SASLGSSAPI_CANNOT_GET_SERVER_FQDN.get(
-                String.valueOf(configEntryDN),
-                getExceptionMessage(e)));
-      }
-    }
-
-
-    if (resultCode == ResultCode.SUCCESS)
-    {
-      String configFileName;
-      try
-      {
-        File tempFile = File.createTempFile("login", "conf");
-        configFileName = tempFile.getAbsolutePath();
-        tempFile.deleteOnExit();
-        BufferedWriter w = new BufferedWriter(new FileWriter(tempFile, false));
-
-        w.write(getClass().getName() + " {");
-        w.newLine();
-
-        w.write("  com.sun.security.auth.module.Krb5LoginModule required " +
-                "storeKey=true useKeyTab=true ");
-
-        String keyTabFile = configuration.getKeytab();
-        if (keyTabFile != null)
-        {
-          w.write("keyTab=\"" + keyTabFile + "\" ");
-        }
-
-        // FIXME -- Should we add the ability to include "debug=true"?
-
-        // FIXME -- Can we get away from hard-coding a protocol here?
-        w.write("principal=\"ldap/" + serverFQDN);
-
-        String realm = configuration.getRealm();
-        if (realm != null)
-        {
-          w.write("@" + realm);
-        }
-        w.write("\";");
-
-        w.newLine();
-
-        w.write("};");
-        w.newLine();
-
-        w.flush();
-        w.close();
-      }
-      catch (Exception e)
-      {
-        if (debugEnabled())
-        {
-          TRACER.debugCaught(DebugLogLevel.ERROR, e);
-        }
-
-        resultCode = DirectoryServer.getServerErrorResultCode();
-
-        messages.add(ERR_SASLGSSAPI_CANNOT_CREATE_JAAS_CONFIG.get(
-                getExceptionMessage(e)));
-
-       return new ConfigChangeResult(resultCode, adminActionRequired, messages);
-      }
-
-      System.setProperty(JAAS_PROPERTY_CONFIG_FILE, configFileName);
-
+          GSSAPISASLMechanismHandlerCfg configuration) {
+      ResultCode        resultCode          = ResultCode.SUCCESS;
+      boolean           adminActionRequired = false;
+      ArrayList<Message> messages            = new ArrayList<Message>();
+      DN identityMapperDN = configuration.getIdentityMapperDN();
+      IdentityMapper<?> newIdentityMapper =
+          DirectoryServer.getIdentityMapper(identityMapperDN);
       identityMapper = newIdentityMapper;
-      serverFQDN     = newFQDN;
-      currentConfig  = configuration;
-    }
+      saslProps = new HashMap<String,String>();
+      saslProps.put(Sasl.QOP, getQOP(configuration));
+      saslProps.put(Sasl.REUSE, "false");
+      this.configuration  = configuration;
+      return new ConfigChangeResult(resultCode, adminActionRequired, messages);
+  }
 
 
-   return new ConfigChangeResult(resultCode, adminActionRequired, messages);
+  /**
+   * Retrieves the QOP (quality-of-protection) from the specified
+   * configuration.
+   *
+   * @param configuration The new configuration to use.
+   * @return A string representing the quality-of-protection.
+   */
+  private String
+  getQOP(GSSAPISASLMechanismHandlerCfg configuration) {
+      QualityOfProtection QOP = configuration.getQualityOfProtection();
+      if(QOP.equals(QualityOfProtection.CONFIDENTIALITY))
+          return "auth-conf";
+      else if(QOP.equals(QualityOfProtection.INTEGRITY))
+          return "auth-int";
+      else
+          return "auth";
   }
 }
-
diff --git a/opends/src/server/org/opends/server/extensions/GSSAPIStateInfo.java b/opends/src/server/org/opends/server/extensions/GSSAPIStateInfo.java
deleted file mode 100644
index ff34857..0000000
--- a/opends/src/server/org/opends/server/extensions/GSSAPIStateInfo.java
+++ /dev/null
@@ -1,531 +0,0 @@
-/*
- * CDDL HEADER START
- *
- * The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License").  You may not use this file except in compliance
- * with the License.
- *
- * You can obtain a copy of the license at
- * trunk/opends/resource/legal-notices/OpenDS.LICENSE
- * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
- * See the License for the specific language governing permissions
- * and limitations under the License.
- *
- * When distributing Covered Code, include this CDDL HEADER in each
- * file and include the License file at
- * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
- * add the following below this CDDL HEADER, with the fields enclosed
- * by brackets "[]" replaced with your own identifying information:
- *      Portions Copyright [yyyy] [name of copyright owner]
- *
- * CDDL HEADER END
- *
- *
- *      Copyright 2006-2008 Sun Microsystems, Inc.
- */
-package org.opends.server.extensions;
-import org.opends.messages.Message;
-
-
-
-import java.security.PrivilegedExceptionAction;
-import java.util.HashMap;
-import javax.security.auth.Subject;
-import javax.security.auth.callback.Callback;
-import javax.security.auth.callback.CallbackHandler;
-import javax.security.auth.callback.NameCallback;
-import javax.security.auth.callback.UnsupportedCallbackException;
-import javax.security.auth.login.LoginContext;
-import javax.security.sasl.AuthorizeCallback;
-import javax.security.sasl.Sasl;
-import javax.security.sasl.SaslServer;
-
-import org.opends.server.api.ClientConnection;
-import org.opends.server.core.BindOperation;
-import org.opends.server.core.DirectoryServer;
-import org.opends.server.protocols.asn1.ASN1OctetString;
-import org.opends.server.types.AuthenticationInfo;
-import org.opends.server.types.ByteString;
-import org.opends.server.types.DirectoryException;
-import org.opends.server.types.Entry;
-import org.opends.server.types.InitializationException;
-import org.opends.server.types.ResultCode;
-
-import static org.opends.server.loggers.debug.DebugLogger.*;
-import org.opends.server.loggers.debug.DebugTracer;
-import org.opends.server.types.DebugLogLevel;
-import static org.opends.messages.ExtensionMessages.*;
-import static org.opends.server.util.ServerConstants.*;
-import static org.opends.server.util.StaticUtils.*;
-
-
-
-/**
- * This class defines a data structure that holds state information needed for
- * processing a SASL GSSAPI bind from a client.
- */
-public class GSSAPIStateInfo
-       implements PrivilegedExceptionAction<Boolean>, CallbackHandler
-{
-  /**
-   * The tracer object for the debug logger.
-   */
-  private static final DebugTracer TRACER = getTracer();
-
-
-
-
-  // The bind operation with which this state is associated.
-  private BindOperation bindOperation;
-
-  // The client connection with which this state is associated.
-  private ClientConnection clientConnection;
-
-  // The entry of the user that authenticated in this session.
-  private Entry userEntry;
-
-  // The GSSAPI authentication handler that created this state information.
-  private GSSAPISASLMechanismHandler gssapiHandler;
-
-  // The login context used to perform server-side authentication.
-  private LoginContext loginContext;
-
-  // The SASL server that will be used to actually perform the authentication.
-  private SaslServer saslServer;
-
-  // The protocol that the client is using to communicate with the server.
-  private String protocol;
-
-  // The FQDN of this system to use in the authentication process.
-  private String serverFQDN;
-
-
-
-
-  /**
-   * Creates a new GSSAPI state info structure with the provided information.
-   *
-   * @param  gssapiHandler  The GSSAPI authentication handler that created this
-   *                        state information.
-   * @param  bindOperation  The bind operation with which this state is
-   *                        associated.
-   * @param  serverFQDN     The fully-qualified domain name for the server to
-   *                        use in the authentication process.
-   *
-   * @throws  InitializationException  If it is not possible to authenticate to
-   *                                   the KDC to verify the client credentials.
-   */
-  public GSSAPIStateInfo(GSSAPISASLMechanismHandler gssapiHandler,
-                         BindOperation bindOperation, String serverFQDN)
-         throws InitializationException
-  {
-    this.gssapiHandler = gssapiHandler;
-    this.bindOperation = bindOperation;
-    this.serverFQDN    = serverFQDN;
-
-    clientConnection = bindOperation.getClientConnection();
-    protocol         = toLowerCase(clientConnection.getProtocol());
-    userEntry        = null;
-
-
-    // Create the LoginContext and do the server-side authentication.
-    // FIXME -- Can this be moved to a one-time call in the GSSAPI handler
-    //          rather than once per GSSAPI bind attempt?
-    try
-    {
-      loginContext =
-           new LoginContext(GSSAPISASLMechanismHandler.class.getName(), this);
-    }
-    catch (Exception e)
-    {
-      if (debugEnabled())
-      {
-        TRACER.debugCaught(DebugLogLevel.ERROR, e);
-      }
-
-      Message message = ERR_SASLGSSAPI_CANNOT_CREATE_LOGIN_CONTEXT.get(
-          getExceptionMessage(e));
-      throw new InitializationException(message, e);
-    }
-
-    try
-    {
-      loginContext.login();
-    }
-    catch (Exception e)
-    {
-      if (debugEnabled())
-      {
-        TRACER.debugCaught(DebugLogLevel.ERROR, e);
-      }
-
-      Message message =
-          ERR_SASLGSSAPI_CANNOT_AUTHENTICATE_SERVER.get(getExceptionMessage(e));
-      throw new InitializationException(message, e);
-    }
-
-
-    saslServer = null;
-  }
-
-
-
-  /**
-   * Sets the bind operation for the next stage of processing in the GSSAPI
-   * authentication.  This must be called before the processing is performed so
-   * that the appropriate response may be sent to the client.
-   *
-   * @param  bindOperation  The bind operation for the next stage of processing
-   *                        in the GSSAPI authentication.
-   */
-  public void setBindOperation(BindOperation bindOperation)
-  {
-    this.bindOperation = bindOperation;
-  }
-
-
-
-  /**
-   * Retrieves the entry of the user that has authenticated on this GSSAPI
-   * session.  This should only be available after a successful GSSAPI
-   * authentication.  The return value of this method should be considered
-   * unreliable if GSSAPI authentication has not yet completed successfully.
-   *
-   * @return  x
-   */
-  public Entry getUserEntry()
-  {
-    return userEntry;
-  }
-
-
-
-  /**
-   * Destroys any sensitive information that might be associated with the SASL
-   * server instance.
-   */
-  public void dispose()
-  {
-    try
-    {
-      saslServer.dispose();
-    }
-    catch (Exception e)
-    {
-      if (debugEnabled())
-      {
-        TRACER.debugCaught(DebugLogLevel.ERROR, e);
-      }
-    }
-  }
-
-
-
-  /**
-   * Processes the next stage of the GSSAPI bind process.  This may be used for
-   * the first stage or any stage thereafter until the authentication is
-   * complete.  It will automatically take care of the JAAS processing behind
-   * the scenes as necessary.
-   */
-  public void processAuthenticationStage()
-  {
-    try
-    {
-      Subject.doAs(loginContext.getSubject(), this);
-    }
-    catch (Exception e)
-    {
-      if (debugEnabled())
-      {
-        TRACER.debugCaught(DebugLogLevel.ERROR, e);
-      }
-    }
-  }
-
-
-
-  /**
-   * Processes a stage of the SASL GSSAPI bind request.  The
-   * <CODE>setBindOperation</CODE> method must have been called to update the
-   * reference to the latest bind request before invoking this method through
-   * <CODE>doAs</CODE> or <CODE>doAsPrivileged</CODE>.
-   *
-   * @return  <CODE>true</CODE> if there was no error during this stage of the
-   *          bind and processing can continue, or <CODE>false</CODE> if an
-   *          error occurred and and processing should not continue.
-   */
-  public Boolean run()
-  {
-    if (saslServer == null)
-    {
-      // Create the SASL server instance for use with this authentication
-      // attempt.
-      try
-      {
-        HashMap<String,String> saslProperties = new HashMap<String,String>();
-
-        // FIXME -- We need to add support for auth-int and auth-conf.
-        // propertyMap.put(Sasl.QOP, "auth,auth-int,auth-conf");
-        saslProperties.put(Sasl.QOP, "auth");
-
-        saslProperties.put(Sasl.REUSE, "false");
-
-        saslServer = Sasl.createSaslServer(SASL_MECHANISM_GSSAPI, "ldap",
-                                           serverFQDN, saslProperties, this);
-      }
-      catch (Exception e)
-      {
-        if (debugEnabled())
-        {
-          TRACER.debugCaught(DebugLogLevel.ERROR, e);
-        }
-
-        Message message = ERR_SASLGSSAPI_CANNOT_CREATE_SASL_SERVER.get(
-                getExceptionMessage(e));
-
-        clientConnection.setSASLAuthStateInfo(null);
-        bindOperation.setAuthFailureReason(message);
-        bindOperation.setResultCode(ResultCode.INVALID_CREDENTIALS);
-        return false;
-      }
-    }
-
-
-    // Get the SASL credentials from the bind request.
-    byte[] clientCredBytes;
-    ByteString clientCredentials = bindOperation.getSASLCredentials();
-    if (clientCredentials == null)
-    {
-      clientCredBytes = new byte[0];
-    }
-    else
-    {
-      clientCredBytes = clientCredentials.value();
-    }
-
-
-    // Process the client SASL credentials and get the data to include in the
-    // server SASL credentials of the response.
-    ASN1OctetString serverSASLCredentials;
-    try
-    {
-      byte[] serverCredBytes = saslServer.evaluateResponse(clientCredBytes);
-
-      if (serverCredBytes == null)
-      {
-        serverSASLCredentials = null;
-      }
-      else
-      {
-        serverSASLCredentials = new ASN1OctetString(serverCredBytes);
-      }
-    }
-    catch (Exception e)
-    {
-      if (debugEnabled())
-      {
-        TRACER.debugCaught(DebugLogLevel.ERROR, e);
-      }
-
-      try
-      {
-        saslServer.dispose();
-      }
-      catch (Exception e2)
-      {
-        if (debugEnabled())
-        {
-          TRACER.debugCaught(DebugLogLevel.ERROR, e2);
-        }
-      }
-
-      Message message = ERR_SASLGSSAPI_CANNOT_EVALUATE_RESPONSE.get(
-              getExceptionMessage(e));
-
-      clientConnection.setSASLAuthStateInfo(null);
-      bindOperation.setAuthFailureReason(message);
-      bindOperation.setResultCode(ResultCode.INVALID_CREDENTIALS);
-      return false;
-    }
-
-
-    // If the authentication is not yet complete, then send a "SASL bind in
-    // progress" response to the client.
-    if (! saslServer.isComplete())
-    {
-      clientConnection.setSASLAuthStateInfo(saslServer);
-      bindOperation.setResultCode(ResultCode.SASL_BIND_IN_PROGRESS);
-      bindOperation.setServerSASLCredentials(serverSASLCredentials);
-      return true;
-    }
-
-
-    // If the authentication is complete, then get the authorization ID from the
-    // SASL server and map that to a user in the directory.
-    String authzID = saslServer.getAuthorizationID();
-    if ((authzID == null) || (authzID.length() == 0))
-    {
-      try
-      {
-        saslServer.dispose();
-      }
-      catch (Exception e)
-      {
-        if (debugEnabled())
-        {
-          TRACER.debugCaught(DebugLogLevel.ERROR, e);
-        }
-      }
-
-      Message message = ERR_SASLGSSAPI_NO_AUTHZ_ID.get();
-
-      clientConnection.setSASLAuthStateInfo(null);
-      bindOperation.setAuthFailureReason(message);
-      bindOperation.setResultCode(ResultCode.INVALID_CREDENTIALS);
-      return false;
-    }
-
-
-    try
-    {
-      userEntry = gssapiHandler.getUserForAuthzID(bindOperation, authzID);
-    }
-    catch (DirectoryException de)
-    {
-      if (debugEnabled())
-      {
-        TRACER.debugCaught(DebugLogLevel.ERROR, de);
-      }
-
-      try
-      {
-        saslServer.dispose();
-      }
-      catch (Exception e)
-      {
-        if (debugEnabled())
-        {
-          TRACER.debugCaught(DebugLogLevel.ERROR, e);
-        }
-      }
-
-      bindOperation.setResultCode(ResultCode.INVALID_CREDENTIALS);
-      bindOperation.setAuthFailureReason(de.getMessageObject());
-      clientConnection.setSASLAuthStateInfo(null);
-      return false;
-    }
-
-
-    // If the user entry is null, then we couldn't map the authorization ID to
-    // a user.
-    if (userEntry == null)
-    {
-      try
-      {
-        saslServer.dispose();
-      }
-      catch (Exception e)
-      {
-        if (debugEnabled())
-        {
-          TRACER.debugCaught(DebugLogLevel.ERROR, e);
-        }
-      }
-
-      Message message = ERR_SASLGSSAPI_CANNOT_MAP_AUTHZID.get(authzID);
-
-      clientConnection.setSASLAuthStateInfo(null);
-      bindOperation.setAuthFailureReason(message);
-      bindOperation.setResultCode(ResultCode.INVALID_CREDENTIALS);
-      return false;
-    }
-    else
-    {
-      bindOperation.setSASLAuthUserEntry(userEntry);
-    }
-
-
-    // The authentication was successful, so set the proper state information
-    // in the client connection and return success.
-    AuthenticationInfo authInfo =
-         new AuthenticationInfo(userEntry, SASL_MECHANISM_GSSAPI,
-                                DirectoryServer.isRootDN(userEntry.getDN()));
-    bindOperation.setAuthenticationInfo(authInfo);
-    bindOperation.setResultCode(ResultCode.SUCCESS);
-
-    // FIXME -- If we're using integrity or confidentiality, then we can't do
-    // this.
-    clientConnection.setSASLAuthStateInfo(null);
-    try
-    {
-      saslServer.dispose();
-    }
-    catch (Exception e)
-    {
-      if (debugEnabled())
-      {
-        TRACER.debugCaught(DebugLogLevel.ERROR, e);
-      }
-    }
-
-    return true;
-  }
-
-
-
-  /**
-   * Handles any callbacks that might be required in order to process a SASL
-   * GSSAPI bind on the server.  In this case, if an authorization ID was
-   * provided, then a callback may be used to determine whether it is
-   * acceptable.
-   *
-   * @param  callbacks  The callbacks needed to provide information for the
-   *                    GSSAPI authentication process.
-   *
-   * @throws  UnsupportedCallbackException  If an unexpected callback is
-   *                                        included in the provided set.
-   */
-  public void handle(Callback[] callbacks)
-         throws UnsupportedCallbackException
-  {
-    for (Callback callback : callbacks)
-    {
-      if (callback instanceof NameCallback)
-      {
-        String authID = toLowerCase(clientConnection.getProtocol()) + "/" +
-                        serverFQDN;
-        ((NameCallback) callback).setName(authID);
-      }
-      else if (callback instanceof AuthorizeCallback)
-      {
-        // FIXME -- Should we allow an authzID different from the authID?
-        // FIXME -- Do we need to do anything else here?
-        AuthorizeCallback authzCallback = (AuthorizeCallback) callback;
-        String authID  = authzCallback.getAuthenticationID();
-        String authzID = authzCallback.getAuthorizationID();
-
-        if (authID.equals(authzID))
-        {
-          authzCallback.setAuthorizedID(authzID);
-          authzCallback.setAuthorized(true);
-        }
-        else
-        {
-          Message message = ERR_SASLGSSAPI_DIFFERENT_AUTHID_AND_AUTHZID.get(
-                  authID, authzID);
-          bindOperation.setAuthFailureReason(message);
-          authzCallback.setAuthorized(false);
-        }
-      }
-      else
-      {
-        // We weren't prepared for this type of callback.
-        Message message =
-            INFO_SASLGSSAPI_UNEXPECTED_CALLBACK.get(String.valueOf(callback));
-        throw new UnsupportedCallbackException(callback, message.toString());
-      }
-    }
-  }
-}
-
diff --git a/opends/src/server/org/opends/server/extensions/NullConnectionSecurityProvider.java b/opends/src/server/org/opends/server/extensions/NullConnectionSecurityProvider.java
index 0564cad..0aa9ae0 100644
--- a/opends/src/server/org/opends/server/extensions/NullConnectionSecurityProvider.java
+++ b/opends/src/server/org/opends/server/extensions/NullConnectionSecurityProvider.java
@@ -173,6 +173,15 @@
   /**
    * {@inheritDoc}
    */
+  @Override
+  public boolean isActive() {
+      //This provider is always active.
+      return true;
+  }
+
+  /**
+   * {@inheritDoc}
+   */
   @Override()
   public ConnectionSecurityProvider newInstance(ClientConnection
                                                       clientConnection,
diff --git a/opends/src/server/org/opends/server/extensions/SASLContext.java b/opends/src/server/org/opends/server/extensions/SASLContext.java
new file mode 100644
index 0000000..2ccc27f
--- /dev/null
+++ b/opends/src/server/org/opends/server/extensions/SASLContext.java
@@ -0,0 +1,894 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License").  You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE
+ * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
+ * add the following below this CDDL HEADER, with the fields enclosed
+ * by brackets "[]" replaced with your own identifying information:
+ *      Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ *
+ *      Copyright 2008 Sun Microsystems, Inc.
+ */
+
+package org.opends.server.extensions;
+
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+import java.util.HashMap;
+import java.util.List;
+import java.util.concurrent.locks.Lock;
+import javax.security.auth.Subject;
+import javax.security.auth.callback.Callback;
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.auth.callback.NameCallback;
+import javax.security.auth.callback.PasswordCallback;
+import javax.security.auth.callback.UnsupportedCallbackException;
+import javax.security.auth.login.LoginContext;
+import javax.security.sasl.AuthorizeCallback;
+import javax.security.sasl.RealmCallback;
+import javax.security.sasl.Sasl;
+import javax.security.sasl.SaslException;
+import javax.security.sasl.SaslServer;
+import org.opends.server.loggers.debug.DebugTracer;
+import org.opends.messages.Message;
+import org.opends.server.api.ClientConnection;
+import org.opends.server.api.IdentityMapper;
+import org.opends.server.core.AccessControlConfigManager;
+import org.opends.server.core.BindOperation;
+import org.opends.server.core.DirectoryServer;
+import org.opends.server.core.PasswordPolicyState;
+import org.opends.server.protocols.asn1.ASN1OctetString;
+import org.opends.server.protocols.internal.InternalClientConnection;
+import org.opends.server.protocols.ldap.LDAPClientConnection;
+import org.opends.server.types.*;
+import static org.opends.messages.ExtensionMessages.*;
+import static org.opends.server.loggers.debug.DebugLogger.*;
+import static org.opends.server.util.ServerConstants.*;
+import static org.opends.server.util.StaticUtils.*;
+
+/**
+ * This class defines the SASL context needed to process GSSAPI and DIGEST-MD5
+ * bind requests from clients.
+ *
+ */
+public class
+SASLContext implements CallbackHandler, PrivilegedExceptionAction<Boolean> {
+
+
+    // The tracer object for the debug logger.
+    private static final DebugTracer TRACER = getTracer();
+
+    // The SASL server to use in the authentication.
+    private SaslServer saslServer=null;
+
+    // The identity mapper to use when mapping identities.
+    private final IdentityMapper<?> identityMapper;
+
+    //The  property set to use when creating the SASL server.
+    private HashMap<String, String>saslProps;
+
+    //The fully qualified domain name to use when creating the SASL server.
+    private String serverFQDN;
+
+    //The SASL mechanism name.
+    private final String mech;
+
+    //The authorization entry used in the authentication.
+    private Entry authEntry=null;
+
+    //The authorization entry used in the authentication.
+    private Entry authzEntry=null;
+
+    //The user name used in the authentication taken from the name callback.
+    private String userName;
+
+    //Error message used by callbacks.
+    private Message cbMsg;
+
+    //The current bind operation used by the callbacks.
+    private BindOperation bindOp;
+
+    //Used to check if negotiated QOP is confidentiality or integrity.
+    private final String confidentiality = "auth-conf";
+    private final String integrity = "auth-int";
+
+
+    /**
+     * Create a SASL context using the specified parameters. A SASL server will
+     * be instantiated only for the DIGEST-MD5 mechanism. The GSSAPI mechanism
+     * must instantiate the SASL server as the login context in a separate step.
+     *
+     * @param saslProps The properties to use in creating the SASL server.
+     * @param serverFQDN The fully qualified domain name to use in creating the
+     *                   SASL server.
+     * @param mech The SASL mechanism name.
+     * @param identityMapper The identity mapper to use in mapping identities.
+     *
+     * @throws SaslException If the SASL server can not be instantiated.
+     */
+    private SASLContext(HashMap<String, String>saslProps, String serverFQDN,
+                          String mech, IdentityMapper<?> identityMapper)
+                          throws SaslException {
+        this.identityMapper = identityMapper;
+        this.mech = mech;
+        this.saslProps = saslProps;
+        this.serverFQDN = serverFQDN;
+        if(mech.equals(SASL_MECHANISM_DIGEST_MD5)) {
+            initSASLServer();
+        }
+    }
+
+
+    /**
+     * Instantiate a GSSAPI/DIGEST-MD5 SASL context using the specified
+     * parameters.
+     *
+     * @param saslProps The properties to use in creating the SASL server.
+     * @param serverFQDN The fully qualified domain name to use in creating the
+     *                   SASL server.
+     * @param mech The SASL mechanism name.
+     * @param identityMapper The identity mapper to use in mapping identities.
+     * @return A fully instantiated SASL context to use in processing a SASL
+     *         bind for the GSSAPI or DIGEST-MD5 mechanisms.
+     *
+     * @throws SaslException If the SASL server can not be instantiated.
+     */
+    public static
+    SASLContext createSASLContext(HashMap<String,String>saslProps,
+                        String serverFQDN, String mech,
+                        IdentityMapper<?> identityMapper) throws SaslException {
+        return (new SASLContext(saslProps,serverFQDN, mech, identityMapper));
+    }
+
+
+    /**
+     * Initialize the SASL server using parameters specified in the
+     * constructor.
+     *
+     * @throws SaslException If the SASL server can not be instantiated.
+     */
+    private void initSASLServer() throws SaslException {
+        this.saslServer = Sasl.createSaslServer(mech, SASL_DEFAULT_PROTOCOL,
+                                                serverFQDN, saslProps, this);
+    }
+
+
+    /**
+     * Wrap the specified clear byte array using the provided offset and length
+     * values. Used only when the SASL server has negotiated
+     * confidentiality/integrity  processing.
+     *
+     * @param clearBytes The clear byte array to wrap.
+     * @param offset The offset into the clear byte array..
+     * @param len The length from the offset of the number of bytes to wrap.
+     * @return A byte array containing the wrapped bytes.
+     *
+     * @throws SaslException If the clear bytes cannot be wrapped.
+     */
+    byte[] wrap(byte[] clearBytes, int offset, int len)
+    throws SaslException {
+        return saslServer.wrap(clearBytes, offset, len);
+    }
+
+
+    /**
+     * Unwrap the specified byte array using the provided offset and length
+     * values. Used only when the SASL server has negotiated
+     * confidentiality/integrity  processing.
+     *
+     * @param bytes The byte array to unwrap.
+     * @param offset The offset in the array.
+     * @param len The length from the offset of the number of bytes to unwrap.
+     * @return A byte array containing the clear or unwrapped bytes.
+     *
+     * @throws SaslException If the bytes cannot be unwrapped.
+     */
+    byte[] unwrap(byte[] bytes, int offset, int len)
+    throws SaslException {
+        return saslServer.unwrap(bytes, offset, len);
+    }
+
+
+    /**
+     * Dispose of the SASL server instance.
+     */
+    void dispose() {
+        try {
+            saslServer.dispose();
+        } catch (SaslException e) {
+            if (debugEnabled()) {
+                TRACER.debugCaught(DebugLogLevel.ERROR, e);
+              }
+        }
+    }
+
+
+    /**
+     * Return the negotiated buffer size.
+     *
+     * @param prop The buffer size property to return.
+     * @return The value of the negotiated buffer size.
+     */
+    int getBufSize(String prop) {
+          String sizeStr =
+              (String) saslServer.getNegotiatedProperty(prop);
+          return Integer.parseInt(sizeStr);
+    }
+
+
+    /**
+     * Return true if the bind has been completed. If the context is supporting
+     * confidentiality/integrity, the security provider will need to check
+     * if the context has completed its handshakes with the client and is
+     * ready to process confidentiality/integrity messages.
+     *
+     * @return {@code true} if the handshaking is complete,
+     *         {@code false} if further handshaking is needed.
+     */
+    boolean isBindComplete() {
+          return saslServer.isComplete();
+    }
+
+
+    /**
+     * Return true if the SASL server has negotiated with the client to support
+     * confidentiality/integrity.
+     *
+     * @return {@code true} if the context support
+     *         confidentiality/integrity, or, {@code false} otherwise.
+     */
+    private boolean isConfidentialIntegrity() {
+      boolean ret = false;
+      String qop = (String) saslServer.getNegotiatedProperty(Sasl.QOP);
+      if(qop.equalsIgnoreCase(confidentiality) ||
+         qop.equalsIgnoreCase(integrity))
+           ret = true;
+      return ret;
+    }
+
+
+    /**
+     * Helper routine to call the SASL server evaluateResponse method with the
+     * specified byte array.
+     *
+     * @param bytes The byte array to pass to the SASL server.
+     * @return A byte array containing the result of the evaluation.
+     *
+     * @throws SaslException If the SASL server cannot evaluate the byte array.
+     */
+    private byte[] evaluateResponse(byte[] bytes) throws SaslException {
+          return saslServer.evaluateResponse(bytes);
+      }
+
+
+    /**
+     * This method is used to process an exception that is thrown during bind
+     * processing. It will try to determine if the exception is a result of
+     * callback processing, and if it is, will try to use a more informative
+     * failure message set by the callback.
+     *
+     * If the exception is a result of a error during the the SASL server
+     * processing, the callback message will be null, and
+     * the method will use the specified message parameter as the
+     * failure reason. This is a more cryptic exception message hard-coded
+     * in the SASL server internals.
+     *
+     * The method also disposes of the SASL server, clears the authentication
+     * state  and sets the result code to INVALID_CREDENTIALs
+     *
+     * @param msg The message to use if the callback message is not null.
+     */
+    private void handleError(Message msg) {
+        dispose();
+        ClientConnection clientConn = bindOp.getClientConnection();
+        clientConn.setSASLAuthStateInfo(null);
+        //Check if the callback message is null and use that message if not.
+        if(cbMsg != null)
+            bindOp.setAuthFailureReason(cbMsg);
+        else
+            bindOp.setAuthFailureReason(msg);
+        bindOp.setResultCode(ResultCode.INVALID_CREDENTIALS);
+    }
+
+
+    /**
+     * Checks the specified authentication information parameter against the
+     * privilege subsystem to see if it has PROXIED_AUTH privileges.
+     *
+     * @param authInfo The authentication information to use in the check.
+     * @return {@code true} if the authentication information has
+     *         PROXIED_AUTH privileges, {@code false} otherwise.
+     */
+    private boolean
+    hasPrivilege(AuthenticationInfo authInfo) {
+        boolean ret = true;
+          InternalClientConnection tempConn =
+               new InternalClientConnection(authInfo);
+          if (! tempConn.hasPrivilege(Privilege.PROXIED_AUTH, bindOp)) {
+              setCallbackMsg(ERR_SASL_AUTHZID_INSUFFICIENT_PRIVILEGES.get(
+                             String.valueOf(authEntry.getDN())));
+              ret = false;
+          }
+          return ret;
+    }
+
+
+    /**
+     * Checks the specified authentication information parameter against the
+     * access control subsystem to see if it has the "proxy" right.
+     *
+     * @param authInfo The authentication information to check access on.
+     * @return {@code true} if the authentication information has
+     *         proxy access, {@code false} otherwise.
+     */
+    private boolean
+    hasPermission(AuthenticationInfo authInfo) {
+        boolean ret = true;
+        Entry e = authzEntry;
+        //If the authz entry is null, use the entry associated with the NULL DN.
+        if(e == null) {
+            try {
+                e = DirectoryServer.getEntry(DN.nullDN());
+            } catch (DirectoryException ex) {
+                return false;
+            }
+        }
+        if (AccessControlConfigManager.getInstance().getAccessControlHandler().
+               mayProxy(authInfo.getAuthenticationEntry(), e,
+                        bindOp) == false) {
+            setCallbackMsg(ERR_SASL_AUTHZID_INSUFFICIENT_ACCESS.get(
+                    String.valueOf(authEntry.getDN())));
+            ret = false;
+        }
+        return ret;
+    }
+
+
+    /**
+     * Sets the callback message to the specified message.
+     *
+     * @param cbMsg The message to set the callback message to.
+     */
+    private void setCallbackMsg(Message cbMsg) {
+        this.cbMsg = cbMsg;
+    }
+
+
+   /**
+     * Process the specified callback array.
+     *
+     *@param callbacks An array of callbacks that need processing.
+     *@throws UnsupportedCallbackException If a callback is not supported.
+     */
+    public void handle(Callback[] callbacks)
+    throws UnsupportedCallbackException {
+        for (Callback callback : callbacks) {
+            if (callback instanceof NameCallback) {
+                nameCallback((NameCallback) callback);
+            } else if (callback instanceof PasswordCallback) {
+                passwordCallback((PasswordCallback) callback);
+            } else if (callback instanceof RealmCallback) {
+                realmCallback((RealmCallback) callback);
+            } else if (callback instanceof AuthorizeCallback)  {
+                authorizeCallback((AuthorizeCallback) callback);
+            } else {
+                Message message =
+                    INFO_SASL_UNSUPPORTED_CALLBACK.get(mech,
+                                                      String.valueOf(callback));
+                throw new UnsupportedCallbackException(callback,
+                        message.toString());
+            }
+        }
+    }
+
+
+    /**
+     * This callback is used to process realm information. It is not used.
+     *
+     * @param callback The realm callback instance to process.
+     */
+    private void realmCallback(RealmCallback callback) {
+    }
+
+
+    /**
+     * This callback is used to process the authorize callback. It is used
+     * during both GSSAPI and DIGEST-MD5 processing. When processing the GSSAPI
+     * mechanism, this is the only callback invoked. When processing the
+     * DIGEST-MD5 mechanism, it is the last callback invoked after the name
+     * and password callbacks respectively.
+     *
+     * @param callback The authorize callback instance to process.
+     */
+    private void authorizeCallback(AuthorizeCallback callback) {
+        String  responseAuthzID = callback.getAuthorizationID();
+        //If the authEntry is null, then we are processing a GSSAPI SASL bind,
+        //and first need to try to map the authentication ID to an user entry.
+        //The authEntry is never null, when processing a DIGEST-MD5 SASL bind.
+        if(authEntry == null) {
+            String  authid = callback.getAuthenticationID();
+            try {
+                authEntry = identityMapper.getEntryForID(authid);
+                if (authEntry == null) {
+                 setCallbackMsg(ERR_SASL_AUTHENTRY_NO_MAPPED_ENTRY.get(authid));
+                 callback.setAuthorized(false);
+                 return;
+                }
+            } catch (DirectoryException de) {
+                if (debugEnabled()) {
+                    TRACER.debugCaught(DebugLogLevel.ERROR, de);
+                }
+                setCallbackMsg(ERR_SASL_CANNOT_MAP_AUTHENTRY.get(authid,
+                        de.getMessage()));
+                callback.setAuthorized(false);
+                return;
+            }
+            userName=authid;
+        }
+        if (responseAuthzID.length() == 0) {
+            setCallbackMsg(ERR_SASLDIGESTMD5_EMPTY_AUTHZID.get());
+            callback.setAuthorized(false);
+            return;
+        } else if (!responseAuthzID.equals(userName))  {
+            String lowerAuthzID = toLowerCase(responseAuthzID);
+            //Process the callback differently depending on if the authzid
+            //string begins with the string "dn:" or not.
+            if (lowerAuthzID.startsWith("dn:")) {
+                authzDNCheck(callback);
+            } else {
+                authzIDCheck(callback);
+            }
+        } else {
+            authzEntry = authEntry;
+            callback.setAuthorized(true);
+        }
+    }
+
+
+    /**
+     * Process the specified authorize callback. This method is called if the
+     * callback's authorization ID does not begin with the string "dn:".
+     *
+     * @param callback The authorize callback to process.
+     */
+    private void authzIDCheck(AuthorizeCallback callback) {
+        String  authzid = callback.getAuthorizationID();
+        String lowerAuthzID = toLowerCase(authzid);
+        String idStr;
+        callback.setAuthorized(true);
+        if (lowerAuthzID.startsWith("u:")) {
+            idStr = authzid.substring(2);
+        } else {
+            idStr = authzid;
+        }
+        if (idStr.length() == 0) {
+            authzEntry = null;
+        } else {
+            try {
+                authzEntry = identityMapper.getEntryForID(idStr);
+                if (authzEntry == null) {
+                  setCallbackMsg(ERR_SASL_AUTHZID_NO_MAPPED_ENTRY.get(authzid));
+                  callback.setAuthorized(false);
+                  return;
+                }
+            } catch (DirectoryException e) {
+                if (debugEnabled()) {
+                    TRACER.debugCaught(DebugLogLevel.ERROR, e);
+                }
+                setCallbackMsg(ERR_SASL_AUTHZID_NO_MAPPED_ENTRY.get(authzid));
+                callback.setAuthorized(false);
+                return;
+            }
+        }
+        if ((authzEntry == null) ||
+                (!authzEntry.getDN().equals(authEntry.getDN()))) {
+            //Create temporary authorization information and run it both
+            //through the privilege and then the access control subsystems.
+            AuthenticationInfo authInfo = new AuthenticationInfo(authEntry,
+                    DirectoryServer.isRootDN(authEntry.getDN()));
+            if(!hasPrivilege(authInfo)) {
+                callback.setAuthorized(false);
+            } else {
+                callback.setAuthorized(hasPermission(authInfo));
+            }
+        }
+    }
+
+    /**
+     * Process the specified authorize callback. This method is called if the
+     * callback's authorization ID begins with the string "dn:".
+     *
+     * @param callback The authorize callback to process.
+     */
+    private void authzDNCheck(AuthorizeCallback callback) {
+        String  responseAuthzID = callback.getAuthorizationID();
+        DN authzDN;
+        callback.setAuthorized(true);
+        try {
+            authzDN = DN.decode(responseAuthzID.substring(3));
+        } catch (DirectoryException e) {
+            if (debugEnabled()) {
+                TRACER.debugCaught(DebugLogLevel.ERROR, e);
+            }
+            setCallbackMsg(ERR_SASL_AUTHZID_INVALID_DN.get(responseAuthzID,
+                                                        e.getMessageObject()));
+            callback.setAuthorized(false);
+            return;
+        }
+        DN actualAuthzDN =
+            DirectoryServer.getActualRootBindDN(authzDN);
+        if (actualAuthzDN != null) {
+            authzDN = actualAuthzDN;
+        }
+        if (!authzDN.equals(authEntry.getDN())) {
+            if (authzDN.isNullDN()) {
+                authzEntry = null;
+            } else {
+                try {
+                  if((authzEntry = DirectoryServer.getEntry(authzDN)) == null) {
+                        setCallbackMsg(ERR_SASL_AUTHZID_NO_SUCH_ENTRY.get(
+                                                      String.valueOf(authzDN)));
+                        callback.setAuthorized(false);
+                        return;
+                    }
+                } catch (DirectoryException e) {
+                    if (debugEnabled()) {
+                        TRACER.debugCaught(DebugLogLevel.ERROR, e);
+                    }
+                    setCallbackMsg(ERR_SASL_AUTHZID_CANNOT_GET_ENTRY
+                          .get(String.valueOf(authzDN), e.getMessageObject()));
+                    callback.setAuthorized(false);
+                    return;
+                }
+            }
+            AuthenticationInfo authInfo = new AuthenticationInfo(authEntry,
+                                  DirectoryServer.isRootDN(authEntry.getDN()));
+            if(!hasPrivilege(authInfo)) {
+                callback.setAuthorized(false);
+            } else
+                callback.setAuthorized(hasPermission(authInfo));
+        }
+    }
+
+    /**
+     * Process the specified password callback. Used only for the DIGEST-MD5
+     * SASL mechanism. The password callback is processed after the name
+     * callback.
+     *
+     * @param passwordCallback The password callback to process.
+     */
+    private void passwordCallback(PasswordCallback passwordCallback) {
+        //If there is no authEntry this is an error.
+        if(authEntry == null) {
+            setCallbackMsg(ERR_SASL_NO_MATCHING_ENTRIES.get(userName));
+            return;
+        }
+        //Try to get a clear password to use.
+        List<ByteString> clearPasswords;
+        try {
+          PasswordPolicyState pwPolicyState =
+                                    new PasswordPolicyState(authEntry, false);
+          clearPasswords = pwPolicyState.getClearPasswords();
+          if ((clearPasswords == null) || clearPasswords.isEmpty()) {
+              setCallbackMsg(
+                 ERR_SASL_NO_REVERSIBLE_PASSWORDS.get(mech,
+                                            String.valueOf(authEntry.getDN())));
+            return;
+          }
+        }
+        catch (Exception e) {
+            if (debugEnabled()) {
+                TRACER.debugCaught(DebugLogLevel.ERROR, e);
+            }
+            setCallbackMsg(ERR_SASL_CANNOT_GET_REVERSIBLE_PASSWORDS.get(
+                    String.valueOf(authEntry.getDN()),mech,
+                    String.valueOf(e)));
+          return;
+        }
+        //Use the first password.
+        char[] password = clearPasswords.get(0).toString().toCharArray();
+        passwordCallback.setPassword(password);
+        return;
+    }
+
+    /**
+     * Process the specified name callback. Used only for DIGEST-MD5 SASL
+     * mechanism.
+     *
+     * @param nameCallback The name callback to process.
+     */
+    private void nameCallback(NameCallback nameCallback) {
+        userName= nameCallback.getDefaultName();
+        String lowerUserName = toLowerCase(userName);
+        //Process the user name differently if it starts with the string "dn:".
+        if (lowerUserName.startsWith("dn:")) {
+            DN userDN;
+            try {
+                userDN = DN.decode(userName.substring(3));
+            } catch (DirectoryException e) {
+                if (debugEnabled()) {
+                    TRACER.debugCaught(DebugLogLevel.ERROR, e);
+                }
+                 setCallbackMsg(ERR_SASL_CANNOT_DECODE_USERNAME_AS_DN.get(
+                                      mech,
+                                      userName, e.getMessageObject()));
+                return;
+            }
+            if (userDN.isNullDN()) {
+              setCallbackMsg(ERR_SASL_USERNAME_IS_NULL_DN.get(
+                                                      mech));
+              return;
+            }
+            DN rootDN = DirectoryServer.getActualRootBindDN(userDN);
+            if (rootDN != null) {
+                userDN = rootDN;
+            }
+            getAuthEntry(userDN);
+        } else {
+            //The entry name is not a DN, try to map it using the identity
+            //mapper.
+            String entryID = userName;
+            if (lowerUserName.startsWith("u:")) {
+                if (lowerUserName.equals("u:")) {
+                    setCallbackMsg(ERR_SASL_ZERO_LENGTH_USERNAME.get(
+                            mech,mech));
+                    return;
+                }
+                entryID = userName.substring(2);
+            }
+            try {
+                authEntry = identityMapper.getEntryForID(entryID);
+            } catch (DirectoryException e) {
+                if (debugEnabled()) {
+                    TRACER.debugCaught(DebugLogLevel.ERROR, e);
+                }
+               setCallbackMsg(ERR_SASLDIGESTMD5_CANNOT_MAP_USERNAME.get(
+                      String.valueOf(userName), e.getMessageObject()));
+                return;
+            }
+        }
+        if (authEntry == null) {
+            //The authEntry is null, this is an error. The password callback
+            //will catch this error. There is no way to stop the processing
+            //from the name callback.
+            return;
+        }
+    }
+
+    /**
+     * Try to get a entry from the directory using the specified DN. Used only
+     * for DIGEST-MD5 SASL mechanism.
+     *
+     * @param userDN The DN of the entry to retrieve from the server.
+     */
+   private void getAuthEntry(DN userDN) {
+       Lock readLock = null;
+       for (int i=0; i < 3; i++) {
+           readLock = LockManager.lockRead(userDN);
+           if (readLock != null) {
+               break;
+           }
+       }
+       if (readLock == null) {
+           setCallbackMsg(INFO_SASL_CANNOT_LOCK_ENTRY.get(
+                                                       String.valueOf(userDN)));
+           return;
+       } try {
+           authEntry = DirectoryServer.getEntry(userDN);
+       } catch (DirectoryException e) {
+           if (debugEnabled()) {
+               TRACER.debugCaught(DebugLogLevel.ERROR, e);
+           }
+           setCallbackMsg(ERR_SASL_CANNOT_GET_ENTRY_BY_DN.get(
+                   String.valueOf(userDN), SASL_MECHANISM_DIGEST_MD5,
+                   e.getMessageObject()));
+           return;
+       } finally {
+           LockManager.unlock(userDN, readLock);
+       }
+   }
+
+   /**
+    * The method performs all GSSAPI processing. It is run as the context of
+    * the login context performed by the GSSAPI mechanism handler. See comments
+    * for processing overview.
+    * @return {@code true} if the authentication processing was successful,
+    *         or, {@code false} otherwise.
+    */
+   public Boolean run() {
+       ClientConnection clientConn = bindOp.getClientConnection();
+       //If the SASL server is null then this is the first handshake and the
+       //server needs to be initialized before any processing can be performed.
+       //If the SASL server cannot be created then all processing is abandoned
+       //and INVALID_CREDENTIALS is returned to the client.
+       if(saslServer == null) {
+           try {
+               initSASLServer();
+           } catch (SaslException e) {
+               if (debugEnabled()) {
+                   TRACER.debugCaught(DebugLogLevel.ERROR, e);
+               }
+               Message msg =
+                   ERR_SASL_CONTEXT_CREATE_ERROR.get(SASL_MECHANISM_DIGEST_MD5,
+                                                     getExceptionMessage(e));
+               clientConn.setSASLAuthStateInfo(null);
+               bindOp.setAuthFailureReason(msg);
+               bindOp.setResultCode(ResultCode.INVALID_CREDENTIALS);
+               return false;
+           }
+       }
+       byte[] clientCredBytes = new byte[0];
+       ASN1OctetString clientCredentials = bindOp.getSASLCredentials();
+       if(clientCredentials != null) {
+           clientCredBytes = clientCredentials.value();
+       }
+       clientConn.setSASLAuthStateInfo(null);
+       try {
+           byte[] responseBytes =
+               evaluateResponse(clientCredBytes);
+           ASN1OctetString responseAuthStr =
+               new ASN1OctetString(responseBytes);
+           //If the bind has not been completed,then
+           //more handshake is needed and SASL_BIND_IN_PROGRESS is returned back
+           //to the client.
+           if (isBindComplete()) {
+               bindOp.setResultCode(ResultCode.SUCCESS);
+               bindOp.setSASLAuthUserEntry(authEntry);
+               AuthenticationInfo authInfo =
+                    new AuthenticationInfo(authEntry, authzEntry,
+                                    mech,
+                                   DirectoryServer.isRootDN(authEntry.getDN()));
+               bindOp.setAuthenticationInfo(authInfo);
+               //If confidentiality/integrity has been negotiated then
+               //create a SASL security provider and save it in the client
+               //connection. If confidentiality/integrity has not been
+               //negotiated, dispose of the SASL server.
+               if(isConfidentialIntegrity()) {
+                   SASLSecurityProvider secProvider =
+                       new SASLSecurityProvider(clientConn, mech, this);
+                   LDAPClientConnection ldapConn =
+                       (LDAPClientConnection) clientConn;
+                       ldapConn.setSASLConnectionSecurityProvider(secProvider);
+               } else {
+                   dispose();
+                   clientConn.setSASLAuthStateInfo(null);
+               }
+           } else {
+               bindOp.setServerSASLCredentials(responseAuthStr);
+               clientConn.setSASLAuthStateInfo(this);
+               bindOp.setResultCode(ResultCode.SASL_BIND_IN_PROGRESS);
+           }
+       } catch (SaslException e) {
+           if (debugEnabled()) {
+               TRACER.debugCaught(DebugLogLevel.ERROR, e);
+           }
+           Message msg =
+               ERR_SASL_PROTOCOL_ERROR.get(mech, getExceptionMessage(e));
+           handleError(msg);
+           return false;
+       }
+       return true;
+   }
+
+
+   /**
+    * Perform the authentication as the specified login context. The specified
+    * bind operation needs to be saved so the callbacks have access to it.
+    * Only used by the GSSAPI mechanism.
+    *
+    * @param loginContext The login context to perform the authentication
+    *                     as.
+    * @param bindOp The bind operation needed by the callbacks to process the
+    *               authentication.
+    */
+   void
+   performAuthentication(LoginContext loginContext, BindOperation bindOp) {
+       this.bindOp = bindOp;
+       try {
+           Subject.doAs(loginContext.getSubject(), this);
+       } catch (PrivilegedActionException e) {
+           if (debugEnabled()) {
+               TRACER.debugCaught(DebugLogLevel.ERROR, e);
+           }
+           Message msg =
+               ERR_SASL_PROTOCOL_ERROR.get(mech, getExceptionMessage(e));
+           handleError(msg);
+       }
+   }
+
+   /**
+    * Process the initial stage of a DIGEST-MD5 SASL bind using the specified
+    * bind operation.
+    *
+    * @param bindOp The bind operation to use in processing.
+    */
+   void
+   evaluateInitialStage(BindOperation bindOp) {
+       this.bindOp = bindOp;
+       ClientConnection clientConn = bindOp.getClientConnection();
+       try {
+           byte[] challengeBuffer = evaluateResponse(new byte[0]);
+           ASN1OctetString challenge = new ASN1OctetString(challengeBuffer);
+           bindOp.setResultCode(ResultCode.SASL_BIND_IN_PROGRESS);
+           bindOp.setServerSASLCredentials(challenge);
+           clientConn.setSASLAuthStateInfo(this);
+       } catch (SaslException e) {
+           if (debugEnabled()) {
+               TRACER.debugCaught(DebugLogLevel.ERROR, e);
+           }
+           Message msg =
+               ERR_SASL_PROTOCOL_ERROR.get(mech,getExceptionMessage(e));
+           handleError(msg);
+       }
+   }
+
+   /**
+    * Evaluate the final stage of a DIGEST-MD5 SASL bind using the specified
+    * bind operation.
+    *
+    * @param bindOp The bind operation to use in processing.
+    */
+   void
+   evaluateFinalStage(BindOperation bindOp) {
+      this.bindOp = bindOp;
+       ASN1OctetString clientCredentials = bindOp.getSASLCredentials();
+       if ((clientCredentials == null) ||
+               (clientCredentials.value().length == 0)) {
+           Message msg =
+               ERR_SASL_NO_CREDENTIALS.get(mech, mech);
+           handleError(msg);
+           return;
+       }
+       ClientConnection clientConn = bindOp.getClientConnection();
+       clientConn.setSASLAuthStateInfo(null);
+       try {
+           byte[] responseBytes =
+                        evaluateResponse(clientCredentials.value());
+           ASN1OctetString responseAuthStr =
+               new ASN1OctetString(responseBytes);
+           bindOp.setResultCode(ResultCode.SUCCESS);
+           bindOp.setServerSASLCredentials(responseAuthStr);
+           bindOp.setSASLAuthUserEntry(authEntry);
+           AuthenticationInfo authInfo =
+                new AuthenticationInfo(authEntry, authzEntry,
+                                       mech,
+                                  DirectoryServer.isRootDN(authEntry.getDN()));
+           bindOp.setAuthenticationInfo(authInfo);
+           //If confidentiality/integrity has been negotiated, then create a
+           //SASL security provider and save it in the client connection for
+           //use in later processing.
+           if(isConfidentialIntegrity()) {
+               SASLSecurityProvider secProvider =
+                   new SASLSecurityProvider(clientConn, mech, this);
+               LDAPClientConnection ldapConn =
+                   (LDAPClientConnection) clientConn;
+               ldapConn.setSASLConnectionSecurityProvider(secProvider);
+           } else {
+               dispose();
+               clientConn.setSASLAuthStateInfo(null);
+           }
+       } catch (SaslException e) {
+           if (debugEnabled()) {
+               TRACER.debugCaught(DebugLogLevel.ERROR, e);
+           }
+           Message msg =
+               ERR_SASL_PROTOCOL_ERROR.get(mech, getExceptionMessage(e));
+           handleError(msg);
+       }
+   }
+}
\ No newline at end of file
diff --git a/opends/src/server/org/opends/server/extensions/SASLSecurityProvider.java b/opends/src/server/org/opends/server/extensions/SASLSecurityProvider.java
new file mode 100644
index 0000000..0ee7e0c
--- /dev/null
+++ b/opends/src/server/org/opends/server/extensions/SASLSecurityProvider.java
@@ -0,0 +1,457 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License").  You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE
+ * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
+ * add the following below this CDDL HEADER, with the fields enclosed
+ * by brackets "[]" replaced with your own identifying information:
+ *      Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ *
+ *      Copyright 2008 Sun Microsystems, Inc.
+ */
+
+package org.opends.server.extensions;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.channels.SelectionKey;
+import java.nio.channels.Selector;
+import java.nio.channels.SocketChannel;
+import java.util.Iterator;
+import javax.security.sasl.Sasl;
+import javax.security.sasl.SaslException;
+import org.opends.server.api.ClientConnection;
+import org.opends.server.api.ConnectionSecurityProvider;
+import org.opends.server.config.ConfigEntry;
+import org.opends.server.config.ConfigException;
+import org.opends.server.loggers.debug.DebugTracer;
+import org.opends.server.protocols.ldap.LDAPClientConnection;
+import org.opends.server.types.DebugLogLevel;
+import org.opends.server.types.DirectoryException;
+import org.opends.server.types.DisconnectReason;
+import org.opends.server.types.InitializationException;
+import static org.opends.server.loggers.debug.DebugLogger.*;
+
+/**
+ * This class provides an implementation of a connection security provider that
+ * provides SASL confidentiality/integrity between the server and client.
+ *
+ */
+public class SASLSecurityProvider extends ConnectionSecurityProvider {
+
+    // The tracer object for the debug logger.
+    private static final DebugTracer TRACER = getTracer();
+
+    //The client connection associated with this provider.
+    private ClientConnection connection;
+
+    //The socket channel associated with this provider.
+    private SocketChannel sockChannel;
+
+    //The SASL context associated with the provider
+    private SASLContext saslContext;
+
+    //The number of bytes in the length buffer.
+    private final int lengthSize = 4;
+
+    //A byte buffer used to hold the length of the clear buffer.
+    private ByteBuffer lengthBuf =  ByteBuffer.allocate(lengthSize);
+
+    //The SASL mechanism name.
+    private String name;
+
+    /**
+     * Create a SASL security provider with the specified parameters that is
+     * capable of processing a confidentiality/integrity SASL connection.
+     *
+     * @param connection The client connection to read/write the bytes.
+     * @param name The SASL mechanism name.
+     * @param saslContext The SASL context to process the data through.
+     */
+    public SASLSecurityProvider(ClientConnection connection, String name,
+                               SASLContext saslContext) {
+      super();
+      this.connection = connection;
+      this.name = name;
+      this.saslContext = saslContext;
+      this.sockChannel = ((LDAPClientConnection) connection).getSocketChannel();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void disconnect(boolean connectionValid) {
+        this.saslContext.dispose();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void finalizeConnectionSecurityProvider() {
+
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public int getClearBufferSize() {
+        return 0;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public int getEncodedBufferSize() {
+        return 0;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public String getSecurityMechanismName() {
+        return name;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void initializeConnectionSecurityProvider(ConfigEntry configEntry)
+            throws ConfigException, InitializationException {
+        this.connection = null;
+        this.sockChannel = null;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public boolean isSecure() {
+        return true;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public ConnectionSecurityProvider newInstance(ClientConnection clientConn,
+                                                  SocketChannel socketChannel) {
+            return new SASLSecurityProvider(clientConn, null,null);
+    }
+
+    /**
+     * Return the clear buffer length as determined by processing the first
+     * 4 bytes of the specified buffer.
+     *
+     * @param byteBuf The buffer to examine the first 4 bytes of.
+     * @return The size of the clear buffer.
+     */
+    private int getBufLength(ByteBuffer byteBuf) {
+        int answer = 0;
+        byte[] buf = byteBuf.array();
+
+        for (int i = 0; i < lengthSize; i++) {
+            answer <<= 8;
+            answer |= ((int)buf[i] & 0xff);
+        }
+        return answer;
+    }
+
+    /**
+     * Read from the socket channel into the specified byte buffer the
+     * number of bytes specified in the total parameter.
+     *
+     * @param byteBuf The byte buffer to put the bytes in.
+     * @param total The total number of bytes to read from the socket channel.
+     * @return The number of bytes read, 0 or -1.
+     * @throws IOException If an error occurred reading the socket channel.
+     */
+    private int readAll(ByteBuffer byteBuf, int total) throws IOException {
+        int count = 0;
+        while(sockChannel.isOpen() && total > 0) {
+            count = sockChannel.read(byteBuf);
+            if(count == -1)
+                return -1;
+           if(count == 0)
+                return 0;
+            total -= count;
+        }
+        if(total > 0)
+            return -1;
+        else
+            return byteBuf.position();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public boolean readData() throws DirectoryException {
+        int recvBufSize = saslContext.getBufSize(Sasl.MAX_BUFFER);
+        try {
+            while(true) {
+                lengthBuf.clear();
+                int readResult = readAll(lengthBuf, lengthSize);
+                if (readResult == -1) {
+                    //Client connection has been closed. Disconnect and return.
+                    connection.disconnect(DisconnectReason.CLIENT_DISCONNECT,
+                            false, null);
+                    return false;
+                } else if(readResult == 0)
+                    return true;
+                int bufLength = getBufLength(lengthBuf);
+                if(bufLength > recvBufSize) {
+                    connection.disconnect(DisconnectReason.CLIENT_DISCONNECT,
+                            false, null);
+                    return false;
+                }
+                ByteBuffer readBuf = ByteBuffer.allocate(bufLength);
+                readResult = readAll(readBuf, bufLength);
+                if (readResult == -1) {
+                    //Client connection has been closed. Disconnect and return.
+                    connection.disconnect(DisconnectReason.CLIENT_DISCONNECT,
+                            false, null);
+                    return false;
+                } else if (readResult == 0)
+                    return true;
+                byte[] inBytes = readBuf.array();
+                byte[] clearBytes =
+                                saslContext.unwrap(inBytes, 0, inBytes.length);
+                ByteBuffer clearBuffer = ByteBuffer.wrap(clearBytes);
+                if (!connection.processDataRead(clearBuffer))
+                    return false;
+            }
+        } catch (SaslException saslEx) {
+            if (debugEnabled()) {
+                TRACER.debugCaught(DebugLogLevel.ERROR, saslEx);
+              }
+            //Error trying to unwrap the data.
+            connection.disconnect(DisconnectReason.IO_ERROR, false, null);
+            return false;
+        } catch (IOException ioe) {
+            // An error occurred while trying to communicate with the client.
+            // Disconnect and return.
+            if (debugEnabled()) {
+                TRACER.debugCaught(DebugLogLevel.ERROR, ioe);
+            }
+            connection.disconnect(DisconnectReason.IO_ERROR, false, null);
+            return false;
+        }
+    }
+
+    /**
+     * Creates a buffer suitable to send to the client using the specified
+     * clear byte array, offset and length of the bytes to wrap.
+     *
+     * @param clearBytes The clear byte array to send to the client.
+     * @param offSet An offset into the byte array to start the wrap at.
+     * @param len The length of the bytes to wrap in the byte array.
+     * @throws SaslException If the wrap of the bytes fails.
+     */
+    private ByteBuffer
+    createSendBuffer(byte[] clearBytes, int offSet, int len)
+    throws SaslException {
+        byte[] wrapBytes = saslContext.wrap(clearBytes, offSet, len);
+        byte[] outBuf = new  byte[wrapBytes.length + lengthSize];
+        writeBufLen(outBuf, wrapBytes.length);
+        System.arraycopy(wrapBytes, 0, outBuf, lengthSize, wrapBytes.length);
+        return ByteBuffer.wrap(outBuf);
+    }
+
+    /**
+     *  Writes the specified len parameter into the buffer in a form that can
+     *  be sent over a network to the client.
+     *
+     * @param buf The buffer to hold the length bytes.
+     * @param len The length to encode.
+     */
+    private void writeBufLen(byte[] buf, int len) {
+        for (int i = 3; i >= 0; i--) {
+            buf[i] = (byte)(len & 0xff);
+            len >>>= 8;
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override()
+    public boolean writeData(ByteBuffer clearData) {
+        int maxSendBufSize = saslContext.getBufSize(Sasl.RAW_SEND_SIZE);
+        int clearLength = clearData.limit();
+        try {
+            if(clearLength < maxSendBufSize) {
+                ByteBuffer sendBuffer =
+                    createSendBuffer(clearData.array(),0,clearLength);
+                return writeChannel(sendBuffer);
+            } else {
+                byte[] clearBytes = clearData.array();
+                for(int i=0; i < clearLength; i += maxSendBufSize) {
+                    int totLength = (clearLength - i) < maxSendBufSize ?
+                                    (clearLength - i) : maxSendBufSize;
+                    ByteBuffer sendBuffer =
+                                     createSendBuffer(clearBytes, i, totLength);
+                    if(!writeChannel(sendBuffer))
+                        return false;
+                }
+            }
+        } catch (SaslException e) {
+            if (debugEnabled()) {
+                TRACER.debugCaught(DebugLogLevel.ERROR, e);
+            }
+            connection.disconnect(DisconnectReason.IO_ERROR, false, null);
+            return false;
+        }
+        return true;
+    }
+
+   /**
+    * Write the specified byte buffer to the socket channel.
+    *
+    * @param buffer The byte buffer to write to the socket channel.
+    * @return {@code true} if the byte buffer was successfully written to the
+    *         socket channel, or, {@code false} if not.
+    */
+    private boolean writeChannel(ByteBuffer buffer) {
+        try {
+            while (buffer.hasRemaining())  {
+                int bytesWritten = sockChannel.write(buffer);
+                if (bytesWritten < 0)  {
+                    connection.disconnect(DisconnectReason.CLIENT_DISCONNECT,
+                                          false, null);
+                    return false;
+                } else if (bytesWritten == 0)  {
+                    return writeWithTimeout(buffer);
+                }
+            }
+        }  catch (IOException ioe)  {
+            if (debugEnabled()) {
+                TRACER.debugCaught(DebugLogLevel.ERROR, ioe);
+            }
+            connection.disconnect(DisconnectReason.IO_ERROR, false, null);
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Writes the specified byte buffer parameter to the socket channel waiting
+     * for a period of time if the buffer cannot be written immediately.
+     *
+     * @param buffer The byte buffer to write to the channel.
+     * @return {@code true} if the bytes were sent, or, {@code false} otherwise.
+     * @throws IOException If an IO error occurs while writing the bytes.
+     */
+    private boolean writeWithTimeout(ByteBuffer buffer) throws IOException {
+        long startTime = System.currentTimeMillis();
+        long waitTime  = connection.getMaxBlockedWriteTimeLimit();
+        if (waitTime <= 0) {
+            // We won't support an infinite time limit, so fall back to using
+            // five minutes, which is a very long timeout given that we're
+            // blocking a worker thread.
+            waitTime = 300000L;
+        }
+        long stopTime = startTime + waitTime;
+        Selector selector = connection.getWriteSelector();
+        if (selector == null) {
+            // The client connection does not provide a selector, so we'll
+            // fall back to a more inefficient way that will work without a
+            // selector.
+            while (buffer.hasRemaining() &&
+                   (System.currentTimeMillis() < stopTime)) {
+                if (sockChannel.write(buffer) < 0) {
+                    // The client connection has been closed
+                    connection.disconnect(DisconnectReason.CLIENT_DISCONNECT,
+                                          false,  null);
+                    return false;
+                }
+            }
+            if (buffer.hasRemaining()) {
+                // If we've gotten here, then the write timed out.
+                // Terminate the client connection.
+                connection.disconnect(DisconnectReason.IO_TIMEOUT, false, null);
+                return false;
+            }
+            return true;
+        }
+        // Register with the selector for handling write operations.
+        SelectionKey key =
+                         sockChannel.register(selector, SelectionKey.OP_WRITE);
+        try
+        {
+            selector.select(waitTime);
+            while (buffer.hasRemaining())
+            {
+                long currentTime = System.currentTimeMillis();
+                if (currentTime >= stopTime) {
+                    // We've been blocked for too long
+                    connection.disconnect(DisconnectReason.IO_TIMEOUT,
+                                          false, null);
+                    return false;
+                }
+                else {
+                    waitTime = stopTime - currentTime;
+                }
+                Iterator<SelectionKey> iterator =
+                                             selector.selectedKeys().iterator();
+                while (iterator.hasNext()) {
+                    SelectionKey k = iterator.next();
+                    if (k.isWritable()) {
+                        int bytesWritten = sockChannel.write(buffer);
+                        if (bytesWritten < 0) {
+                            // The client connection has been closed.
+                            connection.disconnect(
+                                      DisconnectReason.CLIENT_DISCONNECT,
+                                      false, null);
+                            return false;
+                        }
+                        iterator.remove();
+                    }
+                }
+                if (buffer.hasRemaining()) {
+                    selector.select(waitTime);
+                }
+            }
+            return true;
+        } finally {
+            if (key.isValid()) {
+                key.cancel();
+                selector.selectNow();
+            }
+        }
+    }
+
+    /**
+     * Return if the underlying SASL context is active or not. The SASL context
+     * may still be negotiating a multi-stage SASL bind and is not ready to
+     * process confidentiality or integrity data yet.
+     *
+     * @return {@code true} if the underlying SASL context is active or ready
+     *         to process confidentiality/integrity messages, or, {@code false}
+     *         if not.
+    */
+
+    public boolean isActive() {
+        return saslContext.isBindComplete();
+    }
+}
diff --git a/opends/src/server/org/opends/server/extensions/TLSConnectionSecurityProvider.java b/opends/src/server/org/opends/server/extensions/TLSConnectionSecurityProvider.java
index 20f857b..7b26abc 100644
--- a/opends/src/server/org/opends/server/extensions/TLSConnectionSecurityProvider.java
+++ b/opends/src/server/org/opends/server/extensions/TLSConnectionSecurityProvider.java
@@ -339,6 +339,15 @@
   /**
    * {@inheritDoc}
    */
+  @Override
+  public boolean isActive() {
+      //This provider is always active.
+      return true;
+  }
+
+  /**
+   * {@inheritDoc}
+   */
   @Override()
   public ConnectionSecurityProvider newInstance(ClientConnection
                                                       clientConnection,
diff --git a/opends/src/server/org/opends/server/protocols/ldap/LDAPClientConnection.java b/opends/src/server/org/opends/server/protocols/ldap/LDAPClientConnection.java
index 60e3443..c4e0248 100644
--- a/opends/src/server/org/opends/server/protocols/ldap/LDAPClientConnection.java
+++ b/opends/src/server/org/opends/server/protocols/ldap/LDAPClientConnection.java
@@ -59,7 +59,6 @@
 import org.opends.server.core.SearchOperation;
 import org.opends.server.core.SearchOperationBasis;
 import org.opends.server.core.UnbindOperationBasis;
-import org.opends.server.core.networkgroups.NetworkGroup;
 import org.opends.server.extensions.NullConnectionSecurityProvider;
 import org.opends.server.extensions.TLSCapableConnection;
 import org.opends.server.extensions.TLSConnectionSecurityProvider;
@@ -220,7 +219,9 @@
   // if StartTLS is requested.
   private TLSConnectionSecurityProvider tlsSecurityProvider;
 
-
+  //The SASL connection provider used if confidentiality/integrity is negotiated
+  //during a SASL bind (GSSAPI and DIGEST-MD5 only).
+  private ConnectionSecurityProvider saslSecurityProvider;
 
   /**
    * Creates a new LDAP client connection with the provided information.
@@ -237,9 +238,6 @@
 
 
     this.connectionHandler     = connectionHandler;
-    if (connectionHandler.isAdminConnectionHandler()) {
-      setNetworkGroup(NetworkGroup.getAdminNetworkGroup());
-    }
     this.clientChannel         = clientChannel;
     this.securityProvider      = null;
     this.clearSecurityProvider = null;
@@ -479,12 +477,27 @@
    */
   public ConnectionSecurityProvider getConnectionSecurityProvider()
   {
-    return securityProvider;
+      if(saslSecurityProvider != null && saslSecurityProvider.isActive())
+          securityProvider =  saslSecurityProvider;
+      return securityProvider;
   }
 
 
 
   /**
+   * Set the security provider to be used to process SASL (DIGEST-MD5, GSSAPI)
+   * confidentiality/integrity messages.
+   *
+   * @param secProvider The security provider to use.
+   */
+    public void
+    setSASLConnectionSecurityProvider(ConnectionSecurityProvider secProvider) {
+        saslSecurityProvider = secProvider;
+    }
+
+
+
+  /**
    * Specifies the connection security provider for this client connection.
    *
    * @param  securityProvider  The connection security provider to use for
diff --git a/opends/src/server/org/opends/server/util/ServerConstants.java b/opends/src/server/org/opends/server/util/ServerConstants.java
index 954c585..fdbe328 100644
--- a/opends/src/server/org/opends/server/util/ServerConstants.java
+++ b/opends/src/server/org/opends/server/util/ServerConstants.java
@@ -1873,6 +1873,12 @@
        "Kerberos Confidentiality";
 
 
+  /**
+   * The name of the default protocol used.
+   */
+  public static final String SASL_DEFAULT_PROTOCOL = "ldap";
+
+
 
   /**
    * The name of the security mechanism that will be used for connections
@@ -1934,6 +1940,33 @@
 
 
   /**
+   * The name of the security mechanism that will be used for connections whose
+   * communication only SASL authenticated.
+   */
+  public static final String SASL_MECHANISM_AUTHENTICATION_ONLY =
+       "none";
+
+
+
+  /**
+   * The name of the security mechanism that will be used for connections whose
+   * communication is protected using the confidentiality features SASL.
+   */
+  public static final String SASL_MECHANISM_CONFIDENTIALITY =
+       "confidentiality";
+
+
+
+  /**
+   * The name of the security mechanism that will be used for connections whose
+   * communication is verified using SASL integrity.
+   */
+  public static final String SASL_MECHANISM_INTEGRITY =
+       "integrity";
+
+
+
+  /**
    * The OID for the account usable request and response controls.
    */
   public static final String OID_ACCOUNT_USABLE_CONTROL =
diff --git a/opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/DigestMD5SASLMechanismHandlerTestCase.java b/opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/DigestMD5SASLMechanismHandlerTestCase.java
index a0f63dc..4488593 100644
--- a/opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/DigestMD5SASLMechanismHandlerTestCase.java
+++ b/opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/DigestMD5SASLMechanismHandlerTestCase.java
@@ -31,6 +31,7 @@
 import java.util.ArrayList;
 import java.util.List;
 
+import org.testng.annotations.AfterClass;
 import org.testng.annotations.BeforeClass;
 import org.testng.annotations.DataProvider;
 import org.testng.annotations.Test;
@@ -79,9 +80,25 @@
          throws Exception
   {
     TestCaseUtils.startServer();
+
+    
+    TestCaseUtils.dsconfig(
+            "set-sasl-mechanism-handler-prop",
+            "--handler-name", "DIGEST-MD5",
+            "--set", "server-fqdn:" + "127.0.0.1");
+    
   }
 
 
+  @AfterClass
+  public void tearDown() throws Exception {
+   
+    TestCaseUtils.dsconfig(
+            "set-sasl-mechanism-handler-prop",
+            "--handler-name", "DIGEST-MD5",
+            "--remove", "server-fqdn:" + "127.0.0.1");
+  }
+
 
   /**
    * Retrieves a set of invvalid configuration entries.
@@ -234,7 +251,6 @@
       "-o", "mech=DIGEST-MD5",
       "-o", "authid=u:test.user",
       "-o", "authzid=u:test.user",
-      "-o", "realm=o=test",
       "-w", "password",
       "-b", "",
       "-s", "base",
@@ -286,7 +302,6 @@
       "-o", "mech=DIGEST-MD5",
       "-o", "authid=dn:uid=test.user,o=test",
       "-o", "authzid=dn:uid=test.user,o=test",
-      "-o", "realm=o=test",
       "-w", "password",
       "-b", "",
       "-s", "base",
@@ -338,7 +353,6 @@
       "-o", "mech=DIGEST-MD5",
       "-o", "authid=u:test.user",
       "-o", "authzid=u:test.user",
-      "-o", "realm=o=test",
       "-w", "wrongpassword",
       "-b", "",
       "-s", "base",
@@ -390,7 +404,6 @@
       "-o", "mech=DIGEST-MD5",
       "-o", "authid=dn:uid=test.user,o=test",
       "-o", "authzid=dn:uid=test.user,o=test",
-      "-o", "realm=o=test",
       "-w", "wrongpassword",
       "-b", "",
       "-s", "base",
@@ -440,7 +453,6 @@
       "-o", "mech=DIGEST-MD5",
       "-o", "authid=u:test.user",
       "-o", "authzid=u:test.user",
-      "-o", "realm=o=test",
       "-w", "password",
       "-b", "",
       "-s", "base",
@@ -490,7 +502,6 @@
       "-o", "mech=DIGEST-MD5",
       "-o", "authid=dn:uid=test.user,o=test",
       "-o", "authzid=dn:uid=test.user,o=test",
-      "-o", "realm=o=test",
       "-w", "password",
       "-b", "",
       "-s", "base",
@@ -540,7 +551,6 @@
       "-o", "mech=DIGEST-MD5",
       "-o", "authid=dn:invaliddn",
       "-o", "authzid=dn:invaliddn",
-      "-o", "realm=o=test",
       "-w", "password",
       "-b", "",
       "-s", "base",
@@ -590,7 +600,6 @@
       "-o", "mech=DIGEST-MD5",
       "-o", "authid=u:doesntexist",
       "-o", "authzid=u:doesntexist",
-      "-o", "realm=o=test",
       "-w", "password",
       "-b", "",
       "-s", "base",
@@ -640,7 +649,6 @@
       "-o", "mech=DIGEST-MD5",
       "-o", "authid=dn:uid=doesntexist,o=test",
       "-o", "authzid=dn:uid=doesntexist,o=test",
-      "-o", "realm=o=test",
       "-w", "password",
       "-b", "",
       "-s", "base",
@@ -671,7 +679,6 @@
       "-o", "mech=DIGEST-MD5",
       "-o", "authid=u:",
       "-o", "authzid=u:",
-      "-o", "realm=o=test",
       "-w", "",
       "-b", "",
       "-s", "base",
@@ -702,7 +709,6 @@
       "-o", "mech=DIGEST-MD5",
       "-o", "authid=dn:",
       "-o", "authzid=dn:",
-      "-o", "realm=o=test",
       "-w", "",
       "-b", "",
       "-s", "base",
@@ -733,7 +739,6 @@
       "-o", "mech=DIGEST-MD5",
       "-o", "authid=",
       "-o", "authzid=",
-      "-o", "realm=o=test",
       "-w", "",
       "-b", "",
       "-s", "base",
@@ -778,7 +783,6 @@
       "-o", "mech=DIGEST-MD5",
       "-o", "authid=dn:uid=test.user,o=test",
       "-o", "authzid=",
-      "-o", "realm=o=test",
       "-w", "password",
       "-b", "",
       "-s", "base",
@@ -808,7 +812,6 @@
       "-o", "mech=DIGEST-MD5",
       "-o", "authid=dn:cn=Directory Manager",
       "-o", "authzid=dn:cn=Directory Manager",
-      "-o", "realm=o=test",
       "-w", "password",
       "-b", "",
       "-s", "base",
@@ -860,7 +863,6 @@
       "-o", "mech=DIGEST-MD5",
       "-o", "authid=dn:cn=Second Root DN",
       "-o", "authzid=dn:cn=Second Root DN",
-      "-o", "realm=o=test",
       "-w", "password",
       "-b", "",
       "-s", "base",
@@ -909,7 +911,6 @@
       "-o", "mech=DIGEST-MD5",
       "-o", "authid=dn:uid=test.user,o=test",
       "-o", "authzid=dn:uid=nonexistent,o=test",
-      "-o", "realm=o=test",
       "-w", "password",
       "-b", "",
       "-s", "base",
@@ -954,7 +955,6 @@
       "-o", "mech=DIGEST-MD5",
       "-o", "authid=dn:uid=test.user,o=test",
       "-o", "authzid=u:nonexistent",
-      "-o", "realm=o=test",
       "-w", "password",
       "-b", "",
       "-s", "base",
@@ -999,7 +999,6 @@
       "-o", "mech=DIGEST-MD5",
       "-o", "authid=dn:uid=test.user,o=test",
       "-o", "authzid=dn:malformed",
-      "-o", "realm=o=test",
       "-w", "password",
       "-b", "",
       "-s", "base",
diff --git a/opends/tests/unit-tests-testng/src/server/org/opends/server/tools/LDAPAuthenticationHandlerTestCase.java b/opends/tests/unit-tests-testng/src/server/org/opends/server/tools/LDAPAuthenticationHandlerTestCase.java
index d77c72c..aebf22d 100644
--- a/opends/tests/unit-tests-testng/src/server/org/opends/server/tools/LDAPAuthenticationHandlerTestCase.java
+++ b/opends/tests/unit-tests-testng/src/server/org/opends/server/tools/LDAPAuthenticationHandlerTestCase.java
@@ -29,7 +29,9 @@
 
 
 import java.io.File;
+import java.net.InetAddress;
 import java.net.Socket;
+import java.net.UnknownHostException;
 import java.util.ArrayList;
 import java.util.LinkedHashMap;
 import java.util.List;
@@ -41,6 +43,7 @@
 
 import org.opends.server.TestCaseUtils;
 import org.opends.messages.Message;
+import org.opends.server.admin.std.server.DigestMD5SASLMechanismHandlerCfg;
 import org.opends.server.api.SASLMechanismHandler;
 import org.opends.server.controls.PasswordPolicyRequestControl;
 import org.opends.server.core.AddOperation;
@@ -63,6 +66,7 @@
 public class LDAPAuthenticationHandlerTestCase
        extends ToolsTestCase
 {
+   String hostname;
   /**
    * Ensures that the Directory Server is running.
    *
@@ -73,6 +77,7 @@
          throws Exception
   {
     TestCaseUtils.startServer();
+    getFQDN();
   }
 
 
@@ -1412,11 +1417,9 @@
     saslProperties.put("authid", propList);
 
     propList = new ArrayList<String>();
-    propList.add("o=test");
-    saslProperties.put("realm", propList);
 
     LDAPAuthenticationHandler authHandler =
-         new LDAPAuthenticationHandler(r, w, "localhost", messageID);
+         new LDAPAuthenticationHandler(r, w, this.hostname, messageID);
     authHandler.doSASLBind(new ASN1OctetString(),
                            new ASN1OctetString("password"),
                            "DIGEST-MD5", saslProperties, requestControls,
@@ -1476,11 +1479,9 @@
     saslProperties.put("authid", propList);
 
     propList = new ArrayList<String>();
-    propList.add("o=test");
-    saslProperties.put("realm", propList);
-
+ 
     LDAPAuthenticationHandler authHandler =
-         new LDAPAuthenticationHandler(r, w, "localhost", messageID);
+         new LDAPAuthenticationHandler(r, w, this.hostname, messageID);
     authHandler.doSASLBind(new ASN1OctetString(),
                            new ASN1OctetString("password"),
                            "DIGEST-MD5", saslProperties, requestControls,
@@ -1793,15 +1794,11 @@
     saslProperties.put("authid", propList);
 
     propList = new ArrayList<String>();
-    propList.add("o=test");
-    saslProperties.put("realm", propList);
-
-    propList = new ArrayList<String>();
     propList.add("auth");
     saslProperties.put("qop", propList);
 
     LDAPAuthenticationHandler authHandler =
-         new LDAPAuthenticationHandler(r, w, "localhost", messageID);
+         new LDAPAuthenticationHandler(r, w, this.hostname, messageID);
     authHandler.doSASLBind(new ASN1OctetString(),
                            new ASN1OctetString("password"),
                            "DIGEST-MD5", saslProperties, requestControls,
@@ -2412,11 +2409,9 @@
     saslProperties.put("authid", propList);
 
     propList = new ArrayList<String>();
-    propList.add("o=test");
-    saslProperties.put("realm", propList);
 
     LDAPAuthenticationHandler authHandler =
-         new LDAPAuthenticationHandler(r, w, "localhost", messageID);
+         new LDAPAuthenticationHandler(r, w, this.hostname, messageID);
     authHandler.doSASLBind(new ASN1OctetString(),
                            new ASN1OctetString("password"),
                            "DIGEST-MD5", saslProperties, requestControls,
@@ -4154,11 +4149,9 @@
     saslProperties.put("authid", propList);
 
     propList = new ArrayList<String>();
-    propList.add("o=test");
-    saslProperties.put("realm", propList);
 
     LDAPAuthenticationHandler authHandler =
-         new LDAPAuthenticationHandler(r, w, "localhost", messageID);
+         new LDAPAuthenticationHandler(r, w, this.hostname, messageID);
     authHandler.doSASLBind(new ASN1OctetString(),
                            new ASN1OctetString("password"),
                            "DIGEST-MD5", saslProperties, requestControls,
@@ -4290,5 +4283,14 @@
 
     s.close();
   }
+
+  private void getFQDN() {
+      try {
+         this.hostname = InetAddress.getLocalHost().getCanonicalHostName();
+      } catch(UnknownHostException ex) {
+         this.hostname = "localhost";
+      }
+  }
+  
 }
 
diff --git a/opends/tests/unit-tests-testng/src/server/org/opends/server/tools/LDAPCompareTestCase.java b/opends/tests/unit-tests-testng/src/server/org/opends/server/tools/LDAPCompareTestCase.java
index d9afd2b..75c8a45 100644
--- a/opends/tests/unit-tests-testng/src/server/org/opends/server/tools/LDAPCompareTestCase.java
+++ b/opends/tests/unit-tests-testng/src/server/org/opends/server/tools/LDAPCompareTestCase.java
@@ -32,6 +32,7 @@
 import java.io.FileWriter;
 import java.util.ArrayList;
 
+import org.testng.annotations.AfterClass;
 import org.testng.annotations.BeforeClass;
 import org.testng.annotations.DataProvider;
 import org.testng.annotations.Test;
@@ -44,7 +45,7 @@
 import org.opends.server.types.OperatingSystem;
 import org.opends.server.types.ResultCode;
 import org.opends.server.util.Base64;
-
+import org.opends.server.tools.dsconfig.DSConfig;
 import static org.testng.Assert.*;
 
 import static org.opends.server.util.ServerConstants.*;
@@ -77,6 +78,12 @@
   {
     TestCaseUtils.startServer();
 
+    
+    TestCaseUtils.dsconfig(
+            "set-sasl-mechanism-handler-prop",
+            "--handler-name", "DIGEST-MD5",
+            "--set", "server-fqdn:" + "127.0.0.1");
+    
     File pwFile = File.createTempFile("valid-bind-password-", ".txt");
     pwFile.deleteOnExit();
     FileWriter fileWriter = new FileWriter(pwFile);
@@ -92,6 +99,14 @@
     invalidPasswordFile = pwFile.getAbsolutePath();
   }
 
+  @AfterClass
+  public void tearDown() throws Exception {
+   
+    TestCaseUtils.dsconfig(
+            "set-sasl-mechanism-handler-prop",
+            "--handler-name", "DIGEST-MD5",
+            "--remove", "server-fqdn:" + "127.0.0.1");
+  }
 
 
   /**
@@ -883,6 +898,7 @@
          "sn: User",
          "cn: Test User",
          "ds-privilege-name: bypass-acl",
+         "ds-privilege-name: proxied-auth",
          "userPassword: password",
          "ds-pwp-password-policy-dn: cn=Clear UserPassword Policy," +
               "cn=Password Policies,cn=config");
@@ -894,7 +910,7 @@
                          e.getOperationalAttributes());
     assertEquals(addOperation.getResultCode(), ResultCode.SUCCESS);
 
-
+    
     String[] args =
     {
       "-h", "127.0.0.1",
@@ -902,7 +918,6 @@
       "-o", "mech=DIGEST-MD5",
       "-o", "authid=u:test.user",
       "-o", "authzid=u:test.user",
-      "-o", "realm=o=test",
       "-w", "password",
       "--noPropertiesFile",
       "givenName:Test",
diff --git a/opends/tests/unit-tests-testng/src/server/org/opends/server/tools/LDAPModifyTestCase.java b/opends/tests/unit-tests-testng/src/server/org/opends/server/tools/LDAPModifyTestCase.java
index 5ba2057..a746aa2 100644
--- a/opends/tests/unit-tests-testng/src/server/org/opends/server/tools/LDAPModifyTestCase.java
+++ b/opends/tests/unit-tests-testng/src/server/org/opends/server/tools/LDAPModifyTestCase.java
@@ -32,6 +32,7 @@
 import java.io.FileWriter;
 import java.util.ArrayList;
 
+import org.testng.annotations.AfterClass;
 import org.testng.annotations.BeforeClass;
 import org.testng.annotations.DataProvider;
 import org.testng.annotations.Test;
@@ -78,7 +79,12 @@
          throws Exception
   {
     TestCaseUtils.startServer();
-
+    
+    TestCaseUtils.dsconfig(
+            "set-sasl-mechanism-handler-prop",
+            "--handler-name", "DIGEST-MD5",
+            "--set", "server-fqdn:" + "127.0.0.1");
+    
     File pwFile = File.createTempFile("valid-bind-password-", ".txt");
     pwFile.deleteOnExit();
     FileWriter fileWriter = new FileWriter(pwFile);
@@ -100,6 +106,14 @@
   }
 
 
+  @AfterClass
+  public void tearDown() throws Exception {
+   
+    TestCaseUtils.dsconfig(
+            "set-sasl-mechanism-handler-prop",
+            "--handler-name", "DIGEST-MD5",
+            "--remove", "server-fqdn:" + "127.0.0.1");
+  }
 
   /**
    * Retrieves sets of invalid arguments that may not be used to initialize
@@ -824,7 +838,6 @@
       "-o", "mech=DIGEST-MD5",
       "-o", "authid=u:test.user",
       "-o", "authzid=u:test.user",
-      "-o", "realm=o=test",
       "-w", "password",
       "--noPropertiesFile",
       "-f", modifyFilePath
diff --git a/opends/tests/unit-tests-testng/src/server/org/opends/server/tools/LDAPSearchTestCase.java b/opends/tests/unit-tests-testng/src/server/org/opends/server/tools/LDAPSearchTestCase.java
index 11250af..b385a40 100644
--- a/opends/tests/unit-tests-testng/src/server/org/opends/server/tools/LDAPSearchTestCase.java
+++ b/opends/tests/unit-tests-testng/src/server/org/opends/server/tools/LDAPSearchTestCase.java
@@ -32,6 +32,7 @@
 import java.io.FileWriter;
 import java.util.ArrayList;
 
+import org.testng.annotations.AfterClass;
 import org.testng.annotations.BeforeClass;
 import org.testng.annotations.DataProvider;
 import org.testng.annotations.Test;
@@ -74,7 +75,12 @@
          throws Exception
   {
     TestCaseUtils.startServer();
-
+    
+    TestCaseUtils.dsconfig(
+            "set-sasl-mechanism-handler-prop",
+            "--handler-name", "DIGEST-MD5",
+            "--set", "server-fqdn:" + "127.0.0.1");
+    
     File pwFile = File.createTempFile("valid-bind-password-", ".txt");
     pwFile.deleteOnExit();
     FileWriter fileWriter = new FileWriter(pwFile);
@@ -91,6 +97,14 @@
   }
 
 
+  @AfterClass
+  public void tearDown() throws Exception {
+   
+    TestCaseUtils.dsconfig(
+            "set-sasl-mechanism-handler-prop",
+            "--handler-name", "DIGEST-MD5",
+            "--remove", "server-fqdn:" + "127.0.0.1");
+  }
 
   /**
    * Retrieves sets of invalid arguments that may not be used to initialize
@@ -1054,7 +1068,6 @@
       "-o", "mech=DIGEST-MD5",
       "-o", "authid=u:test.user",
       "-o", "authzid=u:test.user",
-      "-o", "realm=o=test",
       "-w", "password",
       "-b", "",
       "-s", "base",
diff --git a/opends/tests/unit-tests-testng/src/server/org/opends/server/types/PrivilegeTestCase.java b/opends/tests/unit-tests-testng/src/server/org/opends/server/types/PrivilegeTestCase.java
index 9a06dfc..e174069 100644
--- a/opends/tests/unit-tests-testng/src/server/org/opends/server/types/PrivilegeTestCase.java
+++ b/opends/tests/unit-tests-testng/src/server/org/opends/server/types/PrivilegeTestCase.java
@@ -114,7 +114,12 @@
          throws Exception
   {
     TestCaseUtils.startServer();
-
+    
+    TestCaseUtils.dsconfig(
+            "set-sasl-mechanism-handler-prop",
+            "--handler-name", "DIGEST-MD5",
+            "--set", "server-fqdn:" + "127.0.0.1");
+    
     TestCaseUtils.initializeTestBackend(true);
     TestCaseUtils.addEntries(
       "dn: cn=Unprivileged Root,cn=Root DNs,cn=config",
@@ -303,6 +308,11 @@
   public void cleanUp()
          throws Exception
   {
+   TestCaseUtils.dsconfig(
+            "set-sasl-mechanism-handler-prop",
+            "--handler-name", "DIGEST-MD5",
+            "--remove", "server-fqdn:" + "127.0.0.1");
+   
     InternalClientConnection conn =
          InternalClientConnection.getRootConnection();
 
@@ -1595,7 +1605,6 @@
       "-o", "mech=DIGEST-MD5",
       "-o", "authid=dn:cn=Privileged User,o=test",
       "-o", "authzid=dn:",
-      "-o", "realm=o=test",
       "-w", "password",
       "-b", "o=test",
       "-s", "base",
@@ -1627,7 +1636,6 @@
       "-o", "mech=DIGEST-MD5",
       "-o", "authid=dn:cn=Privileged User,o=test",
       "-o", "authzid=dn:cn=Privileged User,o=test",
-      "-o", "realm=o=test",
       "-w", "password",
       "-b", "o=test",
       "-s", "base",
@@ -1659,7 +1667,6 @@
       "-o", "mech=DIGEST-MD5",
       "-o", "authid=dn:cn=Privileged User,o=test",
       "-o", "authzid=dn:cn=Unprivileged User,o=test",
-      "-o", "realm=o=test",
       "-w", "password",
       "-b", "o=test",
       "-s", "base",
@@ -1691,7 +1698,6 @@
       "-o", "mech=DIGEST-MD5",
       "-o", "authid=u:privileged.user",
       "-o", "authzid=u:",
-      "-o", "realm=o=test",
       "-w", "password",
       "-b", "o=test",
       "-s", "base",
@@ -1723,7 +1729,6 @@
       "-o", "mech=DIGEST-MD5",
       "-o", "authid=u:privileged.user",
       "-o", "authzid=u:privileged.user",
-      "-o", "realm=o=test",
       "-w", "password",
       "-b", "o=test",
       "-s", "base",
@@ -1755,7 +1760,6 @@
       "-o", "mech=DIGEST-MD5",
       "-o", "authid=u:privileged.user",
       "-o", "authzid=u:unprivileged.user",
-      "-o", "realm=o=test",
       "-w", "password",
       "-b", "o=test",
       "-s", "base",
@@ -1787,7 +1791,6 @@
       "-o", "mech=DIGEST-MD5",
       "-o", "authid=dn:cn=Unprivileged User,o=test",
       "-o", "authzid=dn:",
-      "-o", "realm=o=test",
       "-w", "password",
       "-b", "o=test",
       "-s", "base",
@@ -1819,7 +1822,6 @@
       "-o", "mech=DIGEST-MD5",
       "-o", "authid=dn:cn=Unprivileged User,o=test",
       "-o", "authzid=dn:cn=Unprivileged User,o=test",
-      "-o", "realm=o=test",
       "-w", "password",
       "-b", "o=test",
       "-s", "base",
@@ -1851,7 +1853,6 @@
       "-o", "mech=DIGEST-MD5",
       "-o", "authid=dn:cn=Unprivileged User,o=test",
       "-o", "authzid=dn:cn=Privileged User,o=test",
-      "-o", "realm=o=test",
       "-w", "password",
       "-b", "o=test",
       "-s", "base",
@@ -1883,7 +1884,6 @@
       "-o", "mech=DIGEST-MD5",
       "-o", "authid=u:unprivileged.user",
       "-o", "authzid=u:",
-      "-o", "realm=o=test",
       "-w", "password",
       "-b", "o=test",
       "-s", "base",
@@ -1915,7 +1915,6 @@
       "-o", "mech=DIGEST-MD5",
       "-o", "authid=u:unprivileged.user",
       "-o", "authzid=u:unprivileged.user",
-      "-o", "realm=o=test",
       "-w", "password",
       "-b", "o=test",
       "-s", "base",
@@ -1947,7 +1946,6 @@
       "-o", "mech=DIGEST-MD5",
       "-o", "authid=u:unprivileged.user",
       "-o", "authzid=u:privileged.user",
-      "-o", "realm=o=test",
       "-w", "password",
       "-b", "o=test",
       "-s", "base",

--
Gitblit v1.10.0