From 816ad14e8c1e3d2f147f21e173ba5e80887d70e4 Mon Sep 17 00:00:00 2001 From: CentOS Sources Date: Mar 06 2018 18:39:02 +0000 Subject: import 389-ds-base-1.3.6.1-28.el7_4 --- 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 +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 +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 +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=(=*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 +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 - 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 - 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 - 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 - 1.3.6.1-25