diff --git a/SOURCES/0090-Ticket-48492-heap-corruption-at-schema-replication.patch b/SOURCES/0090-Ticket-48492-heap-corruption-at-schema-replication.patch new file mode 100644 index 0000000..3ed408d --- /dev/null +++ b/SOURCES/0090-Ticket-48492-heap-corruption-at-schema-replication.patch @@ -0,0 +1,103 @@ +From ce824731f4839f7812109b8c04ce704a56eeca4b Mon Sep 17 00:00:00 2001 +From: Noriko Hosoi +Date: Wed, 10 Feb 2016 11:36:32 -0800 +Subject: [PATCH 90/93] Ticket #48492 - heap corruption at schema replication. + +Description: 389-ds-base-1.3.2 and newer uses openldap schema parser, +which is more strict with the definition. For instance, the older +389-ds-base could have a schema such as SINTAX OID in single quotes, +which is not acceptable on the newer version. There was a bug to +handle the error case that caused a crash. + +This patch adds +1) the null reference check to attr_syntax_free (attrsyntax.c), +2) a null init to the output arg in parse_at_str and parse_oc_str + (schema.c) and +3) an error logging to schema_berval_to_atlist & schema_berval_to_oclist + (schema.c) for troubleshooting. + +https://fedorahosted.org/389/ticket/48492 + +Reviewed by wibrown@redhat.com and mreynolds@redhat.com (Thank you, William and Mark!) + +(cherry picked from commit b5bfa2a0386e168ce2196a077169382ae53a94b4) +(cherry picked from commit 9bd53c297683e691fef174bf1aed6842f475fb9f) +--- + ldap/servers/slapd/attrsyntax.c | 3 +++ + ldap/servers/slapd/schema.c | 16 +++++++++++++++- + 2 files changed, 18 insertions(+), 1 deletion(-) + +diff --git a/ldap/servers/slapd/attrsyntax.c b/ldap/servers/slapd/attrsyntax.c +index 4cdcf86..8b2a77a 100644 +--- a/ldap/servers/slapd/attrsyntax.c ++++ b/ldap/servers/slapd/attrsyntax.c +@@ -189,6 +189,9 @@ attr_syntax_check_oids() + void + attr_syntax_free( struct asyntaxinfo *a ) + { ++ if (!a) { ++ return; ++ } + cool_charray_free( a->asi_aliases ); + slapi_ch_free_string(&a->asi_name ); + slapi_ch_free_string(&a->asi_desc ); +diff --git a/ldap/servers/slapd/schema.c b/ldap/servers/slapd/schema.c +index 65cbad5..dd56599 100644 +--- a/ldap/servers/slapd/schema.c ++++ b/ldap/servers/slapd/schema.c +@@ -263,6 +263,9 @@ static PRCallOnceType schema_dse_mandatory_init_callonce = { 0, 0, 0 }; + static int parse_at_str(const char *input, struct asyntaxinfo **asipp, char *errorbuf, size_t errorbufsize, + PRUint32 schema_flags, int is_user_defined, int schema_ds4x_compat, int is_remote) + { ++ if (asipp) { ++ *asipp = NULL; ++ } + #ifdef USE_OPENLDAP + return parse_attr_str(input, asipp, errorbuf, errorbufsize, schema_flags, is_user_defined,schema_ds4x_compat,is_remote); + #else +@@ -274,6 +277,9 @@ static int parse_oc_str(const char *input, struct objclass **oc, char *errorbuf, + size_t errorbufsize, PRUint32 schema_flags, int is_user_defined, + int schema_ds4x_compat, struct objclass* private_schema ) + { ++ if (oc) { ++ *oc = NULL; ++ } + #ifdef USE_OPENLDAP + return parse_objclass_str (input, oc, errorbuf, errorbufsize, schema_flags, is_user_defined, schema_ds4x_compat, private_schema ); + #else +@@ -7146,11 +7152,15 @@ schema_berval_to_oclist(struct berval **oc_berval) + oc_list = NULL; + oc_tail = NULL; + if (oc_berval != NULL) { ++ errorbuf[0] = '\0'; + for (i = 0; oc_berval[i] != NULL; i++) { + /* parse the objectclass value */ + if (LDAP_SUCCESS != (rc = parse_oc_str(oc_berval[i]->bv_val, &oc, + errorbuf, sizeof (errorbuf), DSE_SCHEMA_NO_CHECK | DSE_SCHEMA_USE_PRIV_SCHEMA, 0, + schema_ds4x_compat, oc_list))) { ++ slapi_log_error(SLAPI_LOG_FATAL, "schema", ++ "parse_oc_str returned error: %s\n", ++ errorbuf[0]?errorbuf:"unknown"); + oc_free(&oc); + rc = 1; + break; +@@ -7184,11 +7194,15 @@ schema_berval_to_atlist(struct berval **at_berval) + schema_ds4x_compat = config_get_ds4_compatible_schema(); + + if (at_berval != NULL) { ++ errorbuf[0] = '\0'; + for (i = 0; at_berval[i] != NULL; i++) { + /* parse the objectclass value */ + rc = parse_at_str(at_berval[i]->bv_val, &at, errorbuf, sizeof (errorbuf), + DSE_SCHEMA_NO_CHECK | DSE_SCHEMA_USE_PRIV_SCHEMA, 0, schema_ds4x_compat, 0); +- if(rc){ ++ if (rc) { ++ slapi_log_error(SLAPI_LOG_FATAL, "schema", ++ "parse_oc_str returned error: %s\n", ++ errorbuf[0]?errorbuf:"unknown"); + attr_syntax_free(at); + break; + } +-- +2.4.11 + diff --git a/SOURCES/0091-Ticket-48492-heap-corruption-at-schema-replication.patch b/SOURCES/0091-Ticket-48492-heap-corruption-at-schema-replication.patch new file mode 100644 index 0000000..d70e6b0 --- /dev/null +++ b/SOURCES/0091-Ticket-48492-heap-corruption-at-schema-replication.patch @@ -0,0 +1,218 @@ +From bc3328ebbe1b8279f77ad1020bce9fb638d4c94c Mon Sep 17 00:00:00 2001 +From: Noriko Hosoi +Date: Fri, 8 Apr 2016 14:17:12 -0700 +Subject: [PATCH 91/93] Ticket #48492 - heap corruption at schema replication. + +Bug Description: If nsslapd-enquote-sup-oc is on, the server is supposed to +handle the quoted SYNTAX values although the spec is deprecated. Currently, +if nsslapd-enquote-sup-oc is on, it wraps SYNTAX values with quotes, but the +information is not passed to the openldap schema parser where the parsing the +schema fails. + +Fix Description: This patch passes the info (flag LDAP_SCHEMA_ALLOW_QUOTED) +to the openldap API ldap_str2attributetype if nsslapd-enquote-sup-oc is on. + +Additionally, to support the old style quoted SYNTAX values in the schema +files, loading the schema has to get the enquote information prior to the +configuration parameters evaluated. To pass the information, this patch +accepts the environment variable LDAP_SCHEMA_ALLOW_QUOTED. If it is defined +with any value, old style schema files are processed. + +To set the environment variable, add + LDAP_SCHEMA_ALLOW_QUOTED="on" +to /etc/sysconfig/dirsrv-INSTANCE. + +https://fedorahosted.org/389/ticket/48492 + +Reviewed by firstyear@redhat.com (Thank you, William!!) + +(cherry picked from commit 955dc66d42511c2cc8d6ff18cf030508f6da2770) +(cherry picked from commit 7927e4420fb185ae328d56cfd4741583ae1f667b) +--- + ldap/servers/slapd/schema.c | 66 ++++++++++++++++++++++++++++++++++----------- + 1 file changed, 51 insertions(+), 15 deletions(-) + +diff --git a/ldap/servers/slapd/schema.c b/ldap/servers/slapd/schema.c +index dd56599..806c38d 100644 +--- a/ldap/servers/slapd/schema.c ++++ b/ldap/servers/slapd/schema.c +@@ -1638,6 +1638,16 @@ schema_attr_enum_callback(struct asyntaxinfo *asip, void *arg) + } + + if ( !aew->schema_ds4x_compat ) { ++#if defined (USE_OPENLDAP) ++ /* ++ * These values in quotes are not supported by the openldap parser. ++ * Even if nsslapd-enquote-sup-oc is on, quotes should not be added. ++ */ ++ outp += put_tagged_oid( outp, "SUP ", asip->asi_superior, NULL, 0 ); ++ outp += put_tagged_oid( outp, "EQUALITY ", asip->asi_mr_equality, NULL, 0 ); ++ outp += put_tagged_oid( outp, "ORDERING ", asip->asi_mr_ordering, NULL, 0 ); ++ outp += put_tagged_oid( outp, "SUBSTR ", asip->asi_mr_substring, NULL, 0 ); ++#else + outp += put_tagged_oid( outp, "SUP ", + asip->asi_superior, NULL, aew->enquote_sup_oc ); + outp += put_tagged_oid( outp, "EQUALITY ", +@@ -1646,6 +1656,7 @@ schema_attr_enum_callback(struct asyntaxinfo *asip, void *arg) + asip->asi_mr_ordering, NULL, aew->enquote_sup_oc ); + outp += put_tagged_oid( outp, "SUBSTR ", + asip->asi_mr_substring, NULL, aew->enquote_sup_oc ); ++#endif + } + + outp += put_tagged_oid( outp, "SYNTAX ", syntaxoid, syntaxlengthbuf, +@@ -4105,7 +4116,7 @@ parse_attr_str(const char *input, struct asyntaxinfo **asipp, char *errorbuf, + char **attr_names = NULL; + unsigned long flags = SLAPI_ATTR_FLAG_OVERRIDE; + /* If we ever accept openldap schema directly, then make parser_flags configurable */ +- const int parser_flags = LDAP_SCHEMA_ALLOW_NONE | LDAP_SCHEMA_ALLOW_NO_OID; ++ unsigned int parser_flags = LDAP_SCHEMA_ALLOW_NONE | LDAP_SCHEMA_ALLOW_NO_OID; + int invalid_syntax_error; + int syntaxlength = SLAPI_SYNTAXLENGTH_NONE; + int num_names = 0; +@@ -4113,6 +4124,17 @@ parse_attr_str(const char *input, struct asyntaxinfo **asipp, char *errorbuf, + int rc = 0; + int a, aa; + ++ if (config_get_enquote_sup_oc()) { ++ parser_flags |= LDAP_SCHEMA_ALLOW_QUOTED; ++ } else if (getenv("LDAP_SCHEMA_ALLOW_QUOTED")) { ++ char ebuf[SLAPI_DSE_RETURNTEXT_SIZE]; ++ parser_flags |= LDAP_SCHEMA_ALLOW_QUOTED; ++ if (config_set_enquote_sup_oc(CONFIG_ENQUOTE_SUP_OC_ATTRIBUTE, "on", ebuf, CONFIG_APPLY)) { ++ slapi_log_error(SLAPI_LOG_FATAL, "schema", "Failed to enable %s: %s\n", ++ CONFIG_ENQUOTE_SUP_OC_ATTRIBUTE, ebuf); ++ } ++ } ++ + /* + * OpenLDAP AttributeType struct + * +@@ -4159,7 +4181,7 @@ parse_attr_str(const char *input, struct asyntaxinfo **asipp, char *errorbuf, + /* trim any leading spaces */ + input++; + } +- if((atype = ldap_str2attributetype(input, &rc, &errp, parser_flags )) == NULL){ ++ if((atype = ldap_str2attributetype(input, &rc, &errp, (const unsigned int)parser_flags )) == NULL){ + schema_create_errormsg( errorbuf, errorbufsize, schema_errprefix_at, input, + "Failed to parse attribute, error(%d - %s) at (%s)", rc, ldap_scherr2str(rc), errp ); + return invalid_syntax_error; +@@ -4478,12 +4500,23 @@ parse_objclass_str ( const char *input, struct objclass **oc, char *errorbuf, + char **OrigRequiredAttrsArray, **OrigAllowedAttrsArray; + char *first_oc_name = NULL; + /* If we ever accept openldap schema directly, then make parser_flags configurable */ +- const int parser_flags = LDAP_SCHEMA_ALLOW_NONE | LDAP_SCHEMA_ALLOW_NO_OID; ++ unsigned int parser_flags = LDAP_SCHEMA_ALLOW_NONE | LDAP_SCHEMA_ALLOW_NO_OID; + PRUint8 flags = 0; + int invalid_syntax_error; + int i, j; + int rc = 0; + ++ if (config_get_enquote_sup_oc()) { ++ parser_flags |= LDAP_SCHEMA_ALLOW_QUOTED; ++ } else if (getenv("LDAP_SCHEMA_ALLOW_QUOTED")) { ++ char ebuf[SLAPI_DSE_RETURNTEXT_SIZE]; ++ parser_flags |= LDAP_SCHEMA_ALLOW_QUOTED; ++ if (config_set_enquote_sup_oc(CONFIG_ENQUOTE_SUP_OC_ATTRIBUTE, "on", ebuf, CONFIG_APPLY)) { ++ slapi_log_error(SLAPI_LOG_FATAL, "schema", "Failed to enable %s: %s\n", ++ CONFIG_ENQUOTE_SUP_OC_ATTRIBUTE, ebuf); ++ } ++ } ++ + /* + * openLDAP Objectclass struct + * +@@ -4521,10 +4554,10 @@ parse_objclass_str ( const char *input, struct objclass **oc, char *errorbuf, + * Parse the input and create the openLdap objectclass structure + */ + while(isspace(*input)){ +- /* trim any leading spaces */ ++ /* trim any leading spaces */ + input++; + } +- if((objClass = ldap_str2objectclass(input, &rc, &errp, parser_flags )) == NULL){ ++ if((objClass = ldap_str2objectclass(input, &rc, &errp, (const unsigned int)parser_flags )) == NULL){ + schema_create_errormsg( errorbuf, errorbufsize, schema_errprefix_oc, input, + "Failed to parse objectclass, error(%d) at (%s)", rc, errp ); + return invalid_syntax_error; +@@ -5592,7 +5625,7 @@ get_tagged_oid( const char *tag, const char **inputp, + PR_ASSERT( NULL != *inputp ); + PR_ASSERT( NULL != tag ); + PR_ASSERT( '\0' != tag[ 0 ] ); +- if('(' !=tag[0]) ++ if('(' !=tag[0]) + PR_ASSERT((' ' == tag[ strlen( tag ) - 1 ]) || ('(' == tag[ strlen( tag ) - 1 ])); + + if ( NULL == strstr_fn ) { +@@ -5611,8 +5644,8 @@ get_tagged_oid( const char *tag, const char **inputp, + /* skip past the leading single quote, if present */ + if ( *startp == '\'' ) { + ++startp; +- /* skip past any extra white space */ +- startp = skipWS( startp ); ++ /* skip past any extra white space */ ++ startp = skipWS( startp ); + } + + /* locate the end of the OID */ +@@ -7155,6 +7188,7 @@ schema_berval_to_oclist(struct berval **oc_berval) + errorbuf[0] = '\0'; + for (i = 0; oc_berval[i] != NULL; i++) { + /* parse the objectclass value */ ++ oc = NULL; + if (LDAP_SUCCESS != (rc = parse_oc_str(oc_berval[i]->bv_val, &oc, + errorbuf, sizeof (errorbuf), DSE_SCHEMA_NO_CHECK | DSE_SCHEMA_USE_PRIV_SCHEMA, 0, + schema_ds4x_compat, oc_list))) { +@@ -7197,12 +7231,13 @@ schema_berval_to_atlist(struct berval **at_berval) + errorbuf[0] = '\0'; + for (i = 0; at_berval[i] != NULL; i++) { + /* parse the objectclass value */ ++ at = NULL; + rc = parse_at_str(at_berval[i]->bv_val, &at, errorbuf, sizeof (errorbuf), + DSE_SCHEMA_NO_CHECK | DSE_SCHEMA_USE_PRIV_SCHEMA, 0, schema_ds4x_compat, 0); + if (rc) { + slapi_log_error(SLAPI_LOG_FATAL, "schema", +- "parse_oc_str returned error: %s\n", +- errorbuf[0]?errorbuf:"unknown"); ++ "schema_berval_to_atlist: parse_at_str(%s) failed - %s\n", ++ at_berval[i]->bv_val, errorbuf[0]?errorbuf:"unknown"); + attr_syntax_free(at); + break; + } +@@ -7217,6 +7252,7 @@ schema_berval_to_atlist(struct berval **at_berval) + } + if (rc) { + schema_atlist_free(head); ++ head = NULL; + } + + return head; +@@ -7319,12 +7355,12 @@ schema_attributetypes_superset_check(struct berval **remote_schema, char *type) + static void + modify_schema_internal_mod(Slapi_DN *sdn, Slapi_Mods *smods) + { +- Slapi_PBlock *newpb; ++ Slapi_PBlock *newpb; + int op_result; +- CSN *schema_csn; ++ CSN *schema_csn; + +- /* allocate internal mod components: pblock*/ +- newpb = slapi_pblock_new(); ++ /* allocate internal mod components: pblock*/ ++ newpb = slapi_pblock_new(); + + slapi_modify_internal_set_pb_ext ( + newpb, +@@ -7333,7 +7369,7 @@ modify_schema_internal_mod(Slapi_DN *sdn, Slapi_Mods *smods) + NULL, /* Controls */ + NULL, + (void *)plugin_get_default_component_id(), +- 0); ++ 0); + + /* do modify */ + slapi_modify_internal_pb (newpb); +-- +2.4.11 + diff --git a/SOURCES/0092-Ticket-48808-Paged-results-search-returns-the-blank-.patch b/SOURCES/0092-Ticket-48808-Paged-results-search-returns-the-blank-.patch new file mode 100644 index 0000000..37f3621 --- /dev/null +++ b/SOURCES/0092-Ticket-48808-Paged-results-search-returns-the-blank-.patch @@ -0,0 +1,39 @@ +From 39a14eaab84e7eac940d1d707cabc9610ef570c6 Mon Sep 17 00:00:00 2001 +From: Noriko Hosoi +Date: Tue, 26 Apr 2016 13:53:02 -0700 +Subject: [PATCH 92/93] Ticket #48808 - Paged results search returns the blank + list of entries + +Bug Description: When a simple paged results slot in a connection is +discarded due to an error, e.g., SIZELIMIT_EXCEEDED, the slot was not +properly cleaned up. Then, if the slot was reused, the leftover flag +confused the code and ended up returning the 0 search result. + +Fix Description: This patch adds the clean up code when a slot is re- +used. + +https://fedorahosted.org/389/ticket/48808 + +Reviewed by wibrown@redhat.com (Thank you, William!!) + +(cherry picked from commit 09180b25570696d24c86e3a046fb497c15549c64) +(cherry picked from commit a8486ab3b364a9ae088d6404d025058b04ac358d) +--- + ldap/servers/slapd/pagedresults.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/ldap/servers/slapd/pagedresults.c b/ldap/servers/slapd/pagedresults.c +index d394dab..52d2158 100644 +--- a/ldap/servers/slapd/pagedresults.c ++++ b/ldap/servers/slapd/pagedresults.c +@@ -124,6 +124,7 @@ pagedresults_parse_control_value( Slapi_PBlock *pb, + prp = conn->c_pagedresults.prl_list; + for (i = 0; i < conn->c_pagedresults.prl_maxlen; i++, prp++) { + if (!prp->pr_current_be) { /* unused slot; take it */ ++ _pr_cleanup_one_slot(prp); + prp->pr_current_be = be; + *index = i; + break; +-- +2.4.11 + diff --git a/SOURCES/0093-Ticket-48808-Add-test-case.patch b/SOURCES/0093-Ticket-48808-Add-test-case.patch new file mode 100644 index 0000000..8c92d51 --- /dev/null +++ b/SOURCES/0093-Ticket-48808-Add-test-case.patch @@ -0,0 +1,370 @@ +From fc0ca25b9f143083528cc5f87dc89fe69baf38fd Mon Sep 17 00:00:00 2001 +From: Simon Pichugin +Date: Thu, 28 Apr 2016 11:49:24 +0200 +Subject: [PATCH 93/93] Ticket 48808 - Add test case + +Description: Add test case for paged results search returns the blank +list of entries issue. + +Bug description: After series of actions, paged result search that +should returns list of entries returns blank list of entries. It is +hardly reproducible manually, but it is easy to reproduce with python +automation. + +https://fedorahosted.org/389/ticket/48808 + +Reviewed by: nhosoi and wbrown (Thanks!) + +(cherry picked from commit 91f3e592713ea58602412ed773a497583f2ebd6c) +(cherry picked from commit 99b5048b09e64cea6f8bf5e7d524679960ce0a44) +--- + dirsrvtests/tests/tickets/ticket48808_test.py | 337 ++++++++++++++++++++++++++ + 1 file changed, 337 insertions(+) + create mode 100644 dirsrvtests/tests/tickets/ticket48808_test.py + +diff --git a/dirsrvtests/tests/tickets/ticket48808_test.py b/dirsrvtests/tests/tickets/ticket48808_test.py +new file mode 100644 +index 0000000..3dbceac +--- /dev/null ++++ b/dirsrvtests/tests/tickets/ticket48808_test.py +@@ -0,0 +1,337 @@ ++import time ++import ldap ++import logging ++import pytest ++from random import sample ++from ldap.controls import SimplePagedResultsControl ++from lib389 import DirSrv, Entry, tools, tasks ++from lib389.tools import DirSrvTools ++from lib389._constants import * ++from lib389.properties import * ++from lib389.tasks import * ++from lib389.utils import * ++ ++logging.getLogger(__name__).setLevel(logging.DEBUG) ++log = logging.getLogger(__name__) ++ ++TEST_USER_NAME = 'simplepaged_test' ++TEST_USER_DN = 'uid=%s,%s' % (TEST_USER_NAME, DEFAULT_SUFFIX) ++TEST_USER_PWD = 'simplepaged_test' ++ ++ ++class TopologyStandalone(object): ++ def __init__(self, standalone): ++ standalone.open() ++ self.standalone = standalone ++ ++ ++@pytest.fixture(scope="module") ++def topology(request): ++ # Creating standalone instance ... ++ standalone = DirSrv(verbose=False) ++ args_instance[SER_HOST] = HOST_STANDALONE ++ args_instance[SER_PORT] = PORT_STANDALONE ++ args_instance[SER_SERVERID_PROP] = SERVERID_STANDALONE ++ args_instance[SER_CREATION_SUFFIX] = DEFAULT_SUFFIX ++ args_standalone = args_instance.copy() ++ standalone.allocate(args_standalone) ++ instance_standalone = standalone.exists() ++ if instance_standalone: ++ standalone.delete() ++ standalone.create() ++ standalone.open() ++ ++ # Delete each instance in the end ++ def fin(): ++ standalone.delete() ++ request.addfinalizer(fin) ++ ++ # Clear out the tmp dir ++ standalone.clearTmpDir(__file__) ++ ++ return TopologyStandalone(standalone) ++ ++ ++@pytest.fixture(scope="module") ++def test_user(topology): ++ """User for binding operation""" ++ ++ try: ++ topology.standalone.add_s(Entry((TEST_USER_DN, { ++ 'objectclass': 'top person'.split(), ++ 'objectclass': 'organizationalPerson', ++ 'objectclass': 'inetorgperson', ++ 'cn': TEST_USER_NAME, ++ 'sn': TEST_USER_NAME, ++ 'userpassword': TEST_USER_PWD, ++ 'mail': '%s@redhat.com' % TEST_USER_NAME, ++ 'uid': TEST_USER_NAME ++ }))) ++ except ldap.LDAPError as e: ++ log.error('Failed to add user (%s): error (%s)' % (TEST_USER_DN, ++ e.message['desc'])) ++ raise e ++ ++ ++def add_users(topology, users_num): ++ """Add users to the default suffix ++ and return a list of added user DNs. ++ """ ++ ++ users_list = [] ++ log.info('Adding %d users' % users_num) ++ for num in sample(range(1000), users_num): ++ num_ran = int(round(num)) ++ USER_NAME = 'test%05d' % num_ran ++ USER_DN = 'uid=%s,%s' % (USER_NAME, DEFAULT_SUFFIX) ++ users_list.append(USER_DN) ++ try: ++ topology.standalone.add_s(Entry((USER_DN, { ++ 'objectclass': 'top person'.split(), ++ 'objectclass': 'organizationalPerson', ++ 'objectclass': 'inetorgperson', ++ 'cn': USER_NAME, ++ 'sn': USER_NAME, ++ 'userpassword': 'pass%s' % num_ran, ++ 'mail': '%s@redhat.com' % USER_NAME, ++ 'uid': USER_NAME ++ }))) ++ except ldap.LDAPError as e: ++ log.error('Failed to add user (%s): error (%s)' % (USER_DN, ++ e.message['desc'])) ++ raise e ++ return users_list ++ ++ ++def del_users(topology, users_list): ++ """Delete users with DNs from given list""" ++ ++ log.info('Deleting %d users' % len(users_list)) ++ for user_dn in users_list: ++ try: ++ topology.standalone.delete_s(user_dn) ++ except ldap.LDAPError as e: ++ log.error('Failed to delete user (%s): error (%s)' % (user_dn, ++ e.message['desc'])) ++ raise e ++ ++ ++def change_conf_attr(topology, suffix, attr_name, attr_value): ++ """Change configurational attribute in the given suffix. ++ Funtion returns previous attribute value. ++ """ ++ ++ try: ++ entries = topology.standalone.search_s(suffix, ldap.SCOPE_BASE, ++ 'objectclass=top', ++ [attr_name]) ++ attr_value_bck = entries[0].data.get(attr_name) ++ log.info('Set %s to %s. Previous value - %s. Modified suffix - %s.' % ( ++ attr_name, attr_value, attr_value_bck, suffix)) ++ if attr_value is None: ++ topology.standalone.modify_s(suffix, [(ldap.MOD_DELETE, ++ attr_name, ++ attr_value)]) ++ else: ++ topology.standalone.modify_s(suffix, [(ldap.MOD_REPLACE, ++ attr_name, ++ attr_value)]) ++ except ldap.LDAPError as e: ++ log.error('Failed to change attr value (%s): error (%s)' % (attr_name, ++ e.message['desc'])) ++ raise e ++ ++ return attr_value_bck ++ ++ ++def paged_search(topology, controls, search_flt, searchreq_attrlist): ++ """Search at the DEFAULT_SUFFIX with ldap.SCOPE_SUBTREE ++ using Simple Paged Control(should the first item in the ++ list controls. ++ Return the list with results summarized from all pages ++ """ ++ ++ pages = 0 ++ pctrls = [] ++ all_results = [] ++ req_ctrl = controls[0] ++ msgid = topology.standalone.search_ext(DEFAULT_SUFFIX, ++ ldap.SCOPE_SUBTREE, ++ search_flt, ++ searchreq_attrlist, ++ serverctrls=controls) ++ while True: ++ log.info('Getting page %d' % (pages,)) ++ rtype, rdata, rmsgid, rctrls = topology.standalone.result3(msgid) ++ all_results.extend(rdata) ++ pages += 1 ++ pctrls = [ ++ c ++ for c in rctrls ++ if c.controlType == SimplePagedResultsControl.controlType ++ ] ++ ++ if pctrls: ++ if pctrls[0].cookie: ++ # Copy cookie from response control to request control ++ req_ctrl.cookie = pctrls[0].cookie ++ msgid = topology.standalone.search_ext(DEFAULT_SUFFIX, ++ ldap.SCOPE_SUBTREE, ++ search_flt, ++ searchreq_attrlist, ++ serverctrls=controls) ++ else: ++ break # no more pages available ++ else: ++ break ++ ++ assert not pctrls[0].cookie ++ return all_results ++ ++ ++def test_ticket48808(topology, test_user): ++ log.info('Run multiple paging controls on a single connection') ++ users_num = 100 ++ page_size = 30 ++ users_list = add_users(topology, users_num) ++ search_flt = r'(uid=test*)' ++ searchreq_attrlist = ['dn', 'sn'] ++ ++ log.info('Set user bind') ++ topology.standalone.simple_bind_s(TEST_USER_DN, TEST_USER_PWD) ++ ++ log.info('Create simple paged results control instance') ++ req_ctrl = SimplePagedResultsControl(True, size=page_size, cookie='') ++ controls = [req_ctrl] ++ ++ for ii in xrange(3): ++ log.info('Iteration %d' % ii) ++ msgid = topology.standalone.search_ext(DEFAULT_SUFFIX, ++ ldap.SCOPE_SUBTREE, ++ search_flt, ++ searchreq_attrlist, ++ serverctrls=controls) ++ rtype, rdata, rmsgid, rctrls = topology.standalone.result3(msgid) ++ pctrls = [ ++ c ++ for c in rctrls ++ if c.controlType == SimplePagedResultsControl.controlType ++ ] ++ ++ req_ctrl.cookie = pctrls[0].cookie ++ msgid = topology.standalone.search_ext(DEFAULT_SUFFIX, ++ ldap.SCOPE_SUBTREE, ++ search_flt, ++ searchreq_attrlist, ++ serverctrls=controls) ++ log.info('Set Directory Manager bind back') ++ topology.standalone.simple_bind_s(DN_DM, PASSWORD) ++ del_users(topology, users_list) ++ ++ log.info('Abandon the search') ++ users_num = 10 ++ page_size = 0 ++ users_list = add_users(topology, users_num) ++ search_flt = r'(uid=test*)' ++ searchreq_attrlist = ['dn', 'sn'] ++ ++ log.info('Set user bind') ++ topology.standalone.simple_bind_s(TEST_USER_DN, TEST_USER_PWD) ++ ++ log.info('Create simple paged results control instance') ++ req_ctrl = SimplePagedResultsControl(True, size=page_size, cookie='') ++ controls = [req_ctrl] ++ ++ msgid = topology.standalone.search_ext(DEFAULT_SUFFIX, ++ ldap.SCOPE_SUBTREE, ++ search_flt, ++ searchreq_attrlist, ++ serverctrls=controls) ++ rtype, rdata, rmsgid, rctrls = topology.standalone.result3(msgid) ++ pctrls = [ ++ c ++ for c in rctrls ++ if c.controlType == SimplePagedResultsControl.controlType ++ ] ++ assert not pctrls[0].cookie ++ ++ log.info('Set Directory Manager bind back') ++ topology.standalone.simple_bind_s(DN_DM, PASSWORD) ++ del_users(topology, users_list) ++ ++ log.info("Search should fail with 'nsPagedSizeLimit = 5'" ++ "and 'nsslapd-pagedsizelimit = 15' with 10 users") ++ conf_attr = '15' ++ user_attr = '5' ++ expected_rs = ldap.SIZELIMIT_EXCEEDED ++ users_num = 10 ++ page_size = 10 ++ users_list = add_users(topology, users_num) ++ search_flt = r'(uid=test*)' ++ searchreq_attrlist = ['dn', 'sn'] ++ conf_attr_bck = change_conf_attr(topology, DN_CONFIG, ++ 'nsslapd-pagedsizelimit', conf_attr) ++ user_attr_bck = change_conf_attr(topology, TEST_USER_DN, ++ 'nsPagedSizeLimit', user_attr) ++ ++ log.info('Set user bind') ++ topology.standalone.simple_bind_s(TEST_USER_DN, TEST_USER_PWD) ++ ++ log.info('Create simple paged results control instance') ++ req_ctrl = SimplePagedResultsControl(True, size=page_size, cookie='') ++ controls = [req_ctrl] ++ ++ log.info('Expect to fail with SIZELIMIT_EXCEEDED') ++ with pytest.raises(expected_rs): ++ all_results = paged_search(topology, controls, ++ search_flt, searchreq_attrlist) ++ ++ log.info('Set Directory Manager bind back') ++ topology.standalone.simple_bind_s(DN_DM, PASSWORD) ++ del_users(topology, users_list) ++ change_conf_attr(topology, DN_CONFIG, ++ 'nsslapd-pagedsizelimit', conf_attr_bck) ++ change_conf_attr(topology, TEST_USER_DN, ++ 'nsPagedSizeLimit', user_attr_bck) ++ ++ log.info("Search should pass with 'nsPagedSizeLimit = 15'" ++ "and 'nsslapd-pagedsizelimit = 5' with 10 users") ++ conf_attr = '5' ++ user_attr = '15' ++ users_num = 10 ++ page_size = 10 ++ users_list = add_users(topology, users_num) ++ search_flt = r'(uid=test*)' ++ searchreq_attrlist = ['dn', 'sn'] ++ conf_attr_bck = change_conf_attr(topology, DN_CONFIG, ++ 'nsslapd-pagedsizelimit', conf_attr) ++ user_attr_bck = change_conf_attr(topology, TEST_USER_DN, ++ 'nsPagedSizeLimit', user_attr) ++ ++ log.info('Set user bind') ++ topology.standalone.simple_bind_s(TEST_USER_DN, TEST_USER_PWD) ++ ++ log.info('Create simple paged results control instance') ++ req_ctrl = SimplePagedResultsControl(True, size=page_size, cookie='') ++ controls = [req_ctrl] ++ ++ log.info('Search should PASS') ++ all_results = paged_search(topology, controls, ++ search_flt, searchreq_attrlist) ++ log.info('%d results' % len(all_results)) ++ assert len(all_results) == len(users_list) ++ ++ log.info('Set Directory Manager bind back') ++ topology.standalone.simple_bind_s(DN_DM, PASSWORD) ++ del_users(topology, users_list) ++ change_conf_attr(topology, DN_CONFIG, ++ 'nsslapd-pagedsizelimit', conf_attr_bck) ++ change_conf_attr(topology, TEST_USER_DN, ++ 'nsPagedSizeLimit', user_attr_bck) ++ ++ ++if __name__ == '__main__': ++ # Run isolated ++ # -s for DEBUG mode ++ CURRENT_FILE = os.path.realpath(__file__) ++ pytest.main("-s %s" % CURRENT_FILE) +-- +2.4.11 + diff --git a/SOURCES/0094-Ticket-48862-At-startup-DES-to-AES-password-conversi.patch b/SOURCES/0094-Ticket-48862-At-startup-DES-to-AES-password-conversi.patch new file mode 100644 index 0000000..8f1ea00 --- /dev/null +++ b/SOURCES/0094-Ticket-48862-At-startup-DES-to-AES-password-conversi.patch @@ -0,0 +1,936 @@ +From 72562d03b0f758902e0ee858fd43d5bcfbef379b Mon Sep 17 00:00:00 2001 +From: Mark Reynolds +Date: Tue, 7 Jun 2016 10:02:42 -0400 +Subject: [PATCH] Ticket 48862 - At startup DES to AES password conversion + causes timeout in start script + +Bug Description: At server start all the backends are searches for entries that contain + DES password attributes as defined in the plugin. These are typically + unindexed searches, and if there is a very large backend this can cause + the server startup to timeout. + +Fix Description: At startup only check "cn=config" for entries with DES password attributes. + A new "conversion" task has been created that can be run after startup + to search all backends(if a suffix is not specified), or specific backends. + + dn: cn=convertPasswords, cn=des2aes,cn=tasks,cn=config + objectclass: top + objectclass: extensibleObject + suffix: dc=example,dc=com + suffix: dc=other,dc=suffix + + Another bug was discovered in pw_rever_encode() in pw.c where a "for" loop + counter was accidentially reused by a second "for" loop. This could lead + to an infinite loop/hang. + + Updated the CI test to perform the conversion task. + +https://fedorahosted.org/389/ticket/48862 + +Reviewed by: nhosoi(Thanks!) + +(cherry picked from commit 11f55f3dd2a2c44ddf7b5be54273401add13b1bc) +(cherry picked from commit c0ad918939c40779e463b71c41ab106e7ee890e2) +--- + dirsrvtests/tickets/ticket47462_test.py | 133 ++++++++---- + ldap/servers/slapd/daemon.c | 195 +++++------------- + ldap/servers/slapd/pw.c | 4 +- + ldap/servers/slapd/task.c | 346 +++++++++++++++++++++++++++++++- + 4 files changed, 493 insertions(+), 185 deletions(-) + +diff --git a/dirsrvtests/tickets/ticket47462_test.py b/dirsrvtests/tickets/ticket47462_test.py +index 17854fa..2d2c507 100644 +--- a/dirsrvtests/tickets/ticket47462_test.py ++++ b/dirsrvtests/tickets/ticket47462_test.py +@@ -32,6 +32,7 @@ AGMT_DN = '' + USER_DN = 'cn=test_user,' + DEFAULT_SUFFIX + USER1_DN = 'cn=test_user1,' + DEFAULT_SUFFIX + TEST_REPL_DN = 'cn=test repl,' + DEFAULT_SUFFIX ++DES2AES_TASK_DN = 'cn=convert,cn=des2aes,cn=tasks,cn=config' + + + class TopologyMaster1Master2(object): +@@ -134,6 +135,11 @@ def topology(request): + # clear the tmp directory + master1.clearTmpDir(__file__) + ++ def fin(): ++ master1.delete() ++ master2.delete() ++ request.addfinalizer(fin) ++ + return TopologyMaster1Master2(master1, master2) + + +@@ -144,11 +150,9 @@ def test_ticket47462(topology): + """ + + # +- # First set config as if it's an older version. Set DES to use libdes-plugin, +- # MMR to depend on DES, delete the existing AES plugin, and set a DES password +- # for the replication agreement. +- # +- ++ # First set config as if it's an older version. Set DES to use ++ # libdes-plugin, MMR to depend on DES, delete the existing AES plugin, ++ # and set a DES password for the replication agreement. + # + # Add an extra attribute to the DES plugin args + # +@@ -168,7 +172,9 @@ def test_ticket47462(topology): + + try: + topology.master1.modify_s(MMR_PLUGIN, +- [(ldap.MOD_DELETE, 'nsslapd-plugin-depends-on-named', 'AES')]) ++ [(ldap.MOD_DELETE, ++ 'nsslapd-plugin-depends-on-named', ++ 'AES')]) + + except ldap.NO_SUCH_ATTRIBUTE: + pass +@@ -194,7 +200,8 @@ def test_ticket47462(topology): + # Get the agmt dn, and set the password + # + try: +- entry = topology.master1.search_s('cn=config', ldap.SCOPE_SUBTREE, 'objectclass=nsDS5ReplicationAgreement') ++ entry = topology.master1.search_s('cn=config', ldap.SCOPE_SUBTREE, ++ 'objectclass=nsDS5ReplicationAgreement') + if entry: + agmt_dn = entry[0].dn + log.info('Found agmt dn (%s)' % agmt_dn) +@@ -207,7 +214,8 @@ def test_ticket47462(topology): + + try: + properties = {RA_BINDPW: "password"} +- topology.master1.agreement.setProperties(None, agmt_dn, None, properties) ++ topology.master1.agreement.setProperties(None, agmt_dn, None, ++ properties) + log.info('Successfully modified replication agreement') + except ValueError: + log.error('Failed to update replica agreement: ' + AGMT_DN) +@@ -220,12 +228,14 @@ def test_ticket47462(topology): + topology.master1.add_s(Entry((USER1_DN, + {'objectclass': "top person".split(), + 'sn': 'sn', ++ 'description': 'DES value to convert', + 'cn': 'test_user'}))) + loop = 0 + ent = None + while loop <= 10: + try: +- ent = topology.master2.getEntry(USER1_DN, ldap.SCOPE_BASE, "(objectclass=*)") ++ ent = topology.master2.getEntry(USER1_DN, ldap.SCOPE_BASE, ++ "(objectclass=*)") + break + except ldap.NO_SUCH_OBJECT: + time.sleep(1) +@@ -250,7 +260,8 @@ def test_ticket47462(topology): + # Check that the restart converted existing DES credentials + # + try: +- entry = topology.master1.search_s('cn=config', ldap.SCOPE_SUBTREE, 'nsDS5ReplicaCredentials=*') ++ entry = topology.master1.search_s('cn=config', ldap.SCOPE_SUBTREE, ++ 'nsDS5ReplicaCredentials=*') + if entry: + val = entry[0].getValue('nsDS5ReplicaCredentials') + if val.startswith('{AES-'): +@@ -259,22 +270,25 @@ def test_ticket47462(topology): + log.fatal('Failed to convert credentials from DES to AES!') + assert False + else: +- log.fatal('Failed to find any entries with nsDS5ReplicaCredentials ') ++ log.fatal('Failed to find entries with nsDS5ReplicaCredentials') + assert False + except ldap.LDAPError, e: + log.fatal('Failed to search for replica credentials: ' + e.message['desc']) + assert False + + # +- # Check that the AES plugin exists, and has all the attributes listed in DES plugin. +- # The attributes might not be in the expected order so check all the attributes. ++ # Check that the AES plugin exists, and has all the attributes listed in ++ # DES plugin. The attributes might not be in the expected order so check ++ # all the attributes. + # + try: +- entry = topology.master1.search_s(AES_PLUGIN, ldap.SCOPE_BASE, 'objectclass=*') ++ entry = topology.master1.search_s(AES_PLUGIN, ldap.SCOPE_BASE, ++ 'objectclass=*') + if not entry[0].hasValue('nsslapd-pluginarg0', 'description') and \ + not entry[0].hasValue('nsslapd-pluginarg1', 'description') and \ + not entry[0].hasValue('nsslapd-pluginarg2', 'description'): +- log.fatal('The AES plugin did not have the DES attribute copied over correctly') ++ log.fatal('The AES plugin did not have the DES attribute copied ' + ++ 'over correctly') + assert False + else: + log.info('The AES plugin was correctly setup') +@@ -286,7 +300,8 @@ def test_ticket47462(topology): + # Check that the MMR plugin was updated + # + try: +- entry = topology.master1.search_s(MMR_PLUGIN, ldap.SCOPE_BASE, 'objectclass=*') ++ entry = topology.master1.search_s(MMR_PLUGIN, ldap.SCOPE_BASE, ++ 'objectclass=*') + if not entry[0].hasValue('nsslapd-plugin-depends-on-named', 'AES'): + log.fatal('The MMR Plugin was not correctly updated') + assert False +@@ -300,7 +315,8 @@ def test_ticket47462(topology): + # Check that the DES plugin was correctly updated + # + try: +- entry = topology.master1.search_s(DES_PLUGIN, ldap.SCOPE_BASE, 'objectclass=*') ++ entry = topology.master1.search_s(DES_PLUGIN, ldap.SCOPE_BASE, ++ 'objectclass=*') + if not entry[0].hasValue('nsslapd-pluginPath', 'libpbe-plugin'): + log.fatal('The DES Plugin was not correctly updated') + assert False +@@ -322,7 +338,8 @@ def test_ticket47462(topology): + ent = None + while loop <= 10: + try: +- ent = topology.master2.getEntry(USER_DN, ldap.SCOPE_BASE, "(objectclass=*)") ++ ent = topology.master2.getEntry(USER_DN, ldap.SCOPE_BASE, ++ "(objectclass=*)") + break + except ldap.NO_SUCH_OBJECT: + time.sleep(1) +@@ -336,30 +353,66 @@ def test_ticket47462(topology): + log.fatal('Failed to add test user: ' + e.message['desc']) + assert False + ++ # Check the entry ++ log.info('Entry before running task...') ++ try: ++ entry = topology.master1.search_s(USER1_DN, ++ ldap.SCOPE_BASE, ++ 'objectclass=*') ++ if entry: ++ print(str(entry)) ++ else: ++ log.fatal('Failed to find entries') ++ assert False ++ except ldap.LDAPError as e: ++ log.fatal('Failed to search for entries: ' + ++ e.message['desc']) ++ assert False + +-def test_ticket47462_final(topology): +- topology.master1.delete() +- topology.master2.delete() +- log.info('Testcase PASSED') +- +- +-def run_isolated(): +- ''' +- run_isolated is used to run these test cases independently of a test scheduler (xunit, py.test..) +- To run isolated without py.test, you need to +- - edit this file and comment '@pytest.fixture' line before 'topology' function. +- - set the installation prefix +- - run this program +- ''' +- global installation1_prefix +- global installation2_prefix +- installation1_prefix = None +- installation2_prefix = None ++ # ++ # Test the DES2AES Task on USER1_DN ++ # ++ try: ++ topology.master1.add_s(Entry((DES2AES_TASK_DN, ++ {'objectclass': ['top', ++ 'extensibleObject'], ++ 'suffix': DEFAULT_SUFFIX, ++ 'cn': 'convert'}))) ++ except ldap.LDAPError as e: ++ log.fatal('Failed to add task entry: ' + e.message['desc']) ++ assert False + +- topo = topology(True) +- test_ticket47462(topo) +- test_ticket47462_final(topo) ++ # Wait for task ++ task_entry = Entry(DES2AES_TASK_DN) ++ (done, exitCode) = topology.master1.tasks.checkTask(task_entry, True) ++ if exitCode: ++ log.fatal("Error: des2aes task exited with %d" % (exitCode)) ++ assert False + ++ # Check the entry ++ try: ++ entry = topology.master1.search_s(USER1_DN, ++ ldap.SCOPE_BASE, ++ 'objectclass=*') ++ if entry: ++ val = entry[0].getValue('description') ++ print(str(entry[0])) ++ if val.startswith('{AES-'): ++ log.info('Task: DES credentials have been converted to AES') ++ else: ++ log.fatal('Task: Failed to convert credentials from DES to ' + ++ 'AES! (%s)' % (val)) ++ assert False ++ else: ++ log.fatal('Failed to find entries') ++ assert False ++ except ldap.LDAPError as e: ++ log.fatal('Failed to search for entries: ' + ++ e.message['desc']) ++ assert False + + if __name__ == '__main__': +- run_isolated() ++ # Run isolated ++ # -s for DEBUG mode ++ CURRENT_FILE = os.path.realpath(__file__) ++ pytest.main("-s %s" % CURRENT_FILE) +diff --git a/ldap/servers/slapd/daemon.c b/ldap/servers/slapd/daemon.c +index d702129..29562ae 100644 +--- a/ldap/servers/slapd/daemon.c ++++ b/ldap/servers/slapd/daemon.c +@@ -692,14 +692,12 @@ convert_pbe_des_to_aes() + Slapi_Entry **entries = NULL; + struct slapdplugin *plugin = NULL; + char **attrs = NULL; +- char **backends = NULL; + char *val = NULL; + int converted_des_passwd = 0; +- int disable_des = 1; + int result = -1; + int have_aes = 0; + int have_des = 0; +- int i = 0, ii = 0, be_idx = 0; ++ int i = 0, ii = 0; + + /* + * Check that AES plugin is enabled, and grab all the unique +@@ -733,94 +731,56 @@ convert_pbe_des_to_aes() + + if(have_aes && have_des){ + /* +- * Build a list of all the backend dn's ++ * Find any entries in cn=config that contain DES passwords and convert ++ * them to AES + */ +- Slapi_Backend *be = NULL; +- struct suffixlist *list; +- char *cookie = NULL; +- +- LDAPDebug(LDAP_DEBUG_ANY, "convert_pbe_des_to_aes: " +- "Checking for DES passwords to convert to AES...\n",0,0,0); +- +- be = slapi_get_first_backend(&cookie); +- while (be){ +- int suffix_idx = 0; +- int count = slapi_counter_get_value(be->be_suffixcounter); +- +- list = be->be_suffixlist; +- for (suffix_idx = 0; list && suffix_idx < count; suffix_idx++) { +- char *suffix = (char *)slapi_sdn_get_ndn(list->be_suffix); +- if(charray_inlist(backends, suffix) || strlen(suffix) == 0){ +- list = list->next; +- continue; +- } +- charray_add(&backends, slapi_ch_strdup(suffix)); +- list = list->next; +- } +- be = slapi_get_next_backend (cookie); +- } +- slapi_ch_free ((void **)&cookie); ++ slapi_log_error(SLAPI_LOG_HOUSE, "convert_pbe_des_to_aes", ++ "Converting DES passwords to AES...\n"); + +- /* +- * Search for the password attributes +- */ + for (i = 0; attrs && attrs[i]; i++){ + char *filter = PR_smprintf("%s=*", attrs[i]); +- /* +- * Loop over all the backends looking for the password attribute +- */ +- for(be_idx = 0; backends && backends[be_idx]; be_idx++){ +- pb = slapi_pblock_new(); +- slapi_search_internal_set_pb(pb, backends[be_idx], +- LDAP_SCOPE_SUBTREE, filter, NULL, 0, NULL, NULL, +- (void *)plugin_get_default_component_id(), +- SLAPI_OP_FLAG_IGNORE_UNINDEXED); +- slapi_search_internal_pb(pb); +- slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_RESULT, &result); +- if (LDAP_SUCCESS != result) { +- slapi_log_error(SLAPI_LOG_TRACE, "convert_pbe_des_to_aes: ", +- "Failed to search for password attribute (%s) error (%d), skipping suffix (%s)\n", +- attrs[i], result, backends[be_idx]); +- slapi_free_search_results_internal(pb); +- slapi_pblock_destroy(pb); +- pb = NULL; +- continue; +- } +- slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, &entries); +- for (ii = 0; entries && entries[ii]; ii++){ +- if((val = slapi_entry_attr_get_charptr(entries[ii], attrs[i]))){ +- if(strlen(val) >= 5 && strncmp(val,"{DES}", 5) == 0){ +- /* +- * We have a DES encoded password, convert it AES +- */ +- Slapi_PBlock *mod_pb = NULL; +- Slapi_Value *sval = NULL; +- LDAPMod mod_replace; +- LDAPMod *mods[2]; +- char *replace_val[2]; +- char *passwd = NULL; +- +- /* decode the DES password */ +- if(pw_rever_decode(val, &passwd, attrs[i]) == -1){ +- LDAPDebug(LDAP_DEBUG_ANY,"convert_pbe_des_to_aes: " +- "Failed to decode existing DES password for (%s)\n", +- slapi_entry_get_dn(entries[ii]), 0, 0); +- disable_des = 0; +- goto done; +- } + +- /* encode the password */ ++ pb = slapi_pblock_new(); ++ slapi_search_internal_set_pb(pb, "cn=config", ++ LDAP_SCOPE_SUBTREE, filter, NULL, 0, NULL, NULL, ++ (void *)plugin_get_default_component_id(), ++ SLAPI_OP_FLAG_IGNORE_UNINDEXED); ++ slapi_search_internal_pb(pb); ++ slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, &entries); ++ for (ii = 0; entries && entries[ii]; ii++){ ++ if((val = slapi_entry_attr_get_charptr(entries[ii], attrs[i]))){ ++ if(strlen(val) >= 5 && strncmp(val,"{DES}", 5) == 0){ ++ /* ++ * We have a DES encoded password, convert it to AES ++ */ ++ Slapi_PBlock *mod_pb = NULL; ++ Slapi_Value *sval = NULL; ++ LDAPMod mod_replace; ++ LDAPMod *mods[2]; ++ char *replace_val[2]; ++ char *passwd = NULL; ++ int rc = 0; ++ ++ /* decode the DES password */ ++ if(pw_rever_decode(val, &passwd, attrs[i]) == -1){ ++ slapi_log_error(SLAPI_LOG_FATAL ,"convert_pbe_des_to_aes", ++ "Failed to decode existing DES password for (%s)\n", ++ slapi_entry_get_dn(entries[ii])); ++ rc = -1; ++ } ++ ++ /* encode the password */ ++ if (rc == 0){ + sval = slapi_value_new_string(passwd); + if(pw_rever_encode(&sval, attrs[i]) == -1){ +- LDAPDebug(LDAP_DEBUG_ANY,"convert_pbe_des_to_aes: " ++ slapi_log_error(SLAPI_LOG_FATAL, "convert_pbe_des_to_aes", + "failed to encode AES password for (%s)\n", +- slapi_entry_get_dn(entries[ii]), 0, 0); +- slapi_ch_free_string(&passwd); +- slapi_value_free(&sval); +- disable_des = 0; +- goto done; ++ slapi_entry_get_dn(entries[ii])); ++ rc = -1; + } ++ } + ++ if (rc == 0){ + /* replace the attribute in the entry */ + replace_val[0] = (char *)slapi_value_get_string(sval); + replace_val[1] = NULL; +@@ -837,83 +797,34 @@ convert_pbe_des_to_aes() + + slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_RESULT, &result); + if (LDAP_SUCCESS != result) { +- LDAPDebug(LDAP_DEBUG_ANY,"convert_pbe_des_to_aes: " ++ slapi_log_error(SLAPI_LOG_FATAL, "convert_pbe_des_to_aes" + "Failed to convert password for (%s) error (%d)\n", +- slapi_entry_get_dn(entries[ii]), result, 0); +- disable_des = 0; ++ slapi_entry_get_dn(entries[ii]), result); + } else { +- LDAPDebug(LDAP_DEBUG_ANY,"convert_pbe_des_to_aes: " ++ slapi_log_error(SLAPI_LOG_HOUSE, "convert_pbe_des_to_aes", + "Successfully converted password for (%s)\n", +- slapi_entry_get_dn(entries[ii]), result, 0); ++ slapi_entry_get_dn(entries[ii])); + converted_des_passwd = 1; + } +- slapi_ch_free_string(&passwd); +- slapi_value_free(&sval); +- slapi_pblock_destroy(mod_pb); + } +- slapi_ch_free_string(&val); ++ slapi_ch_free_string(&passwd); ++ slapi_value_free(&sval); ++ slapi_pblock_destroy(mod_pb); + } ++ slapi_ch_free_string(&val); + } +- slapi_free_search_results_internal(pb); +- slapi_pblock_destroy(pb); +- pb = NULL; + } ++ slapi_free_search_results_internal(pb); ++ slapi_pblock_destroy(pb); ++ pb = NULL; + slapi_ch_free_string(&filter); + } + if (!converted_des_passwd){ +- slapi_log_error(SLAPI_LOG_FATAL, "convert_pbe_des_to_aes", ++ slapi_log_error(SLAPI_LOG_HOUSE, "convert_pbe_des_to_aes", + "No DES passwords found to convert.\n"); + } + } +- +-done: + charray_free(attrs); +- charray_free(backends); +- slapi_free_search_results_internal(pb); +- slapi_pblock_destroy(pb); +- +- if (have_aes && have_des){ +- /* +- * If a conversion attempt did not fail then we can disable the DES plugin +- */ +- if(converted_des_passwd && disable_des){ +- /* +- * Disable the DES plugin - this also prevents potentially expensive +- * searches at every server startup. +- */ +- LDAPMod mod_replace; +- LDAPMod *mods[2]; +- char *replace_val[2]; +- char *des_dn = "cn=DES,cn=Password Storage Schemes,cn=plugins,cn=config"; +- +- replace_val[0] = "off"; +- replace_val[1] = NULL; +- mod_replace.mod_op = LDAP_MOD_REPLACE; +- mod_replace.mod_type = "nsslapd-pluginEnabled"; +- mod_replace.mod_values = replace_val; +- mods[0] = &mod_replace; +- mods[1] = 0; +- +- pb = slapi_pblock_new(); +- slapi_modify_internal_set_pb(pb, des_dn, mods, 0, 0, +- (void *)plugin_get_default_component_id(), 0); +- slapi_modify_internal_pb(pb); +- slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_RESULT, &result); +- if (LDAP_SUCCESS != result) { +- LDAPDebug(LDAP_DEBUG_ANY,"convert_pbe_des_to_aes: " +- "Failed to disable DES plugin (%s), error (%d)\n", +- des_dn, result, 0); +- } else { +- LDAPDebug(LDAP_DEBUG_ANY,"convert_pbe_des_to_aes: " +- "Successfully disabled DES plugin (%s)\n", +- des_dn, 0, 0); +- } +- slapi_pblock_destroy(pb); +- LDAPDebug(LDAP_DEBUG_ANY,"convert_pbe_des_to_aes: " +- "All DES passwords have been converted to AES.\n", +- 0, 0, 0); +- } +- } + } + + #ifdef ENABLE_NUNC_STANS +diff --git a/ldap/servers/slapd/pw.c b/ldap/servers/slapd/pw.c +index 4e222d7..883ef80 100644 +--- a/ldap/servers/slapd/pw.c ++++ b/ldap/servers/slapd/pw.c +@@ -516,10 +516,10 @@ pw_rever_encode(Slapi_Value **vals, char * attr_name) + for ( p = get_plugin_list(PLUGIN_LIST_REVER_PWD_STORAGE_SCHEME); p != NULL; p = p->plg_next ) + { + char *L_attr = NULL; +- int i = 0; ++ int i = 0, ii = 0; + + /* Get the appropriate encoding function */ +- for ( L_attr = p->plg_argv[i]; iplg_argc; L_attr = p->plg_argv[++i] ) ++ for ( L_attr = p->plg_argv[ii]; iiplg_argc; L_attr = p->plg_argv[++ii] ) + { + if (slapi_attr_types_equivalent(L_attr, attr_name)) + { +diff --git a/ldap/servers/slapd/task.c b/ldap/servers/slapd/task.c +index db3c222..405f0bf 100644 +--- a/ldap/servers/slapd/task.c ++++ b/ldap/servers/slapd/task.c +@@ -53,6 +53,8 @@ static int shutting_down = 0; + #define TASK_TOMBSTONE_FIXUP_BACKEND "backend" + #define TASK_TOMBSTONE_FIXUP_SUFFIX "suffix" + #define TASK_TOMBSTONE_FIXUP_STRIPCSN "stripcsn" ++#define TASK_DES2AES "des2aes task" ++ + + #define LOG_BUFFER 256 + /* if the cumul. log gets larger than this, it's truncated: */ +@@ -83,8 +85,10 @@ static const char *fetch_attr(Slapi_Entry *e, const char *attrname, + const char *default_val); + static Slapi_Entry *get_internal_entry(Slapi_PBlock *pb, char *dn); + static void modify_internal_entry(char *dn, LDAPMod **mods); +- + static void fixup_tombstone_task_destructor(Slapi_Task *task); ++static void task_des2aes_thread(void *arg); ++static void des2aes_task_destructor(Slapi_Task *task); ++ + + /*********************************** + * Public Functions +@@ -2425,6 +2429,345 @@ fixup_tombstone_task_destructor(Slapi_Task *task) + "fixup_tombstone_task_destructor <--\n" ); + } + ++/* ++ * des2aes Task ++ * ++ * Convert any DES passwords to AES ++ * ++ * dn: cn=convertPasswords, cn=des2aes,cn=tasks,cn=config ++ * objectclass: top ++ * objectclass: extensibleObject ++ * suffix: dc=example,dc=com (If empty all backends are checked) ++ * suffix: dc=other,dc=suffix ++ */ ++struct task_des2aes_data ++{ ++ char **suffixes; ++ Slapi_Task *task; ++}; ++ ++static int ++task_des2aes(Slapi_PBlock *pb, Slapi_Entry *e, Slapi_Entry *eAfter, ++ int *returncode, char *returntext, void *arg) ++{ ++ struct task_des2aes_data *task_data = NULL; ++ PRThread *thread = NULL; ++ Slapi_Task *task = NULL; ++ char **suffix = NULL; ++ char **bases = NULL; ++ int rc = SLAPI_DSE_CALLBACK_OK; ++ ++ /* Get the suffixes */ ++ if((suffix = slapi_entry_attr_get_charray(e, "suffix"))){ ++ int i; ++ for (i = 0; suffix && suffix[i]; i++){ ++ /* Make sure "suffix" is NUL terminated string */ ++ char *dn = slapi_create_dn_string("%s", suffix[i]); ++ ++ if(dn){ ++ if(slapi_dn_syntax_check(pb, dn, 1)){ ++ /* invalid suffix name */ ++ PR_snprintf(returntext, SLAPI_DSE_RETURNTEXT_SIZE, ++ "Invalid DN syntax (%s) specified for \"suffix\"\n", ++ suffix[i]); ++ *returncode = LDAP_INVALID_DN_SYNTAX; ++ slapi_ch_free_string(&dn); ++ rc = SLAPI_DSE_CALLBACK_ERROR; ++ goto error; ++ } else { ++ slapi_ch_array_add(&bases, dn); ++ } ++ } else{ ++ PR_snprintf(returntext, SLAPI_DSE_RETURNTEXT_SIZE, ++ "Invalid DN (%s) specified for \"suffix\"\n", suffix[i]); ++ *returncode = LDAP_INVALID_DN_SYNTAX; ++ rc = SLAPI_DSE_CALLBACK_ERROR; ++ goto error; ++ } ++ } ++ } ++ ++ /* Build the task data and fire off a thread to perform the conversion */ ++ task = slapi_new_task(slapi_entry_get_ndn(e)); ++ ++ /* register our destructor for cleaning up our private data */ ++ slapi_task_set_destructor_fn(task, des2aes_task_destructor); ++ task_data = (struct task_des2aes_data *)slapi_ch_calloc(1, sizeof(struct task_des2aes_data)); ++ task_data->suffixes = bases; ++ task_data->task = task; ++ ++ /* Start the conversion thread */ ++ thread = PR_CreateThread(PR_USER_THREAD, task_des2aes_thread, ++ (void *)task_data, PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD, ++ PR_UNJOINABLE_THREAD, SLAPD_DEFAULT_THREAD_STACKSIZE); ++ if (thread == NULL) { ++ PR_snprintf(returntext, SLAPI_DSE_RETURNTEXT_SIZE, ++ "unable to create des2aes thread!\n"); ++ slapi_log_error(SLAPI_LOG_FATAL, TASK_DES2AES, ++ "unable to create des2aes thread!\n"); ++ *returncode = LDAP_OPERATIONS_ERROR; ++ slapi_task_finish(task, *returncode); ++ rc = SLAPI_DSE_CALLBACK_ERROR; ++ } ++ ++error: ++ if (rc == SLAPI_DSE_CALLBACK_ERROR){ ++ slapi_ch_array_free(bases); ++ slapi_ch_array_free(suffix); ++ slapi_ch_free((void **)&task_data); ++ } ++ return rc; ++} ++ ++static void ++task_des2aes_thread(void *arg) ++{ ++ struct task_des2aes_data *task_data = arg; ++ Slapi_PBlock *pb = NULL; ++ Slapi_Entry **entries = NULL; ++ Slapi_Task *task = task_data->task; ++ struct slapdplugin *plugin = NULL; ++ char **attrs = NULL; ++ char **backends = NULL; ++ char *val = NULL; ++ int converted_des_passwd = 0; ++ int result = -1; ++ int have_aes = 0; ++ int have_des = 0; ++ int i = 0, ii = 0, be_idx = 0; ++ int rc = 0; ++ ++ /* ++ * Check that AES plugin is enabled, and grab all the unique ++ * password attributes. ++ */ ++ for ( plugin = get_plugin_list(PLUGIN_LIST_REVER_PWD_STORAGE_SCHEME); ++ plugin != NULL; ++ plugin = plugin->plg_next ) ++ { ++ char *plugin_arg = NULL; ++ ++ if(plugin->plg_started && strcasecmp(plugin->plg_name, "AES") == 0){ ++ /* We have the AES plugin, and its enabled */ ++ have_aes = 1; ++ } ++ if(plugin->plg_started && strcasecmp(plugin->plg_name, "DES") == 0){ ++ /* We have the DES plugin, and its enabled */ ++ have_des = 1; ++ } ++ /* Gather all the unique password attributes from all the PBE plugins */ ++ for ( i = 0, plugin_arg = plugin->plg_argv[i]; ++ i < plugin->plg_argc; ++ plugin_arg = plugin->plg_argv[++i] ) ++ { ++ if(charray_inlist(attrs, plugin_arg)){ ++ continue; ++ } ++ charray_add(&attrs, slapi_ch_strdup(plugin_arg)); ++ } ++ } ++ ++ if(have_aes && have_des){ ++ if(task_data->suffixes == NULL){ ++ /* ++ * Build a list of all the backend dn's ++ */ ++ Slapi_Backend *be = NULL; ++ struct suffixlist *list; ++ char *cookie = NULL; ++ ++ slapi_log_error(SLAPI_LOG_FATAL, TASK_DES2AES, ++ "Checking for DES passwords to convert to AES...\n"); ++ slapi_task_log_notice(task, ++ "Checking for DES passwords to convert to AES...\n"); ++ ++ be = slapi_get_first_backend(&cookie); ++ while (be){ ++ int suffix_idx = 0; ++ int count = slapi_counter_get_value(be->be_suffixcounter); ++ ++ list = be->be_suffixlist; ++ for (suffix_idx = 0; list && suffix_idx < count; suffix_idx++) { ++ char *suffix = (char *)slapi_sdn_get_ndn(list->be_suffix); ++ if(charray_inlist(backends, suffix) || strlen(suffix) == 0){ ++ list = list->next; ++ continue; ++ } ++ charray_add(&backends, slapi_ch_strdup(suffix)); ++ list = list->next; ++ } ++ be = slapi_get_next_backend (cookie); ++ } ++ slapi_ch_free ((void **)&cookie); ++ } else { ++ backends = task_data->suffixes; ++ } ++ ++ /* ++ * Search for the password attributes ++ */ ++ for (i = 0; attrs && attrs[i]; i++){ ++ char *filter = PR_smprintf("%s=*", attrs[i]); ++ /* ++ * Loop over all the backends looking for the password attribute ++ */ ++ for(be_idx = 0; backends && backends[be_idx]; be_idx++){ ++ pb = slapi_pblock_new(); ++ slapi_search_internal_set_pb(pb, backends[be_idx], ++ LDAP_SCOPE_SUBTREE, filter, NULL, 0, NULL, NULL, ++ (void *)plugin_get_default_component_id(), ++ SLAPI_OP_FLAG_IGNORE_UNINDEXED); ++ slapi_search_internal_pb(pb); ++ slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_RESULT, &result); ++ if (LDAP_SUCCESS != result) { ++ slapi_log_error(SLAPI_LOG_FATAL, "convert_pbe_des_to_aes: ", ++ "Failed to search for password attribute (%s) error (%d), skipping suffix (%s)\n", ++ attrs[i], result, backends[be_idx]); ++ slapi_task_log_notice(task, ++ "Failed to search for password attribute (%s) error (%d), skipping suffix (%s)\n", ++ attrs[i], result, backends[be_idx]); ++ slapi_free_search_results_internal(pb); ++ slapi_pblock_destroy(pb); ++ pb = NULL; ++ continue; ++ } ++ slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, &entries); ++ for (ii = 0; entries && entries[ii]; ii++){ ++ if((val = slapi_entry_attr_get_charptr(entries[ii], attrs[i]))){ ++ if(strlen(val) >= 5 && strncmp(val,"{DES}", 5) == 0){ ++ /* ++ * We have a DES encoded password, convert it AES ++ */ ++ Slapi_PBlock *mod_pb = NULL; ++ Slapi_Value *sval = NULL; ++ LDAPMod mod_replace; ++ LDAPMod *mods[2]; ++ char *replace_val[2]; ++ char *passwd = NULL; ++ ++ /* Decode the DES password */ ++ if(pw_rever_decode(val, &passwd, attrs[i]) == -1){ ++ slapi_log_error(SLAPI_LOG_FATAL, TASK_DES2AES, ++ "Failed to decode existing DES password for (%s)\n", ++ slapi_entry_get_dn(entries[ii])); ++ slapi_task_log_notice(task, ++ "Failed to decode existing DES password for (%s)\n", ++ slapi_entry_get_dn(entries[ii])); ++ rc = 1; ++ goto done; ++ } ++ ++ /* Encode the password */ ++ sval = slapi_value_new_string(passwd); ++ if(pw_rever_encode(&sval, attrs[i]) == -1){ ++ slapi_log_error(SLAPI_LOG_FATAL, TASK_DES2AES, ++ "failed to encode AES password for (%s)\n", ++ slapi_entry_get_dn(entries[ii])); ++ slapi_task_log_notice(task, ++ "failed to encode AES password for (%s)\n", ++ slapi_entry_get_dn(entries[ii])); ++ slapi_ch_free_string(&passwd); ++ slapi_value_free(&sval); ++ rc = 1; ++ goto done; ++ } ++ ++ /* Replace the attribute in the entry */ ++ replace_val[0] = (char *)slapi_value_get_string(sval); ++ replace_val[1] = NULL; ++ mod_replace.mod_op = LDAP_MOD_REPLACE; ++ mod_replace.mod_type = attrs[i]; ++ mod_replace.mod_values = replace_val; ++ mods[0] = &mod_replace; ++ mods[1] = 0; ++ ++ mod_pb = slapi_pblock_new(); ++ slapi_modify_internal_set_pb(mod_pb, slapi_entry_get_dn(entries[ii]), ++ mods, 0, 0, (void *)plugin_get_default_component_id(), 0); ++ slapi_modify_internal_pb(mod_pb); ++ ++ slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_RESULT, &result); ++ if (LDAP_SUCCESS != result) { ++ slapi_log_error(SLAPI_LOG_FATAL, TASK_DES2AES, ++ "Failed to convert password for (%s) error (%d)\n", ++ slapi_entry_get_dn(entries[ii]), result); ++ slapi_task_log_notice(task, ++ "Failed to convert password for (%s) error (%d)\n", ++ slapi_entry_get_dn(entries[ii]), result); ++ rc = 1; ++ } else { ++ slapi_log_error(SLAPI_LOG_FATAL, TASK_DES2AES, ++ "Successfully converted password for (%s)\n", ++ slapi_entry_get_dn(entries[ii])); ++ slapi_task_log_notice(task, ++ "Successfully converted password for (%s)\n", ++ slapi_entry_get_dn(entries[ii])); ++ converted_des_passwd = 1; ++ } ++ slapi_ch_free_string(&passwd); ++ slapi_value_free(&sval); ++ slapi_pblock_destroy(mod_pb); ++ } ++ slapi_ch_free_string(&val); ++ } ++ } ++ slapi_free_search_results_internal(pb); ++ slapi_pblock_destroy(pb); ++ pb = NULL; ++ } ++ slapi_ch_free_string(&filter); ++ } ++ if (!converted_des_passwd){ ++ slapi_log_error(SLAPI_LOG_FATAL, TASK_DES2AES, ++ "No DES passwords found to convert.\n"); ++ slapi_task_log_notice(task, "No DES passwords found to convert.\n"); ++ } ++ } else { ++ /* No AES/DES */ ++ if (!have_des){ ++ slapi_log_error(SLAPI_LOG_FATAL, TASK_DES2AES, ++ "DES plugin not enabled\n"); ++ slapi_task_log_notice(task, "DES plugin not enabled\n"); ++ } ++ if (!have_aes){ ++ slapi_log_error(SLAPI_LOG_FATAL, TASK_DES2AES, ++ "AES plugin not enabled\n"); ++ slapi_task_log_notice(task, "AES plugin not enabled\n"); ++ } ++ slapi_log_error(SLAPI_LOG_FATAL, TASK_DES2AES, ++ "Unable to convert passwords\n"); ++ slapi_task_log_notice(task, "Unable to convert passwords\n"); ++ rc = 1; ++ } ++ ++done: ++ charray_free(attrs); ++ charray_free(backends); ++ slapi_free_search_results_internal(pb); ++ slapi_pblock_destroy(pb); ++ slapi_task_finish(task, rc); ++} ++ ++static void ++des2aes_task_destructor(Slapi_Task *task) ++{ ++ slapi_log_error(SLAPI_LOG_TRACE, TASK_DES2AES, ++ "des2aes_task_destructor -->\n" ); ++ if (task) { ++ struct task_des2aes_data *task_data = (struct task_des2aes_data *)slapi_task_get_data(task); ++ while (slapi_task_get_refcount(task) > 0) { ++ /* Yield to wait for the task to finish. */ ++ DS_Sleep (PR_MillisecondsToInterval(100)); ++ } ++ if (task_data) { ++ slapi_ch_array_free(task_data->suffixes); ++ slapi_ch_free((void **)&task_data); ++ } ++ } ++ slapi_log_error(SLAPI_LOG_TRACE, TASK_DES2AES, ++ "des2aes_task_destructor <--\n" ); ++} ++ + /* cleanup old tasks that may still be in the DSE from a previous session + * (this can happen if the server crashes [no matter how unlikely we like + * to think that is].) +@@ -2506,6 +2849,7 @@ void task_init(void) + slapi_task_register_handler("upgradedb", task_upgradedb_add); + slapi_task_register_handler("sysconfig reload", task_sysconfig_reload_add); + slapi_task_register_handler("fixup tombstones", task_fixup_tombstones_add); ++ slapi_task_register_handler("des2aes", task_des2aes); + } + + /* called when the server is shutting down -- abort all existing tasks */ +-- +2.4.11 + diff --git a/SPECS/389-ds-base.spec b/SPECS/389-ds-base.spec index 2777cec..b44a6b1 100644 --- a/SPECS/389-ds-base.spec +++ b/SPECS/389-ds-base.spec @@ -34,7 +34,7 @@ Summary: 389 Directory Server (base) Name: 389-ds-base Version: 1.3.4.0 -Release: %{?relprefix}30%{?prerel}%{?dist} +Release: %{?relprefix}32%{?prerel}%{?dist} License: GPLv3+ URL: http://port389.org/ Group: System Environment/Daemons @@ -212,6 +212,11 @@ Patch85: 0086-Ticket-48445-keep-alive-entries-can-break-replicatio.patc Patch86: 0087-Ticket-48420-change-severity-of-some-messages-relate.patch Patch87: 0088-Ticket-48757-License-tag-does-not-match-actual-licen.patch Patch88: 0089-Ticket-47888-DES-to-AES-password-conversion-fails-if.patch +Patch89: 0090-Ticket-48492-heap-corruption-at-schema-replication.patch +Patch90: 0091-Ticket-48492-heap-corruption-at-schema-replication.patch +Patch91: 0092-Ticket-48808-Paged-results-search-returns-the-blank-.patch +Patch92: 0093-Ticket-48808-Add-test-case.patch +Patch93: 0094-Ticket-48862-At-startup-DES-to-AES-password-conversi.patch %description 389 Directory Server is an LDAPv3 compliant server. The base package includes @@ -364,6 +369,11 @@ cp %{SOURCE2} README.devel %patch86 -p1 %patch87 -p1 %patch88 -p1 +%patch89 -p1 +%patch90 -p1 +%patch91 -p1 +%patch92 -p1 +%patch93 -p1 %build %if %{use_nunc_stans} @@ -554,10 +564,19 @@ fi %{_libdir}/%{pkgname}/libslapd.so.* %{_libdir}/%{pkgname}/libns-dshttpd.so* %if %{use_nunc_stans} -%{_libdir}/%{pkgname}/libnunc-stans.so* +%{_libdir}/%{pkgname}/libnunc-stans.so.* %endif %changelog +* Thu Jun 9 2016 Noriko Hosoi - 1.3.4.0-32 +- release 1.3.4.0-32 +- Resolves: bug 1344293 - At startup DES to AES password conversion causes timeout in start script (DS 48862) + +* Thu May 12 2016 Noriko Hosoi - 1.3.4.0-31 +- release 1.3.4.0-31 +- Resolves: bug 1335423 - heap corruption at schema replication. (DS 48492) +- Resolves: bug 1335107 - Paged results search returns the blank list of entries (DS 48808) + * Wed Mar 30 2016 Noriko Hosoi - 1.3.4.0-30 - release 1.3.4.0-30 - Resolves: bug 1321891 - DES to AES password conversion fails if a backend is empty (DS 48777)