From c6d87264184efb58a89fc61b2c074f91bdd395ba Mon Sep 17 00:00:00 2001 From: William Brown Date: Feb 27 2018 00:20:16 +0000 Subject: Ticket 49447 - PBKDF2 on upgrade Bug Description: We changed the default password hash mech to PBKDF2 on upgrade - but if the upgrade from 1.3.x to 1.4.x, this new plugin object doesn't exist. This causes the server to fail to start. Adittionally, one could imagine an admin error could cause this also to be omitted Fix Description: Create and bootstrap the plugins on startup https://pagure.io/389-ds-base/issue/49447 Author: wibrown Review by: mreynolds --- diff --git a/dirsrvtests/tests/suites/password/pbkdf2_upgrade_plugin_test.py b/dirsrvtests/tests/suites/password/pbkdf2_upgrade_plugin_test.py new file mode 100644 index 0000000..3e29eb9 --- /dev/null +++ b/dirsrvtests/tests/suites/password/pbkdf2_upgrade_plugin_test.py @@ -0,0 +1,48 @@ +# --- BEGIN COPYRIGHT BLOCK --- +# Copyright (C) 2018 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.topologies import topology_st +from lib389.password_plugins import PBKDF2Plugin + +def test_pbkdf2_upgrade(topology_st): + """On upgrade pbkdf2 doesn't ship. We need to be able to + provide this on upgrade to make sure default hashes work. + However, password plugins are special - they need really + early bootstap so that setting the default has specs work. + + This tests that the removal of the pbkdf2 plugin causes + it to be re-bootstrapped and added. + + :id: c2198692-7c02-433b-af5b-3be54920571a + :setup: Single instance + :steps: 1. Remove the PBKDF2 plugin + 2. Restart the server + 3. Restart the server + :expectedresults: + 1. Plugin is removed (IE pre-upgrade state) + 2. The plugin is bootstrapped and added + 3. No change (already bootstrapped) + + """ + # Remove the pbkdf2 plugin config + p1 = PBKDF2Plugin(topology_st.standalone) + assert(p1.exists()) + p1._protected = False + p1.delete() + # Restart + topology_st.standalone.restart() + # check it's been readded. + p2 = PBKDF2Plugin(topology_st.standalone) + assert(p2.exists()) + # Now restart to make sure we still work from the non-bootstrap form + topology_st.standalone.restart() + p3 = PBKDF2Plugin(topology_st.standalone) + assert(p3.exists()) + + diff --git a/ldap/servers/slapd/config.c b/ldap/servers/slapd/config.c index c8d57e7..9b3b5a6 100644 --- a/ldap/servers/slapd/config.c +++ b/ldap/servers/slapd/config.c @@ -35,6 +35,19 @@ extern char *slapd_SSL3ciphers; extern char *localuser; char *rel2abspath(char *); +static char *bootstrap_plugins[] = { + "dn: cn=PBKDF2_SHA256,cn=Password Storage Schemes,cn=plugins,cn=config\n" + "objectclass: top\n" + "objectclass: nsSlapdPlugin\n" + "cn: PBKDF2_SHA256\n" + "nsslapd-pluginpath: libpwdstorage-plugin\n" + "nsslapd-plugininitfunc: pbkdf2_sha256_pwd_storage_scheme_init\n" + "nsslapd-plugintype: pwdstoragescheme\n" + "nsslapd-pluginenabled: on", + + NULL +}; + /* See if the given entry has an attribute with the given name and the given value; if value is NULL, just test for the presence of the given @@ -494,9 +507,33 @@ slapd_bootstrap_config(const char *configdir) val[0] = 0; } - if (e) + if (e) { slapi_entry_free(e); + } + } /* (entrystr = dse_read_next_entry(buf, &lastp) */ + /* + * Okay, now we have to add "fake" plugins into memory + * so that password can work. They'll be created properly + * later in dse.ldif. + */ + + for (size_t i = 0; bootstrap_plugins[i] != NULL; i++) { + /* Convert the str to an entry */ + char *temp = strdup(bootstrap_plugins[i]); + Slapi_Entry *e = slapi_str2entry(temp, 0); + slapi_ch_free_string(&temp); + /* Try and apply it */ + if (e == NULL) { + continue; + } + if (plugin_setup(e, 0, 0, 1, returntext) != 0) { + slapi_log_err(SLAPI_LOG_TRACE, "slapd_bootstrap_config", "Application of plugin failed, maybe already there?\n"); + } else { + slapi_log_err(SLAPI_LOG_TRACE, "slapd_bootstrap_config", "Application of plugin SUCCESS\n"); + } + slapi_entry_free(e); } + /* kexcoff: initialize rootpwstoragescheme and pw_storagescheme * if not explicilty set in the config file */ diff --git a/ldap/servers/slapd/fedse.c b/ldap/servers/slapd/fedse.c index 68b910e..08e33c0 100644 --- a/ldap/servers/slapd/fedse.c +++ b/ldap/servers/slapd/fedse.c @@ -112,7 +112,22 @@ static const char *internal_entries[] = "objectclass:top\n" "objectclass:nsSNMP\n" "cn:SNMP\n" - "nsSNMPEnabled: on\n"}; + "nsSNMPEnabled: on\n", + + "dn: cn=Password Storage Schemes,cn=plugins,cn=config\n" + "objectclass: top\n" + "objectclass: nsContainer\n" + "cn: Password Storage Schemes\n", + + "dn: cn=PBKDF2_SHA256,cn=Password Storage Schemes,cn=plugins,cn=config\n" + "objectclass: top\n" + "objectclass: nsSlapdPlugin\n" + "cn: PBKDF2_SHA256\n" + "nsslapd-pluginpath: libpwdstorage-plugin\n" + "nsslapd-plugininitfunc: pbkdf2_sha256_pwd_storage_scheme_init\n" + "nsslapd-plugintype: pwdstoragescheme\n" + "nsslapd-pluginenabled: on\n", +}; static int NUM_INTERNAL_ENTRIES = sizeof(internal_entries) / sizeof(internal_entries[0]); diff --git a/src/lib389/lib389/__init__.py b/src/lib389/lib389/__init__.py index cea9b42..34a3580 100644 --- a/src/lib389/lib389/__init__.py +++ b/src/lib389/lib389/__init__.py @@ -1205,6 +1205,7 @@ class DirSrv(SimpleLDAPObject, object): env['ASAN_SYMBOLIZER_PATH'] = "/usr/bin/llvm-symbolizer" env['ASAN_OPTIONS'] = "symbolize=1 detect_deadlocks=1 log_path=%s/ns-slapd-%s.asan" % (self.ds_paths.run_dir, self.serverid) env.update(os.environ) + output = None try: output = subprocess.check_output(["%s/ns-slapd" % self.get_sbin_dir(), "-D",