!19 【LTS升级】update samba to 4.11.12

Merge pull request !19 from yu_boyun/openEuler-20.03-LTS
This commit is contained in:
openeuler-ci-bot 2020-08-31 19:59:11 +08:00 committed by Gitee
commit afc685fe74
34 changed files with 53 additions and 3879 deletions

View File

@ -1,222 +0,0 @@
From 0e77fa7747d789bd8c9256373498a352251f6877 Mon Sep 17 00:00:00 2001
From: Andrew Bartlett <abartlet@samba.org>
Date: Mon, 30 Mar 2020 09:44:20 +0000
Subject: [PATCH 1/4] CVE-2020-10700: dsdb: Add test for ASQ and ASQ in
combination with paged_results
Thanks to Andrei Popa <andrei.popa@next-gen.ro> 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 <abartlet@samba.org>
Reviewed-by: Gary Lockyer <gary@catalyst.net.nz>
---
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 <http://www.gnu.org/licenses/>.
+
+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] <host>")
+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

View File

@ -1,82 +0,0 @@
From 34f9e6e969913629f9241522020c5895dc9636dc Mon Sep 17 00:00:00 2001
From: Andrew Bartlett <abartlet@samba.org>
Date: Wed, 11 Mar 2020 16:43:31 +1300
Subject: [PATCH 3/4] 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 <andrei.popa@next-gen.ro> 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 <abartlet@samba.org>
Reviewed-by: Gary Lockyer <gary@catalyst.net.nz>
---
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

View File

@ -1,547 +0,0 @@
From b01952c6fb15b92fff3ad1bf8f1cf579875e5483 Mon Sep 17 00:00:00 2001
From: Gary Lockyer <gary@catalyst.net.nz>
Date: Fri, 3 Apr 2020 12:18:03 +1300
Subject: [PATCH 1/8] 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 <gary@catalyst.net.nz>
Reviewed-by: Andrew Bartlett <abartlet@samba.org>
---
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

View File

