[Pkg-fedora-ds-maintainers] 389-ds-base: Changes to 'upstream-experimental'

Timo Aaltonen tjaalton at moszumanska.debian.org
Wed May 10 06:37:01 UTC 2017


Rebased ref, commits from common ancestor:
commit 3bff0e2644253961d3b8374b8ccf7aa06086c057
Author: Mark Reynolds <mreynolds at redhat.com>
Date:   Thu Apr 27 10:48:29 2017 -0400

    Bump version to 1.3.6.5

diff --git a/VERSION.sh b/VERSION.sh
index fc5be48..16e67e7 100644
--- a/VERSION.sh
+++ b/VERSION.sh
@@ -10,7 +10,7 @@ vendor="389 Project"
 # PACKAGE_VERSION is constructed from these
 VERSION_MAJOR=1
 VERSION_MINOR=3
-VERSION_MAINT=6.4
+VERSION_MAINT=6.5
 # NOTE: VERSION_PREREL is automatically set for builds made out of a git tree
 VERSION_PREREL=
 VERSION_DATE=$(date -u +%Y%m%d)

commit b41483399523fd53c4ee7b4e9ca0ef1e6fa8e5e6
Author: William Brown <firstyear at redhat.com>
Date:   Wed Apr 26 15:48:30 2017 +1000

    Ticket 49231 - fix sasl mech handling
    
    Bug Description:  In our sasl code we had two issues. One was that
    we did not correctly apply the list of sasl allowed mechs to our
    rootdse list in ids_sasl_listmech. The second was that on config
    reset, we did not correctly set null to the value.
    
    Fix Description:  Fix the handling of the mech lists to allow
    reset, and allow the mech list to be updated properly.
    
    https://pagure.io/389-ds-base/issue/49231
    
    Author: wibrown
    
    Review by: mreynolds (Thanks!)
    
    (cherry picked from commit 639535771d13de86446a361a515713ae15f65722)

diff --git a/dirsrvtests/tests/suites/sasl/allowed_mechs.py b/dirsrvtests/tests/suites/sasl/allowed_mechs.py
new file mode 100644
index 0000000..a3e385e
--- /dev/null
+++ b/dirsrvtests/tests/suites/sasl/allowed_mechs.py
@@ -0,0 +1,43 @@
+# --- BEGIN COPYRIGHT BLOCK ---
+# Copyright (C) 2017 Red Hat, Inc.
+# All rights reserved.
+#
+# License: GPL (version 3 or any later version).
+# See LICENSE for details.
+# --- END COPYRIGHT BLOCK ---
+#
+
+import pytest
+import ldap
+
+import time
+
+from lib389.topologies import topology_st
+
+def test_sasl_allowed_mechs(topology_st):
+    standalone = topology_st.standalone
+
+    # Get the supported mechs. This should contain PLAIN, GSSAPI, EXTERNAL at least
+    orig_mechs = standalone.rootdse.supported_sasl()
+    print(orig_mechs)
+    assert('GSSAPI' in orig_mechs)
+    assert('PLAIN' in orig_mechs)
+    assert('EXTERNAL' in orig_mechs)
+
+    # Now edit the supported mechs. CHeck them again.
+    standalone.config.set('nsslapd-allowed-sasl-mechanisms', 'EXTERNAL, PLAIN')
+
+    limit_mechs = standalone.rootdse.supported_sasl()
+    print(limit_mechs)
+    assert('PLAIN' in limit_mechs)
+    assert('EXTERNAL' in limit_mechs)
+    assert('GSSAPI' not in limit_mechs)
+
+    # Do a config reset
+    standalone.config.reset('nsslapd-allowed-sasl-mechanisms')
+
+    # check the supported list is the same as our first check.
+    final_mechs = standalone.rootdse.supported_sasl()
+    print(final_mechs)
+    assert(set(final_mechs) == set(orig_mechs))
+
diff --git a/ldap/servers/slapd/charray.c b/ldap/servers/slapd/charray.c
index 5551dcc..6b89714 100644
--- a/ldap/servers/slapd/charray.c
+++ b/ldap/servers/slapd/charray.c
@@ -348,8 +348,9 @@ slapi_str2charray_ext( char *str, char *brkstr, int allow_dups )
             }
         }
 
-        if ( !dup_found )
+        if ( !dup_found ) {
             res[i++] = slapi_ch_strdup( s );
+        }
     }
     res[i] = NULL;
 
@@ -413,10 +414,11 @@ charray_subtract(char **a, char **b, char ***c)
     char **bp, **cp, **tmp;
     char **p;
 
-    if (c)
+    if (c) {
         tmp = *c = cool_charray_dup(a);
-    else
+    } else {
         tmp = a;
+    }
 
     for (cp = tmp; cp && *cp; cp++) {
         for (bp = b; bp && *bp; bp++) {
@@ -433,12 +435,48 @@ charray_subtract(char **a, char **b, char ***c)
             for (p = cp+1; *p && *p == (char *)SUBTRACT_DEL; p++)
                 ;
             *cp = *p;    
-            if (*p == NULL)
+            if (*p == NULL) {
                 break;
-            else
+            } else {
                 *p = SUBTRACT_DEL;
+            }
+        }
+    }
+}
+
+/*
+ * Provides the intersection of two arrays.
+ * IE if you have:
+ * (A, B, C)
+ * (B, D, E)
+ * result is (B,)
+ * a and b are NOT consumed in the process.
+ */
+char **
+charray_intersection(char **a, char **b) {
+    char **result;
+    size_t rp = 0;
+
+    if (a == NULL || b == NULL) {
+        return NULL;
+    }
+
+    size_t a_len = 0;
+    /* Find how long A is. */
+    for (; a[a_len] != NULL; a_len++);
+
+    /* Allocate our result, it can't be bigger than A */
+    result = (char **)slapi_ch_calloc(1, sizeof(char *) * (a_len + 1));
+
+    /* For each in A, see if it's in b */
+    for (size_t i = 0; a[i] != NULL; i++) {
+        if (charray_get_index(b, a[i]) != -1) {
+            result[rp] = slapi_ch_strdup(a[i]);
+            rp++;
         }
     }
+
+    return result;
 }
 
 int
