From d048be119faafbb9d83bb2f0f8995d6070b16d52 Mon Sep 17 00:00:00 2001
From: dugan <dugan@localhost>
Date: Mon, 01 Dec 2008 19:16:36 +0000
Subject: [PATCH] These changes implement a new ACI bind rule keyword "ssf". This keyword allows users to control the level of access based on the security level of the connection.

---
 opends/src/server/org/opends/server/extensions/SASLSecurityProvider.java                              |   10 
 opends/src/server/org/opends/server/authorization/dseecompat/TimeOfDay.java                           |    9 
 opends/src/server/org/opends/server/extensions/SASLContext.java                                       |  102 ++++---
 opends/src/server/org/opends/server/authorization/dseecompat/AciContainer.java                        |    7 
 opends/src/server/org/opends/server/api/ConnectionSecurityProvider.java                               |    7 
 opends/src/server/org/opends/server/extensions/TLSConnectionSecurityProvider.java                     |   51 +++
 opends/tests/unit-tests-testng/src/server/org/opends/server/authorization/dseecompat/SSFTestCase.java |  294 ++++++++++++++++++++++
 opends/src/messages/messages/access_control.properties                                                |    9 
 opends/src/server/org/opends/server/authorization/dseecompat/BindRule.java                            |    5 
 opends/src/server/org/opends/server/authorization/dseecompat/AciEvalContext.java                      |   91 ++++--
 opends/tests/unit-tests-testng/src/server/org/opends/server/authorization/dseecompat/AciTestCase.java |   36 ++
 opends/src/server/org/opends/server/authorization/dseecompat/EnumBindRuleKeyword.java                 |    7 
 opends/src/server/org/opends/server/authorization/dseecompat/SSF.java                                 |  120 +++++++++
 opends/src/server/org/opends/server/extensions/NullConnectionSecurityProvider.java                    |    8 
 14 files changed, 669 insertions(+), 87 deletions(-)

diff --git a/opends/src/messages/messages/access_control.properties b/opends/src/messages/messages/access_control.properties
index b5d7767..d22b932 100644
--- a/opends/src/messages/messages/access_control.properties
+++ b/opends/src/messages/messages/access_control.properties
@@ -364,3 +364,12 @@
 SEVERE_WARN_ACI_ATTRIBUTE_NOT_INDEXED_96=Backend %s does not have a \
  presence index defined for attribute type %s.  Access control initialization \
  may take a very long time to complete in this backend
+ SEVERE_WARN_ACI_SYNTAX_INVALID_SSF_FORMAT_97=The provided Access Control \
+ Instruction (ACI) bind rule SSF expression "%s" failed to parse for \
+ the following reason: "%s"
+ SEVERE_WARN_ACI_SYNTAX_INVALID_SSF_RANGE_98=The provided Access Control \
+ Instruction (ACI) bind rule ssf expression value "%s" is not in the \
+ valid range. A valid ssf value is in the range of 1 to 1024
+ SEVERE_WARN_ACI_SYNTAX_INVALID_TIMEOFDAY_FORMAT_99=The provided Access Control \
+ Instruction (ACI) bind rule timeofday expression "%s" failed to parse for \
+ the following reason: "%s"
diff --git a/opends/src/server/org/opends/server/api/ConnectionSecurityProvider.java b/opends/src/server/org/opends/server/api/ConnectionSecurityProvider.java
index a2a510d..e7f4ca5 100644
--- a/opends/src/server/org/opends/server/api/ConnectionSecurityProvider.java
+++ b/opends/src/server/org/opends/server/api/ConnectionSecurityProvider.java
@@ -223,5 +223,12 @@
    *          already disconnected the client.
    */
   public abstract boolean writeData(ByteBuffer clearData);
+
+  /**
+   * Return the Security Strength Factor.
+   *
+   * @return The SSF.
+   */
+  public abstract int getSSF();
 }
 
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 b661486..6a9c7b0 100644
--- a/opends/src/server/org/opends/server/authorization/dseecompat/AciContainer.java
+++ b/opends/src/server/org/opends/server/authorization/dseecompat/AciContainer.java
@@ -980,4 +980,11 @@
     else
       evalAllAttributes &= ~v;
   }
+
+  /**
+   * {@inheritDoc}
+   */
+  public int getCurrentSSF() {
+      return clientConnection.getConnectionSecurityProvider().getSSF();
+  }
 }
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 e9b437c..ec47623 100644
--- a/opends/src/server/org/opends/server/authorization/dseecompat/AciEvalContext.java
+++ b/opends/src/server/org/opends/server/authorization/dseecompat/AciEvalContext.java
@@ -87,7 +87,7 @@
 
     /**
      * Check if the remote client is bound anonymously.
-     * @return True if client is bound anonymously.
+     * @return {@code true} if client is bound anonymously.
      */
     public boolean isAnonymousUser();
 
@@ -118,6 +118,7 @@
      * @param authMethod The required authentication method.
      * @param saslMech The required SASL mechanism if the authentication method
      * is SASL.
+     *
      * @return An evaluation result indicating whether the client connection
      * has been authenticated using the required authentication method.
      */
@@ -126,14 +127,15 @@
 
     /**
      * Get the  address of the bound connection.
-     * @return The  address of the bound connection.
+     * @return The address of the bound connection.
      */
     public InetAddress getRemoteAddress();
 
     /**
-     * Return true if this is an add operation, needed by the userattr
+     * Return true if this is an add operation needed by the userattr
      * USERDN parent inheritance level 0 processing.
-     * @return True if this is an add operation.
+     *
+     * @return {@code true} if this is an add operation.
      */
     public boolean isAddOperation();
 
@@ -143,60 +145,61 @@
      * ClientConnection.isMemberOf() method, which checks authorization
      * DN membership in the specified group.
      * @param group The group to check membership in.
