diff --git a/Makefile.am b/Makefile.am
index b6ebf925c..546f89e5e 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1419,7 +1419,8 @@ libpwdstorage_plugin_la_SOURCES = ldap/servers/plugins/pwdstorage/clear_pwd.c \
 	ldap/servers/plugins/pwdstorage/pwd_util.c \
 	ldap/servers/plugins/pwdstorage/sha_pwd.c \
 	ldap/servers/plugins/pwdstorage/smd5_pwd.c \
-	ldap/servers/plugins/pwdstorage/ssha_pwd.c
+	ldap/servers/plugins/pwdstorage/ssha_pwd.c \
+	ldap/servers/plugins/pwdstorage/pbkdf2_pwd.c
 
 libpwdstorage_plugin_la_CPPFLAGS = $(PLUGIN_CPPFLAGS)
 libpwdstorage_plugin_la_LIBADD = libslapd.la $(NSS_LINK) $(NSPR_LINK) $(LIBCRYPT)
diff --git a/dirsrvtests/tests/tickets/ticket397_test.py b/dirsrvtests/tests/tickets/ticket397_test.py
new file mode 100644
index 000000000..4bf4eda90
--- /dev/null
+++ b/dirsrvtests/tests/tickets/ticket397_test.py
@@ -0,0 +1,151 @@
+import os
+import sys
+import time
+import ldap
+import logging
+import pytest
+from lib389 import DirSrv, Entry, tools, tasks
+from lib389.tools import DirSrvTools
+from lib389._constants import *
+from lib389.properties import *
+from lib389.tasks import *
+from lib389.utils import *
+
+DEBUGGING = False
+USER_DN = 'uid=user,ou=People,%s' % DEFAULT_SUFFIX
+
+if DEBUGGING:
+    logging.getLogger(__name__).setLevel(logging.DEBUG)
+else:
+    logging.getLogger(__name__).setLevel(logging.INFO)
+
+
+log = logging.getLogger(__name__)
+
+
+class TopologyStandalone(object):
+    """The DS Topology Class"""
+    def __init__(self, standalone):
+        """Init"""
+        standalone.open()
+        self.standalone = standalone
+
+
+@pytest.fixture(scope="module")
+def topology(request):
+    """Create DS Deployment"""
+
+    # Creating standalone instance ...
+    if DEBUGGING:
+        standalone = DirSrv(verbose=True)
+    else:
+        standalone = DirSrv(verbose=False)
+    args_instance[SER_HOST] = HOST_STANDALONE
+    args_instance[SER_PORT] = PORT_STANDALONE
+    args_instance[SER_SERVERID_PROP] = SERVERID_STANDALONE
+    args_instance[SER_CREATION_SUFFIX] = DEFAULT_SUFFIX
+    args_standalone = args_instance.copy()
+    standalone.allocate(args_standalone)
+    instance_standalone = standalone.exists()
+    if instance_standalone:
+        standalone.delete()
+    standalone.create()
+    standalone.open()
+
+    def fin():
+        """If we are debugging just stop the instances, otherwise remove
+        them
+        """
+        if DEBUGGING:
+            standalone.stop()
+        else:
+            standalone.delete()
+
+    request.addfinalizer(fin)
+
+    # Clear out the tmp dir
+    standalone.clearTmpDir(__file__)
+
+    return TopologyStandalone(standalone)
+
+def _test_bind(inst, password):
+    result = True
+    userconn = ldap.initialize("ldap://%s:%s" % (HOST_STANDALONE, PORT_STANDALONE))
+    try:
+        userconn.simple_bind_s(USER_DN, password)
+        userconn.unbind_s()
+    except ldap.INVALID_CREDENTIALS:
+        result = False
+    return result
+
+def _test_algo(inst, algo_name):
+    inst.config.set('passwordStorageScheme', algo_name)
+
+    if DEBUGGING:
+        print('Testing %s' % algo_name)
+
+    # Create the user with a password
+    inst.add_s(Entry((
+                USER_DN, {
+                    'objectClass': 'top account simplesecurityobject'.split(),
+                     'uid': 'user',
+                     'userpassword': ['Secret123', ]
+                })))
+
+    # Make sure when we read the userPassword field, it is the correct ALGO
+    pw_field = inst.search_s(USER_DN, ldap.SCOPE_BASE, '(objectClass=*)', ['userPassword']  )[0]
+
+    if DEBUGGING:
+        print(pw_field.getValue('userPassword'))
+
+    if algo_name != 'CLEAR':
+        lalgo_name = algo_name.lower()
+        lpw_algo_name = pw_field.getValue('userPassword').lower()
+        assert(lpw_algo_name.startswith("{%s}" % lalgo_name))
+    # Now make sure a bind works
+    assert(_test_bind(inst, 'Secret123'))
+    # Bind with a wrong shorter password, should fail
+    assert(not _test_bind(inst, 'Wrong'))
+    # Bind with a wrong longer password, should fail
+    assert(not _test_bind(inst, 'This is even more wrong'))
+    # Bind with a password that has the algo in the name
+    assert(not _test_bind(inst, '{%s}SomeValues....' % algo_name))
+    # Bind with a wrong exact length password.
+    assert(not _test_bind(inst, 'Alsowrong'))
+    # Bind with a subset password, should fail
+    assert(not _test_bind(inst, 'Secret'))
+    if algo_name != 'CRYPT':
+        # Bind with a subset password that is 1 char shorter, to detect off by 1 in clear
+        assert(not _test_bind(inst, 'Secret12'))
+        # Bind with a superset password, should fail
+        assert(not _test_bind(inst, 'Secret123456'))
+    # Delete the user
+    inst.delete_s(USER_DN)
+    # done!
+
+def test_397(topology):
+    """
+    Assert that all of our password algorithms correctly PASS and FAIL varying
+    password conditions.
+
+    """
+    if DEBUGGING:
+        # Add debugging steps(if any)...
+        log.info("ATTACH NOW")
+        time.sleep(30)
+
+    # Merge this to the password suite in the future
+
+    for algo in ('PBKDF2_SHA256', ):
+        for i in range(0, 10):
+            _test_algo(topology.standalone, algo)
+
+    log.info('Test PASSED')
+
+
+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/ldif/template-dse.ldif.in b/ldap/ldif/template-dse.ldif.in
index 8258b70a4..7e519f2a6 100644
--- a/ldap/ldif/template-dse.ldif.in
+++ b/ldap/ldif/template-dse.ldif.in
@@ -211,6 +211,15 @@ nsslapd-plugininitfunc: ns_mta_md5_pwd_storage_scheme_init
 nsslapd-plugintype: pwdstoragescheme
 nsslapd-pluginenabled: on
 