@ -1,52 +0,0 @@
Backport of:
From d3be674c3ffa3541e2ba757e2c6dfb32508db440 Mon Sep 17 00:00:00 2001
From: Gary Lockyer <gary@catalyst.net.nz>
Date: Wed, 8 Apr 2020 15:30:52 +1200
Subject: [PATCH 3/8] 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 <gary@catalyst.net.nz>
Reviewed-by: Andrew Bartlett <abartlet@samba.org>
---
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
--- a/lib/util/asn1.c
+++ b/lib/util/asn1.c
@@ -647,6 +647,16 @@ bool asn1_start_tag(struct asn1_data *da
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;

View File

@ -1,96 +0,0 @@
From 9944df6ef1e421331ea1ca773f7e5652262d5d1b Mon Sep 17 00:00:00 2001
From: Gary Lockyer <gary@catalyst.net.nz>
Date: Tue, 7 Apr 2020 09:09:01 +1200
Subject: [PATCH 5/8] 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 <gary@catalyst.net.nz>
Reviewed-by: Andrew Bartlett <abartlet@samba.org>
---
.../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
--- /dev/null
+++ b/docs-xml/smbdotconf/ldap/ldapmaxanonrequest.xml
@@ -0,0 +1,18 @@
+<samba:parameter name="ldap max anonymous request size"
+ context="G"
+ type="integer"
+ xmlns:samba="http://www.samba.org/samba/DTD/samba-doc">
+<description>
+ <para>
+ This parameter specifies the maximum permitted size (in bytes)
+ for an LDAP request received on an anonymous connection.
+ </para>
+
+ <para>
+ If the request size exceeds this limit the request will be
+ rejected.
+ </para>
+</description>
+<value type="default">256000</value>
+<value type="example">500000</value>
+</samba:parameter>
--- /dev/null
+++ b/docs-xml/smbdotconf/ldap/ldapmaxauthrequest.xml
@@ -0,0 +1,18 @@
+<samba:parameter name="ldap max authenticated request size"
+ context="G"
+ type="integer"
+ xmlns:samba="http://www.samba.org/samba/DTD/samba-doc">
+<description>
+ <para>
+ This parameter specifies the maximum permitted size (in bytes)
+ for an LDAP request received on an authenticated connection.
+ </para>
+
+ <para>
+ If the request size exceeds this limit the request will be
+ rejected.
+ </para>
+</description>
+<value type="default">16777216</value>
+<value type="example">4194304</value>
+</samba:parameter>
--- a/lib/param/loadparm.c
+++ b/lib/param/loadparm.c
@@ -3027,6 +3027,11 @@ struct loadparm_context *loadparm_init(T
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;
--- a/source3/param/loadparm.c
+++ b/source3/param/loadparm.c
@@ -956,6 +956,9 @@ static void init_globals(struct loadparm
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();
}

View File

@ -1,163 +0,0 @@
Backport of:
From 85619363d3280346b2253fe44bf67d4881a53ebd Mon Sep 17 00:00:00 2001
From: Gary Lockyer <gary@catalyst.net.nz>
Date: Wed, 8 Apr 2020 15:32:22 +1200
Subject: [PATCH 6/8] 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 <gary@catalyst.net.nz>
Reviewed-by: Andrew Bartlett <abartlet@samba.org>
---
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
--- a/source4/ldap_server/ldap_server.c
+++ b/source4/ldap_server/ldap_server.c
@@ -441,6 +441,10 @@ static void ldapsrv_accept_tls_done(stru
}
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(struc
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(struc
}
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(struc
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(struc
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 tas
}
}
+/*
+ * 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 = {

View File

@ -1,211 +0,0 @@
Backport of:
From 9be121c7055fde841be15f8d570ff49801b68bff Mon Sep 17 00:00:00 2001
From: Gary Lockyer <gary@catalyst.net.nz>
Date: Wed, 8 Apr 2020 08:49:23 +1200
Subject: [PATCH 7/8] 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 <gary@catalyst.net.nz>
Reviewed-by: Andrew Bartlett <abartlet@samba.org>
---
.../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
--- /dev/null
+++ b/docs-xml/smbdotconf/ldap/ldapmaxsearchrequest.xml
@@ -0,0 +1,18 @@
+<samba:parameter name="ldap max search request size"
+ context="G"
+ type="integer"
+ xmlns:samba="http://www.samba.org/samba/DTD/samba-doc">
+<description>
+ <para>
+ This parameter specifies the maximum permitted size (in bytes)
+ for an LDAP search request.
+ </para>
+
+ <para>
+ If the request size exceeds this limit the request will be
+ rejected.
+ </para>
+</description>
+<value type="default">256000</value>
+<value type="example">4194304</value>
+</samba:parameter>
--- a/lib/param/loadparm.c
+++ b/lib/param/loadparm.c
@@ -3031,6 +3031,8 @@ struct loadparm_context *loadparm_init(T
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)) {
--- 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(stru
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(stru
}
/* 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
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
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
*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;
}
--- a/libcli/ldap/ldap_message.c
+++ b/libcli/ldap/ldap_message.c
@@ -1162,6 +1162,7 @@ static bool ldap_decode_attribs(TALLOC_C
/* 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)
{
--- 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,
--- a/source3/param/loadparm.c
+++ b/source3/param/loadparm.c
@@ -958,6 +958,7 @@ static void init_globals(struct loadparm
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();
--- a/source4/ldap_server/ldap_server.c
+++ b/source4/ldap_server/ldap_server.c
@@ -538,6 +538,7 @@ static void ldapsrv_call_read_done(struc
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(struc
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;
--- a/source4/libcli/ldap/ldap_client.c
+++ b/source4/libcli/ldap/ldap_client.c
@@ -277,6 +277,7 @@ static void ldap_connection_recv_done(st
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(st
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);

View File

@ -1,66 +0,0 @@
From ee3156c76b86c11829f6f3fe1e3c940b45899c56 Mon Sep 17 00:00:00 2001
From: Gary Lockyer <gary@catalyst.net.nz>
Date: Wed, 8 Apr 2020 10:46:44 +1200
Subject: [PATCH 8/8] 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 <gary@catalyst.net.nz>
Reviewed-by: Andrew Bartlett <abartlet@samba.org>
---
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

View File

@ -1,35 +0,0 @@
From b8628cb44766ac4c4817b1a50f09ca316425bd8b Mon Sep 17 00:00:00 2001
From: Andrew Bartlett <abartlet@samba.org>
Date: Tue, 5 May 2020 12:54:59 +1200
Subject: [PATCH 01/22] CVE-2020-10730: vlv: Use strcmp(), not strncmp()
checking the NULL terminated control OIDs
The end result is the same, as sizeof() includes the trailing NUL, but this
avoids having to think about that.
BUG: https://bugzilla.samba.org/show_bug.cgi?id=14364
Signed-off-by: Andrew Bartlett <abartlet@samba.org>
Reviewed-by: Gary Lockyer <gary@catalyst.net.nz>
---
source4/dsdb/samdb/ldb_modules/vlv_pagination.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/source4/dsdb/samdb/ldb_modules/vlv_pagination.c b/source4/dsdb/samdb/ldb_modules/vlv_pagination.c
index 980177cb05e..31e64b4bd78 100644
--- a/source4/dsdb/samdb/ldb_modules/vlv_pagination.c
+++ b/source4/dsdb/samdb/ldb_modules/vlv_pagination.c
@@ -682,8 +682,8 @@ vlv_copy_down_controls(TALLOC_CTX *mem_ctx, struct ldb_control **controls)
if (control->oid == NULL) {
break;
}
- if (strncmp(control->oid, LDB_CONTROL_VLV_REQ_OID, sizeof(LDB_CONTROL_VLV_REQ_OID)) == 0 ||
- strncmp(control->oid, LDB_CONTROL_SERVER_SORT_OID, sizeof(LDB_CONTROL_SERVER_SORT_OID)) == 0) {
+ if (strcmp(control->oid, LDB_CONTROL_VLV_REQ_OID) == 0 ||
+ strcmp(control->oid, LDB_CONTROL_SERVER_SORT_OID) == 0) {
continue;
}
new_controls[j] = talloc_steal(new_controls, control);
--
2.17.1

View File

@ -1,49 +0,0 @@
From 303947c58abf9311a666fe63ebd4ce26655ff36e Mon Sep 17 00:00:00 2001
From: Gary Lockyer <gary@catalyst.net.nz>
Date: Wed, 13 May 2020 10:56:56 +1200
Subject: [PATCH 10/22] CVE-2020-10730: lib ldb: Check if
ldb_lock_backend_callback called twice
Prevent use after free issues if ldb_lock_backend_callback is called
twice, usually due to ldb_module_done being called twice. This can happen if a
module ignores the return value from function a function that calls
ldb_module_done as part of it's error handling.
BUG: https://bugzilla.samba.org/show_bug.cgi?id=14364
Signed-off-by: Gary Lockyer <gary@catalyst.net.nz>
Reviewed-by: Andrew Bartlett <abartlet@samba.org>
---
lib/ldb/common/ldb.c | 9 ++++++++-
1 file changed, 8 insertions(+), 1 deletion(-)
diff --git a/lib/ldb/common/ldb.c b/lib/ldb/common/ldb.c
index 95e9138a56b..2d0926ffaf9 100644
--- a/lib/ldb/common/ldb.c
+++ b/lib/ldb/common/ldb.c
@@ -1018,6 +1018,13 @@ static int ldb_lock_backend_callback(struct ldb_request *req,
struct ldb_db_lock_context *lock_context;
int ret;
+ if (req->context == NULL) {
+ /*
+ * The usual way to get here is to ignore the return codes
+ * and continuing processing after an error.
+ */
+ abort();
+ }
lock_context = talloc_get_type(req->context,
struct ldb_db_lock_context);
@@ -1032,7 +1039,7 @@ static int ldb_lock_backend_callback(struct ldb_request *req,
* If this is a LDB_REPLY_DONE or an error, unlock the
* DB by calling the destructor on this context
*/
- talloc_free(lock_context);
+ TALLOC_FREE(req->context);
return ret;
}
--
2.17.1

View File

@ -1,46 +0,0 @@
From 2041c05d9b41fb0255c3492d118628c14a0c4b3d Mon Sep 17 00:00:00 2001
From: Andrew Bartlett <abartlet@samba.org>
Date: Tue, 5 May 2020 12:55:57 +1200
Subject: [PATCH 02/22] CVE-2020-10730: vlv: Do not re-ASQ search the results
of an ASQ search with VLV
This is a silly combination, but at least try and keep the results sensible
and avoid a double-dereference.
BUG: https://bugzilla.samba.org/show_bug.cgi?id=14364
Signed-off-by: Andrew Bartlett <abartlet@samba.org>
Reviewed-by: Gary Lockyer <gary@catalyst.net.nz>
---
source4/dsdb/samdb/ldb_modules/vlv_pagination.c | 11 +++++++++++
1 file changed, 11 insertions(+)
diff --git a/source4/dsdb/samdb/ldb_modules/vlv_pagination.c b/source4/dsdb/samdb/ldb_modules/vlv_pagination.c
index 31e64b4bd78..d58a62482c9 100644
--- a/source4/dsdb/samdb/ldb_modules/vlv_pagination.c
+++ b/source4/dsdb/samdb/ldb_modules/vlv_pagination.c
@@ -682,10 +682,21 @@ vlv_copy_down_controls(TALLOC_CTX *mem_ctx, struct ldb_control **controls)
if (control->oid == NULL) {
break;
}
+ /*
+ * Do not re-use VLV, nor the server-sort, both are
+ * already handled here.
+ */
if (strcmp(control->oid, LDB_CONTROL_VLV_REQ_OID) == 0 ||
strcmp(control->oid, LDB_CONTROL_SERVER_SORT_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);
j++;
}
--
2.17.1

View File

@ -1,57 +0,0 @@
From cf10f9b9a9a2f94afc526995a4034c1c6f05f5b4 Mon Sep 17 00:00:00 2001
From: Andrew Bartlett <abartlet@samba.org>
Date: Tue, 5 May 2020 13:16:48 +1200
Subject: [PATCH 03/22] CVE-2020-10730: selftest: Add test to confirm VLV
interaction with ASQ
Tested against Windows 1709.
BUG: https://bugzilla.samba.org/show_bug.cgi?id=14364
Signed-off-by: Andrew Bartlett <abartlet@samba.org>
Reviewed-by: Gary Lockyer <gary@catalyst.net.nz>
---
source4/dsdb/tests/python/asq.py | 27 +++++++++++++++++++++++++++
1 file changed, 27 insertions(+)
diff --git a/source4/dsdb/tests/python/asq.py b/source4/dsdb/tests/python/asq.py
index a32c9f40cd3..1c93a45f131 100644
--- a/source4/dsdb/tests/python/asq.py
+++ b/source4/dsdb/tests/python/asq.py
@@ -162,6 +162,33 @@ class ASQLDAPTest(samba.tests.TestCase):
self.assertIn(ldb.Dn(self.ldb, str(group)),
self.members)
+ def test_asq_vlv(self):
+ """Testing ASQ behaviour with VLV 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!
+
+ """
+
+ sort_control = "server_sort:1:0:cn"
+
+ msgs = self.ldb.search(base=self.top_dn,
+ scope=ldb.SCOPE_BASE,
+ attrs=["objectGUID", "cn", "member"],
+ controls=["asq:1:member",
+ sort_control,
+ "vlv:1:20:20:11:0"])
+
+ 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
--
2.17.1

View File

@ -1,53 +0,0 @@
From 3fd7ce69761fd2e21a85101772196aafc5ae57df Mon Sep 17 00:00:00 2001
From: Andrew Bartlett <abartlet@samba.org>
Date: Tue, 5 May 2020 16:34:11 +1200
Subject: [PATCH 04/22] CVE-2020-10730: vlv: Another workaround for mixing ASQ
and VLV
This is essentially an alternative patch, but without the correct
behaviour. Instead this just avoids a segfault.
Included in case we have something simialr again in
another module.
BUG: https://bugzilla.samba.org/show_bug.cgi?id=14364
Signed-off-by: Andrew Bartlett <abartlet@samba.org>
Reviewed-by: Gary Lockyer <gary@catalyst.net.nz>
---
.../dsdb/samdb/ldb_modules/vlv_pagination.c | 19 +++++++++++++++----
1 file changed, 15 insertions(+), 4 deletions(-)
diff --git a/source4/dsdb/samdb/ldb_modules/vlv_pagination.c b/source4/dsdb/samdb/ldb_modules/vlv_pagination.c
index d58a62482c9..720b5e95638 100644
--- a/source4/dsdb/samdb/ldb_modules/vlv_pagination.c
+++ b/source4/dsdb/samdb/ldb_modules/vlv_pagination.c
@@ -442,10 +442,21 @@ static int vlv_results(struct vlv_context *ac)
ret = vlv_search_by_dn_guid(ac->module, ac, &result, guid,
ac->req->op.search.attrs);
- if (ret == LDAP_NO_SUCH_OBJECT) {
- /* The thing isn't there, which we quietly
- ignore and go on to send an extra one
- instead. */
+ if (ret == LDAP_NO_SUCH_OBJECT
+ || result->count != 1) {
+ /*
+ * The thing isn't there, which we quietly
+ * ignore and go on to send an extra one
+ * instead.
+ *
+ * result->count == 0 or > 1 can only
+ * happen if ASQ (which breaks all the
+ * rules) is somehow invoked (as this
+ * is a BASE search).
+ *
+ * (We skip the ASQ cookie for the
+ * GUID searches)
+ */
if (last_i < ac->store->num_entries - 1) {
last_i++;
}
--
2.17.1

View File

@ -1,92 +0,0 @@
From 01cce3d1fc69f04cdc237425b2f2ad1f2ac973d4 Mon Sep 17 00:00:00 2001
From: Andrew Bartlett <abartlet@samba.org>
Date: Wed, 6 May 2020 16:19:01 +1200
Subject: [PATCH 05/22] CVE-2020-10730: selftest: Add test to show that VLV and
paged_results are incompatible
As tested against Windows Server 1709
BUG: https://bugzilla.samba.org/show_bug.cgi?id=14364
Signed-off-by: Andrew Bartlett <abartlet@samba.org>
Reviewed-by: Gary Lockyer <gary@catalyst.net.nz>
---
source4/dsdb/tests/python/asq.py | 27 +++++++++++++++++++++++++++
source4/dsdb/tests/python/vlv.py | 23 +++++++++++++++++++++++
2 files changed, 50 insertions(+)
diff --git a/source4/dsdb/tests/python/asq.py b/source4/dsdb/tests/python/asq.py
index 1c93a45f131..33973d66c37 100644
--- a/source4/dsdb/tests/python/asq.py
+++ b/source4/dsdb/tests/python/asq.py
@@ -189,6 +189,33 @@ class ASQLDAPTest(samba.tests.TestCase):
self.assertIn(ldb.Dn(self.ldb, str(group)),
self.members)
+ def test_asq_vlv_paged(self):
+ """Testing ASQ behaviour with VLV and 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!
+
+ Thankfully combining both of these gives
+ unavailable-critical-extension against Windows 1709
+
+ """
+
+ sort_control = "server_sort:1:0:cn"
+
+ try:
+ msgs = self.ldb.search(base=self.top_dn,
+ scope=ldb.SCOPE_BASE,
+ attrs=["objectGUID", "cn", "member"],
+ controls=["asq:1:member",
+ sort_control,
+ "vlv:1:20:20:11:0",
+ "paged_results:1:1024"])
+ self.fail("should have failed with LDAP_UNAVAILABLE_CRITICAL_EXTENSION")
+ except ldb.LdbError as e:
+ (enum, estr) = e.args
+ self.assertEqual(enum, ldb.ERR_UNSUPPORTED_CRITICAL_EXTENSION)
+
if "://" not in url:
if os.path.isfile(url):
url = "tdb://%s" % url
diff --git a/source4/dsdb/tests/python/vlv.py b/source4/dsdb/tests/python/vlv.py
index 2efcaa5e7a3..f3c603e3a39 100644
--- a/source4/dsdb/tests/python/vlv.py
+++ b/source4/dsdb/tests/python/vlv.py
@@ -1644,6 +1644,29 @@ class PagedResultsTests(TestsWithUserOU):
page_size=len(self.users))
self.assertEqual(results, set_2[ps*2:])
+ def test_vlv_paged(self):
+ """Testing behaviour with VLV and paged_results set.
+
+ A strange combination, certainly
+
+ Thankfully combining both of these gives
+ unavailable-critical-extension against Windows 1709
+
+ """
+ sort_control = "server_sort:1:0:cn"
+
+ try:
+ msgs = self.ldb.search(base=self.base_dn,
+ scope=ldb.SCOPE_SUBTREE,
+ attrs=["objectGUID", "cn", "member"],
+ controls=["vlv:1:20:20:11:0",
+ sort_control,
+ "paged_results:1:1024"])
+ self.fail("should have failed with LDAP_UNAVAILABLE_CRITICAL_EXTENSION")
+ except ldb.LdbError as e:
+ (enum, estr) = e.args
+ self.assertEqual(enum, ldb.ERR_UNSUPPORTED_CRITICAL_EXTENSION)
+
if "://" not in host:
if os.path.isfile(host):
--
2.17.1

View File

@ -1,35 +0,0 @@
From c7608e43c933d9a33d94e32371080e64cc1d4fcb Mon Sep 17 00:00:00 2001
From: Andrew Bartlett <abartlet@samba.org>
Date: Wed, 6 May 2020 17:05:30 +1200
Subject: [PATCH 06/22] CVE-2020-10730: dsdb: Fix crash when vlv and
paged_results are combined
The GUID is not returned in the DN for some reason in this (to be banned)
combination.
BUG: https://bugzilla.samba.org/show_bug.cgi?id=14364
Signed-off-by: Andrew Bartlett <abartlet@samba.org>
Reviewed-by: Gary Lockyer <gary@catalyst.net.nz>
---
source4/dsdb/samdb/ldb_modules/paged_results.c | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/source4/dsdb/samdb/ldb_modules/paged_results.c b/source4/dsdb/samdb/ldb_modules/paged_results.c
index dc211dd18ce..f720a2e4337 100644
--- a/source4/dsdb/samdb/ldb_modules/paged_results.c
+++ b/source4/dsdb/samdb/ldb_modules/paged_results.c
@@ -416,6 +416,10 @@ static int paged_search_callback(struct ldb_request *req,
guid_blob = ldb_dn_get_extended_component(ares->message->dn,
"GUID");
+ if (guid_blob == NULL) {
+ return ldb_module_done(ac->req, NULL, NULL,
+ LDB_ERR_OPERATIONS_ERROR);
+ }
status = GUID_from_ndr_blob(guid_blob, &guid);
if (!NT_STATUS_IS_OK(status)) {
return ldb_module_done(ac->req, NULL, NULL,
--
2.17.1

View File

@ -1,48 +0,0 @@
From 0c8cd0a9fbd9d17c1d7219f977ca35f88f0a2ea3 Mon Sep 17 00:00:00 2001
From: Andrew Bartlett <abartlet@samba.org>
Date: Wed, 6 May 2020 16:18:19 +1200
Subject: [PATCH 07/22] CVE-2020-10730: dsdb: Ban the combination of
paged_results and VLV
This (two different paging controls) makes no sense and fails against
Windows Server 1709.
BUG: https://bugzilla.samba.org/show_bug.cgi?id=14364
Signed-off-by: Andrew Bartlett <abartlet@samba.org>
Reviewed-by: Gary Lockyer <gary@catalyst.net.nz>
---
source4/dsdb/samdb/ldb_modules/paged_results.c | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/source4/dsdb/samdb/ldb_modules/paged_results.c b/source4/dsdb/samdb/ldb_modules/paged_results.c
index f720a2e4337..aa49a6e4aa5 100644
--- a/source4/dsdb/samdb/ldb_modules/paged_results.c
+++ b/source4/dsdb/samdb/ldb_modules/paged_results.c
@@ -589,6 +589,7 @@ static int paged_search(struct ldb_module *module, struct ldb_request *req)
{
struct ldb_context *ldb;
struct ldb_control *control;
+ struct ldb_control *vlv_control;
struct private_data *private_data;
struct ldb_paged_control *paged_ctrl;
struct ldb_request *search_req;
@@ -612,6 +613,15 @@ static int paged_search(struct ldb_module *module, struct ldb_request *req)
private_data = talloc_get_type(ldb_module_get_private(module),
struct private_data);
+ vlv_control = ldb_request_get_control(req, LDB_CONTROL_VLV_REQ_OID);
+ if (vlv_control != NULL) {
+ /*
+ * VLV and paged_results are not allowed at the same
+ * time
+ */
+ return LDB_ERR_UNSUPPORTED_CRITICAL_EXTENSION;
+ }
+
ac = talloc_zero(req, struct paged_context);
if (ac == NULL) {
ldb_set_errstring(ldb, "Out of Memory");
--
2.17.1

View File

@ -1,138 +0,0 @@
From dcf713038ff10e35a74ee255f1634be81103e360 Mon Sep 17 00:00:00 2001
From: Gary Lockyer <gary@catalyst.net.nz>
Date: Mon, 18 May 2020 12:36:57 +1200
Subject: [PATCH 08/22] CVE-2020-10730: s4 dsdb paged_results: Prevent repeat
call of ldb_module_done
Check the return code from paged_results, if it is not LDB_SUCCESS
ldb_module_done has already been called, and SHOULD NOT be called again.
BUG: https://bugzilla.samba.org/show_bug.cgi?id=14364
Signed-off-by: Gary Lockyer <gary@catalyst.net.nz>
Reviewed-by: Andrew Bartlett <abartlet@samba.org>
---
.../dsdb/samdb/ldb_modules/paged_results.c | 43 +++++++++++++++----
1 file changed, 34 insertions(+), 9 deletions(-)
diff --git a/source4/dsdb/samdb/ldb_modules/paged_results.c b/source4/dsdb/samdb/ldb_modules/paged_results.c
index aa49a6e4aa5..735883e8802 100644
--- a/source4/dsdb/samdb/ldb_modules/paged_results.c
+++ b/source4/dsdb/samdb/ldb_modules/paged_results.c
@@ -237,14 +237,16 @@ static int paged_search_by_dn_guid(struct ldb_module *module,
return ret;
}
-static int paged_results(struct paged_context *ac)
+static int paged_results(struct paged_context *ac, struct ldb_reply *ares)
{
struct ldb_paged_control *paged;
unsigned int i, num_ctrls;
int ret;
if (ac->store == NULL) {
- return LDB_ERR_OPERATIONS_ERROR;
+ ret = LDB_ERR_OPERATIONS_ERROR;
+ return ldb_module_done(
+ ac->req, ac->controls, ares->response, ret);
}
while (ac->store->last_i < ac->store->num_entries && ac->size > 0) {
@@ -273,12 +275,17 @@ static int paged_results(struct paged_context *ac)
instead. */
continue;
} else if (ret != LDB_SUCCESS) {
- return ret;
+ return ldb_module_done(
+ ac->req, ac->controls, ares->response, ret);
}
ret = ldb_module_send_entry(ac->req, result->msgs[0],
NULL);
if (ret != LDB_SUCCESS) {
+ /*
+ * ldb_module_send_entry will have called
+ * ldb_module_done if an error occurred.
+ */
return ret;
}
}
@@ -289,6 +296,10 @@ static int paged_results(struct paged_context *ac)
*/
ret = send_referrals(ac->store, ac->req);
if (ret != LDB_SUCCESS) {
+ /*
+ * send_referrals will have called ldb_module_done
+ * if an error occurred.
+ */
return ret;
}
}
@@ -305,7 +316,9 @@ static int paged_results(struct paged_context *ac)
ac->controls = talloc_array(ac, struct ldb_control *, num_ctrls +1);
if (ac->controls == NULL) {
- return LDB_ERR_OPERATIONS_ERROR;
+ ret = LDB_ERR_OPERATIONS_ERROR;
+ return ldb_module_done(
+ ac->req, ac->controls, ares->response, ret);
}
ac->controls[num_ctrls] = NULL;
@@ -316,20 +329,26 @@ static int paged_results(struct paged_context *ac)
ac->controls[i] = talloc(ac->controls, struct ldb_control);
if (ac->controls[i] == NULL) {
- return LDB_ERR_OPERATIONS_ERROR;
+ ret = LDB_ERR_OPERATIONS_ERROR;
+ return ldb_module_done(
+ ac->req, ac->controls, ares->response, ret);
}
ac->controls[i]->oid = talloc_strdup(ac->controls[i],
LDB_CONTROL_PAGED_RESULTS_OID);
if (ac->controls[i]->oid == NULL) {
- return LDB_ERR_OPERATIONS_ERROR;
+ ret = LDB_ERR_OPERATIONS_ERROR;
+ return ldb_module_done(
+ ac->req, ac->controls, ares->response, ret);
}
ac->controls[i]->critical = 0;
paged = talloc(ac->controls[i], struct ldb_paged_control);
if (paged == NULL) {
- return LDB_ERR_OPERATIONS_ERROR;
+ ret = LDB_ERR_OPERATIONS_ERROR;
+ return ldb_module_done(
+ ac->req, ac->controls, ares->response, ret);
}
ac->controls[i]->data = paged;
@@ -456,7 +475,13 @@ static int paged_search_callback(struct ldb_request *req,
store->result_array_size = store->num_entries;
ac->store->controls = talloc_move(ac->store, &ares->controls);
- ret = paged_results(ac);
+ ret = paged_results(ac, ares);
+ if (ret != LDB_SUCCESS) {
+ /* paged_results will have called ldb_module_done
+ * if an error occurred
+ */
+ return ret;
+ }
return ldb_module_done(ac->req, ac->controls,
ares->response, ret);
}
@@ -768,7 +793,7 @@ static int paged_search(struct ldb_module *module, struct ldb_request *req)
LDB_SUCCESS);
}
- ret = paged_results(ac);
+ ret = paged_results(ac, NULL);
if (ret != LDB_SUCCESS) {
return ldb_module_done(req, NULL, NULL, ret);
}
--
2.17.1

View File

@ -1,171 +0,0 @@
From ae6e9445ac8bf8f6870a8caa24406153cd2ee2bf Mon Sep 17 00:00:00 2001
From: Gary Lockyer <gary@catalyst.net.nz>
Date: Mon, 18 May 2020 12:37:39 +1200
Subject: [PATCH 09/22] CVE-2020-10730: s4 dsdb vlv_pagination: Prevent repeat
call of ldb_module_done
Check the return code from vlv_results, if it is not LDB_SUCCESS
ldb_module_done has already been called, and SHOULD NOT be called again.
BUG: https://bugzilla.samba.org/show_bug.cgi?id=14364
Signed-off-by: Gary Lockyer <gary@catalyst.net.nz>
Reviewed-by: Andrew Bartlett <abartlet@samba.org>
---
.../dsdb/samdb/ldb_modules/vlv_pagination.c | 61 +++++++++++++++----
1 file changed, 49 insertions(+), 12 deletions(-)
diff --git a/source4/dsdb/samdb/ldb_modules/vlv_pagination.c b/source4/dsdb/samdb/ldb_modules/vlv_pagination.c
index 720b5e95638..b103bda5f52 100644
--- a/source4/dsdb/samdb/ldb_modules/vlv_pagination.c
+++ b/source4/dsdb/samdb/ldb_modules/vlv_pagination.c
@@ -387,7 +387,7 @@ static int vlv_calc_real_offset(int offset, int denominator, int n_entries)
has been prepared earlier and saved -- or by vlv_search_callback() when a
search has just been completed. */
-static int vlv_results(struct vlv_context *ac)
+static int vlv_results(struct vlv_context *ac, struct ldb_reply *ares)
{
struct ldb_vlv_resp_control *vlv;
unsigned int num_ctrls;
@@ -397,7 +397,9 @@ static int vlv_results(struct vlv_context *ac)
int target = 0;
if (ac->store == NULL) {
- return LDB_ERR_OPERATIONS_ERROR;
+ ret = LDB_ERR_OPERATIONS_ERROR;
+ return ldb_module_done(
+ ac->req, ac->controls, ares->response, ret);
}
if (ac->store->first_ref) {
@@ -406,6 +408,10 @@ static int vlv_results(struct vlv_context *ac)
*/
ret = send_referrals(ac->store, ac->req);
if (ret != LDB_SUCCESS) {
+ /*
+ * send_referrals will have called ldb_module_done
+ * if there was an error.
+ */
return ret;
}
}
@@ -419,14 +425,23 @@ static int vlv_results(struct vlv_context *ac)
vlv_details,
sort_details, &ret);
if (ret != LDB_SUCCESS) {
- return ret;
+ return ldb_module_done(
+ ac->req,
+ ac->controls,
+ ares->response,
+ ret);
}
} else {
target = vlv_calc_real_offset(vlv_details->match.byOffset.offset,
vlv_details->match.byOffset.contentCount,
ac->store->num_entries);
if (target == -1) {
- return LDB_ERR_OPERATIONS_ERROR;
+ ret = LDB_ERR_OPERATIONS_ERROR;
+ return ldb_module_done(
+ ac->req,
+ ac->controls,
+ ares->response,
+ ret);
}
}
@@ -462,12 +477,20 @@ static int vlv_results(struct vlv_context *ac)
}
continue;
} else if (ret != LDB_SUCCESS) {
- return ret;
+ return ldb_module_done(
+ ac->req,
+ ac->controls,
+ ares->response,
+ ret);
}
ret = ldb_module_send_entry(ac->req, result->msgs[0],
NULL);
if (ret != LDB_SUCCESS) {
+ /*
+ * ldb_module_send_entry will have called
+ * ldb_module_done if there was an error
+ */
return ret;
}
}
@@ -488,7 +511,9 @@ static int vlv_results(struct vlv_context *ac)
ac->controls = talloc_array(ac, struct ldb_control *, num_ctrls + 1);
if (ac->controls == NULL) {
- return LDB_ERR_OPERATIONS_ERROR;
+ ret = LDB_ERR_OPERATIONS_ERROR;
+ return ldb_module_done(
+ ac->req, ac->controls, ares->response, ret);
}
ac->controls[num_ctrls] = NULL;
@@ -498,20 +523,26 @@ static int vlv_results(struct vlv_context *ac)
ac->controls[i] = talloc(ac->controls, struct ldb_control);
if (ac->controls[i] == NULL) {
- return LDB_ERR_OPERATIONS_ERROR;
+ ret = LDB_ERR_OPERATIONS_ERROR;
+ return ldb_module_done(
+ ac->req, ac->controls, ares->response, ret);
}
ac->controls[i]->oid = talloc_strdup(ac->controls[i],
LDB_CONTROL_VLV_RESP_OID);
if (ac->controls[i]->oid == NULL) {
- return LDB_ERR_OPERATIONS_ERROR;
+ ret = LDB_ERR_OPERATIONS_ERROR;
+ return ldb_module_done(
+ ac->req, ac->controls, ares->response, ret);
}
ac->controls[i]->critical = 0;
vlv = talloc(ac->controls[i], struct ldb_vlv_resp_control);
if (vlv == NULL) {
- return LDB_ERR_OPERATIONS_ERROR;
+ ret = LDB_ERR_OPERATIONS_ERROR;
+ return ldb_module_done(
+ ac->req, ac->controls, ares->response, ret);
}
ac->controls[i]->data = vlv;
@@ -600,7 +631,13 @@ static int vlv_search_callback(struct ldb_request *req, struct ldb_reply *ares)
store->result_array_size = store->num_entries;
ac->store->controls = talloc_move(ac->store, &ares->controls);
- ret = vlv_results(ac);
+ ret = vlv_results(ac, ares);
+ if (ret != LDB_SUCCESS) {
+ /* vlv_results will have called ldb_module_done
+ * if there was an error.
+ */
+ return ret;
+ }
return ldb_module_done(ac->req, ac->controls,
ares->response, ret);
}
@@ -845,9 +882,9 @@ static int vlv_search(struct ldb_module *module, struct ldb_request *req)
return ret;
}
- ret = vlv_results(ac);
+ ret = vlv_results(ac, NULL);
if (ret != LDB_SUCCESS) {
- return ldb_module_done(req, NULL, NULL, ret);
+ return ret;
}
return ldb_module_done(req, ac->controls, NULL,
LDB_SUCCESS);
--
2.17.1

