Blob Blame History Raw
From 72562d03b0f758902e0ee858fd43d5bcfbef379b Mon Sep 17 00:00:00 2001
From: Mark Reynolds <mreynolds@redhat.com>
Date: Tue, 7 Jun 2016 10:02:42 -0400
Subject: [PATCH] Ticket 48862 - At startup DES to AES password conversion
 causes timeout in start script

Bug Description:  At server start all the backends are searches for entries that contain
                  DES password attributes as defined in the plugin.  These are typically
                  unindexed searches, and if there is a very large backend this can cause
                  the server startup to timeout.

Fix Description:  At startup only check "cn=config" for entries with DES password attributes.
                  A new "conversion" task has been created that can be run after startup
                  to search all backends(if a suffix is not specified), or specific backends.

                      dn: cn=convertPasswords, cn=des2aes,cn=tasks,cn=config
                      objectclass: top
                      objectclass: extensibleObject
                      suffix: dc=example,dc=com
                      suffix: dc=other,dc=suffix

                  Another bug was discovered in pw_rever_encode() in pw.c where a "for" loop
                  counter was accidentially reused by a second "for" loop.  This could lead
                  to an infinite loop/hang.

                  Updated the CI test to perform the conversion task.

https://fedorahosted.org/389/ticket/48862

Reviewed by: nhosoi(Thanks!)

(cherry picked from commit 11f55f3dd2a2c44ddf7b5be54273401add13b1bc)
(cherry picked from commit c0ad918939c40779e463b71c41ab106e7ee890e2)
---
 dirsrvtests/tickets/ticket47462_test.py | 133 ++++++++----
 ldap/servers/slapd/daemon.c             | 195 +++++-------------
 ldap/servers/slapd/pw.c                 |   4 +-
 ldap/servers/slapd/task.c               | 346 +++++++++++++++++++++++++++++++-
 4 files changed, 493 insertions(+), 185 deletions(-)

diff --git a/dirsrvtests/tickets/ticket47462_test.py b/dirsrvtests/tickets/ticket47462_test.py
index 17854fa..2d2c507 100644
--- a/dirsrvtests/tickets/ticket47462_test.py
+++ b/dirsrvtests/tickets/ticket47462_test.py
@@ -32,6 +32,7 @@ AGMT_DN = ''
 USER_DN = 'cn=test_user,' + DEFAULT_SUFFIX
 USER1_DN = 'cn=test_user1,' + DEFAULT_SUFFIX
 TEST_REPL_DN = 'cn=test repl,' + DEFAULT_SUFFIX
+DES2AES_TASK_DN = 'cn=convert,cn=des2aes,cn=tasks,cn=config'
 
 
 class TopologyMaster1Master2(object):
@@ -134,6 +135,11 @@ def topology(request):
     # clear the tmp directory
     master1.clearTmpDir(__file__)
 
+    def fin():
+        master1.delete()
+        master2.delete()
+    request.addfinalizer(fin)
+
     return TopologyMaster1Master2(master1, master2)
 
 