-     * @return True if the authorization DN of the operation is a
+     * @return {@code true} if the authorization DN of the operation is a
      * member of the specified group.
      */
     public boolean isMemberOf(Group<?> group);
 
   /**
    * Returns true if the hashtable of ACIs that matched the targattrfilters
-   * keyword evaluation is empty.  Used by geteffectiverights evaluation to
-   * determine the access value to put in the "write" rights evaluation field.
+   * keyword evaluation is empty.  Used in a geteffectiverights control
+   * evaluation to determine the access value to put in the "write" rights
+   * evaluation field.
    *
-   * @return True if there were not any ACIs that matched targattrfilters
-   *         keyword evaluation.
+   * @return {@code true} if there were not any ACIs that matched
+   *         targattrfilters keyword evaluation.
    */
     public boolean isTargAttrFilterMatchAciEmpty();
 
   /**
    * The context maintains a hashtable of ACIs that matched the targattrfilters
    * keyword evaluation.  The hasTargAttrFiltersMatchAci method returns true if
-   * the specified ACI is contained in that hashtable. Used by
-   * geteffectiverights evaluation to determine the access value to put in the
-   * "write" rights evaluation field.
+   * the specified ACI is contained in that hashtable. Used in a
+   * geteffectiverights control evaluation to determine the access value to put
+   * in the "write" rights evaluation field.
    *
    * @param aci The ACI that to evaluate if it contains a match during
    *            targattrfilters keyword evaluation.
    *
-   * @return True if a specified ACI matched targattrfilters evaluation.
+   * @return {@code true} if a specified ACI matched targattrfilters evaluation.
    */
     public boolean hasTargAttrFiltersMatchAci(Aci aci);
 
   /**
    * Return true if an ACI that evaluated to deny or allow has an
-   * targattrfilters keyword. Used by geteffectiverights
+   * targattrfilters keyword. Used by geteffectiverights control
    * evaluation to determine the access value to put in the "write" rights
    * evaluation field.
    *
    * @param flag  The integer value specifying either a deny or allow, but not
    * both.
    *
-   * @return   True if the ACI that evaluated to
+   * @return  {@code true} if the ACI has an targetattrfilters keyword.
    */
     public boolean hasTargAttrFiltersMatchOp(int flag);
 
   /**
-   * Returns true if the evaluation context is being used in a
-   * geteffectiverights evaluation.
+   * Returns {@code true} if the evaluation context is being used in a
+   * geteffectiverights control evaluation.
    *
-   * @return  True if the evaluation context is being used in a
-   * geteffectiverights evaluation.
+   * @return  {@code true} if the evaluation context is being used in a
+   * geteffectiverights control evaluation.
    */
     public boolean isGetEffectiveRightsEval();
 
   /**
    * Set the name of the ACI that last matched a targattrfilters rule. Used
-   * in geteffectiverights targattrfilters "write" rights evaluation.
+   * in geteffectiverights control targattrfilters "write" evaluation.
    *
    * @param name The ACI name string matching the targattrfilters rule.
    */
@@ -205,8 +208,8 @@
   /**
    * Set a flag that specifies that a ACI that evaluated to either deny or
    * allow contains a targattrfilters keyword. Used by geteffectiverights
-   * evaluation to determine the access value to put in the "write" rights
-   * evaluation field.
+   * control evaluation to determine the access value to put in the "write"
+   * rights evaluation field.
    *
    * @param flag Either the integer value representing an allow or a deny,
    *             but not both.
@@ -215,7 +218,7 @@
 
   /**
    * Set the reason the last access evaluation was evaluated the way it
-   * was. Used by geteffectiverights evaluation to eventually build the
+   * was. Used by geteffectiverights control evaluation to eventually build the
    * summary string.
    *
    * @param reason  The enumeration representing the reason of the last access
@@ -225,7 +228,8 @@
 
   /**
    * Return the reason the last access evaluation was evaluated the way it
-   * was. Used by geteffectiverights evaluation to build the summary string.
+   * was. Used by geteffectiverights control evaluation to build the summary
+   * string.
    *
    * @return The enumeration representing the reason of the last access
    * evaluation.
@@ -234,7 +238,7 @@
 
   /**
    * Set the ACI that decided that last access evaluation. Used by
-   * geteffectiverights evaluation to the build summary string.
+   * geteffectiverights control evaluation to the build summary string.
    *
    * @param aci The ACI that decided the last access evaluation.
    */
@@ -245,13 +249,13 @@
    *
    * @param rights The rights mask to check.
    *
-   * @return True if the evaluation context contains a access right set.
+   * @return {@code true} if the evaluation context contains a access right set.
    */
     public boolean hasRights(int rights);
 
   /**
    * Return the name of the ACI that decided the last access evaluation. Used
-   * by geteffectiverights evaluation to build the summmary string.
+   * by geteffectiverights control evaluation to build the summary string.
    *
    * @return The name of the ACI that decided the last access evaluation.
    */
@@ -259,10 +263,10 @@
 
   /**
    * Return true if a evaluation context is being used in proxied authorization
-   * evaluation.
+   * control evaluation.
    *
-   * @return  True if evaluation context is being used in proxied authorization
-   * evaluation.
+   * @return  {@code true} if evaluation context is being used in proxied
+   *          authorization control evaluation.
    */
     public boolean isProxiedAuthorization();
 
@@ -275,15 +279,16 @@
 
   /**
    * Set the value of the summary string to the specified string.
-   * Used in geteffectiverights evaluation to build summary string.
+   * Used in get effective rights evaluation to build summary string.
    *
    * @param summary The string to set the summary string to
    */
     public void setEvalSummary(String summary);
 
   /**
-   * Return the access evaluation summary string. Used by the geteffectiverights
-   * evaluation when a aclRightsInfo attribute was specified in a search.
+   * Return the access evaluation summary string. Used in a geteffectiverights
+   * control evaluation when an aclRightsInfo attribute was specified in a
+   * search request.
    *
    * @return   The string describing the access evaluation.
    */
@@ -291,7 +296,7 @@
 
   /**
    * Return a string representation of the current right being evaluated.
-   * Used in geteffectiverights evaluation to build summary string.
+   * Used in geteffectiverights control evaluation to build summary string.
    *
    * @return  String representation of the current right being evaluated.
    */
@@ -299,9 +304,9 @@
 
     /**
    * Return the name of the ACI that last matched a targattrfilters rule. Used
-   * in geteffectiverights evaluation.
+   * in geteffectiverights control evaluation.
    *
-   * @return   The name of the ACI that last matched a targattrfilters rule.
+   * @return The name of the ACI that last matched a targattrfilters rule.
    */
     public String getTargAttrFiltersAciName();
 
@@ -315,9 +320,19 @@
    * This method is used to replace the current resource entry with that saved
    * entry and back.
    *
-   * @param val Specifies if the saved entry should be used or not. True if it
-   * should be used, false if the original resource entry should be used.
+   * @param val Specifies if the saved entry should be used or not. {@code true}
+   * if it should be used, {@code false} if the original resource entry should
+   * be used.
    *
    */
     public void useFullResourceEntry(boolean val);
