mirror of
https://git.centos.org/rpms/389-ds-base.git
synced 2025-02-23 08:12:54 +00:00
import 389-ds-base-1.3.6.1-28.el7_4
This commit is contained in:
parent
8a4a2a924a
commit
816ad14e8c
5 changed files with 830 additions and 2 deletions
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
Loading…
Add table
Reference in a new issue