@@ -144,11 +150,9 @@ def test_ticket47462(topology):
     """
 
     #
-    # First set config as if it's an older version.  Set DES to use libdes-plugin,
-    # MMR to depend on DES, delete the existing AES plugin, and set a DES password
-    # for the replication agreement.
-    #
-
+    # First set config as if it's an older version.  Set DES to use
+    # libdes-plugin, MMR to depend on DES, delete the existing AES plugin,
+    # and set a DES password for the replication agreement.
     #
     # Add an extra attribute to the DES plugin args
     #
@@ -168,7 +172,9 @@ def test_ticket47462(topology):
 
     try:
         topology.master1.modify_s(MMR_PLUGIN,
-                      [(ldap.MOD_DELETE, 'nsslapd-plugin-depends-on-named', 'AES')])
+                      [(ldap.MOD_DELETE,
+                        'nsslapd-plugin-depends-on-named',
+                        'AES')])
 
     except ldap.NO_SUCH_ATTRIBUTE:
         pass
@@ -194,7 +200,8 @@ def test_ticket47462(topology):
     # Get the agmt dn, and set the password
     #
     try:
-        entry = topology.master1.search_s('cn=config', ldap.SCOPE_SUBTREE, 'objectclass=nsDS5ReplicationAgreement')
+        entry = topology.master1.search_s('cn=config', ldap.SCOPE_SUBTREE,
+                                          'objectclass=nsDS5ReplicationAgreement')
         if entry:
             agmt_dn = entry[0].dn
             log.info('Found agmt dn (%s)' % agmt_dn)
@@ -207,7 +214,8 @@ def test_ticket47462(topology):
 
     try:
         properties = {RA_BINDPW: "password"}
-        topology.master1.agreement.setProperties(None, agmt_dn, None, properties)
+        topology.master1.agreement.setProperties(None, agmt_dn, None,
+                                                 properties)
         log.info('Successfully modified replication agreement')
     except ValueError:
         log.error('Failed to update replica agreement: ' + AGMT_DN)
@@ -220,12 +228,14 @@ def test_ticket47462(topology):
         topology.master1.add_s(Entry((USER1_DN,
                                       {'objectclass': "top person".split(),
                                        'sn': 'sn',
+                                       'description': 'DES value to convert',
                                        'cn': 'test_user'})))
         loop = 0
         ent = None
         while loop <= 10:
             try:
-                ent = topology.master2.getEntry(USER1_DN, ldap.SCOPE_BASE, "(objectclass=*)")
+                ent = topology.master2.getEntry(USER1_DN, ldap.SCOPE_BASE,
+                                                "(objectclass=*)")
                 break
             except ldap.NO_SUCH_OBJECT:
                 time.sleep(1)
@@ -250,7 +260,8 @@ def test_ticket47462(topology):
     # Check that the restart converted existing DES credentials
     #
     try:
-        entry = topology.master1.search_s('cn=config', ldap.SCOPE_SUBTREE, 'nsDS5ReplicaCredentials=*')
+        entry = topology.master1.search_s('cn=config', ldap.SCOPE_SUBTREE,
+                                          'nsDS5ReplicaCredentials=*')
         if entry:
             val = entry[0].getValue('nsDS5ReplicaCredentials')
             if val.startswith('{AES-'):
@@ -259,22 +270,25 @@ def test_ticket47462(topology):
                 log.fatal('Failed to convert credentials from DES to AES!')
                 assert False
         else:
-            log.fatal('Failed to find any entries with nsDS5ReplicaCredentials ')
+            log.fatal('Failed to find entries with nsDS5ReplicaCredentials')
             assert False
     except ldap.LDAPError, e:
         log.fatal('Failed to search for replica credentials: ' + e.message['desc'])
         assert False
 
     #
-    # Check that the AES plugin exists, and has all the attributes listed in DES plugin.
-    # The attributes might not be in the expected order so check all the attributes.
+    # Check that the AES plugin exists, and has all the attributes listed in
+    # DES plugin.  The attributes might not be in the expected order so check
+    # all the attributes.
     #
     try:
-        entry = topology.master1.search_s(AES_PLUGIN, ldap.SCOPE_BASE, 'objectclass=*')
+        entry = topology.master1.search_s(AES_PLUGIN, ldap.SCOPE_BASE,
+                                          'objectclass=*')
         if not entry[0].hasValue('nsslapd-pluginarg0', 'description') and \
            not entry[0].hasValue('nsslapd-pluginarg1', 'description') and \
            not entry[0].hasValue('nsslapd-pluginarg2', 'description'):
-            log.fatal('The AES plugin did not have the DES attribute copied over correctly')
+            log.fatal('The AES plugin did not have the DES attribute copied ' +
+                      'over correctly')
             assert False
         else:
             log.info('The AES plugin was correctly setup')
@@ -286,7 +300,8 @@ def test_ticket47462(topology):
     # Check that the MMR plugin was updated
     #
     try:
-        entry = topology.master1.search_s(MMR_PLUGIN, ldap.SCOPE_BASE, 'objectclass=*')
+        entry = topology.master1.search_s(MMR_PLUGIN, ldap.SCOPE_BASE,
+                                          'objectclass=*')
         if not entry[0].hasValue('nsslapd-plugin-depends-on-named', 'AES'):
             log.fatal('The MMR Plugin was not correctly updated')
             assert False
@@ -300,7 +315,8 @@ def test_ticket47462(topology):
     # Check that the DES plugin was correctly updated
     #
     try:
-        entry = topology.master1.search_s(DES_PLUGIN, ldap.SCOPE_BASE, 'objectclass=*')
+        entry = topology.master1.search_s(DES_PLUGIN, ldap.SCOPE_BASE,
+                                          'objectclass=*')
         if not entry[0].hasValue('nsslapd-pluginPath', 'libpbe-plugin'):
             log.fatal('The DES Plugin was not correctly updated')
             assert False
@@ -322,7 +338,8 @@ def test_ticket47462(topology):
         ent = None
         while loop <= 10:
             try:
-                ent = topology.master2.getEntry(USER_DN, ldap.SCOPE_BASE, "(objectclass=*)")
+                ent = topology.master2.getEntry(USER_DN, ldap.SCOPE_BASE,
+                                                "(objectclass=*)")
                 break
             except ldap.NO_SUCH_OBJECT:
                 time.sleep(1)
@@ -336,30 +353,66 @@ def test_ticket47462(topology):
         log.fatal('Failed to add test user: ' + e.message['desc'])
         assert False
 
+    # Check the entry
+    log.info('Entry before running task...')
+    try:
+        entry = topology.master1.search_s(USER1_DN,
+                                          ldap.SCOPE_BASE,
+                                          'objectclass=*')
+        if entry:
+            print(str(entry))
+        else:
+            log.fatal('Failed to find entries')
+            assert False
+    except ldap.LDAPError as e:
+        log.fatal('Failed to search for entries: ' +
+                  e.message['desc'])
+        assert False
 
-def test_ticket47462_final(topology):
-    topology.master1.delete()
-    topology.master2.delete()
-    log.info('Testcase PASSED')
-
-
-def run_isolated():
-    '''
-        run_isolated is used to run these test cases independently of a test scheduler (xunit, py.test..)
-        To run isolated without py.test, you need to
-            - edit this file and comment '@pytest.fixture' line before 'topology' function.
-            - set the installation prefix
-            - run this program
-    '''
-    global installation1_prefix
-    global installation2_prefix
-    installation1_prefix = None
-    installation2_prefix = None
+    #
+    # Test the DES2AES Task on USER1_DN
+    #
+    try:
+        topology.master1.add_s(Entry((DES2AES_TASK_DN,
+                                      {'objectclass': ['top',
+                                                       'extensibleObject'],
+                                       'suffix': DEFAULT_SUFFIX,
+                                       'cn': 'convert'})))
+    except ldap.LDAPError as e:
+        log.fatal('Failed to add task entry: ' + e.message['desc'])
+        assert False
 
-    topo = topology(True)
-    test_ticket47462(topo)
-    test_ticket47462_final(topo)
+    # Wait for task
+    task_entry = Entry(DES2AES_TASK_DN)
+    (done, exitCode) = topology.master1.tasks.checkTask(task_entry, True)
+    if exitCode:
+        log.fatal("Error: des2aes task exited with %d" % (exitCode))
+        assert False
 
+    # Check the entry
+    try:
+        entry = topology.master1.search_s(USER1_DN,
+                                          ldap.SCOPE_BASE,
+                                          'objectclass=*')
+        if entry:
+            val = entry[0].getValue('description')
+            print(str(entry[0]))
+            if val.startswith('{AES-'):
+                log.info('Task: DES credentials have been converted to AES')
+            else:
+                log.fatal('Task: Failed to convert credentials from DES to ' +
+                          'AES! (%s)' % (val))
+                assert False
+        else:
+            log.fatal('Failed to find entries')
+            assert False
+    except ldap.LDAPError as e:
+        log.fatal('Failed to search for entries: ' +
+                  e.message['desc'])
+        assert False
 
 if __name__ == '__main__':
-    run_isolated()
+    # Run isolated
+    # -s for DEBUG mode
+    CURRENT_FILE = os.path.realpath(__file__)
+    pytest.main("-s %s" % CURRENT_FILE)
diff --git a/ldap/servers/slapd/daemon.c b/ldap/servers/slapd/daemon.c
index d702129..29562ae 100644
--- a/ldap/servers/slapd/daemon.c
+++ b/ldap/servers/slapd/daemon.c
@@ -692,14 +692,12 @@ convert_pbe_des_to_aes()
     Slapi_Entry **entries = NULL;
     struct slapdplugin *plugin = NULL;
     char **attrs = NULL;
-    char **backends = NULL;
     char *val = NULL;
     int converted_des_passwd = 0;
-    int disable_des = 1;
     int result = -1;
     int have_aes = 0;
     int have_des = 0;
-    int i = 0, ii = 0, be_idx = 0;
+    int i = 0, ii = 0;
 
     /*
      * Check that AES plugin is enabled, and grab all the unique
@@ -733,94 +731,56 @@ convert_pbe_des_to_aes()
 
     if(have_aes && have_des){
         /*
-         * Build a list of all the backend dn's
+         * Find any entries in cn=config that contain DES passwords and convert
+         * them to AES
          */
-        Slapi_Backend *be = NULL;
-        struct suffixlist *list;
-        char *cookie = NULL;
-
-        LDAPDebug(LDAP_DEBUG_ANY, "convert_pbe_des_to_aes:  "
-                "Checking for DES passwords to convert to AES...\n",0,0,0);
-
-        be = slapi_get_first_backend(&cookie);
-        while (be){
-            int suffix_idx = 0;
-            int count = slapi_counter_get_value(be->be_suffixcounter);
-
-            list = be->be_suffixlist;
-            for (suffix_idx = 0; list && suffix_idx < count; suffix_idx++) {
-                char *suffix = (char *)slapi_sdn_get_ndn(list->be_suffix);
-                if(charray_inlist(backends, suffix) || strlen(suffix) == 0){
-                    list = list->next;
-                    continue;
-                }
-                charray_add(&backends, slapi_ch_strdup(suffix));
-                list = list->next;
-            }
-            be = slapi_get_next_backend (cookie);
-        }
-        slapi_ch_free ((void **)&cookie);
+        slapi_log_error(SLAPI_LOG_HOUSE,  "convert_pbe_des_to_aes",
+                "Converting DES passwords to AES...\n");
 
-        /*
-         * Search for the password attributes
-         */
         for (i = 0; attrs && attrs[i]; i++){
             char *filter = PR_smprintf("%s=*", attrs[i]);
-            /*
-             * Loop over all the backends looking for the password attribute
-             */
-            for(be_idx = 0; backends && backends[be_idx]; be_idx++){
-                pb = slapi_pblock_new();
-                slapi_search_internal_set_pb(pb, backends[be_idx],
-                        LDAP_SCOPE_SUBTREE, filter, NULL, 0, NULL, NULL,
-                        (void *)plugin_get_default_component_id(),
-                        SLAPI_OP_FLAG_IGNORE_UNINDEXED);
-                slapi_search_internal_pb(pb);
-                slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_RESULT, &result);
-                if (LDAP_SUCCESS != result) {
-                    slapi_log_error(SLAPI_LOG_TRACE, "convert_pbe_des_to_aes: ",
-                        "Failed to search for password attribute (%s) error (%d), skipping suffix (%s)\n",
-                        attrs[i], result, backends[be_idx]);
-                    slapi_free_search_results_internal(pb);
-                    slapi_pblock_destroy(pb);
-                    pb = NULL;
-                    continue;
-                }
-                slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, &entries);
-                for (ii = 0; entries && entries[ii]; ii++){
-                    if((val = slapi_entry_attr_get_charptr(entries[ii], attrs[i]))){
-                        if(strlen(val) >= 5 && strncmp(val,"{DES}", 5) == 0){
-                            /*
-                             * We have a DES encoded password, convert it AES
-                             */
-                            Slapi_PBlock *mod_pb = NULL;
-                            Slapi_Value *sval = NULL;
-                            LDAPMod mod_replace;
-                            LDAPMod *mods[2];
-                            char *replace_val[2];
-                            char *passwd = NULL;
-
-                            /* decode the DES password */
-                            if(pw_rever_decode(val, &passwd, attrs[i]) == -1){
-                                LDAPDebug(LDAP_DEBUG_ANY,"convert_pbe_des_to_aes: "
-                                        "Failed to decode existing DES password for (%s)\n",
-                                        slapi_entry_get_dn(entries[ii]), 0, 0);
-                                disable_des = 0;
-                                goto done;
-                            }
 
-                            /* encode the password */
+            pb = slapi_pblock_new();
+            slapi_search_internal_set_pb(pb, "cn=config",
+                LDAP_SCOPE_SUBTREE, filter, NULL, 0, NULL, NULL,
+                (void *)plugin_get_default_component_id(),
+                SLAPI_OP_FLAG_IGNORE_UNINDEXED);
+            slapi_search_internal_pb(pb);
+            slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, &entries);
+            for (ii = 0; entries && entries[ii]; ii++){
+                if((val = slapi_entry_attr_get_charptr(entries[ii], attrs[i]))){
+                    if(strlen(val) >= 5 && strncmp(val,"{DES}", 5) == 0){
+                        /*
+                         * We have a DES encoded password, convert it to AES
+                         */
+                        Slapi_PBlock *mod_pb = NULL;
+                        Slapi_Value *sval = NULL;
+                        LDAPMod mod_replace;
+                        LDAPMod *mods[2];
+                        char *replace_val[2];
+                        char *passwd = NULL;
+                        int rc = 0;
+
+                        /* decode the DES password */
+                        if(pw_rever_decode(val, &passwd, attrs[i]) == -1){
+                            slapi_log_error(SLAPI_LOG_FATAL ,"convert_pbe_des_to_aes",
+                                    "Failed to decode existing DES password for (%s)\n",
+                                    slapi_entry_get_dn(entries[ii]));
+                            rc = -1;
+                        }
+
+                        /* encode the password */
+                        if (rc == 0){
                             sval = slapi_value_new_string(passwd);
                             if(pw_rever_encode(&sval, attrs[i]) == -1){
-                                LDAPDebug(LDAP_DEBUG_ANY,"convert_pbe_des_to_aes: "
+                                slapi_log_error(SLAPI_LOG_FATAL, "convert_pbe_des_to_aes",
                                         "failed to encode AES password for (%s)\n",
-                                        slapi_entry_get_dn(entries[ii]), 0, 0);
-                                slapi_ch_free_string(&passwd);
-                                slapi_value_free(&sval);
-                                disable_des = 0;
-                                goto done;
+                                        slapi_entry_get_dn(entries[ii]));
+                                rc = -1;
                             }
+                        }
 
+                        if (rc == 0){
                             /* replace the attribute in the entry */
                             replace_val[0] = (char *)slapi_value_get_string(sval);
                             replace_val[1] = NULL;
@@ -837,83 +797,34 @@ convert_pbe_des_to_aes()
 
                             slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_RESULT, &result);
                             if (LDAP_SUCCESS != result) {
-                                LDAPDebug(LDAP_DEBUG_ANY,"convert_pbe_des_to_aes: "
+                                slapi_log_error(SLAPI_LOG_FATAL, "convert_pbe_des_to_aes"
                                         "Failed to convert password for (%s) error (%d)\n",
-                                        slapi_entry_get_dn(entries[ii]), result, 0);
-                                disable_des = 0;
+                                        slapi_entry_get_dn(entries[ii]), result);
                             } else {
-                                LDAPDebug(LDAP_DEBUG_ANY,"convert_pbe_des_to_aes: "
+                                slapi_log_error(SLAPI_LOG_HOUSE, "convert_pbe_des_to_aes",
                                         "Successfully converted password for (%s)\n",
-                                         slapi_entry_get_dn(entries[ii]), result, 0);
+                                         slapi_entry_get_dn(entries[ii]));
                                 converted_des_passwd = 1;
                             }
-                            slapi_ch_free_string(&passwd);
-                            slapi_value_free(&sval);
-                            slapi_pblock_destroy(mod_pb);
                         }
-                        slapi_ch_free_string(&val);
+                        slapi_ch_free_string(&passwd);
+                        slapi_value_free(&sval);
+                        slapi_pblock_destroy(mod_pb);
                     }
+                    slapi_ch_free_string(&val);
                 }
-                slapi_free_search_results_internal(pb);
-                slapi_pblock_destroy(pb);
-                pb = NULL;
             }
+            slapi_free_search_results_internal(pb);
+            slapi_pblock_destroy(pb);
+            pb = NULL;
             slapi_ch_free_string(&filter);
         }
         if (!converted_des_passwd){
-            slapi_log_error(SLAPI_LOG_FATAL, "convert_pbe_des_to_aes",
+            slapi_log_error(SLAPI_LOG_HOUSE, "convert_pbe_des_to_aes",
                 "No DES passwords found to convert.\n");
         }
     }
-
-done:
     charray_free(attrs);
-    charray_free(backends);
-    slapi_free_search_results_internal(pb);
-    slapi_pblock_destroy(pb);
-
-    if (have_aes && have_des){
-        /*
-         * If a conversion attempt did not fail then we can disable the DES plugin
-         */
-        if(converted_des_passwd && disable_des){
-            /*
-             * Disable the DES plugin - this also prevents potentially expensive
-             * searches at every server startup.
-             */
-            LDAPMod mod_replace;
-            LDAPMod *mods[2];
-            char *replace_val[2];
-            char *des_dn = "cn=DES,cn=Password Storage Schemes,cn=plugins,cn=config";
-
-            replace_val[0] = "off";
-            replace_val[1] = NULL;
-            mod_replace.mod_op = LDAP_MOD_REPLACE;
-            mod_replace.mod_type = "nsslapd-pluginEnabled";
-            mod_replace.mod_values = replace_val;
-            mods[0] = &mod_replace;
-            mods[1] = 0;
-
-            pb = slapi_pblock_new();
-            slapi_modify_internal_set_pb(pb, des_dn, mods, 0, 0,
-                    (void *)plugin_get_default_component_id(), 0);
-            slapi_modify_internal_pb(pb);
-            slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_RESULT, &result);
-            if (LDAP_SUCCESS != result) {
-                LDAPDebug(LDAP_DEBUG_ANY,"convert_pbe_des_to_aes: "
-                        "Failed to disable DES plugin (%s), error (%d)\n",
-                        des_dn, result, 0);
-            } else {
-                LDAPDebug(LDAP_DEBUG_ANY,"convert_pbe_des_to_aes: "
-                        "Successfully disabled DES plugin (%s)\n",
-                        des_dn, 0, 0);
-            }
-            slapi_pblock_destroy(pb);
-            LDAPDebug(LDAP_DEBUG_ANY,"convert_pbe_des_to_aes: "
-                      "All DES passwords have been converted to AES.\n",
-                      0, 0, 0);
-        }
-    }
 }
 
 #ifdef ENABLE_NUNC_STANS
diff --git a/ldap/servers/slapd/pw.c b/ldap/servers/slapd/pw.c
index 4e222d7..883ef80 100644
--- a/ldap/servers/slapd/pw.c
+++ b/ldap/servers/slapd/pw.c
@@ -516,10 +516,10 @@ pw_rever_encode(Slapi_Value **vals, char * attr_name)
 	for ( p = get_plugin_list(PLUGIN_LIST_REVER_PWD_STORAGE_SCHEME); p != NULL; p = p->plg_next )
 	{
 		char *L_attr = NULL;
-		int i = 0;
+		int i = 0, ii = 0;
 
 		/* Get the appropriate encoding function */
-		for ( L_attr = p->plg_argv[i]; i<p->plg_argc; L_attr = p->plg_argv[++i] )
+		for ( L_attr = p->plg_argv[ii]; ii<p->plg_argc; L_attr = p->plg_argv[++ii] )
 		{
 			if (slapi_attr_types_equivalent(L_attr, attr_name))
 			{
diff --git a/ldap/servers/slapd/task.c b/ldap/servers/slapd/task.c
index db3c222..405f0bf 100644
--- a/ldap/servers/slapd/task.c
+++ b/ldap/servers/slapd/task.c
@@ -53,6 +53,8 @@ static int shutting_down = 0;
 #define TASK_TOMBSTONE_FIXUP_BACKEND "backend"
 #define TASK_TOMBSTONE_FIXUP_SUFFIX "suffix"
 #define TASK_TOMBSTONE_FIXUP_STRIPCSN "stripcsn"
+#define TASK_DES2AES "des2aes task"
+
 
 #define LOG_BUFFER              256
 /* if the cumul. log gets larger than this, it's truncated: */
@@ -83,8 +85,10 @@ static const char *fetch_attr(Slapi_Entry *e, const char *attrname,
                               const char *default_val);
 static Slapi_Entry *get_internal_entry(Slapi_PBlock *pb, char *dn);
 static void modify_internal_entry(char *dn, LDAPMod **mods);
-
 static void fixup_tombstone_task_destructor(Slapi_Task *task);
+static void task_des2aes_thread(void *arg);
+static void des2aes_task_destructor(Slapi_Task *task);
+
 
 /***********************************
  * Public Functions
@@ -2425,6 +2429,345 @@ fixup_tombstone_task_destructor(Slapi_Task *task)
                     "fixup_tombstone_task_destructor <--\n" );
 }
 
+/*
+ * des2aes Task
+ *
+ * Convert any DES passwords to AES
+ *
+ * dn: cn=convertPasswords, cn=des2aes,cn=tasks,cn=config
+ * objectclass: top
+ * objectclass: extensibleObject
+ * suffix: dc=example,dc=com (If empty all backends are checked)
+ * suffix: dc=other,dc=suffix
+ */
+struct task_des2aes_data
+{
+    char **suffixes;
+    Slapi_Task *task;
+};
+
+static int
+task_des2aes(Slapi_PBlock *pb, Slapi_Entry *e, Slapi_Entry *eAfter,
+             int *returncode, char *returntext, void *arg)
+{
+    struct task_des2aes_data *task_data = NULL;
+    PRThread *thread = NULL;
+    Slapi_Task *task = NULL;
+    char **suffix = NULL;
+    char **bases = NULL;
+    int rc = SLAPI_DSE_CALLBACK_OK;
+
+    /* Get the suffixes */
+    if((suffix = slapi_entry_attr_get_charray(e, "suffix"))){
+        int i;
+        for (i = 0; suffix && suffix[i]; i++){
+            /* Make sure "suffix" is NUL terminated string */
+            char *dn = slapi_create_dn_string("%s", suffix[i]);
+
+            if(dn){
+                if(slapi_dn_syntax_check(pb, dn, 1)){
+                    /* invalid suffix name */
+                    PR_snprintf(returntext, SLAPI_DSE_RETURNTEXT_SIZE,
+                        "Invalid DN syntax (%s) specified for \"suffix\"\n",
+                        suffix[i]);
+                    *returncode = LDAP_INVALID_DN_SYNTAX;
+                    slapi_ch_free_string(&dn);
+                    rc = SLAPI_DSE_CALLBACK_ERROR;
+                    goto error;
+                } else {
+                    slapi_ch_array_add(&bases, dn);
+                }
+            } else{
+                PR_snprintf(returntext, SLAPI_DSE_RETURNTEXT_SIZE,
+                    "Invalid DN (%s) specified for \"suffix\"\n", suffix[i]);
+                    *returncode = LDAP_INVALID_DN_SYNTAX;
+                rc = SLAPI_DSE_CALLBACK_ERROR;
+                goto error;
+            }
+        }
+    }
+
+    /* Build the task data and fire off a thread to perform the conversion */
+    task = slapi_new_task(slapi_entry_get_ndn(e));
+
+    /* register our destructor for cleaning up our private data */
+    slapi_task_set_destructor_fn(task, des2aes_task_destructor);
+    task_data = (struct task_des2aes_data *)slapi_ch_calloc(1, sizeof(struct task_des2aes_data));
+    task_data->suffixes = bases;
+    task_data->task = task;
+
+    /* Start the conversion thread */
+    thread = PR_CreateThread(PR_USER_THREAD, task_des2aes_thread,
+                             (void *)task_data, PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD,
+                             PR_UNJOINABLE_THREAD, SLAPD_DEFAULT_THREAD_STACKSIZE);
+    if (thread == NULL) {
+        PR_snprintf(returntext, SLAPI_DSE_RETURNTEXT_SIZE,
+                "unable to create des2aes thread!\n");
+        slapi_log_error(SLAPI_LOG_FATAL, TASK_DES2AES,
+                        "unable to create des2aes thread!\n");
+        *returncode = LDAP_OPERATIONS_ERROR;
+        slapi_task_finish(task, *returncode);
+        rc = SLAPI_DSE_CALLBACK_ERROR;
+    }
+
+error:
+    if (rc == SLAPI_DSE_CALLBACK_ERROR){
+        slapi_ch_array_free(bases);
+        slapi_ch_array_free(suffix);
+        slapi_ch_free((void **)&task_data);
+    }
+    return rc;
+}
+
+static void
+task_des2aes_thread(void *arg)
+{
+    struct task_des2aes_data *task_data = arg;
+    Slapi_PBlock *pb = NULL;
+    Slapi_Entry **entries = NULL;
+    Slapi_Task *task = task_data->task;
+    struct slapdplugin *plugin = NULL;
+    char **attrs = NULL;
+    char **backends = NULL;
+    char *val = NULL;
+    int converted_des_passwd = 0;
+    int result = -1;
+    int have_aes = 0;
+    int have_des = 0;
+    int i = 0, ii = 0, be_idx = 0;
+    int rc = 0;
+
+    /*
+     * Check that AES plugin is enabled, and grab all the unique
+     * password attributes.
+     */
+    for ( plugin = get_plugin_list(PLUGIN_LIST_REVER_PWD_STORAGE_SCHEME);
+          plugin != NULL;
+          plugin = plugin->plg_next )
+    {
+        char *plugin_arg = NULL;
+
+        if(plugin->plg_started && strcasecmp(plugin->plg_name, "AES") == 0){
+            /* We have the AES plugin, and its enabled */
+            have_aes = 1;
+        }
+        if(plugin->plg_started && strcasecmp(plugin->plg_name, "DES") == 0){
+            /* We have the DES plugin, and its enabled */
+            have_des = 1;
+        }
+        /* Gather all the unique password attributes from all the PBE plugins */
+        for ( i = 0, plugin_arg = plugin->plg_argv[i];
+              i < plugin->plg_argc;
+              plugin_arg = plugin->plg_argv[++i] )
+        {
+            if(charray_inlist(attrs, plugin_arg)){
+                continue;
+            }
+            charray_add(&attrs, slapi_ch_strdup(plugin_arg));
+        }
+    }
+
+    if(have_aes && have_des){
+        if(task_data->suffixes == NULL){
+            /*
+             * Build a list of all the backend dn's
+             */
+            Slapi_Backend *be = NULL;
+            struct suffixlist *list;
+            char *cookie = NULL;
+
+            slapi_log_error(SLAPI_LOG_FATAL, TASK_DES2AES,
+                "Checking for DES passwords to convert to AES...\n");
+            slapi_task_log_notice(task,
+                "Checking for DES passwords to convert to AES...\n");
+
+            be = slapi_get_first_backend(&cookie);
+            while (be){
+                int suffix_idx = 0;
+                int count = slapi_counter_get_value(be->be_suffixcounter);
+
+                list = be->be_suffixlist;
+                for (suffix_idx = 0; list && suffix_idx < count; suffix_idx++) {
+                    char *suffix = (char *)slapi_sdn_get_ndn(list->be_suffix);
+                    if(charray_inlist(backends, suffix) || strlen(suffix) == 0){
+                        list = list->next;
+                        continue;
+                    }
+                    charray_add(&backends, slapi_ch_strdup(suffix));
+                    list = list->next;
+                }
+                be = slapi_get_next_backend (cookie);
+            }
+            slapi_ch_free ((void **)&cookie);
+        } else {
+            backends = task_data->suffixes;
+        }
+
+        /*
+         * Search for the password attributes
+         */
+        for (i = 0; attrs && attrs[i]; i++){
+            char *filter = PR_smprintf("%s=*", attrs[i]);
+            /*
+             * Loop over all the backends looking for the password attribute
+             */
+            for(be_idx = 0; backends && backends[be_idx]; be_idx++){
+                pb = slapi_pblock_new();
+                slapi_search_internal_set_pb(pb, backends[be_idx],
+                        LDAP_SCOPE_SUBTREE, filter, NULL, 0, NULL, NULL,
+                        (void *)plugin_get_default_component_id(),
+                        SLAPI_OP_FLAG_IGNORE_UNINDEXED);
+                slapi_search_internal_pb(pb);
+                slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_RESULT, &result);
+                if (LDAP_SUCCESS != result) {
+                    slapi_log_error(SLAPI_LOG_FATAL, "convert_pbe_des_to_aes: ",
+                        "Failed to search for password attribute (%s) error (%d), skipping suffix (%s)\n",
+                        attrs[i], result, backends[be_idx]);
+                    slapi_task_log_notice(task,
+                        "Failed to search for password attribute (%s) error (%d), skipping suffix (%s)\n",
+                        attrs[i], result, backends[be_idx]);
+                    slapi_free_search_results_internal(pb);
+                    slapi_pblock_destroy(pb);
+                    pb = NULL;
+                    continue;
+                }
+                slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, &entries);
+                for (ii = 0; entries && entries[ii]; ii++){
+                    if((val = slapi_entry_attr_get_charptr(entries[ii], attrs[i]))){
+                        if(strlen(val) >= 5 && strncmp(val,"{DES}", 5) == 0){
+                            /*
+                             * We have a DES encoded password, convert it AES
+                             */
+                            Slapi_PBlock *mod_pb = NULL;
+                            Slapi_Value *sval = NULL;
+                            LDAPMod mod_replace;
+                            LDAPMod *mods[2];
+                            char *replace_val[2];
+                            char *passwd = NULL;
+
+                            /* Decode the DES password */
+                            if(pw_rever_decode(val, &passwd, attrs[i]) == -1){
+                                slapi_log_error(SLAPI_LOG_FATAL, TASK_DES2AES,
+                                        "Failed to decode existing DES password for (%s)\n",
+                                        slapi_entry_get_dn(entries[ii]));
+                                slapi_task_log_notice(task,
+                                    "Failed to decode existing DES password for (%s)\n",
+                                    slapi_entry_get_dn(entries[ii]));
+                                rc = 1;
+                                goto done;
+                            }
+
+                            /* Encode the password */
+                            sval = slapi_value_new_string(passwd);
+                            if(pw_rever_encode(&sval, attrs[i]) == -1){
+                                slapi_log_error(SLAPI_LOG_FATAL, TASK_DES2AES,
+                                    "failed to encode AES password for (%s)\n",
+                                    slapi_entry_get_dn(entries[ii]));
+                                slapi_task_log_notice(task,
+                                    "failed to encode AES password for (%s)\n",
+                                    slapi_entry_get_dn(entries[ii]));
+                                slapi_ch_free_string(&passwd);
+                                slapi_value_free(&sval);
+                                rc = 1;
+                                goto done;
+                            }
+
+                            /* Replace the attribute in the entry */
+                            replace_val[0] = (char *)slapi_value_get_string(sval);
+                            replace_val[1] = NULL;
+                            mod_replace.mod_op = LDAP_MOD_REPLACE;
+                            mod_replace.mod_type = attrs[i];
+                            mod_replace.mod_values = replace_val;
+                            mods[0] = &mod_replace;
+                            mods[1] = 0;
+
+                            mod_pb = slapi_pblock_new();
+                            slapi_modify_internal_set_pb(mod_pb, slapi_entry_get_dn(entries[ii]),
+                                    mods, 0, 0, (void *)plugin_get_default_component_id(), 0);
+                            slapi_modify_internal_pb(mod_pb);
+
+                            slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_RESULT, &result);
+                            if (LDAP_SUCCESS != result) {
+                                slapi_log_error(SLAPI_LOG_FATAL, TASK_DES2AES,
+                                    "Failed to convert password for (%s) error (%d)\n",
+                                    slapi_entry_get_dn(entries[ii]), result);
+                                slapi_task_log_notice(task,
+                                    "Failed to convert password for (%s) error (%d)\n",
+                                    slapi_entry_get_dn(entries[ii]), result);
+                                rc = 1;
+                            } else {
+                                slapi_log_error(SLAPI_LOG_FATAL, TASK_DES2AES,
+                                    "Successfully converted password for (%s)\n",
+                                     slapi_entry_get_dn(entries[ii]));
+                                slapi_task_log_notice(task,
+                                   "Successfully converted password for (%s)\n",
+                                    slapi_entry_get_dn(entries[ii]));
+                                converted_des_passwd = 1;
+                            }
+                            slapi_ch_free_string(&passwd);
+                            slapi_value_free(&sval);
+                            slapi_pblock_destroy(mod_pb);
+                        }
+                        slapi_ch_free_string(&val);
+                    }
+                }
+                slapi_free_search_results_internal(pb);
+                slapi_pblock_destroy(pb);
+                pb = NULL;
+            }
+            slapi_ch_free_string(&filter);
+        }
+        if (!converted_des_passwd){
+            slapi_log_error(SLAPI_LOG_FATAL, TASK_DES2AES,
+                "No DES passwords found to convert.\n");
+            slapi_task_log_notice(task, "No DES passwords found to convert.\n");
+        }
+    } else {
+        /* No AES/DES */
+        if (!have_des){
+            slapi_log_error(SLAPI_LOG_FATAL, TASK_DES2AES,
+                            "DES plugin not enabled\n");
+            slapi_task_log_notice(task, "DES plugin not enabled\n");
+        }
+        if (!have_aes){
+            slapi_log_error(SLAPI_LOG_FATAL, TASK_DES2AES,
+                        "AES plugin not enabled\n");
+            slapi_task_log_notice(task, "AES plugin not enabled\n");
+        }
+        slapi_log_error(SLAPI_LOG_FATAL, TASK_DES2AES,
+            "Unable to convert passwords\n");
+        slapi_task_log_notice(task, "Unable to convert passwords\n");
+        rc = 1;
+    }
+
+done:
+    charray_free(attrs);
+    charray_free(backends);
+    slapi_free_search_results_internal(pb);
+    slapi_pblock_destroy(pb);
+    slapi_task_finish(task, rc);
+}
+
+static void
+des2aes_task_destructor(Slapi_Task *task)
+{
+    slapi_log_error(SLAPI_LOG_TRACE, TASK_DES2AES,
+                    "des2aes_task_destructor -->\n" );
+    if (task) {
+        struct task_des2aes_data *task_data = (struct task_des2aes_data *)slapi_task_get_data(task);
+        while (slapi_task_get_refcount(task) > 0) {
+            /* Yield to wait for the task to finish. */
+            DS_Sleep (PR_MillisecondsToInterval(100));
+        }
+        if (task_data) {
+            slapi_ch_array_free(task_data->suffixes);
+            slapi_ch_free((void **)&task_data);
+        }
+    }
+    slapi_log_error(SLAPI_LOG_TRACE, TASK_DES2AES,
+                    "des2aes_task_destructor <--\n" );
+}
+
 /* cleanup old tasks that may still be in the DSE from a previous session
  * (this can happen if the server crashes [no matter how unlikely we like
  * to think that is].)
@@ -2506,6 +2849,7 @@ void task_init(void)
     slapi_task_register_handler("upgradedb", task_upgradedb_add);
     slapi_task_register_handler("sysconfig reload", task_sysconfig_reload_add);
     slapi_task_register_handler("fixup tombstones", task_fixup_tombstones_add);
+    slapi_task_register_handler("des2aes", task_des2aes);
 }
 
 /* called when the server is shutting down -- abort all existing tasks */
-- 
2.4.11