+
+
+    /**
+     * Return the current SSF (Security Strength Factor) of the underlying
+     * connection.
+     *
+     * @return The current SSF of the connection.
+     */
+    public int getCurrentSSF();
 }
diff --git a/opends/src/server/org/opends/server/authorization/dseecompat/BindRule.java b/opends/src/server/org/opends/server/authorization/dseecompat/BindRule.java
index 8ab34e6..18e3611 100644
--- a/opends/src/server/org/opends/server/authorization/dseecompat/BindRule.java
+++ b/opends/src/server/org/opends/server/authorization/dseecompat/BindRule.java
@@ -534,6 +534,11 @@
                 rule = UserAttr.decode(expr, op);
                 break;
             }
+            case SSF:
+            {
+                rule = SSF.decode(expr, op);
+                break;
+            }
             default:  {
                 Message message = WARN_ACI_SYNTAX_INVALID_BIND_RULE_KEYWORD.get(
                     keyword.toString());
diff --git a/opends/src/server/org/opends/server/authorization/dseecompat/EnumBindRuleKeyword.java b/opends/src/server/org/opends/server/authorization/dseecompat/EnumBindRuleKeyword.java
index e645fe2..5772743 100644
--- a/opends/src/server/org/opends/server/authorization/dseecompat/EnumBindRuleKeyword.java
+++ b/opends/src/server/org/opends/server/authorization/dseecompat/EnumBindRuleKeyword.java
@@ -77,7 +77,12 @@
      * The enumeration type when the bind rule has specified keyword of
      * authmethod.
      */
-    AUTHMETHOD ("authmethod");
+    AUTHMETHOD ("authmethod"),
+    /**
+     * The enumeration type when the bind rule has specified keyword of
+     * ssf.
+     */
+    SSF("ssf");
 
     /*
      * The keyword name.
diff --git a/opends/src/server/org/opends/server/authorization/dseecompat/SSF.java b/opends/src/server/org/opends/server/authorization/dseecompat/SSF.java
new file mode 100644
index 0000000..0f227b2
--- /dev/null
+++ b/opends/src/server/org/opends/server/authorization/dseecompat/SSF.java
@@ -0,0 +1,120 @@
+/*
+ * 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.authorization.dseecompat;
+
+import org.opends.messages.Message;
+import static org.opends.messages.AccessControlMessages.*;
+
+/**
+ * The class represents the ssf keyword in a bind rule.SSF stands for
+ * security strength factor.
+ *
+ */
+public class SSF implements KeywordBindRule {
+
+    /*
+     *  Enumeration representing the bind rule operation type.
+     */
+    private EnumBindRuleType type=null;
+
+    private static final int MAX_KEY_BITS=1024;
+    private int ssf;
+
+    private SSF(int ssf, EnumBindRuleType type) {
+        this.ssf = ssf;
+        this.type = type;
+    }
+
+    /**
+     * Create SSF instance using the specified expression string and bind rule
+     * type enumeration.
+     * @param expr The expression string.
+     * @param type The bind rule type enumeration.
+     * @return A SSF instance.
+     * @throws AciException If the SSF instance cannot be created.
+     */
+    static SSF
+    decode(String expr, EnumBindRuleType type) throws AciException  {
+        int valueAsInt = 0;
+        try {
+            valueAsInt = Integer.parseInt(expr);
+        } catch (NumberFormatException nfe) {
+            Message message =
+                 WARN_ACI_SYNTAX_INVALID_SSF_FORMAT.get(expr, nfe.getMessage());
+            throw new AciException(message);
+        }
+        if ((valueAsInt <= 0) || (valueAsInt > MAX_KEY_BITS)) {
+            Message message = WARN_ACI_SYNTAX_INVALID_SSF_RANGE.get(expr);
+            throw new AciException(message);
+        }
+        return new SSF(valueAsInt, type);
+    }
+
+    /**
+     * Evaluate the specified evaluation context.
+     * @param evalCtx The evaluation context to evaluate.
+     *
+     * @return An evaluation result enumeration containing the result of the
+     *         context evaluation.
+     */
+    public EnumEvalResult evaluate(AciEvalContext evalCtx) {
+        EnumEvalResult matched=EnumEvalResult.FALSE;
+        int currentSSF = evalCtx.getCurrentSSF();
+        switch (type) {
+        case EQUAL_BINDRULE_TYPE:
+            if (currentSSF == ssf)
+                matched=EnumEvalResult.TRUE;
+            break;
+
+        case NOT_EQUAL_BINDRULE_TYPE:
+            if (currentSSF != ssf)
+                matched=EnumEvalResult.TRUE;
+            break;
+
+        case LESS_OR_EQUAL_BINDRULE_TYPE:
+            if (currentSSF <= ssf)
+                matched=EnumEvalResult.TRUE;
+            break;
+
+        case LESS_BINDRULE_TYPE:
+            if (currentSSF < ssf)
+                matched=EnumEvalResult.TRUE;
+            break;
+
+        case GREATER_OR_EQUAL_BINDRULE_TYPE:
+            if (currentSSF >= ssf)
+                matched=EnumEvalResult.TRUE;
+            break;
+
+        case GREATER_BINDRULE_TYPE:
+            if (currentSSF > ssf)
+                matched=EnumEvalResult.TRUE;
+        }
+        return matched.getRet(type, false);
+    }
+}
\ No newline at end of file
diff --git a/opends/src/server/org/opends/server/authorization/dseecompat/TimeOfDay.java b/opends/src/server/org/opends/server/authorization/dseecompat/TimeOfDay.java
index 6d62ccf..4a7358f 100644
--- a/opends/src/server/org/opends/server/authorization/dseecompat/TimeOfDay.java
+++ b/opends/src/server/org/opends/server/authorization/dseecompat/TimeOfDay.java
@@ -71,12 +71,19 @@
      */
     public static TimeOfDay decode(String expr,  EnumBindRuleType type)
     throws AciException  {
+        int valueAsInt = 0;
         if (!Pattern.matches(timeofdayRegex, expr))
         {
             Message message = WARN_ACI_SYNTAX_INVALID_TIMEOFDAY.get(expr);
             throw new AciException(message);
          }
-        int valueAsInt = Integer.parseInt(expr);
+        try {
+            valueAsInt = Integer.parseInt(expr);
+        } catch (NumberFormatException nfe) {
+          Message message =
+           WARN_ACI_SYNTAX_INVALID_TIMEOFDAY_FORMAT.get(expr, nfe.getMessage());
+            throw new AciException(message);
+        }
         if ((valueAsInt < 0) || (valueAsInt > 2359))
         {
             Message message = WARN_ACI_SYNTAX_INVALID_TIMEOFDAY_RANGE.get(expr);
diff --git a/opends/src/server/org/opends/server/extensions/NullConnectionSecurityProvider.java b/opends/src/server/org/opends/server/extensions/NullConnectionSecurityProvider.java
index 0aa9ae0..3563114 100644
--- a/opends/src/server/org/opends/server/extensions/NullConnectionSecurityProvider.java
+++ b/opends/src/server/org/opends/server/extensions/NullConnectionSecurityProvider.java
@@ -498,5 +498,13 @@
       }
     }
   }