+dn: cn=PBKDF2_SHA256,cn=Password Storage Schemes,cn=plugins,cn=config
+objectclass: top
+objectclass: nsSlapdPlugin
+cn: PBKDF2_SHA256
+nsslapd-pluginpath: libpwdstorage-plugin
+nsslapd-plugininitfunc: pbkdf2_sha256_pwd_storage_scheme_init
+nsslapd-plugintype: pwdstoragescheme
+nsslapd-pluginenabled: on
+
 dn: cn=AES,cn=Password Storage Schemes,cn=plugins,cn=config
 objectclass: top
 objectclass: nsSlapdPlugin
diff --git a/ldap/servers/plugins/pwdstorage/pbkdf2_pwd.c b/ldap/servers/plugins/pwdstorage/pbkdf2_pwd.c
new file mode 100644
index 000000000..1b3e5556d
--- /dev/null
+++ b/ldap/servers/plugins/pwdstorage/pbkdf2_pwd.c
@@ -0,0 +1,216 @@
+/** 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 **/
+
+#ifdef HAVE_CONFIG_H
+#  include <config.h>
+#endif
+
+/*
+ * slapd hashed password routines
+ *
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+
+#include "pwdstorage.h"
+
+#include <pk11pub.h>
+
+/* Need this for htonl and ntohl */
+#include <arpa/inet.h>
+
+/* WB Nist recommend 128 bits (16 bytes) in 2016, may as well go for more to future proof. */
+/* !!!!!!!! NEVER CHANGE THESE VALUES !!!!!!!! */
+#define PBKDF2_SALT_LENGTH 64
+#define PBKDF2_ITERATIONS_LENGTH 4
+/* If this isn't 256 NSS explodes without setting an error code .... */
+#define PBKDF2_HASH_LENGTH 256
+#define PBKDF2_TOTAL_LENGTH (PBKDF2_ITERATIONS_LENGTH + PBKDF2_SALT_LENGTH + PBKDF2_HASH_LENGTH)
+/* ======== END NEVER CHANGE THESE VALUES ==== */
+
+/* 
+ * WB - It's important we keep this private, and we increment it over time.
+ * Administrators are likely to forget to update it, or they will set it too low.
+ * We therfore keep it private, so we can increase it as our security recomendations
+ * change and improve.
+ *
+ * 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
+
+static const char *schemeName = PBKDF2_SHA256_SCHEME_NAME;
+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};
+
+void
+pbkdf2_sha256_extract(char *hash_in, SECItem *salt, PRUint32 *iterations)
+{
+    /*
+     * This will take the input of hash_in (generated from pbkdf2_sha256_hash) and
+     * populate the hash (output of nss pkbdf2), salt, and iterations.
+     * Enough space should be avaliable in these for the values to fit into.
+     */
+
+    memcpy(iterations, hash_in, PBKDF2_ITERATIONS_LENGTH);
+    /* We use ntohl on this value to make sure it's correct endianess. */
+    *iterations = ntohl(*iterations);
+
+    /* warning: pointer targets in assignment differ in signedness [-Wpointer-sign] */
+    salt->data = (unsigned char *)(hash_in + PBKDF2_ITERATIONS_LENGTH);
+    salt->len = PBKDF2_SALT_LENGTH;
+}
+
+SECStatus
+pbkdf2_sha256_hash(char *hash_out, size_t hash_out_len, SECItem *pwd, SECItem *salt, PRUint32 iterations)
+{
+    SECItem *result = NULL;
+    SECAlgorithmID *algid = NULL;
+    PK11SlotInfo *slot = NULL;
+    PK11SymKey *symkey = NULL;
+
+    /* We assume that NSS is already started. */
+    algid = PK11_CreatePBEV2AlgorithmID(SEC_OID_PKCS5_PBKDF2, SEC_OID_HMAC_SHA256, SEC_OID_HMAC_SHA256, hash_out_len, iterations, salt);
+
+    if (algid != NULL) {
+        /* Gets the best slot that provides SHA256HMAC and PBKDF2 (may not be the default!) */
+        slot = PK11_GetBestSlotMultiple(mechanism_array, 2, NULL);
+        if (slot != NULL) {
+            symkey = PK11_PBEKeyGen(slot, algid, pwd, PR_FALSE, NULL);
+            PK11_FreeSlot(slot);
+            if (symkey == NULL) {
+                /* We try to get the Error here but NSS has two or more error interfaces, and sometimes it uses none of them. */
+                PRInt32 status = PORT_GetError();
+                slapi_log_err(SLAPI_LOG_ERR, (char *)schemeName, "Unable to retrieve symkey from NSS. Error code might be %d ???\n", status);
+                slapi_log_err(SLAPI_LOG_ERR, (char *)schemeName, "The most likely cause is your system has nss 3.21 or lower. PBKDF2 requires nss 3.22 or higher.\n");
+                return SECFailure;
+            }
+        } else {
+            slapi_log_err(SLAPI_LOG_ERR, (char *)schemeName, "Unable to retrieve slot from NSS.\n");
+            return SECFailure;
+        }
+        SECOID_DestroyAlgorithmID(algid, PR_TRUE);
+    } else {
+        /* Uh oh! */
+        slapi_log_err(SLAPI_LOG_ERR, (char *)schemeName, "Unable to generate algorithm ID.\n");
+        return SECFailure;
+    }
+
+    if (PK11_ExtractKeyValue(symkey) == SECSuccess) {
+        result = PK11_GetKeyData(symkey);
+        if (result != NULL && result->len <= hash_out_len) {
+            memcpy(hash_out, result->data, result->len);
+            PK11_FreeSymKey(symkey);
+        } else {
+            PK11_FreeSymKey(symkey);
+            slapi_log_err(SLAPI_LOG_ERR, (char *)schemeName, "Unable to retrieve (get) hash output.\n");
+            return SECFailure;
+        }
+    } else {
+        slapi_log_err(SLAPI_LOG_ERR, (char *)schemeName, "Unable to extract hash output.\n");
+        return SECFailure;
+    }
+
+    return SECSuccess;
+}
+
+char *
+pbkdf2_sha256_pw_enc(const char *pwd)
+{
+    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;
+    char salt[PBKDF2_SALT_LENGTH];
+
+    memset(hash, 0, PBKDF2_TOTAL_LENGTH);
+    memset(salt, 0, PBKDF2_SALT_LENGTH);
+    saltItem.data = (unsigned char *)salt;
+    saltItem.len = PBKDF2_SALT_LENGTH;
+    passItem.data = (unsigned char *)pwd;
+    passItem.len = strlen(pwd);
+
+    /* make a new random salt */
+    slapi_rand_array(salt, PBKDF2_SALT_LENGTH);
+
+    /*
+     * Preload the salt and iterations to the output.
+     * memcpy the iterations to the hash_out
+     * We use ntohl on this value to make sure it's correct endianess.
+     */
+    iterations = htonl(iterations);
+    memcpy(hash, &iterations, PBKDF2_ITERATIONS_LENGTH);
+    /* memcpy the salt to the hash_out */
+    memcpy(hash + PBKDF2_ITERATIONS_LENGTH, saltItem.data, PBKDF2_SALT_LENGTH);
+
+    /*
+     *                      This offset is to make the hash function put the values
+     *                      In the correct part of the memory.
+     */
+    if ( pbkdf2_sha256_hash(hash + PBKDF2_ITERATIONS_LENGTH + PBKDF2_SALT_LENGTH, PBKDF2_HASH_LENGTH, &passItem, &saltItem, PBKDF2_ITERATIONS) != SECSuccess ) {
+        slapi_log_err(SLAPI_LOG_ERR, (char *)schemeName, "Could not generate pbkdf2_sha256_hash!\n");
+        return NULL;
+    }
+
+    sprintf(enc, "%c%s%c", PWD_HASH_PREFIX_START, schemeName, PWD_HASH_PREFIX_END);
+    (void)PL_Base64Encode( hash, PBKDF2_TOTAL_LENGTH, enc + 2 + schemeNameLength);
+    PR_ASSERT(enc[encsize - 1] == '\0');
+
+    slapi_log_err(SLAPI_LOG_PLUGIN, (char *)schemeName, "Generated hash %s\n", enc);
+
+    return enc;
+}
+
+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 ];
+    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);
+
+    /* Decode the DBpwd to bytes from b64 */
+    if ( PL_Base64Decode( dbpwd, dbpwd_len, dbhash) == NULL ) {
+        slapi_log_err(SLAPI_LOG_ERR, (char *)schemeName, "Unable to base64 decode dbpwd value\n");
+        return result;
+    }
+    /* extract the fields */
+    pbkdf2_sha256_extract(dbhash, &saltItem, &iterations);
+
+    /* Now send the userpw to the hash function, with the salt + iter. */
+    if ( pbkdf2_sha256_hash(userhash, PBKDF2_HASH_LENGTH, &passItem, &saltItem, iterations) != SECSuccess ) {
+        slapi_log_err(SLAPI_LOG_ERR, (char *)schemeName, "Unable to hash userpwd value\n");
+        return result;
+    }
+    /* Now compare the result of pbkdf2_sha256_hash. */
+    result = memcmp(userhash, hash, PBKDF2_HASH_LENGTH);
+
+    return result;
+}
+
+
diff --git a/ldap/servers/plugins/pwdstorage/pwd_init.c b/ldap/servers/plugins/pwdstorage/pwd_init.c
index 5efd9ca1c..d66bb98cc 100644
--- a/ldap/servers/plugins/pwdstorage/pwd_init.c
+++ b/ldap/servers/plugins/pwdstorage/pwd_init.c
@@ -44,6 +44,8 @@ static Slapi_PluginDesc md5_pdesc = { "md5-password-storage-scheme", VENDOR, DS_
 
 static Slapi_PluginDesc smd5_pdesc = { "smd5-password-storage-scheme", VENDOR, DS_PACKAGE_VERSION, "Salted MD5 hash algorithm (SMD5)" };
 
+static Slapi_PluginDesc pbkdf2_sha256_pdesc = { "pbkdf2-sha256-password-storage-scheme", VENDOR, DS_PACKAGE_VERSION, "Salted PBKDF2 SHA256 hash algorithm (PBKDF2_SHA256)" };
+
 static char *plugin_name = "NSPwdStoragePlugin";
 
 int
@@ -336,3 +338,21 @@ smd5_pwd_storage_scheme_init( Slapi_PBlock *pb )
 	slapi_log_err(SLAPI_LOG_PLUGIN, plugin_name, "<= smd5_pwd_storage_scheme_init %d\n\n", rc );
 	return( rc );
 }