View File

@ -1,272 +0,0 @@
From ddd3ed7ce2e2776839c463010bd975f01dd0977d Mon Sep 17 00:00:00 2001
From: Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
Date: Thu, 11 Jun 2020 17:38:51 +1200
Subject: [PATCH 12/22] CVE-2020-10745: pytests: hand-rolled invalid dns/nbt
packet tests
The client libraries don't allow us to make packets that are broken in
certain ways, so we need to construct them as byte strings.
These tests all fail at present, proving the server is rendered
unresponsive, which is the crux of CVE-2020-10745.
BUG: https://bugzilla.samba.org/show_bug.cgi?id=14378
Signed-off-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
---
python/samba/tests/dns_packet.py | 211 +++++++++++++++++++++++++++++++
selftest/knownfail.d/dns_packet | 2 +
source4/selftest/tests.py | 10 ++
3 files changed, 223 insertions(+)
create mode 100644 python/samba/tests/dns_packet.py
create mode 100644 selftest/knownfail.d/dns_packet
diff --git a/python/samba/tests/dns_packet.py b/python/samba/tests/dns_packet.py
new file mode 100644
index 00000000000..c4f843eb613
--- /dev/null
+++ b/python/samba/tests/dns_packet.py
@@ -0,0 +1,211 @@
+# Tests of malformed DNS packets
+# Copyright (C) Catalyst.NET ltd
+#
+# written by Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
+#
+# 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 <http://www.gnu.org/licenses/>.
+
+"""Sanity tests for DNS and NBT server parsing.
+
+We don't use a proper client library so we can make improper packets.
+"""
+
+import os
+import struct
+import socket
+import select
+from samba.dcerpc import dns, nbt
+
+from samba.tests import TestCase
+
+
+def _msg_id():
+ while True:
+ for i in range(1, 0xffff):
+ yield i
+
+
+SERVER = os.environ['SERVER_IP']
+SERVER_NAME = f"{os.environ['SERVER']}.{os.environ['REALM']}"
+TIMEOUT = 0.5
+
+
+def encode_netbios_bytes(chars):
+ """Even RFC 1002 uses distancing quotes when calling this "compression"."""
+ out = []
+ chars = (chars + b' ')[:16]
+ for c in chars:
+ out.append((c >> 4) + 65)
+ out.append((c & 15) + 65)
+ return bytes(out)
+
+
+class TestDnsPacketBase(TestCase):
+ msg_id = _msg_id()
+
+ def tearDown(self):
+ # we need to ensure the DNS server is responsive before
+ # continuing.
+ for i in range(40):
+ ok = self._known_good_query()
+ if ok:
+ return
+ print(f"the server is STILL unresponsive after {40 * TIMEOUT} seconds")
+
+ def decode_reply(self, data):
+ header = data[:12]
+ id, flags, n_q, n_a, n_rec, n_exta = struct.unpack('!6H',
+ header)
+ return {
+ 'rcode': flags & 0xf
+ }
+
+ def construct_query(self, names):
+ """Create a query packet containing one query record.
+
+ *names* is either a single string name in the usual dotted
+ form, or a list of names. In the latter case, each name can
+ be a dotted string or a list of byte components, which allows
+ dots in components. Where I say list, I mean non-string
+ iterable.
+
+ Examples:
+
+ # these 3 are all the same
+ "example.com"
+ ["example.com"]
+ [[b"example", b"com"]]
+
+ # this is three names in the same request
+ ["example.com",
+ [b"example", b"com", b"..!"],
+ (b"first component", b" 2nd component")]
+ """
+ header = struct.pack('!6H',
+ next(self.msg_id),
+ 0x0100, # query, with recursion
+ len(names), # number of queries
+ 0x0000, # no answers
+ 0x0000, # no records
+ 0x0000, # no extra records
+ )
+ tail = struct.pack('!BHH',
+ 0x00, # root node
+ self.qtype,
+ 0x0001, # class IN-ternet
+ )
+ encoded_bits = []
+ for name in names:
+ if isinstance(name, str):
+ bits = name.encode('utf8').split(b'.')
+ else:
+ bits = name
+
+ for b in bits:
+ encoded_bits.append(b'%c%s' % (len(b), b))
+ encoded_bits.append(tail)
+
+ return header + b''.join(encoded_bits)
+
+ def _test_query(self, names=(), expected_rcode=None):
+
+ if isinstance(names, str):
+ names = [names]
+
+ packet = self.construct_query(names)
+ s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+ s.sendto(packet, self.server)
+ r, _, _ = select.select([s], [], [], TIMEOUT)
+ s.close()
+ # It is reasonable to not reply to these packets (Windows
+ # doesn't), but it is not reasonable to render the server
+ # unresponsive.
+ if r != [s]:
+ ok = self._known_good_query()
+ self.assertTrue(ok, f"the server is unresponsive")
+
+ def _known_good_query(self):
+ if self.server[1] == 53:
+ name = SERVER_NAME
+ expected_rcode = dns.DNS_RCODE_OK
+ else:
+ name = [encode_netbios_bytes(b'nxdomain'), b'nxdomain']
+ expected_rcode = nbt.NBT_RCODE_NAM
+
+ packet = self.construct_query([name])
+ s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+ s.sendto(packet, self.server)
+ r, _, _ = select.select([s], [], [], TIMEOUT)
+ if r != [s]:
+ s.close()
+ return False
+
+ data, addr = s.recvfrom(4096)
+ s.close()
+ rcode = self.decode_reply(data)['rcode']
+ return expected_rcode == rcode
+
+
+class TestDnsPackets(TestDnsPacketBase):
+ server = (SERVER, 53)
+ qtype = 1 # dns type A
+
+ def _test_many_repeated_components(self, label, n, expected_rcode=None):
+ name = [label] * n
+ self._test_query([name],
+ expected_rcode=expected_rcode)
+
+ def test_127_very_dotty_components(self):
+ label = b'.' * 63
+ self._test_many_repeated_components(label, 127)
+
+ def test_127_half_dotty_components(self):
+ label = b'x.' * 31 + b'x'
+ self._test_many_repeated_components(label, 127)
+
+
+class TestNbtPackets(TestDnsPacketBase):
+ server = (SERVER, 137)
+ qtype = 0x20 # NBT_QTYPE_NETBIOS
+
+ def _test_nbt_encode_query(self, names, *args, **kwargs):
+ if isinstance(names, str):
+ names = [names]
+
+ nbt_names = []
+ for name in names:
+ if isinstance(name, str):
+ bits = name.encode('utf8').split(b'.')
+ else:
+ bits = name
+
+ encoded = [encode_netbios_bytes(bits[0])]
+ encoded.extend(bits[1:])
+ nbt_names.append(encoded)
+
+ self._test_query(nbt_names, *args, **kwargs)
+
+ def _test_many_repeated_components(self, label, n, expected_rcode=None):
+ name = [label] * n
+ name[0] = encode_netbios_bytes(label)
+ self._test_query([name],
+ expected_rcode=expected_rcode)
+
+ def test_127_very_dotty_components(self):
+ label = b'.' * 63
+ self._test_many_repeated_components(label, 127)
+
+ def test_127_half_dotty_components(self):
+ label = b'x.' * 31 + b'x'
+ self._test_many_repeated_components(label, 127)
diff --git a/selftest/knownfail.d/dns_packet b/selftest/knownfail.d/dns_packet
new file mode 100644
index 00000000000..6e2e5a699de
--- /dev/null
+++ b/selftest/knownfail.d/dns_packet
@@ -0,0 +1,2 @@
+samba.tests.dns_packet.samba.tests.dns_packet.TestDnsPackets.test_127_very_dotty_components
+samba.tests.dns_packet.samba.tests.dns_packet.TestNbtPackets.test_127_very_dotty_components
diff --git a/source4/selftest/tests.py b/source4/selftest/tests.py
index f7645365384..6281b7e8f12 100755
--- a/source4/selftest/tests.py
+++ b/source4/selftest/tests.py
@@ -421,6 +421,16 @@ plantestsuite_loadlist("samba.tests.dns_wildcard", "ad_dc", [python, os.path.joi
plantestsuite_loadlist("samba.tests.dns_invalid", "ad_dc", [python, os.path.join(srcdir(), "python/samba/tests/dns_invalid.py"), '$SERVER_IP', '--machine-pass', '-U"$USERNAME%$PASSWORD"', '--workgroup=$DOMAIN', '$LOADLIST', '$LISTOPT'])
+plantestsuite_loadlist("samba.tests.dns_packet",
+ "ad_dc",
+ [python,
+ '-msamba.subunit.run',
+ '$LOADLIST',
+ "$LISTOPT"
+ "samba.tests.dns_packet"
+ ])
+
+
for t in smbtorture4_testsuites("dns_internal."):
plansmbtorture4testsuite(t, "ad_dc_default:local", '//$SERVER/whavever')
--
2.17.1

View File

@ -1,316 +0,0 @@
From ddeabf87957ce73e12030977948418c93436a05c Mon Sep 17 00:00:00 2001
From: Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
Date: Fri, 12 Jun 2020 14:26:38 +1200
Subject: [PATCH 13/22] CVE-2020-10745: librpc/tests: cmocka tests of dns and
ndr strings
These time the push and pull function in isolation.
Timing should be under 0.0001 seconds on even quite old hardware; we
assert it must be under 0.2 seconds.
BUG: https://bugzilla.samba.org/show_bug.cgi?id=14378
(backported from master commit)
[abartlet@samba.org: backported due to differences in pre-existing
tests - eg test_ndr - mentioned in wscript_build and tests.py]
Signed-off-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
---
librpc/tests/test_ndr_dns_nbt.c | 236 +++++++++++++++++++++++++++++++
librpc/wscript_build | 13 ++
selftest/knownfail.d/ndr_dns_nbt | 4 +
source4/selftest/tests.py | 2 +
4 files changed, 255 insertions(+)
create mode 100644 librpc/tests/test_ndr_dns_nbt.c
create mode 100644 selftest/knownfail.d/ndr_dns_nbt
diff --git a/librpc/tests/test_ndr_dns_nbt.c b/librpc/tests/test_ndr_dns_nbt.c
new file mode 100644
index 00000000000..1e2ef45c10d
--- /dev/null
+++ b/librpc/tests/test_ndr_dns_nbt.c
@@ -0,0 +1,236 @@
+/*
+ * Tests for librpc ndr functions
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "replace.h"
+#include <setjmp.h>
+#include <cmocka.h>
+
+#include "includes.h"
+#include "librpc/ndr/libndr.h"
+#include "librpc/gen_ndr/ndr_dns.h"
+#include "librpc/gen_ndr/ndr_nbt.h"
+#include "lib/util/time.h"
+
+#define NBT_NAME "EOGFGLGPCACACACACACACACACACACACA" /* "neko" */
+
+
+static DATA_BLOB generate_obnoxious_dns_name(TALLOC_CTX *mem_ctx,
+ size_t n_labels,
+ size_t dot_every,
+ bool is_nbt)
+{
+ size_t i, j;
+ char *s;
+ DATA_BLOB name = data_blob_talloc(mem_ctx, NULL, 64 * n_labels + 1);
+ assert_non_null(name.data);
+
+ s = (char*)name.data;
+ if (is_nbt) {
+ size_t len = strlen(NBT_NAME);
+ *s = len;
+ s++;
+ memcpy(s, NBT_NAME, len);
+ s += len;
+ n_labels--;
+ }
+
+ for (i = 0; i < n_labels; i++) {
+ *s = 63;
+ s++;
+ for (j = 0; j < 63; j++) {
+ if (j % dot_every == (dot_every - 1)) {
+ *s = '.';
+ } else {
+ *s = 'x';
+ }
+ s++;
+ }
+ }
+ *s = 0;
+ s++;
+ name.length = s - (char*)name.data;
+ return name;
+}
+
+
+static char *_test_ndr_pull_dns_string_list(TALLOC_CTX *mem_ctx,
+ size_t n_labels,
+ size_t dot_every,
+ bool is_nbt)
+{
+ enum ndr_err_code ndr_err;
+ DATA_BLOB blob = generate_obnoxious_dns_name(mem_ctx,
+ n_labels,
+ dot_every,
+ is_nbt);
+
+ char *name;
+ ndr_pull_flags_fn_t fn;
+
+ if (is_nbt) {
+ fn = (ndr_pull_flags_fn_t)ndr_pull_nbt_string;
+ } else {
+ fn = (ndr_pull_flags_fn_t)ndr_pull_dns_string;
+ }
+
+ ndr_err = ndr_pull_struct_blob(&blob,
+ mem_ctx,
+ &name,
+ fn);
+ /* Success here is not expected, but we let it go to measure timing. */
+ if (ndr_err == NDR_ERR_SUCCESS) {
+ printf("pull succeed\n");
+ } else {
+ assert_int_equal(ndr_err, NDR_ERR_STRING);
+ }
+
+ TALLOC_FREE(blob.data);
+ return name;
+}
+
+
+static void _test_ndr_push_dns_string_list(TALLOC_CTX *mem_ctx,
+ char *name,
+ bool is_nbt)
+{
+ DATA_BLOB blob;
+ enum ndr_err_code ndr_err;
+ ndr_push_flags_fn_t fn;
+
+ if (is_nbt) {
+ fn = (ndr_push_flags_fn_t)ndr_push_nbt_string;
+ } else {
+ fn = (ndr_push_flags_fn_t)ndr_push_dns_string;
+ }
+
+ ndr_err = ndr_push_struct_blob(&blob,
+ mem_ctx,
+ name,
+ fn);
+
+ /* Success here is not expected, but we let it go to measure timing. */
+ if (ndr_err == NDR_ERR_SUCCESS) {
+ printf("push succeed\n");
+ } else {
+ assert_int_equal(ndr_err, NDR_ERR_STRING);
+ }
+}
+
+
+static uint64_t elapsed_time(struct timespec start, const char *print)
+{
+ struct timespec end;
+ unsigned long long microsecs;
+ clock_gettime_mono(&end);
+ end.tv_sec -= start.tv_sec;
+ if (end.tv_nsec < start.tv_nsec) {
+ /* we need to borrow */
+ end.tv_nsec += 1000 * 1000 * 1000;
+ end.tv_sec -= 1;
+ }
+ end.tv_nsec -= start.tv_nsec;
+ microsecs = end.tv_sec * 1000000;
+ microsecs += end.tv_nsec / 1000;
+
+ if (print != NULL) {
+ printf(" %s: %llu microseconds\n", print, microsecs);
+ }
+ return microsecs;
+}
+
+
+static void test_ndr_dns_string_half_dots(void **state)
+{
+ TALLOC_CTX *mem_ctx = talloc_new(NULL);
+ char *name;
+ struct timespec start;
+ uint64_t elapsed;
+
+ clock_gettime_mono(&start);
+ name =_test_ndr_pull_dns_string_list(mem_ctx, 127, 2, false);
+ elapsed_time(start, "pull");
+ _test_ndr_push_dns_string_list(mem_ctx, name, false);
+ elapsed = elapsed_time(start, "total");
+ assert_in_range(elapsed, 0, 200000);
+ talloc_free(mem_ctx);
+}
+
+static void test_ndr_nbt_string_half_dots(void **state)
+{
+ TALLOC_CTX *mem_ctx = talloc_new(NULL);
+ char *name;
+ struct timespec start;
+ uint64_t elapsed;
+
+ clock_gettime_mono(&start);
+ name =_test_ndr_pull_dns_string_list(mem_ctx, 127, 2, true);
+ elapsed_time(start, "pull");
+ _test_ndr_push_dns_string_list(mem_ctx, name, true);
+ elapsed = elapsed_time(start, "total");
+ assert_in_range(elapsed, 0, 200000);
+ talloc_free(mem_ctx);
+}
+
+static void test_ndr_dns_string_all_dots(void **state)
+{
+ TALLOC_CTX *mem_ctx = talloc_new(NULL);
+ char *name;
+ struct timespec start;
+ uint64_t elapsed;
+
+ clock_gettime_mono(&start);
+ name =_test_ndr_pull_dns_string_list(mem_ctx, 127, 1, false);
+ elapsed_time(start, "pull");
+ _test_ndr_push_dns_string_list(mem_ctx, name, false);
+ elapsed = elapsed_time(start, "total");
+ assert_in_range(elapsed, 0, 200000);
+ talloc_free(mem_ctx);
+}
+
+static void test_ndr_nbt_string_all_dots(void **state)
+{
+ TALLOC_CTX *mem_ctx = talloc_new(NULL);
+ char *name;
+ struct timespec start;
+ uint64_t elapsed;
+
+ clock_gettime_mono(&start);
+ name =_test_ndr_pull_dns_string_list(mem_ctx, 127, 1, true);
+ elapsed_time(start, "pull");
+ _test_ndr_push_dns_string_list(mem_ctx, name, true);
+ elapsed = elapsed_time(start, "total");
+ assert_in_range(elapsed, 0, 200000);
+ talloc_free(mem_ctx);
+}
+
+
+
+int main(int argc, const char **argv)
+{
+ const struct CMUnitTest tests[] = {
+ cmocka_unit_test(test_ndr_nbt_string_half_dots),
+ cmocka_unit_test(test_ndr_dns_string_half_dots),
+ cmocka_unit_test(test_ndr_nbt_string_all_dots),
+ cmocka_unit_test(test_ndr_dns_string_all_dots),
+ };
+
+ cmocka_set_message_output(CM_OUTPUT_SUBUNIT);
+ return cmocka_run_group_tests(tests, NULL, NULL);
+}
diff --git a/librpc/wscript_build b/librpc/wscript_build
index 70fe8c2f7fe..e3be298c725 100644
--- a/librpc/wscript_build
+++ b/librpc/wscript_build
@@ -656,3 +656,16 @@ bld.SAMBA_SUBSYSTEM('NDR_FSRVP_STATE',
source='gen_ndr/ndr_fsrvp_state.c',
public_deps='ndr'
)
+#
+# Cmocka tests
+#
+
+bld.SAMBA_BINARY('test_ndr_dns_nbt',
+ source='tests/test_ndr_dns_nbt.c',
+ deps='''
+ cmocka
+ ndr
+ ndr_nbt
+ NDR_DNS
+ ''',
+ install=False)
diff --git a/selftest/knownfail.d/ndr_dns_nbt b/selftest/knownfail.d/ndr_dns_nbt
new file mode 100644
index 00000000000..f30217c4033
--- /dev/null
+++ b/selftest/knownfail.d/ndr_dns_nbt
@@ -0,0 +1,4 @@
+librpc.ndr.ndr_dns_nbt.test_ndr_dns_string_all_dots
+librpc.ndr.ndr_dns_nbt.test_ndr_dns_string_half_dots
+librpc.ndr.ndr_dns_nbt.test_ndr_nbt_string_all_dots
+librpc.ndr.ndr_dns_nbt.test_ndr_nbt_string_half_dots
diff --git a/source4/selftest/tests.py b/source4/selftest/tests.py
index 6281b7e8f12..e8dbed71a18 100755
--- a/source4/selftest/tests.py
+++ b/source4/selftest/tests.py
@@ -1337,6 +1337,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("librpc.ndr.ndr_dns_nbt", "none",
+ [os.path.join(bindir(), "test_ndr_dns_nbt")])
# process restart and limit tests, these break the environment so need to run
# in their own specific environment
--
2.17.1

View File

@ -1,241 +0,0 @@
From 37cacb8f41b9b2ea19a9c1bbfade4ea250dced46 Mon Sep 17 00:00:00 2001
From: Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
Date: Sat, 25 Apr 2020 11:02:08 +1200
Subject: [PATCH 14/22] CVE-2020-10745: ndr_dns: move ndr_push_dns_string core
into sharable function
This is because ndr_nbt.c does almost exactly the same thing with
almost exactly the same code, and they both do it wrong. Soon they
will both be using the better version that this will become. Though in
this patch we just move the code, not fix it.
BUG: https://bugzilla.samba.org/show_bug.cgi?id=14378
Signed-off-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
---
librpc/ndr/ndr_dns.c | 79 +++-------------------------------
librpc/ndr/ndr_dns_utils.c | 88 ++++++++++++++++++++++++++++++++++++++
librpc/ndr/ndr_dns_utils.h | 5 +++
librpc/wscript_build | 2 +-
4 files changed, 99 insertions(+), 75 deletions(-)
create mode 100644 librpc/ndr/ndr_dns_utils.c
create mode 100644 librpc/ndr/ndr_dns_utils.h
diff --git a/librpc/ndr/ndr_dns.c b/librpc/ndr/ndr_dns.c
index d37c8cc2ece..68a3c9de782 100644
--- a/librpc/ndr/ndr_dns.c
+++ b/librpc/ndr/ndr_dns.c
@@ -33,6 +33,7 @@
#include "librpc/gen_ndr/ndr_dnsp.h"
#include "system/locale.h"
#include "lib/util/util_net.h"
+#include "ndr_dns_utils.h"
/* don't allow an unlimited number of name components */
#define MAX_COMPONENTS 128
@@ -159,80 +160,10 @@ _PUBLIC_ enum ndr_err_code ndr_push_dns_string(struct ndr_push *ndr,
int ndr_flags,
const char *s)
{
- if (!(ndr_flags & NDR_SCALARS)) {
- return NDR_ERR_SUCCESS;
- }
-
- while (s && *s) {
- enum ndr_err_code ndr_err;
- char *compname;
- size_t complen;
- uint32_t offset;
-
- if (!(ndr->flags & LIBNDR_FLAG_NO_COMPRESSION)) {
- /* see if we have pushed the remaining string already,
- * if so we use a label pointer to this string
- */
- ndr_err = ndr_token_retrieve_cmp_fn(&ndr->dns_string_list, s,
- &offset,
- (comparison_fn_t)strcmp,
- false);
- if (NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
- uint8_t b[2];
-
- if (offset > 0x3FFF) {
- return ndr_push_error(ndr, NDR_ERR_STRING,
- "offset for dns string " \
- "label pointer " \
- "%u[%08X] > 0x00003FFF",
- offset, offset);
- }
-
- b[0] = 0xC0 | (offset>>8);
- b[1] = (offset & 0xFF);
-
- return ndr_push_bytes(ndr, b, 2);
- }
- }
-
- complen = strcspn(s, ".");
-
- /* we need to make sure the length fits into 6 bytes */
- if (complen > 0x3F) {
- return ndr_push_error(ndr, NDR_ERR_STRING,
- "component length %u[%08X] > " \
- "0x0000003F",
- (unsigned)complen,
- (unsigned)complen);
- }
-
- compname = talloc_asprintf(ndr, "%c%*.*s",
- (unsigned char)complen,
- (unsigned char)complen,
- (unsigned char)complen, s);
- NDR_ERR_HAVE_NO_MEMORY(compname);
-
- /* remember the current component + the rest of the string
- * so it can be reused later
- */
- if (!(ndr->flags & LIBNDR_FLAG_NO_COMPRESSION)) {
- NDR_CHECK(ndr_token_store(ndr, &ndr->dns_string_list, s,
- ndr->offset));
- }
-
- /* push just this component into the blob */
- NDR_CHECK(ndr_push_bytes(ndr, (const uint8_t *)compname,
- complen+1));
- talloc_free(compname);
-
- s += complen;
- if (*s == '.') s++;
- }
-
- /* if we reach the end of the string and have pushed the last component
- * without using a label pointer, we need to terminate the string
- */
- return ndr_push_bytes(ndr, (const uint8_t *)"", 1);
+ return ndr_push_dns_string_list(ndr,
+ &ndr->dns_string_list,
+ ndr_flags,
+ s);
}
_PUBLIC_ enum ndr_err_code ndr_pull_dns_txt_record(struct ndr_pull *ndr, int ndr_flags, struct dns_txt_record *r)
diff --git a/librpc/ndr/ndr_dns_utils.c b/librpc/ndr/ndr_dns_utils.c
new file mode 100644
index 00000000000..2d9b5f1bc1e
--- /dev/null
+++ b/librpc/ndr/ndr_dns_utils.c
@@ -0,0 +1,88 @@
+#include "includes.h"
+#include "../librpc/ndr/libndr.h"
+#include "ndr_dns_utils.h"
+
+
+/**
+ push a dns/nbt string list to the wire
+*/
+enum ndr_err_code ndr_push_dns_string_list(struct ndr_push *ndr,
+ struct ndr_token_list *string_list,
+ int ndr_flags,
+ const char *s)
+{
+ if (!(ndr_flags & NDR_SCALARS)) {
+ return NDR_ERR_SUCCESS;
+ }
+
+ while (s && *s) {
+ enum ndr_err_code ndr_err;
+ char *compname;
+ size_t complen;
+ uint32_t offset;
+
+ if (!(ndr->flags & LIBNDR_FLAG_NO_COMPRESSION)) {
+ /* see if we have pushed the remaining string already,
+ * if so we use a label pointer to this string
+ */
+ ndr_err = ndr_token_retrieve_cmp_fn(string_list, s,
+ &offset,
+ (comparison_fn_t)strcmp,
+ false);
+ if (NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ uint8_t b[2];
+
+ if (offset > 0x3FFF) {
+ return ndr_push_error(ndr, NDR_ERR_STRING,
+ "offset for dns string " \
+ "label pointer " \
+ "%u[%08X] > 0x00003FFF",
+ offset, offset);
+ }
+
+ b[0] = 0xC0 | (offset>>8);
+ b[1] = (offset & 0xFF);
+
+ return ndr_push_bytes(ndr, b, 2);
+ }
+ }
+
+ complen = strcspn(s, ".");
+
+ /* we need to make sure the length fits into 6 bytes */
+ if (complen > 0x3F) {
+ return ndr_push_error(ndr, NDR_ERR_STRING,
+ "component length %u[%08X] > " \
+ "0x0000003F",
+ (unsigned)complen,
+ (unsigned)complen);
+ }
+
+ compname = talloc_asprintf(ndr, "%c%*.*s",
+ (unsigned char)complen,
+ (unsigned char)complen,
+ (unsigned char)complen, s);
+ NDR_ERR_HAVE_NO_MEMORY(compname);
+
+ /* remember the current component + the rest of the string
+ * so it can be reused later
+ */
+ if (!(ndr->flags & LIBNDR_FLAG_NO_COMPRESSION)) {
+ NDR_CHECK(ndr_token_store(ndr, string_list, s,
+ ndr->offset));
+ }
+
+ /* push just this component into the blob */
+ NDR_CHECK(ndr_push_bytes(ndr, (const uint8_t *)compname,
+ complen+1));
+ talloc_free(compname);
+
+ s += complen;
+ if (*s == '.') s++;
+ }
+
+ /* if we reach the end of the string and have pushed the last component
+ * without using a label pointer, we need to terminate the string
+ */
+ return ndr_push_bytes(ndr, (const uint8_t *)"", 1);
+}
diff --git a/librpc/ndr/ndr_dns_utils.h b/librpc/ndr/ndr_dns_utils.h
new file mode 100644
index 00000000000..823e3201112
--- /dev/null
+++ b/librpc/ndr/ndr_dns_utils.h
@@ -0,0 +1,5 @@
+
+enum ndr_err_code ndr_push_dns_string_list(struct ndr_push *ndr,
+ struct ndr_token_list *string_list,
+ int ndr_flags,
+ const char *s);
diff --git a/librpc/wscript_build b/librpc/wscript_build
index e3be298c725..c165500644b 100644
--- a/librpc/wscript_build
+++ b/librpc/wscript_build
@@ -31,7 +31,7 @@ bld.SAMBA_SUBSYSTEM('NDR_DNSSERVER',
)
bld.SAMBA_SUBSYSTEM('NDR_DNS',
- source='gen_ndr/ndr_dns.c ndr/ndr_dns.c',
+ source='gen_ndr/ndr_dns.c ndr/ndr_dns.c ndr/ndr_dns_utils.c',
public_deps='ndr NDR_DNSP'
)
--
2.17.1

View File

@ -1,28 +0,0 @@
From b687813ac362ff71085d192a4b7821235345feea Mon Sep 17 00:00:00 2001
From: Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
Date: Sat, 25 Apr 2020 11:03:30 +1200
Subject: [PATCH 15/22] CVE-2020-10745: ndr/dns_utils: correct a comment
BUG: https://bugzilla.samba.org/show_bug.cgi?id=14378
Signed-off-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
---
librpc/ndr/ndr_dns_utils.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/librpc/ndr/ndr_dns_utils.c b/librpc/ndr/ndr_dns_utils.c
index 2d9b5f1bc1e..2ce300863bc 100644
--- a/librpc/ndr/ndr_dns_utils.c
+++ b/librpc/ndr/ndr_dns_utils.c
@@ -49,7 +49,7 @@ enum ndr_err_code ndr_push_dns_string_list(struct ndr_push *ndr,
complen = strcspn(s, ".");
- /* we need to make sure the length fits into 6 bytes */
+ /* the length must fit into 6 bits (i.e. <= 63) */
if (complen > 0x3F) {
return ndr_push_error(ndr, NDR_ERR_STRING,
"component length %u[%08X] > " \
--
2.17.1

View File

@ -1,54 +0,0 @@
From 507503f80e8913450364dcd8ab080f3211b6f855 Mon Sep 17 00:00:00 2001
From: Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
Date: Sat, 25 Apr 2020 11:10:18 +1200
Subject: [PATCH 16/22] CVE-2020-10745: ndr_dns: do not allow consecutive dots
The empty subdomain component is reserved for the root domain, which we
should only (and always) see at the end of the list. That is, we expect
"example.com.", but never "example..com".
BUG: https://bugzilla.samba.org/show_bug.cgi?id=14378
Signed-off-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
---
librpc/ndr/ndr_dns_utils.c | 6 ++++++
selftest/knownfail.d/dns_packet | 1 -
selftest/knownfail.d/ndr_dns_nbt | 1 -
3 files changed, 6 insertions(+), 2 deletions(-)
diff --git a/librpc/ndr/ndr_dns_utils.c b/librpc/ndr/ndr_dns_utils.c
index 2ce300863bc..6931dac422d 100644
--- a/librpc/ndr/ndr_dns_utils.c
+++ b/librpc/ndr/ndr_dns_utils.c
@@ -58,6 +58,12 @@ enum ndr_err_code ndr_push_dns_string_list(struct ndr_push *ndr,
(unsigned)complen);
}
+ if (complen == 0 && s[complen] == '.') {
+ return ndr_push_error(ndr, NDR_ERR_STRING,
+ "component length is 0 "
+ "(consecutive dots)");
+ }
+
compname = talloc_asprintf(ndr, "%c%*.*s",
(unsigned char)complen,
(unsigned char)complen,
diff --git a/selftest/knownfail.d/dns_packet b/selftest/knownfail.d/dns_packet
index 6e2e5a699de..0662266f689 100644
--- a/selftest/knownfail.d/dns_packet
+++ b/selftest/knownfail.d/dns_packet
@@ -1,2 +1 @@
-samba.tests.dns_packet.samba.tests.dns_packet.TestDnsPackets.test_127_very_dotty_components
samba.tests.dns_packet.samba.tests.dns_packet.TestNbtPackets.test_127_very_dotty_components
diff --git a/selftest/knownfail.d/ndr_dns_nbt b/selftest/knownfail.d/ndr_dns_nbt
index f30217c4033..e11c121b7a7 100644
--- a/selftest/knownfail.d/ndr_dns_nbt
+++ b/selftest/knownfail.d/ndr_dns_nbt
@@ -1,4 +1,3 @@
-librpc.ndr.ndr_dns_nbt.test_ndr_dns_string_all_dots
librpc.ndr.ndr_dns_nbt.test_ndr_dns_string_half_dots
librpc.ndr.ndr_dns_nbt.test_ndr_nbt_string_all_dots
librpc.ndr.ndr_dns_nbt.test_ndr_nbt_string_half_dots
--
2.17.1

View File

@ -1,55 +0,0 @@
From 83b00656ea0e8cfdce8a9c1cef71e41477e8e6f0 Mon Sep 17 00:00:00 2001
From: Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
Date: Fri, 15 May 2020 00:06:08 +1200
Subject: [PATCH 17/22] CVE-2020-10745: dns_util/push: forbid names longer than
255 bytes
As per RFC 1035.
BUG: https://bugzilla.samba.org/show_bug.cgi?id=14378
Signed-off-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
---
librpc/ndr/ndr_dns_utils.c | 10 +++++++++-
selftest/knownfail.d/ndr_dns_nbt | 1 -
2 files changed, 9 insertions(+), 2 deletions(-)
diff --git a/librpc/ndr/ndr_dns_utils.c b/librpc/ndr/ndr_dns_utils.c
index 6931dac422d..b7f11dbab4e 100644
--- a/librpc/ndr/ndr_dns_utils.c
+++ b/librpc/ndr/ndr_dns_utils.c
@@ -11,6 +11,8 @@ enum ndr_err_code ndr_push_dns_string_list(struct ndr_push *ndr,
int ndr_flags,
const char *s)
{
+ const char *start = s;
+
if (!(ndr_flags & NDR_SCALARS)) {
return NDR_ERR_SUCCESS;
}
@@ -84,7 +86,13 @@ enum ndr_err_code ndr_push_dns_string_list(struct ndr_push *ndr,
talloc_free(compname);
s += complen;
- if (*s == '.') s++;
+ if (*s == '.') {
+ s++;
+ }
+ if (s - start > 255) {
+ return ndr_push_error(ndr, NDR_ERR_STRING,
+ "name > 255 character long");
+ }
}
/* if we reach the end of the string and have pushed the last component
diff --git a/selftest/knownfail.d/ndr_dns_nbt b/selftest/knownfail.d/ndr_dns_nbt
index e11c121b7a7..603395c8c50 100644
--- a/selftest/knownfail.d/ndr_dns_nbt
+++ b/selftest/knownfail.d/ndr_dns_nbt
@@ -1,3 +1,2 @@
-librpc.ndr.ndr_dns_nbt.test_ndr_dns_string_half_dots
librpc.ndr.ndr_dns_nbt.test_ndr_nbt_string_all_dots
librpc.ndr.ndr_dns_nbt.test_ndr_nbt_string_half_dots
--
2.17.1

View File

@ -1,267 +0,0 @@
From 23e9eb71052e02aecf726609db0256c0d93e0b57 Mon Sep 17 00:00:00 2001
From: Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
Date: Fri, 15 May 2020 10:52:45 +1200
Subject: [PATCH 18/22] CVE-2020-10745: ndr/dns-utils: prepare for NBT
compatibility
NBT has a funny thing where it sometimes needs to send a trailing dot as
part of the last component, because the string representation is a user
name. In DNS, "example.com", and "example.com." are the same, both
having three components ("example", "com", ""); in NBT, we want to treat
them differently, with the second form having the three components
("example", "com.", "").
This retains the logic of e6e2ec0001fe3c010445e26cc0efddbc1f73416b.
Also DNS compression cannot be turned off for NBT.
BUG: https://bugzilla.samba.org/show_bug.cgi?id=14378
Signed-off-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
---
librpc/ndr/ndr_dns.c | 3 +-
librpc/ndr/ndr_dns_utils.c | 42 ++++++++++++++++---
librpc/ndr/ndr_dns_utils.h | 3 +-
librpc/ndr/ndr_nbt.c | 72 ++++----------------------------
librpc/wscript_build | 3 +-
selftest/knownfail.d/dns_packet | 1 -
selftest/knownfail.d/ndr_dns_nbt | 2 -
7 files changed, 49 insertions(+), 77 deletions(-)
delete mode 100644 selftest/knownfail.d/ndr_dns_nbt
diff --git a/librpc/ndr/ndr_dns.c b/librpc/ndr/ndr_dns.c
index 68a3c9de782..966e0b59786 100644
--- a/librpc/ndr/ndr_dns.c
+++ b/librpc/ndr/ndr_dns.c
@@ -163,7 +163,8 @@ _PUBLIC_ enum ndr_err_code ndr_push_dns_string(struct ndr_push *ndr,
return ndr_push_dns_string_list(ndr,
&ndr->dns_string_list,
ndr_flags,
- s);
+ s,
+ false);
}
_PUBLIC_ enum ndr_err_code ndr_pull_dns_txt_record(struct ndr_pull *ndr, int ndr_flags, struct dns_txt_record *r)
diff --git a/librpc/ndr/ndr_dns_utils.c b/librpc/ndr/ndr_dns_utils.c
index b7f11dbab4e..325d9c68bea 100644
--- a/librpc/ndr/ndr_dns_utils.c
+++ b/librpc/ndr/ndr_dns_utils.c
@@ -9,9 +9,32 @@
enum ndr_err_code ndr_push_dns_string_list(struct ndr_push *ndr,
struct ndr_token_list *string_list,
int ndr_flags,
- const char *s)
+ const char *s,
+ bool is_nbt)
{
const char *start = s;
+ bool use_compression;
+ size_t max_length;
+ if (is_nbt) {
+ use_compression = true;
+ /*
+ * Max length is longer in NBT/Wins, because Windows counts
+ * the semi-decompressed size of the netbios name (16 bytes)
+ * rather than the wire size of 32, which is what you'd expect
+ * if it followed RFC1002 (it uses the short form in
+ * [MS-WINSRA]). In other words the maximum size of the
+ * "scope" is 237, not 221.
+ *
+ * We make the size limit slightly larger than 255 + 16,
+ * because the 237 scope limit is already enforced in the
+ * winsserver code with a specific return value; bailing out
+ * here would muck with that.
+ */
+ max_length = 274;
+ } else {
+ use_compression = !(ndr->flags & LIBNDR_FLAG_NO_COMPRESSION);
+ max_length = 255;
+ }
if (!(ndr_flags & NDR_SCALARS)) {
return NDR_ERR_SUCCESS;
@@ -23,7 +46,7 @@ enum ndr_err_code ndr_push_dns_string_list(struct ndr_push *ndr,
size_t complen;
uint32_t offset;
- if (!(ndr->flags & LIBNDR_FLAG_NO_COMPRESSION)) {
+ if (use_compression) {
/* see if we have pushed the remaining string already,
* if so we use a label pointer to this string
*/
@@ -66,6 +89,14 @@ enum ndr_err_code ndr_push_dns_string_list(struct ndr_push *ndr,
"(consecutive dots)");
}
+ if (is_nbt && s[complen] == '.' && s[complen + 1] == '\0') {
+ /* nbt names are sometimes usernames, and we need to
+ * keep a trailing dot to ensure it is byte-identical,
+ * (not just semantically identical given DNS
+ * semantics). */
+ complen++;
+ }
+
compname = talloc_asprintf(ndr, "%c%*.*s",
(unsigned char)complen,
(unsigned char)complen,
@@ -75,7 +106,7 @@ enum ndr_err_code ndr_push_dns_string_list(struct ndr_push *ndr,
/* remember the current component + the rest of the string
* so it can be reused later
*/
- if (!(ndr->flags & LIBNDR_FLAG_NO_COMPRESSION)) {
+ if (use_compression) {
NDR_CHECK(ndr_token_store(ndr, string_list, s,
ndr->offset));
}
@@ -89,9 +120,10 @@ enum ndr_err_code ndr_push_dns_string_list(struct ndr_push *ndr,
if (*s == '.') {
s++;
}
- if (s - start > 255) {
+ if (s - start > max_length) {
return ndr_push_error(ndr, NDR_ERR_STRING,
- "name > 255 character long");
+ "name > %zu character long",
+ max_length);
}
}
diff --git a/librpc/ndr/ndr_dns_utils.h b/librpc/ndr/ndr_dns_utils.h
index 823e3201112..71a65433bbb 100644
--- a/librpc/ndr/ndr_dns_utils.h
+++ b/librpc/ndr/ndr_dns_utils.h
@@ -2,4 +2,5 @@
enum ndr_err_code ndr_push_dns_string_list(struct ndr_push *ndr,
struct ndr_token_list *string_list,
int ndr_flags,
- const char *s);
+ const char *s,
+ bool is_nbt);
diff --git a/librpc/ndr/ndr_nbt.c b/librpc/ndr/ndr_nbt.c
index 838f947a168..e8dd7549a53 100644
--- a/librpc/ndr/ndr_nbt.c
+++ b/librpc/ndr/ndr_nbt.c
@@ -25,6 +25,8 @@
#include "includes.h"
#include "../libcli/nbt/libnbt.h"
#include "../libcli/netlogon/netlogon.h"
+#include "ndr_dns_utils.h"
+
/* don't allow an unlimited number of name components */
#define MAX_COMPONENTS 128
@@ -141,71 +143,11 @@ _PUBLIC_ enum ndr_err_code ndr_pull_nbt_string(struct ndr_pull *ndr, int ndr_fla
*/
_PUBLIC_ enum ndr_err_code ndr_push_nbt_string(struct ndr_push *ndr, int ndr_flags, const char *s)
{
- if (!(ndr_flags & NDR_SCALARS)) {
- return NDR_ERR_SUCCESS;
- }
-
- while (s && *s) {
- enum ndr_err_code ndr_err;
- char *compname;
- size_t complen;
- uint32_t offset;
-
- /* see if we have pushed the remaining string already,
- * if so we use a label pointer to this string
- */
- ndr_err = ndr_token_retrieve_cmp_fn(&ndr->nbt_string_list, s, &offset, (comparison_fn_t)strcmp, false);
- if (NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
- uint8_t b[2];
-
- if (offset > 0x3FFF) {
- return ndr_push_error(ndr, NDR_ERR_STRING,
- "offset for nbt string label pointer %u[%08X] > 0x00003FFF",
- offset, offset);
- }
-
- b[0] = 0xC0 | (offset>>8);
- b[1] = (offset & 0xFF);
-
- return ndr_push_bytes(ndr, b, 2);
- }
-
- complen = strcspn(s, ".");
-
- /* we need to make sure the length fits into 6 bytes */
- if (complen > 0x3F) {
- return ndr_push_error(ndr, NDR_ERR_STRING,
- "component length %u[%08X] > 0x0000003F",
- (unsigned)complen, (unsigned)complen);
- }
-
- if (s[complen] == '.' && s[complen+1] == '\0') {
- complen++;
- }
-
- compname = talloc_asprintf(ndr, "%c%*.*s",
- (unsigned char)complen,
- (unsigned char)complen,
- (unsigned char)complen, s);
- NDR_ERR_HAVE_NO_MEMORY(compname);
-
- /* remember the current componemt + the rest of the string
- * so it can be reused later
- */
- NDR_CHECK(ndr_token_store(ndr, &ndr->nbt_string_list, s, ndr->offset));
-
- /* push just this component into the blob */
- NDR_CHECK(ndr_push_bytes(ndr, (const uint8_t *)compname, complen+1));
- talloc_free(compname);
-
- s += complen;
- if (*s == '.') s++;
- }
-
- /* if we reach the end of the string and have pushed the last component
- * without using a label pointer, we need to terminate the string
- */
- return ndr_push_bytes(ndr, (const uint8_t *)"", 1);
+ return ndr_push_dns_string_list(ndr,
+ &ndr->dns_string_list,
+ ndr_flags,
+ s,
+ true);
}
diff --git a/librpc/wscript_build b/librpc/wscript_build
index c165500644b..4917928a9c4 100644
--- a/librpc/wscript_build
+++ b/librpc/wscript_build
@@ -401,7 +401,7 @@ bld.SAMBA_SUBSYSTEM('NDR_SCHANNEL',
bld.SAMBA_LIBRARY('ndr_nbt',
source='gen_ndr/ndr_nbt.c ndr/ndr_nbt.c',
- public_deps='ndr NDR_NBT_BUF NDR_SECURITY',
+ public_deps='ndr NDR_NBT_BUF NDR_SECURITY NDR_DNS',
public_headers='gen_ndr/nbt.h gen_ndr/ndr_nbt.h ndr/ndr_nbt.h',
header_path=[ ('gen_ndr*', 'gen_ndr'), ('ndr*', 'ndr')],
pc_files='ndr_nbt.pc',
@@ -666,6 +666,5 @@ bld.SAMBA_BINARY('test_ndr_dns_nbt',
cmocka
ndr
ndr_nbt
- NDR_DNS
''',
install=False)
diff --git a/selftest/knownfail.d/dns_packet b/selftest/knownfail.d/dns_packet
index 0662266f689..e69de29bb2d 100644
--- a/selftest/knownfail.d/dns_packet
+++ b/selftest/knownfail.d/dns_packet
@@ -1 +0,0 @@
-samba.tests.dns_packet.samba.tests.dns_packet.TestNbtPackets.test_127_very_dotty_components
diff --git a/selftest/knownfail.d/ndr_dns_nbt b/selftest/knownfail.d/ndr_dns_nbt
deleted file mode 100644
index 603395c8c50..00000000000
--- a/selftest/knownfail.d/ndr_dns_nbt
+++ /dev/null
@@ -1,2 +0,0 @@
-librpc.ndr.ndr_dns_nbt.test_ndr_nbt_string_all_dots
-librpc.ndr.ndr_dns_nbt.test_ndr_nbt_string_half_dots
--
2.17.1

View File

@ -1,74 +0,0 @@
From 4def2dc554754033174c60f5860f51b46d8502c1 Mon Sep 17 00:00:00 2001
From: Andrew Bartlett <abartlet@samba.org>
Date: Fri, 5 Jun 2020 22:14:48 +1200
Subject: [PATCH 21/22] CVE-2020-10760 dsdb: Ensure a proper talloc tree for
saved controls
Otherwise a paged search on the GC port will fail as the ->data was
not kept around for the second page of searches.
An example command to produce this is
bin/ldbsearch --paged -H ldap://$SERVER:3268 -U$USERNAME%$PASSWORD
This shows up later in the partition module as:
ERROR: AddressSanitizer: heap-use-after-free on address 0x60b00151ef20 at pc 0x7fec3f801aac bp 0x7ffe8472c270 sp 0x7ffe8472c260
READ of size 4 at 0x60b00151ef20 thread T0 (ldap(0))
#0 0x7fec3f801aab in talloc_chunk_from_ptr ../../lib/talloc/talloc.c:526
#1 0x7fec3f801aab in __talloc_get_name ../../lib/talloc/talloc.c:1559
#2 0x7fec3f801aab in talloc_check_name ../../lib/talloc/talloc.c:1582
#3 0x7fec1b86b2e1 in partition_search ../../source4/dsdb/samdb/ldb_modules/partition.c:780
or
smb_panic_default: PANIC (pid 13287): Bad talloc magic value - unknown value
(from source4/dsdb/samdb/ldb_modules/partition.c:780)
BUG: https://bugzilla.samba.org/show_bug.cgi?id=14402
Signed-off-by: Andrew Bartlett <abartlet@samba.org>
---
source4/dsdb/samdb/ldb_modules/paged_results.c | 8 ++++++++
source4/dsdb/samdb/ldb_modules/vlv_pagination.c | 7 +++++++
2 files changed, 15 insertions(+)
diff --git a/source4/dsdb/samdb/ldb_modules/paged_results.c b/source4/dsdb/samdb/ldb_modules/paged_results.c
index 735883e8802..3eea3236e7d 100644
--- a/source4/dsdb/samdb/ldb_modules/paged_results.c
+++ b/source4/dsdb/samdb/ldb_modules/paged_results.c
@@ -523,6 +523,14 @@ paged_results_copy_down_controls(TALLOC_CTX *mem_ctx,
continue;
}
new_controls[j] = talloc_steal(new_controls, control);
+
+ /*
+ * Sadly the caller is not obliged to make this a
+ * proper talloc tree, so we do so here.
+ */
+ if (control->data) {
+ talloc_steal(control, control->data);
+ }
j++;
}
new_controls[j] = NULL;
diff --git a/source4/dsdb/samdb/ldb_modules/vlv_pagination.c b/source4/dsdb/samdb/ldb_modules/vlv_pagination.c
index b103bda5f52..d6d6039e849 100644
--- a/source4/dsdb/samdb/ldb_modules/vlv_pagination.c
+++ b/source4/dsdb/samdb/ldb_modules/vlv_pagination.c
@@ -746,6 +746,13 @@ vlv_copy_down_controls(TALLOC_CTX *mem_ctx, struct ldb_control **controls)
continue;
}
new_controls[j] = talloc_steal(new_controls, control);
+ /*
+ * Sadly the caller is not obliged to make this a
+ * proper talloc tree, so we do so here.
+ */
+ if (control->data) {
+ talloc_steal(control, control->data);
+ }
j++;
}
new_controls[j] = NULL;
--
2.17.1

View File

@ -1,247 +0,0 @@
From df599b6b79010759279eb7f52486f1d0a59d06d3 Mon Sep 17 00:00:00 2001
From: Andrew Bartlett <abartlet@samba.org>
Date: Mon, 8 Jun 2020 16:32:14 +1200
Subject: [PATCH 22/22] CVE-2020-10760 dsdb: Add tests for paged_results and
VLV over the Global Catalog port
This should avoid a regression.
(backported from master patch)
[abartlet@samba.org: sort=True parameter on test_paged_delete_during_search
is not in 4.11]
Signed-off-by: Andrew Bartlett <abartlet@samba.org>
---
selftest/knownfail.d/vlv | 2 +-
source4/dsdb/tests/python/vlv.py | 171 +++++++++++++++++++------------
2 files changed, 107 insertions(+), 66 deletions(-)
diff --git a/selftest/knownfail.d/vlv b/selftest/knownfail.d/vlv
index f187a2ed55e..7ae02baf17b 100644
--- a/selftest/knownfail.d/vlv
+++ b/selftest/knownfail.d/vlv
@@ -1,2 +1,2 @@
samba4.ldap.vlv.python.*__main__.VLVTests.test_vlv_change_search_expr
-samba4.ldap.vlv.python.*__main__.PagedResultsTests.test_paged_cant_change_controls_data
+samba4.ldap.vlv.python.*__main__.PagedResultsTestsRW.test_paged_cant_change_controls_data
diff --git a/source4/dsdb/tests/python/vlv.py b/source4/dsdb/tests/python/vlv.py
index f3c603e3a39..ba03b425a5b 100644
--- a/source4/dsdb/tests/python/vlv.py
+++ b/source4/dsdb/tests/python/vlv.py
@@ -152,7 +152,7 @@ class TestsWithUserOU(samba.tests.TestCase):
super(TestsWithUserOU, self).setUp()
self.ldb = SamDB(host, credentials=creds,
session_info=system_session(lp), lp=lp)
-
+ self.ldb_ro = self.ldb
self.base_dn = self.ldb.domain_dn()
self.tree_dn = "ou=vlvtesttree,%s" % self.base_dn
self.ou = "ou=vlvou,%s" % self.tree_dn
@@ -199,8 +199,60 @@ class TestsWithUserOU(samba.tests.TestCase):
self.ldb.delete(self.tree_dn, ['tree_delete:1'])
-class VLVTests(TestsWithUserOU):
+class VLVTestsBase(TestsWithUserOU):
+
+ # Run a vlv search and return important fields of the response control
+ def vlv_search(self, attr, expr, cookie="", after_count=0, offset=1):
+ sort_ctrl = "server_sort:1:0:%s" % attr
+ ctrl = "vlv:1:0:%d:%d:0" % (after_count, offset)
+ if cookie:
+ ctrl += ":" + cookie
+
+ res = self.ldb_ro.search(self.ou,
+ expression=expr,
+ scope=ldb.SCOPE_ONELEVEL,
+ attrs=[attr],
+ controls=[ctrl, sort_ctrl])
+ results = [str(x[attr][0]) for x in res]
+
+ ctrls = [str(c) for c in res.controls if
+ str(c).startswith('vlv')]
+ self.assertEqual(len(ctrls), 1)
+
+ spl = ctrls[0].rsplit(':')
+ cookie = ""
+ if len(spl) == 6:
+ cookie = spl[-1]
+
+ return results, cookie
+
+
+class VLVTestsRO(VLVTestsBase):
+ def test_vlv_simple_double_run(self):
+ """Do the simplest possible VLV query to confirm if VLV
+ works at all. Useful for showing VLV as a whole works
+ on Global Catalog (for example)"""
+ attr = 'roomNumber'
+ expr = "(objectclass=user)"
+ # Start new search
+ full_results, cookie = self.vlv_search(attr, expr,
+ after_count=len(self.users))
+
+ results, cookie = self.vlv_search(attr, expr, cookie=cookie,
+ after_count=len(self.users))
+ expected_results = full_results
+ self.assertEqual(results, expected_results)
+
+
+class VLVTestsGC(VLVTestsRO):
+ def setUp(self):
+ super(VLVTestsRO, self).setUp()
+ self.ldb_ro = SamDB(host + ":3268", credentials=creds,
+ session_info=system_session(lp), lp=lp)
+
+
+class VLVTests(VLVTestsBase):
def get_full_list(self, attr, include_cn=False):
"""Fetch the whole list sorted on the attribute, using the VLV.
This way you get a VLV cookie."""
@@ -1081,31 +1133,6 @@ class VLVTests(TestsWithUserOU):
controls=[sort_control,
"vlv:0:1:1:1:0:%s" % vlv_cookies[-1]])
- # Run a vlv search and return important fields of the response control
- def vlv_search(self, attr, expr, cookie="", after_count=0, offset=1):
- sort_ctrl = "server_sort:1:0:%s" % attr
- ctrl = "vlv:1:0:%d:%d:0" % (after_count, offset)
- if cookie:
- ctrl += ":" + cookie
-
- res = self.ldb.search(self.ou,
- expression=expr,
- scope=ldb.SCOPE_ONELEVEL,
- attrs=[attr],
- controls=[ctrl, sort_ctrl])
- results = [str(x[attr][0]) for x in res]
-
- ctrls = [str(c) for c in res.controls if
- str(c).startswith('vlv')]
- self.assertEqual(len(ctrls), 1)
-
- spl = ctrls[0].rsplit(':')
- cookie = ""
- if len(spl) == 6:
- cookie = spl[-1]
-
- return results, cookie
-
def test_vlv_modify_during_view(self):
attr = 'roomNumber'
expr = "(objectclass=user)"
@@ -1218,11 +1245,11 @@ class PagedResultsTests(TestsWithUserOU):
if subtree:
scope = ldb.SCOPE_SUBTREE
- res = self.ldb.search(ou,
- expression=expr,
- scope=scope,
- controls=controls,
- **kwargs)
+ res = self.ldb_ro.search(ou,
+ expression=expr,
+ scope=scope,
+ controls=controls,
+ **kwargs)
results = [str(r['cn'][0]) for r in res]
ctrls = [str(c) for c in res.controls if
@@ -1235,6 +1262,53 @@ class PagedResultsTests(TestsWithUserOU):
cookie = spl[-1]
return results, cookie
+
+class PagedResultsTestsRO(PagedResultsTests):
+
+ def test_paged_search_lockstep(self):
+ expr = "(objectClass=*)"
+ ps = 3
+
+ all_results, _ = self.paged_search(expr, page_size=len(self.users)+1)
+
+ # Run two different but overlapping paged searches simultaneously.
+ set_1_index = int((len(all_results))//3)
+ set_2_index = int((2*len(all_results))//3)
+ set_1 = all_results[set_1_index:]
+ set_2 = all_results[:set_2_index+1]
+ set_1_expr = "(cn>=%s)" % (all_results[set_1_index])
+ set_2_expr = "(cn<=%s)" % (all_results[set_2_index])
+
+ results, cookie1 = self.paged_search(set_1_expr, page_size=ps)
+ self.assertEqual(results, set_1[:ps])
+ results, cookie2 = self.paged_search(set_2_expr, page_size=ps)
+ self.assertEqual(results, set_2[:ps])
+
+ results, cookie1 = self.paged_search(set_1_expr, cookie=cookie1,
+ page_size=ps)
+ self.assertEqual(results, set_1[ps:ps*2])
+ results, cookie2 = self.paged_search(set_2_expr, cookie=cookie2,
+ page_size=ps)
+ self.assertEqual(results, set_2[ps:ps*2])
+
+ results, _ = self.paged_search(set_1_expr, cookie=cookie1,
+ page_size=len(self.users))
+ self.assertEqual(results, set_1[ps*2:])
+ results, _ = self.paged_search(set_2_expr, cookie=cookie2,
+ page_size=len(self.users))
+ self.assertEqual(results, set_2[ps*2:])
+
+
+class PagedResultsTestsGC(PagedResultsTestsRO):
+
+ def setUp(self):
+ super(PagedResultsTestsRO, self).setUp()
+ self.ldb_ro = SamDB(host + ":3268", credentials=creds,
+ session_info=system_session(lp), lp=lp)
+
+
+class PagedResultsTestsRW(PagedResultsTests):
+
def test_paged_delete_during_search(self):
expr = "(objectClass=*)"
@@ -1611,39 +1685,6 @@ class PagedResultsTests(TestsWithUserOU):
cookie, attrs=changed_attrs,
extra_ctrls=[])
- def test_paged_search_lockstep(self):
- expr = "(objectClass=*)"
- ps = 3
-
- all_results, _ = self.paged_search(expr, page_size=len(self.users)+1)
-
- # Run two different but overlapping paged searches simultaneously.
- set_1_index = int((len(all_results))//3)
- set_2_index = int((2*len(all_results))//3)
- set_1 = all_results[set_1_index:]
- set_2 = all_results[:set_2_index+1]
- set_1_expr = "(cn>=%s)" % (all_results[set_1_index])
- set_2_expr = "(cn<=%s)" % (all_results[set_2_index])
-
- results, cookie1 = self.paged_search(set_1_expr, page_size=ps)
- self.assertEqual(results, set_1[:ps])
- results, cookie2 = self.paged_search(set_2_expr, page_size=ps)
- self.assertEqual(results, set_2[:ps])
-
- results, cookie1 = self.paged_search(set_1_expr, cookie=cookie1,
- page_size=ps)
- self.assertEqual(results, set_1[ps:ps*2])
- results, cookie2 = self.paged_search(set_2_expr, cookie=cookie2,
- page_size=ps)
- self.assertEqual(results, set_2[ps:ps*2])
-
- results, _ = self.paged_search(set_1_expr, cookie=cookie1,
- page_size=len(self.users))
- self.assertEqual(results, set_1[ps*2:])
- results, _ = self.paged_search(set_2_expr, cookie=cookie2,
- page_size=len(self.users))
- self.assertEqual(results, set_2[ps*2:])
-
def test_vlv_paged(self):
"""Testing behaviour with VLV and paged_results set.
--
2.17.1

View File

@ -1,65 +0,0 @@
From 11034ea33fca9b8a1c2e14480e70069b55fca6a2 Mon Sep 17 00:00:00 2001
From: Andrew Bartlett <abartlet@samba.org>
Date: Thu, 25 Jun 2020 11:59:54 +1200
Subject: [PATCH 19/22] CVE-2020-14303 Ensure an empty packet will not DoS the
NBT server
Signed-off-by: Andrew Bartlett <abartlet@samba.org>
---
python/samba/tests/dns_packet.py | 19 +++++++++++++++++++
selftest/knownfail.d/empty-nbt | 1 +
2 files changed, 20 insertions(+)
create mode 100644 selftest/knownfail.d/empty-nbt
diff --git a/python/samba/tests/dns_packet.py b/python/samba/tests/dns_packet.py
index c4f843eb613..ae7bcb3ad8c 100644
--- a/python/samba/tests/dns_packet.py
+++ b/python/samba/tests/dns_packet.py
@@ -156,6 +156,19 @@ class TestDnsPacketBase(TestCase):
rcode = self.decode_reply(data)['rcode']
return expected_rcode == rcode
+ def _test_empty_packet(self):
+
+ packet = b""
+ s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+ s.sendto(packet, self.server)
+ s.close()
+
+ # It is reasonable not to reply to an empty packet
+ # but it is not reasonable to render the server
+ # unresponsive.
+ ok = self._known_good_query()
+ self.assertTrue(ok, f"the server is unresponsive")
+
class TestDnsPackets(TestDnsPacketBase):
server = (SERVER, 53)
@@ -174,6 +187,9 @@ class TestDnsPackets(TestDnsPacketBase):
label = b'x.' * 31 + b'x'
self._test_many_repeated_components(label, 127)
+ def test_empty_packet(self):
+ self._test_empty_packet()
+
class TestNbtPackets(TestDnsPacketBase):
server = (SERVER, 137)
@@ -209,3 +225,6 @@ class TestNbtPackets(TestDnsPacketBase):
def test_127_half_dotty_components(self):
label = b'x.' * 31 + b'x'
self._test_many_repeated_components(label, 127)
+
+ def test_empty_packet(self):
+ self._test_empty_packet()
diff --git a/selftest/knownfail.d/empty-nbt b/selftest/knownfail.d/empty-nbt
new file mode 100644
index 00000000000..e4bcccab4e5
--- /dev/null
+++ b/selftest/knownfail.d/empty-nbt
@@ -0,0 +1 @@
+^samba.tests.dns_packet.samba.tests.dns_packet.TestNbtPackets.test_empty_packet
\ No newline at end of file
--
2.17.1

View File

@ -1,58 +0,0 @@
From 153c8db09b26455aa9802ff95943dd8a75f31893 Mon Sep 17 00:00:00 2001
From: Gary Lockyer <gary@catalyst.net.nz>
Date: Wed, 24 Jun 2020 14:27:08 +1200
Subject: [PATCH 20/22] CVE-2020-14303: s4 nbt: fix busy loop on empty UDP
packet
An empty UDP packet put the nbt server into a busy loop that consumes
100% of a cpu.
BUG: https://bugzilla.samba.org/show_bug.cgi?id=14417
Signed-off-by: Gary Lockyer <gary@catalyst.net.nz>
---
libcli/nbt/nbtsocket.c | 17 ++++++++++++++++-
selftest/knownfail.d/empty-nbt | 1 -
2 files changed, 16 insertions(+), 2 deletions(-)
delete mode 100644 selftest/knownfail.d/empty-nbt
diff --git a/libcli/nbt/nbtsocket.c b/libcli/nbt/nbtsocket.c
index 33d53fba993..8aecaf73247 100644
--- a/libcli/nbt/nbtsocket.c
+++ b/libcli/nbt/nbtsocket.c
@@ -167,8 +167,23 @@ static void nbt_name_socket_recv(struct nbt_name_socket *nbtsock)
return;
}
+ /*
+ * Given a zero length, data_blob_talloc() returns the
+ * NULL blob {NULL, 0}.
+ *
+ * We only want to error return here on a real out of memory condition
+ * (i.e. dsize != 0, so the UDP packet has data, but the return of the
+ * allocation failed, so blob.data==NULL).
+ *
+ * Given an actual zero length UDP packet having blob.data == NULL
+ * isn't an out of memory error condition, that's the defined semantics
+ * of data_blob_talloc() when asked for zero bytes.
+ *
+ * We still need to continue to do the zero-length socket_recvfrom()
+ * read in order to clear the "read pending" condition on the socket.
+ */
blob = data_blob_talloc(tmp_ctx, NULL, dsize);
- if (blob.data == NULL) {
+ if (blob.data == NULL && dsize != 0) {
talloc_free(tmp_ctx);
return;
}
diff --git a/selftest/knownfail.d/empty-nbt b/selftest/knownfail.d/empty-nbt
deleted file mode 100644
index e4bcccab4e5..00000000000
--- a/selftest/knownfail.d/empty-nbt
+++ /dev/null
@@ -1 +0,0 @@
-^samba.tests.dns_packet.samba.tests.dns_packet.TestNbtPackets.test_empty_packet
\ No newline at end of file
--
2.17.1

7
samba-4.11.12.tar.asc Normal file
View File

@ -0,0 +1,7 @@
-----BEGIN PGP SIGNATURE-----
iHMEABECADMWIQRS+8C4bZVLCEMyTNxvM5FbZWi36gUCX0TCrBUcc2FtYmEtYnVn
c0BzYW1iYS5vcmcACgkQbzORW2Vot+pkIgCePoB8Vb0cE4j0tafdJDK411aAP9YA
n0xcJD8v1hc1/qlhXLjA6bG3i7Rx
=GXx8
-----END PGP SIGNATURE-----

View File

@ -0,0 +1,28 @@
From 7f2c62dcdebb387b086df37c8dd38a6027d8b631 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?G=C3=BCnther=20Deschner?= <gd@samba.org>
Date: Tue, 25 Aug 2020 10:37:18 +0200
Subject: [PATCH] lib/util: do not install /usr/bin/test_util
BUG: https://bugzilla.samba.org/show_bug.cgi?id=14166
Guenther
Signed-off-by: Guenther Deschner <gd@samba.org>
---
lib/util/wscript_build | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/lib/util/wscript_build b/lib/util/wscript_build
index 4ac0a9cc064..e04752cb130 100644
--- a/lib/util/wscript_build
+++ b/lib/util/wscript_build
@@ -291,4 +291,5 @@ else:
bld.SAMBA_BINARY('test_util',
source='tests/test_util.c',
deps='cmocka replace talloc samba-util',
- local_include=False)
+ local_include=False,
+ install=False)
--
2.26.2

View File

@ -1,7 +0,0 @@
-----BEGIN PGP SIGNATURE-----
iHMEABECADMWIQRS+8C4bZVLCEMyTNxvM5FbZWi36gUCXjALthUcc2FtYmEtYnVn
c0BzYW1iYS5vcmcACgkQbzORW2Vot+od+ACgpzREKkVcyLse9EwufX0vS/JMUYIA
n2xGjOlyTFJJUD9heWInjmYzy4W0
=472O
-----END PGP SIGNATURE-----

View File

@ -4,7 +4,7 @@
%define talloc_version 2.2.0
%define tdb_version 1.4.2
%define tevent_version 0.10.0
%define ldb_version 2.0.8
%define ldb_version 2.0.12
%undefine _strict_symbol_defs_build
@ -48,8 +48,8 @@
%define samba_depver %{version}-%{release}
Name: samba
Version: 4.11.6
Release: 8
Version: 4.11.12
Release: 1
Summary: A suite for Linux to interoperate with Windows
License: GPLv3+ and LGPLv3+
@ -69,35 +69,7 @@ Source201: README.downgrade
Patch0: 0000-use-gnutls-for-des-cbc.patch
Patch1: 0001-handle-removal-des-enctypes-from-krb5.patch
Patch2: 0002-samba-tool-create-working-private-krb5.conf.patch
Patch3: CVE-2020-10700-1.patch
Patch4: CVE-2020-10700-3.patch
Patch5: CVE-2020-10704-1.patch
Patch6: CVE-2020-10704-3.patch
Patch7: CVE-2020-10704-5.patch
Patch8: CVE-2020-10704-6.patch
Patch9: CVE-2020-10704-7.patch
Patch10: CVE-2020-10704-8.patch
Patch11: CVE-2020-10730-1.patch
Patch12: CVE-2020-10730-2.patch
Patch13: CVE-2020-10730-3.patch
Patch14: CVE-2020-10730-4.patch
Patch15: CVE-2020-10730-5.patch
Patch16: CVE-2020-10730-6.patch
Patch17: CVE-2020-10730-7.patch
Patch18: CVE-2020-10730-8.patch
Patch19: CVE-2020-10730-9.patch
Patch20: CVE-2020-10730-10.patch
Patch21: CVE-2020-10745-1.patch
Patch22: CVE-2020-10745-2.patch
Patch23: CVE-2020-10745-3.patch
Patch24: CVE-2020-10745-4.patch
Patch25: CVE-2020-10745-5.patch
Patch26: CVE-2020-10745-6.patch
Patch27: CVE-2020-10745-7.patch
Patch28: CVE-2020-14303-1.patch
Patch29: CVE-2020-14303-2.patch
Patch30: CVE-2020-10760-1.patch
Patch31: CVE-2020-10760-2.patch
Patch3: samba-4.11.13-lib_util_wscript.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
@ -1856,6 +1828,7 @@ fi
%{python3_sitearch}/samba/tests/__pycache__/hostconfig.*.pyc
%{python3_sitearch}/samba/tests/__pycache__/join.*.pyc
%{python3_sitearch}/samba/tests/__pycache__/krb5_credentials.*.pyc
%{python3_sitearch}/samba/tests/__pycache__/ldap_raw.*.pyc
%{python3_sitearch}/samba/tests/__pycache__/ldap_referrals.*.pyc
%{python3_sitearch}/samba/tests/__pycache__/loadparm.*.pyc
%{python3_sitearch}/samba/tests/__pycache__/libsmb.*.pyc
@ -2039,6 +2012,7 @@ fi
%{python3_sitearch}/samba/tests/kcc/kcc_utils.py
%{python3_sitearch}/samba/tests/kcc/ldif_import_export.py
%{python3_sitearch}/samba/tests/krb5_credentials.py
%{python3_sitearch}/samba/tests/ldap_raw.py
%{python3_sitearch}/samba/tests/ldap_referrals.py
%{python3_sitearch}/samba/tests/libsmb.py
%{python3_sitearch}/samba/tests/loadparm.py
@ -2113,6 +2087,9 @@ fi
%{python3_sitearch}/samba/tests/samba_tool/__pycache__/user.*.pyc
%{python3_sitearch}/samba/tests/samba_tool/__pycache__/user_check_password_script.*.pyc
%{python3_sitearch}/samba/tests/samba_tool/__pycache__/user_virtualCryptSHA.*.pyc
%{python3_sitearch}/samba/tests/samba_tool/__pycache__/user_virtualCryptSHA_base.*.pyc
%{python3_sitearch}/samba/tests/samba_tool/__pycache__/user_virtualCryptSHA_gpg.*.pyc
%{python3_sitearch}/samba/tests/samba_tool/__pycache__/user_virtualCryptSHA_userPassword.*.pyc
%{python3_sitearch}/samba/tests/samba_tool/__pycache__/user_wdigest.*.pyc
%{python3_sitearch}/samba/tests/samba_tool/__pycache__/visualize.*.pyc
%{python3_sitearch}/samba/tests/samba_tool/__pycache__/visualize_drs.*.pyc
@ -2144,6 +2121,9 @@ fi
%{python3_sitearch}/samba/tests/samba_tool/user.py
%{python3_sitearch}/samba/tests/samba_tool/user_check_password_script.py
%{python3_sitearch}/samba/tests/samba_tool/user_virtualCryptSHA.py
%{python3_sitearch}/samba/tests/samba_tool/user_virtualCryptSHA_base.py
%{python3_sitearch}/samba/tests/samba_tool/user_virtualCryptSHA_gpg.py
%{python3_sitearch}/samba/tests/samba_tool/user_virtualCryptSHA_userPassword.py
%{python3_sitearch}/samba/tests/samba_tool/user_wdigest.py
%{python3_sitearch}/samba/tests/samba_tool/visualize.py
%{python3_sitearch}/samba/tests/samba_tool/visualize_drs.py
@ -3076,6 +3056,12 @@ fi
%{_mandir}/man*
%changelog
* Mon Aug 31 2020 yuboyun <yuboyun@huawei.com> - 4.11.12-1
- Type:NA
- ID:NA
- SUG:NA
- DESC:update to 4.11.12
* Wed Aug 05 2020 yuboyun <yuboyun@huawei.com> - 4.11.6-8
- Type:cves
- ID:CVE-2020-10730 CVE-2020-10745 CVE-2020-14303 CVE-2020-10760