diff --git a/CVE-2020-10700-1.patch b/CVE-2020-10700-1.patch new file mode 100644 index 0000000000000000000000000000000000000000..946d03eb34da312a37ab9dd29532f28fbb4f1392 --- /dev/null +++ b/CVE-2020-10700-1.patch @@ -0,0 +1,221 @@ +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 + diff --git a/CVE-2020-10700-2.patch b/CVE-2020-10700-2.patch new file mode 100644 index 0000000000000000000000000000000000000000..0a93436d9b5d62dbfc86846b9590c6264af8149a --- /dev/null +++ b/CVE-2020-10700-2.patch @@ -0,0 +1,84 @@ +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 + diff --git a/CVE-2020-10700-3.patch b/CVE-2020-10700-3.patch new file mode 100644 index 0000000000000000000000000000000000000000..cda1fc7959c2ca3bee9727546db0b5cdaa8fa70a --- /dev/null +++ b/CVE-2020-10700-3.patch @@ -0,0 +1,81 @@ +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 + diff --git a/CVE-2020-10704-1.patch b/CVE-2020-10704-1.patch new file mode 100644 index 0000000000000000000000000000000000000000..d2475e1f9ffc51e9e7256f598755a695ef7b8774 --- /dev/null +++ b/CVE-2020-10704-1.patch @@ -0,0 +1,546 @@ +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 + diff --git a/CVE-2020-10704-2.patch b/CVE-2020-10704-2.patch new file mode 100644 index 0000000000000000000000000000000000000000..0d261f2a455da490d1cf35c1393ed8936bc80837 --- /dev/null +++ b/CVE-2020-10704-2.patch @@ -0,0 +1,54 @@ +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; +2.17.1 + diff --git a/CVE-2020-10704-3.patch b/CVE-2020-10704-3.patch new file mode 100644 index 0000000000000000000000000000000000000000..60e224704b11ce9a77724bf25473479a20e8e402 --- /dev/null +++ b/CVE-2020-10704-3.patch @@ -0,0 +1,109 @@ +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 + diff --git a/CVE-2020-10704-4.patch b/CVE-2020-10704-4.patch new file mode 100644 index 0000000000000000000000000000000000000000..9ac1a1a9369301875faf4c9bcbcbd3ae599568e9 --- /dev/null +++ b/CVE-2020-10704-4.patch @@ -0,0 +1,166 @@ +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/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 + diff --git a/CVE-2020-10704-5.patch b/CVE-2020-10704-5.patch new file mode 100644 index 0000000000000000000000000000000000000000..c3b2a5571596277a43acc2a4aec324bdd8f4639f --- /dev/null +++ b/CVE-2020-10704-5.patch @@ -0,0 +1,229 @@ +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/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 + diff --git a/CVE-2020-10704-6.patch b/CVE-2020-10704-6.patch new file mode 100644 index 0000000000000000000000000000000000000000..66aeff9ed8ec3264dabc62255a9c6a68a7cce374 --- /dev/null +++ b/CVE-2020-10704-6.patch @@ -0,0 +1,67 @@ +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 + diff --git a/samba.spec b/samba.spec index 49a270f58463dcf92d872be57fc688c9d6729f7e..2d032a8edaf31df1708d3db94827560f03d22cf5 100644 --- a/samba.spec +++ b/samba.spec @@ -49,7 +49,7 @@ Name: samba Version: 4.11.6 -Release: 5 +Release: 6 Summary: A suite for Linux to interoperate with Windows License: GPLv3+ and LGPLv3+ @@ -69,6 +69,15 @@ Source201: README.downgrade Patch100: 0000-use-gnutls-for-des-cbc.patch Patch101: 0001-handle-removal-des-enctypes-from-krb5.patch Patch102: 0002-samba-tool-create-working-private-krb5.conf.patch +Patch103: CVE-2020-10700-1.patch +Patch104: CVE-2020-10700-2.patch +Patch105: CVE-2020-10700-3.patch +Patch106: CVE-2020-10704-1.patch +Patch107: CVE-2020-10704-2.patch +Patch108: CVE-2020-10704-3.patch +Patch109: CVE-2020-10704-4.patch +Patch110: CVE-2020-10704-5.patch +Patch111: CVE-2020-10704-6.patch BuildRequires: avahi-devel cups-devel dbus-devel docbook-style-xsl e2fsprogs-devel gawk gnupg2 gnutls-devel >= 3.4.7 gpgme-devel BuildRequires: jansson-devel krb5-devel >= %{required_mit_krb5} libacl-devel libaio-devel libarchive-devel libattr-devel @@ -3079,6 +3088,12 @@ fi %{_mandir}/man8/* %changelog +* Mon May 18 2020 chenzhen - 4.11.6-6 +- Type:cves +- ID:CVE-2020-10700 CVE-2020-10704 +- SUG:restart +- DESC:fix CVE-2020-10700 CVE-2020-10704 + * Sat Mar 21 2020 songnannan - 4.11.6-5 - bugfix about update