+
+int
+pbkdf2_sha256_pwd_storage_scheme_init(Slapi_PBlock *pb)
+{
+    int rc;
+
+    slapi_log_error(SLAPI_LOG_PLUGIN, plugin_name, "=> pbkdf2_sha256_pwd_storage_scheme_init\n");
+
+    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_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);
+
+    slapi_log_error(SLAPI_LOG_PLUGIN, plugin_name, "<= pbkdf2_sha256_pwd_storage_scheme_init %d\n", rc);
+    return rc;
+}
+
diff --git a/ldap/servers/plugins/pwdstorage/pwdstorage.h b/ldap/servers/plugins/pwdstorage/pwdstorage.h
index 1e085c74f..27e708d9c 100644
--- a/ldap/servers/plugins/pwdstorage/pwdstorage.h
+++ b/ldap/servers/plugins/pwdstorage/pwdstorage.h
@@ -54,6 +54,9 @@
 #define MD5_NAME_LEN 3
 #define SALTED_MD5_SCHEME_NAME "SMD5"
 #define SALTED_MD5_NAME_LEN 4
+#define PBKDF2_SHA256_SCHEME_NAME "PBKDF2_SHA256"
+#define PBKDF2_SHA256_NAME_LEN 13
+
 
 SECStatus sha_salted_hash(char *hash_out, const char *pwd, struct berval *salt, unsigned int secOID);
 int sha_pw_cmp( const char *userpwd, const char *dbpwd, unsigned int shaLen );
