From ffd2779db53995c0f759539fe2d9cf425f7464c8 Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Mon, 30 Mar 2020 09:44:20 +0000 Subject: [PATCH 01/12] CVE-2020-10700: dsdb: Add test for ASQ and ASQ in combination with paged_results Thanks to Andrei Popa for finding, reporting and working with us to diagnose this issue! BUG: https://bugzilla.samba.org/show_bug.cgi?id=14331 Signed-off-by: Andrew Bartlett Reviewed-by: Gary Lockyer --- selftest/knownfail.d/asq | 1 + source4/dsdb/tests/python/asq.py | 171 +++++++++++++++++++++++++++++++ source4/selftest/tests.py | 1 + 3 files changed, 173 insertions(+) create mode 100644 selftest/knownfail.d/asq create mode 100644 source4/dsdb/tests/python/asq.py diff --git a/selftest/knownfail.d/asq b/selftest/knownfail.d/asq new file mode 100644 index 00000000000..eb0e3e0aba1 --- /dev/null +++ b/selftest/knownfail.d/asq @@ -0,0 +1 @@ +samba4.asq.python\(ad_dc_default\).__main__.ASQLDAPTest.test_asq_paged \ No newline at end of file diff --git a/source4/dsdb/tests/python/asq.py b/source4/dsdb/tests/python/asq.py new file mode 100644 index 00000000000..a32c9f40cd3 --- /dev/null +++ b/source4/dsdb/tests/python/asq.py @@ -0,0 +1,171 @@ +#!/usr/bin/env python3 +# +# Test ASQ LDAP control behaviour in Samba +# Copyright (C) Andrew Bartlett 2019-2020 +# +# Based on Unit tests for the notification control +# Copyright (C) Stefan Metzmacher 2016 +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +import optparse +import sys +import os +import random + +sys.path.insert(0, "bin/python") +import samba +from samba.tests.subunitrun import SubunitOptions, TestProgram + +import samba.getopt as options + +from samba.auth import system_session +from samba import ldb +from samba.samdb import SamDB +from samba.ndr import ndr_unpack +from samba import gensec +from samba.credentials import Credentials +import samba.tests + +from ldb import SCOPE_SUBTREE, SCOPE_ONELEVEL, SCOPE_BASE, LdbError +from ldb import ERR_TIME_LIMIT_EXCEEDED, ERR_ADMIN_LIMIT_EXCEEDED, ERR_UNWILLING_TO_PERFORM +from ldb import Message + +parser = optparse.OptionParser("large_ldap.py [options] ") +sambaopts = options.SambaOptions(parser) +parser.add_option_group(sambaopts) +parser.add_option_group(options.VersionOptions(parser)) +# use command line creds if available +credopts = options.CredentialsOptions(parser) +parser.add_option_group(credopts) +subunitopts = SubunitOptions(parser) +parser.add_option_group(subunitopts) +opts, args = parser.parse_args() + +if len(args) < 1: + parser.print_usage() + sys.exit(1) + +url = args[0] + +lp = sambaopts.get_loadparm() +creds = credopts.get_credentials(lp) + + +class ASQLDAPTest(samba.tests.TestCase): + + def setUp(self): + super(ASQLDAPTest, self).setUp() + self.ldb = samba.Ldb(url, credentials=creds, session_info=system_session(lp), lp=lp) + self.base_dn = self.ldb.get_default_basedn() + self.NAME_ASQ="asq_" + format(random.randint(0, 99999), "05") + self.OU_NAME_ASQ= self.NAME_ASQ + "_ou" + self.ou_dn = ldb.Dn(self.ldb, "ou=" + self.OU_NAME_ASQ + "," + str(self.base_dn)) + + samba.tests.delete_force(self.ldb, self.ou_dn, + controls=['tree_delete:1']) + + self.ldb.add({ + "dn": self.ou_dn, + "objectclass": "organizationalUnit", + "ou": self.OU_NAME_ASQ}) + + self.members = [] + self.members2 = [] + + for x in range(20): + name = self.NAME_ASQ + "_" + str(x) + dn = ldb.Dn(self.ldb, + "cn=" + name + "," + str(self.ou_dn)) + self.members.append(dn) + self.ldb.add({ + "dn": dn, + "objectclass": "group"}) + + for x in range(20): + name = self.NAME_ASQ + "_" + str(x + 20) + dn = ldb.Dn(self.ldb, + "cn=" + name + "," + str(self.ou_dn)) + self.members2.append(dn) + self.ldb.add({ + "dn": dn, + "objectclass": "group", + "member": [str(x) for x in self.members]}) + + name = self.NAME_ASQ + "_" + str(x + 40) + self.top_dn = ldb.Dn(self.ldb, + "cn=" + name + "," + str(self.ou_dn)) + self.ldb.add({ + "dn": self.top_dn, + "objectclass": "group", + "member": [str(x) for x in self.members2]}) + + def tearDown(self): + samba.tests.delete_force(self.ldb, self.ou_dn, + controls=['tree_delete:1']) + + def test_asq(self): + """Testing ASQ behaviour. + + ASQ is very strange, it turns a BASE search into a search for + all the objects pointed to by the specified attribute, + returning multiple entries! + + """ + + msgs = self.ldb.search(base=self.top_dn, + scope=ldb.SCOPE_BASE, + attrs=["objectGUID", "cn", "member"], + controls=["asq:1:member"]) + + self.assertEqual(len(msgs), 20) + + for msg in msgs: + self.assertNotEqual(msg.dn, self.top_dn) + self.assertIn(msg.dn, self.members2) + for group in msg["member"]: + self.assertIn(ldb.Dn(self.ldb, str(group)), + self.members) + + def test_asq_paged(self): + """Testing ASQ behaviour with paged_results set. + + ASQ is very strange, it turns a BASE search into a search for + all the objects pointed to by the specified attribute, + returning multiple entries! + + """ + + msgs = self.ldb.search(base=self.top_dn, + scope=ldb.SCOPE_BASE, + attrs=["objectGUID", "cn", "member"], + controls=["asq:1:member", + "paged_results:1:1024"]) + + self.assertEqual(len(msgs), 20) + + for msg in msgs: + self.assertNotEqual(msg.dn, self.top_dn) + self.assertIn(msg.dn, self.members2) + for group in msg["member"]: + self.assertIn(ldb.Dn(self.ldb, str(group)), + self.members) + +if "://" not in url: + if os.path.isfile(url): + url = "tdb://%s" % url + else: + url = "ldap://%s" % url + +TestProgram(module=__name__, opts=subunitopts) diff --git a/source4/selftest/tests.py b/source4/selftest/tests.py index ae2b10ae659..52db18a872b 100755 --- a/source4/selftest/tests.py +++ b/source4/selftest/tests.py @@ -885,6 +885,7 @@ plantestsuite_loadlist("samba4.tokengroups.krb5.python(ad_dc_default)", "ad_dc_d plantestsuite_loadlist("samba4.tokengroups.ntlm.python(ad_dc_default)", "ad_dc_default:local", [python, os.path.join(DSDB_PYTEST_DIR, "token_group.py"), '$SERVER', '-U"$USERNAME%$PASSWORD"', '--workgroup=$DOMAIN', '-k', 'no', '$LOADLIST', '$LISTOPT']) plantestsuite("samba4.sam.python(fl2008r2dc)", "fl2008r2dc", [python, os.path.join(DSDB_PYTEST_DIR, "sam.py"), '$SERVER', '-U"$USERNAME%$PASSWORD"', '--workgroup=$DOMAIN']) plantestsuite("samba4.sam.python(ad_dc_default)", "ad_dc_default", [python, os.path.join(DSDB_PYTEST_DIR, "sam.py"), '$SERVER', '-U"$USERNAME%$PASSWORD"', '--workgroup=$DOMAIN']) +plantestsuite("samba4.asq.python(ad_dc_default)", "ad_dc_default", [python, os.path.join(DSDB_PYTEST_DIR, "asq.py"), '$SERVER', '-U"$USERNAME%$PASSWORD"', '--workgroup=$DOMAIN']) plantestsuite("samba4.user_account_control.python(ad_dc_default)", "ad_dc_default", [python, os.path.join(DSDB_PYTEST_DIR, "user_account_control.py"), '$SERVER', '-U"$USERNAME%$PASSWORD"', '--workgroup=$DOMAIN']) for env in ['ad_dc_default:local', 'schema_dc:local']: -- 2.17.1 From 24e621b4dde15a26f4fbf1a2e2bc7ecdb77d26a4 Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Wed, 11 Mar 2020 16:41:34 +1300 Subject: [PATCH 02/12] CVE-2020-10700: ldb: Always use ldb_next_request() in ASQ module We want to keep going down the module stack, and not start from the top again. ASQ is above the ACL modules, but below paged_results and we do not wish to re-trigger that work. Thanks to Andrei Popa for finding, reporting and working with us to diagnose this issue! BUG: https://bugzilla.samba.org/show_bug.cgi?id=14331 Signed-off-by: Andrew Bartlett Reviewed-by: Gary Lockyer --- lib/ldb/modules/asq.c | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/lib/ldb/modules/asq.c b/lib/ldb/modules/asq.c index 7482de826f0..4eba941ae0b 100644 --- a/lib/ldb/modules/asq.c +++ b/lib/ldb/modules/asq.c @@ -311,12 +311,9 @@ static int asq_build_multiple_requests(struct asq_context *ac, bool *terminated) static int asq_search_continue(struct asq_context *ac) { - struct ldb_context *ldb; bool terminated = false; int ret; - ldb = ldb_module_get_ctx(ac->module); - switch (ac->step) { case ASQ_SEARCH_BASE: @@ -328,7 +325,7 @@ static int asq_search_continue(struct asq_context *ac) ac->step = ASQ_SEARCH_MULTI; - return ldb_request(ldb, ac->reqs[ac->cur_req]); + return ldb_next_request(ac->module, ac->reqs[ac->cur_req]); case ASQ_SEARCH_MULTI: @@ -339,7 +336,7 @@ static int asq_search_continue(struct asq_context *ac) return asq_search_terminate(ac); } - return ldb_request(ldb, ac->reqs[ac->cur_req]); + return ldb_next_request(ac->module, ac->reqs[ac->cur_req]); } return LDB_ERR_OPERATIONS_ERROR; @@ -347,14 +344,11 @@ static int asq_search_continue(struct asq_context *ac) static int asq_search(struct ldb_module *module, struct ldb_request *req) { - struct ldb_context *ldb; struct ldb_request *base_req; struct ldb_control *control; struct asq_context *ac; int ret; - ldb = ldb_module_get_ctx(module); - /* check if there's an ASQ control */ control = ldb_request_get_control(req, LDB_CONTROL_ASQ_OID); if (control == NULL) { @@ -385,7 +379,7 @@ static int asq_search(struct ldb_module *module, struct ldb_request *req) ac->step = ASQ_SEARCH_BASE; - return ldb_request(ldb, base_req); + return ldb_next_request(ac->module, base_req); } static int asq_init(struct ldb_module *module) -- 2.17.1 From 980831bb97c0caca95cf1d24d475f829f3c0a1d1 Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Wed, 11 Mar 2020 16:43:31 +1300 Subject: [PATCH 03/12] CVE-2020-10700: dsdb: Do not permit the ASQ control for the GUID search in paged_results ASQ is a very strange control and a BASE search can return multiple results that are NOT the requested DN, but the DNs pointed to by it! Thanks to Andrei Popa for finding, reporting and working with us to diagnose this issue! BUG: https://bugzilla.samba.org/show_bug.cgi?id=14331 Signed-off-by: Andrew Bartlett Reviewed-by: Gary Lockyer --- selftest/knownfail.d/asq | 1 - source4/dsdb/samdb/ldb_modules/paged_results.c | 18 +++++++++++++----- 2 files changed, 13 insertions(+), 6 deletions(-) delete mode 100644 selftest/knownfail.d/asq diff --git a/selftest/knownfail.d/asq b/selftest/knownfail.d/asq deleted file mode 100644 index eb0e3e0aba1..00000000000 --- a/selftest/knownfail.d/asq +++ /dev/null @@ -1 +0,0 @@ -samba4.asq.python\(ad_dc_default\).__main__.ASQLDAPTest.test_asq_paged \ No newline at end of file diff --git a/source4/dsdb/samdb/ldb_modules/paged_results.c b/source4/dsdb/samdb/ldb_modules/paged_results.c index 940d2254fb0..dc211dd18ce 100644 --- a/source4/dsdb/samdb/ldb_modules/paged_results.c +++ b/source4/dsdb/samdb/ldb_modules/paged_results.c @@ -483,8 +483,14 @@ paged_results_copy_down_controls(TALLOC_CTX *mem_ctx, if (control->oid == NULL) { continue; } - if (strncmp(control->oid, LDB_CONTROL_PAGED_RESULTS_OID, - sizeof(LDB_CONTROL_PAGED_RESULTS_OID)) == 0) { + if (strcmp(control->oid, LDB_CONTROL_PAGED_RESULTS_OID) == 0) { + continue; + } + /* + * ASQ changes everything, do not copy it down for the + * per-GUID search + */ + if (strcmp(control->oid, LDB_CONTROL_ASQ_OID) == 0) { continue; } new_controls[j] = talloc_steal(new_controls, control); @@ -534,21 +540,23 @@ static bool paged_controls_same(struct ldb_request *req, num_non_null_req_controls = 0; for (i=0; req->controls[i] != NULL; i++) { - if (req->controls[i]->oid != NULL) { + if (req->controls[i]->oid != NULL && + strcmp(req->controls[i]->oid, + LDB_CONTROL_ASQ_OID) != 0) { num_non_null_req_controls++; } } /* At this point we have the number of non-null entries for both * control lists and we know that: - * 1. down_controls does not contain the paged control + * 1. down_controls does not contain the paged control or ASQ * (because paged_results_copy_down_controls excludes it) * 2. req->controls does contain the paged control * (because this function is only called if this is true) * 3. down_controls is a subset of non-null controls in req->controls * (checked above) * So to confirm that the two lists are identical except for the paged - * control, all we need to check is: */ + * control and possibly ASQ, all we need to check is: */ if (num_non_null_req_controls == num_down_controls + 1) { return true; } -- 2.17.1 From 2a611988caca66ebe2b88051b3bad148b442c72c Mon Sep 17 00:00:00 2001 From: Karolin Seeger Date: Fri, 17 Apr 2020 13:28:52 +0200 Subject: [PATCH 04/12] CVE-2020-10700: ldb: Bump version up to 2.0.10. BUG: https://bugzilla.samba.org/show_bug.cgi?id=14331 Signed-off-by: Karolin Seeger Reviewed-by: Ralph Boehme --- lib/ldb/ABI/ldb-2.0.10.sigs | 283 +++++++++++++++++++++++++++++ lib/ldb/ABI/pyldb-util-2.0.10.sigs | 2 + lib/ldb/wscript | 2 +- 3 files changed, 286 insertions(+), 1 deletion(-) create mode 100644 lib/ldb/ABI/ldb-2.0.10.sigs create mode 100644 lib/ldb/ABI/pyldb-util-2.0.10.sigs diff --git a/lib/ldb/ABI/ldb-2.0.10.sigs b/lib/ldb/ABI/ldb-2.0.10.sigs new file mode 100644 index 00000000000..5049dc64ce1 --- /dev/null +++ b/lib/ldb/ABI/ldb-2.0.10.sigs @@ -0,0 +1,283 @@ +ldb_add: int (struct ldb_context *, const struct ldb_message *) +ldb_any_comparison: int (struct ldb_context *, void *, ldb_attr_handler_t, const struct ldb_val *, const struct ldb_val *) +ldb_asprintf_errstring: void (struct ldb_context *, const char *, ...) +ldb_attr_casefold: char *(TALLOC_CTX *, const char *) +ldb_attr_dn: int (const char *) +ldb_attr_in_list: int (const char * const *, const char *) +ldb_attr_list_copy: const char **(TALLOC_CTX *, const char * const *) +ldb_attr_list_copy_add: const char **(TALLOC_CTX *, const char * const *, const char *) +ldb_base64_decode: int (char *) +ldb_base64_encode: char *(TALLOC_CTX *, const char *, int) +ldb_binary_decode: struct ldb_val (TALLOC_CTX *, const char *) +ldb_binary_encode: char *(TALLOC_CTX *, struct ldb_val) +ldb_binary_encode_string: char *(TALLOC_CTX *, const char *) +ldb_build_add_req: int (struct ldb_request **, struct ldb_context *, TALLOC_CTX *, const struct ldb_message *, struct ldb_control **, void *, ldb_request_callback_t, struct ldb_request *) +ldb_build_del_req: int (struct ldb_request **, struct ldb_context *, TALLOC_CTX *, struct ldb_dn *, struct ldb_control **, void *, ldb_request_callback_t, struct ldb_request *) +ldb_build_extended_req: int (struct ldb_request **, struct ldb_context *, TALLOC_CTX *, const char *, void *, struct ldb_control **, void *, ldb_request_callback_t, struct ldb_request *) +ldb_build_mod_req: int (struct ldb_request **, struct ldb_context *, TALLOC_CTX *, const struct ldb_message *, struct ldb_control **, void *, ldb_request_callback_t, struct ldb_request *) +ldb_build_rename_req: int (struct ldb_request **, struct ldb_context *, TALLOC_CTX *, struct ldb_dn *, struct ldb_dn *, struct ldb_control **, void *, ldb_request_callback_t, struct ldb_request *) +ldb_build_search_req: int (struct ldb_request **, struct ldb_context *, TALLOC_CTX *, struct ldb_dn *, enum ldb_scope, const char *, const char * const *, struct ldb_control **, void *, ldb_request_callback_t, struct ldb_request *) +ldb_build_search_req_ex: int (struct ldb_request **, struct ldb_context *, TALLOC_CTX *, struct ldb_dn *, enum ldb_scope, struct ldb_parse_tree *, const char * const *, struct ldb_control **, void *, ldb_request_callback_t, struct ldb_request *) +ldb_casefold: char *(struct ldb_context *, TALLOC_CTX *, const char *, size_t) +ldb_casefold_default: char *(void *, TALLOC_CTX *, const char *, size_t) +ldb_check_critical_controls: int (struct ldb_control **) +ldb_comparison_binary: int (struct ldb_context *, void *, const struct ldb_val *, const struct ldb_val *) +ldb_comparison_fold: int (struct ldb_context *, void *, const struct ldb_val *, const struct ldb_val *) +ldb_connect: int (struct ldb_context *, const char *, unsigned int, const char **) +ldb_control_to_string: char *(TALLOC_CTX *, const struct ldb_control *) +ldb_controls_except_specified: struct ldb_control **(struct ldb_control **, TALLOC_CTX *, struct ldb_control *) +ldb_debug: void (struct ldb_context *, enum ldb_debug_level, const char *, ...) +ldb_debug_add: void (struct ldb_context *, const char *, ...) +ldb_debug_end: void (struct ldb_context *, enum ldb_debug_level) +ldb_debug_set: void (struct ldb_context *, enum ldb_debug_level, const char *, ...) +ldb_delete: int (struct ldb_context *, struct ldb_dn *) +ldb_dn_add_base: bool (struct ldb_dn *, struct ldb_dn *) +ldb_dn_add_base_fmt: bool (struct ldb_dn *, const char *, ...) +ldb_dn_add_child: bool (struct ldb_dn *, struct ldb_dn *) +ldb_dn_add_child_fmt: bool (struct ldb_dn *, const char *, ...) +ldb_dn_add_child_val: bool (struct ldb_dn *, const char *, struct ldb_val) +ldb_dn_alloc_casefold: char *(TALLOC_CTX *, struct ldb_dn *) +ldb_dn_alloc_linearized: char *(TALLOC_CTX *, struct ldb_dn *) +ldb_dn_canonical_ex_string: char *(TALLOC_CTX *, struct ldb_dn *) +ldb_dn_canonical_string: char *(TALLOC_CTX *, struct ldb_dn *) +ldb_dn_check_local: bool (struct ldb_module *, struct ldb_dn *) +ldb_dn_check_special: bool (struct ldb_dn *, const char *) +ldb_dn_compare: int (struct ldb_dn *, struct ldb_dn *) +ldb_dn_compare_base: int (struct ldb_dn *, struct ldb_dn *) +ldb_dn_copy: struct ldb_dn *(TALLOC_CTX *, struct ldb_dn *) +ldb_dn_escape_value: char *(TALLOC_CTX *, struct ldb_val) +ldb_dn_extended_add_syntax: int (struct ldb_context *, unsigned int, const struct ldb_dn_extended_syntax *) +ldb_dn_extended_filter: void (struct ldb_dn *, const char * const *) +ldb_dn_extended_syntax_by_name: const struct ldb_dn_extended_syntax *(struct ldb_context *, const char *) +ldb_dn_from_ldb_val: struct ldb_dn *(TALLOC_CTX *, struct ldb_context *, const struct ldb_val *) +ldb_dn_get_casefold: const char *(struct ldb_dn *) +ldb_dn_get_comp_num: int (struct ldb_dn *) +ldb_dn_get_component_name: const char *(struct ldb_dn *, unsigned int) +ldb_dn_get_component_val: const struct ldb_val *(struct ldb_dn *, unsigned int) +ldb_dn_get_extended_comp_num: int (struct ldb_dn *) +ldb_dn_get_extended_component: const struct ldb_val *(struct ldb_dn *, const char *) +ldb_dn_get_extended_linearized: char *(TALLOC_CTX *, struct ldb_dn *, int) +ldb_dn_get_ldb_context: struct ldb_context *(struct ldb_dn *) +ldb_dn_get_linearized: const char *(struct ldb_dn *) +ldb_dn_get_parent: struct ldb_dn *(TALLOC_CTX *, struct ldb_dn *) +ldb_dn_get_rdn_name: const char *(struct ldb_dn *) +ldb_dn_get_rdn_val: const struct ldb_val *(struct ldb_dn *) +ldb_dn_has_extended: bool (struct ldb_dn *) +ldb_dn_is_null: bool (struct ldb_dn *) +ldb_dn_is_special: bool (struct ldb_dn *) +ldb_dn_is_valid: bool (struct ldb_dn *) +ldb_dn_map_local: struct ldb_dn *(struct ldb_module *, void *, struct ldb_dn *) +ldb_dn_map_rebase_remote: struct ldb_dn *(struct ldb_module *, void *, struct ldb_dn *) +ldb_dn_map_remote: struct ldb_dn *(struct ldb_module *, void *, struct ldb_dn *) +ldb_dn_minimise: bool (struct ldb_dn *) +ldb_dn_new: struct ldb_dn *(TALLOC_CTX *, struct ldb_context *, const char *) +ldb_dn_new_fmt: struct ldb_dn *(TALLOC_CTX *, struct ldb_context *, const char *, ...) +ldb_dn_remove_base_components: bool (struct ldb_dn *, unsigned int) +ldb_dn_remove_child_components: bool (struct ldb_dn *, unsigned int) +ldb_dn_remove_extended_components: void (struct ldb_dn *) +ldb_dn_replace_components: bool (struct ldb_dn *, struct ldb_dn *) +ldb_dn_set_component: int (struct ldb_dn *, int, const char *, const struct ldb_val) +ldb_dn_set_extended_component: int (struct ldb_dn *, const char *, const struct ldb_val *) +ldb_dn_update_components: int (struct ldb_dn *, const struct ldb_dn *) +ldb_dn_validate: bool (struct ldb_dn *) +ldb_dump_results: void (struct ldb_context *, struct ldb_result *, FILE *) +ldb_error_at: int (struct ldb_context *, int, const char *, const char *, int) +ldb_errstring: const char *(struct ldb_context *) +ldb_extended: int (struct ldb_context *, const char *, void *, struct ldb_result **) +ldb_extended_default_callback: int (struct ldb_request *, struct ldb_reply *) +ldb_filter_attrs: int (struct ldb_context *, const struct ldb_message *, const char * const *, struct ldb_message *) +ldb_filter_from_tree: char *(TALLOC_CTX *, const struct ldb_parse_tree *) +ldb_get_config_basedn: struct ldb_dn *(struct ldb_context *) +ldb_get_create_perms: unsigned int (struct ldb_context *) +ldb_get_default_basedn: struct ldb_dn *(struct ldb_context *) +ldb_get_event_context: struct tevent_context *(struct ldb_context *) +ldb_get_flags: unsigned int (struct ldb_context *) +ldb_get_opaque: void *(struct ldb_context *, const char *) +ldb_get_root_basedn: struct ldb_dn *(struct ldb_context *) +ldb_get_schema_basedn: struct ldb_dn *(struct ldb_context *) +ldb_global_init: int (void) +ldb_handle_get_event_context: struct tevent_context *(struct ldb_handle *) +ldb_handle_new: struct ldb_handle *(TALLOC_CTX *, struct ldb_context *) +ldb_handle_use_global_event_context: void (struct ldb_handle *) +ldb_handler_copy: int (struct ldb_context *, void *, const struct ldb_val *, struct ldb_val *) +ldb_handler_fold: int (struct ldb_context *, void *, const struct ldb_val *, struct ldb_val *) +ldb_init: struct ldb_context *(TALLOC_CTX *, struct tevent_context *) +ldb_ldif_message_redacted_string: char *(struct ldb_context *, TALLOC_CTX *, enum ldb_changetype, const struct ldb_message *) +ldb_ldif_message_string: char *(struct ldb_context *, TALLOC_CTX *, enum ldb_changetype, const struct ldb_message *) +ldb_ldif_parse_modrdn: int (struct ldb_context *, const struct ldb_ldif *, TALLOC_CTX *, struct ldb_dn **, struct ldb_dn **, bool *, struct ldb_dn **, struct ldb_dn **) +ldb_ldif_read: struct ldb_ldif *(struct ldb_context *, int (*)(void *), void *) +ldb_ldif_read_file: struct ldb_ldif *(struct ldb_context *, FILE *) +ldb_ldif_read_file_state: struct ldb_ldif *(struct ldb_context *, struct ldif_read_file_state *) +ldb_ldif_read_free: void (struct ldb_context *, struct ldb_ldif *) +ldb_ldif_read_string: struct ldb_ldif *(struct ldb_context *, const char **) +ldb_ldif_write: int (struct ldb_context *, int (*)(void *, const char *, ...), void *, const struct ldb_ldif *) +ldb_ldif_write_file: int (struct ldb_context *, FILE *, const struct ldb_ldif *) +ldb_ldif_write_redacted_trace_string: char *(struct ldb_context *, TALLOC_CTX *, const struct ldb_ldif *) +ldb_ldif_write_string: char *(struct ldb_context *, TALLOC_CTX *, const struct ldb_ldif *) +ldb_load_modules: int (struct ldb_context *, const char **) +ldb_map_add: int (struct ldb_module *, struct ldb_request *) +ldb_map_delete: int (struct ldb_module *, struct ldb_request *) +ldb_map_init: int (struct ldb_module *, const struct ldb_map_attribute *, const struct ldb_map_objectclass *, const char * const *, const char *, const char *) +ldb_map_modify: int (struct ldb_module *, struct ldb_request *) +ldb_map_rename: int (struct ldb_module *, struct ldb_request *) +ldb_map_search: int (struct ldb_module *, struct ldb_request *) +ldb_match_message: int (struct ldb_context *, const struct ldb_message *, const struct ldb_parse_tree *, enum ldb_scope, bool *) +ldb_match_msg: int (struct ldb_context *, const struct ldb_message *, const struct ldb_parse_tree *, struct ldb_dn *, enum ldb_scope) +ldb_match_msg_error: int (struct ldb_context *, const struct ldb_message *, const struct ldb_parse_tree *, struct ldb_dn *, enum ldb_scope, bool *) +ldb_match_msg_objectclass: int (const struct ldb_message *, const char *) +ldb_mod_register_control: int (struct ldb_module *, const char *) +ldb_modify: int (struct ldb_context *, const struct ldb_message *) +ldb_modify_default_callback: int (struct ldb_request *, struct ldb_reply *) +ldb_module_call_chain: char *(struct ldb_request *, TALLOC_CTX *) +ldb_module_connect_backend: int (struct ldb_context *, const char *, const char **, struct ldb_module **) +ldb_module_done: int (struct ldb_request *, struct ldb_control **, struct ldb_extended *, int) +ldb_module_flags: uint32_t (struct ldb_context *) +ldb_module_get_ctx: struct ldb_context *(struct ldb_module *) +ldb_module_get_name: const char *(struct ldb_module *) +ldb_module_get_ops: const struct ldb_module_ops *(struct ldb_module *) +ldb_module_get_private: void *(struct ldb_module *) +ldb_module_init_chain: int (struct ldb_context *, struct ldb_module *) +ldb_module_load_list: int (struct ldb_context *, const char **, struct ldb_module *, struct ldb_module **) +ldb_module_new: struct ldb_module *(TALLOC_CTX *, struct ldb_context *, const char *, const struct ldb_module_ops *) +ldb_module_next: struct ldb_module *(struct ldb_module *) +ldb_module_popt_options: struct poptOption **(struct ldb_context *) +ldb_module_send_entry: int (struct ldb_request *, struct ldb_message *, struct ldb_control **) +ldb_module_send_referral: int (struct ldb_request *, char *) +ldb_module_set_next: void (struct ldb_module *, struct ldb_module *) +ldb_module_set_private: void (struct ldb_module *, void *) +ldb_modules_hook: int (struct ldb_context *, enum ldb_module_hook_type) +ldb_modules_list_from_string: const char **(struct ldb_context *, TALLOC_CTX *, const char *) +ldb_modules_load: int (const char *, const char *) +ldb_msg_add: int (struct ldb_message *, const struct ldb_message_element *, int) +ldb_msg_add_empty: int (struct ldb_message *, const char *, int, struct ldb_message_element **) +ldb_msg_add_fmt: int (struct ldb_message *, const char *, const char *, ...) +ldb_msg_add_linearized_dn: int (struct ldb_message *, const char *, struct ldb_dn *) +ldb_msg_add_steal_string: int (struct ldb_message *, const char *, char *) +ldb_msg_add_steal_value: int (struct ldb_message *, const char *, struct ldb_val *) +ldb_msg_add_string: int (struct ldb_message *, const char *, const char *) +ldb_msg_add_value: int (struct ldb_message *, const char *, const struct ldb_val *, struct ldb_message_element **) +ldb_msg_canonicalize: struct ldb_message *(struct ldb_context *, const struct ldb_message *) +ldb_msg_check_string_attribute: int (const struct ldb_message *, const char *, const char *) +ldb_msg_copy: struct ldb_message *(TALLOC_CTX *, const struct ldb_message *) +ldb_msg_copy_attr: int (struct ldb_message *, const char *, const char *) +ldb_msg_copy_shallow: struct ldb_message *(TALLOC_CTX *, const struct ldb_message *) +ldb_msg_diff: struct ldb_message *(struct ldb_context *, struct ldb_message *, struct ldb_message *) +ldb_msg_difference: int (struct ldb_context *, TALLOC_CTX *, struct ldb_message *, struct ldb_message *, struct ldb_message **) +ldb_msg_element_compare: int (struct ldb_message_element *, struct ldb_message_element *) +ldb_msg_element_compare_name: int (struct ldb_message_element *, struct ldb_message_element *) +ldb_msg_element_equal_ordered: bool (const struct ldb_message_element *, const struct ldb_message_element *) +ldb_msg_find_attr_as_bool: int (const struct ldb_message *, const char *, int) +ldb_msg_find_attr_as_dn: struct ldb_dn *(struct ldb_context *, TALLOC_CTX *, const struct ldb_message *, const char *) +ldb_msg_find_attr_as_double: double (const struct ldb_message *, const char *, double) +ldb_msg_find_attr_as_int: int (const struct ldb_message *, const char *, int) +ldb_msg_find_attr_as_int64: int64_t (const struct ldb_message *, const char *, int64_t) +ldb_msg_find_attr_as_string: const char *(const struct ldb_message *, const char *, const char *) +ldb_msg_find_attr_as_uint: unsigned int (const struct ldb_message *, const char *, unsigned int) +ldb_msg_find_attr_as_uint64: uint64_t (const struct ldb_message *, const char *, uint64_t) +ldb_msg_find_common_values: int (struct ldb_context *, TALLOC_CTX *, struct ldb_message_element *, struct ldb_message_element *, uint32_t) +ldb_msg_find_duplicate_val: int (struct ldb_context *, TALLOC_CTX *, const struct ldb_message_element *, struct ldb_val **, uint32_t) +ldb_msg_find_element: struct ldb_message_element *(const struct ldb_message *, const char *) +ldb_msg_find_ldb_val: const struct ldb_val *(const struct ldb_message *, const char *) +ldb_msg_find_val: struct ldb_val *(const struct ldb_message_element *, struct ldb_val *) +ldb_msg_new: struct ldb_message *(TALLOC_CTX *) +ldb_msg_normalize: int (struct ldb_context *, TALLOC_CTX *, const struct ldb_message *, struct ldb_message **) +ldb_msg_remove_attr: void (struct ldb_message *, const char *) +ldb_msg_remove_element: void (struct ldb_message *, struct ldb_message_element *) +ldb_msg_rename_attr: int (struct ldb_message *, const char *, const char *) +ldb_msg_sanity_check: int (struct ldb_context *, const struct ldb_message *) +ldb_msg_sort_elements: void (struct ldb_message *) +ldb_next_del_trans: int (struct ldb_module *) +ldb_next_end_trans: int (struct ldb_module *) +ldb_next_init: int (struct ldb_module *) +ldb_next_prepare_commit: int (struct ldb_module *) +ldb_next_read_lock: int (struct ldb_module *) +ldb_next_read_unlock: int (struct ldb_module *) +ldb_next_remote_request: int (struct ldb_module *, struct ldb_request *) +ldb_next_request: int (struct ldb_module *, struct ldb_request *) +ldb_next_start_trans: int (struct ldb_module *) +ldb_op_default_callback: int (struct ldb_request *, struct ldb_reply *) +ldb_options_copy: const char **(TALLOC_CTX *, const char **) +ldb_options_find: const char *(struct ldb_context *, const char **, const char *) +ldb_options_get: const char **(struct ldb_context *) +ldb_pack_data: int (struct ldb_context *, const struct ldb_message *, struct ldb_val *, uint32_t) +ldb_parse_control_from_string: struct ldb_control *(struct ldb_context *, TALLOC_CTX *, const char *) +ldb_parse_control_strings: struct ldb_control **(struct ldb_context *, TALLOC_CTX *, const char **) +ldb_parse_tree: struct ldb_parse_tree *(TALLOC_CTX *, const char *) +ldb_parse_tree_attr_replace: void (struct ldb_parse_tree *, const char *, const char *) +ldb_parse_tree_copy_shallow: struct ldb_parse_tree *(TALLOC_CTX *, const struct ldb_parse_tree *) +ldb_parse_tree_walk: int (struct ldb_parse_tree *, int (*)(struct ldb_parse_tree *, void *), void *) +ldb_qsort: void (void * const, size_t, size_t, void *, ldb_qsort_cmp_fn_t) +ldb_register_backend: int (const char *, ldb_connect_fn, bool) +ldb_register_extended_match_rule: int (struct ldb_context *, const struct ldb_extended_match_rule *) +ldb_register_hook: int (ldb_hook_fn) +ldb_register_module: int (const struct ldb_module_ops *) +ldb_rename: int (struct ldb_context *, struct ldb_dn *, struct ldb_dn *) +ldb_reply_add_control: int (struct ldb_reply *, const char *, bool, void *) +ldb_reply_get_control: struct ldb_control *(struct ldb_reply *, const char *) +ldb_req_get_custom_flags: uint32_t (struct ldb_request *) +ldb_req_is_untrusted: bool (struct ldb_request *) +ldb_req_location: const char *(struct ldb_request *) +ldb_req_mark_trusted: void (struct ldb_request *) +ldb_req_mark_untrusted: void (struct ldb_request *) +ldb_req_set_custom_flags: void (struct ldb_request *, uint32_t) +ldb_req_set_location: void (struct ldb_request *, const char *) +ldb_request: int (struct ldb_context *, struct ldb_request *) +ldb_request_add_control: int (struct ldb_request *, const char *, bool, void *) +ldb_request_done: int (struct ldb_request *, int) +ldb_request_get_control: struct ldb_control *(struct ldb_request *, const char *) +ldb_request_get_status: int (struct ldb_request *) +ldb_request_replace_control: int (struct ldb_request *, const char *, bool, void *) +ldb_request_set_state: void (struct ldb_request *, int) +ldb_reset_err_string: void (struct ldb_context *) +ldb_save_controls: int (struct ldb_control *, struct ldb_request *, struct ldb_control ***) +ldb_schema_attribute_add: int (struct ldb_context *, const char *, unsigned int, const char *) +ldb_schema_attribute_add_with_syntax: int (struct ldb_context *, const char *, unsigned int, const struct ldb_schema_syntax *) +ldb_schema_attribute_by_name: const struct ldb_schema_attribute *(struct ldb_context *, const char *) +ldb_schema_attribute_fill_with_syntax: int (struct ldb_context *, TALLOC_CTX *, const char *, unsigned int, const struct ldb_schema_syntax *, struct ldb_schema_attribute *) +ldb_schema_attribute_remove: void (struct ldb_context *, const char *) +ldb_schema_attribute_remove_flagged: void (struct ldb_context *, unsigned int) +ldb_schema_attribute_set_override_handler: void (struct ldb_context *, ldb_attribute_handler_override_fn_t, void *) +ldb_schema_set_override_GUID_index: void (struct ldb_context *, const char *, const char *) +ldb_schema_set_override_indexlist: void (struct ldb_context *, bool) +ldb_search: int (struct ldb_context *, TALLOC_CTX *, struct ldb_result **, struct ldb_dn *, enum ldb_scope, const char * const *, const char *, ...) +ldb_search_default_callback: int (struct ldb_request *, struct ldb_reply *) +ldb_sequence_number: int (struct ldb_context *, enum ldb_sequence_type, uint64_t *) +ldb_set_create_perms: void (struct ldb_context *, unsigned int) +ldb_set_debug: int (struct ldb_context *, void (*)(void *, enum ldb_debug_level, const char *, va_list), void *) +ldb_set_debug_stderr: int (struct ldb_context *) +ldb_set_default_dns: void (struct ldb_context *) +ldb_set_errstring: void (struct ldb_context *, const char *) +ldb_set_event_context: void (struct ldb_context *, struct tevent_context *) +ldb_set_flags: void (struct ldb_context *, unsigned int) +ldb_set_modules_dir: void (struct ldb_context *, const char *) +ldb_set_opaque: int (struct ldb_context *, const char *, void *) +ldb_set_require_private_event_context: void (struct ldb_context *) +ldb_set_timeout: int (struct ldb_context *, struct ldb_request *, int) +ldb_set_timeout_from_prev_req: int (struct ldb_context *, struct ldb_request *, struct ldb_request *) +ldb_set_utf8_default: void (struct ldb_context *) +ldb_set_utf8_fns: void (struct ldb_context *, void *, char *(*)(void *, void *, const char *, size_t)) +ldb_setup_wellknown_attributes: int (struct ldb_context *) +ldb_should_b64_encode: int (struct ldb_context *, const struct ldb_val *) +ldb_standard_syntax_by_name: const struct ldb_schema_syntax *(struct ldb_context *, const char *) +ldb_strerror: const char *(int) +ldb_string_to_time: time_t (const char *) +ldb_string_utc_to_time: time_t (const char *) +ldb_timestring: char *(TALLOC_CTX *, time_t) +ldb_timestring_utc: char *(TALLOC_CTX *, time_t) +ldb_transaction_cancel: int (struct ldb_context *) +ldb_transaction_cancel_noerr: int (struct ldb_context *) +ldb_transaction_commit: int (struct ldb_context *) +ldb_transaction_prepare_commit: int (struct ldb_context *) +ldb_transaction_start: int (struct ldb_context *) +ldb_unpack_data: int (struct ldb_context *, const struct ldb_val *, struct ldb_message *) +ldb_unpack_data_flags: int (struct ldb_context *, const struct ldb_val *, struct ldb_message *, unsigned int) +ldb_unpack_get_format: int (const struct ldb_val *, uint32_t *) +ldb_val_dup: struct ldb_val (TALLOC_CTX *, const struct ldb_val *) +ldb_val_equal_exact: int (const struct ldb_val *, const struct ldb_val *) +ldb_val_map_local: struct ldb_val (struct ldb_module *, void *, const struct ldb_map_attribute *, const struct ldb_val *) +ldb_val_map_remote: struct ldb_val (struct ldb_module *, void *, const struct ldb_map_attribute *, const struct ldb_val *) +ldb_val_string_cmp: int (const struct ldb_val *, const char *) +ldb_val_to_time: int (const struct ldb_val *, time_t *) +ldb_valid_attr_name: int (const char *) +ldb_vdebug: void (struct ldb_context *, enum ldb_debug_level, const char *, va_list) +ldb_wait: int (struct ldb_handle *, enum ldb_wait_type) diff --git a/lib/ldb/ABI/pyldb-util-2.0.10.sigs b/lib/ldb/ABI/pyldb-util-2.0.10.sigs new file mode 100644 index 00000000000..74d6719d2bc --- /dev/null +++ b/lib/ldb/ABI/pyldb-util-2.0.10.sigs @@ -0,0 +1,2 @@ +pyldb_Dn_FromDn: PyObject *(struct ldb_dn *) +pyldb_Object_AsDn: bool (TALLOC_CTX *, PyObject *, struct ldb_context *, struct ldb_dn **) diff --git a/lib/ldb/wscript b/lib/ldb/wscript index c3f2f42b8c1..43571a7593f 100644 --- a/lib/ldb/wscript +++ b/lib/ldb/wscript @@ -1,7 +1,7 @@ #!/usr/bin/env python APPNAME = 'ldb' -VERSION = '2.0.9' +VERSION = '2.0.10' import sys, os -- 2.17.1 From f931ec7b3875442d0c097c62b8e82bee4cb65584 Mon Sep 17 00:00:00 2001 From: Gary Lockyer Date: Fri, 3 Apr 2020 12:18:03 +1300 Subject: [PATCH 05/12] CVE-2020-10704: lib util asn1: Add ASN.1 max tree depth Add maximum parse tree depth to the call to asn1_init, which will be used to limit the depth of the ASN.1 parse tree. Credit to OSS-Fuzz REF: https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=20454 BUG: https://bugzilla.samba.org/show_bug.cgi?id=14334 Signed-off-by: Gary Lockyer Reviewed-by: Andrew Bartlett --- auth/gensec/gensec_util.c | 2 +- lib/util/asn1.c | 17 +++++++++- lib/util/asn1.h | 9 +++++- lib/util/tests/asn1_tests.c | 2 +- libcli/auth/spnego_parse.c | 6 ++-- libcli/cldap/cldap.c | 2 +- libcli/ldap/ldap_message.c | 2 +- source3/lib/tldap.c | 4 +-- source3/lib/tldap_util.c | 4 +-- source3/libsmb/clispnego.c | 4 +-- source3/torture/torture.c | 2 +- source4/auth/gensec/gensec_krb5.c | 4 +-- source4/ldap_server/ldap_server.c | 2 +- source4/libcli/ldap/ldap_client.c | 2 +- source4/libcli/ldap/ldap_controls.c | 48 ++++++++++++++--------------- 15 files changed, 66 insertions(+), 44 deletions(-) diff --git a/auth/gensec/gensec_util.c b/auth/gensec/gensec_util.c index 20c9c2a1fbb..e185acc0c20 100644 --- a/auth/gensec/gensec_util.c +++ b/auth/gensec/gensec_util.c @@ -76,7 +76,7 @@ NTSTATUS gensec_generate_session_info_pac(TALLOC_CTX *mem_ctx, static bool gensec_gssapi_check_oid(const DATA_BLOB *blob, const char *oid) { bool ret = false; - struct asn1_data *data = asn1_init(NULL); + struct asn1_data *data = asn1_init(NULL, ASN1_MAX_TREE_DEPTH); if (!data) return false; diff --git a/lib/util/asn1.c b/lib/util/asn1.c index 51da5424956..ec6e674ce20 100644 --- a/lib/util/asn1.c +++ b/lib/util/asn1.c @@ -36,15 +36,19 @@ struct asn1_data { off_t ofs; struct nesting *nesting; bool has_error; + unsigned depth; + unsigned max_depth; }; /* allocate an asn1 structure */ -struct asn1_data *asn1_init(TALLOC_CTX *mem_ctx) +struct asn1_data *asn1_init(TALLOC_CTX *mem_ctx, unsigned max_depth) { struct asn1_data *ret = talloc_zero(mem_ctx, struct asn1_data); if (ret == NULL) { DEBUG(0,("asn1_init failed! out of memory\n")); + return ret; } + ret->max_depth = max_depth; return ret; } @@ -480,6 +484,11 @@ bool asn1_check_BOOLEAN(struct asn1_data *data, bool v) /* load a struct asn1_data structure with a lump of data, ready to be parsed */ bool asn1_load(struct asn1_data *data, DATA_BLOB blob) { + /* + * Save the maximum depth + */ + unsigned max_depth = data->max_depth; + ZERO_STRUCTP(data); data->data = (uint8_t *)talloc_memdup(data, blob.data, blob.length); if (!data->data) { @@ -487,6 +496,7 @@ bool asn1_load(struct asn1_data *data, DATA_BLOB blob) return false; } data->length = blob.length; + data->max_depth = max_depth; return true; } @@ -1103,9 +1113,14 @@ bool asn1_extract_blob(struct asn1_data *asn1, TALLOC_CTX *mem_ctx, */ void asn1_load_nocopy(struct asn1_data *data, uint8_t *buf, size_t len) { + /* + * Save max_depth + */ + unsigned max_depth = data->max_depth; ZERO_STRUCTP(data); data->data = buf; data->length = len; + data->max_depth = max_depth; } int asn1_peek_full_tag(DATA_BLOB blob, uint8_t tag, size_t *packet_size) diff --git a/lib/util/asn1.h b/lib/util/asn1.h index ddd69863574..fc365724e93 100644 --- a/lib/util/asn1.h +++ b/lib/util/asn1.h @@ -45,7 +45,14 @@ typedef struct asn1_data ASN1_DATA; #define ASN1_MAX_OIDS 20 -struct asn1_data *asn1_init(TALLOC_CTX *mem_ctx); +/* + * The maximum permitted depth for an ASN.1 parse tree, the limit is chosen + * to align with the value for windows. Note that this value will trigger + * ASAN stack overflow errors. + */ +#define ASN1_MAX_TREE_DEPTH 512 + +struct asn1_data *asn1_init(TALLOC_CTX *mem_ctx, unsigned max_depth); void asn1_free(struct asn1_data *data); bool asn1_has_error(const struct asn1_data *data); void asn1_set_error(struct asn1_data *data); diff --git a/lib/util/tests/asn1_tests.c b/lib/util/tests/asn1_tests.c index e4b386ad785..ab5262c4ffb 100644 --- a/lib/util/tests/asn1_tests.c +++ b/lib/util/tests/asn1_tests.c @@ -330,7 +330,7 @@ static bool test_asn1_Integer(struct torture_context *tctx) DATA_BLOB blob; int val; - data = asn1_init(mem_ctx); + data = asn1_init(mem_ctx, ASN1_MAX_TREE_DEPTH); if (!data) { goto err; } diff --git a/libcli/auth/spnego_parse.c b/libcli/auth/spnego_parse.c index f538b44552c..f7f19b10778 100644 --- a/libcli/auth/spnego_parse.c +++ b/libcli/auth/spnego_parse.c @@ -296,7 +296,7 @@ ssize_t spnego_read_data(TALLOC_CTX *mem_ctx, DATA_BLOB data, struct spnego_data return ret; } - asn1 = asn1_init(mem_ctx); + asn1 = asn1_init(mem_ctx, ASN1_MAX_TREE_DEPTH); if (asn1 == NULL) { return -1; } @@ -339,7 +339,7 @@ ssize_t spnego_read_data(TALLOC_CTX *mem_ctx, DATA_BLOB data, struct spnego_data ssize_t spnego_write_data(TALLOC_CTX *mem_ctx, DATA_BLOB *blob, struct spnego_data *spnego) { - struct asn1_data *asn1 = asn1_init(mem_ctx); + struct asn1_data *asn1 = asn1_init(mem_ctx, ASN1_MAX_TREE_DEPTH); ssize_t ret = -1; if (asn1 == NULL) { @@ -411,7 +411,7 @@ bool spnego_write_mech_types(TALLOC_CTX *mem_ctx, DATA_BLOB *blob) { bool ret = false; - struct asn1_data *asn1 = asn1_init(mem_ctx); + struct asn1_data *asn1 = asn1_init(mem_ctx, ASN1_MAX_TREE_DEPTH); if (asn1 == NULL) { return false; diff --git a/libcli/cldap/cldap.c b/libcli/cldap/cldap.c index daba37a21d7..8fa9ce0b273 100644 --- a/libcli/cldap/cldap.c +++ b/libcli/cldap/cldap.c @@ -229,7 +229,7 @@ static bool cldap_socket_recv_dgram(struct cldap_socket *c, goto error; } - asn1 = asn1_init(in); + asn1 = asn1_init(in, ASN1_MAX_TREE_DEPTH); if (!asn1) { goto nomem; } diff --git a/libcli/ldap/ldap_message.c b/libcli/ldap/ldap_message.c index f21598374a1..ba82bddeab1 100644 --- a/libcli/ldap/ldap_message.c +++ b/libcli/ldap/ldap_message.c @@ -390,7 +390,7 @@ _PUBLIC_ bool ldap_encode(struct ldap_message *msg, const struct ldap_control_handler *control_handlers, DATA_BLOB *result, TALLOC_CTX *mem_ctx) { - struct asn1_data *data = asn1_init(mem_ctx); + struct asn1_data *data = asn1_init(mem_ctx, ASN1_MAX_TREE_DEPTH); int i, j; if (!data) return false; diff --git a/source3/lib/tldap.c b/source3/lib/tldap.c index d6c6e8859a6..bf5fc05d785 100644 --- a/source3/lib/tldap.c +++ b/source3/lib/tldap.c @@ -632,7 +632,7 @@ static void tldap_msg_received(struct tevent_req *subreq) goto fail; } - data = asn1_init(talloc_tos()); + data = asn1_init(talloc_tos(), ASN1_MAX_TREE_DEPTH); if (data == NULL) { status = TLDAP_NO_MEMORY; goto fail; @@ -763,7 +763,7 @@ static struct tevent_req *tldap_req_create(TALLOC_CTX *mem_ctx, if (req == NULL) { return NULL; } - state->out = asn1_init(state); + state->out = asn1_init(state, ASN1_MAX_TREE_DEPTH); if (state->out == NULL) { goto err; } diff --git a/source3/lib/tldap_util.c b/source3/lib/tldap_util.c index 1b86962a32e..168932a8a96 100644 --- a/source3/lib/tldap_util.c +++ b/source3/lib/tldap_util.c @@ -644,7 +644,7 @@ static struct tevent_req *tldap_ship_paged_search( struct tldap_control *pgctrl; struct asn1_data *asn1 = NULL; - asn1 = asn1_init(state); + asn1 = asn1_init(state, ASN1_MAX_TREE_DEPTH); if (asn1 == NULL) { return NULL; } @@ -783,7 +783,7 @@ static void tldap_search_paged_done(struct tevent_req *subreq) TALLOC_FREE(state->cookie.data); - asn1 = asn1_init(talloc_tos()); + asn1 = asn1_init(talloc_tos(), ASN1_MAX_TREE_DEPTH); if (tevent_req_nomem(asn1, req)) { return; } diff --git a/source3/libsmb/clispnego.c b/source3/libsmb/clispnego.c index 4a0fbcd73af..1608f6a9960 100644 --- a/source3/libsmb/clispnego.c +++ b/source3/libsmb/clispnego.c @@ -50,7 +50,7 @@ bool spnego_parse_negTokenInit(TALLOC_CTX *ctx, *secblob = data_blob_null; } - data = asn1_init(talloc_tos()); + data = asn1_init(talloc_tos(), ASN1_MAX_TREE_DEPTH); if (data == NULL) { return false; } @@ -171,7 +171,7 @@ DATA_BLOB spnego_gen_krb5_wrap(TALLOC_CTX *ctx, const DATA_BLOB ticket, const ui ASN1_DATA *data; DATA_BLOB ret = data_blob_null; - data = asn1_init(talloc_tos()); + data = asn1_init(talloc_tos(), ASN1_MAX_TREE_DEPTH); if (data == NULL) { return data_blob_null; } diff --git a/source3/torture/torture.c b/source3/torture/torture.c index a795e61125f..c4b0a7bc4f9 100644 --- a/source3/torture/torture.c +++ b/source3/torture/torture.c @@ -11370,7 +11370,7 @@ tldap_build_extended_control(enum tldap_extended_val val) ZERO_STRUCT(empty_control); if (val != EXTENDED_NONE) { - data = asn1_init(talloc_tos()); + data = asn1_init(talloc_tos(), ASN1_MAX_TREE_DEPTH); if (!data) { return NULL; diff --git a/source4/auth/gensec/gensec_krb5.c b/source4/auth/gensec/gensec_krb5.c index 0323da87d29..b735063656a 100644 --- a/source4/auth/gensec/gensec_krb5.c +++ b/source4/auth/gensec/gensec_krb5.c @@ -444,7 +444,7 @@ static DATA_BLOB gensec_gssapi_gen_krb5_wrap(TALLOC_CTX *mem_ctx, const DATA_BLO struct asn1_data *data; DATA_BLOB ret = data_blob_null; - data = asn1_init(mem_ctx); + data = asn1_init(mem_ctx, ASN1_MAX_TREE_DEPTH); if (!data || !ticket->data) { return ret; } @@ -478,7 +478,7 @@ static DATA_BLOB gensec_gssapi_gen_krb5_wrap(TALLOC_CTX *mem_ctx, const DATA_BLO static bool gensec_gssapi_parse_krb5_wrap(TALLOC_CTX *mem_ctx, const DATA_BLOB *blob, DATA_BLOB *ticket, uint8_t tok_id[2]) { bool ret = false; - struct asn1_data *data = asn1_init(mem_ctx); + struct asn1_data *data = asn1_init(mem_ctx, ASN1_MAX_TREE_DEPTH); int data_remaining; if (!data) { diff --git a/source4/ldap_server/ldap_server.c b/source4/ldap_server/ldap_server.c index 709b7bcacfa..6d329329909 100644 --- a/source4/ldap_server/ldap_server.c +++ b/source4/ldap_server/ldap_server.c @@ -560,7 +560,7 @@ static void ldapsrv_call_read_done(struct tevent_req *subreq) return; } - asn1 = asn1_init(call); + asn1 = asn1_init(call, ASN1_MAX_TREE_DEPTH); if (asn1 == NULL) { ldapsrv_terminate_connection(conn, "no memory"); return; diff --git a/source4/libcli/ldap/ldap_client.c b/source4/libcli/ldap/ldap_client.c index da84adc7769..2d75af6af6e 100644 --- a/source4/libcli/ldap/ldap_client.c +++ b/source4/libcli/ldap/ldap_client.c @@ -284,7 +284,7 @@ static void ldap_connection_recv_done(struct tevent_req *subreq) return; } - asn1 = asn1_init(conn); + asn1 = asn1_init(conn, ASN1_MAX_TREE_DEPTH); if (asn1 == NULL) { TALLOC_FREE(msg); ldap_error_handler(conn, NT_STATUS_NO_MEMORY); diff --git a/source4/libcli/ldap/ldap_controls.c b/source4/libcli/ldap/ldap_controls.c index 716ca148308..df012a158e0 100644 --- a/source4/libcli/ldap/ldap_controls.c +++ b/source4/libcli/ldap/ldap_controls.c @@ -32,7 +32,7 @@ static bool decode_server_sort_response(void *mem_ctx, DATA_BLOB in, void *_out) { void **out = (void **)_out; DATA_BLOB attr; - struct asn1_data *data = asn1_init(mem_ctx); + struct asn1_data *data = asn1_init(mem_ctx, ASN1_MAX_TREE_DEPTH); struct ldb_sort_resp_control *lsrc; if (!data) return false; @@ -79,7 +79,7 @@ static bool decode_server_sort_request(void *mem_ctx, DATA_BLOB in, void *_out) void **out = (void **)_out; DATA_BLOB attr; DATA_BLOB rule; - struct asn1_data *data = asn1_init(mem_ctx); + struct asn1_data *data = asn1_init(mem_ctx, ASN1_MAX_TREE_DEPTH); struct ldb_server_sort_control **lssc; int num; @@ -166,7 +166,7 @@ static bool decode_extended_dn_request(void *mem_ctx, DATA_BLOB in, void *_out) return true; } - data = asn1_init(mem_ctx); + data = asn1_init(mem_ctx, ASN1_MAX_TREE_DEPTH); if (!data) return false; if (!asn1_load(data, in)) { @@ -198,7 +198,7 @@ static bool decode_extended_dn_request(void *mem_ctx, DATA_BLOB in, void *_out) static bool decode_sd_flags_request(void *mem_ctx, DATA_BLOB in, void *_out) { void **out = (void **)_out; - struct asn1_data *data = asn1_init(mem_ctx); + struct asn1_data *data = asn1_init(mem_ctx, ASN1_MAX_TREE_DEPTH); struct ldb_sd_flags_control *lsdfc; if (!data) return false; @@ -232,7 +232,7 @@ static bool decode_sd_flags_request(void *mem_ctx, DATA_BLOB in, void *_out) static bool decode_search_options_request(void *mem_ctx, DATA_BLOB in, void *_out) { void **out = (void **)_out; - struct asn1_data *data = asn1_init(mem_ctx); + struct asn1_data *data = asn1_init(mem_ctx, ASN1_MAX_TREE_DEPTH); struct ldb_search_options_control *lsoc; if (!data) return false; @@ -267,7 +267,7 @@ static bool decode_paged_results_request(void *mem_ctx, DATA_BLOB in, void *_out { void **out = (void **)_out; DATA_BLOB cookie; - struct asn1_data *data = asn1_init(mem_ctx); + struct asn1_data *data = asn1_init(mem_ctx, ASN1_MAX_TREE_DEPTH); struct ldb_paged_control *lprc; if (!data) return false; @@ -316,7 +316,7 @@ static bool decode_dirsync_request(void *mem_ctx, DATA_BLOB in, void *_out) { void **out = (void **)_out; DATA_BLOB cookie; - struct asn1_data *data = asn1_init(mem_ctx); + struct asn1_data *data = asn1_init(mem_ctx, ASN1_MAX_TREE_DEPTH); struct ldb_dirsync_control *ldc; if (!data) return false; @@ -372,7 +372,7 @@ static bool decode_asq_control(void *mem_ctx, DATA_BLOB in, void *_out) { void **out = (void **)_out; DATA_BLOB source_attribute; - struct asn1_data *data = asn1_init(mem_ctx); + struct asn1_data *data = asn1_init(mem_ctx, ASN1_MAX_TREE_DEPTH); struct ldb_asq_control *lac; if (!data) return false; @@ -433,7 +433,7 @@ static bool decode_verify_name_request(void *mem_ctx, DATA_BLOB in, void *_out) { void **out = (void **)_out; DATA_BLOB name; - struct asn1_data *data = asn1_init(mem_ctx); + struct asn1_data *data = asn1_init(mem_ctx, ASN1_MAX_TREE_DEPTH); struct ldb_verify_name_control *lvnc; int len; @@ -485,7 +485,7 @@ static bool decode_verify_name_request(void *mem_ctx, DATA_BLOB in, void *_out) static bool encode_verify_name_request(void *mem_ctx, void *in, DATA_BLOB *out) { struct ldb_verify_name_control *lvnc = talloc_get_type(in, struct ldb_verify_name_control); - struct asn1_data *data = asn1_init(mem_ctx); + struct asn1_data *data = asn1_init(mem_ctx, ASN1_MAX_TREE_DEPTH); DATA_BLOB gc_utf16; if (!data) return false; @@ -528,7 +528,7 @@ static bool decode_vlv_request(void *mem_ctx, DATA_BLOB in, void *_out) { void **out = (void **)_out; DATA_BLOB assertion_value, context_id; - struct asn1_data *data = asn1_init(mem_ctx); + struct asn1_data *data = asn1_init(mem_ctx, ASN1_MAX_TREE_DEPTH); struct ldb_vlv_req_control *lvrc; if (!data) return false; @@ -626,7 +626,7 @@ static bool decode_vlv_response(void *mem_ctx, DATA_BLOB in, void *_out) { void **out = (void **)_out; DATA_BLOB context_id; - struct asn1_data *data = asn1_init(mem_ctx); + struct asn1_data *data = asn1_init(mem_ctx, ASN1_MAX_TREE_DEPTH); struct ldb_vlv_resp_control *lvrc; if (!data) return false; @@ -682,7 +682,7 @@ static bool decode_vlv_response(void *mem_ctx, DATA_BLOB in, void *_out) static bool encode_server_sort_response(void *mem_ctx, void *in, DATA_BLOB *out) { struct ldb_sort_resp_control *lsrc = talloc_get_type(in, struct ldb_sort_resp_control); - struct asn1_data *data = asn1_init(mem_ctx); + struct asn1_data *data = asn1_init(mem_ctx, ASN1_MAX_TREE_DEPTH); if (!data) return false; @@ -716,7 +716,7 @@ static bool encode_server_sort_response(void *mem_ctx, void *in, DATA_BLOB *out) static bool encode_server_sort_request(void *mem_ctx, void *in, DATA_BLOB *out) { struct ldb_server_sort_control **lssc = talloc_get_type(in, struct ldb_server_sort_control *); - struct asn1_data *data = asn1_init(mem_ctx); + struct asn1_data *data = asn1_init(mem_ctx, ASN1_MAX_TREE_DEPTH); int num; if (!data) return false; @@ -782,7 +782,7 @@ static bool encode_extended_dn_request(void *mem_ctx, void *in, DATA_BLOB *out) return true; } - data = asn1_init(mem_ctx); + data = asn1_init(mem_ctx, ASN1_MAX_TREE_DEPTH); if (!data) return false; @@ -810,7 +810,7 @@ static bool encode_extended_dn_request(void *mem_ctx, void *in, DATA_BLOB *out) static bool encode_sd_flags_request(void *mem_ctx, void *in, DATA_BLOB *out) { struct ldb_sd_flags_control *lsdfc = talloc_get_type(in, struct ldb_sd_flags_control); - struct asn1_data *data = asn1_init(mem_ctx); + struct asn1_data *data = asn1_init(mem_ctx, ASN1_MAX_TREE_DEPTH); if (!data) return false; @@ -838,7 +838,7 @@ static bool encode_sd_flags_request(void *mem_ctx, void *in, DATA_BLOB *out) static bool encode_search_options_request(void *mem_ctx, void *in, DATA_BLOB *out) { struct ldb_search_options_control *lsoc = talloc_get_type(in, struct ldb_search_options_control); - struct asn1_data *data = asn1_init(mem_ctx); + struct asn1_data *data = asn1_init(mem_ctx, ASN1_MAX_TREE_DEPTH); if (!data) return false; @@ -866,7 +866,7 @@ static bool encode_search_options_request(void *mem_ctx, void *in, DATA_BLOB *ou static bool encode_paged_results_request(void *mem_ctx, void *in, DATA_BLOB *out) { struct ldb_paged_control *lprc = talloc_get_type(in, struct ldb_paged_control); - struct asn1_data *data = asn1_init(mem_ctx); + struct asn1_data *data = asn1_init(mem_ctx, ASN1_MAX_TREE_DEPTH); if (!data) return false; @@ -901,7 +901,7 @@ static bool encode_paged_results_request(void *mem_ctx, void *in, DATA_BLOB *out static bool encode_asq_control(void *mem_ctx, void *in, DATA_BLOB *out) { struct ldb_asq_control *lac = talloc_get_type(in, struct ldb_asq_control); - struct asn1_data *data = asn1_init(mem_ctx); + struct asn1_data *data = asn1_init(mem_ctx, ASN1_MAX_TREE_DEPTH); if (!data) return false; @@ -936,7 +936,7 @@ static bool encode_asq_control(void *mem_ctx, void *in, DATA_BLOB *out) static bool encode_dirsync_request(void *mem_ctx, void *in, DATA_BLOB *out) { struct ldb_dirsync_control *ldc = talloc_get_type(in, struct ldb_dirsync_control); - struct asn1_data *data = asn1_init(mem_ctx); + struct asn1_data *data = asn1_init(mem_ctx, ASN1_MAX_TREE_DEPTH); if (!data) return false; @@ -972,7 +972,7 @@ static bool encode_dirsync_request(void *mem_ctx, void *in, DATA_BLOB *out) static bool encode_vlv_request(void *mem_ctx, void *in, DATA_BLOB *out) { struct ldb_vlv_req_control *lvrc = talloc_get_type(in, struct ldb_vlv_req_control); - struct asn1_data *data = asn1_init(mem_ctx); + struct asn1_data *data = asn1_init(mem_ctx, ASN1_MAX_TREE_DEPTH); if (!data) return false; @@ -1040,7 +1040,7 @@ static bool encode_vlv_request(void *mem_ctx, void *in, DATA_BLOB *out) static bool encode_vlv_response(void *mem_ctx, void *in, DATA_BLOB *out) { struct ldb_vlv_resp_control *lvrc = talloc_get_type(in, struct ldb_vlv_resp_control); - struct asn1_data *data = asn1_init(mem_ctx); + struct asn1_data *data = asn1_init(mem_ctx, ASN1_MAX_TREE_DEPTH); if (!data) return false; @@ -1083,7 +1083,7 @@ static bool encode_openldap_dereference(void *mem_ctx, void *in, DATA_BLOB *out) { struct dsdb_openldap_dereference_control *control = talloc_get_type(in, struct dsdb_openldap_dereference_control); int i,j; - struct asn1_data *data = asn1_init(mem_ctx); + struct asn1_data *data = asn1_init(mem_ctx, ASN1_MAX_TREE_DEPTH); if (!data) return false; @@ -1132,7 +1132,7 @@ static bool encode_openldap_dereference(void *mem_ctx, void *in, DATA_BLOB *out) static bool decode_openldap_dereference(void *mem_ctx, DATA_BLOB in, void *_out) { void **out = (void **)_out; - struct asn1_data *data = asn1_init(mem_ctx); + struct asn1_data *data = asn1_init(mem_ctx, ASN1_MAX_TREE_DEPTH); struct dsdb_openldap_dereference_result_control *control; struct dsdb_openldap_dereference_result **r = NULL; int i = 0; -- 2.17.1 From f9d5c246f8ac0bc14706059df5c615acddef13db Mon Sep 17 00:00:00 2001 From: Gary Lockyer Date: Thu, 2 Apr 2020 15:25:53 +1300 Subject: [PATCH 06/12] CVE-2020-10704: libcli ldap: test recursion depth in ldap_decode_filter_tree Add tests to check that ASN.1 ldap requests with deeply nested elements are rejected. Previously there was no check on the on the depth of nesting and excessive nesting could cause a stack overflow. Credit to OSS-Fuzz REF: https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=20454 BUG: https://bugzilla.samba.org/show_bug.cgi?id=14334 Signed-off-by: Gary Lockyer Reviewed-by: Andrew Bartlett --- libcli/ldap/tests/data/10000-or.dat | Bin 0 -> 39875 bytes libcli/ldap/tests/data/ldap-recursive.dat | Bin 0 -> 970 bytes libcli/ldap/tests/ldap_message_test.c | 271 ++++++++++++++++++++++ libcli/ldap/wscript_build | 15 ++ selftest/knownfail.d/ldap_message | 2 + source4/selftest/tests.py | 2 + 6 files changed, 290 insertions(+) create mode 100644 libcli/ldap/tests/data/10000-or.dat create mode 100644 libcli/ldap/tests/data/ldap-recursive.dat create mode 100644 libcli/ldap/tests/ldap_message_test.c create mode 100644 selftest/knownfail.d/ldap_message diff --git a/libcli/ldap/tests/data/10000-or.dat b/libcli/ldap/tests/data/10000-or.dat new file mode 100644 index 0000000000000000000000000000000000000000..e2d6de2ce330e80d1c2f28854b3542b789de9834 GIT binary patch literal 39875 zc-kD-rSAaXyYS`roI!w%Kp?Q+*WKOS-QC^Y-QC^K-f#keKp?O|V2nT@5C{YUfj}S- z2m}Hf7i{OauGr+-)f^si@s|BwD}{a^Y&^?&Gp*Z-#fRsW0rXZ=t5AN4=zzt?}K|5pFC{ww_# z`p@;B=|9zfBK|-AKmI@dKmI@dKmI@dKmI@dKmI@dKmI@dKmI@dKmI@dKmI@dKmI@d zKmI@dKmI@dKmI@dKmI@dKmI@dKmI@dKmI@dKmI@dKmI@dKmK3-U;bbIU;bbIU;bbI zU;bbIU;bbIU;bbIU;bbIU;bbIU;bbIU;bbIU;bbIU;bbIU;bbIU;bbIU;bbIU;bbI zU;bbIU;bbIU;bbIU;bbIU;bbIU;bbIU;bbIU;bbIU;bbIU;bbIU;bbIU;bbIU;bbI zU;bbIU;bbIU;ZEdAO0WyAO0WyAO0WyAO0WyAO0WyAO0WyAO0WyAO0WyAO0WyAO0Wy zAO0WyAO0WyAO0WyAO0WyAO0WyAO0WyAO0WyAO0WyAO0WyAO0WyAO0WyAO0WyAO0Wy zAO0WyAO0WyAO0WyAO0WyAO0WyAO0WyAO0WyAO0WyAO3ItZ~kxoZ~kxoZ~kxoZ~kxo zZ~kxoZ~kxoZ~kxoZ~kxoZ~kxoZ~kxoZ~kxoZ~kxoZ~kxoZ~kxoZ~kxoZ~kxoZ~kxo zZ~kxoZ~kxoZ~kxoZ~kxoZ~kxoZ~kxoZ~kxoZ~kxoZ~kxoZ~kxoZ~kxoZ~kxoZ~kxo zZ~kxoZ~ia-Fa9t7Fa9t7Fa9t7Fa9t7Fa9t7Fa9t7Fa9t7Fa9t7Fa9t7Fa9t7Fa9t7 zFa9t7Fa9t7Fa9t7Fa9t7Fa9t7Fa9t7Fa9t7Fa9t7Fa9t7Fa9t7Fa9t7Fa9t7Fa9t7 zFa9t7Fa9t7Fa9t7Fa9t7Fa9t7Fa9t7Fa9t7FaA&dPySE-PySE-PySE-PySE-PySE- zPySE-PySE-PySE-PySE-PySE-PySE-PySE-PySE-PySE-PySE-PySE-PySE-PySE- zPySE-PySE-PySE-PySE-PySE-PySE-PySE-PySE-PySE-PySE-PySE-PySE-PySE- zPyP@75B?AS5B?AS5B?AS5B?AS5B?AS5B?AS5B?AS5B?AS5B?AS5B?AS5B?AS5B?AS z5B?AS5B?AS5B?AS5B?AS5B?AS5B?AS5B?AS5B?AS5B?AS5B?AS5B?AS5B?AS5B?AS z5B?AS5B?AS5B?AS5B?AS5B?AS5B?AS5B_)lcm8+&cm8+&cm8+&cm8+&cm8+&cm8+& zcm8+&cm8+&cm8+&cm8+&cm8+&cm8+&cm8+&cm8+&cm8+&cm8+&cm8+&cm8+&cm8+& zcm8+&cm8+&cm8+&cm8+&cm8+&cm8+&cm8+&cm8+&cm8+&cm8+&cm8+&cm8+&cm6m2 zH~u&NH~u&NH~u&NH~u&NH~u&NH~u&NH~u&NH~u&NH~u&NH~u&NH~u&NH~u&NH~u&N zH~u&NH~u&NH~u&NH~u&NH~u&NH~u&NH~u&NH~u&NH~u&NH~u&NH~u&NH~u&NH~u&N zH~u&NH~u&NH~u&NH~u&NH~u&NH~v@tSN>Q2SN>Q2SN>Q2SN>Q2SN>Q2SN>Q2SN>Q2 zSN>Q2SN>Q2SN>Q2SN>Q2SN>Q2SN>Q2SN>Q2SN>Q2SN>Q2SN>Q2SN>Q2SN>Q2SN>Q2 zSN>Q2SN>Q2SN>Q2SN>Q2SN>Q2SN>Q2SN>Q2SN>Q2SN>Q2SN>Q2SN>Q2SN<3N7ycLi z7ycLi7ycLi7ycLi7ycLi7ycLi7ycLi7ycLi7ycLi7ycLi7ycLi7ycLi7ycLi7ycLi z7ycLi7ycLi7ycLi7ycLi7ycLi7ycLi7ycLi7ycLi7ycLi7ycLi7ycLi7ycLi7ycLi z7ycLi7ycLi7ycLi7ycLi7yf7dXZ~mYXZ~mYXZ~mYXZ~mYXZ~mYXZ~mYXZ~mYXZ~mY zXZ~mYXZ~mYXZ~mYXZ~mYXZ~mYXZ~mYXZ~mYXZ~mYXZ~mYXZ~mYXZ~mYXZ~mYXZ~mY zXZ~mYXZ~mYXZ~mYXZ~mYXZ~mYXZ~mYXZ~mYXZ~mYXZ~mYXZ~mYXZ|PtC;lh?C;lh? zC;lh?C;lh?C;lh?C;lh?C;lh?C;lh?C;lh?C;lh?C;lh?C;lh?C;lh?C;lh?C;lh? zC;lh?C;lh?C;lh?C;lh?C;lh?C;lh?C;lh?C;lh?C;lh?C;lh?C;lh?C;lh?C;lh? zC;lh?C;lh?C;lh?C;mtNNB&3tNB&3tNB&3tNB&3tNB&3tNB&3tNB&3tNB&3tNB&3t zNB&3tNB&3tNB&3tNB&3tNB&3tNB&3tNB&3tNB&3tNB&3tNB&3tNB&3tNB&3tNB&3t zNB&3tNB&3tNB&3tNB&3tNB&3tNB&3tNB&3tNB&3tNB&3tNB#%?2mS~C2mS~C2mS~C z2mS~C2mS~C2mS~C2mS~C2mS~C2mS~C2mS~C2mS~C2mS~C2mS~C2mS~C2mS~C2mS~C z2mS~C2mS~C2mS~C2mS~C2mS~C2mS~C2mS~C2mS~C2mS~C2mS~C2mS~C2mS~C2mS~C z2mS~C2mS~C2mX8hd;WX=d;WX=d;WX=d;WX=d;WX=d;WX=d;WX=d;WX=d;WX=d;WX= zd;WX=d;WX=d;WX=d;WX=d;WX=d;WX=d;WX=d;WX=d;WX=d;WX=d;WX=d;WX=d;WX= zd;WX=d;WX=d;WX=d;WX=d;WX=d;WX=d;WX=d;WX=d;UBAJN`TVJN`TVJN`TVJN`TV zJN`TVJN`TVJN`TVJN`TVJN`TVJN`TVJN`TVJN`TVJN`TVJN`TVJN`TVJN`TVJN`TV zJN`TVJN`TVJN`TVJN`TVJN`TVJN`TVJN`TVJN`TVJN`TVJN`TVJN`TVJN`TVJN`TV zJN`TVJN{e#TmD=ATmD=ATmD=ATmD=ATmD=ATmD=ATmD=ATmD=ATmD=ATmD=ATmD=A zTmD=ATmD=ATmD=ATmD=ATmD=ATmD=ATmD=ATmD=ATmD=ATmD=ATmD=ATmD=ATmD=A zTmD=ATmD=ATmD=ATmD=ATmD=ATmD=ATmD=ATmBpV8~z*q8~z*q8~z*q8~z*q8~z*q z8~z*q8~z*q8~z*q8~z*q8~z*q8~z*q8~z*q8~z*q8~z*q8~z*q8~z*q8~z*q8~z*q z8~z*q8~z*q8~z*q8~z*q8~z*q8~z*q8~z*q8~z*q8~z*q8~z*q8~z*q8~z*q8~z*q z8~$tlYyNBgYyNBgYyNBgYyNBgYyNBgYyNBgYyNBgYyNBgYyNBgYyNBgYyNBgYyNBg zYyNBgYyNBgYyNBgYyNBgYyNBgYyNBgYyNBgYyNBgYyNBgYyNBgYyNBgYyNBgYyNBg zYyNBgYyNBgYyNBgYyNBgYyNBgYyNBgYyK<#EB-6~EB-6~EB-6~EB-6~EB-6~EB-6~ zEB-6~EB-6~EB-6~EB-6~EB-6~EB-6~EB-6~EB-6~EB-6~EB-6~EB-6~EB-6~EB-6~ zEB-6~EB-6~EB-6~EB-6~EB-6~EB-6~EB-6~EB-6~EB-6~EB-6~EB-6~EB-6~EB;IV zOa4p#Oa4p#Oa4p#Oa4p#Oa4p#Oa4p#Oa4p#Oa4p#Oa4p#Oa4p#Oa4p#Oa4p#Oa4p# zOa4p#Oa4p#Oa4p#Oa4p#Oa4p#Oa4p#Oa4p#Oa4p#Oa4p#Oa4p#Oa4p#Oa4p#Oa4p# zOa4p#Oa4p#Oa4p#Oa4p#Oa4p#Oa2S~3;qlK3;qlK3;qlK3;qlK3;qlK3;qlK3;qlK z3;qlK3;qlK3;qlK3;qlK3;qlK3;qlK3;qlK3;qlK3;qlK3;qlK3;qlK3;qlK3;qlK z3;qlK3;qlK3;qlK3;qlK3;qlK3;qlK3;qlK3;qlK3;qlK3;qlK3;qlK3;uKdbN+Mw zbN+MwbN+MwbN+MwbN+MwbN+MwbN+MwbN+MwbN+MwbN+MwbN+MwbN+MwbN+MwbN+Mw zbN+MwbN+MwbN+MwbN+MwbN+MwbN+MwbN+MwbN+MwbN+MwbN+MwbN+MwbN+MwbN+Mw zbN+MwbN+MwbN+MwbN+MwbN(~_GyXIFGyXIFGyXIFGyXIFGyXIFGyXIFGyXIFGyXIF zGyXIFGyXIFGyXIFGyXIFGyXGv2l1Ju_{_(6=5d^z@t^UZ@t^UZ@t^UZ@t^UZ@t^UZ z@t^UZ@t^UZ@t^UZ@t^UZ@t^UZ@t^UZ@t^UZ@t^UZ@t^UZ@t^UZ@t^UZ@t^Xa@}Kgb z@}Kgb@}Kgb@}Kgb@}Kgb@}Kgb@}Kgb@}Kgb@}Kgb@}Kgb@}Kgb@}Kgb@}Kgb@}Kgb z@}Kgb@}Kgb@}Kgb@}Kgb@}Kgb@}Kgb@}Kgb@}Kgb@}Kgb@}Kgb@}Kgb@}Kgb@}Kgb z@}Kgb@}Kgb@}Kgb@}Kgb@}KaZ@SpIX@SpIX@SpIX@SpIX@SpIX@SpIX@SpIX@SpIX z@SpIX@SpIX@SpIX@SpIX@SpIX@SpIX@SpIX@SpIX@SpIX@SpIX@SpIX@SpIX@SpIX z@SpIX@SpIX@SpIX@SpIX@SpIX@SpIX@SpIX@SpIX@SpIX@SpIX@E`LZ^B?mc^B?mc z^B?mc^B?mc^B?mc^B?mc^B?mc^B?mc^B?mc^B?mc^B?mc^B?mc^B?mc^B?mc^B?mc z^B?mc^B?mc^B?mc^B?mc^B?mc^B?mc^B?mc^B?mc^B?mc^B?mc^B?mc^B?mc^B?mc z^B?mc^B?mc^B?mc^B?ga@gMOY@gMOY@gMOY@gMOY@gMOY@gMOY@gMOY@gMOY@gMOY z@gMOY@gMOY@gMOY@gMOY@gMOY@gMOY@gMOY@gMOY@gMOY@gMOY@gMOY@gMOY@gMOY z@gMOY@gMOY@gMOY@gMOY@gMOY@gMOY@gMOY@gMOY@gMOY@gMRZ@*naa@*naa@*naa z@*naa@*naa@*naa@*naa@*naa@*naa@*naa@*naa@*naa@*naa@*naa@*naa@*naa z@*naa@*naa@*naa@*naa@*naa@*naa@*naa@*naa@*naa@*naa@*naa@*naa@*naa z@*naa@*naa@*nUY@E`CW@E`CW@E`CW@E`CW@E`CW@E`CW@E`CW@E`CW@E`CW@E`CW z@E`CW@E`CW@E`CW@E`CW@E`CW@E`CW@E`CW@E`CW@E`CW@E`CW@E`CW@E`CW@E`CW z@E`CW@E`CW@E`CW@E`CW@E`CW@E`CW@E`CW@E`CW@bB~Q^Y8QT^Y8QT^Y8QT^Y8QT z^Y8QT^Y8QT^Y8QT^Y8QT^Y8QT^Y8QT^Y8QT^Y8QT^Y8QT^Y8QT^Y8QT^Y8QT^Y8QT z^Y8QT^Y8QT^Y8QT^Y8QT^Y8QT^Y8QT^Y8QT^Y8QT^Y8QT^Y8QT^Y8QT^Y8QT^Y8QT z^Y8QT^Y8KR@$d2P@$d2P@$d2P@$d2P@$d2P@$d2P@$d2P@$d2P@$d2P@$d2P@$d2P z@$d2P@$d2P@$d2P@$d2P@$d2P@$d2P@$d2P@$d2P@$d2P@$d2P@$d2P@$d2P@$d2P z@$d2P@$d2P@$d2P@$d2P@$d2P@$d2P@$d2P@$d5Q^6&ER^6&ER^6&ER^6&ER^6&ER z^6&ER^6&ER^6&ER^6&ER^6&ER^6&ER^6&ER^6&ER^6&ER^6&ER^6&ER^6&ER^6&ER z^6&ER^6&ER^6&ER^6&ER^6&ER^6&ER^6&ER^6&ER^6&ER^6&ER^6&ER^6&ER^6&ER z^6&8P@bB>N@bB>N@bB>N@bB>N@bB>N@bB>N@bB>N@bB>N@bB>N@bB>N@bB>N@bB>N z@bB>N@bB>N@bB>N@bB>N@bB>N@bB>N@bB>N@bB>N@bB>N@bB>N@bB>N@bB>N@bB>N z@bB>N@bB>N@bB>N@bB>N@bB>N@bB>N@Ne^P^KbKS^KbKS^KbKS^KbKS^KbKS^KbKS z^KbKS^KbKS^KbKS^KbKS^KbKS^KbKS^KbKS^KbKS^KbKS^KbKS^KbKS^KbKS^KbKS z^KbKS^KbKS^KbKS^KbKS^KbKS^KbKS^KbKS^KbKS^KbKS^KbKS^KbKS^KbKS^KbEQ z@o({O@o({O@o({O@o({O@o({O@o({O@o({O@o({O@o({O@o({O@o({O@o({O@o({O z@o({O@o({O@o({O@o({O@o({O@o({O@o({O@o({O@o({O@o({O@o({O@o({O@o({O z@o({O@o({O@o({O@o({O@o({O@o(~P@^A8Q@^A8Q@^A8Q@^A8Q@^A8Q@^A8Q@^A8Q z@^A8Q@^A8Q@^A8Q@^A8Q@^A8Q@^A8Q@^A8Q@^A8Q@^A8Q@^A8Q@^A8Q@^A8Q@^A8Q z@^A8Q@^A8Q@^A8Q@^A8Q@^A8Q@^A8Q@^A8Q@^A8Q@^A8Q@^A8Q@^A8Q@^A2O@Ne*M z@Ne*M@Ne*M@Ne*M@Ne*M@Ne*M@Ne*M@Ne*M@Ne*M@Ne*M@Ne*M@Ne*M@Ne*M@Ne*M z@Ne*M@Ne*M@Ne*M@Ne*M@Ne*M@Ne*M@Ne*M@Ne*M@Ne*M@Ne*M@Ne*M@Ne*M@Ne*M z@Ne*M@Ne*M@Ne*M@Ne*M@UQc)^RM%-^RM%-^RM%-^RM%-^RM%-^RM%-^RM%-^RM%- z^RM%-^RM%-^RM%-^RM%-^RM%-^RM%-^RM%-^RM%-^RM%-^RM%-^RM%-^RM%-^RM%- z^RM%-^RM%-^RM%-^RM%-^RM%-^RM%-^RM%-^RM%-^RM%-^RM%-^RMx*@vrf(@vrf( z@vrf(@vrf(@vrf(@vrf(@vrf(@vrf(@vrf(@vrf(@vrf(@vrf(@vrf(@vrf(@vrf( z@vrf(@vrf(@vrf(@vrf(@vrf(@vrf(@vrf(@vrf(@vrf(@vrf(@vrf(@vrf(@vrf( z@vrf(@vrf(@vrf(@vri)@~`r*@~`r*@~`r*@~`r*@~`r*@~`r*@~`r*@~`r*@~`r* z@~`r*@~`r*@~`r*@~`r*@~`r*@~`r*@~`r*@~`r*@~`r*@~`r*@~`r*@~`r*@~`r* z@~`r*@~`r*@~`r*@~`r*@~`r*@~`r*@~`r*@~`r*@~`r*@~`l(@UQT%@UQT%@UQT% z@UQT%@UQT%@UQT%@UQT%@UQT%@UQT%@UQT%@UQT%@UQT%@UQT%@UQT%@UQT%@UQT% z@UQT%@UQT%@UQT%@UQT%@UQT%@UQT%@UQT%@UQT%@UQT%@UQT%@UQT%@UQT%@UQT% z@UQT%@UQT%@GtW(^Dpx+^Dpx+^Dpx+^Dpx+^Dpx+^Dpx+^Dpx+^Dpx+^Dpx+^Dpx+ z^Dpx+^Dpx+^Dpx+^Dpx+^Dpx+^Dpx+^Dpx+^Dpx+^Dpx+^Dpx+^Dpx+^Dpx+^Dpx+ z^Dpx+^Dpx+^Dpx+^Dpx+^Dpx+^Dpx+^Dpx+^Dpx+^Dpr)@h|Z&@h|Z&@h|Z&@h|Z& z@h|Z&@h|Z&@h|Z&@h|Z&@h|Z&@h|Z&@h|Z&@h|Z&@h|Z&@h|Z&@h|Z&@h|Z&@h|Z& z@h|Z&@h|Z&@h|Z&@h|Z&@h|Z&@h|Z&@h|Z&@h|Z&@h|Z&@h|Z&@h|Z&@h|Z&@h|Z& z@h|Z&@h|c(@-Ol)@-Ol)@-Ol)@-Ol)@-Ol)@-Ol)@-Ol)@-Ol)@-Ol)@-Ol)@-Ol) z@-Ol)@-Ol)@-Ol)@-Ol)@-Ol)@-Ol)@-Ol)@-Ol)@-Ol)@-Ol)@-Ol)@-Ol)@-Ol) z@-Ol)@-Ol)@-Ol)@-Ol)@-Ol)@-Ol)@-Ol)@-Of&@GtN$@GtN$@GtN$@GtN$@GtN$ z@GtN$@GtN$@GtN$@GtN$@GtN$@GtN$@GtN$@GtN$@GtN$@GtN$@GtN$@GtN$@GtN$ z@GtN$@GtN$@GtN$@GtN$@GtN$@GtN$@GtN$@GtN$@GtN$@GtN$@GtN$@GtN$@GtN$ z@Xzzl^Uw3o^Uw3o^Uw3o^Uw3o^Uw3o^Uw3o^Uw3o^Uw3o^Uw3o^Uw3o^Uw3o^Uw3o z^Uw3o^Uw3o^Uw3o^Uw3o^Uw3o^Uw3o^Uw3o^Uw3o^Uw3o^Uw3o^Uw3o^Uw3o^Uw3o z^Uw3o^Uw3o^Uw3o^Uw3o^Uw3o^Uw3o^Uv|m@z3$k@z3$k@z3$k@z3$k@z3$k@z3$k z@z3$k@z3$k@z3$k@z3$k@z3$k@z3$k@z3$k@z3$k@z3$k@z3$k@z3$k@z3$k@z3$k z@z3$k@z3$k@z3$k@z3$k@z3$k@z3$k@z3$k@z3$k@z3$k@z3$k@z3$k@z3$k@z3(l z^3U?m^3U?m^3U?m^3U?m^3U?m^3U?m^3U?m^3U?m^3U?m^3U?m^3U?m^3U?m^3U?m z^3U?m^3U?m^3U?m^3U?m^3U?m^3U?m^3U?m^3U?m^3U?m^3U?m^3U?m^3U?m^3U?m z^3U?m^3U?m^3U?m^3U?m^3U?m^3U+k@Xzqi@Xzqi@Xzqi@Xzqi@Xzqi@Xzqi@Xzqi z@Xzqi@Xzqi@Xzqi@Xzqi@Xzqi@Xzqi@Xzqi@Xzqi@Xzqi@Xzqi@Xzqi@Xzqi@Xzqi z@Xzqi@Xzqi@Xzqi@Xzqi@Xzqi@Xzqi@Xzqi@Xzqi@Xzqi@Xzqi@Xzqi@K5tk^H1|n z^H1|n^H1|n^H1|n^H1|n^H1|n^H1|n^H1|n^H1|n^H1|n^H1|n^H1|n^H1|n^H1|n z^H1|n^H1|n^H1|n^H1|n^H1|n^H1|n^H1|n^H1|n^H1|n^H1|n^H1|n^H1|n^H1|n z^H1|n^H1|n^H1|n^H1|n^H1?l@lWwj@lWwj@lWwj@lWwj@lWwj@lWwj@lWwj@lWwj z@lWwj@lWwj@lWwj@lWwj@lWwj@lWwj@lWwj@lWwj@lWwj@lWwj@lWwj@lWwj@lWwj z@lWwj@lWwj@lWwj@lWwj@lWwj@lWwj@lWwj@lWwj@lWwj@lWwj@lWzk@=x+l@=x+l z@=x+l@=x+l@=x+l@=x+l@=x+l@=x+l@=x+l@=x+l@=x+l@=x+l@=x+l@=x+l@=x+l z@=x+l@=x+l@=x+l@=x+l@=x+l@=x+l@=x+l@=x+l@=x+l@=x+l@=x+l@=x+l@=x+l z@=x+l@=x+l@=x+l@=x$j@K5kh@K5kh@K5kh@K5kh@K5kh@K5kh@K5kh@K5kh@K5kh z@K5kh@K5kh@K5kh@K5kh@K5kh@K5kh@K5kh@K5kh@K5kh@K5kh@K5kh@K5kh@K5kh z@K5kh@K5kh@K5kh@K5kh@K5kh@K5kh@K5kh@K5kh@K5kh@Q?G4^N;h7^N;h7^N;h7 z^N;h7^N;h7^N;h7^N;h7^N;h7^N;h7^N;h7^N;h7^N;h7^N;h7^N;h7^N;h7^N;h7 z^N;h7^N;h7^N;h7^N;h7^N;h7^N;h7^N;h7^N;h7^N;h7^N;h7^N;h7^N;h7^N;h7 z^N;h7^N;h7^N;b5@sIJ3@sIJ3@sIJ3@sIJ3@sIJ3@sIJ3@sIJ3@sIJ3@sIJ3@sIJ3 z@sIJ3@sIJ3@sIJ3@sIJ3@sIJ3@sIJ3@sIJ3@sIJ3@sIJ3@sIJ3@sIJ3@sIJ3@sIJ3 z@sIJ3@sIJ3@sIJ3@sIJ3@sIJ3@sIJ3@sIJ3@sIJ3@sIM4@{jV5@{jV5@{jV5@{jV5 z@{jV5@{jV5@{jV5@{jV5@{jV5@{jV5@{jV5@{jV5@{jV5@{jV5@{jV5@{jV5@{jV5 z@{jV5@{jV5@{jV5@{jV5@{jV5@{jV5@{jV5@{jV5@{jV5@{jV5@{jV5@{jV5@{jV5 z@{jV5@{jP3@Q?71@Q?71@Q?71@Q?71@Q?71@Q?71@Q?71@Q?71@Q?71@Q?71@Q?71 z@Q?71@Q?71@Q?71@Q?71@Q?71@Q?71@Q?71@Q?71@Q?71@Q?71@Q?71@Q?71@Q?71 z@Q?71@Q?71@Q?71@Q?71@Q?71@Q?71@Q?71@DKA3^AGb6^AGb6^AGb6^AGb6^AGb6 z^AGb6^AGb6^AGb6^AGb6^AGb6^AGb6^AGb6^AGb6^AGb6^AGb6^AGb6^AGb6^AGb6 z^AGb6^AGb6^AGb6^AGb6^AGb6^AGb6^AGb6^AGb6^AGb6^AGb6^AGb6^AGb6^AGb6 z^AGV4@elD2@elD2@elD2@elD2@elD2@elD2@elD2@elD2@elD2@elD2@elD2@elD2 z@elD2@elD2@elD2@elD2@elD2@elD2@elD2@elD2@elD2@elD2@elD2@elD2@elD2 z@elD2@elD2@elD2@elD2@elD2@elD2@elG3@(=P4@(=P4@(=P4@(=P4@(=P4@(=P4 z@(=P4@(=P4@(=P4@(=P4@(=P4@(=P4@(=P4@(=P4@(=P4@(=P4@(=P4@(=P4@(=P4 z@(=P4@(=P4@(=P4@(=P4@(=P4@(=P4@(=P4@(=P4@(=P4@(=P4@(=P4@(=P4@(=J2 z@DK10@DK10@DK10@DK10@DK10@DK10@DK10@DK10@DK10@DK10@DK10@DK10@DK10 z@DK10@DK10@DK10@DK10@DK10@DK10@DK10@DK10@DK10@DK10@DK10@DK10@DK10 z@DK10@DK10@DK10@DK10@DK10@b~lg^Y`=j^Y`=j^Y`=j^Y`=j^Y`=j^Y`=j^Y`=j z^Y`=j^Y`=j^Y`=j^Y`=j^Y`=j^Y`=j^Y`=j^Y`=j^Y`=j^Y`=j^Y`=j^Y`=j^Y`=j z^Y`=j^Y`=j^Y`=j^Y`=j^Y`=j^Y`=j^Y`=j^Y`=j^Y`=j^Y`=j^Y`=j^Y`)h@%Qof z@%Qof@%Qof@%Qof@%Qof@%Qof@%Qof@%Qof@%Qof@%Qof@%Qof@%Qof@%Qof@%Qof z@%Qof@%Qof@%Qof@%Qof@%Qof@%Qof@%Qof@%Qof@%Qof@%Qof@%Qof@%Qof@%Qof z@%Qof@%Qof@%Qof@%Qof@%Qrg^7r!h^7r!h^7r!h^7r!h^7r!h^7r!h^7r!h^7r!h z^7r!h^7r!h^7r!h^7r!h^7r!h^7r!h^7r!h^7r!h^7r!h^7r!h^7r!h^7r!h^7r!h z^7r!h^7r!h^7r!h^7r!h^7r!h^7r!h^7r!h^7r!h^7r!h^7r!h^7ruf@b~cd@b~cd z@b~cd@b~cd@b~cd@b~cd@b~cd@b~cd@b~cd@b~cd@b~cd@b~cd@b~cd@b~cd@b~cd z@b~cd@b~cd@b~cd@b~cd@b~cd@b~cd@b~cd@b~cd@b~cd@b~cd@b~cd@b~cd@b~cd z@b~cd@b~cd@b~cd@OSff^LO)i^LO)i^LO)i^LO)i^LO)i^LO)i^LO)i^LO)i^LO)i z^LO)i^LO)i^LO)i^LO)i^LO)i^LO)i^LO)i^LO)i^LO)i^LO)i^LO)i^LO)i^LO)i z^LO)i^LO)i^LO)i^LO)i^LO)i^LO)i^LO)i^LO)i^LO)i^LO!g@ptie@ptie@ptie z@ptie@ptie@ptie@ptie@ptie@ptie@ptie@ptie@ptie@ptie@ptie@ptie@ptie z@ptie@ptie@ptie@ptie@ptie@ptie@ptie@ptie@ptie@ptie@ptie@ptie@ptie z@ptie@ptie@ptlf@^|ug@^|ug@^|ug@^|ug@^|ug@^|ug@^|ug@^|ug@^|ug@^|ug z@^|ug@^|ug@^|ug@^|ug@^|ug@^|ug@^|ug@^|ug@^|ug@^|ug@^|ug@^|ug@^|ug z@^|ug@^|ug@^|ug@^|ug@^|ug@^|ug@^|ug@^|ug@^|oe@OSWc@OSWc@OSWc@OSWc z@OSWc@OSWc@OSWc@OSWc@OSWc@OSWc@OSWc@OSWc@OSWc@OSWc@OSWc@OSWc@OSWc z@OSWc@OSWc@OSWc@OSWc@OSWc@OSWc@OSWc@OSWc@OSWc@OSWc@OSWc@OSWc@OSWc z@OSWc@VE1~^SAT2^SAT2^SAT2^SAT2^SAT2^SAT2^SAT2^SAT2^SAT2^SAT2^SAT2 z^SAT2^SAT2^SAT2^SAT2^SAT2^SAT2^SAT2^SAT2^SAT2^SAT2^SAT2^SAT2^SAT2 z^SAT2^SAT2^SAT2^SAT2^SAT2^SAT2^SAT2^SAN0@wf4}@wf4}@wf4}@wf4}@wf4} z@wf4}@wf4}@wf4}@wf4}@wf4}@wf4}@wf4}@wf4}@wf4}@wf4}@wf4}@wf4}@wf4} z@wf4}@wf4}@wf4}@wf4}@wf4}@wf4}@wf4}@wf4}@wf4}@wf4}@wf4}@wf4}@wf4} z@wf7~^0)H0^0)H0^0)H0^0)H0^0)H0^0)H0^0)H0^0)H0^0)H0^0)H0^0)H0^0)H0 z^0)H0^0)H0^0)H0^0)H0^0)H0^0)H0^0)H0^0)H0^0)H0^0)H0^0)H0^0)H0^0)H0 z^0)H0^0)H0^0)H0^0)H0^0)H0^0)H0^0)A}@VD@{@VD@{@VD@{@VD@{@VD@{@VD@{ z@VD@{@VD@{@VD@{@VD@{@VD@{@VD@{@VD@{@VD@{@VD@{@VD@{@VD@{@VD@{@VD@{ z@VD@{@VD@{@VD@{@VD@{@VD@{@VD@{@VD@{@VD@{@VD@{@VD@{@VD@{@VD@{@Hg`} z^EdN1^EdN1^EdN1^EdN1^EdN1^EdN1^EdN1^EdN1^EdN1^EdN1^EdN1^EdN1^EdN1 z^EdN1^EdN1^EdN1^EdN1^EdN1^EdN1^EdN1^EdN1^EdN1^EdN1^EdN1^EdN1^EdN1 z^EdN1^EdN1^EdN1^EdN1^EdN1^EdG~@i*}|@i*}|@i*}|@i*}|@i*}|@i*}|@i*}| z@i*}|@i*}|@i*}|@i*}|@i*}|@i*}|@i*}|@i*}|@i*}|@i*}|@i*}|@i*}|@i*}| z@i*}|@i*}|@i*}|@i*}|@i*}|@i*}|@i*}|@i*}|@i*}|@i*}|@i*}|@i+1}@;CA~ z@;CA~@;CA~@;CA~@;CA~@;CA~@;CA~@;CA~@;CA~@;CA~@;CA~@;CA~@;CA~@;CA~ z@;CA~@;CA~@;CA~@;CA~@;CA~@;CA~@;CA~@;CA~@;CA~@;CA~@;CA~@;CA~@;CA~ z@;CA~@;CA~@;CA~@;CA~@;C4|@Hg-`@Hg-`@Hg-`@Hg-`@Hg-`@Hg-`@Hg-`@Hg-` z@Hg-`@Hg-`@Hg-`@Hg-`@Hg-`@Hg-`@Hg-`@Hg-`@Hg-`@Hg-`@Hg-`@Hg-`@Hg-` z@Hg-`@Hg-`@Hg-`@Hg-`@Hg-`@Hg-`@Hg-`@Hg-`@Hg-`@Hg-`@YnO#^Vjp&^Vjp& z^Vjp&^Vjp&^Vjp&^Vjp&^Vjp&^Vjp&^Vjp&^Vjp&^Vjp&^Vjp&^Vjp&^Vjp&^Vjp& z^Vjp&^Vjp&^Vjp&^Vjp&^Vjp&^Vjp&^Vjp&^Vjp&^Vjp&^Vjp&^Vjp&^Vjp&^Vjp& z^Vjp&^Vjp&^Vjp&^Vjj$@z?R!@z?R!@z?R!@z?R!@z?R!@z?R!@z?R!@z?R!@z?R! z@z?R!@z?R!@z?R!@z?R!@z?R!@z?R!@z?R!@z?R!@z?R!@z?R!@z?R!@z?R!@z?R! z@z?R!@z?R!@z?R!@z?R!@z?R!@z?R!@z?R!@z?R!@z?R!@z?U#^4Id$^4Id$^4Id$ z^4Id$^4Id$^4Id$^4Id$^4Id$^4Id$^4Id$^4Id$^4Id$^4Id$^4Id$^4Id$^4Id$ z^4Id$^4Id$^4Id$^4Id$^4Id$^4Id$^4Id$^4Id$^4Id$^4Id$^4Id$^4Id$^4Id$ z^4Id$^4Id$^4IX!@YnFy@YnFy@YnFy@YnFy@YnFy@YnFy@YnFy@YnFy@YnFy@YnFy z@YnFy@YnFy@YnFy@YnFy@YnFy@YnFy@YnFy@YnFy@YnFy@YnFy@YnFy@YnFy@YnFy z@YnFy@YnFy@YnFy@YnFy@YnFy@YnFy@YnFy@YnFy@K^I!^H=j%^H=j%^H=j%^H=j% z^H=j%^H=j%^H=j%^H=j%^H=j%^H=j%^H=j%^H=j%^H=j%^H=j%^H=j%^H=j%^H=j% z^H=j%^H=j%^H=j%^H=j%^H=j%^H=j%^H=j%^H=j%^H=j%^H=j%^H=j%^H=j%^H=j% z^H=j%^H=d#@mKLz@mKLz@mKLz@mKLz@mKLz@mKLz@mKLz@mKLz@mKLz@mKLz@mKLz z@mKLz@mKLz@mKLz@mKLz@mKLz@mKLz@mKLz@mKLz@mKLz@mKLz@mKLz@mKLz@mKLz z@mKLz@mKLz@mKLz@mKLz@mKLz@mKLz@mKLz@mKO!@>lX#@>lX#@>lX#@>lX#@>lX# z@>lX#@>lX#@>lX#@>lX#@>lX#@>lX#@>lX#@>lX#@>lX#@>lX#@>lX#@>lX#@>lX# z@>lX#@>lX#@>lX#@>lX#@>lX#@>lX#@>lX#@>lX#@>lX#@>lX#@>lX#@>lX#@>lX# z@>lRz@K^9x@K^9x@K^9x@K^9x@K^9x@K^9x@K^9x@K^9x@K^9x@K^9x@K^9x@K^9x z@K^9x@K^9x@K^9x@K^9x@K^9x@K^9x@K^9x@K^9x@K^9x@K^9x@K^9x@K^9x@K^9x z@K^9x@K^9x@K^9x@K^9x@K^9x@K^9x@R#$K^Oy6N^Oy6N^Oy6N^Oy6N^Oy6N^Oy6N z^Oy6N^Oy6N^Oy6N^Oy6N^Oy6N^Oy6N^Oy6N^Oy6N^Oy6N^Oy6N^Oy6N^Oy6N^Oy6N z^Oy6N^Oy6N^Oy6N^Oy6N^Oy6N^Oy6N^Oy6N^Oy6N^Oy6N^Oy6N^Oy6N^Oy6N^Oy0L z@t5(J@t5(J@t5(J@t5(J@t5(J@t5(J@t5(J@t5(J@t5(J@t5(J@t5(J@t5(J@t5(J z@t5(J@t5(J@t5(J@t5(J@t5(J@t5(J@t5(J@t5(J@t5(J@t5(J@t5(J@t5(J@t5(J z@t5(J@t5(J@t5(J@t5(J@t5(J@t5+K@|W_L@|W_L@|W_L@|W_L@|W_L@|W_L@|W_L z@|W_L@|W_L@|W_L@|W_L@|W_L@|W_L@|W_L@|W_L@|W_L@|W_L@|W_L@|W_L@|W_L z@|W_L@|W_L@|W_L@|W_L@|W_L@|W_L@|W_L@|W_L@|W_L@|W_L@|W_L@|Wq^QZHt z^QZHt^QZHt^QZHt^QZHt^QZHt^QZHt^QZHt^QZHt^QZHt^QZHt^QZHt^QZHt^QZHt z^QZHt^QZHt^QZHt^QZHt^QZHt^QZHt^QZHt^QZHt^QZHt^QZHt^QZHt^QZHt^QZHt z^QZHt^QZHt^QZHt^QZHt^QZBr@u%^p@u%^p@u%^p@u%^p@u%^p@u%^p@u%^p@u%^p z@u%^p@u%^p@u%^p@u%^p@u%^p@u%^p@u%^p@u%^p@u%^p@u%^p@u%^p@u%^p@u%^p z@u%^p@u%^p@u%^p@u%^p@u%^p@u%^p@u%^p@u%^p@u%^p@u%^p@u%{q@~85r@~85r z@~85r@~85r@~85r@~85r@~85r@~85r@~85r@~85r@~85r@~85r@~85r@~85r@~85r z@~85r@~85r@~85r@~85r@~85r@~85r@~85r@~85r@~85r@~85r@~85r@~85r@~85r z@~85r@~85r@~85r@~7~p@Tc&n@Tc&n@Tc&n@Tc&n@Tc&n@Tc&n@Tc&n@Tc&n@Tc&n z@Tc&n@Tc&n@Tc&n@Tc&n@Tc&n@Tc&n@Tc&n@Tc&n@Tc&n@Tc&n@Tc&n@Tc&n@Tc&n z@Tc&n@Tc&n@Tc&n@Tc&n@Tc&n@Tc&n@Tc&n@Tc&n@Tc&n@F(*p^C$Bs^C$Bs^C$Bs z^C$Bs^C$Bs^C$Bs^C$Bs^C$Bs^C$Bs^C$Bs^C$Bs^C$Bs^C$Bs^C$Bs^C$Bs^C$Bs z^C$Bs^C$Bs^C$Bs^C$Bs^C$Bs^C$Bs^C$Bs^C$Bs^C$Bs^C$Bs^C$Bs^C$Bs^C$Bs z^C$Bs^C$Bs^C$5q@h9;o@h9;o@h9;o@h9;o@h9;o@h9;o@h9;o@h9;o@h9;o@h9;o z@h9;o@h9;o@h9;o@h9;o@h9;o@h9;o@h9;o@h9;o@h9;o@h9;o@h9;o@h9;o@h9;o z@h9;o@h9;o@h9;o@h9;o@h9;o@h9;o@h9;o@h9;o@h9>p@+a~q@+a~q@+a~q@+a~q z@+a~q@+a~q@+a~q@+a~q@+a~q@+a~q@+a~q@+a~q@+a~q@+a~q@+a~q@+a~q@+a~q z@+a~q@+a~q@+a~q@+a~q@+a~q@+a~q@+a~q@+a~q@+a~q@+a~q@+a~q@+a~q@+a~q z@+a~q@+a^o@F(ym@F(ym@F(ym@F(ym@F(ym@F(ym@F(ym@F(ym@F(ym@F(ym@F(ym z@F(ym@F(ym@F(ym@F(ym@F(ym@F(ym@F(ym@F(ym@F(ym@F(ym@F(ym@F(ym@F(ym z@F(ym@F(ym@F(ym@F(ym@F(ym@F(ym@F(ym@W=DV^T+eY^T+eY^T+eY^T+eY^T+eY z^T+eY^T+eY^T+eY^T+eY^T+eY^T+eY^T+eY^T+eY^T+eY^T+eY^T+eY^T+eY^T+eY z^T+eY^T+eY^T+eY^T+eY^T+eY^T+eY^T+eY^T+eY^T+eY^T+eY^T+eY^T+eY^T+eY z^T+YW@yGGU@yGGU@yGGU@yGGU@yGGU@yGGU@yGGU@yGGU@yGGU@yGGU@yGGU@yGGU z@yGGU@yGGU@yGGU@yGGU@yGGU@yGGU@yGGU@yGGU@yGGU@yGGU@yGGU@yGGU@yGGU z@yGGU@yGGU@yGGU@yGGU@yGGU@yGGU@yGJV^2hSW^2hSW^2hSW^2hSW^2hSW^2hSW z^2hSW^2hSW^2hSW^2hSW^2hSW^2hSW^2hSW^2hSW^2hSW^2hSW^2hSW^2hSW^2hSW z^2hSW^2hSW^2hSW^2hSW^2hSW^2hSW^2hSW^2hSW^2hSW^2hSW^2hSW^2hSW^2hMU z@W=4S@W=4S@W=4S@W=4S@W=4S@W=4S@W=4S@W=4S@W=4S@W=4S@W=4S@W=4S@W=4S z@W=4S@W=4S@W=4S@W=4S@W=4S@W=4S@W=4S@W=4S@W=4S@W=4S@W=4S@W=4S@W=4S z@W=4S@W=4S@W=4S@W=4S@W=4S@JI7U^GEYX^GEYX^GEYX^GEYX^GEYX^GEYX^GEYX z^GEYX^GEYX^GEYX^GEYX^GEYX^GEYX^GEYX^GEYX^GEYX^GEYX^GEYX^GEYX^GEYX z^GEYX^GEYX^GEYX^GEYX^GEYX^GEYX^GEYX^GEYX^GEYX^GEYX^GEYX^GESV@kjAT z@kjAT@kjAT@kjAT@kjAT@kjAT@kjAT@kjAT@kjAT@kjAT@kjAT@kjAT@kjAT@kjAT z@kjAT@kjAT@kjAT@kjAT@kjAT@kjAT@kjAT@kjAT@kjAT@kjAT@kjAT@kjAT@kjAT z@kjAT@kjAT@kjAT@kjAT@kjDU@<;MV@<;MV@<;MV@<;MV@<;MV@<;MV@<;MV@<;MV z@<;MV@<;MV@<;MV@<;MV@<;MV@<;MV@<;MV@<;MV@<;MV@<;MV@<;MV@<;MV@<;MV z@<;MV@<;MV@<;MV@<;MV@<;MV@<;MV@<;MV@<;MV@<;MV@<;MV@<;GT@JH}R@JH}R z@JH}R@JH}R@JH}R@JH}R@JH}R@JH}R@JH}R@JH}R@JH}R@JH}R@JH}R@JH}R@JH}R z@JH}R@JH}R@JH}R@JH}R@JH}R@JH}R@JH}R@JH}R@JH}R@JH}R@JH}R@JH}R@JH}R z@JH}R@JH}R@JH}R@Q3q<^M~_?^M~_?^M~_?^M~_?^M~_?^M~_?^M~_?^M~_?^M~_? z^M~_?^M~_?^M~_?^M~_?^M~_?^M~_?^M~_?^M~_?^M~_?^M~_?^M~_?^M~_?^M~_? z^M~_?^M~_?^M~_?^M~_?^M~_?^M~_?^M~_?^M~_?^M~_?^M~<=@rUt;@rUt;@rUt; z@rUt;@rUt;@rUt;@rUt;@rUt;@rUt;@rUt;@rUt;@rUt;@rUt;@rUt;@rUt;@rUt; z@rUt;@rUt;@rUt;@rUt;@rUt;@rUt;@rUt;@rUt;@rUt;@rUt;@rUt;@rUt;@rUt; z@rUt;@rUt;@rUw<@`v(=@`v(=@`v(=@`v(=@`v(=@`v(=@`v(=@`v(=@`v(=@`v(= z@`v(=@`v(=@`v(=@`v(=@`v(=@`v(=@`v(=@`v(=@`v(=@`v(=@`v(=@`v(=@`v(= z@`v(=@`v(=@`v(=@`v(=@`v(=@`v(=@`v(=@`v(=@`vz;@Q3h+@Q3h+@Q3h+@Q3h+ z@Q3h+@Q3h+@Q3h+@Q3h+@Q3h+@Q3h+@Q3h+@Q3h+@Q3h+@Q3h+@Q3h+@Q3h+@Q3h+ z@Q3h+@Q3h+@Q3h+@Q3h+@Q3h+@Q3h+@Q3h+@Q3h+@Q3h+@Q3h+@Q3h+@Q3h+@Q3h+ z@Q3h+@CWk;^9S<>^9S<>^9S<>^9S<>^9S<>^9S<>^9S<>^9S<>^9S<>^9S<>^9S<> z^9S<>^9S<>^9S<>^9S<>^9S<>^9S<>^9S<>^9S<>^9S<>^9S<>^9S<>^9S<>^9S<> z^9S<>^9S<>^9S<>^9S<>^9S<>^9S<>^9S<>^9S(<@dxn-@dxn-@dxn-@dxn-@dxn- z@dxn-@dxn-@dxn-@dxn-@dxn-@dxn-@dxn-@dxn-@dxn-@dxn-@dxn-@dxn-@dxn- z@dxn-@dxn-@dxn-@dxn-@dxn-@dxn-@dxn-@dxn-@dxn-@dxn-@dxn-@dxn-@dxn- z@dxq;@(1z<@(1z<@(1z<@(1z<@(1z<@(1z<@(1z<@(1z<@(1z<@(1z<@(1z<@(1z< z@(1z<@(1z<@(1z<@(1z<@(1z<@(1z<@(1z<@(1z<@(1z<@(1z<@(1z<@(1z<@(1z< z@(1z<@(1z<@(1z<@(1z<@(1z<@(1z<@(1t-@CWb*@CWb*@CWb*@CWb*@CWb*@CWb* z@CWb*@CWb*@CWb*@CWb*@CWb*@CWb*@CWb*@CWb*@CWb*@CWb*@CWb*@CWb*@CWb* z@CWb*@CWb*@CWb*@CWb*@CWb*@CWb*@CWb*@CWb*@CWb*@CWb*@CWb*@CWb*@c)DV zAN>E|{|Em+`2WHG5B`7f|AYS@{Qqym^TGj8cn|=z`+mRQ_hyorNivyaGLt09B*|ow zBr`LUWF_euV?;!ZF+PnkMnptJM8p^)A|fJUL`0->{=s?tAOFYy@qhdu|HuFFfBYZ+ z$N%wv{2%|v|M7qPAOFYy@qhdu|HuFFfBYZ+$N%wv{2%|v|M7qPAOFYy@qhdu|HuFF zfBYZ+$N%wv{2%|v|M7qPAOFYy@qhdu|J(oezx{9j+yC~z{cr!<|MtKAZ~xo>_P_ma z|J(oezx{9j+yC~z{cr!<|MtKAZ~xo>_P_ma|J(oezx{9j+yC~z{cr!<|MtKAZ~xo> z_P_ma|J(oezx{9j+yC~z{cr!<|MtKAZ~xo>_P_ma|I7dKzx*%%%m4Df{4f8@|MI{5 zFaOK`^1u8q|I7dKzx*%%%m4Df{4f8@|MI{5FaOK`^1u8q|I7dKzx*%%%m4Df{4f8@ z|MI{5FaOK`^1u8q|I7dKzx*%%%m4Df{4f8@|MI{5FaOK`^1u8q|I`2UKmAYt)Bp58 z{ZIeX|MWloPyf^Z^gsPi|I`2UKmAYt)Bp58{ZIeX|MWloPyf^Z^gsPi|I`2UKmAYt z)Bp58{ZIeX|MWloPyf^Z^gsPi|I`2UKmAYt)Bp58{ZIeX|MWloPyf^Z^gsPi|HJ?A zKl~5>!~gI<{15-b|L{Nj5C6me@IU+y|HJ?AKl~5>!~gI<{15-b|L{Nj5C6me@IU+y z|HJ?AKl~5>!~gI<{15-b|L{Nj5C6me@IU+y|HJ?AKl~5>!~gI<{15-b|L{Nj5C6me z@IU+y|J{H0-~D(0-GBGr{dfP}fA`=0cmLgg_uu_@|J{H0-~D(0-GBGr{dfP}fA`=0 zcmLgg_uu_@|J{H0-~D(0-GBGr{dfP}fA`=0cmLgg_uu_@|J{H0-~D(0-GBGr{dfP} zfA`=0cmLgg_uu_@|IL5%-~2cK&42UX{5Sv2fAin`H~-Cl^WXe8|IL5%-~2cK&42UX z{5Sv2fAin`H~-Cl^WXe8|IL5%-~2cK&42UX{5Sv2fAin`H~-Cl^WXe8|IL5%-~2cK z&42UX{5Sv2fAin`H~-Cl^WXe8|J8r>U;S7A)qnM0{a63hfAwGeSO3+2^ zU;S7A)qnM0{a63hfAwGeSO3+2^U;S7A)qnM0{a63hfAwGeSO3+2^U;S7A)qnM0{a63hfAwGeSO3+2^n{3rj(fAXLFC;!QR@}K-C|H*&ypZq8P$$#>n{3rj(fAXLFC;!QR@}K-C|H*&y zpZq8P$$#>n{3rj(fAXLFC;!QR@}K-C|H*&ypZq8P$$#>n{3rj(fAXLFC;!QR@}K-C z|IvT+AN@!F(SP(G{YU@NfAk;yNB_}(^dJ34|IvT+AN@!F(SP(G{YU@NfAk;yNB_}( z^dJ34|IvT+AN@!F(SP(G{YU@NfAk;yNB_}(^dJ34|IvT+AN@!F(SP(G{YU@NfAk;y zNB_}(^dJ34|G|IoAN&XZ!GG`{{0INRfAAmt2mir;@E`mK|G|IoAN&XZ!GG`{{0INR zfAAmt2mir;@E`mK|G|IoAN&XZ!GG`{{0INRfAAmt2mir;@E`mK|G|IoAN&XZ!GG`{ z{0INRfAAmt2mir;@E`mK|K7j%@BMrK-oN+n{d@o3zxVI`d;i|Q_wW6C|K7j%@BMrK z-oN+n{d@o3zxVI`d;i|Q_wW6C|K7j%@BMrK-oN+n{d@o3zxVI`d;i|Q_wW6C|K7j% z@BMrK-oN+n{d@o3zxVI`d;i|Q_wW6C|IWYj@BBOe&cE~T{5${7zw__>JO9qV^Y8pS z|IWYj@BBOe&cE~T{5${7zw__>JO9qV^Y8pS|IWYj@BBOe&cE~T{5${7zw__>JO9qV z^Y8pS|IWYj@BBOe&cE~T{5${7zw__>JO9qV^Y8pS|JJ|tZ~a^U*1z>{{agRmzx8kZ zTmRO-^>6)K|JJ|tZ~a^U*1z>{{agRmzx8kZTmRO-^>6)K|JJ|tZ~a^U*1z>{{agRm zzx8kZTmRO-^>6)K|JJ|tZ~a^U*1z>{{agRmzx8kZTmRO-^>6)K|Hi-ZZ~Pno#=r4z z{2TwqzwvMU8~?_?@o)Sa|Hi-ZZ~Pno#=r4z{2TwqzwvMU8~?_?@o)Sa|Hi-ZZ~Pno z#=r4z{2TwqzwvMU8~?_?@o)Sa|Hi-ZZ~Pno#=r4z{2TwqzwvMU8~?_?@o)Sa|JuLy zul;NP+Q0U%{cHc)zxJ>FYyaB6_OJbG|JuLyul;NP+Q0U%{cHc)zxJ>FYyaB6_OJbG z|JuLyul;NP+Q0U%{cHc)zxJ>FYyaB6_OJbG|JuLyul;NP+Q0U%{cHc)zxJ>FYyaB6 z_OJbG|H{Aeuly_j%D?ij{44*;zw)pAEC0&B@~`|W|H{Aeuly_j%D?ij{44*;zw)pA zEC0&B@~`|W|H{Aeuly_j%D?ij{44*;zw)pAEC0&B@~`|W|H{Aeuly_j%D?ij{44*; zzw)pAEC0&B@~`|W|I)woFa1mZ(!caC{Y(GSzw|HtOaIcp^e_EO|I)woFa1mZ(!caC z{Y(GSzw|HtOaIcp^e_EO|I)woFa1mZ(!caC{Y(GSzw|HtOaIcp^e_EO|I)woFa1mZ z(!caC{Y(GSzw|HtOaIcp^e_EO|H8lUFZ>Jt!oTn@{0slWzwj^o3;)8u@Gtxe|H8lU zFZ>Jt!oTn@{0slWzwj^o3;)8u@Gtxe|H8lUFZ>Jt!oTn@{0slWzwj^o3;)8u@Gtxe z|H8lUFZ>Jt!oTn@{0slWzwj^o3;)8u@Gtxe|J* z)Iar4{Zs$cKlM-jQ~%UI^-uj%|I|PAPyJK>)Iar4{Zs$cKlM-jQ~%UI^-uj%|I|PA zPyJK>)Iar4{Zs$cKlM-jQ~%UI^-uj%|I|PAPyJK>)Iar4{Zs$cKlM-jQ~%UI^-uj% z|HMD>Py7@A#6R&*{1gAgKk-le6aU0N@lX5{|HMD>Py7@A#6R&*{1gAgKk-le6aU0N z@lX5{|HMD>Py7@A#6R&*{1gAgKk-le6aU0N@lX5{|HMD>Py7@A#6R&*{1gAgKk-le z6aU0N@lX5{|JXnFkNso+*gy7<{bT>wKlYFPWB=Gc_K*Ez|JXnFkNso+*gy7<{bT>w zKlYFPWB=Gc_K*Ez|JXnFkNso+*gy7<{bT>wKlYFPWB=Gc_K*Ez|JXnFkNso+*gy7< z{bT>wKlYFPWB=Gc_K*Ez|Hwb`kNhM5$UpLr{3HL!Kk|?KBmc-h@{jx@|Hwb`kNhM5 z$UpLr{3HL!Kk|?KBmc-h@{jx@|Hwb`kNhM5$UpLr{3HL!Kk|?KBmc-h@{jx@|Hwb` zkNhM5$UpLr{3HL!Kk|?KBmc-h@{jzZD7^m{-v15n|AhCy!~0+1{eF1=Gra#1-hU78 zzlHZ-!}~Ac{paxhQ+U4@-tUI@JK_Du@cu)1|318b7v8@O@85*?ufzLS;r+|-{zZ7d z9o}z+_nYDUMtDPbzaHMNh4-uB{j>1?X?Xu6ynh_tuY~u@;r&v0|0w)n_=E8K;fvvS z!|#OO4qphL51$L44W9|W6@D}PM)>vcYvI%3SHq{muY_L?zZ8Bkd@}q(`1$a2;b+6o zgr5$d2p>TN5hYV4~Gwh4~8EOKNLO?elXnO40~9^9LCUx THq@c~^E{2dm`1<%kstVfGkj_X literal 0 Hc-jL100001 diff --git a/libcli/ldap/tests/data/ldap-recursive.dat b/libcli/ldap/tests/data/ldap-recursive.dat new file mode 100644 index 0000000000000000000000000000000000000000..dd18d857660df41c30024264d4623680a9404613 GIT binary patch literal 970 zc-k{)Vm`*i$ei57yq|@EixGsFz=)BNVWC1hcYab. + * + */ + +/* + * from cmocka.c: + * These headers or their equivalents should be included prior to + * including + * this header file. + * + * #include + * #include + * #include + * + * This allows test applications to use custom definitions of C standard + * library functions and types. + * + */ +#include +#include +#include +#include + +#include "lib/util/attr.h" +#include "includes.h" +#include "lib/util/asn1.h" +#include "libcli/ldap/ldap_message.h" +#include "libcli/ldap/ldap_proto.h" + +/* + * declare the internal cmocka cm_print so we can output messages in + * sub unit format + */ +void cm_print_error(const char * const format, ...); +/* + * helper function and macro to compare an ldap error code constant with the + * coresponding nt_status code + */ +#define NT_STATUS_LDAP_V(code) (0xF2000000 | code) +static void _assert_ldap_status_equal( + int a, + NTSTATUS b, + const char * const file, + const int line) +{ + _assert_int_equal(NT_STATUS_LDAP_V(a), NT_STATUS_V(b), file, line); +} + +#define assert_ldap_status_equal(a, b) \ + _assert_ldap_status_equal((a), (b), __FILE__, __LINE__) + +/* + * helper function and macro to assert there were no errors in the last + * file operation + */ +static void _assert_not_ferror( + FILE *f, + const char * const file, + const int line) +{ + if (f == NULL || ferror(f)) { + cm_print_error("ferror (%d) %s\n", errno, strerror(errno)); + _fail(file, line); + } +} + +#define assert_not_ferror(f) \ + _assert_not_ferror((f), __FILE__, __LINE__) + +struct test_ctx { +}; + +static int setup(void **state) +{ + struct test_ctx *test_ctx; + + test_ctx = talloc_zero(NULL, struct test_ctx); + *state = test_ctx; + return 0; +} + +static int teardown(void **state) +{ + struct test_ctx *test_ctx = talloc_get_type_abort(*state, + struct test_ctx); + + TALLOC_FREE(test_ctx); + return 0; +} + +/* + * Test that an empty request is handled correctly + */ +static void test_empty_input(void **state) +{ + struct test_ctx *test_ctx = talloc_get_type_abort( + *state, + struct test_ctx); + struct asn1_data *asn1; + struct ldap_message *ldap_msg; + NTSTATUS status; + uint8_t buf[0]; + size_t len = 0; + + + asn1 = asn1_init(test_ctx, ASN1_MAX_TREE_DEPTH); + assert_non_null(asn1); + + asn1_load_nocopy(asn1, buf, len); + + ldap_msg = talloc(test_ctx, struct ldap_message); + assert_non_null(ldap_msg); + + status = ldap_decode(asn1, samba_ldap_control_handlers(), ldap_msg); + assert_ldap_status_equal(LDAP_PROTOCOL_ERROR, status); +} + +/* + * Check that a request is rejected it it's recursion depth exceeds + * the maximum value specified. This test uses a very deeply nested query, + * 10,000 or clauses. + * + */ +static void test_recursion_depth_large(void **state) +{ + struct test_ctx *test_ctx = talloc_get_type_abort( + *state, + struct test_ctx); + struct asn1_data *asn1; + struct ldap_message *ldap_msg; + NTSTATUS status; + FILE *f = NULL; + uint8_t *buffer = NULL; + const size_t BUFF_SIZE = 1048576; + size_t len; + + + /* + * Load a test data file containg 10,000 or clauses in encoded as + * an ASN.1 packet. + */ + buffer = talloc_zero_array(test_ctx, uint8_t, BUFF_SIZE); + f = fopen("./libcli/ldap/tests/data/10000-or.dat", "r"); + assert_not_ferror(f); + len = fread(buffer, sizeof(uint8_t), BUFF_SIZE, f); + assert_not_ferror(f); + assert_true(len > 0); + + asn1 = asn1_init(test_ctx, ASN1_MAX_TREE_DEPTH); + assert_non_null(asn1); + asn1_load_nocopy(asn1, buffer, len); + + ldap_msg = talloc(test_ctx, struct ldap_message); + assert_non_null(ldap_msg); + + status = ldap_decode(asn1, samba_ldap_control_handlers(), ldap_msg); + assert_ldap_status_equal(LDAP_PROTOCOL_ERROR, status); +} + +/* + * Check that a request is not rejected it it's recursion depth equals the + * maximum value + */ +static void test_recursion_depth_equals_max(void **state) +{ + struct test_ctx *test_ctx = talloc_get_type_abort( + *state, + struct test_ctx); + struct asn1_data *asn1; + struct ldap_message *ldap_msg; + NTSTATUS status; + FILE *f = NULL; + uint8_t *buffer = NULL; + const size_t BUFF_SIZE = 1048576; + size_t len; + + + buffer = talloc_zero_array(test_ctx, uint8_t, BUFF_SIZE); + f = fopen("./libcli/ldap/tests/data/ldap-recursive.dat", "r"); + assert_not_ferror(f); + len = fread(buffer, sizeof(uint8_t), BUFF_SIZE, f); + assert_not_ferror(f); + assert_true(len > 0); + + asn1 = asn1_init(test_ctx, 4); + assert_non_null(asn1); + asn1_load_nocopy(asn1, buffer, len); + + ldap_msg = talloc(test_ctx, struct ldap_message); + assert_non_null(ldap_msg); + + status = ldap_decode(asn1, samba_ldap_control_handlers(), ldap_msg); + assert_true(NT_STATUS_IS_OK(status)); +} + +/* + * Check that a request is rejected it it's recursion depth is greater than the + * maximum value + */ +static void test_recursion_depth_greater_than_max(void **state) +{ + struct test_ctx *test_ctx = talloc_get_type_abort( + *state, + struct test_ctx); + struct asn1_data *asn1; + struct ldap_message *ldap_msg; + NTSTATUS status; + FILE *f = NULL; + uint8_t *buffer = NULL; + const size_t BUFF_SIZE = 1048576; + size_t len; + + + buffer = talloc_zero_array(test_ctx, uint8_t, BUFF_SIZE); + f = fopen("./libcli/ldap/tests/data/ldap-recursive.dat", "r"); + assert_not_ferror(f); + len = fread(buffer, sizeof(uint8_t), BUFF_SIZE, f); + assert_not_ferror(f); + assert_true(len > 0); + + asn1 = asn1_init(test_ctx, 3); + assert_non_null(asn1); + asn1_load_nocopy(asn1, buffer, len); + + ldap_msg = talloc(test_ctx, struct ldap_message); + assert_non_null(ldap_msg); + + status = ldap_decode(asn1, samba_ldap_control_handlers(), ldap_msg); + assert_ldap_status_equal(LDAP_PROTOCOL_ERROR, status); +} + +int main(_UNUSED_ int argc, _UNUSED_ const char **argv) +{ + const struct CMUnitTest tests[] = { + cmocka_unit_test_setup_teardown( + test_empty_input, + setup, + teardown), + cmocka_unit_test_setup_teardown( + test_recursion_depth_large, + setup, + teardown), + cmocka_unit_test_setup_teardown( + test_recursion_depth_equals_max, + setup, + teardown), + cmocka_unit_test_setup_teardown( + test_recursion_depth_greater_than_max, + setup, + teardown), + }; + + cmocka_set_message_output(CM_OUTPUT_SUBUNIT); + return cmocka_run_group_tests(tests, NULL, NULL); +} diff --git a/libcli/ldap/wscript_build b/libcli/ldap/wscript_build index db5b1df497a..d0aa7c11ced 100644 --- a/libcli/ldap/wscript_build +++ b/libcli/ldap/wscript_build @@ -6,3 +6,18 @@ bld.SAMBA_LIBRARY('cli-ldap-common', private_headers='ldap_message.h ldap_errors.h ldap_ndr.h', deps='samba-util asn1util NDR_SECURITY tevent', private_library=True) + +bld.SAMBA_BINARY( + 'test_ldap_message', + source='tests/ldap_message_test.c', + deps=''' + cmocka + talloc + ldb + samba-util + asn1util + NDR_SECURITY + cli-ldap + ''', + install=False +) diff --git a/selftest/knownfail.d/ldap_message b/selftest/knownfail.d/ldap_message new file mode 100644 index 00000000000..242eff45e59 --- /dev/null +++ b/selftest/knownfail.d/ldap_message @@ -0,0 +1,2 @@ +^libcli.ldap.ldap_message.test_recursion_depth_greater_than_max\(none\) +^libcli.ldap.ldap_message.test_recursion_depth_large\(none\) diff --git a/source4/selftest/tests.py b/source4/selftest/tests.py index 52db18a872b..251ace24052 100755 --- a/source4/selftest/tests.py +++ b/source4/selftest/tests.py @@ -1322,6 +1322,8 @@ plantestsuite("samba4.dcerpc.dnsserver.dnsutils", "none", [os.path.join(bindir(), "test_rpc_dns_server_dnsutils")]) plantestsuite("libcli.drsuapi.repl_decrypt", "none", [os.path.join(bindir(), "test_repl_decrypt")]) +plantestsuite("libcli.ldap.ldap_message", "none", + [os.path.join(bindir(), "test_ldap_message")]) # process restart and limit tests, these break the environment so need to run # in their own specific environment -- 2.17.1 From ef35bb12674bf591385c4629c84b1a76f231755b Mon Sep 17 00:00:00 2001 From: Gary Lockyer Date: Wed, 8 Apr 2020 15:30:52 +1200 Subject: [PATCH 07/12] CVE-2020-10704: lib util asn1: Check parse tree depth Check the current depth of the parse tree and reject the input if the depth exceeds that passed to asn1_init Credit to OSS-Fuzz REF: https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=20454 BUG: https://bugzilla.samba.org/show_bug.cgi?id=14334 Signed-off-by: Gary Lockyer Reviewed-by: Andrew Bartlett --- lib/util/asn1.c | 13 +++++++++++++ selftest/knownfail.d/ldap_message | 2 -- 2 files changed, 13 insertions(+), 2 deletions(-) delete mode 100644 selftest/knownfail.d/ldap_message diff --git a/lib/util/asn1.c b/lib/util/asn1.c index ec6e674ce20..ee3cff9cb65 100644 --- a/lib/util/asn1.c +++ b/lib/util/asn1.c @@ -647,6 +647,16 @@ bool asn1_start_tag(struct asn1_data *data, uint8_t tag) uint8_t b; struct nesting *nesting; + /* + * Check the depth of the parse tree and prevent it from growing + * too large. + */ + data->depth++; + if (data->depth > data->max_depth) { + data->has_error = true; + return false; + } + if (!asn1_read_uint8(data, &b)) return false; @@ -703,6 +713,9 @@ bool asn1_end_tag(struct asn1_data *data) { struct nesting *nesting; + if (data->depth > 0) { + data->depth--; + } /* make sure we read it all */ if (asn1_tag_remaining(data) != 0) { data->has_error = true; diff --git a/selftest/knownfail.d/ldap_message b/selftest/knownfail.d/ldap_message deleted file mode 100644 index 242eff45e59..00000000000 --- a/selftest/knownfail.d/ldap_message +++ /dev/null @@ -1,2 +0,0 @@ -^libcli.ldap.ldap_message.test_recursion_depth_greater_than_max\(none\) -^libcli.ldap.ldap_message.test_recursion_depth_large\(none\) -- 2.17.1 From e5c4b58314e9e8dba63b3078061619ae8b5d4ad3 Mon Sep 17 00:00:00 2001 From: Gary Lockyer Date: Tue, 14 Apr 2020 13:32:32 +1200 Subject: [PATCH 08/12] CVE-2020-10704: ldapserver tests: Limit search request sizes Add tests to ensure that overly long (> 256000 bytes) LDAP search requests are rejected. Credit to OSS-Fuzz REF: https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=20454 BUG: https://bugzilla.samba.org/show_bug.cgi?id=14334 Signed-off-by: Gary Lockyer Reviewed-by: Andrew Bartlett --- python/samba/tests/ldap_raw.py | 234 +++++++++++++++++++++++++++++++++ selftest/knownfail.d/ldap_raw | 1 + source4/selftest/tests.py | 5 + 3 files changed, 240 insertions(+) create mode 100644 python/samba/tests/ldap_raw.py create mode 100644 selftest/knownfail.d/ldap_raw diff --git a/python/samba/tests/ldap_raw.py b/python/samba/tests/ldap_raw.py new file mode 100644 index 00000000000..334fabce230 --- /dev/null +++ b/python/samba/tests/ldap_raw.py @@ -0,0 +1,234 @@ +# Integration tests for the ldap server, using raw socket IO +# +# Tests for handling of malformed or large packets. +# +# Copyright (C) Catalyst.Net Ltd 2020 +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +import socket + +import samba.tests +from samba.tests import TestCase + + +# +# LDAP Operations +# +SEARCH = b'\x63' + +EQUALS = b'\xa3' + + +# +# ASN.1 Element types +# +BOOLEAN = b'\x01' +INTEGER = b'\x02' +OCTET_STRING = b'\x04' +NULL = b'\x05' +ENUMERATED = b'\x0a' +SEQUENCE = b'\x30' +SET = b'\x31' + + +# +# ASN.1 Helper functions. +# +def encode_element(ber_type, data): + ''' Encode an ASN.1 BER element. ''' + if data is None: + return ber_type + encode_length(0) + return ber_type + encode_length(len(data)) + data + + +def encode_length(length): + ''' Encode the length of an ASN.1 BER element. ''' + + if length > 0xFFFFFF: + return b'\x84' + length.to_bytes(4, "big") + if length > 0xFFFF: + return b'\x83' + length.to_bytes(3, "big") + if length > 0xFF: + return b'\x82' + length.to_bytes(2, "big") + if length > 0x7F: + return b'\x81' + length.to_bytes(1, "big") + return length.to_bytes(1, "big") + + +def encode_string(string): + ''' Encode an octet string ''' + return encode_element(OCTET_STRING, string) + + +def encode_boolean(boolean): + ''' Encode a boolean value ''' + if boolean: + return encode_element(BOOLEAN, b'\xFF') + return encode_element(BOOLEAN, b'\x00') + + +def encode_integer(integer): + ''' Encode an integer value ''' + bit_len = integer.bit_length() + byte_len = (bit_len // 8) + 1 + return encode_element(INTEGER, integer.to_bytes(byte_len, "big")) + + +def encode_enumerated(enum): + ''' Encode an enumerated value ''' + return encode_element(ENUMERATED, enum.to_bytes(1, "big")) + + +def encode_sequence(sequence): + ''' Encode a sequence ''' + return encode_element(SEQUENCE, sequence) + + +class RawLdapTest(TestCase): + """A raw Ldap Test case.""" + + def setUp(self): + super(RawLdapTest, self).setUp() + + self.host = samba.tests.env_get_var_value('SERVER') + self.port = 389 + self.socket = None + self.connect() + + def tearDown(self): + self.disconnect() + super(RawLdapTest, self).tearDown() + + def disconnect(self): + ''' Disconnect from and clean up the connection to the server ''' + if self.socket is None: + return + self.socket.close() + self.socket = None + + def connect(self): + ''' Open a socket stream connection to the server ''' + try: + self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + self.socket.settimeout(10) + self.socket.connect((self.host, self.port)) + except socket.error: + self.socket.close() + raise + + def send(self, req): + ''' Send the request to the server ''' + try: + self.socket.sendall(req) + except socket.error: + self.disconnect() + raise + + def recv(self, num_recv=0xffff, timeout=None): + ''' recv an array of bytes from the server ''' + data = None + try: + if timeout is not None: + self.socket.settimeout(timeout) + data = self.socket.recv(num_recv, 0) + self.socket.settimeout(10) + if len(data) == 0: + self.disconnect() + return None + except socket.timeout: + # We ignore timeout's as the ldap server will drop the connection + # on the errors we're testing. So returning None on a timeout is + # the desired behaviour. + self.socket.settimeout(10) + except socket.error: + self.disconnect() + raise + return data + + def test_search_equals_maximum_permitted_size(self): + ''' + Check that an LDAP search request equal to the maximum size is accepted + ''' + + # Lets build an ldap search packet to query the RootDSE + header = encode_string(None) # Base DN, "" + header += encode_enumerated(0) # Enumeration scope + header += encode_enumerated(0) # Enumeration dereference + header += encode_integer(0) # Integer size limit + header += encode_integer(0) # Integer time limit + header += encode_boolean(False) # Boolean attributes only + + # + # build an equality search of the form x...x=y...y + # With the length of x...x and y...y chosen to generate an + # ldap request of 256000 bytes. + x = encode_string(b'x' * 127974) + y = encode_string(b'y' * 127979) + equals = encode_element(EQUALS, x + y) + trailer = encode_sequence(None) + search = encode_element(SEARCH, header + equals + trailer) + + msg_no = encode_integer(1) + packet = encode_sequence(msg_no + search) + # + # The length of the packet should be equal to the + # Maximum length of a search query + self.assertEqual(256000, len(packet)) + + self.send(packet) + data = self.recv() + self.assertIsNotNone(data) + + # Should be a sequence + self.assertEqual(SEQUENCE, data[0:1]) + + def test_search_exceeds_maximum_permitted_size(self): + ''' + Test that a search query longer than the maximum permitted + size is rejected. + ''' + + # Lets build an ldap search packet to query the RootDSE + header = encode_string(None) # Base DN, "" + header += encode_enumerated(0) # Enumeration scope + header += encode_enumerated(0) # Enumeration dereference + header += encode_integer(0) # Integer size limit + header += encode_integer(0) # Integer time limit + header += encode_boolean(False) # Boolean attributes only + + # + # build an equality search of the form x...x=y...y + # With the length of x...x and y...y chosen to generate an + # ldap request of 256001 bytes. + x = encode_string(b'x' * 127979) + y = encode_string(b'y' * 127975) + equals = encode_element(EQUALS, x + y) + trailer = encode_sequence(None) + search = encode_element(SEARCH, header + equals + trailer) + + msg_no = encode_integer(1) + packet = encode_sequence(msg_no + search) + # + # The length of the sequence data should be one greater than the + # Maximum length of a search query + self.assertEqual(256001, len(packet)) + + self.send(packet) + data = self.recv() + # + # The connection should be closed by the server and we should not + # see any data. + self.assertIsNone(data) diff --git a/selftest/knownfail.d/ldap_raw b/selftest/knownfail.d/ldap_raw new file mode 100644 index 00000000000..8bd2ee55166 --- /dev/null +++ b/selftest/knownfail.d/ldap_raw @@ -0,0 +1 @@ +^samba.tests.ldap_raw.samba.tests.ldap_raw.RawLdapTest.test_search_exceeds_maximum_permitted_size\(ad_dc\) diff --git a/source4/selftest/tests.py b/source4/selftest/tests.py index 251ace24052..f7645365384 100755 --- a/source4/selftest/tests.py +++ b/source4/selftest/tests.py @@ -881,6 +881,11 @@ plantestsuite_loadlist("samba4.ldap_modify_order.normal_user.python(ad_dc_defaul '$LOADLIST', '$LISTOPT']) +planoldpythontestsuite("ad_dc", + "samba.tests.ldap_raw", + extra_args=['-U"$USERNAME%$PASSWORD"'], + environ={'TEST_ENV': 'ad_dc'}) + plantestsuite_loadlist("samba4.tokengroups.krb5.python(ad_dc_default)", "ad_dc_default:local", [python, os.path.join(DSDB_PYTEST_DIR, "token_group.py"), '$SERVER', '-U"$USERNAME%$PASSWORD"', '--workgroup=$DOMAIN', '-k', 'yes', '$LOADLIST', '$LISTOPT']) plantestsuite_loadlist("samba4.tokengroups.ntlm.python(ad_dc_default)", "ad_dc_default:local", [python, os.path.join(DSDB_PYTEST_DIR, "token_group.py"), '$SERVER', '-U"$USERNAME%$PASSWORD"', '--workgroup=$DOMAIN', '-k', 'no', '$LOADLIST', '$LISTOPT']) plantestsuite("samba4.sam.python(fl2008r2dc)", "fl2008r2dc", [python, os.path.join(DSDB_PYTEST_DIR, "sam.py"), '$SERVER', '-U"$USERNAME%$PASSWORD"', '--workgroup=$DOMAIN']) -- 2.17.1 From fde648202947e96deb911ac1f6941455fc272468 Mon Sep 17 00:00:00 2001 From: Gary Lockyer Date: Tue, 7 Apr 2020 09:09:01 +1200 Subject: [PATCH 09/12] CVE-2020-10704: smb.conf: Add max ldap request sizes Add two new smb.conf parameters to control the maximum permitted ldap request size. Adds: ldap max anonymous request size default 250Kb ldap max authenticated request size default 16Mb Credit to OSS-Fuzz REF: https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=20454 BUG: https://bugzilla.samba.org/show_bug.cgi?id=14334 Signed-off-by: Gary Lockyer Reviewed-by: Andrew Bartlett --- .../smbdotconf/ldap/ldapmaxanonrequest.xml | 18 ++++++++++++++++++ .../smbdotconf/ldap/ldapmaxauthrequest.xml | 18 ++++++++++++++++++ lib/param/loadparm.c | 5 +++++ source3/param/loadparm.c | 3 +++ 4 files changed, 44 insertions(+) create mode 100644 docs-xml/smbdotconf/ldap/ldapmaxanonrequest.xml create mode 100644 docs-xml/smbdotconf/ldap/ldapmaxauthrequest.xml diff --git a/docs-xml/smbdotconf/ldap/ldapmaxanonrequest.xml b/docs-xml/smbdotconf/ldap/ldapmaxanonrequest.xml new file mode 100644 index 00000000000..61bdcec674d --- /dev/null +++ b/docs-xml/smbdotconf/ldap/ldapmaxanonrequest.xml @@ -0,0 +1,18 @@ + + + + This parameter specifies the maximum permitted size (in bytes) + for an LDAP request received on an anonymous connection. + + + + If the request size exceeds this limit the request will be + rejected. + + +256000 +500000 + diff --git a/docs-xml/smbdotconf/ldap/ldapmaxauthrequest.xml b/docs-xml/smbdotconf/ldap/ldapmaxauthrequest.xml new file mode 100644 index 00000000000..c5934f73f95 --- /dev/null +++ b/docs-xml/smbdotconf/ldap/ldapmaxauthrequest.xml @@ -0,0 +1,18 @@ + + + + This parameter specifies the maximum permitted size (in bytes) + for an LDAP request received on an authenticated connection. + + + + If the request size exceeds this limit the request will be + rejected. + + +16777216 +4194304 + diff --git a/lib/param/loadparm.c b/lib/param/loadparm.c index 883d4167bf4..8e3521c918e 100644 --- a/lib/param/loadparm.c +++ b/lib/param/loadparm.c @@ -3025,6 +3025,11 @@ struct loadparm_context *loadparm_init(TALLOC_CTX *mem_ctx) lpcfg_do_global_parameter(lp_ctx, "debug encryption", "no"); + lpcfg_do_global_parameter( + lp_ctx, "ldap max anonymous request size", "256000"); + lpcfg_do_global_parameter( + lp_ctx, "ldap max authenticated request size", "16777216"); + for (i = 0; parm_table[i].label; i++) { if (!(lp_ctx->flags[i] & FLAG_CMDLINE)) { lp_ctx->flags[i] |= FLAG_DEFAULT; diff --git a/source3/param/loadparm.c b/source3/param/loadparm.c index a8d5fdc5954..dc1ad7aeafb 100644 --- a/source3/param/loadparm.c +++ b/source3/param/loadparm.c @@ -956,6 +956,9 @@ static void init_globals(struct loadparm_context *lp_ctx, bool reinit_globals) Globals.prefork_backoff_increment = 10; Globals.prefork_maximum_backoff = 120; + Globals.ldap_max_anonymous_request_size = 256000; + Globals.ldap_max_authenticated_request_size = 16777216; + /* Now put back the settings that were set with lp_set_cmdline() */ apply_lp_set_cmdline(); } -- 2.17.1 From 5cf90961748bfcbd8781317a9a80f4ca806a19fb Mon Sep 17 00:00:00 2001 From: Gary Lockyer Date: Wed, 8 Apr 2020 15:32:22 +1200 Subject: [PATCH 10/12] CVE-2020-10704: S4 ldap server: Limit request sizes Check the size of authenticated and anonymous ldap requests and reject them if they exceed the limits in smb.conf Credit to OSS-Fuzz REF: https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=20454 BUG: https://bugzilla.samba.org/show_bug.cgi?id=14334 Signed-off-by: Gary Lockyer Reviewed-by: Andrew Bartlett --- selftest/knownfail.d/ldap_raw | 1 - source4/ldap_server/ldap_server.c | 96 ++++++++++++++++++++++++++++++- 2 files changed, 95 insertions(+), 2 deletions(-) delete mode 100644 selftest/knownfail.d/ldap_raw diff --git a/selftest/knownfail.d/ldap_raw b/selftest/knownfail.d/ldap_raw deleted file mode 100644 index 8bd2ee55166..00000000000 --- a/selftest/knownfail.d/ldap_raw +++ /dev/null @@ -1 +0,0 @@ -^samba.tests.ldap_raw.samba.tests.ldap_raw.RawLdapTest.test_search_exceeds_maximum_permitted_size\(ad_dc\) diff --git a/source4/ldap_server/ldap_server.c b/source4/ldap_server/ldap_server.c index 6d329329909..a730667abb9 100644 --- a/source4/ldap_server/ldap_server.c +++ b/source4/ldap_server/ldap_server.c @@ -441,6 +441,10 @@ static void ldapsrv_accept_tls_done(struct tevent_req *subreq) } static void ldapsrv_call_read_done(struct tevent_req *subreq); +static NTSTATUS ldapsrv_packet_check( + void *private_data, + DATA_BLOB blob, + size_t *packet_size); static bool ldapsrv_call_read_next(struct ldapsrv_connection *conn) { @@ -494,7 +498,7 @@ static bool ldapsrv_call_read_next(struct ldapsrv_connection *conn) conn->connection->event.ctx, conn->sockets.active, 7, /* initial_read_size */ - ldap_full_packet, + ldapsrv_packet_check, conn); if (subreq == NULL) { ldapsrv_terminate_connection(conn, "ldapsrv_call_read_next: " @@ -520,6 +524,9 @@ static bool ldapsrv_call_read_next(struct ldapsrv_connection *conn) } static void ldapsrv_call_process_done(struct tevent_req *subreq); +static int ldapsrv_check_packet_size( + struct ldapsrv_connection *conn, + size_t size); static void ldapsrv_call_read_done(struct tevent_req *subreq) { @@ -530,6 +537,7 @@ static void ldapsrv_call_read_done(struct tevent_req *subreq) struct ldapsrv_call *call; struct asn1_data *asn1; DATA_BLOB blob; + int ret = LDAP_SUCCESS; conn->sockets.read_req = NULL; @@ -560,6 +568,14 @@ static void ldapsrv_call_read_done(struct tevent_req *subreq) return; } + ret = ldapsrv_check_packet_size(conn, blob.length); + if (ret != LDAP_SUCCESS) { + ldapsrv_terminate_connection( + conn, + "Request packet too large"); + return; + } + asn1 = asn1_init(call, ASN1_MAX_TREE_DEPTH); if (asn1 == NULL) { ldapsrv_terminate_connection(conn, "no memory"); @@ -1362,6 +1378,84 @@ static void ldapsrv_post_fork(struct task_server *task, struct process_details * } } +/* + * Check the size of an ldap request packet. + * + * For authenticated connections the maximum packet size is controlled by + * the smb.conf parameter "ldap max authenticated request size" + * + * For anonymous connections the maximum packet size is controlled by + * the smb.conf parameter "ldap max anonymous request size" + */ +static int ldapsrv_check_packet_size( + struct ldapsrv_connection *conn, + size_t size) +{ + bool is_anonymous = false; + size_t max_size = 0; + + max_size = lpcfg_ldap_max_anonymous_request_size(conn->lp_ctx); + if (size <= max_size) { + return LDAP_SUCCESS; + } + + /* + * Request is larger than the maximum unauthenticated request size. + * As this code is called frequently we avoid calling + * security_token_is_anonymous if possible + */ + if (conn->session_info != NULL && + conn->session_info->security_token != NULL) { + is_anonymous = security_token_is_anonymous( + conn->session_info->security_token); + } + + if (is_anonymous) { + DBG_WARNING( + "LDAP request size (%zu) exceeds (%zu)\n", + size, + max_size); + return LDAP_UNWILLING_TO_PERFORM; + } + + max_size = lpcfg_ldap_max_authenticated_request_size(conn->lp_ctx); + if (size > max_size) { + DBG_WARNING( + "LDAP request size (%zu) exceeds (%zu)\n", + size, + max_size); + return LDAP_UNWILLING_TO_PERFORM; + } + return LDAP_SUCCESS; + +} + +/* + * Check that the blob contains enough data to be a valid packet + * If there is a packet header check the size to ensure that it does not + * exceed the maximum sizes. + * + */ +static NTSTATUS ldapsrv_packet_check( + void *private_data, + DATA_BLOB blob, + size_t *packet_size) +{ + NTSTATUS ret; + struct ldapsrv_connection *conn = private_data; + int result = LDB_SUCCESS; + + ret = ldap_full_packet(private_data, blob, packet_size); + if (!NT_STATUS_IS_OK(ret)) { + return ret; + } + result = ldapsrv_check_packet_size(conn, *packet_size); + if (result != LDAP_SUCCESS) { + return NT_STATUS_LDAP(result); + } + return NT_STATUS_OK; +} + NTSTATUS server_service_ldap_init(TALLOC_CTX *ctx) { static const struct service_details details = { -- 2.17.1 From 458b014a4d10e89f99cc0b54a815b50d6c817dce Mon Sep 17 00:00:00 2001 From: Gary Lockyer Date: Wed, 8 Apr 2020 08:49:23 +1200 Subject: [PATCH 11/12] CVE-2020-10704: libcli ldap_message: Add search size limits to ldap_decode Add search request size limits to ldap_decode calls. The ldap server uses the smb.conf variable "ldap max search request size" which defaults to 250Kb. For cldap the limit is hard coded as 4096. Credit to OSS-Fuzz REF: https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=20454 BUG: https://bugzilla.samba.org/show_bug.cgi?id=14334 Signed-off-by: Gary Lockyer Reviewed-by: Andrew Bartlett --- .../smbdotconf/ldap/ldapmaxsearchrequest.xml | 18 ++++++++++++++ lib/param/loadparm.c | 2 ++ libcli/cldap/cldap.c | 18 +++++++++++--- libcli/ldap/ldap_message.c | 1 + libcli/ldap/ldap_message.h | 5 ++++ libcli/ldap/tests/ldap_message_test.c | 24 +++++++++++++++---- source3/param/loadparm.c | 1 + source4/ldap_server/ldap_server.c | 10 ++++++-- source4/libcli/ldap/ldap_client.c | 3 ++- 9 files changed, 72 insertions(+), 10 deletions(-) create mode 100644 docs-xml/smbdotconf/ldap/ldapmaxsearchrequest.xml diff --git a/docs-xml/smbdotconf/ldap/ldapmaxsearchrequest.xml b/docs-xml/smbdotconf/ldap/ldapmaxsearchrequest.xml new file mode 100644 index 00000000000..ebeb0816c01 --- /dev/null +++ b/docs-xml/smbdotconf/ldap/ldapmaxsearchrequest.xml @@ -0,0 +1,18 @@ + + + + This parameter specifies the maximum permitted size (in bytes) + for an LDAP search request. + + + + If the request size exceeds this limit the request will be + rejected. + + +256000 +4194304 + diff --git a/lib/param/loadparm.c b/lib/param/loadparm.c index 8e3521c918e..e0c6adec9c8 100644 --- a/lib/param/loadparm.c +++ b/lib/param/loadparm.c @@ -3029,6 +3029,8 @@ struct loadparm_context *loadparm_init(TALLOC_CTX *mem_ctx) lp_ctx, "ldap max anonymous request size", "256000"); lpcfg_do_global_parameter( lp_ctx, "ldap max authenticated request size", "16777216"); + lpcfg_do_global_parameter( + lp_ctx, "ldap max search request size", "256000"); for (i = 0; parm_table[i].label; i++) { if (!(lp_ctx->flags[i] & FLAG_CMDLINE)) { diff --git a/libcli/cldap/cldap.c b/libcli/cldap/cldap.c index 8fa9ce0b273..25c1b40f8d9 100644 --- a/libcli/cldap/cldap.c +++ b/libcli/cldap/cldap.c @@ -111,6 +111,11 @@ struct cldap_search_state { struct tevent_req *req; }; +/* + * For CLDAP we limit the maximum search request size to 4kb + */ +#define MAX_SEARCH_REQUEST 4096 + static int cldap_socket_destructor(struct cldap_socket *c) { while (c->searches.list) { @@ -224,6 +229,9 @@ static bool cldap_socket_recv_dgram(struct cldap_socket *c, void *p; struct cldap_search_state *search; NTSTATUS status; + struct ldap_request_limits limits = { + .max_search_size = MAX_SEARCH_REQUEST + }; if (in->recv_errno != 0) { goto error; @@ -242,7 +250,7 @@ static bool cldap_socket_recv_dgram(struct cldap_socket *c, } /* this initial decode is used to find the message id */ - status = ldap_decode(asn1, NULL, in->ldap_msg); + status = ldap_decode(asn1, &limits, NULL, in->ldap_msg); if (!NT_STATUS_IS_OK(status)) { goto nterror; } @@ -770,6 +778,9 @@ NTSTATUS cldap_search_recv(struct tevent_req *req, struct cldap_search_state); struct ldap_message *ldap_msg; NTSTATUS status; + struct ldap_request_limits limits = { + .max_search_size = MAX_SEARCH_REQUEST + }; if (tevent_req_is_nterror(req, &status)) { goto failed; @@ -780,7 +791,7 @@ NTSTATUS cldap_search_recv(struct tevent_req *req, goto nomem; } - status = ldap_decode(state->response.asn1, NULL, ldap_msg); + status = ldap_decode(state->response.asn1, &limits, NULL, ldap_msg); if (!NT_STATUS_IS_OK(status)) { goto failed; } @@ -796,7 +807,8 @@ NTSTATUS cldap_search_recv(struct tevent_req *req, *io->out.response = ldap_msg->r.SearchResultEntry; /* decode the 2nd part */ - status = ldap_decode(state->response.asn1, NULL, ldap_msg); + status = ldap_decode( + state->response.asn1, &limits, NULL, ldap_msg); if (!NT_STATUS_IS_OK(status)) { goto failed; } diff --git a/libcli/ldap/ldap_message.c b/libcli/ldap/ldap_message.c index ba82bddeab1..d38fa0b3b61 100644 --- a/libcli/ldap/ldap_message.c +++ b/libcli/ldap/ldap_message.c @@ -1162,6 +1162,7 @@ static bool ldap_decode_attribs(TALLOC_CTX *mem_ctx, struct asn1_data *data, /* This routine returns LDAP status codes */ _PUBLIC_ NTSTATUS ldap_decode(struct asn1_data *data, + const struct ldap_request_limits *limits, const struct ldap_control_handler *control_handlers, struct ldap_message *msg) { diff --git a/libcli/ldap/ldap_message.h b/libcli/ldap/ldap_message.h index 2f64881c053..19bfb99ac97 100644 --- a/libcli/ldap/ldap_message.h +++ b/libcli/ldap/ldap_message.h @@ -213,10 +213,15 @@ struct ldap_control_handler { bool (*encode)(void *mem_ctx, void *in, DATA_BLOB *out); }; +struct ldap_request_limits { + unsigned max_search_size; +}; + struct asn1_data; struct ldap_message *new_ldap_message(TALLOC_CTX *mem_ctx); NTSTATUS ldap_decode(struct asn1_data *data, + const struct ldap_request_limits *limits, const struct ldap_control_handler *control_handlers, struct ldap_message *msg); bool ldap_encode(struct ldap_message *msg, diff --git a/libcli/ldap/tests/ldap_message_test.c b/libcli/ldap/tests/ldap_message_test.c index 9cc9cc5d8a0..c5aacd4bc6b 100644 --- a/libcli/ldap/tests/ldap_message_test.c +++ b/libcli/ldap/tests/ldap_message_test.c @@ -117,6 +117,9 @@ static void test_empty_input(void **state) NTSTATUS status; uint8_t buf[0]; size_t len = 0; + struct ldap_request_limits limits = { + .max_search_size = 256000, + }; asn1 = asn1_init(test_ctx, ASN1_MAX_TREE_DEPTH); @@ -127,7 +130,8 @@ static void test_empty_input(void **state) ldap_msg = talloc(test_ctx, struct ldap_message); assert_non_null(ldap_msg); - status = ldap_decode(asn1, samba_ldap_control_handlers(), ldap_msg); + status = ldap_decode( + asn1, &limits, samba_ldap_control_handlers(), ldap_msg); assert_ldap_status_equal(LDAP_PROTOCOL_ERROR, status); } @@ -149,6 +153,9 @@ static void test_recursion_depth_large(void **state) uint8_t *buffer = NULL; const size_t BUFF_SIZE = 1048576; size_t len; + struct ldap_request_limits limits = { + .max_search_size = 256000, + }; /* @@ -169,7 +176,8 @@ static void test_recursion_depth_large(void **state) ldap_msg = talloc(test_ctx, struct ldap_message); assert_non_null(ldap_msg); - status = ldap_decode(asn1, samba_ldap_control_handlers(), ldap_msg); + status = ldap_decode( + asn1, &limits, samba_ldap_control_handlers(), ldap_msg); assert_ldap_status_equal(LDAP_PROTOCOL_ERROR, status); } @@ -189,6 +197,9 @@ static void test_recursion_depth_equals_max(void **state) uint8_t *buffer = NULL; const size_t BUFF_SIZE = 1048576; size_t len; + struct ldap_request_limits limits = { + .max_search_size = 256000, + }; buffer = talloc_zero_array(test_ctx, uint8_t, BUFF_SIZE); @@ -205,7 +216,8 @@ static void test_recursion_depth_equals_max(void **state) ldap_msg = talloc(test_ctx, struct ldap_message); assert_non_null(ldap_msg); - status = ldap_decode(asn1, samba_ldap_control_handlers(), ldap_msg); + status = ldap_decode( + asn1, &limits, samba_ldap_control_handlers(), ldap_msg); assert_true(NT_STATUS_IS_OK(status)); } @@ -225,6 +237,9 @@ static void test_recursion_depth_greater_than_max(void **state) uint8_t *buffer = NULL; const size_t BUFF_SIZE = 1048576; size_t len; + struct ldap_request_limits limits = { + .max_search_size = 256000, + }; buffer = talloc_zero_array(test_ctx, uint8_t, BUFF_SIZE); @@ -241,7 +256,8 @@ static void test_recursion_depth_greater_than_max(void **state) ldap_msg = talloc(test_ctx, struct ldap_message); assert_non_null(ldap_msg); - status = ldap_decode(asn1, samba_ldap_control_handlers(), ldap_msg); + status = ldap_decode( + asn1, &limits, samba_ldap_control_handlers(), ldap_msg); assert_ldap_status_equal(LDAP_PROTOCOL_ERROR, status); } diff --git a/source3/param/loadparm.c b/source3/param/loadparm.c index dc1ad7aeafb..5ee6d33c7fc 100644 --- a/source3/param/loadparm.c +++ b/source3/param/loadparm.c @@ -958,6 +958,7 @@ static void init_globals(struct loadparm_context *lp_ctx, bool reinit_globals) Globals.ldap_max_anonymous_request_size = 256000; Globals.ldap_max_authenticated_request_size = 16777216; + Globals.ldap_max_search_request_size = 256000; /* Now put back the settings that were set with lp_set_cmdline() */ apply_lp_set_cmdline(); diff --git a/source4/ldap_server/ldap_server.c b/source4/ldap_server/ldap_server.c index a730667abb9..a9b162b284e 100644 --- a/source4/ldap_server/ldap_server.c +++ b/source4/ldap_server/ldap_server.c @@ -538,6 +538,7 @@ static void ldapsrv_call_read_done(struct tevent_req *subreq) struct asn1_data *asn1; DATA_BLOB blob; int ret = LDAP_SUCCESS; + struct ldap_request_limits limits = {0}; conn->sockets.read_req = NULL; @@ -593,8 +594,13 @@ static void ldapsrv_call_read_done(struct tevent_req *subreq) return; } - status = ldap_decode(asn1, samba_ldap_control_handlers(), - call->request); + limits.max_search_size = + lpcfg_ldap_max_search_request_size(conn->lp_ctx); + status = ldap_decode( + asn1, + &limits, + samba_ldap_control_handlers(), + call->request); if (!NT_STATUS_IS_OK(status)) { ldapsrv_terminate_connection(conn, nt_errstr(status)); return; diff --git a/source4/libcli/ldap/ldap_client.c b/source4/libcli/ldap/ldap_client.c index 2d75af6af6e..cf276679594 100644 --- a/source4/libcli/ldap/ldap_client.c +++ b/source4/libcli/ldap/ldap_client.c @@ -277,6 +277,7 @@ static void ldap_connection_recv_done(struct tevent_req *subreq) struct ldap_message *msg; struct asn1_data *asn1; DATA_BLOB blob; + struct ldap_request_limits limits = {0}; msg = talloc_zero(conn, struct ldap_message); if (msg == NULL) { @@ -306,7 +307,7 @@ static void ldap_connection_recv_done(struct tevent_req *subreq) asn1_load_nocopy(asn1, blob.data, blob.length); - status = ldap_decode(asn1, samba_ldap_control_handlers(), msg); + status = ldap_decode(asn1, &limits, samba_ldap_control_handlers(), msg); asn1_free(asn1); if (!NT_STATUS_IS_OK(status)) { TALLOC_FREE(msg); -- 2.17.1 From 40a58a973c0cfb7b259198c8f5df15ada39240ab Mon Sep 17 00:00:00 2001 From: Gary Lockyer Date: Wed, 8 Apr 2020 10:46:44 +1200 Subject: [PATCH 12/12] CVE-2020-10704 libcli ldap: Check search request lengths. Check the search request lengths against the limits passed to ldap_decode. Credit to OSS-Fuzz REF: https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=20454 BUG: https://bugzilla.samba.org/show_bug.cgi?id=14334 Signed-off-by: Gary Lockyer Reviewed-by: Andrew Bartlett --- lib/util/asn1.c | 7 +++++++ lib/util/asn1.h | 1 + libcli/ldap/ldap_message.c | 4 ++++ 3 files changed, 12 insertions(+) diff --git a/lib/util/asn1.c b/lib/util/asn1.c index ee3cff9cb65..32d7981d28f 100644 --- a/lib/util/asn1.c +++ b/lib/util/asn1.c @@ -1159,3 +1159,10 @@ int asn1_peek_full_tag(DATA_BLOB blob, uint8_t tag, size_t *packet_size) *packet_size = size; return 0; } + +/* + * Get the length of the ASN.1 data + */ +size_t asn1_get_length(const struct asn1_data *asn1) { + return asn1->length; +} diff --git a/lib/util/asn1.h b/lib/util/asn1.h index fc365724e93..de92a767f14 100644 --- a/lib/util/asn1.h +++ b/lib/util/asn1.h @@ -106,5 +106,6 @@ bool asn1_extract_blob(struct asn1_data *asn1, TALLOC_CTX *mem_ctx, DATA_BLOB *pblob); void asn1_load_nocopy(struct asn1_data *data, uint8_t *buf, size_t len); int asn1_peek_full_tag(DATA_BLOB blob, uint8_t tag, size_t *packet_size); +size_t asn1_get_length(const struct asn1_data *asn1); #endif /* _ASN_1_H */ diff --git a/libcli/ldap/ldap_message.c b/libcli/ldap/ldap_message.c index d38fa0b3b61..69a48279532 100644 --- a/libcli/ldap/ldap_message.c +++ b/libcli/ldap/ldap_message.c @@ -1259,7 +1259,11 @@ _PUBLIC_ NTSTATUS ldap_decode(struct asn1_data *data, struct ldap_SearchRequest *r = &msg->r.SearchRequest; int sizelimit, timelimit; const char **attrs = NULL; + size_t request_size = asn1_get_length(data); msg->type = LDAP_TAG_SearchRequest; + if (request_size > limits->max_search_size) { + goto prot_err; + } if (!asn1_start_tag(data, tag)) goto prot_err; if (!asn1_read_OctetString_talloc(msg, data, &r->basedn)) goto prot_err; if (!asn1_read_enumerated(data, (int *)(void *)&(r->scope))) goto prot_err; -- 2.17.1