+
+  /**
+   * Always returns 0, there is no cipher used.
+   * @return Returns 0 always.
+   */
+  public int getSSF() {
+      return 0;
+  }
 }
 
diff --git a/opends/src/server/org/opends/server/extensions/SASLContext.java b/opends/src/server/org/opends/server/extensions/SASLContext.java
index 2ccc27f..c4ba120 100644
--- a/opends/src/server/org/opends/server/extensions/SASLContext.java
+++ b/opends/src/server/org/opends/server/extensions/SASLContext.java
@@ -86,7 +86,7 @@
     private String serverFQDN;
 
     //The SASL mechanism name.
-    private final String mech;
+    private final String mechanism;
 
     //The authorization entry used in the authentication.
     private Entry authEntry=null;
@@ -116,19 +116,19 @@
      * @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 mechanism 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)
+                          String mechanism, IdentityMapper<?> identityMapper)
                           throws SaslException {
         this.identityMapper = identityMapper;
-        this.mech = mech;
+        this.mechanism = mechanism;
         this.saslProps = saslProps;
         this.serverFQDN = serverFQDN;
-        if(mech.equals(SASL_MECHANISM_DIGEST_MD5)) {
+        if(mechanism.equals(SASL_MECHANISM_DIGEST_MD5)) {
             initSASLServer();
         }
     }
@@ -141,7 +141,7 @@
      * @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 mechanism 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.
@@ -150,21 +150,19 @@
      */
     public static
     SASLContext createSASLContext(HashMap<String,String>saslProps,
-                        String serverFQDN, String mech,
+                        String serverFQDN, String mechanism,
                         IdentityMapper<?> identityMapper) throws SaslException {
-        return (new SASLContext(saslProps,serverFQDN, mech, identityMapper));
+      return (new SASLContext(saslProps,serverFQDN, mechanism, 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);
+       this.saslServer = Sasl.createSaslServer(mechanism, SASL_DEFAULT_PROTOCOL,
+                                               serverFQDN, saslProps, this);
     }
 
 
@@ -189,7 +187,7 @@
     /**
      * Unwrap the specified byte array using the provided offset and length
      * values. Used only when the SASL server has negotiated
-     * confidentiality/integrity  processing.
+     * confidentiality or integrity processing.
      *
      * @param bytes The byte array to unwrap.
      * @param offset The offset in the array.
@@ -230,15 +228,38 @@
           return Integer.parseInt(sizeStr);
     }
 
+    /**
+     * Return the Security Strength Factor of the cipher if the QOP property
+     * is confidentiality, or, 1 if it is integrity.
+     *
+     * @return The SSF of the cipher used during confidentiality or
+     *         integrity processing.
+     */
+    int getSSF() {
+        int ssf = 0;
+        String qop = (String) saslServer.getNegotiatedProperty(Sasl.QOP);
+        if(qop.equalsIgnoreCase(integrity)) {
+            ssf = 1;
+        } else {
+            String negStrength =
+                (String) saslServer.getNegotiatedProperty(Sasl.STRENGTH);
+            if(negStrength.equalsIgnoreCase("low"))
+                ssf = 40;
+            else if (negStrength.equalsIgnoreCase("medium"))
+                ssf = 56;
+            else
+                ssf = 128;
+        }
+        return ssf;
+    }
 
     /**
-     * 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 bind has been completed. If the context is
+     * supporting confidentiality or integrity, the security provider will need
+     * to check if the context has completed the handshake with the client
+     * and is ready to process confidentiality or integrity messages.
      *
-     * @return {@code true} if the handshaking is complete,
-     *         {@code false} if further handshaking is needed.
+     * @return {@code true} if the handshaking is complete.
      */
     boolean isBindComplete() {
           return saslServer.isComplete();
@@ -247,10 +268,10 @@
 
     /**
      * Return true if the SASL server has negotiated with the client to support
-     * confidentiality/integrity.
+     * confidentiality or integrity.
      *
-     * @return {@code true} if the context support
-     *         confidentiality/integrity, or, {@code false} otherwise.
+     * @return {@code true} if the context supports confidentiality or
+     *         integrity.
      */
     private boolean isConfidentialIntegrity() {
       boolean ret = false;
@@ -312,7 +333,7 @@
      *
      * @param authInfo The authentication information to use in the check.
      * @return {@code true} if the authentication information has
-     *         PROXIED_AUTH privileges, {@code false} otherwise.
+     *         PROXIED_AUTH privileges.
      */
     private boolean
     hasPrivilege(AuthenticationInfo authInfo) {
@@ -334,7 +355,7 @@
      *
      * @param authInfo The authentication information to check access on.
      * @return {@code true} if the authentication information has
-     *         proxy access, {@code false} otherwise.
+     *         proxy access.
      */
     private boolean
     hasPermission(AuthenticationInfo authInfo) {
@@ -388,7 +409,7 @@
                 authorizeCallback((AuthorizeCallback) callback);
             } else {
                 Message message =
-                    INFO_SASL_UNSUPPORTED_CALLBACK.get(mech,
+                    INFO_SASL_UNSUPPORTED_CALLBACK.get(mechanism,
                                                       String.valueOf(callback));
                 throw new UnsupportedCallbackException(callback,
                         message.toString());
@@ -586,7 +607,7 @@
           clearPasswords = pwPolicyState.getClearPasswords();
           if ((clearPasswords == null) || clearPasswords.isEmpty()) {
               setCallbackMsg(
-                 ERR_SASL_NO_REVERSIBLE_PASSWORDS.get(mech,
+                 ERR_SASL_NO_REVERSIBLE_PASSWORDS.get(mechanism,
                                             String.valueOf(authEntry.getDN())));
             return;
           }
@@ -596,7 +617,7 @@
                 TRACER.debugCaught(DebugLogLevel.ERROR, e);
             }
             setCallbackMsg(ERR_SASL_CANNOT_GET_REVERSIBLE_PASSWORDS.get(
-                    String.valueOf(authEntry.getDN()),mech,
+                    String.valueOf(authEntry.getDN()),mechanism,
                     String.valueOf(e)));
           return;
         }
@@ -625,13 +646,13 @@
                     TRACER.debugCaught(DebugLogLevel.ERROR, e);
                 }
                  setCallbackMsg(ERR_SASL_CANNOT_DECODE_USERNAME_AS_DN.get(
-                                      mech,
+                                      mechanism,
                                       userName, e.getMessageObject()));
                 return;
             }
             if (userDN.isNullDN()) {
               setCallbackMsg(ERR_SASL_USERNAME_IS_NULL_DN.get(
-                                                      mech));
+                                                      mechanism));
               return;
             }
             DN rootDN = DirectoryServer.getActualRootBindDN(userDN);
@@ -646,7 +667,7 @@
             if (lowerUserName.startsWith("u:")) {
                 if (lowerUserName.equals("u:")) {
                     setCallbackMsg(ERR_SASL_ZERO_LENGTH_USERNAME.get(
-                            mech,mech));
+                            mechanism,mechanism));
                     return;
                 }
                 entryID = userName.substring(2);
@@ -707,8 +728,7 @@
     * 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.
+    * @return {@code true} if the authentication processing was successful.
     */
    public Boolean run() {
        ClientConnection clientConn = bindOp.getClientConnection();
@@ -751,7 +771,7 @@
                bindOp.setSASLAuthUserEntry(authEntry);
                AuthenticationInfo authInfo =
                     new AuthenticationInfo(authEntry, authzEntry,
-                                    mech,
+                                    mechanism,
                                    DirectoryServer.isRootDN(authEntry.getDN()));
                bindOp.setAuthenticationInfo(authInfo);
                //If confidentiality/integrity has been negotiated then
@@ -760,7 +780,7 @@
                //negotiated, dispose of the SASL server.
                if(isConfidentialIntegrity()) {
                    SASLSecurityProvider secProvider =
-                       new SASLSecurityProvider(clientConn, mech, this);
+                       new SASLSecurityProvider(clientConn, mechanism, this);
                    LDAPClientConnection ldapConn =
                        (LDAPClientConnection) clientConn;
                        ldapConn.setSASLConnectionSecurityProvider(secProvider);
@@ -778,7 +798,7 @@
                TRACER.debugCaught(DebugLogLevel.ERROR, e);
            }
            Message msg =
-               ERR_SASL_PROTOCOL_ERROR.get(mech, getExceptionMessage(e));
+               ERR_SASL_PROTOCOL_ERROR.get(mechanism, getExceptionMessage(e));
            handleError(msg);
            return false;
        }
@@ -806,7 +826,7 @@
                TRACER.debugCaught(DebugLogLevel.ERROR, e);
            }
            Message msg =
