From c9e5dad69e2b497f118efac56f43cc6c74b6a695 Mon Sep 17 00:00:00 2001 From: CentOS Sources Date: Apr 12 2017 10:05:23 +0000 Subject: import 389-ds-base-1.3.5.10-20.el7_3 --- diff --git a/SOURCES/0070-Ticket-49121-ns-slapd-crashes-in-ldif_sput-due-to-th.patch b/SOURCES/0070-Ticket-49121-ns-slapd-crashes-in-ldif_sput-due-to-th.patch new file mode 100644 index 0000000..c95e8a0 --- /dev/null +++ b/SOURCES/0070-Ticket-49121-ns-slapd-crashes-in-ldif_sput-due-to-th.patch @@ -0,0 +1,351 @@ +From f03bfc51387fcfe15122ee994626738f71b1935c Mon Sep 17 00:00:00 2001 +From: Noriko Hosoi +Date: Sun, 12 Feb 2017 17:26:46 -0800 +Subject: [PATCH 70/71] Ticket #49121 - ns-slapd crashes in ldif_sput due to + the output buf size is less than the real size. + +Description: There were missing pieces in the entry size calculation +when an attribute had no a_present_values nor a_deleted_values. +1) There was no chance to add the size of the attribute type name since + preceding entry2str_internal_size_valueset did not add any size if + the value was empty. The type name size is now explicitly added. +2) a_deletioncsn is added in entry2str_internal_put_attrlist by calling + valueset_add_string with empty value. The size was not included in + the allocated memory to store the entire entry as a string. Now the + size is added. + +Adding CI test ticket49121_test.py. + +https://pagure.io/389-ds-base/issue/49121 + +Reviewed by wibrown@redhat.com (Thank you, William!!) + +(cherry picked from commit 543fe89edb0a6410a740a4fff738cace7bc57078) +--- + dirsrvtests/tests/data/ticket49121/utf8str.txt | 1 + + dirsrvtests/tests/tickets/ticket49121_test.py | 211 +++++++++++++++++++++++++ + ldap/servers/slapd/entry.c | 55 ++++--- + 3 files changed, 244 insertions(+), 23 deletions(-) + create mode 100644 dirsrvtests/tests/data/ticket49121/utf8str.txt + create mode 100644 dirsrvtests/tests/tickets/ticket49121_test.py + +diff --git a/dirsrvtests/tests/data/ticket49121/utf8str.txt b/dirsrvtests/tests/data/ticket49121/utf8str.txt +new file mode 100644 +index 0000000..0005c4e +--- /dev/null ++++ b/dirsrvtests/tests/data/ticket49121/utf8str.txt +@@ -0,0 +1 @@ ++あいうえお +diff --git a/dirsrvtests/tests/tickets/ticket49121_test.py b/dirsrvtests/tests/tickets/ticket49121_test.py +new file mode 100644 +index 0000000..6450297 +--- /dev/null ++++ b/dirsrvtests/tests/tickets/ticket49121_test.py +@@ -0,0 +1,211 @@ ++# --- BEGIN COPYRIGHT BLOCK --- ++# Copyright (C) 2017 Red Hat, Inc. ++# All rights reserved. ++# ++# License: GPL (version 3 or any later version). ++# See LICENSE for details. ++# --- END COPYRIGHT BLOCK --- ++# ++import pytest ++import sys ++import codecs ++from lib389.tasks import * ++from lib389.utils import * ++from lib389.topologies import topology_m2 ++ ++DEBUGGING = os.getenv('DEBUGGING', False) ++ ++if DEBUGGING: ++ logging.getLogger(__name__).setLevel(logging.DEBUG) ++else: ++ logging.getLogger(__name__).setLevel(logging.INFO) ++log = logging.getLogger(__name__) ++ ++ ++def test_ticket49121(topology_m2): ++ """ ++ Creating some users. ++ Deleting quite a number of attributes which may or may not be in the entry. ++ The attribute type names are to be long. ++ Under the conditions, it did not estimate the size of string format entry ++ shorter than the real size and caused the Invalid write / server crash. ++ """ ++ reload(sys) ++ sys.setdefaultencoding('utf-8') ++ log.info('DefaultEncoding: %s' % sys.getdefaultencoding()) ++ ++ utf8file = os.path.join(topology_m2.ms["master1"].getDir(__file__, DATA_DIR), "ticket49121/utf8str.txt") ++ utf8obj = codecs.open(utf8file, 'r', 'utf-8') ++ utf8strorig = utf8obj.readline() ++ utf8str = utf8strorig.encode('utf-8').rstrip('\n') ++ utf8obj.close() ++ assert(utf8str) ++ ++ # Get the sbin directory so we know where to replace 'ns-slapd' ++ sbin_dir = topology_m2.ms["master1"].get_sbin_dir() ++ log.info('sbin_dir: %s' % sbin_dir) ++ ++ # stop M1 to do the next updates ++ topology_m2.ms["master1"].stop(30) ++ topology_m2.ms["master2"].stop(30) ++ ++ # wait for the servers shutdown ++ time.sleep(5) ++ ++ # Enable valgrind ++ if not topology_m2.ms["master1"].has_asan(): ++ valgrind_enable(sbin_dir) ++ ++ # start M1 to do the next updates ++ topology_m2.ms["master1"].start() ++ topology_m2.ms["master2"].start() ++ ++ for idx in range(1, 10): ++ try: ++ USER_DN = 'CN=user%d,ou=People,%s' % (idx, DEFAULT_SUFFIX) ++ log.info('adding user %s...' % (USER_DN)) ++ topology_m2.ms["master1"].add_s(Entry((USER_DN, ++ {'objectclass': 'top person extensibleObject'.split(' '), ++ 'cn': 'user%d' % idx, ++ 'sn': 'SN%d-%s' % (idx, utf8str)}))) ++ except ldap.LDAPError as e: ++ log.fatal('Failed to add user (%s): error %s' % (USER_DN, e.message['desc'])) ++ assert False ++ ++ for i in range(1, 3): ++ time.sleep(3) ++ for idx in range(1, 10): ++ try: ++ USER_DN = 'CN=user%d,ou=People,%s' % (idx, DEFAULT_SUFFIX) ++ log.info('[%d] modify user %s - replacing attrs...' % (i, USER_DN)) ++ topology_m2.ms["master1"].modify_s( ++ USER_DN, [(ldap.MOD_REPLACE, 'cn', 'user%d' % idx), ++ (ldap.MOD_REPLACE, 'ABCDEFGH_ID', ['239001ad-06dd-e011-80fa-c00000ad5174', ++ '240f0878-c552-e411-b0f3-000006040037']), ++ (ldap.MOD_REPLACE, 'attr1', 'NEW_ATTR'), ++ (ldap.MOD_REPLACE, 'attr20000000000000000000000000000000000000000', None), ++ (ldap.MOD_REPLACE, 'attr30000000000000000000000000000000000000000000000', None), ++ (ldap.MOD_REPLACE, 'attr40000000000000000000000000000', None), ++ (ldap.MOD_REPLACE, 'attr50000000000000000000000000000000000000', None), ++ (ldap.MOD_REPLACE, 'attr600000000000000000000000000000000000000000', None), ++ (ldap.MOD_REPLACE, 'attr7000000000000000000000000000000', None), ++ (ldap.MOD_REPLACE, 'attr8000000000000000000000000000000000', None), ++ (ldap.MOD_REPLACE, 'attr900000000000000000', None), ++ (ldap.MOD_REPLACE, 'attr1000000000000000000000', None), ++ (ldap.MOD_REPLACE, 'attr110000000000000', None), ++ (ldap.MOD_REPLACE, 'attr120000000000000', None), ++ (ldap.MOD_REPLACE, 'attr130000000000000000000000000000000000000000000000', None), ++ (ldap.MOD_REPLACE, 'attr140000000000000000000000000000', None), ++ (ldap.MOD_REPLACE, 'attr150000000000000000000000000000000000000000000000000000000000000', None), ++ (ldap.MOD_REPLACE, 'attr1600000000000000000000000000000000000000000', None), ++ (ldap.MOD_REPLACE, 'attr17000000000000000000000000000000', None), ++ (ldap.MOD_REPLACE, 'attr18000000000000000000000000000000000', None), ++ (ldap.MOD_REPLACE, 'attr1900000000000000000', None), ++ (ldap.MOD_REPLACE, 'attr2000000000000000000000', None), ++ (ldap.MOD_REPLACE, 'attr210000000000000000000000000000000000000000', None), ++ (ldap.MOD_REPLACE, 'attr220000000000000000000000000000000000000000', None), ++ (ldap.MOD_REPLACE, 'attr230000000000000000000000000000000000000000', None), ++ (ldap.MOD_REPLACE, 'attr240000000000000000000000000000000000000000', None), ++ (ldap.MOD_REPLACE, 'attr25000000000000000000000000000000', None), ++ (ldap.MOD_REPLACE, 'attr260000000000000000000000000000000000000000', None), ++ (ldap.MOD_REPLACE, 'attr270000000000000000000000000000000000000000000000000000000000000000000000', None), ++ (ldap.MOD_REPLACE, 'attr280000000000000000000000000000000000000000', None), ++ (ldap.MOD_REPLACE, 'attr29000000000000000000000000000000000000000000000000000000000', None), ++ (ldap.MOD_REPLACE, 'attr3000000000000000000000000', None), ++ (ldap.MOD_REPLACE, 'attr310000000000000000000000000000000000000000000000', None), ++ (ldap.MOD_REPLACE, 'attr320000000000000000000000000000000000000000000000', None), ++ (ldap.MOD_REPLACE, 'attr330000000000000000000000000000000000000000000000', None), ++ (ldap.MOD_REPLACE, 'attr340000000000000000000000000000000000000000000000', None), ++ (ldap.MOD_REPLACE, 'attr350000000000000000000000000000000000000000', None), ++ (ldap.MOD_REPLACE, 'attr360000000000000000000000000000000000000000000000', None), ++ (ldap.MOD_REPLACE, 'attr370000000000000000000000000000000000000000000000', None), ++ (ldap.MOD_REPLACE, 'attr380000000000000000000000000000000000000000000000', None), ++ (ldap.MOD_REPLACE, 'attr390000000000000000000000000000000000000000000000', None), ++ (ldap.MOD_REPLACE, 'attr4000000000000000000000000', None), ++ (ldap.MOD_REPLACE, 'attr410000000000000000000000000000', None), ++ (ldap.MOD_REPLACE, 'attr420000000000000000000000000000', None), ++ (ldap.MOD_REPLACE, 'attr430000000000000000000000000000', None), ++ (ldap.MOD_REPLACE, 'attr440000000000000000000000000000', None), ++ (ldap.MOD_REPLACE, 'attr4500000000000000000000000000000000000000', None), ++ (ldap.MOD_REPLACE, 'attr460000000000000000000000000000', None), ++ (ldap.MOD_REPLACE, 'attr470000000000000000000000000000000000000000000000000000000000', None), ++ (ldap.MOD_REPLACE, 'attr480000000000000000000000000000', None), ++ (ldap.MOD_REPLACE, 'attr49000000000000000000000000000000000000000000000', None), ++ (ldap.MOD_REPLACE, 'attr5000000000000000000000000', None), ++ (ldap.MOD_REPLACE, 'attr510000000000000', None), ++ (ldap.MOD_REPLACE, 'attr520000000000000', None), ++ (ldap.MOD_REPLACE, 'attr530000000000000', None), ++ (ldap.MOD_REPLACE, 'attr540000000000000', None), ++ (ldap.MOD_REPLACE, 'attr550000000000000000000000000000000000000', None), ++ (ldap.MOD_REPLACE, 'attr5600000000000000000000000000000000000000000', None), ++ (ldap.MOD_REPLACE, 'attr57000000000000000000000000000000', None), ++ (ldap.MOD_REPLACE, 'attr58000000000000000000000000000000000', None), ++ (ldap.MOD_REPLACE, 'attr5900000000000000000', None), ++ (ldap.MOD_REPLACE, 'attr6000000000000000000000000', None), ++ (ldap.MOD_REPLACE, 'attr6100000000000000000000000000000000000000000', None), ++ (ldap.MOD_REPLACE, 'attr6200000000000000000000000000000000000000000', None), ++ (ldap.MOD_REPLACE, 'attr6300000000000000000000000000000000000000000', None), ++ (ldap.MOD_REPLACE, 'attr6400000000000000000000000000000000000000000', None), ++ (ldap.MOD_REPLACE, 'attr65000000000000000000000000000000000000000000000000000000000000000000000000000000', None), ++ (ldap.MOD_REPLACE, 'attr6600000000000000000000000000000000000000000', None), ++ (ldap.MOD_REPLACE, 'attr6700000000000000000000000000000000000000000', None), ++ (ldap.MOD_REPLACE, 'attr6800000000000000000000000000000000000000000', None), ++ (ldap.MOD_REPLACE, 'attr690000000000000000000000000000000000000000000000000000000000', None), ++ (ldap.MOD_REPLACE, 'attr7000000000000000000000000', None), ++ (ldap.MOD_REPLACE, 'attr71000000000000000000000000000000', None), ++ (ldap.MOD_REPLACE, 'attr72000000000000000000000000000000', None), ++ (ldap.MOD_REPLACE, 'attr73000000000000000000000000000000', None), ++ (ldap.MOD_REPLACE, 'attr74000000000000000000000000000000', None), ++ (ldap.MOD_REPLACE, 'attr750000000000000000000000000000000000000000000', None), ++ (ldap.MOD_REPLACE, 'attr7600000000000000000000000000000000000000000', None), ++ (ldap.MOD_REPLACE, 'attr77000000000000000000000000000000', None), ++ (ldap.MOD_REPLACE, 'attr78000000000000000000000000000000000000000000000000000000000000000', None), ++ (ldap.MOD_REPLACE, 'attr79000000000000000000000000000000', None), ++ (ldap.MOD_REPLACE, 'attr800000000000000000000000000000000000000000000000', None), ++ (ldap.MOD_REPLACE, 'attr81000000000000000000000000000000000', None), ++ (ldap.MOD_REPLACE, 'attr82000000000000000000000000000000000', None), ++ (ldap.MOD_REPLACE, 'attr83000000000000000000000000000000000', None), ++ (ldap.MOD_REPLACE, 'attr84000000000000000000000000000000000', None), ++ (ldap.MOD_REPLACE, 'attr85000000000000000000000000000000000000000000000000000', None), ++ (ldap.MOD_REPLACE, 'attr8600000000000000000000000000000000000000000', None), ++ (ldap.MOD_REPLACE, 'attr87000000000000000000000000000000', None), ++ (ldap.MOD_REPLACE, 'attr88000000000000000000000000000000000', None), ++ (ldap.MOD_REPLACE, 'attr89000000000000000000000000000000000', None), ++ (ldap.MOD_REPLACE, 'attr9000000000000000000000000000000000000000000000000000', None)]) ++ except ldap.LDAPError as e: ++ log.fatal('Failed to modify user - deleting attrs (%s): error %s' % (USER_DN, e.message['desc'])) ++ ++ if not topology_m2.ms["master1"].has_asan(): ++ results_file = valgrind_get_results_file(topology_m2.ms["master1"]) ++ ++ # Stop master2 ++ topology_m2.ms["master1"].stop(30) ++ topology_m2.ms["master2"].stop(30) ++ ++ # Check for leak ++ if not topology_m2.ms["master1"].has_asan(): ++ # Check for invalid read/write ++ if valgrind_check_file(results_file, VALGRIND_INVALID_STR): ++ log.info('Valgrind reported invalid!') ++ assert False ++ else: ++ log.info('Valgrind is happy!') ++ ++ # Disable valgrind ++ if not topology_m2.ms["master1"].has_asan(): ++ valgrind_disable(sbin_dir) ++ ++ # start M1 to do the next updates ++ topology_m2.ms["master1"].start() ++ topology_m2.ms["master2"].start() ++ ++ log.info('Testcase PASSED') ++ 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/slapd/entry.c b/ldap/servers/slapd/entry.c +index 0cd3b60..ed99a38 100644 +--- a/ldap/servers/slapd/entry.c ++++ b/ldap/servers/slapd/entry.c +@@ -1472,7 +1472,8 @@ bail: + } + + static size_t +-entry2str_internal_size_valueset( const char *attrtype, const Slapi_ValueSet *vs, int entry2str_ctrl, int attribute_state, int value_state ) ++entry2str_internal_size_valueset( const Slapi_Attr *a, const char *attrtype, const Slapi_ValueSet *vs, ++ int entry2str_ctrl, int attribute_state, int value_state ) + { + size_t elen= 0; + if(!valueset_isempty(vs)) +@@ -1485,6 +1486,12 @@ entry2str_internal_size_valueset( const char *attrtype, const Slapi_ValueSet *vs + attribute_state, value_state ); + } + } ++ if(entry2str_ctrl & SLAPI_DUMP_STATEINFO) { ++ /* ";adcsn-" + a->a_deletioncsn */ ++ if ( a && a->a_deletioncsn ) { ++ elen += 1 + LDIF_CSNPREFIX_MAXLENGTH + CSN_STRSIZE; ++ } ++ } + return elen; + } + +@@ -1501,30 +1508,34 @@ entry2str_internal_size_attrlist( const Slapi_Attr *attrlist, int entry2str_ctrl + continue; + + /* Count the space required for the present and deleted values */ +- elen+= entry2str_internal_size_valueset(a->a_type, &a->a_present_values, +- entry2str_ctrl, attribute_state, +- VALUE_PRESENT); +- if(entry2str_ctrl & SLAPI_DUMP_STATEINFO) +- { +- elen+= entry2str_internal_size_valueset(a->a_type, &a->a_deleted_values, +- entry2str_ctrl, attribute_state, +- VALUE_DELETED); +- /* ";adcsn-" + a->a_deletioncsn */ +- if ( a->a_deletioncsn ) +- { +- elen += 1 + LDIF_CSNPREFIX_MAXLENGTH + CSN_STRSIZE; +- } +- if ( valueset_isempty(&a->a_deleted_values)) { ++ elen += entry2str_internal_size_valueset(a, a->a_type, &a->a_present_values, ++ entry2str_ctrl, attribute_state, VALUE_PRESENT); ++ if (entry2str_ctrl & SLAPI_DUMP_STATEINFO) { ++ elen += entry2str_internal_size_valueset(a, a->a_type, &a->a_deleted_values, ++ entry2str_ctrl, attribute_state, VALUE_DELETED); ++ if (valueset_isempty(&a->a_deleted_values) && valueset_isempty(&a->a_present_values)) { + /* this means the entry is deleted and has no more attributes, + * when writing the attr to disk we would loose the AD-csn. +- * Add an empty value to the set of deleted values. This will +- * never be seen by any client. It will never be moved to the ++ * Add an empty value to the set of deleted values. This will ++ * never be seen by any client. It will never be moved to the + * present values and is only used to preserve the AD-csn + * We need to add the size for that. + */ + elen += 1 + LDIF_CSNPREFIX_MAXLENGTH + CSN_STRSIZE; +- /* need also space for ";deletedattribute;deleted" */ +- elen += DELETED_ATTR_STRSIZE + DELETED_VALUE_STRSIZE; ++ /* need also space for ";deletedattribute;deleted" */ ++ elen += DELETED_ATTR_STRSIZE + DELETED_VALUE_STRSIZE; ++ /* ++ * If a_deleted_values is empty && if a_deletioncsn is NULL, ++ * a_deletioncsn is initialized via valueset_add_string. ++ * The size needs to be added. ++ */ ++ /* ";adcsn-" + a->a_deletioncsn */ ++ elen += 1 + LDIF_CSNPREFIX_MAXLENGTH + CSN_STRSIZE; ++ /* ++ * When both a_present_values & a_deleted_values are empty, ++ * the type size is not added. ++ */ ++ elen += PL_strlen(a->a_type); + } + } + } +@@ -1811,10 +1822,8 @@ entry2str_internal_ext( Slapi_Entry *e, int *len, int entry2str_ctrl) + if (NULL != slapi_entry_get_rdn_const(e)) + { + slapi_value_set_string(&rdnvalue, slapi_entry_get_rdn_const(e)); +- elen += entry2str_internal_size_value("rdn", &rdnvalue, +- entry2str_ctrl, +- ATTRIBUTE_PRESENT, +- VALUE_PRESENT); ++ elen += entry2str_internal_size_value("rdn", &rdnvalue, entry2str_ctrl, ++ ATTRIBUTE_PRESENT, VALUE_PRESENT); + } + + /* Count the space required for the present attributes */ +-- +2.7.4 + diff --git a/SOURCES/0071-Issue-49122-Filtered-nsrole-that-uses-nsrole-crashes.patch b/SOURCES/0071-Issue-49122-Filtered-nsrole-that-uses-nsrole-crashes.patch new file mode 100644 index 0000000..69ec148 --- /dev/null +++ b/SOURCES/0071-Issue-49122-Filtered-nsrole-that-uses-nsrole-crashes.patch @@ -0,0 +1,465 @@ +From a8b10d7a4f1cad499fa1ba245dd73ca7beac4589 Mon Sep 17 00:00:00 2001 +From: Mark Reynolds +Date: Mon, 27 Feb 2017 07:59:30 -0500 +Subject: [PATCH 71/71] Issue 49122 - Filtered nsrole that uses nsrole crashes + the server + +Bug Description: When evaluating a filter role that uses "nsrole" in the filter + crashes the server due infinite loop that leads to a stack + overflow. + +Fix Description: Virtual attributes are not allowed to be used in role filters. + We were already checking for COS attributes, but not nsrole. + + Also did some minor code cleanup + +https://pagure.io/389-ds-base/issue/49122 + +Reviewed by: nhosoi(Thanks!) + +(cherry picked from commit a95889def41d3869692d7259a9213b1f9238f3c8) +(cherry picked from commit d589950cdd8ac9a0756b67cfe4ae3a33da094065) +--- + dirsrvtests/tests/tickets/ticket49122_test.py | 73 ++++++++ + ldap/servers/plugins/roles/roles_cache.c | 235 +++++++++++++++----------- + 2 files changed, 205 insertions(+), 103 deletions(-) + create mode 100644 dirsrvtests/tests/tickets/ticket49122_test.py + +diff --git a/dirsrvtests/tests/tickets/ticket49122_test.py b/dirsrvtests/tests/tickets/ticket49122_test.py +new file mode 100644 +index 0000000..bd553f2 +--- /dev/null ++++ b/dirsrvtests/tests/tickets/ticket49122_test.py +@@ -0,0 +1,73 @@ ++import time ++import ldap ++import logging ++import pytest ++from lib389 import DirSrv, Entry, tools, tasks ++from lib389.tools import DirSrvTools ++from lib389._constants import * ++from lib389.properties import * ++from lib389.tasks import * ++from lib389.utils import * ++from lib389.topologies import topology_st as topo ++ ++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_DN = 'uid=user,' + DEFAULT_SUFFIX ++ROLE_DN = 'cn=Filtered_Role_That_Includes_Empty_Role,' + DEFAULT_SUFFIX ++ ++ ++def test_ticket49122(topo): ++ """Search for non-existant role and make sure the server does not crash ++ """ ++ ++ # Enable roles plugin ++ topo.standalone.plugins.enable(name=PLUGIN_ROLES) ++ topo.standalone.restart() ++ ++ # Add invalid role ++ try: ++ topo.standalone.add_s(Entry(( ++ ROLE_DN, {'objectclass': ['top', 'ldapsubentry', 'nsroledefinition', ++ 'nscomplexroledefinition', 'nsfilteredroledefinition'], ++ 'cn': 'Filtered_Role_That_Includes_Empty_Role', ++ 'nsRoleFilter': '(!(nsrole=cn=This_Is_An_Empty_Managed_NsRoleDefinition,dc=example,dc=com))', ++ 'description': 'A filtered role with filter that will crash the server'}))) ++ except ldap.LDAPError as e: ++ topo.standalone.log.fatal('Failed to add filtered role: error ' + e.message['desc']) ++ assert False ++ ++ # Add test user ++ try: ++ topo.standalone.add_s(Entry(( ++ USER_DN, {'objectclass': "top extensibleObject".split(), ++ 'uid': 'user'}))) ++ except ldap.LDAPError as e: ++ topo.standalone.log.fatal('Failed to add test user: error ' + str(e)) ++ assert False ++ ++ if DEBUGGING: ++ # Add debugging steps(if any)... ++ print "Attach gdb" ++ time.sleep(20) ++ ++ # Search for the role ++ try: ++ topo.standalone.search_s(USER_DN, ldap.SCOPE_SUBTREE, 'objectclass=*', ['nsrole']) ++ except ldap.LDAPError as e: ++ topo.standalone.log.fatal('Search failed: error ' + str(e)) ++ assert False ++ ++ topo.standalone.log.info('Test Passed') ++ ++ ++if __name__ == '__main__': ++ # Run isolated ++ # -s for DEBUG mode ++ CURRENT_FILE = os.path.realpath(__file__) ++ pytest.main("-s %s" % CURRENT_FILE) ++ +diff --git a/ldap/servers/plugins/roles/roles_cache.c b/ldap/servers/plugins/roles/roles_cache.c +index 147d113..66c8553 100644 +--- a/ldap/servers/plugins/roles/roles_cache.c ++++ b/ldap/servers/plugins/roles/roles_cache.c +@@ -1073,6 +1073,26 @@ static int roles_cache_create_role_under(roles_cache_def** roles_cache_suffix, S + return(rc); + } + ++/* ++ * Check that we are not using nsrole in the filter ++ */ ++static int roles_check_filter(Slapi_Filter *filter_list) ++{ ++ Slapi_Filter *f; ++ char *type = NULL; ++ ++ for ( f = slapi_filter_list_first( filter_list ); ++ f != NULL; ++ f = slapi_filter_list_next( filter_list, f ) ) ++ { ++ slapi_filter_get_attribute_type(f, &type); ++ if (strcasecmp(type, NSROLEATTR) == 0){ ++ return -1; ++ } ++ } ++ ++ return 0; ++} + + /* roles_cache_create_object_from_entry + ------------------------------------ +@@ -1088,17 +1108,17 @@ static int roles_cache_create_object_from_entry(Slapi_Entry *role_entry, role_ob + int rc = 0; + int type = 0; + role_object *this_role = NULL; +- char *rolescopeDN = NULL; ++ char *rolescopeDN = NULL; + + slapi_log_error(SLAPI_LOG_PLUGIN, ROLES_PLUGIN_SUBSYSTEM, + "--> roles_cache_create_object_from_entry\n"); + +- *result = NULL; ++ *result = NULL; + +- /* Do not allow circular dependencies */ +- if ( hint > MAX_NESTED_ROLES ) ++ /* Do not allow circular dependencies */ ++ if ( hint > MAX_NESTED_ROLES ) + { +- char *ndn = NULL; ++ char *ndn = NULL; + + ndn = slapi_entry_get_ndn( role_entry ); + slapi_log_error( +@@ -1111,85 +1131,83 @@ static int roles_cache_create_object_from_entry(Slapi_Entry *role_entry, role_ob + return (0); + } + +- /* Create the role cache definition */ +- this_role = (role_object*)slapi_ch_calloc(1, sizeof(role_object)); +- if (this_role == NULL ) ++ /* Create the role cache definition */ ++ this_role = (role_object*)slapi_ch_calloc(1, sizeof(role_object)); ++ if (this_role == NULL ) + { +- return ENOMEM; +- } ++ return ENOMEM; ++ } + +- /* Check the entry is OK */ +- /* Determine role type and assign to structure */ +- /* We determine the role type by reading the objectclass */ ++ /* Check the entry is OK */ ++ /* Determine role type and assign to structure */ ++ /* We determine the role type by reading the objectclass */ + if ( roles_cache_is_role_entry(role_entry) == 0 ) + { +- /* Bad type */ +- slapi_ch_free((void**)&this_role); +- return SLAPI_ROLE_DEFINITION_ERROR; +- } ++ /* Bad type */ ++ slapi_ch_free((void**)&this_role); ++ return SLAPI_ROLE_DEFINITION_ERROR; ++ } + +- type = roles_cache_determine_class(role_entry); ++ type = roles_cache_determine_class(role_entry); + +- if (type != 0) ++ if (type != 0) + { +- this_role->type = type; +- } ++ this_role->type = type; ++ } + else + { +- /* Bad type */ +- slapi_ch_free((void**)&this_role); +- return SLAPI_ROLE_DEFINITION_ERROR; +- } ++ /* Bad type */ ++ slapi_ch_free((void**)&this_role); ++ return SLAPI_ROLE_DEFINITION_ERROR; ++ } + + this_role->dn = slapi_sdn_new(); + slapi_sdn_copy(slapi_entry_get_sdn(role_entry),this_role->dn); +- +- rolescopeDN = slapi_entry_attr_get_charptr(role_entry, ROLE_SCOPE_DN); +- if (rolescopeDN) { +- Slapi_DN *rolescopeSDN; +- Slapi_DN *top_rolescopeSDN, *top_this_roleSDN; +- +- /* Before accepting to use this scope, first check if it belongs to the same suffix */ +- rolescopeSDN = slapi_sdn_new_dn_byref(rolescopeDN); +- if ((strlen((char *) slapi_sdn_get_ndn(rolescopeSDN)) > 0) && +- (slapi_dn_syntax_check(NULL, (char *) slapi_sdn_get_ndn(rolescopeSDN), 1) == 0)) { +- top_rolescopeSDN = roles_cache_get_top_suffix(rolescopeSDN); +- top_this_roleSDN = roles_cache_get_top_suffix(this_role->dn); +- if (slapi_sdn_compare(top_rolescopeSDN, top_this_roleSDN) == 0) { +- /* rolescopeDN belongs to the same suffix as the role, we can use this scope */ +- this_role->rolescopedn = rolescopeSDN; +- } else { +- slapi_log_error(SLAPI_LOG_FATAL, ROLES_PLUGIN_SUBSYSTEM, +- "%s: invalid %s - %s not in the same suffix. Scope skipped.\n", +- (char*) slapi_sdn_get_dn(this_role->dn), +- ROLE_SCOPE_DN, +- rolescopeDN); +- slapi_sdn_free(&rolescopeSDN); +- } +- slapi_sdn_free(&top_rolescopeSDN); +- slapi_sdn_free(&top_this_roleSDN); +- } else { +- /* this is an invalid DN, just ignore this parameter*/ +- slapi_log_error(SLAPI_LOG_FATAL, ROLES_PLUGIN_SUBSYSTEM, +- "%s: invalid %s - %s not a valid DN. Scope skipped.\n", +- (char*) slapi_sdn_get_dn(this_role->dn), +- ROLE_SCOPE_DN, +- rolescopeDN); +- slapi_sdn_free(&rolescopeSDN); +- } +- } ++ ++ rolescopeDN = slapi_entry_attr_get_charptr(role_entry, ROLE_SCOPE_DN); ++ if (rolescopeDN) { ++ Slapi_DN *rolescopeSDN; ++ Slapi_DN *top_rolescopeSDN, *top_this_roleSDN; ++ ++ /* Before accepting to use this scope, first check if it belongs to the same suffix */ ++ rolescopeSDN = slapi_sdn_new_dn_byref(rolescopeDN); ++ if ((strlen((char *) slapi_sdn_get_ndn(rolescopeSDN)) > 0) && ++ (slapi_dn_syntax_check(NULL, (char *) slapi_sdn_get_ndn(rolescopeSDN), 1) == 0)) { ++ top_rolescopeSDN = roles_cache_get_top_suffix(rolescopeSDN); ++ top_this_roleSDN = roles_cache_get_top_suffix(this_role->dn); ++ if (slapi_sdn_compare(top_rolescopeSDN, top_this_roleSDN) == 0) { ++ /* rolescopeDN belongs to the same suffix as the role, we can use this scope */ ++ this_role->rolescopedn = rolescopeSDN; ++ } else { ++ slapi_log_error(SLAPI_LOG_FATAL, ROLES_PLUGIN_SUBSYSTEM, ++ "roles_cache_create_object_from_entry - %s: invalid %s - %s not in the same suffix. Scope skipped.\n", ++ (char*) slapi_sdn_get_dn(this_role->dn), ++ ROLE_SCOPE_DN, ++ rolescopeDN); ++ slapi_sdn_free(&rolescopeSDN); ++ } ++ slapi_sdn_free(&top_rolescopeSDN); ++ slapi_sdn_free(&top_this_roleSDN); ++ } else { ++ /* this is an invalid DN, just ignore this parameter*/ ++ slapi_log_error(SLAPI_LOG_FATAL, ROLES_PLUGIN_SUBSYSTEM, ++ "roles_cache_create_object_from_entry - %s: invalid %s - %s not a valid DN. Scope skipped.\n", ++ (char*) slapi_sdn_get_dn(this_role->dn), ++ ROLE_SCOPE_DN, ++ rolescopeDN); ++ slapi_sdn_free(&rolescopeSDN); ++ } ++ } + +- /* Depending upon role type, pull out the remaining information we need */ ++ /* Depending upon role type, pull out the remaining information we need */ + switch (this_role->type) + { + case ROLE_TYPE_MANAGED: +- + /* Nothing further needed */ + break; + + case ROLE_TYPE_FILTERED: + { +- + Slapi_Filter *filter = NULL; + char *filter_attr_value = NULL; + Slapi_PBlock *pb = NULL; +@@ -1203,6 +1221,7 @@ static int roles_cache_create_object_from_entry(Slapi_Entry *role_entry, role_ob + slapi_ch_free((void**)&this_role); + return SLAPI_ROLE_ERROR_NO_FILTER_SPECIFIED; + } ++ + /* search (&(objectclass=costemplate)(filter_attr_value))*/ + /* if found, reject it (returning SLAPI_ROLE_ERROR_FILTER_BAD) */ + pb = slapi_pblock_new(); +@@ -1211,33 +1230,33 @@ static int roles_cache_create_object_from_entry(Slapi_Entry *role_entry, role_ob + Slapi_Entry **cosentries = NULL; + char *costmpl_filter = NULL; + if ((*filter_attr_value == '(') && +- (*(filter_attr_value+strlen(filter_attr_value)-1) == ')')) { ++ (*(filter_attr_value+strlen(filter_attr_value)-1) == ')')) { + costmpl_filter = +- slapi_ch_smprintf("(&(objectclass=costemplate)%s)", +- filter_attr_value); ++ slapi_ch_smprintf("(&(objectclass=costemplate)%s)", ++ filter_attr_value); + } else { + costmpl_filter = +- slapi_ch_smprintf("(&(objectclass=costemplate)(%s))", +- filter_attr_value); ++ slapi_ch_smprintf("(&(objectclass=costemplate)(%s))", ++ filter_attr_value); + } + slapi_search_internal_set_pb(pb, parent, LDAP_SCOPE_SUBTREE, +- costmpl_filter, NULL, 0, NULL, +- NULL, roles_get_plugin_identity(), +- 0); ++ costmpl_filter, NULL, 0, NULL, ++ NULL, roles_get_plugin_identity(), ++ 0); + slapi_search_internal_pb(pb); + slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, +- &cosentries); ++ &cosentries); + slapi_ch_free_string(&costmpl_filter); + slapi_ch_free_string(&parent); + if (cosentries && *cosentries) { + slapi_free_search_results_internal(pb); + slapi_pblock_destroy(pb); + slapi_log_error(SLAPI_LOG_FATAL, ROLES_PLUGIN_SUBSYSTEM, +- "%s: not allowed to refer virtual attribute " +- "in the value of %s %s. The %s is disabled.\n", +- (char*)slapi_sdn_get_ndn(this_role->dn), +- ROLE_FILTER_ATTR_NAME, filter_attr_value, +- ROLE_FILTER_ATTR_NAME); ++ "roles_cache_create_object_from_entry - %s: not allowed to refer virtual attribute " ++ "in the value of %s %s. The %s is disabled.\n", ++ (char*)slapi_sdn_get_ndn(this_role->dn), ++ ROLE_FILTER_ATTR_NAME, filter_attr_value, ++ ROLE_FILTER_ATTR_NAME); + slapi_ch_free_string(&filter_attr_value); + slapi_ch_free((void**)&this_role); + return SLAPI_ROLE_ERROR_FILTER_BAD; +@@ -1248,16 +1267,27 @@ static int roles_cache_create_object_from_entry(Slapi_Entry *role_entry, role_ob + + /* Turn it into a slapi filter object */ + filter = slapi_str2filter(filter_attr_value); +- slapi_ch_free_string(&filter_attr_value); +- +- if ( filter == NULL ) ++ if ( filter == NULL ) + { + /* An error has occured */ + slapi_ch_free((void**)&this_role); ++ slapi_ch_free_string(&filter_attr_value); ++ return SLAPI_ROLE_ERROR_FILTER_BAD; ++ } ++ if (roles_check_filter(filter)) { ++ slapi_log_error(SLAPI_LOG_FATAL, ROLES_PLUGIN_SUBSYSTEM, ++ "roles_cache_create_object_from_entry - \"%s\": not allowed to use \"nsrole\" " ++ "in the role filter \"%s\". %s is disabled.\n", ++ (char*)slapi_sdn_get_ndn(this_role->dn), ++ filter_attr_value, ++ ROLE_FILTER_ATTR_NAME); ++ slapi_ch_free((void**)&this_role); ++ slapi_ch_free_string(&filter_attr_value); + return SLAPI_ROLE_ERROR_FILTER_BAD; + } + /* Store on the object */ + this_role->filter = filter; ++ slapi_ch_free_string(&filter_attr_value); + + break; + } +@@ -1276,50 +1306,49 @@ static int roles_cache_create_object_from_entry(Slapi_Entry *role_entry, role_ob + int i = 0; + char *string = NULL; + Slapi_DN nested_role_dn; +- role_object_nested *nested_role_object = NULL; ++ role_object_nested *nested_role_object = NULL; + +- for ( i = 0; va[i] != NULL; i++ ) ++ for ( i = 0; va[i] != NULL; i++ ) + { +- string = (char*)slapi_value_get_string(va[i]); ++ string = (char*)slapi_value_get_string(va[i]); + +- /* Make a DN from the string */ +- slapi_sdn_init_dn_byref(&nested_role_dn,string); ++ /* Make a DN from the string */ ++ slapi_sdn_init_dn_byref(&nested_role_dn,string); + + slapi_log_error(SLAPI_LOG_PLUGIN, + ROLES_PLUGIN_SUBSYSTEM, "roles_cache_create_object_from_entry: dn %s, nested %s\n", + (char*)slapi_sdn_get_ndn(this_role->dn),string); + +- /* Make a role object nested from the DN */ +- rc = roles_cache_object_nested_from_dn(&nested_role_dn,&nested_role_object); ++ /* Make a role object nested from the DN */ ++ rc = roles_cache_object_nested_from_dn(&nested_role_dn,&nested_role_object); + +- /* Insert it into the nested list */ +- if ( (rc == 0) && nested_role_object) ++ /* Insert it into the nested list */ ++ if ( (rc == 0) && nested_role_object) + { + /* Add to the tree where avl_data is a role_object_nested struct */ +- rc = roles_cache_insert_object_nested(&(this_role->avl_tree),nested_role_object); +- } +- slapi_sdn_done(&nested_role_dn); +- } +- } +- ++ rc = roles_cache_insert_object_nested(&(this_role->avl_tree),nested_role_object); ++ } ++ slapi_sdn_done(&nested_role_dn); ++ } ++ } ++ + break; + } + + default: +- slapi_log_error(SLAPI_LOG_FATAL, +- ROLES_PLUGIN_SUBSYSTEM, "wrong role type\n"); ++ slapi_log_error(SLAPI_LOG_FATAL, ROLES_PLUGIN_SUBSYSTEM, ++ "roles_cache_create_object_from_entry - wrong role type\n"); + } + +- if ( rc == 0 ) ++ if ( rc == 0 ) + { +- *result = this_role; +- } ++ *result = this_role; ++ } + + slapi_log_error(SLAPI_LOG_PLUGIN, ROLES_PLUGIN_SUBSYSTEM, +- "<-- roles_cache_create_object_from_entry\n"); +- ++ "<-- roles_cache_create_object_from_entry\n"); + +- return rc; ++ return rc; + } + + /* roles_cache_determine_class: +-- +2.7.4 + diff --git a/SOURCES/0072-fix-for-cve-2017-2668-simple-return-text-if-suffix-n.patch b/SOURCES/0072-fix-for-cve-2017-2668-simple-return-text-if-suffix-n.patch new file mode 100644 index 0000000..74a3355 --- /dev/null +++ b/SOURCES/0072-fix-for-cve-2017-2668-simple-return-text-if-suffix-n.patch @@ -0,0 +1,110 @@ +From 4239f2f010e6f8126348f4cd19e2b34d371965b5 Mon Sep 17 00:00:00 2001 +From: Ludwig Krispenz +Date: Mon, 3 Apr 2017 09:32:20 +0200 +Subject: [PATCH] fix for cve 2017-2668 - simple return text if suffix not + found + +Resolves: https://bugzilla.redhat.com/show_bug.cgi?id=1436575 + +Signed-off-by: Mark Reynolds +--- + ldap/servers/slapd/defbackend.c | 75 ++--------------------------------------- + 1 file changed, 2 insertions(+), 73 deletions(-) + +diff --git a/ldap/servers/slapd/defbackend.c b/ldap/servers/slapd/defbackend.c +index da4a701..105fea9 100644 +--- a/ldap/servers/slapd/defbackend.c ++++ b/ldap/servers/slapd/defbackend.c +@@ -171,50 +171,7 @@ defbackend_abandon( Slapi_PBlock *pb ) + } + + +-#define DEFBE_NO_SUCH_SUFFIX "No such suffix" +-/* +- * Generate a "No such suffix" return text +- * Example: +- * cn=X,dc=bogus,dc=com ==> "No such suffix (dc=bogus,dc=com)" +- * if the last rdn starts with "dc=", print all last dc= rdn's. +- * cn=X,cn=bogus ==> "No such suffix (cn=bogus)" +- * otherwise, print the very last rdn. +- * cn=X,z=bogus ==> "No such suffix (x=bogus)" +- * it is true even if it is an invalid rdn. +- * cn=X,bogus ==> "No such suffix (bogus)" +- * another example of invalid rdn. +- */ +-static void +-_defbackend_gen_returntext(char *buffer, size_t buflen, char **dns) +-{ +- int dnidx; +- int sidx; +- struct suffix_repeat { +- char *suffix; +- int size; +- } candidates[] = { +- {"dc=", 3}, /* dc could be repeated. otherwise the last rdn is used. */ +- {NULL, 0} +- }; +- PR_snprintf(buffer, buflen, "%s (", DEFBE_NO_SUCH_SUFFIX); +- for (dnidx = 0; dns[dnidx]; dnidx++) ; /* finding the last */ +- dnidx--; /* last rdn */ +- for (sidx = 0; candidates[sidx].suffix; sidx++) { +- if (!PL_strncasecmp(dns[dnidx], candidates[sidx].suffix, candidates[sidx].size)) { +- while (!PL_strncasecmp(dns[--dnidx], candidates[sidx].suffix, candidates[sidx].size)) ; +- PL_strcat(buffer, dns[++dnidx]); /* the first "dn=", e.g. */ +- for (++dnidx; dns[dnidx]; dnidx++) { +- PL_strcat(buffer, ","); +- PL_strcat(buffer, dns[dnidx]); +- } +- PL_strcat(buffer, ")"); +- return; /* finished the task */ +- } +- } +- PL_strcat(buffer, dns[dnidx]); +- PL_strcat(buffer, ")"); +- return; +-} ++#define DEFBE_NO_SUCH_SUFFIX "No suffix for bind dn found" + + static int + defbackend_bind( Slapi_PBlock *pb ) +@@ -236,36 +193,8 @@ defbackend_bind( Slapi_PBlock *pb ) + slapi_counter_increment(g_get_global_snmp_vars()->ops_tbl.dsAnonymousBinds); + rc = SLAPI_BIND_ANONYMOUS; + } else { +- Slapi_DN *sdn = NULL; +- char *suffix = NULL; +- char **dns = NULL; +- +- if (pb->pb_op) { +- sdn = operation_get_target_spec(pb->pb_op); +- if (sdn) { +- dns = slapi_ldap_explode_dn(slapi_sdn_get_dn(sdn), 0); +- if (dns) { +- size_t dnlen = slapi_sdn_get_ndn_len(sdn); +- size_t len = dnlen + sizeof(DEFBE_NO_SUCH_SUFFIX) + 4; +- suffix = slapi_ch_malloc(len); +- if (dnlen) { +- _defbackend_gen_returntext(suffix, len, dns); +- } else { +- PR_snprintf(suffix, len, "%s", DEFBE_NO_SUCH_SUFFIX); +- } +- } +- } +- } +- if (suffix) { +- slapi_pblock_set(pb, SLAPI_PB_RESULT_TEXT, suffix); +- } else { +- slapi_pblock_set(pb, SLAPI_PB_RESULT_TEXT, DEFBE_NO_SUCH_SUFFIX); +- } ++ slapi_pblock_set(pb, SLAPI_PB_RESULT_TEXT, DEFBE_NO_SUCH_SUFFIX); + send_ldap_result(pb, LDAP_INVALID_CREDENTIALS, NULL, "", 0, NULL); +- if (dns) { +- slapi_ldap_value_free(dns); +- } +- slapi_ch_free_string(&suffix); + rc = SLAPI_BIND_FAIL; + } + +-- +2.9.3 + diff --git a/SPECS/389-ds-base.spec b/SPECS/389-ds-base.spec index cdda586..14f5c8e 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.5.10 -Release: %{?relprefix}18%{?prerel}%{?dist} +Release: %{?relprefix}20%{?prerel}%{?dist} License: GPLv3+ URL: https://www.port389.org/ Group: System Environment/Daemons @@ -205,6 +205,9 @@ Patch66: 0066-Ticket-49079-deadlock-on-cos-cache-rebuild.patch Patch67: 0067-Ticket-49016-un-register-migration-remove-may-fail-i.patch Patch68: 0068-Ticket-49016-un-register-migration-remove-may-fail-i.patch Patch69: 0069-fix-for-reg-in-49008-check-if-ruv-element-exists.patch +Patch70: 0070-Ticket-49121-ns-slapd-crashes-in-ldif_sput-due-to-th.patch +Patch71: 0071-Issue-49122-Filtered-nsrole-that-uses-nsrole-crashes.patch +Patch72: 0072-fix-for-cve-2017-2668-simple-return-text-if-suffix-n.patch %description 389 Directory Server is an LDAPv3 compliant server. The base package includes @@ -360,6 +363,9 @@ cp %{SOURCE2} README.devel %patch67 -p1 %patch68 -p1 %patch69 -p1 +%patch70 -p1 +%patch71 -p1 +%patch72 -p1 %build %if %{use_nunc_stans} @@ -597,6 +603,15 @@ fi %{_sysconfdir}/%{pkgname}/dirsrvtests %changelog +* Mon Apr 3 2017 Mark Reynolds - 1.3.5.10-20 +- Bump version to 1.3.5.10-20 +- Resolves: bug 1437005 - CVE-2017-2668 389-ds-base: Remote crash via crafted LDAP messages + +* Fri Mar 3 2017 Mark Reynolds - 1.3.5.10-19 +- Release 1.3.5.10-19 +- Resolves: bug 1429495 - ns-slapd dies under heavy load +- Resolves: bug 1429498 - A filtered nsrole that specifies an empty nsrole in its nsRoleFilter will result in a segfault + * Thu Feb 16 2017 Mark Reynolds - 1.3.5.10-18 - Release 1.3.5.10-18 - Resolves: bug 1387340 - Aborted operation can leave RUV in incorrect state