diff --git a/SOURCES/0087-Ticket-49509-Indexing-of-internationalized-matching-.patch b/SOURCES/0087-Ticket-49509-Indexing-of-internationalized-matching-.patch
new file mode 100644
index 0000000..e75f258
--- /dev/null
+++ b/SOURCES/0087-Ticket-49509-Indexing-of-internationalized-matching-.patch
@@ -0,0 +1,263 @@
+From 41a037c8310d204d21e9c3161d2015dd5177cff6 Mon Sep 17 00:00:00 2001
+From: Thierry Bordaz <tbordaz@redhat.com>
+Date: Tue, 19 Dec 2017 11:53:13 +0100
+Subject: [PATCH] Ticket 49509 - Indexing of internationalized matching rules
+ is failing
+
+Bug Description:
+	Indexing of the internationalized matching rules tests if a
+	matching rule indexer handle or not a given OID.
+	A side effect of https://pagure.io/389-ds-base/issue/49097 is that
+	the returned indexing callbacks are lost.
+	Indeed, the indexing callbacks (and potentially others fields) were
+	stored in the temporary pblock that was memcpy to the provided
+	pblock in case of success
+
+Fix Description:
+	The fix basically restores the previous behavior but do not
+	memcpy pblock. It read/store the pblock fields that are
+	inputs/outputs of slapi_mr_indexer_create.
+
+https://pagure.io/389-ds-base/issue/49509
+
+Reviewed by: Ludwig Krispenz
+
+Platforms tested: F23
+
+Flag Day: no
+
+Doc impact: no
+---
+ ldap/servers/slapd/plugin_mr.c | 202 ++++++++++++++++++++++++++++++-----------
+ 1 file changed, 148 insertions(+), 54 deletions(-)
+
+diff --git a/ldap/servers/slapd/plugin_mr.c b/ldap/servers/slapd/plugin_mr.c
+index d216d12b9..b3cd4adf0 100644
+--- a/ldap/servers/slapd/plugin_mr.c
++++ b/ldap/servers/slapd/plugin_mr.c
+@@ -145,6 +145,82 @@ plugin_mr_bind (char* oid, struct slapdplugin* plugin)
+ 	slapi_log_err(SLAPI_LOG_FILTER, "plugin_mr_bind", "<=\n");
+ }
+ 
++void
++mr_indexer_init_pb(Slapi_PBlock* src_pb, Slapi_PBlock* dst_pb)
++{
++    char* oid;
++    char *type;
++    uint32_t usage;
++    void *object;
++    IFP destroyFn;
++    IFP indexFn, indexSvFn;
++    
++    /* matching rule plugin arguments */
++    slapi_pblock_get(src_pb, SLAPI_PLUGIN_MR_OID,             &oid);
++    slapi_pblock_get(src_pb, SLAPI_PLUGIN_MR_TYPE,            &type);
++    slapi_pblock_get(src_pb, SLAPI_PLUGIN_MR_USAGE,           &usage);
++    
++    slapi_pblock_set(dst_pb, SLAPI_PLUGIN_MR_OID,             oid);
++    slapi_pblock_set(dst_pb, SLAPI_PLUGIN_MR_TYPE,            type);
++    slapi_pblock_set(dst_pb, SLAPI_PLUGIN_MR_USAGE,           &usage);
++    
++    /* matching rule plugin functions */
++    slapi_pblock_get(src_pb, SLAPI_PLUGIN_MR_INDEX_FN,          &indexFn);
++    slapi_pblock_get(src_pb, SLAPI_PLUGIN_MR_INDEX_SV_FN,       &indexSvFn);
++    
++    slapi_pblock_set(dst_pb, SLAPI_PLUGIN_MR_INDEX_FN,          indexFn);
++    slapi_pblock_set(dst_pb, SLAPI_PLUGIN_MR_INDEX_SV_FN,       indexSvFn);
++
++    /* common */
++    slapi_pblock_get(src_pb, SLAPI_PLUGIN_OBJECT,        &object);
++    slapi_pblock_get(src_pb, SLAPI_PLUGIN_DESTROY_FN,    &destroyFn);
++
++    slapi_pblock_set(dst_pb, SLAPI_PLUGIN_OBJECT,        object);
++    slapi_pblock_set(dst_pb, SLAPI_PLUGIN_DESTROY_FN,    destroyFn);
++
++
++}
++
++/*
++ *  Retrieves the matching rule plugin able to index/sort the provided OID/type
++ * 
++ *  The Matching rules able to index/sort a given OID are stored in a global list: global_mr_oids
++ *
++ *  The retrieval is done in 3 phases:
++ *      - It first searches (in global_mr_oids) for the already bound OID->MR
++ *      - Else, look first in old style MR plugin
++ *        for each registered 'syntax' and 'matchingrule' plugins having a
++ *        SLAPI_PLUGIN_MR_INDEXER_CREATE_FN, it binds (plugin_mr_bind) the first
++ *        plugin that support the OID
++ *      - Else, look in new style MR plugin
++ *        for each registered 'syntax' and 'matchingrule' plugins, it binds (plugin_mr_bind) the first
++ *        plugin that contains OID in its plg_mr_names
++ *
++ * Inputs:
++ *  SLAPI_PLUGIN_MR_OID
++ *      should contain the OID of the matching rule that you want used for indexing or sorting.
++ *  SLAPI_PLUGIN_MR_TYPE
++ *      should contain the attribute type that you want used for indexing or sorting.
++ *  SLAPI_PLUGIN_MR_USAGE
++ *      should specify if the indexer will be used for indexing (SLAPI_PLUGIN_MR_USAGE_INDEX)
++ *      or for sorting (SLAPI_PLUGIN_MR_USAGE_SORT)
++ *
++ *
++ * Output:
++ *
++ *  SLAPI_PLUGIN_MR_OID
++ *      contain the OFFICIAL OID of the matching rule that you want used for indexing or sorting.
++ *  SLAPI_PLUGIN_MR_INDEX_FN
++ *      specifies the indexer function responsible for indexing or sorting of struct berval **
++ *  SLAPI_PLUGIN_MR_INDEX_SV_FN
++ *      specifies the indexer function responsible for indexing or sorting of Slapi_Value **
++ *  SLAPI_PLUGIN_OBJECT
++ *      contain any information that you want passed to the indexer function.
++ *  SLAPI_PLUGIN_DESTROY_FN
++ *      specifies the function responsible for freeing any memory allocated by this indexer factory function.
++ *      For example, memory allocated for a structure that you pass to the indexer function using SLAPI_PLUGIN_OBJECT.
++ *
++ */
+ int /* an LDAP error code, hopefully LDAP_SUCCESS */
+ slapi_mr_indexer_create (Slapi_PBlock* opb)
+ {
+@@ -152,60 +228,73 @@ slapi_mr_indexer_create (Slapi_PBlock* opb)
+     char* oid;
+     if (!(rc = slapi_pblock_get (opb, SLAPI_PLUGIN_MR_OID, &oid)))
+     {
+-		IFP createFn = NULL;
+-		struct slapdplugin* mrp = plugin_mr_find_registered (oid);
+-		if (mrp != NULL)
+-		{
+-		    if (!(rc = slapi_pblock_set (opb, SLAPI_PLUGIN, mrp)) &&
+-				!(rc = slapi_pblock_get (opb, SLAPI_PLUGIN_MR_INDEXER_CREATE_FN, &createFn)) &&
+-				createFn != NULL)
+-			{
+-				rc = createFn (opb);
+-		    }
+-		}
+-		else
+-		{
+-		    /* call each plugin, until one is able to handle this request. */
+-		    rc = LDAP_UNAVAILABLE_CRITICAL_EXTENSION;
+-		    for (mrp = get_plugin_list(PLUGIN_LIST_MATCHINGRULE); mrp != NULL; mrp = mrp->plg_next)
+-		    {
+-				IFP indexFn = NULL;
+-				IFP indexSvFn = NULL;
+-				Slapi_PBlock pb;
+-				memcpy (&pb, opb, sizeof(Slapi_PBlock));
+-				slapi_pblock_set(&pb, SLAPI_PLUGIN, mrp);
+-				if (slapi_pblock_get(&pb, SLAPI_PLUGIN_MR_INDEXER_CREATE_FN, &createFn)) {
+-					/* plugin not a matchingrule type */
+-					continue;
+-				}
+-				if (createFn && !createFn(&pb)) {
+-					slapi_pblock_get(&pb, SLAPI_PLUGIN_MR_INDEX_FN, &indexFn);
+-					slapi_pblock_get(&pb, SLAPI_PLUGIN_MR_INDEX_SV_FN, &indexSvFn);
+-					if (indexFn || indexSvFn) {
+-						/* Success: this plugin can handle it. */
+-						memcpy(opb, &pb, sizeof (Slapi_PBlock));
+-						plugin_mr_bind(oid, mrp); /* for future reference */
+-						rc = 0; /* success */
+-						break;
+-					}
+-
+-				}
+-		    }
+-			if (rc != 0) {
+-				/* look for a new syntax-style mr plugin */
+-				struct slapdplugin *pi = plugin_mr_find(oid);
+-				if (pi) {
+-					Slapi_PBlock pb;
+-					memcpy (&pb, opb, sizeof(Slapi_PBlock));
+-					slapi_pblock_set(&pb, SLAPI_PLUGIN, pi);
+-					rc = default_mr_indexer_create(&pb);
+-					if (!rc) {
+-						memcpy (opb, &pb, sizeof(Slapi_PBlock));
+-						plugin_mr_bind (oid, pi); /* for future reference */
+-					}
+-				}
+-			}
+-		}
++        IFP createFn = NULL;
++        struct slapdplugin* mrp = plugin_mr_find_registered(oid);
++        if (mrp != NULL) {
++            /* Great the matching OID -> MR plugin was already found, just reuse it */
++            if (!(rc = slapi_pblock_set(opb, SLAPI_PLUGIN, mrp)) &&
++                    !(rc = slapi_pblock_get(opb, SLAPI_PLUGIN_MR_INDEXER_CREATE_FN, &createFn)) &&
++                    createFn != NULL) {
++                rc = createFn(opb);
++            }
++        } else {
++            /* We need to find in the MR plugins list, the MR plugin that will be able to handle OID
++             *
++             * It can be "old style" MR plugin (i.e. collation) that define indexer
++             *
++             * It can be "now style" MR plugin that contain OID string in 'plg_mr_names'
++             * (ie. ces, cis, bin...) where plg_mr_names is defined in 'mr_plugin_table' in each file
++             * ces.c, cis.c...
++             * New style MR plugin have NULL indexer create function but rather use a default indexer
++             */
++
++            /* Look for a old syntax-style mr plugin
++             * call each plugin, until one is able to handle this request.
++             */
++            rc = LDAP_UNAVAILABLE_CRITICAL_EXTENSION;
++
++            for (mrp = get_plugin_list(PLUGIN_LIST_MATCHINGRULE); mrp != NULL; mrp = mrp->plg_next) {
++
++                Slapi_PBlock *pb = slapi_pblock_new();
++                mr_indexer_init_pb(opb, pb);
++                slapi_pblock_set(pb, SLAPI_PLUGIN, mrp);
++                /* This is associated with the pb_plugin struct, so it comes with mrp */
++                if (slapi_pblock_get(pb, SLAPI_PLUGIN_MR_INDEXER_CREATE_FN, &createFn)) {
++                    /* plugin not a matchingrule type */
++                    slapi_pblock_destroy(pb);
++                    continue;
++                }
++
++                if (createFn && !createFn(pb)) {
++                    IFP indexFn = NULL;
++                    IFP indexSvFn = NULL;
++                    /* These however, are in the pblock direct, so we need to copy them. */
++                    slapi_pblock_get(pb, SLAPI_PLUGIN_MR_INDEX_FN, &indexFn);
++                    slapi_pblock_get(pb, SLAPI_PLUGIN_MR_INDEX_SV_FN, &indexSvFn);
++                    if (indexFn || indexSvFn) {
++                        /* Success: this plugin can handle it. */
++                        mr_indexer_init_pb(pb, opb);
++                        plugin_mr_bind(oid, mrp); /* for future reference */
++                        rc = 0; /* success */
++                        slapi_pblock_destroy(pb);
++                        break;
++                    }
++                }
++                slapi_pblock_destroy(pb);
++            }
++            if (rc != 0) {
++                /* look for a new syntax-style mr plugin */
++                struct slapdplugin *pi = plugin_mr_find(oid);
++                if (pi) {
++                    slapi_pblock_set(opb, SLAPI_PLUGIN, pi);
++                    rc = default_mr_indexer_create(opb);
++                    if (!rc) {
++                        plugin_mr_bind(oid, pi); /* for future reference */
++                    }
++                    slapi_pblock_set(opb, SLAPI_PLUGIN, NULL);
++                }
++            }
++        }
+     }
+     return rc;
+ }
+@@ -683,6 +772,11 @@ default_mr_indexer_create(Slapi_PBlock* pb)
+ 	slapi_pblock_set(pb, SLAPI_PLUGIN_MR_INDEX_FN, mr_wrap_mr_index_fn);
+ 	slapi_pblock_set(pb, SLAPI_PLUGIN_MR_INDEX_SV_FN, mr_wrap_mr_index_sv_fn);
+ 	slapi_pblock_set(pb, SLAPI_PLUGIN_DESTROY_FN, default_mr_indexer_destroy);
++
++        /* Note the two following setting are in the slapdplugin struct SLAPI_PLUGIN
++         * so they are not really output of the function but will just
++         * be stored in the bound (OID <--> plugin) list (plugin_mr_find_registered/plugin_mr_bind)
++         */
+ 	slapi_pblock_set(pb, SLAPI_PLUGIN_MR_INDEXER_CREATE_FN, default_mr_indexer_create);
+ 	slapi_pblock_set(pb, SLAPI_PLUGIN_MR_FILTER_CREATE_FN, default_mr_filter_create);
+ 	rc = 0;
+-- 
+2.13.6
+
diff --git a/SOURCES/0088-Ticket-bz1525628-1.3.6-backport-invalid-password-mig.patch b/SOURCES/0088-Ticket-bz1525628-1.3.6-backport-invalid-password-mig.patch
new file mode 100644
index 0000000..b5b280c
--- /dev/null
+++ b/SOURCES/0088-Ticket-bz1525628-1.3.6-backport-invalid-password-mig.patch
@@ -0,0 +1,292 @@
+From 4219c54d9706f5597e8186d4f983b30587c2762e Mon Sep 17 00:00:00 2001
+From: Mark Reynolds <mreynolds@redhat.com>
+Date: Tue, 13 Feb 2018 10:32:06 -0500
+Subject: [PATCH] Ticket bz1525628 1.3.6 backport - invalid password migration 
+ causes unauth bind
+
+Bug Description:  Slapi_ct_memcmp expects both inputs to be
+at LEAST size n. If they are not, we only compared UP to n.
+
+Invalid migrations of passwords (IE {CRYPT}XX) would create
+a pw which is just salt and no hash. ct_memcmp would then
+only verify the salt bits and would allow the authentication.
+
+This relies on an administrative mistake both of allowing
+password migration (nsslapd-allow-hashed-passwords) and then
+subsequently migrating an INVALID password to the server.
+
+Fix Description:  slapi_ct_memcmp now access n1, n2 size
+and will FAIL if they are not the same, but will still compare
+n bytes, where n is the "longest" memory, to the first byte
+of the other to prevent length disclosure of the shorter
+value (generally the mis-migrated password)
+
+https://bugzilla.redhat.com/show_bug.cgi?id=1525628
+
+Author: wibrown
+---
+ ldap/servers/plugins/pwdstorage/clear_pwd.c |  4 +-
+ ldap/servers/plugins/pwdstorage/crypt_pwd.c |  4 +-
+ ldap/servers/plugins/pwdstorage/md5_pwd.c   | 14 +++----
+ ldap/servers/plugins/pwdstorage/sha_pwd.c   | 18 +++++++--
+ ldap/servers/plugins/pwdstorage/smd5_pwd.c  | 60 +++++++++++++++--------------
+ ldap/servers/slapd/ch_malloc.c              | 37 +++++++++++++++---
+ ldap/servers/slapd/slapi-plugin.h           |  2 +-
+ 7 files changed, 88 insertions(+), 51 deletions(-)
+
+diff --git a/ldap/servers/plugins/pwdstorage/clear_pwd.c b/ldap/servers/plugins/pwdstorage/clear_pwd.c
+index b9b362d34..050e60dd7 100644
+--- a/ldap/servers/plugins/pwdstorage/clear_pwd.c
++++ b/ldap/servers/plugins/pwdstorage/clear_pwd.c
+@@ -39,7 +39,7 @@ clear_pw_cmp( const char *userpwd, const char *dbpwd )
+          * However, even if the first part of userpw matches dbpwd, but len !=, we
+          * have already failed anyawy. This prevents substring matching.
+          */
+-        if (slapi_ct_memcmp(userpwd, dbpwd, len_dbp) != 0) {
++        if (slapi_ct_memcmp(userpwd, dbpwd, len_user, len_dbp) != 0) {
+             result = 1;
+         }
+     } else {
+@@ -51,7 +51,7 @@ clear_pw_cmp( const char *userpwd, const char *dbpwd )
+          * dbpwd to itself. We have already got result == 1 if we are here, so we are
+          * just trying to take up time!
+          */
+-        if (slapi_ct_memcmp(dbpwd, dbpwd, len_dbp)) {
++        if (slapi_ct_memcmp(dbpwd, dbpwd, len_dbp, len_dbp)) {
+             /* Do nothing, we have the if to fix a coverity check. */
+         }
+     }
+diff --git a/ldap/servers/plugins/pwdstorage/crypt_pwd.c b/ldap/servers/plugins/pwdstorage/crypt_pwd.c
+index dfd5af94b..ff8eabb07 100644
+--- a/ldap/servers/plugins/pwdstorage/crypt_pwd.c
++++ b/ldap/servers/plugins/pwdstorage/crypt_pwd.c
+@@ -56,13 +56,13 @@ crypt_close(Slapi_PBlock *pb __attribute__((unused)))
+ int
+ crypt_pw_cmp( const char *userpwd, const char *dbpwd )
+ {
+-    int rc;
++    int32_t rc;
+     char *cp;
+     PR_Lock(cryptlock);
+     /* we use salt (first 2 chars) of encoded password in call to crypt() */
+     cp = crypt( userpwd, dbpwd );
+     if (cp) {
+-       rc= slapi_ct_memcmp( dbpwd, cp, strlen(dbpwd));
++       rc= slapi_ct_memcmp(dbpwd, cp, strlen(dbpwd), strlen(cp));
+     } else {
+        rc = -1;
+     }
+diff --git a/ldap/servers/plugins/pwdstorage/md5_pwd.c b/ldap/servers/plugins/pwdstorage/md5_pwd.c
+index b27994667..88c11688b 100644
+--- a/ldap/servers/plugins/pwdstorage/md5_pwd.c
++++ b/ldap/servers/plugins/pwdstorage/md5_pwd.c
+@@ -30,12 +30,12 @@
+ int
+ md5_pw_cmp( const char *userpwd, const char *dbpwd )
+ {
+-   int rc=-1;
+-   char * bver;
+-   PK11Context *ctx=NULL;
++   int32_t rc=-1;
++   char *bver;
++   PK11Context *ctx = NULL;
+    unsigned int outLen;
+    unsigned char hash_out[MD5_HASH_LEN];
+-   unsigned char b2a_out[MD5_HASH_LEN*2]; /* conservative */
++   unsigned char b2a_out[MD5_HASH_LEN * 2]; /* conservative */
+    SECItem binary_item;
+ 
+    ctx = PK11_CreateDigestContext(SEC_OID_MD5);
+@@ -57,10 +57,10 @@ md5_pw_cmp( const char *userpwd, const char *dbpwd )
+    bver = NSSBase64_EncodeItem(NULL, (char *)b2a_out, sizeof b2a_out, &binary_item);
+    /* bver points to b2a_out upon success */
+    if (bver) {
+-	   rc = slapi_ct_memcmp(bver,dbpwd, strlen(dbpwd));
++       rc = slapi_ct_memcmp(bver,dbpwd, strlen(dbpwd), strlen(bver));
+    } else {
+-	   slapi_log_err(SLAPI_LOG_PLUGIN, MD5_SUBSYSTEM_NAME,
+-					   "Could not base64 encode hashed value for password compare");
++       slapi_log_err(SLAPI_LOG_PLUGIN, MD5_SUBSYSTEM_NAME,
++                     "Could not base64 encode hashed value for password compare");
+    }
+ loser:
+    return rc;
+diff --git a/ldap/servers/plugins/pwdstorage/sha_pwd.c b/ldap/servers/plugins/pwdstorage/sha_pwd.c
+index 5f41c5b93..c9db8964a 100644
+--- a/ldap/servers/plugins/pwdstorage/sha_pwd.c
++++ b/ldap/servers/plugins/pwdstorage/sha_pwd.c
+@@ -49,7 +49,7 @@ sha_pw_cmp (const char *userpwd, const char *dbpwd, unsigned int shaLen )
+     char userhash[MAX_SHA_HASH_SIZE];
+     char quick_dbhash[MAX_SHA_HASH_SIZE + SHA_SALT_LENGTH + 3];
+     char *dbhash = quick_dbhash;
+-    struct berval salt;
++    struct berval salt = {0};
+     PRUint32 hash_len;
+     unsigned int secOID;
+     char *schemeName;
+@@ -120,10 +120,20 @@ sha_pw_cmp (const char *userpwd, const char *dbpwd, unsigned int shaLen )
+     }
+ 
+     /* the proof is in the comparison... */
+-    if ( hash_len >= shaLen ) {
+-        result = slapi_ct_memcmp( userhash, dbhash, shaLen );
++    if (hash_len >= shaLen) {
++        /*
++         * This say "if the hash has a salt IE >, OR if they are equal, check the hash component ONLY.
++         * This is why we repeat shaLen twice, even though it seems odd. If you have a dbhast of ssha
++         * it's len is 28, and the userpw is 20, but 0 - 20 is the sha, and 21-28 is the salt, which
++         * has already been processed into userhash.
++         * The case where dbpwd is truncated is handled above in "invalid base64" arm.
++         */
++        result = slapi_ct_memcmp(userhash, dbhash, shaLen, shaLen);
+     } else {
+-        result = slapi_ct_memcmp( userhash, dbhash + OLD_SALT_LENGTH, hash_len - OLD_SALT_LENGTH );
++        /* This case is for if the salt is at the START, which only applies to DS40B1 case.
++         * May never be a valid check...
++         */
++        result = slapi_ct_memcmp(userhash, dbhash + OLD_SALT_LENGTH, shaLen, hash_len - OLD_SALT_LENGTH);
+     }
+ 
+ loser:
+diff --git a/ldap/servers/plugins/pwdstorage/smd5_pwd.c b/ldap/servers/plugins/pwdstorage/smd5_pwd.c
+index 2e9d195ea..f6b4bb4a0 100644
+--- a/ldap/servers/plugins/pwdstorage/smd5_pwd.c
++++ b/ldap/servers/plugins/pwdstorage/smd5_pwd.c
+@@ -52,35 +52,37 @@ smd5_pw_cmp( const char *userpwd, const char *dbpwd )
+    /*
+     * Decode hash stored in database.
+     */
+-   hash_len = pwdstorage_base64_decode_len(dbpwd, 0);
+-   if ( hash_len >= sizeof(quick_dbhash) ) { /* get more space: */
+-      dbhash = (char*) slapi_ch_calloc( hash_len + 1, sizeof(char) );
+-      if ( dbhash == NULL ) goto loser;
+-   } else {
+-      memset( quick_dbhash, 0, sizeof(quick_dbhash) );
+-   }
+-
+-   hashresult = PL_Base64Decode( dbpwd, 0, dbhash );
+-   if (NULL == hashresult) {
+-      slapi_log_err(SLAPI_LOG_PLUGIN, SALTED_MD5_SUBSYSTEM_NAME,
+-            "smd5_pw_cmp: userPassword \"%s\" is the wrong length "
+-            "or is not properly encoded BASE64\n", dbpwd );
+-      goto loser;
+-   }
+-
+-   salt.bv_val = (void*)(dbhash + MD5_LENGTH); /* salt starts after hash value */
+-   salt.bv_len = hash_len - MD5_LENGTH; /* remaining bytes must be salt */
+-
+-   /* create the hash */
+-   memset( userhash, 0, sizeof(userhash) );
+-   PK11_DigestBegin(ctx);
+-   PK11_DigestOp(ctx, (const unsigned char *)userpwd, strlen(userpwd));
+-   PK11_DigestOp(ctx, (unsigned char*)(salt.bv_val), salt.bv_len);
+-   PK11_DigestFinal(ctx, userhash, &outLen, sizeof userhash);
+-   PK11_DestroyContext(ctx, 1);
+-
+-   /* Compare everything up to the salt. */
+-   rc = slapi_ct_memcmp( userhash, dbhash, MD5_LENGTH );
++    hash_len = pwdstorage_base64_decode_len(dbpwd, 0);
++    if (hash_len >= sizeof(quick_dbhash)) { /* get more space: */
++        dbhash = (char *)slapi_ch_calloc(hash_len + 1, sizeof(char));
++        if (dbhash == NULL)
++            goto loser;
++    } else {
++        memset(quick_dbhash, 0, sizeof(quick_dbhash));
++    }
++
++    hashresult = PL_Base64Decode(dbpwd, 0, dbhash);
++    if (NULL == hashresult) {
++        slapi_log_err(SLAPI_LOG_PLUGIN, SALTED_MD5_SUBSYSTEM_NAME,
++                      "smd5_pw_cmp: userPassword \"%s\" is the wrong length "
++                      "or is not properly encoded BASE64\n",
++                      dbpwd);
++        goto loser;
++    }
++
++    salt.bv_val = (void *)(dbhash + MD5_LENGTH); /* salt starts after hash value */
++    salt.bv_len = hash_len - MD5_LENGTH;         /* remaining bytes must be salt */
++
++    /* create the hash */
++    memset(userhash, 0, sizeof(userhash));
++    PK11_DigestBegin(ctx);
++    PK11_DigestOp(ctx, (const unsigned char *)userpwd, strlen(userpwd));
++    PK11_DigestOp(ctx, (unsigned char *)(salt.bv_val), salt.bv_len);
++    PK11_DigestFinal(ctx, userhash, &outLen, sizeof userhash);
++    PK11_DestroyContext(ctx, 1);
++
++    /* Compare everything up to the salt. */
++    rc = slapi_ct_memcmp(userhash, dbhash, MD5_LENGTH, MD5_LENGTH);
+ 
+ loser:
+    if ( dbhash && dbhash != quick_dbhash ) slapi_ch_free_string( (char **)&dbhash );
+diff --git a/ldap/servers/slapd/ch_malloc.c b/ldap/servers/slapd/ch_malloc.c
+index 52ccb64e8..da0b5f6d8 100644
+--- a/ldap/servers/slapd/ch_malloc.c
++++ b/ldap/servers/slapd/ch_malloc.c
+@@ -343,8 +343,8 @@ slapi_ch_smprintf(const char *fmt, ...)
+ 
+ /* Constant time memcmp. Does not shortcircuit on failure! */
+ /* This relies on p1 and p2 both being size at least n! */
+-int
+-slapi_ct_memcmp( const void *p1, const void *p2, size_t n)
++int32_t
++slapi_ct_memcmp( const void *p1, const void *p2, size_t n1, size_t n2)
+ {
+     int result = 0;
+     const unsigned char *_p1 = (const unsigned char *)p1;
+@@ -353,10 +353,35 @@ slapi_ct_memcmp( const void *p1, const void *p2, size_t n)
+     if (_p1 == NULL || _p2 == NULL) {
+         return 2;
+     }
+-
+-    for (size_t i = 0; i < n; i++) {
+-        if (_p1[i] ^ _p2[i]) {
+-            result = 1;
++    if (n1 == n2) {
++        for (size_t i = 0; i < n1; i++) {
++            if (_p1[i] ^ _p2[i]) {
++                result = 1;
++            }
++        }
++    } else {
++        const unsigned char *_pa;
++        const unsigned char *_pb;
++        size_t nl;
++        if (n2 > n1) {
++            _pa = _p2;
++            _pb = _p2;
++            nl = n2;
++        } else {
++            _pa = _p1;
++            _pb = _p1;
++            nl = n1;
++        }
++        /* We already fail as n1 != n2 */
++        result = 3;
++        for (size_t i = 0; i < nl; i++) {
++            if (_pa[i] ^ _pb[i]) {
++                /*
++                 * If we don't mutate result here, dead code elimination
++                 * we remove for loop.
++                 */
++                result = 4;
++            }
+         }
+     }
+     return result;
+diff --git a/ldap/servers/slapd/slapi-plugin.h b/ldap/servers/slapd/slapi-plugin.h
+index 16aa1b711..8555be939 100644
+--- a/ldap/servers/slapd/slapi-plugin.h
++++ b/ldap/servers/slapd/slapi-plugin.h
+@@ -5856,7 +5856,7 @@ char * slapi_ch_smprintf(const char *fmt, ...)
+  * \param n length in bytes of the content of p1 AND p2.
+  * \return 0 on match. 1 on non-match. 2 on presence of NULL pointer in p1 or p2.
+  */
+-int slapi_ct_memcmp( const void *p1, const void *p2, size_t n);
++int32_t slapi_ct_memcmp( const void *p1, const void *p2, size_t n1, size_t n2);
+ 
+ /*
+  * syntax plugin routines
+-- 
+2.13.6
+
diff --git a/SOURCES/0089-Ticket-49545-final-substring-extended-filter-search-.patch b/SOURCES/0089-Ticket-49545-final-substring-extended-filter-search-.patch
new file mode 100644
index 0000000..02cce65
--- /dev/null
+++ b/SOURCES/0089-Ticket-49545-final-substring-extended-filter-search-.patch
@@ -0,0 +1,66 @@
+From 73dd295434a03be28531cea40fde041ce7bd2d7e Mon Sep 17 00:00:00 2001
+From: Mark Reynolds <mreynolds@redhat.com>
+Date: Tue, 13 Feb 2018 10:35:35 -0500
+Subject: [PATCH] Ticket 49545 - final substring extended filter search returns
+  invalid result
+
+Bug Description:
+	During a search (using extended filter with final substring), the server
+	checks the filter before returning the matching entries.
+	When checking the attribute value against the filter, it
+	uses the wrong value.
+
+Fix Description:
+	Make suree it uses the right portion of the attribute value, in order
+	to generate the keys to compare.
+
+https://pagure.io/389-ds-base/issue/49545
+
+Reviewed by: Ludwig Krispenz
+---
+ ldap/servers/plugins/collation/orfilter.c | 20 ++++++++++++++++++--
+ 1 file changed, 18 insertions(+), 2 deletions(-)
+
+diff --git a/ldap/servers/plugins/collation/orfilter.c b/ldap/servers/plugins/collation/orfilter.c
+index 866936afe..8f10f81b6 100644
+--- a/ldap/servers/plugins/collation/orfilter.c
++++ b/ldap/servers/plugins/collation/orfilter.c
+@@ -180,17 +180,33 @@ ss_filter_match (or_filter_t* or, struct berval** vals)
+ 	    } else {		/* final */
+ 		auto size_t attempts = MAX_CHAR_COMBINING;
+ 		auto char* limit = v.bv_val;
++                auto char *end;
+ 		auto struct berval** vkeys;
+ 		auto struct berval* vals[2];
+ 		auto struct berval key;
++
+ 		rc = -1;
+ 		vals[0] = &v;
+ 		vals[1] = NULL;
+ 		key.bv_val = (*k)->bv_val;
+ 		key.bv_len = (*k)->bv_len - 1;
+-		v.bv_val = (*vals)->bv_val + (*vals)->bv_len;
++                /* In the following lines it will loop to find
++                 * if the end of the attribute value matches the 'final' of the filter
++                 * Short summary:
++                 * vals contains the attribute value :for example "hello world"
++                 * key contain the key generated from the indexing of final part of the filter.
++                 * for example filter=(<attribut>=*ld), so key contains the indexing("ld").
++                 * 
++                 * The loop will iterate over the attribute value (vals) from the end of string
++                 * to the begining. So it will try to index('d'), index('ld'), index('rld'), index('orld')...
++                 * 
++                 * At each iteration if the key generated from indexing the portion of vals, matches 
++                 * the key generate from the final part of the filter, then the loop stops => we are done
++                 */
++                end = v.bv_val + v.bv_len - 1;
++                v.bv_val = end;
+ 		while(1) {
+-		    v.bv_len = (*vals)->bv_len - (v.bv_val - (*vals)->bv_val);
++                    v.bv_len = end - v.bv_val + 1;
+ 		    vkeys = ix->ix_index (ix, vals, NULL);
+ 		    if (vkeys && vkeys[0]) {
+ 			auto const struct berval* vkey = vkeys[0];
+-- 
+2.13.6
+
diff --git a/SOURCES/0090-Ticket-49471-heap-buffer-overflow-in-ss_unescape.patch b/SOURCES/0090-Ticket-49471-heap-buffer-overflow-in-ss_unescape.patch
new file mode 100644
index 0000000..ca57d86
--- /dev/null
+++ b/SOURCES/0090-Ticket-49471-heap-buffer-overflow-in-ss_unescape.patch
@@ -0,0 +1,189 @@
+From 715bdd7fd707d4addf52c21051ec3ab90951a691 Mon Sep 17 00:00:00 2001
+From: Thierry Bordaz <tbordaz@redhat.com>
+Date: Wed, 6 Dec 2017 15:14:57 +0100
+Subject: [PATCH] Ticket 49471 - heap-buffer-overflow in ss_unescape
+
+Bug Description:
+	Two problems here
+		- when searching for wildcard and escape char, ss_unescape assumes the string
+		  is at least 3 chars longs. So memcmp can overflow a shorter string
+		- while splitting a string into substring pattern, it loops over
+		  wildcard and can overpass the string end
+
+Fix Description:
+	For the first problem, it checks the string size is long enough to memcmp
+        a wildcard or an escape
+	For the second it exits from the loop  as soon as the end of the string is reached
+
+https://pagure.io/389-ds-base/issue/49471
+
+Reviewed by: William Brown
+
+Platforms tested: F23
+
+Flag Day: no
+
+Doc impact: no
+
+(cherry picked from commit 5991388ce75fba8885579b769711d57acfd43cd3)
+(cherry picked from commit 3fb1c408cb4065de8d9c0c1de050d08969d51bb0)
+---
+ dirsrvtests/tests/tickets/ticket49471_test.py | 79 +++++++++++++++++++++++++++
+ ldap/servers/plugins/collation/orfilter.c     | 48 +++++++++-------
+ 2 files changed, 106 insertions(+), 21 deletions(-)
+ create mode 100644 dirsrvtests/tests/tickets/ticket49471_test.py
+
+diff --git a/dirsrvtests/tests/tickets/ticket49471_test.py b/dirsrvtests/tests/tickets/ticket49471_test.py
+new file mode 100644
+index 000000000..0456a5182
+--- /dev/null
++++ b/dirsrvtests/tests/tickets/ticket49471_test.py
+@@ -0,0 +1,79 @@
++import logging
++import pytest
++import os
++import time
++import ldap
++from lib389._constants import *
++from lib389.topologies import topology_st as topo
++from lib389 import Entry
++
++DEBUGGING = os.getenv("DEBUGGING", default=False)
++if DEBUGGING:
++    logging.getLogger(__name__).setLevel(logging.DEBUG)
++else:
++    logging.getLogger(__name__).setLevel(logging.INFO)
++log = logging.getLogger(__name__)
++
++
++USER_CN='user_'
++def _user_get_dn(no):
++    cn = '%s%d' % (USER_CN, no)
++    dn = 'cn=%s,ou=people,%s' % (cn, SUFFIX)
++    return (cn, dn)
++
++def add_user(server, no, desc='dummy', sleep=True):
++    (cn, dn) = _user_get_dn(no)
++    log.fatal('Adding user (%s): ' % dn)
++    server.add_s(Entry((dn, {'objectclass': ['top', 'person', 'inetuser', 'userSecurityInformation'],
++                             'cn': [cn],
++                             'description': [desc],
++                             'sn': [cn],
++                             'description': ['add on that host']})))
++    if sleep:
++        time.sleep(2)
++
++def test_ticket49471(topo):
++    """Specify a test case purpose or name here
++
++    :id: 457ab172-9455-4eb2-89a0-150e3de5993f
++    :setup: Fill in set up configuration here
++    :steps:
++        1. Fill in test case steps here
++        2. And indent them like this (RST format requirement)
++    :expectedresults:
++        1. Fill in the result that is expected
++        2. For each test step
++    """
++
++    # If you need any test suite initialization,
++    # please, write additional fixture for that (including finalizer).
++    # Topology for suites are predefined in lib389/topologies.py.
++
++    # If you need host, port or any other data about instance,
++    # Please, use the instance object attributes for that (for example, topo.ms["master1"].serverid)
++
++    S1 = topo.standalone
++    add_user(S1, 1)
++
++    Filter = "(description:2.16.840.1.113730.3.3.2.1.1.6:=\*on\*)"
++    ents = S1.search_s(SUFFIX, ldap.SCOPE_SUBTREE, Filter)
++    assert len(ents) == 1
++
++    #
++    # The following is for the test 49491
++    # skipped here else it crashes in ASAN
++    #Filter = "(description:2.16.840.1.113730.3.3.2.1.1.6:=\*host)"
++    #ents = S1.search_s(SUFFIX, ldap.SCOPE_SUBTREE, Filter)
++    #assert len(ents) == 1
++
++    if DEBUGGING:
++        # Add debugging steps(if any)...
++        pass
++
++
++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/plugins/collation/orfilter.c b/ldap/servers/plugins/collation/orfilter.c
+index 8f10f81b6..438efafef 100644
+--- a/ldap/servers/plugins/collation/orfilter.c
++++ b/ldap/servers/plugins/collation/orfilter.c
+@@ -317,19 +317,21 @@ ss_unescape (struct berval* val)
+     char* t = s;
+     char* limit = s + val->bv_len;
+     while (s < limit) {
+-	if (!memcmp (s, "\\2a", 3) ||
+-	    !memcmp (s, "\\2A", 3)) {
+-	    *t++ = WILDCARD;
+-	    s += 3;
+-	} else if (!memcmp (s, "\\5c", 3) ||
+-		   !memcmp (s, "\\5C", 3)) {
+-	    *t++ = '\\';
+-	    s += 3;
+-	} else {
+-	    if (t == s) LDAP_UTF8INC (t);
+-	    else t += LDAP_UTF8COPY (t, s);
+-	    LDAP_UTF8INC (s);
+-	}
++        if (((limit - s) >= 3) &&
++                (!memcmp(s, "\\2a", 3) || !memcmp(s, "\\2A", 3))) {
++            *t++ = WILDCARD;
++            s += 3;
++        } else if ((limit - s) >= 3 &&
++                (!memcmp(s, "\\5c", 3) || !memcmp(s, "\\5C", 3))) {
++            *t++ = '\\';
++            s += 3;
++        } else {
++            if (t == s)
++                LDAP_UTF8INC(t);
++            else
++                t += LDAP_UTF8COPY(t, s);
++            LDAP_UTF8INC(s);
++        }
+     }
+     val->bv_len = t - val->bv_val;
+ }
+@@ -405,14 +407,18 @@ ss_filter_values (struct berval* pattern, int* query_op)
+     n = 0;
+     s = pattern->bv_val;
+     for (p = s; p < plimit; LDAP_UTF8INC(p)) {
+-	switch (*p) {
+-	  case WILDCARD:
+-	    result[n++] = ss_filter_value (s, p-s, &val);
+-	    while (++p != plimit && *p == WILDCARD);
+-	    s = p;
+-	    break;
+-	  default: break;
+-	}
++        switch (*p) {
++        case WILDCARD:
++            result[n++] = ss_filter_value(s, p - s, &val);
++            while (p != plimit && *p == WILDCARD) p++;
++            s = p;
++            break;
++        default:
++            break;
++        }
++        if (p >= plimit) {
++            break;
++        }
+     }
+     if (p != s || s == plimit) {
+ 	result[n++] = ss_filter_value (s, p-s, &val);
+-- 
+2.13.6
+
diff --git a/SPECS/389-ds-base.spec b/SPECS/389-ds-base.spec
index 61d84f7..fb486df 100644
--- a/SPECS/389-ds-base.spec
+++ b/SPECS/389-ds-base.spec
@@ -30,7 +30,7 @@
 Summary:          389 Directory Server (base)
 Name:             389-ds-base
 Version:          1.3.6.1
-Release:          %{?relprefix}26%{?prerel}%{?dist}
+Release:          %{?relprefix}28%{?prerel}%{?dist}
 License:          GPLv3+
 URL:              https://www.port389.org/
 Group:            System Environment/Daemons
@@ -220,6 +220,10 @@ Patch83:          0083-Ticket-49410-opened-connection-can-remain-no-longer-.patc
 Patch84:          0084-Ticket-48118-backport-changelog-can-be-erronously-re.patch
 Patch85:          0085-Ticket-49495-Fix-memory-management-is-vattr.patch
 Patch86:          0086-CVE-2017-15134-389-ds-base-Remote-DoS-via-search-fil.patch
+Patch87:          0087-Ticket-49509-Indexing-of-internationalized-matching-.patch
+Patch88:          0088-Ticket-bz1525628-1.3.6-backport-invalid-password-mig.patch
+Patch89:          0089-Ticket-49545-final-substring-extended-filter-search-.patch
+Patch90:          0090-Ticket-49471-heap-buffer-overflow-in-ss_unescape.patch
 
 %description
 389 Directory Server is an LDAPv3 compliant server.  The base package includes
@@ -376,6 +380,10 @@ cp %{SOURCE2} README.devel
 %patch84 -p1
 %patch85 -p1
 %patch86 -p1
+%patch87 -p1
+%patch88 -p1
+%patch89 -p1
+%patch90 -p1
 
 %build
 
@@ -608,8 +616,18 @@ fi
 %{_sysconfdir}/%{pkgname}/dirsrvtests
 
 %changelog
+* Mon Feb 26 2018 Mark Reynolds <mreynolds@redhat.com> - 1.3.6.1-28
+- Bump version to 1.3.6.1-28
+- Resolves: Bug 1540105 - CVE-2018-1054 - remote Denial of Service (DoS) via search filters in SetUnicodeStringFromUTF_8
+
+* Tue Feb 13 2018 Mark Reynolds <mreynolds@redhat.com> - 1.3.6.1-27
+- Bump version to 1.3.6.1-27
+- Resolves: Bug 1536343 - Indexing of internationalized matching rules is failing
+- Resolves: Bug 1535539 - CVE-2017-15135 - Authentication bypass due to lack of size check in slapi_ct_memcmp function
+- Resolves: Bug 1540105 - CVE-2018-1054 - remote Denial of Service (DoS) via search filters in SetUnicodeStringFromUTF_8
+
 * Tue Jan 16 2018 Mark Reynolds <mreynolds@redhat.com> - 1.3.6.1-26
-- Bump version to 1.3.6.1-25
+- Bump version to 1.3.6.1-26
 - Resolves: Bug 1534430 - crash in slapi_filter_sprintf 
 
 * Mon Dec 18 2017 Mark Reynolds <mreynolds@redhat.com> - 1.3.6.1-25