-               ERR_SASL_PROTOCOL_ERROR.get(mech, getExceptionMessage(e));
+               ERR_SASL_PROTOCOL_ERROR.get(mechanism, getExceptionMessage(e));
            handleError(msg);
        }
    }
@@ -832,7 +852,7 @@
                TRACER.debugCaught(DebugLogLevel.ERROR, e);
            }
            Message msg =
-               ERR_SASL_PROTOCOL_ERROR.get(mech,getExceptionMessage(e));
+               ERR_SASL_PROTOCOL_ERROR.get(mechanism,getExceptionMessage(e));
            handleError(msg);
        }
    }
@@ -850,7 +870,7 @@
        if ((clientCredentials == null) ||
                (clientCredentials.value().length == 0)) {
            Message msg =
-               ERR_SASL_NO_CREDENTIALS.get(mech, mech);
+               ERR_SASL_NO_CREDENTIALS.get(mechanism, mechanism);
            handleError(msg);
            return;
        }
@@ -866,7 +886,7 @@
            bindOp.setSASLAuthUserEntry(authEntry);
            AuthenticationInfo authInfo =
                 new AuthenticationInfo(authEntry, authzEntry,
-                                       mech,
+                                       mechanism,
                                   DirectoryServer.isRootDN(authEntry.getDN()));
            bindOp.setAuthenticationInfo(authInfo);
            //If confidentiality/integrity has been negotiated, then create a
@@ -874,7 +894,7 @@
            //use in later processing.
            if(isConfidentialIntegrity()) {
                SASLSecurityProvider secProvider =
-                   new SASLSecurityProvider(clientConn, mech, this);
+                   new SASLSecurityProvider(clientConn, mechanism, this);
                LDAPClientConnection ldapConn =
                    (LDAPClientConnection) clientConn;
                ldapConn.setSASLConnectionSecurityProvider(secProvider);
@@ -887,7 +907,7 @@
                TRACER.debugCaught(DebugLogLevel.ERROR, e);
            }
            Message msg =
-               ERR_SASL_PROTOCOL_ERROR.get(mech, getExceptionMessage(e));
+               ERR_SASL_PROTOCOL_ERROR.get(mechanism, getExceptionMessage(e));
            handleError(msg);
        }
    }
diff --git a/opends/src/server/org/opends/server/extensions/SASLSecurityProvider.java b/opends/src/server/org/opends/server/extensions/SASLSecurityProvider.java
index 0ee7e0c..64a64b7 100644
--- a/opends/src/server/org/opends/server/extensions/SASLSecurityProvider.java
+++ b/opends/src/server/org/opends/server/extensions/SASLSecurityProvider.java
@@ -454,4 +454,14 @@
     public boolean isActive() {
         return saslContext.isBindComplete();
     }
+
+    /**
+     * Return the cipher Security Strength Function of the cipher used in the
+     * SSAL context.
+     *
+     * @return The cipher SSF of the cipher used in the SASL context.
+     */
+    public int getSSF() {
+        return saslContext.getSSF();
+    }
 }
diff --git a/opends/src/server/org/opends/server/extensions/TLSConnectionSecurityProvider.java b/opends/src/server/org/opends/server/extensions/TLSConnectionSecurityProvider.java
index 7b26abc..9b99eaa 100644
--- a/opends/src/server/org/opends/server/extensions/TLSConnectionSecurityProvider.java
+++ b/opends/src/server/org/opends/server/extensions/TLSConnectionSecurityProvider.java
@@ -34,6 +34,8 @@
 import java.nio.ByteBuffer;
 import java.nio.channels.SocketChannel;
 import java.security.cert.Certificate;