@@ -82,6 +85,10 @@ char *md5_pw_enc( const char *pwd );
 int smd5_pw_cmp( const char *userpwd, const char *dbpwd );
 char *smd5_pw_enc( const char *pwd );
 
+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);
+
 /* Utility functions */
 PRUint32 pwdstorage_base64_decode_len(const char *encval, PRUint32 enclen);
 
diff --git a/ldap/servers/slapd/pw.c b/ldap/servers/slapd/pw.c
index 2506a2b91..5af04d216 100644
--- a/ldap/servers/slapd/pw.c
+++ b/ldap/servers/slapd/pw.c
@@ -262,6 +262,13 @@ pw_val2scheme( char *val, char **valpwdp, int first_is_default )
 	if (NULL == val) {
 		return( NULL );
 	}
+
+    /*
+     * Future implementors of new password mechanisms may find that this function
+     * is causing them trouble. If your hash ends up as {CLEAR}{NEWMECH}.... it
+     * because NEWMECH > PWD_MAX_NAME_LEN. Update pw.h!
+     */
+
 	if ( *val != PWD_HASH_PREFIX_START ||
 	    ( end = strchr( val, PWD_HASH_PREFIX_END )) == NULL ||
 	    ( namelen = end - val - 1 ) > PWD_MAX_NAME_LEN ) {
diff --git a/ldap/servers/slapd/pw.h b/ldap/servers/slapd/pw.h
index 58e744125..8e07582f3 100644
--- a/ldap/servers/slapd/pw.h
+++ b/ldap/servers/slapd/pw.h
@@ -19,7 +19,8 @@
 #ifndef _SLAPD_PW_H_
 #define _SLAPD_PW_H_
 
-#define PWD_MAX_NAME_LEN    10
+// Updated to the 13 for PBKDF2_SHA256
+#define PWD_MAX_NAME_LEN    13
 
 #define PWD_HASH_PREFIX_START	'{'
 #define PWD_HASH_PREFIX_END	'}'