diff --git a/ldap/servers/slapd/libglobs.c b/ldap/servers/slapd/libglobs.c
index 9c4a61b..e488885 100644
--- a/ldap/servers/slapd/libglobs.c
+++ b/ldap/servers/slapd/libglobs.c
@@ -7091,9 +7091,30 @@ config_set_entryusn_import_init( const char *attrname, char *value,
     return retVal;
 }
 
+char **
+config_get_allowed_sasl_mechs_array(void)
+{
+    /*
+     * array of mechs. If is null, returns NULL thanks to ch_array_dup.
+     * Caller must free!
+     */
+    char **retVal;
+    slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+
+    CFG_LOCK_READ(slapdFrontendConfig);
+    retVal = slapi_ch_array_dup(slapdFrontendConfig->allowed_sasl_mechs_array);
+    CFG_UNLOCK_READ(slapdFrontendConfig);
+
+    return retVal;
+}
+
 char *
-config_get_allowed_sasl_mechs()
+config_get_allowed_sasl_mechs(void)
 {
+    /*
+     * Space seperated list of allowed mechs
+     * if this is NULL, means *all* mechs are allowed!
+     */
     char *retVal;
     slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
 
@@ -7114,22 +7135,35 @@ config_set_allowed_sasl_mechs(const char *attrname, char *value, char *errorbuf
         return LDAP_SUCCESS;
     }
 
-    /* cyrus sasl doesn't like comma separated lists */
-    remove_commas(value);
+    /* During a reset, the value is "", so we have to handle this case. */
+    if (strcmp(value, "") != 0) {
+        /* cyrus sasl doesn't like comma separated lists */
+        remove_commas(value);
+
+        if(invalid_sasl_mech(value)){
+            slapi_log_err(SLAPI_LOG_ERR,"config_set_allowed_sasl_mechs",
+                    "Invalid value/character for sasl mechanism (%s).  Use ASCII "
+                    "characters, upto 20 characters, that are upper-case letters, "
+                    "digits, hyphens, or underscores\n", value);
+            return LDAP_UNWILLING_TO_PERFORM;
+        }
 
-    if(invalid_sasl_mech(value)){
-        slapi_log_err(SLAPI_LOG_ERR,"config_set_allowed_sasl_mechs",
-                "Invalid value/character for sasl mechanism (%s).  Use ASCII "
-                "characters, upto 20 characters, that are upper-case letters, "
-                "digits, hyphens, or underscores\n", value);
-        return LDAP_UNWILLING_TO_PERFORM;
+        CFG_LOCK_WRITE(slapdFrontendConfig);
+        slapi_ch_free_string(&slapdFrontendConfig->allowed_sasl_mechs);
+        slapi_ch_array_free(slapdFrontendConfig->allowed_sasl_mechs_array);
+        slapdFrontendConfig->allowed_sasl_mechs = slapi_ch_strdup(value);
+        slapdFrontendConfig->allowed_sasl_mechs_array = slapi_str2charray_ext(value, " ", 0);
+        CFG_UNLOCK_WRITE(slapdFrontendConfig);
+    } else {
+        /* If this value is "", we need to set the list to *all* possible mechs */
+        CFG_LOCK_WRITE(slapdFrontendConfig);
+        slapi_ch_free_string(&slapdFrontendConfig->allowed_sasl_mechs);
+        slapi_ch_array_free(slapdFrontendConfig->allowed_sasl_mechs_array);
+        slapdFrontendConfig->allowed_sasl_mechs = NULL;
+        slapdFrontendConfig->allowed_sasl_mechs_array = NULL;
+        CFG_UNLOCK_WRITE(slapdFrontendConfig);
     }
 
-    CFG_LOCK_WRITE(slapdFrontendConfig);
-    slapi_ch_free_string(&slapdFrontendConfig->allowed_sasl_mechs);
-    slapdFrontendConfig->allowed_sasl_mechs = slapi_ch_strdup(value);
-    CFG_UNLOCK_WRITE(slapdFrontendConfig);
-
     return LDAP_SUCCESS;
 }
 
diff --git a/ldap/servers/slapd/proto-slap.h b/ldap/servers/slapd/proto-slap.h
index fdb4bf0..9696ead 100644
--- a/ldap/servers/slapd/proto-slap.h
+++ b/ldap/servers/slapd/proto-slap.h
@@ -553,6 +553,7 @@ size_t config_get_ndn_cache_size(void);
 int config_get_ndn_cache_enabled(void);
 int config_get_return_orig_type_switch(void);
 char *config_get_allowed_sasl_mechs(void);
+char **config_get_allowed_sasl_mechs_array(void);
 int config_set_allowed_sasl_mechs(const char *attrname, char *value, char *errorbuf, int apply);
 int config_get_schemamod(void);
 int config_set_ignore_vattrs(const char *attrname, char *value, char *errorbuf, int apply);
diff --git a/ldap/servers/slapd/saslbind.c b/ldap/servers/slapd/saslbind.c
index 328d919..3c85b1c 100644
--- a/ldap/servers/slapd/saslbind.c
+++ b/ldap/servers/slapd/saslbind.c
@@ -753,7 +753,10 @@ void ids_sasl_server_new(Connection *conn)
  */
 char **ids_sasl_listmech(Slapi_PBlock *pb)
 {
-    char **ret, **others;
+    char **ret;
+    char **config_ret;
+    char **sup_ret;
+    char **others;
     const char *str;
     char *dupstr;
     sasl_conn_t *sasl_conn;
@@ -766,32 +769,43 @@ char **ids_sasl_listmech(Slapi_PBlock *pb)
     slapi_pblock_get(pb, SLAPI_CONNECTION, &pb_conn);
 
     /* hard-wired mechanisms and slapi plugin registered mechanisms */
-    ret = slapi_get_supported_saslmechanisms_copy();
+    sup_ret = slapi_get_supported_saslmechanisms_copy();
 
-    if (pb_conn == NULL) {
-        return ret;
+    /* If we have a connection, get the provided list from SASL */
+    if (pb_conn != NULL) {
+        sasl_conn = (sasl_conn_t*)pb_conn->c_sasl_conn;
+        if (sasl_conn != NULL) {
+            /* sasl library mechanisms are connection dependent */
+            PR_EnterMonitor(pb_conn->c_mutex);
+            if (sasl_listmech(sasl_conn,
+                              NULL,     /* username */
+                              "", ",", "",
+                              &str, NULL, NULL) == SASL_OK) {
+                slapi_log_err(SLAPI_LOG_TRACE, "ids_sasl_listmech", "sasl library mechs: %s\n", str);
+                /* merge into result set */
+                dupstr = slapi_ch_strdup(str);
+                others = slapi_str2charray_ext(dupstr, ",", 0 /* don't list duplicate mechanisms */);
+                charray_merge(&sup_ret, others, 1);
+                charray_free(others);
+                slapi_ch_free((void**)&dupstr);
+            }
+            PR_ExitMonitor(pb_conn->c_mutex);
+        }
     }
 
-    sasl_conn = (sasl_conn_t*)pb_conn->c_sasl_conn;
-    if (sasl_conn == NULL) {
-        return ret;
-    }
+    /* Get the servers "allowed" list */
+    config_ret = config_get_allowed_sasl_mechs_array();
 
-    /* sasl library mechanisms are connection dependent */
-    PR_EnterMonitor(pb_conn->c_mutex);
-    if (sasl_listmech(sasl_conn, 
-                      NULL,     /* username */
-                      "", ",", "",
-                      &str, NULL, NULL) == SASL_OK) {
-        slapi_log_err(SLAPI_LOG_TRACE, "ids_sasl_listmech", "sasl library mechs: %s\n", str);
-        /* merge into result set */
-        dupstr = slapi_ch_strdup(str);
-        others = slapi_str2charray_ext(dupstr, ",", 0 /* don't list duplicate mechanisms */);
-        charray_merge(&ret, others, 1);
-        charray_free(others);
-        slapi_ch_free((void**)&dupstr);
+    /* Remove any content that isn't in the allowed list */
+    if (config_ret != NULL) {
+        /* Get the set of supported mechs in the insection of the two */
+        ret = charray_intersection(sup_ret, config_ret);
+        charray_free(sup_ret);
+        charray_free(config_ret);
+    } else {
+        /* The allowed list was empty, just take our supported list. */
+        ret = sup_ret;
     }
-    PR_ExitMonitor(pb_conn->c_mutex);
 
     slapi_log_err(SLAPI_LOG_TRACE, "ids_sasl_listmech", "<=\n");
 
diff --git a/ldap/servers/slapd/slap.h b/ldap/servers/slapd/slap.h
index 52c9c40..5469b51 100644
--- a/ldap/servers/slapd/slap.h
+++ b/ldap/servers/slapd/slap.h
@@ -2425,6 +2425,7 @@ typedef struct _slapdFrontendConfig {
   int pagedsizelimit;
   char *default_naming_context; /* Default naming context (normalized) */
   char *allowed_sasl_mechs;     /* comma/space separated list of allowed sasl mechs */
+  char **allowed_sasl_mechs_array;     /* Array of allow sasl mechs */
   int sasl_max_bufsize;         /* The max receive buffer size for SASL */
 
   /* disk monitoring */
diff --git a/ldap/servers/slapd/slapi-private.h b/ldap/servers/slapd/slapi-private.h
index f7041bd..5b76e89 100644
--- a/ldap/servers/slapd/slapi-private.h
+++ b/ldap/servers/slapd/slapi-private.h
@@ -831,6 +831,7 @@ int charray_remove(char **a, const char *s, int freeit);
 char ** cool_charray_dup( char **a );
 void cool_charray_free( char **array );
 void charray_subtract( char **a, char **b, char ***c );
+char **charray_intersection(char **a, char **b);
 int charray_get_index(char **array, char *s);
 int charray_normdn_add(char ***chararray, char *dn, char *errstr);
 

commit 823cb708e0507cf063d0750bd069c4df58a8af09
Author: William Brown <firstyear at redhat.com>
Date:   Wed Apr 26 12:55:14 2017 +1000

    Ticket 49233 - Fix crash in persistent search
    
    Bug Description:  During the fixes to the pblock, we accidentally
    introduced a free to an operation linked to a persistent search.
    
    Fix Description:  NULL the operation in the PB before connection
    threadmain loops.
    
    https://pagure.io/389-ds-base/issue/49233
    
    Author: wibrown
    
    Review by: tbordaz, mreynolds (Thanks!!)
    
    (cherry picked from commit 565314cde101ec1d1d71527ea1b35f12504e9c97)

diff --git a/dirsrvtests/tests/suites/psearch/psearch_test.py b/dirsrvtests/tests/suites/psearch/psearch_test.py
new file mode 100644
index 0000000..4f53bb9
--- /dev/null
+++ b/dirsrvtests/tests/suites/psearch/psearch_test.py
@@ -0,0 +1,53 @@
+# --- BEGIN COPYRIGHT BLOCK ---
+# Copyright (C) 2016 Red Hat, Inc.
+# All rights reserved.
+#
+# License: GPL (version 3 or any later version).
+# See LICENSE for details.
+# --- END COPYRIGHT BLOCK ---
+#
+import pytest
+from lib389._constants import DEFAULT_SUFFIX
+from lib389.topologies import topology_st
+from lib389.idm.group import Groups
+import ldap
+from ldap.controls.psearch import PersistentSearchControl,EntryChangeNotificationControl
+
+def _run_psearch(inst, msg_id):
+    results = []
+    while True:
+        try:
+            _, data, _, _, _, _ = inst.result4(msgid=msg_id, all=0, timeout=1.0, add_ctrls=1, add_intermediates=1,
+                                                                resp_ctrl_classes={EntryChangeNotificationControl.controlType:EntryChangeNotificationControl})
+            # See if there are any entry changes
+            for dn, entry, srv_ctrls in data:
+                ecn_ctrls = filter(lambda c: c.controlType == EntryChangeNotificationControl.controlType, srv_ctrls)
+                if ecn_ctrls:
+                    inst.log.info('%s has changed!' % dn)
+                    results.append(dn)
+        except ldap.TIMEOUT:
+            # There are no more results, so we timeout.
+            inst.log.info('No more results')
+            return results
+
+def test_psearch(topology_st):
+    # Create the search control
+    psc = PersistentSearchControl()
+    # do a search extended with the control
+    msg_id = topology_st.standalone.search_ext(base=DEFAULT_SUFFIX, scope=ldap.SCOPE_SUBTREE, attrlist=['*'], serverctrls=[psc])
+    # Get the result for the message id with result4
+    _run_psearch(topology_st.standalone, msg_id)
+    # Change an entry / add one
+    groups = Groups(topology_st.standalone, DEFAULT_SUFFIX)
+    group = groups.create(properties={'cn': 'group1', 'description': 'testgroup'})
+    # Now run the result again and see what's there.
+    results = _run_psearch(topology_st.standalone, msg_id)
+    # assert our group is in the changeset.
+    assert(group.dn == results[0])
+
+
+if __name__ == '__main__':
+    # Run isolated
+    # -s for DEBUG mode
+    CURRENT_FILE = os.path.realpath(__file__)
+    pytest.main("-s %s" % CURRENT_FILE)
diff --git a/ldap/servers/slapd/connection.c b/ldap/servers/slapd/connection.c
index 7aa2716..f6242c3 100644
--- a/ldap/servers/slapd/connection.c
+++ b/ldap/servers/slapd/connection.c
@@ -1524,6 +1524,7 @@ connection_threadmain()
 		if( op_shutdown ) {
 			slapi_log_err(SLAPI_LOG_TRACE, "connection_threadmain",
 				"op_thread received shutdown signal\n");
+			slapi_pblock_destroy(pb);
 			g_decr_active_threadcnt();
 			return;
 		}
@@ -1548,6 +1549,7 @@ connection_threadmain()
 				case CONN_SHUTDOWN:
 					slapi_log_err(SLAPI_LOG_TRACE, "connection_threadmain",
 						"op_thread received shutdown signal\n");
+					slapi_pblock_destroy(pb);
 					g_decr_active_threadcnt();
 					return;
 				case CONN_FOUND_WORK_TO_DO:
@@ -1778,7 +1780,7 @@ done:
 			connection_release_nolock(conn);
 			PR_ExitMonitor(conn->c_mutex);
 			signal_listner();
-            slapi_pblock_destroy(pb);
+			slapi_pblock_destroy(pb);
 			return;
 		}
 		/*
@@ -1798,9 +1800,10 @@ done:
 			    connection_release_nolock (conn); /* psearch acquires ref to conn - release this one now */
 			    PR_ExitMonitor(conn->c_mutex);
 			    /* ps_add makes a shallow copy of the pb - so we
-			     * can't free it or init it here - just memset it to 0
+			     * can't free it or init it here - just set operation to NULL.
 			     * ps_send_results will call connection_remove_operation_ext to free it
 			     */
+                slapi_pblock_set(pb, SLAPI_OPERATION, NULL);
 	            slapi_pblock_init(pb);
 		} else {
 			/* delete from connection operation queue & decr refcnt */

commit 64ff8370622e097dd75f268f6730bc82916f619e
Author: Thierry Bordaz <tbordaz at redhat.com>
Date:   Fri Apr 21 17:16:55 2017 +0200

    Ticket 49230 - slapi_register_plugin creates config entry where it should not
    
    Bug Description:
        slapi-register-plugin systematically create an entry under
        'cn=plugins,cn=config' because it is not taking into account
        the flag 'add_entry in 'plugin_setup'.
    
        This is potentially a regression introduced by
        https://pagure.io/389-ds-base/issue/49066 (TBC)
    
    Fix Description:
        Test 'add_entry' before adding the entry
    
        https://pagure.io/389-ds-base/issue/49230
    
    Review by: Mark Reynolds, William Brown

diff --git a/ldap/servers/slapd/plugin.c b/ldap/servers/slapd/plugin.c
index 77043ff..6131c4b 100644
--- a/ldap/servers/slapd/plugin.c
+++ b/ldap/servers/slapd/plugin.c
@@ -2848,7 +2848,7 @@ add_entry - if true, the entry will be added to the DIT using the given
 ************************************/
 int
 plugin_setup(Slapi_Entry *plugin_entry, struct slapi_componentid *group,
-		slapi_plugin_init_fnptr p_initfunc, int add_entry __attribute__((unused)), char *returntext)
+		slapi_plugin_init_fnptr p_initfunc, int add_entry, char *returntext)
 {
 	int ii = 0;
 	char attrname[SLAPD_TYPICAL_ATTRIBUTE_NAME_MAX_LENGTH];
@@ -3142,11 +3142,13 @@ plugin_setup(Slapi_Entry *plugin_entry, struct slapi_componentid *group,
 		add_plugin_entry_dn(dn_copy);
 	}
 
-    /* make a copy of the plugin entry for our own use because it will
-       be freed later by the caller */
-    Slapi_Entry *e_copy = slapi_entry_dup(plugin_entry);
-    /* new_plugin_entry(&plugin_entries, plugin_entry, plugin); */
-    new_plugin_entry(&dep_plugin_entries, e_copy, plugin);
+	if (add_entry) {
+		/* make a copy of the plugin entry for our own use because it will
+		   be freed later by the caller */
+		Slapi_Entry *e_copy = slapi_entry_dup(plugin_entry);
+		/* new_plugin_entry(&plugin_entries, plugin_entry, plugin); */
+		new_plugin_entry(&dep_plugin_entries, e_copy, plugin);
+	}
 
 PLUGIN_CLEANUP:
 	if (status) {

commit e086b834f43b417007e3f0b0a822db2a2350c2a9
Author: William Brown <firstyear at redhat.com>
Date:   Tue Apr 25 14:41:30 2017 +1000

    Ticket 49135 - PBKDF2 should determine rounds at startup
    
    Bug Description:  We used a hardcoded number of rounds for PBKDF2
    
    Fix Description:  Rather than hardcoding rounds at startup, we define
    an attacker "work" factor. We have chosen 40 milliseconds for now.
    
    Based on this factor, we then run a test to determine the CPU performance
    of the system. If the CPU performance is belowe a threshold, we use
    10,000 rounds. If it is above, we scale the rounds up to our work
    factor. This way, each attempt by an attacker on a password should
    take 40 milliseconds - enough to cause them headaches, but still
    have a fast ldap server (given a bind takes about 500 milliseconds
    on my laptop today).
    
    https://pagure.io/389-ds-base/issue/49135
    
    Author: wibrown
    
    Review by: mreynolds (Thanks!!)

diff --git a/Makefile.am b/Makefile.am
index bedccbf..e2421ab 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -2008,13 +2008,19 @@ test_slapd_SOURCES = test/main.c \
 	test/libslapd/pblock/analytics.c \
 	test/libslapd/pblock/v3_compat.c \
 	test/libslapd/operation/v3_compat.c \
-	test/libslapd/spal/meminfo.c
+	test/libslapd/spal/meminfo.c \
+	test/plugins/test.c \
+	test/plugins/pwdstorage/pbkdf2.c
 
-test_slapd_LDADD = libslapd.la
+# We need to link a lot of plugins for this test.
+test_slapd_LDADD = 	libslapd.la \
+					libpwdstorage-plugin.la
 test_slapd_LDFLAGS = $(AM_CPPFLAGS) $(CMOCKA_LINKS)
 ### WARNING: Slap.h needs cert.h, which requires the -I/lib/ldaputil!!!
 ### WARNING: Slap.h pulls ssl.h, which requires nss!!!!
-test_slapd_CPPFLAGS = $(AM_CPPFLAGS) $(DSPLUGIN_CPPFLAGS) $(DSINTERNAL_CPPFLAGS)
+# We need to pull in plugin header paths too:
+test_slapd_CPPFLAGS = 	$(AM_CPPFLAGS) $(DSPLUGIN_CPPFLAGS) $(DSINTERNAL_CPPFLAGS) \
+						-I$(srcdir)/ldap/servers/plugins/pwdstorage
 
 test_libsds_SOURCES =  src/libsds/test/test_sds.c \
 	src/libsds/test/test_sds_bpt.c \
diff --git a/dirsrvtests/tests/suites/password/pwd_algo_test.py b/dirsrvtests/tests/suites/password/pwd_algo_test.py
index 4b3fb33..b3f03fe 100644
--- a/dirsrvtests/tests/suites/password/pwd_algo_test.py
+++ b/dirsrvtests/tests/suites/password/pwd_algo_test.py
@@ -69,9 +69,20 @@ def test_pwd_algo_test(topology_st):
     password conditions.
     """
 
-    for algo in (
-            'CLEAR', 'CRYPT', 'MD5', 'SHA', 'SHA256', 'SHA384', 'SHA512', 'SMD5', 'SSHA', 'SSHA256', 'SSHA384',
-            'SSHA512'):
+    for algo in ('CLEAR',
+                 'CRYPT',
+                 'MD5',
+                 'SHA',
+                 'SHA256',
+                 'SHA384',
+                 'SHA512',
+                 'SMD5',
+                 'SSHA',
+                 'SSHA256',
+                 'SSHA384',
+                 'SSHA512',
+                 'PBKDF2_SHA256',
+                 ):
         _test_algo(topology_st.standalone, algo)
 
     log.info('Test PASSED')
diff --git a/ldap/servers/plugins/pwdstorage/pbkdf2_pwd.c b/ldap/servers/plugins/pwdstorage/pbkdf2_pwd.c
index b228700..f08b6ab 100644
--- a/ldap/servers/plugins/pwdstorage/pbkdf2_pwd.c
+++ b/ldap/servers/plugins/pwdstorage/pbkdf2_pwd.c
@@ -44,7 +44,10 @@
  * At the same time we MUST increase this with each version of Directory Server
  * This value is written into the hash, so it's safe to change.
  */
-#define PBKDF2_ITERATIONS 30000
+
+#define PBKDF2_MILLISECONDS 40
+
+static PRUint32 PBKDF2_ITERATIONS = 30000;
 
 static const char *schemeName = PBKDF2_SHA256_SCHEME_NAME;
 static const PRUint32 schemeNameLength = PBKDF2_SHA256_NAME_LEN;
@@ -52,6 +55,10 @@ static const PRUint32 schemeNameLength = PBKDF2_SHA256_NAME_LEN;
 /* For requesting the slot which supports these types */
 static CK_MECHANISM_TYPE mechanism_array[] = {CKM_SHA256_HMAC, CKM_PKCS5_PBKD2};
 
+/* Used in our startup benching code */
+#define PBKDF2_BENCH_ROUNDS 50000
+#define PBKDF2_BENCH_LOOP 10
+
 void
 pbkdf2_sha256_extract(char *hash_in, SECItem *salt, PRUint32 *iterations)
 {
@@ -124,12 +131,11 @@ pbkdf2_sha256_hash(char *hash_out, size_t hash_out_len, SECItem *pwd, SECItem *s
 }
 
 char *
-pbkdf2_sha256_pw_enc(const char *pwd)
+pbkdf2_sha256_pw_enc_rounds(const char *pwd, PRUint32 iterations)
 {
     char hash[ PBKDF2_TOTAL_LENGTH ];
     size_t encsize = 3 + schemeNameLength + LDIF_BASE64_LEN(PBKDF2_TOTAL_LENGTH);
     char *enc = slapi_ch_calloc(encsize, sizeof(char));
-    PRUint32 iterations = PBKDF2_ITERATIONS;
 
     SECItem saltItem;
     SECItem passItem;
@@ -174,24 +180,24 @@ pbkdf2_sha256_pw_enc(const char *pwd)
     return enc;
 }
 
+char *
+pbkdf2_sha256_pw_enc(const char *pwd) {
+    return pbkdf2_sha256_pw_enc_rounds(pwd, PBKDF2_ITERATIONS);
+}
+
 PRInt32
 pbkdf2_sha256_pw_cmp(const char *userpwd, const char *dbpwd)
 {
     PRInt32 result = 1; /* Default to fail. */
-    char dbhash[ PBKDF2_TOTAL_LENGTH ];
-    char userhash[ PBKDF2_HASH_LENGTH ];
+    char dbhash[ PBKDF2_TOTAL_LENGTH ] = {0};
+    char userhash[ PBKDF2_HASH_LENGTH ] = {0};
     PRUint32 dbpwd_len = strlen(dbpwd);
     SECItem saltItem;
     SECItem passItem;
     PRUint32 iterations = 0;
 
-    /* Our hash value is always at a known offset. */
-    char *hash = dbhash + PBKDF2_ITERATIONS_LENGTH + PBKDF2_SALT_LENGTH;
-
     slapi_log_err(SLAPI_LOG_PLUGIN, (char *)schemeName, "Comparing password\n");
 
-    memset(dbhash, 0, PBKDF2_TOTAL_LENGTH);
-
     passItem.data = (unsigned char *)userpwd;
     passItem.len = strlen(userpwd);
 
@@ -208,10 +214,101 @@ pbkdf2_sha256_pw_cmp(const char *userpwd, const char *dbpwd)
         slapi_log_err(SLAPI_LOG_ERR, (char *)schemeName, "Unable to hash userpwd value\n");
         return result;
     }
+
+    /* Our hash value is always at a known offset in the decoded string. */
+    char *hash = dbhash + PBKDF2_ITERATIONS_LENGTH + PBKDF2_SALT_LENGTH;
+
     /* Now compare the result of pbkdf2_sha256_hash. */
     result = memcmp(userhash, hash, PBKDF2_HASH_LENGTH);
 
     return result;
 }
 
+uint64_t
+pbkdf2_sha256_benchmark_iterations() {
+    /* Time how long it takes to do PBKDF2_BENCH_LOOP attempts of PBKDF2_BENCH_ROUNDS rounds */
+    uint64_t time_nsec = 0;
+    char *results[PBKDF2_BENCH_LOOP] = {0};
+    struct timespec start_time;
+    struct timespec finish_time;
+
+    clock_gettime(CLOCK_MONOTONIC, &start_time);
+
+    for (size_t i = 0; i < PBKDF2_BENCH_LOOP; i++) {
+        results[i] = pbkdf2_sha256_pw_enc_rounds("Eequee9mutheuchiehe4", PBKDF2_BENCH_ROUNDS);
+    }
+
+    clock_gettime(CLOCK_MONOTONIC, &finish_time);
+
+    for (size_t i = 0; i < PBKDF2_BENCH_LOOP; i++) {
+        slapi_ch_free((void **)&(results[i]));
+    }
+
+    /* Work out the execution time. */
+    time_nsec = (finish_time.tv_sec - start_time.tv_sec) * 1000000000;
+    if (finish_time.tv_nsec > start_time.tv_nsec) {
+        time_nsec += finish_time.tv_nsec - start_time.tv_nsec;
+    } else {
+        time_nsec += 1000000000 - (start_time.tv_nsec - finish_time.tv_nsec);
+    }
+
+    time_nsec = time_nsec / PBKDF2_BENCH_LOOP;
+
+    return time_nsec;
+}
+
+PRUint32
+pbkdf2_sha256_calculate_iterations(uint64_t time_nsec) {
+    /*
+     * So we know that we have nsec for a single round of PBKDF2_BENCH_ROUNDS now.
+     * first, we get the cost of "every 1000 rounds"
+     */
+    uint64_t number_thou_rounds = PBKDF2_BENCH_ROUNDS / 1000;
+    uint64_t thou_time_nsec = time_nsec / number_thou_rounds;
+
+    /*
+     * Now we have the cost of 1000 rounds. Now, knowing this we say
+     * we want an attacker to have to expend say ... example 8 ms of work
+     * to try a password. So this is 1,000,000 ns = 1ms, ergo
+     * 8,000,000
+     */
+    uint64_t attack_work_nsec = PBKDF2_MILLISECONDS * 1000000;
+
+    /*
+     * Knowing the attacker time and our cost, we can divide this
+     * to get how many thousands of rounds we should use.
+     */
+    uint64_t thou_rounds = (attack_work_nsec / thou_time_nsec);
+
+    /*
+     * Finally, we make the rounds in terms of thousands, and cast it.
+     */
+    PRUint32 final_rounds = thou_rounds * 1000;
+
+    if (final_rounds < 10000) {
+        final_rounds = 10000;
+    }
+
+    return final_rounds;
+}
+
+
+int
+pbkdf2_sha256_start(Slapi_PBlock *pb __attribute__((unused))) {
+    /* Run the time generator */
+    uint64_t time_nsec = pbkdf2_sha256_benchmark_iterations();
+    /* Calculate the iterations */
+    /* set it globally */
+    PBKDF2_ITERATIONS = pbkdf2_sha256_calculate_iterations(time_nsec);
+    /* Make a note of it. */
+    slapi_log_err(SLAPI_LOG_PLUGIN, (char *)schemeName, "Based on CPU performance, chose %"PRIu32" rounds\n", PBKDF2_ITERATIONS);
+    return 0;
+}
+
+/* Do we need the matching close function? */
+int
+pbkdf2_sha256_close(Slapi_PBlock *pb __attribute__((unused))) {
+    return 0;
+}
+
 
diff --git a/ldap/servers/plugins/pwdstorage/pwd_init.c b/ldap/servers/plugins/pwdstorage/pwd_init.c
index 0781c09..16d2f32 100644
--- a/ldap/servers/plugins/pwdstorage/pwd_init.c
+++ b/ldap/servers/plugins/pwdstorage/pwd_init.c
@@ -349,6 +349,8 @@ pbkdf2_sha256_pwd_storage_scheme_init(Slapi_PBlock *pb)
 
     rc = slapi_pblock_set(pb, SLAPI_PLUGIN_VERSION, (void *) SLAPI_PLUGIN_VERSION_01);
     rc |= slapi_pblock_set(pb, SLAPI_PLUGIN_DESCRIPTION, (void *)&pbkdf2_sha256_pdesc);
+    rc |= slapi_pblock_set(pb, SLAPI_PLUGIN_START_FN, (void*)&pbkdf2_sha256_start);
+    rc |= slapi_pblock_set(pb, SLAPI_PLUGIN_CLOSE_FN, (void*)&pbkdf2_sha256_close);
     rc |= slapi_pblock_set(pb, SLAPI_PLUGIN_PWD_STORAGE_SCHEME_ENC_FN, (void *)pbkdf2_sha256_pw_enc);
     rc |= slapi_pblock_set(pb, SLAPI_PLUGIN_PWD_STORAGE_SCHEME_CMP_FN, (void *)pbkdf2_sha256_pw_cmp);
     rc |= slapi_pblock_set(pb, SLAPI_PLUGIN_PWD_STORAGE_SCHEME_NAME, PBKDF2_SHA256_SCHEME_NAME);
diff --git a/ldap/servers/plugins/pwdstorage/pwdstorage.h b/ldap/servers/plugins/pwdstorage/pwdstorage.h
index d48b634..2909998 100644
--- a/ldap/servers/plugins/pwdstorage/pwdstorage.h
+++ b/ldap/servers/plugins/pwdstorage/pwdstorage.h
@@ -7,22 +7,20 @@
  * See LICENSE for details. 
  * END COPYRIGHT BLOCK **/
 
+#pragma once
+
 #ifdef HAVE_CONFIG_H
 #  include <config.h>
 #endif
 
-#ifndef _PWDSTORAGE_H
-#define _PWDSTORAGE_H
-
-#include "slapi-plugin.h"
-#include "slapi-private.h"
+#include <slapi-plugin.h>
+#include <slapi-private.h>
 #include <ssl.h>
-#include "nspr.h"
-#include "plbase64.h"
-#include "ldif.h"
+#include <nspr.h>
+#include <plbase64.h>
+#include <ldif.h>
 #include "md5.h"
 
-
 #define PWD_HASH_PREFIX_START   '{'
 #define PWD_HASH_PREFIX_END '}'
 
@@ -86,11 +84,16 @@ char *md5_pw_enc( const char *pwd );
 int smd5_pw_cmp( const char *userpwd, const char *dbpwd );
 char *smd5_pw_enc( const char *pwd );
 
+int pbkdf2_sha256_start(Slapi_PBlock *pb);
+int pbkdf2_sha256_close(Slapi_PBlock *pb);
 SECStatus pbkdf2_sha256_hash(char *hash_out, size_t hash_out_len, SECItem *pwd, SECItem *salt, PRUint32 iterations);
 char * pbkdf2_sha256_pw_enc(const char *pwd);
 int pbkdf2_sha256_pw_cmp(const char *userpwd, const char *dbpwd);
 
+/* For testing pbkdf2 only */
+uint64_t pbkdf2_sha256_benchmark_iterations();
+PRUint32 pbkdf2_sha256_calculate_iterations();
+
 /* Utility functions */
 PRUint32 pwdstorage_base64_decode_len(const char *encval, PRUint32 enclen);
 
-#endif /* _PWDSTORAGE_H */
diff --git a/test/main.c b/test/main.c
index d84f8df..36447ea 100644
--- a/test/main.c
+++ b/test/main.c
@@ -12,5 +12,8 @@ int
 main ( int argc __attribute__((unused)), char **argv __attribute__((unused))) {
     int result = 0;
     result += run_libslapd_tests();
+    result += run_plugin_tests();
+
+    PR_Cleanup();
     return result;
 }
diff --git a/test/plugins/pwdstorage/pbkdf2.c b/test/plugins/pwdstorage/pbkdf2.c
new file mode 100644
index 0000000..abadbfb
--- /dev/null
+++ b/test/plugins/pwdstorage/pbkdf2.c
@@ -0,0 +1,73 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright (C) 2017 Red Hat, Inc.
+ * All rights reserved.
+ *
+ * License: GPL (version 3 or any later version).
+ * See LICENSE for details.
+ * END COPYRIGHT BLOCK **/
+
+#include "../../test_slapd.h"
+
+#include <nss3/nss.h>
+#include <pwdstorage.h>
+
+int
+test_plugin_pwdstorage_nss_setup(void **state __attribute__((unused))) {
+    int result = NSS_Initialize(NULL, "", "", SECMOD_DB, NSS_INIT_READONLY|NSS_INIT_NOCERTDB|NSS_INIT_NOMODDB);
+    assert_true(result == 0);
+    return result;
+}
+
+int
+test_plugin_pwdstorage_nss_stop(void **state __attribute__((unused))) {
+    NSS_Shutdown();
+    return 0;
+}
+
+void
+test_plugin_pwdstorage_pbkdf2_auth(void **state __attribute__((unused))) {
+
+    /* Check that given various known passwords and hashes they validate (or don't) */
+
+    /* 'password' */
+    const char *password_a = "AAB1MHPzX9ZP+HDQYp/+qxQwJAW5cXhRvXX1+w0NBMVX6FyMv2uzIvtBfvn6A3o84gKW9fBl5hGPeH87bQMZs977SvCV09P8MV/fkkjH7EoYNXoSQ6FFBpjm3orFplT9Y5PY14xRvJS4iicQ82uKaaARlkbn0uLaHBNS18uz1YFzuYUlf4lqh+uy1VzAR3YQW9FWKL9TYCsTRx75EGUMYj/f7826CqrHNubnljh4s5gi31y+2qsdzdRerT1ISZC5z0kQbkXZYM7UCa4hlbSQl3mO6lpyxk44oiPkbKKii+bS+KRdIMeMgFawXo2L4+IYx+qXvJRwyi1M8vIxK+dnc2kOrLF9E7rZvs0hn9PuXMW3Itq46wPL3R51wo+0ki4gA36ZNF3PegbjFiAvrh24/D3SQMBjfk1YMDstNGJaMefd3bS1";
+
+    /*
+     * 'password' - but we mucked with the rounds
+     * note the 5th char  of the b64 is "L" not "M'. This changes the rounds from
+     * 30000 to 29996, which means we should fail
+     */
+    const char *password_a_rounds = "AAB1LHPzX9ZP+HDQYp/+qxQwJAW5cXhRvXX1+w0NBMVX6FyMv2uzIvtBfvn6A3o84gKW9fBl5hGPeH87bQMZs977SvCV09P8MV/fkkjH7EoYNXoSQ6FFBpjm3orFplT9Y5PY14xRvJS4iicQ82uKaaARlkbn0uLaHBNS18uz1YFzuYUlf4lqh+uy1VzAR3YQW9FWKL9TYCsTRx75EGUMYj/f7826CqrHNubnljh4s5gi31y+2qsdzdRerT1ISZC5z0kQbkXZYM7UCa4hlbSQl3mO6lpyxk44oiPkbKKii+bS+KRdIMeMgFawXo2L4+IYx+qXvJRwyi1M8vIxK+dnc2kOrLF9E7rZvs0hn9PuXMW3Itq46wPL3R51wo+0ki4gA36ZNF3PegbjFiAvrh24/D3SQMBjfk1YMDstNGJaMefd3bS1";
+
+    /*
+     * 'password' - but we mucked with the salt  Note the change in the 8th char from
+     * z to 0.
+     */
+    const char *password_a_salt = "AAB1MHP0X9ZP+HDQYp/+qxQwJAW5cXhRvXX1+w0NBMVX6FyMv2uzIvtBfvn6A3o84gKW9fBl5hGPeH87bQMZs977SvCV09P8MV/fkkjH7EoYNXoSQ6FFBpjm3orFplT9Y5PY14xRvJS4iicQ82uKaaARlkbn0uLaHBNS18uz1YFzuYUlf4lqh+uy1VzAR3YQW9FWKL9TYCsTRx75EGUMYj/f7826CqrHNubnljh4s5gi31y+2qsdzdRerT1ISZC5z0kQbkXZYM7UCa4hlbSQl3mO6lpyxk44oiPkbKKii+bS+KRdIMeMgFawXo2L4+IYx+qXvJRwyi1M8vIxK+dnc2kOrLF9E7rZvs0hn9PuXMW3Itq46wPL3R51wo+0ki4gA36ZNF3PegbjFiAvrh24/D3SQMBjfk1YMDstNGJaMefd3bS1";
+
+    assert_true(pbkdf2_sha256_pw_cmp("password", password_a) == 0);
+    assert_false(pbkdf2_sha256_pw_cmp("password", password_a_rounds) == 0);
+    assert_false(pbkdf2_sha256_pw_cmp("password", password_a_salt) == 0);
+    assert_false(pbkdf2_sha256_pw_cmp("password_b", password_a) == 0);
+}
+
+void
+test_plugin_pwdstorage_pbkdf2_rounds(void **state __attribute__((unused))){
+    /* Check the benchmark, and make sure we get a valid timestamp */
+    assert_true(pbkdf2_sha256_benchmark_iterations() > 0);
+    /*
+     * provide various values to the calculator, to check we get the right
+     * number of rounds back.
+     */
+    /*
+     * On a very slow system, we get the default min rounds out.
+     */
+    assert_true(pbkdf2_sha256_calculate_iterations(1000000000) == 10000);
+    /*
+     * On a "fast" system, we should see more rounds.
+     */
+    assert_true(pbkdf2_sha256_calculate_iterations(200000000) == 10000);
+    assert_true(pbkdf2_sha256_calculate_iterations(100000000) == 20000);
+    assert_true(pbkdf2_sha256_calculate_iterations(50000000) == 40000);
+}
+
diff --git a/test/plugins/test.c b/test/plugins/test.c
new file mode 100644
index 0000000..d853913
--- /dev/null
+++ b/test/plugins/test.c
@@ -0,0 +1,31 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright (C) 2017 Red Hat, Inc.
+ * All rights reserved.
+ *
+ * License: GPL (version 3 or any later version).
+ * See LICENSE for details.
+ * END COPYRIGHT BLOCK **/
+
+#include "../test_slapd.h"
+
+void
+test_plugin_hello(void **state __attribute__((unused))) {
+    /* It works! */
+    assert_int_equal(1, 1);
+}
+
+int
+run_plugin_tests (void) {
+    const struct CMUnitTest tests[] = {
+        cmocka_unit_test(test_plugin_hello),
+        cmocka_unit_test_setup_teardown(test_plugin_pwdstorage_pbkdf2_auth,
+                                        test_plugin_pwdstorage_nss_setup,
+                                        test_plugin_pwdstorage_nss_stop),
+        cmocka_unit_test_setup_teardown(test_plugin_pwdstorage_pbkdf2_rounds,
+                                        test_plugin_pwdstorage_nss_setup,
+                                        test_plugin_pwdstorage_nss_stop),
+    };
+    return cmocka_run_group_tests(tests, NULL, NULL);
+}
+
+
diff --git a/test/test_slapd.h b/test/test_slapd.h



More information about the Pkg-fedora-ds-maintainers mailing list