+import java.util.LinkedHashMap;
+import java.util.Map;
 import javax.net.ssl.SSLContext;
 import javax.net.ssl.SSLEngine;
 import javax.net.ssl.SSLEngineResult;
@@ -76,16 +78,12 @@
    */
   private static final DebugTracer TRACER = getTracer();
 
-
-
   /**
    * The SSL context name that should be used for this TLS connection security
    * provider.
    */
   private static final String SSL_CONTEXT_INSTANCE_NAME = "TLS";
 
-
-
   // The buffer that will be used when reading clear-text data.
   private ByteBuffer clearInBuffer;
 
@@ -127,6 +125,27 @@
   // The set of protocols to allow.
   private String[] enabledProtocols;
 
+  //Map of cipher phrases to effective key size (bits). Taken from the
+  //following RFCs: 5289, 4346, 3268,4132 and 4162.
+  private static final Map<String, Integer> cipherMap;
+
+  static {
+      cipherMap = new LinkedHashMap<String, Integer>();
+      cipherMap.put("_WITH_AES_256_CBC_", new Integer(256));
+      cipherMap.put("_WITH_CAMELLIA_256_CBC_", new Integer(256));
+      cipherMap.put("_WITH_AES_256_GCM_", new Integer(256));
+      cipherMap.put("_WITH_3DES_EDE_CBC_", new Integer(168));
+      cipherMap.put("_WITH_AES_128_GCM_", new Integer(128));
+      cipherMap.put("_WITH_SEED_CBC_", new Integer(128));
+      cipherMap.put("_WITH_CAMELLIA_128_CBC_", new Integer(128));
+      cipherMap.put("_WITH_AES_128_CBC_", new Integer(128));
+      cipherMap.put("_WITH_IDEA_CBC_", new Integer(128));
+      cipherMap.put("_WITH_DES_CBC_", new Integer(56));
+      cipherMap.put("_WITH_RC2_CBC_40_", new Integer(40));
+      cipherMap.put("_WITH_RC4_40_", new Integer(40));
+      cipherMap.put("_WITH_DES40_CBC_", new Integer(40));
+      cipherMap.put("_WITH_NULL_", new Integer(0));
+  };
 
 
   /**
@@ -175,7 +194,7 @@
 
     // Create an SSL session based on the configured key and trust stores in the
     // Directory Server.
-    KeyManagerProvider keyManagerProvider =
+    KeyManagerProvider<?> keyManagerProvider =
          DirectoryServer.getKeyManagerProvider(
               clientConnection.getKeyManagerProviderDN());
     if (keyManagerProvider == null)
@@ -183,7 +202,7 @@
       keyManagerProvider = new NullKeyManagerProvider();
     }
 
-    TrustManagerProvider trustManagerProvider =
+    TrustManagerProvider<?> trustManagerProvider =
          DirectoryServer.getTrustManagerProvider(
               clientConnection.getTrustManagerProviderDN());
     if (trustManagerProvider == null)
@@ -1039,5 +1058,25 @@
       return null;
     }
   }
+
+
+  /**
+   * Return the Security Strength FActor of the cipher used in the current
+   * TLS session.
+   *
+   * @return The cipher SSF used in the current TLS session.
+   */
+
+  public int getSSF() {
+      int cipherKeySSF = 0;
+      String cipherString = sslEngine.getSession().getCipherSuite();
+      for(Map.Entry<String, Integer> mapEntry : cipherMap.entrySet()) {
+          if(cipherString.indexOf(mapEntry.getKey()) >= 0) {
+              cipherKeySSF = mapEntry.getValue().intValue();
+              break;
+          }
+      }
+      return cipherKeySSF;
+  }
 }
 
diff --git a/opends/tests/unit-tests-testng/src/server/org/opends/server/authorization/dseecompat/AciTestCase.java b/opends/tests/unit-tests-testng/src/server/org/opends/server/authorization/dseecompat/AciTestCase.java
index 5cef3bc..eb6f158 100644
--- a/opends/tests/unit-tests-testng/src/server/org/opends/server/authorization/dseecompat/AciTestCase.java
+++ b/opends/tests/unit-tests-testng/src/server/org/opends/server/authorization/dseecompat/AciTestCase.java
@@ -29,6 +29,8 @@
 
 import org.opends.server.DirectoryServerTestCase;
 import org.opends.server.TestCaseUtils;
+import org.opends.server.core.DirectoryServer;
+import org.opends.server.protocols.ldap.LDAPResultCode;
 import org.opends.server.tools.LDAPModify;
 import org.opends.server.tools.LDAPSearch;
 import org.opends.server.tools.LDAPDelete;
@@ -38,10 +40,24 @@
 import org.testng.Assert;
 
 import java.io.*;
+import java.util.Hashtable;
 import java.util.Map;
 import java.util.HashMap;
 import java.util.ArrayList;
 
+import javax.naming.Context;
+import javax.naming.NamingException;
+import javax.naming.NoPermissionException;
+import javax.naming.directory.AttributeModificationException;
+import javax.naming.directory.BasicAttribute;
+import javax.naming.directory.DirContext;
+import javax.naming.directory.InitialDirContext;
+import javax.naming.directory.ModificationItem;
+import javax.naming.ldap.InitialLdapContext;
+import javax.naming.ldap.LdapContext;
+import javax.naming.ldap.StartTlsRequest;
+import javax.naming.ldap.StartTlsResponse;
+
 
 @Test(groups = {"precommit", "dseecompat"}, sequential = true)
 public abstract class  AciTestCase extends DirectoryServerTestCase {
@@ -335,6 +351,25 @@
     ldapModify(argList.toArray(args), rc);
   }
 
+  protected void JNDIModify(Hashtable<?, ?> env, String name,
+                            String attr, String val, int rc) {
+      try {
+          DirContext ctx = new InitialDirContext(env);
+          ModificationItem[] mods = new ModificationItem[1 ];
+          mods[0] = new ModificationItem(DirContext.ADD_ATTRIBUTE,
+                  new BasicAttribute(attr, val));
+          ctx.modifyAttributes(name, mods);
+          ctx.close();
+      } catch (NoPermissionException npe) {
+          Assert.assertEquals(LDAPResultCode.INSUFFICIENT_ACCESS_RIGHTS,  rc,
+                              "Returned error: " + npe.getMessage());
+          return;
+      } catch (NamingException ex) {
+          Assert.assertEquals(-1,  rc, "Returned error: " + ex.getMessage());
+      }
+      Assert.assertEquals(LDAPResultCode.SUCCESS, rc, "");
+  }
+
   private void ldapModify(String[] args, int rc) {
     oStream.reset();
     int retVal =LDAPModify.mainModify(args, false, oStream, oStream);
@@ -616,4 +651,5 @@
     }
     return attrMap;
   }
+
 }
diff --git a/opends/tests/unit-tests-testng/src/server/org/opends/server/authorization/dseecompat/SSFTestCase.java b/opends/tests/unit-tests-testng/src/server/org/opends/server/authorization/dseecompat/SSFTestCase.java
new file mode 100644
index 0000000..0011e41
--- /dev/null
+++ b/opends/tests/unit-tests-testng/src/server/org/opends/server/authorization/dseecompat/SSFTestCase.java
@@ -0,0 +1,294 @@
+/*
+ * 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.
+ */
+
+/**
+ * Unit test to test the ssf ACI bind rule keyword.
+ */
+
+package org.opends.server.authorization.dseecompat;
+
+import java.util.Hashtable;
+
+import javax.naming.Context;
+
+import org.opends.server.TestCaseUtils;
+import org.opends.server.protocols.ldap.LDAPResultCode;
+import org.testng.annotations.*;
+import static org.opends.server.config.ConfigConstants.*;
+
+public class SSFTestCase extends AciTestCase {
+
+    private static final String newUser="uid=new.user,ou=People,o=test";
+    private static final String descriptionStr = "description of user.1";
+    private static final String factory = "com.sun.jndi.ldap.LdapCtxFactory";
+    private static final String pwdPolicy = "Aci Temp Policy";
+    private static final String pwdPolicyDN =
+                       "cn=" + pwdPolicy + ",cn=Password Policies,cn=config";
+
+    private static final String[] newEntry = new String[] {
+        "objectClass: top",
+        "objectClass: person",
+        "objectClass: organizationalPerson",
+        "objectClass: inetOrgPerson",
+        "uid: new.user",
+        "givenName: New",
+        "sn: User",
+        "cn: New User",
+        "mail: new.user@test.com",
+        "ds-pwp-password-policy-dn:" + pwdPolicyDN
+      };
+
+    private static final
+    String integrityACI = "(targetattr=\"" + "*" + "\")" +
+            "(version 3.0; acl \"integrity aci\";" +
+            "allow(all) (userdn=\"ldap:///self\" and ssf = \"1\");)";
+
+    private static final
+    String greaterIntegrityACI = "(targetattr=\"" + "*" + "\")" +
+            "(version 3.0; acl \"greater integrity aci\";" +
+            "allow(all) (userdn=\"ldap:///self\" and ssf > \"1\");)";
+
+
+    private static final
+    String medStrengthACI = "(targetattr=\"" + "*" + "\")" +
+            "(version 3.0; acl \"56 bit key aci\";" +
+            "allow(all) (userdn=\"ldap:///self\" and ssf = \"56\");)";
+
+
+    private static final
+    String hiStrengthACI = "(targetattr=\"" + "*" + "\")" +
+            "(version 3.0; acl \"128 bit key aci\";" +
+            "allow(all) (userdn=\"ldap:///self\" and ssf = \"128\");)";
+
+
+    private static final
+    String hiPlusStrengthACI = "(targetattr=\"" + "*" + "\")" +
+            "(version 3.0; acl \"greater 128 bit aci\";" +
+            "allow(all) (userdn=\"ldap:///self\" and ssf > \"128\");)";
+
+
+    @BeforeClass
+    public void setupClass() throws Exception {
+      TestCaseUtils.startServer();
+      TestCaseUtils.dsconfig(
+              "create-password-policy",
+              "--policy-name", pwdPolicy,
+              "--set", "password-attribute:userPassword",
+              "--set", "default-password-storage-scheme: Clear"
+              );
+      TestCaseUtils.dsconfig(
+              "set-sasl-mechanism-handler-prop",
+              "--handler-name", "DIGEST-MD5",
+              "--set", "server-fqdn:localhost");
+      deleteAttrFromAdminEntry(ACCESS_HANDLER_DN, ATTR_AUTHZ_GLOBAL_ACI);
+      String aciLdif=makeAddLDIF(ATTR_AUTHZ_GLOBAL_ACI, ACCESS_HANDLER_DN,
+                    G_SCHEMA, G_DSE, G_USER_OPS, G_CONTROL, E_EXTEND_OP);
+      LDIFAdminModify(aciLdif, DIR_MGR_DN, PWD);
+      addEntries("o=test");
+      String newUserLDIF=makeAddEntryLDIF(newUser, newEntry);
+      LDIFAdd(newUserLDIF, DIR_MGR_DN, PWD, null, LDAPResultCode.SUCCESS);
+      String pwdILDIF =
+          makeAddLDIF("userpassword", newUser, "password");
+      LDIFModify(pwdILDIF, DIR_MGR_DN, PWD);
+    }
+
+    @AfterClass(alwaysRun = true)
+    public void tearDown() throws Exception {
+         String aciLdif=makeAddLDIF(ATTR_AUTHZ_GLOBAL_ACI, ACCESS_HANDLER_DN,
+                G_READ_ACI, G_SELF_MOD, G_SCHEMA, G_DSE, G_USER_OPS, G_CONTROL,
+                E_EXTEND_OP);
+         LDIFAdminModify(aciLdif, DIR_MGR_DN, PWD);
+         TestCaseUtils.dsconfig(
+                 "delete-password-policy",
+                 "--policy-name", pwdPolicy
+                 );
+         TestCaseUtils.dsconfig(
+                 "set-sasl-mechanism-handler-prop",
+                 "--handler-name", "DIGEST-MD5",
+                 "--reset", "server-fqdn",
+                 "--reset", "quality-of-protection");
+
+     }
+
+    //Valid ssf statements. Not the complete ACI.
+    @DataProvider(name = "validStatements")
+    public Object[][] valids() {
+      return new Object[][] {
+              {"1"},
+              {"40"},
+              {"56"},
+              {"128"},
+              {"256"},
+              {"129"},
+      };
+    }
+
+     //Invalid ssf statements. Not the complete ACI.
+    @DataProvider(name = "invalidStatements")
+    public Object[][] invalids() {
+      return new Object[][] {
+              {"-1"},
+              {"0"},
+              {"not valid"},
+              {"1025"},
+              {"10000"},
+      };
+    }
+
+    private EnumBindRuleType bindRuleType = EnumBindRuleType.EQUAL_BINDRULE_TYPE;
+
+    /**
+     * Test valid ssf statements.
+     *
+     * @param statement The ssf statement to attempt to decode.
+     * @throws AciException  If an unexpected result happens.
+     */
+    @Test(dataProvider = "validStatements")
+    public void testValidStatements(String statement) throws AciException {
+        SSF.decode(statement, bindRuleType);
+    }
+
+    /**
+     * Test invalid ssf statements.
+     *
+     * @param statement The ssf statement to attempt to decode.
+     * @throws Exception  If an unexpected result happens.
+     */
+    @Test(expectedExceptions= AciException.class,
+          dataProvider="invalidStatements")
+    public void testInvalidStatements(String statement)  throws Exception {
+      try {
+        SSF.decode(statement, bindRuleType);
+      } catch (AciException e) {
+        throw e;
+      } catch (Exception e) {
+        System.out.println(
+                "Invalid ssf  <" + statement +
+                "> threw wrong exception type.");
+        throw e;
+      }
+      throw new RuntimeException(
+              "Invalid ssf <" + statement +
+              "> did not throw an exception.");
+    }
+
+    /**
+     * Test ssf bind rule using ssf value for integrity.
+     *
+     * @throws Exception If a test doesn't pass.
+     */
+    @Test()
+    public void testIntegrity() throws Exception {
+        //set QOP to integrity.
+        TestCaseUtils.dsconfig(
+                "set-sasl-mechanism-handler-prop",
+                "--handler-name", "DIGEST-MD5",
+                "--set", "quality-of-protection:" + "integrity");
+
+        //Configure JNDI props.
+        Hashtable<String, String> env = new Hashtable<String, String>();
+        env.put(Context.INITIAL_CONTEXT_FACTORY, factory);
+        int port = TestCaseUtils.getServerLdapPort();
+        String url = "ldap://localhost:" + Integer.valueOf(port);
+        env.put(Context.PROVIDER_URL, url);
+        env.put(Context.SECURITY_AUTHENTICATION, "DIGEST-MD5");
+        String principal = "dn:" + newUser;
+        env.put(Context.SECURITY_PRINCIPAL, principal);
+        env.put(Context.SECURITY_CREDENTIALS, "password");
+        //Select integrity QOP.
+        env.put("javax.security.sasl.qop", "auth-int");
+        //Add ACI with ssf > 1, should fail.
+        String addACILDIF = makeAddLDIF("aci", newUser, greaterIntegrityACI);
+        LDIFModify(addACILDIF, DIR_MGR_DN, PWD);
+        JNDIModify(env, newUser, "description", descriptionStr,
+                  LDAPResultCode.INSUFFICIENT_ACCESS_RIGHTS);
+        deleteAttrFromEntry(newUser, "aci");
+        //Add ACI with ssf = 1.
+         addACILDIF = makeAddLDIF("aci", newUser, integrityACI);
+        LDIFModify(addACILDIF, DIR_MGR_DN, PWD);
+        //Should succeed.
+        JNDIModify(env, newUser, "description", descriptionStr,
+                   LDAPResultCode.SUCCESS);
+        deleteAttrFromEntry(newUser, "aci");
+        deleteAttrFromEntry(newUser, "description");
+    }
+
+    /**
+     * Test confidentiality settings using DIGEST-MD5.
+     * @throws Exception
+     */
+    @Test()
+    public void testConfidentiality() throws Exception {
+        //set QOP to integrity.
+        TestCaseUtils.dsconfig(
+                "set-sasl-mechanism-handler-prop",
+                "--handler-name", "DIGEST-MD5",
+                "--set", "quality-of-protection:" + "confidentiality");
+        Hashtable<String, String> env = new Hashtable<String, String>();
+        env.put(Context.INITIAL_CONTEXT_FACTORY, factory);
+        int port = TestCaseUtils.getServerLdapPort();
+        String url = "ldap://localhost:" + Integer.valueOf(port);
+        env.put(Context.PROVIDER_URL, url);
+        env.put(Context.SECURITY_AUTHENTICATION, "DIGEST-MD5");
+        String principal = "dn:" + newUser;
+        env.put(Context.SECURITY_PRINCIPAL, principal);
+        env.put(Context.SECURITY_CREDENTIALS, "password");
+        //Select integrity QOP.
+        env.put("javax.security.sasl.qop", "auth-conf");
+        //Add ACI with ssf > 1, should succeed.
+        String addACILDIF = makeAddLDIF("aci", newUser, greaterIntegrityACI);
+        LDIFModify(addACILDIF, DIR_MGR_DN, PWD);
+        JNDIModify(env, newUser, "description", descriptionStr,
+                  LDAPResultCode.SUCCESS);
+        deleteAttrFromEntry(newUser, "aci");
+        deleteAttrFromEntry(newUser, "description");
+        //Test medium strength.
+        addACILDIF = makeAddLDIF("aci", newUser, medStrengthACI);
+        LDIFModify(addACILDIF, DIR_MGR_DN, PWD);
+        env.put("javax.security.sasl.strength", "medium");
+        JNDIModify(env, newUser, "description", descriptionStr,
+                LDAPResultCode.SUCCESS);
+        deleteAttrFromEntry(newUser, "aci");
+        deleteAttrFromEntry(newUser, "description");
+        //Test high strength.
+        addACILDIF = makeAddLDIF("aci", newUser, hiStrengthACI);
+        LDIFModify(addACILDIF, DIR_MGR_DN, PWD);
+        env.put("javax.security.sasl.strength", "high");
+        JNDIModify(env, newUser, "description", descriptionStr,
+                LDAPResultCode.SUCCESS);
+        deleteAttrFromEntry(newUser, "aci");
+        deleteAttrFromEntry(newUser, "description");
+        //Fail DIGEST-MD5 only goes to 128.
+        addACILDIF = makeAddLDIF("aci", newUser, hiPlusStrengthACI);
+        LDIFModify(addACILDIF, DIR_MGR_DN, PWD);
+        env.put("javax.security.sasl.strength", "high");
+        JNDIModify(env, newUser, "description", descriptionStr,
+                   LDAPResultCode.INSUFFICIENT_ACCESS_RIGHTS);
+        deleteAttrFromEntry(newUser, "aci");
+        deleteAttrFromEntry(newUser, "description");
+    }
+}

--
Gitblit v1.10.0