aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--main/samba/0001-CVE-2018-10858-libsmb-Ensure-smbc_urlencode-can-t-ov.patch40
-rw-r--r--main/samba/0001-CVE-2018-10918-cracknames-Fix-DoS-NULL-pointer-de-re.patch91
-rw-r--r--main/samba/0001-CVE-2018-10919-security-Move-object-specific-access-.patch145
-rw-r--r--main/samba/0001-CVE-2018-1139-libcli-auth-Do-not-allow-ntlmv1-over-S.patch68
-rw-r--r--main/samba/0002-CVE-2018-10858-libsmb-Harden-smbc_readdir_internal-a.patch158
-rw-r--r--main/samba/0002-CVE-2018-10919-security-Add-more-comments-to-the-obj.patch78
-rw-r--r--main/samba/0002-CVE-2018-1139-libcli-auth-fix-debug-messages-in-hash.patch62
-rw-r--r--main/samba/0003-CVE-2018-10919-tests-Add-tests-for-guessing-confiden.patch996
-rw-r--r--main/samba/0003-CVE-2018-1139-s3-utils-use-enum-ntlm_auth_level-in-n.patch47
-rw-r--r--main/samba/0004-CVE-2018-10919-tests-Add-test-case-for-object-visibi.patch117
-rw-r--r--main/samba/0004-CVE-2018-1139-selftest-verify-whether-ntlmv1-can-be-.patch38
-rw-r--r--main/samba/0005-CVE-2018-10919-security-Fix-checking-of-object-speci.patch58
-rw-r--r--main/samba/0005-CVE-2018-1139-libcli-auth-Add-initial-tests-for-ntlm.patch472
-rw-r--r--main/samba/0006-CVE-2018-10919-tests-test-ldap-searches-for-non-exis.patch39
-rw-r--r--main/samba/0007-CVE-2018-10919-acl_read-Split-access_mask-logic-out-.patch91
-rw-r--r--main/samba/0008-CVE-2018-10919-acl_read-Small-refactor-to-aclread_ca.patch72
-rw-r--r--main/samba/0009-CVE-2018-10919-acl_read-Flip-the-logic-in-the-dirsyn.patch62
-rw-r--r--main/samba/0010-CVE-2018-10919-acl_read-Fix-unauthorized-attribute-a.patch351
-rw-r--r--main/samba/0011-CVE-2018-10919-tests-Add-extra-test-for-dirsync-dele.patch261
-rw-r--r--main/samba/APKBUILD45
20 files changed, 3290 insertions, 1 deletions
diff --git a/main/samba/0001-CVE-2018-10858-libsmb-Ensure-smbc_urlencode-can-t-ov.patch b/main/samba/0001-CVE-2018-10858-libsmb-Ensure-smbc_urlencode-can-t-ov.patch
new file mode 100644
index 0000000000..0cdbe7c4ba
--- /dev/null
+++ b/main/samba/0001-CVE-2018-10858-libsmb-Ensure-smbc_urlencode-can-t-ov.patch
@@ -0,0 +1,40 @@
+From 02db55b4074e0ceebb87a75105e8ef79c3dcf032 Mon Sep 17 00:00:00 2001
+From: Jeremy Allison <jra@samba.org>
+Date: Fri, 15 Jun 2018 15:07:17 -0700
+Subject: [PATCH] CVE-2018-10858: libsmb: Ensure smbc_urlencode() can't
+ overwrite passed in buffer.
+
+BUG: https://bugzilla.samba.org/show_bug.cgi?id=13453
+
+CVE-2018-10858: Insufficient input validation on client directory
+ listing in libsmbclient.
+
+Signed-off-by: Jeremy Allison <jra@samba.org>
+Reviewed-by: Ralph Boehme <slow@samba.org>
+---
+ source3/libsmb/libsmb_path.c | 9 +++++++--
+ 1 file changed, 7 insertions(+), 2 deletions(-)
+
+diff --git a/source3/libsmb/libsmb_path.c b/source3/libsmb/libsmb_path.c
+index 01b0a61e483..ed70ab37550 100644
+--- a/source3/libsmb/libsmb_path.c
++++ b/source3/libsmb/libsmb_path.c
+@@ -173,8 +173,13 @@ smbc_urlencode(char *dest,
+ }
+ }
+
+- *dest++ = '\0';
+- max_dest_len--;
++ if (max_dest_len == 0) {
++ /* Ensure we return -1 if no null termination. */
++ return -1;
++ }
++
++ *dest++ = '\0';
++ max_dest_len--;
+
+ return max_dest_len;
+ }
+--
+2.18.0
+
diff --git a/main/samba/0001-CVE-2018-10918-cracknames-Fix-DoS-NULL-pointer-de-re.patch b/main/samba/0001-CVE-2018-10918-cracknames-Fix-DoS-NULL-pointer-de-re.patch
new file mode 100644
index 0000000000..d9fc9ca4a7
--- /dev/null
+++ b/main/samba/0001-CVE-2018-10918-cracknames-Fix-DoS-NULL-pointer-de-re.patch
@@ -0,0 +1,91 @@
+From 49d940f8e335b8af6daf65ac6d3cce45db09ca8e Mon Sep 17 00:00:00 2001
+From: Andrew Bartlett <abartlet@samba.org>
+Date: Mon, 30 Jul 2018 14:00:18 +1200
+Subject: [PATCH] CVE-2018-10918: cracknames: Fix DoS (NULL pointer de-ref)
+ when not servicePrincipalName is set on a user
+
+This regression was introduced in Samba 4.7 by bug 12842 and in
+master git commit eb2e77970e41c1cb62c041877565e939c78ff52d.
+
+BUG: https://bugzilla.samba.org/show_bug.cgi?id=13552
+
+CVE-2018-10918: Denial of Service Attack on AD DC DRSUAPI server.
+
+Signed-off-by: Andrew Bartlett <abartlet@samba.org>
+Reviewed-by: Gary Lockyer <gary@catalyst.net.nz>
+---
+ source4/dsdb/samdb/cracknames.c | 8 ++++-
+ source4/torture/drs/python/cracknames.py | 38 ++++++++++++++++++++++++
+ 2 files changed, 45 insertions(+), 1 deletion(-)
+
+diff --git a/source4/dsdb/samdb/cracknames.c b/source4/dsdb/samdb/cracknames.c
+index d43f510b949..3b215ac0ec9 100644
+--- a/source4/dsdb/samdb/cracknames.c
++++ b/source4/dsdb/samdb/cracknames.c
+@@ -1253,7 +1253,13 @@ static WERROR DsCrackNameOneFilter(struct ldb_context *sam_ctx, TALLOC_CTX *mem_
+ return WERR_OK;
+ }
+ case DRSUAPI_DS_NAME_FORMAT_SERVICE_PRINCIPAL: {
+- if (result->elements[0].num_values > 1) {
++ struct ldb_message_element *el
++ = ldb_msg_find_element(result,
++ "servicePrincipalName");
++ if (el == NULL) {
++ info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
++ return WERR_OK;
++ } else if (el->num_values > 1) {
+ info1->status = DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE;
+ return WERR_OK;
+ }
+diff --git a/source4/torture/drs/python/cracknames.py b/source4/torture/drs/python/cracknames.py
+index d8c8ae53d60..9bf90f9c997 100644
+--- a/source4/torture/drs/python/cracknames.py
++++ b/source4/torture/drs/python/cracknames.py
+@@ -149,6 +149,44 @@ class DrsCracknamesTestCase(drs_base.DrsBaseTestCase):
+
+ self.ldb_dc1.delete(user)
+
++ def test_NoSPNAttribute(self):
++ """
++ Verifies that, if we try and cracknames with the desired output
++ being an SPN, it returns
++ DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE.
++ """
++ username = "Cracknames_no_SPN"
++ user = "cn=%s,%s" % (username, self.ou)
++
++ user_record = {
++ "dn": user,
++ "objectclass": "user",
++ "sAMAccountName" : username,
++ "userPrincipalName" : "test4@test.com",
++ "displayName" : "test4"}
++
++ self.ldb_dc1.add(user_record)
++
++ (result, ctr) = self._do_cracknames(user,
++ drsuapi.DRSUAPI_DS_NAME_FORMAT_FQDN_1779,
++ drsuapi.DRSUAPI_DS_NAME_FORMAT_GUID)
++
++ self.assertEquals(ctr.count, 1)
++ self.assertEquals(ctr.array[0].status,
++ drsuapi.DRSUAPI_DS_NAME_STATUS_OK)
++
++ user_guid = ctr.array[0].result_name
++
++ (result, ctr) = self._do_cracknames(user_guid,
++ drsuapi.DRSUAPI_DS_NAME_FORMAT_GUID,
++ drsuapi.DRSUAPI_DS_NAME_FORMAT_SERVICE_PRINCIPAL)
++
++ self.assertEquals(ctr.count, 1)
++ self.assertEquals(ctr.array[0].status,
++ drsuapi.DRSUAPI_DS_NAME_STATUS_NOT_FOUND)
++
++ self.ldb_dc1.delete(user)
++
+ def _do_cracknames(self, name, format_offered, format_desired):
+ req = drsuapi.DsNameRequest1()
+ names = drsuapi.DsNameString()
+--
+2.18.0
+
diff --git a/main/samba/0001-CVE-2018-10919-security-Move-object-specific-access-.patch b/main/samba/0001-CVE-2018-10919-security-Move-object-specific-access-.patch
new file mode 100644
index 0000000000..0e4fa8be91
--- /dev/null
+++ b/main/samba/0001-CVE-2018-10919-security-Move-object-specific-access-.patch
@@ -0,0 +1,145 @@
+From 12f97f9f69d3ace751c9b49f739aecc4e452dd35 Mon Sep 17 00:00:00 2001
+From: Tim Beale <timbeale@catalyst.net.nz>
+Date: Thu, 19 Jul 2018 16:03:36 +1200
+Subject: [PATCH] CVE-2018-10919 security: Move object-specific access checks
+ into separate function
+
+Object-specific access checks refer to a specific section of the
+MS-ADTS, and the code closely matches the spec. We need to extend this
+logic to properly handle the Control-Access Right (CR), so it makes
+sense to split the logic out into its own function.
+
+This patch just moves the code, and should not alter the logic (apart
+from ading in the boolean grant_access return variable.
+
+BUG: https://bugzilla.samba.org/show_bug.cgi?id=13434
+
+Signed-off-by: Tim Beale <timbeale@catalyst.net.nz>
+---
+ libcli/security/access_check.c | 86 +++++++++++++++++++++++-----------
+ 1 file changed, 59 insertions(+), 27 deletions(-)
+
+diff --git a/libcli/security/access_check.c b/libcli/security/access_check.c
+index b4c850b613e..b4e62441542 100644
+--- a/libcli/security/access_check.c
++++ b/libcli/security/access_check.c
+@@ -374,6 +374,57 @@ static const struct GUID *get_ace_object_type(struct security_ace *ace)
+ return NULL;
+ }
+
++/**
++ * Evaluates access rights specified in a object-specific ACE for an AD object.
++ * This logic corresponds to MS-ADTS 5.1.3.3.3 Checking Object-Specific Access.
++ * @param[in] ace - the ACE being processed
++ * @param[in/out] tree - remaining_access gets updated for the tree
++ * @param[out] grant_access - set to true if the ACE grants sufficient access
++ * rights to the object/attribute
++ * @returns NT_STATUS_OK, unless access was denied
++ */
++static NTSTATUS check_object_specific_access(struct security_ace *ace,
++ struct object_tree *tree,
++ bool *grant_access)
++{
++ struct object_tree *node = NULL;
++ const struct GUID *type = NULL;
++
++ *grant_access = false;
++
++ /*
++ * check only in case we have provided a tree,
++ * the ACE has an object type and that type
++ * is in the tree
++ */
++ type = get_ace_object_type(ace);
++
++ if (!tree) {
++ return NT_STATUS_OK;
++ }
++
++ if (!type) {
++ node = tree;
++ } else {
++ if (!(node = get_object_tree_by_GUID(tree, type))) {
++ return NT_STATUS_OK;
++ }
++ }
++
++ if (ace->type == SEC_ACE_TYPE_ACCESS_ALLOWED_OBJECT) {
++ object_tree_modify_access(node, ace->access_mask);
++ if (node->remaining_access == 0) {
++ *grant_access = true;
++ return NT_STATUS_OK;
++ }
++ } else {
++ if (node->remaining_access & ace->access_mask){
++ return NT_STATUS_ACCESS_DENIED;
++ }
++ }
++ return NT_STATUS_OK;
++}
++
+ /**
+ * @brief Perform directoryservice (DS) related access checks for a given user
+ *
+@@ -405,8 +456,6 @@ NTSTATUS sec_access_check_ds(const struct security_descriptor *sd,
+ {
+ uint32_t i;
+ uint32_t bits_remaining;
+- struct object_tree *node;
+- const struct GUID *type;
+ struct dom_sid self_sid;
+
+ dom_sid_parse(SID_NT_SELF, &self_sid);
+@@ -456,6 +505,8 @@ NTSTATUS sec_access_check_ds(const struct security_descriptor *sd,
+ for (i=0; bits_remaining && i < sd->dacl->num_aces; i++) {
+ struct dom_sid *trustee;
+ struct security_ace *ace = &sd->dacl->aces[i];
++ NTSTATUS status;
++ bool grant_access = false;
+
+ if (ace->flags & SEC_ACE_FLAG_INHERIT_ONLY) {
+ continue;
+@@ -486,34 +537,15 @@ NTSTATUS sec_access_check_ds(const struct security_descriptor *sd,
+ break;
+ case SEC_ACE_TYPE_ACCESS_DENIED_OBJECT:
+ case SEC_ACE_TYPE_ACCESS_ALLOWED_OBJECT:
+- /*
+- * check only in case we have provided a tree,
+- * the ACE has an object type and that type
+- * is in the tree
+- */
+- type = get_ace_object_type(ace);
+-
+- if (!tree) {
+- continue;
+- }
++ status = check_object_specific_access(ace, tree,
++ &grant_access);
+
+- if (!type) {
+- node = tree;
+- } else {
+- if (!(node = get_object_tree_by_GUID(tree, type))) {
+- continue;
+- }
++ if (!NT_STATUS_IS_OK(status)) {
++ return status;
+ }
+
+- if (ace->type == SEC_ACE_TYPE_ACCESS_ALLOWED_OBJECT) {
+- object_tree_modify_access(node, ace->access_mask);
+- if (node->remaining_access == 0) {
+- return NT_STATUS_OK;
+- }
+- } else {
+- if (node->remaining_access & ace->access_mask){
+- return NT_STATUS_ACCESS_DENIED;
+- }
++ if (grant_access) {
++ return NT_STATUS_OK;
+ }
+ break;
+ default: /* Other ACE types not handled/supported */
+--
+2.18.0
+
diff --git a/main/samba/0001-CVE-2018-1139-libcli-auth-Do-not-allow-ntlmv1-over-S.patch b/main/samba/0001-CVE-2018-1139-libcli-auth-Do-not-allow-ntlmv1-over-S.patch
new file mode 100644
index 0000000000..7200342cf3
--- /dev/null
+++ b/main/samba/0001-CVE-2018-1139-libcli-auth-Do-not-allow-ntlmv1-over-S.patch
@@ -0,0 +1,68 @@
+From c25460ee1f1b10bf69eaaf1ac937da225854d1d1 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?G=C3=BCnther=20Deschner?= <gd@samba.org>
+Date: Tue, 13 Mar 2018 16:56:20 +0100
+Subject: [PATCH] CVE-2018-1139 libcli/auth: Do not allow ntlmv1 over SMB1 when
+ it is disabled via "ntlm auth".
+
+This fixes a regression that came in via 00db3aba6cf9ebaafdf39ee2f9c7ba5ec2281ea0.
+
+Found by Vivek Das <vdas@redhat.com> (Red Hat QE).
+
+In order to demonstrate simply run:
+
+smbclient //server/share -U user%password -mNT1 -c quit \
+--option="client ntlmv2 auth"=no \
+--option="client use spnego"=no
+
+against a server that uses "ntlm auth = ntlmv2-only" (our default
+setting).
+
+BUG: https://bugzilla.samba.org/show_bug.cgi?id=13360
+
+CVE-2018-1139: Weak authentication protocol allowed.
+
+Guenther
+
+Pair-Programmed-With: Stefan Metzmacher <metze@samba.org>
+Signed-off-by: Guenther Deschner <gd@samba.org>
+Reviewed-by: Andreas Schneider <asn@samba.org>
+Reviewed-by: Jeremy Allison <jra@samba.org>
+Reviewed-by: Gary Lockyer <gary@catalyst.net.nz>
+---
+ libcli/auth/ntlm_check.c | 2 +-
+ selftest/knownfail | 3 ++-
+ selftest/knownfail.d/ntlm | 2 --
+ 3 files changed, 3 insertions(+), 4 deletions(-)
+ delete mode 100644 selftest/knownfail.d/ntlm
+
+diff --git a/libcli/auth/ntlm_check.c b/libcli/auth/ntlm_check.c
+index 1c6499bd210..b68e9c87888 100644
+--- a/libcli/auth/ntlm_check.c
++++ b/libcli/auth/ntlm_check.c
+@@ -572,7 +572,7 @@ NTSTATUS ntlm_password_check(TALLOC_CTX *mem_ctx,
+ - I think this is related to Win9X pass-though authentication
+ */
+ DEBUG(4,("ntlm_password_check: Checking NT MD4 password in LM field\n"));
+- if (ntlm_auth) {
++ if (ntlm_auth == NTLM_AUTH_ON) {
+ if (smb_pwd_check_ntlmv1(mem_ctx,
+ lm_response,
+ stored_nt->hash, challenge,
+diff --git a/selftest/knownfail b/selftest/knownfail
+index 21ef797ec0f..baf3d57a31a 100644
+--- a/selftest/knownfail
++++ b/selftest/knownfail
+@@ -304,8 +304,9 @@
+ ^samba4.smb.signing.*disabled.*signing=off.*\(ad_dc\)
+ # fl2000dc doesn't support AES
+ ^samba4.krb5.kdc.*as-req-aes.*fl2000dc
+-# nt4_member and ad_member don't support ntlmv1
++# nt4_member and ad_member don't support ntlmv1 (not even over SMB1)
+ ^samba3.blackbox.smbclient_auth.plain.*_member.*option=clientntlmv2auth=no.member.creds.*as.user
++^samba3.blackbox.smbclient_auth.plain.*_member.*option=clientntlmv2auth=no.*mNT1.member.creds.*as.user
+ #nt-vfs server blocks read with execute access
+ ^samba4.smb2.read.access
+ #ntvfs server blocks copychunk with execute access on read handle
+--
+2.18.0
+
diff --git a/main/samba/0002-CVE-2018-10858-libsmb-Harden-smbc_readdir_internal-a.patch b/main/samba/0002-CVE-2018-10858-libsmb-Harden-smbc_readdir_internal-a.patch
new file mode 100644
index 0000000000..0d020cfa72
--- /dev/null
+++ b/main/samba/0002-CVE-2018-10858-libsmb-Harden-smbc_readdir_internal-a.patch
@@ -0,0 +1,158 @@
+From 011d25d5f653246770fa58b7dcff26740369c6ef Mon Sep 17 00:00:00 2001
+From: Jeremy Allison <jra@samba.org>
+Date: Fri, 15 Jun 2018 15:08:17 -0700
+Subject: [PATCH] CVE-2018-10858: libsmb: Harden smbc_readdir_internal()
+ against returns from malicious servers.
+
+BUG: https://bugzilla.samba.org/show_bug.cgi?id=13453
+
+CVE-2018-10858: Insufficient input validation on client directory
+ listing in libsmbclient.
+
+Signed-off-by: Jeremy Allison <jra@samba.org>
+Reviewed-by: Ralph Boehme <slow@samba.org>
+---
+ source3/libsmb/libsmb_dir.c | 57 +++++++++++++++++++++++++++++++-----
+ source3/libsmb/libsmb_path.c | 2 +-
+ 2 files changed, 51 insertions(+), 8 deletions(-)
+
+diff --git a/source3/libsmb/libsmb_dir.c b/source3/libsmb/libsmb_dir.c
+index 72441c46736..54c2bcb3c73 100644
+--- a/source3/libsmb/libsmb_dir.c
++++ b/source3/libsmb/libsmb_dir.c
+@@ -943,27 +943,47 @@ SMBC_closedir_ctx(SMBCCTX *context,
+
+ }
+
+-static void
++static int
+ smbc_readdir_internal(SMBCCTX * context,
+ struct smbc_dirent *dest,
+ struct smbc_dirent *src,
+ int max_namebuf_len)
+ {
+ if (smbc_getOptionUrlEncodeReaddirEntries(context)) {
++ int remaining_len;
+
+ /* url-encode the name. get back remaining buffer space */
+- max_namebuf_len =
++ remaining_len =
+ smbc_urlencode(dest->name, src->name, max_namebuf_len);
+
++ /* -1 means no null termination. */
++ if (remaining_len < 0) {
++ return -1;
++ }
++
+ /* We now know the name length */
+ dest->namelen = strlen(dest->name);
+
++ if (dest->namelen + 1 < 1) {
++ /* Integer wrap. */
++ return -1;
++ }
++
++ if (dest->namelen + 1 >= max_namebuf_len) {
++ /* Out of space for comment. */
++ return -1;
++ }
++
+ /* Save the pointer to the beginning of the comment */
+ dest->comment = dest->name + dest->namelen + 1;
+
++ if (remaining_len < 1) {
++ /* No room for comment null termination. */
++ return -1;
++ }
++
+ /* Copy the comment */
+- strncpy(dest->comment, src->comment, max_namebuf_len - 1);
+- dest->comment[max_namebuf_len - 1] = '\0';
++ strlcpy(dest->comment, src->comment, remaining_len);
+
+ /* Save other fields */
+ dest->smbc_type = src->smbc_type;
+@@ -973,10 +993,21 @@ smbc_readdir_internal(SMBCCTX * context,
+ } else {
+
+ /* No encoding. Just copy the entry as is. */
++ if (src->dirlen > max_namebuf_len) {
++ return -1;
++ }
+ memcpy(dest, src, src->dirlen);
++ if (src->namelen + 1 < 1) {
++ /* Integer wrap */
++ return -1;
++ }
++ if (src->namelen + 1 >= max_namebuf_len) {
++ /* Comment off the end. */
++ return -1;
++ }
+ dest->comment = (char *)(&dest->name + src->namelen + 1);
+ }
+-
++ return 0;
+ }
+
+ /*
+@@ -988,6 +1019,7 @@ SMBC_readdir_ctx(SMBCCTX *context,
+ SMBCFILE *dir)
+ {
+ int maxlen;
++ int ret;
+ struct smbc_dirent *dirp, *dirent;
+ TALLOC_CTX *frame = talloc_stackframe();
+
+@@ -1037,7 +1069,12 @@ SMBC_readdir_ctx(SMBCCTX *context,
+ dirp = &context->internal->dirent;
+ maxlen = sizeof(context->internal->_dirent_name);
+
+- smbc_readdir_internal(context, dirp, dirent, maxlen);
++ ret = smbc_readdir_internal(context, dirp, dirent, maxlen);
++ if (ret == -1) {
++ errno = EINVAL;
++ TALLOC_FREE(frame);
++ return NULL;
++ }
+
+ dir->dir_next = dir->dir_next->next;
+
+@@ -1095,6 +1132,7 @@ SMBC_getdents_ctx(SMBCCTX *context,
+ */
+
+ while ((dirlist = dir->dir_next)) {
++ int ret;
+ struct smbc_dirent *dirent;
+ struct smbc_dirent *currentEntry = (struct smbc_dirent *)ndir;
+
+@@ -1109,8 +1147,13 @@ SMBC_getdents_ctx(SMBCCTX *context,
+ /* Do urlencoding of next entry, if so selected */
+ dirent = &context->internal->dirent;
+ maxlen = sizeof(context->internal->_dirent_name);
+- smbc_readdir_internal(context, dirent,
++ ret = smbc_readdir_internal(context, dirent,
+ dirlist->dirent, maxlen);
++ if (ret == -1) {
++ errno = EINVAL;
++ TALLOC_FREE(frame);
++ return -1;
++ }
+
+ reqd = dirent->dirlen;
+
+diff --git a/source3/libsmb/libsmb_path.c b/source3/libsmb/libsmb_path.c
+index ed70ab37550..5b53b386a67 100644
+--- a/source3/libsmb/libsmb_path.c
++++ b/source3/libsmb/libsmb_path.c
+@@ -173,7 +173,7 @@ smbc_urlencode(char *dest,
+ }
+ }
+
+- if (max_dest_len == 0) {
++ if (max_dest_len <= 0) {
+ /* Ensure we return -1 if no null termination. */
+ return -1;
+ }
+--
+2.18.0
+
diff --git a/main/samba/0002-CVE-2018-10919-security-Add-more-comments-to-the-obj.patch b/main/samba/0002-CVE-2018-10919-security-Add-more-comments-to-the-obj.patch
new file mode 100644
index 0000000000..f500faea47
--- /dev/null
+++ b/main/samba/0002-CVE-2018-10919-security-Add-more-comments-to-the-obj.patch
@@ -0,0 +1,78 @@
+From 81865e8584a0f597650a9df31d49bad3e7549d26 Mon Sep 17 00:00:00 2001
+From: Tim Beale <timbeale@catalyst.net.nz>
+Date: Fri, 20 Jul 2018 13:13:50 +1200
+Subject: [PATCH] CVE-2018-10919 security: Add more comments to the
+ object-specific access checks
+
+Reading the spec and then reading the code makes sense, but we could
+comment the code more so it makes sense on its own.
+
+BUG: https://bugzilla.samba.org/show_bug.cgi?id=13434
+
+Signed-off-by: Tim Beale <timbeale@catalyst.net.nz>
+---
+ libcli/security/access_check.c | 30 ++++++++++++++++++++++--------
+ 1 file changed, 22 insertions(+), 8 deletions(-)
+
+diff --git a/libcli/security/access_check.c b/libcli/security/access_check.c
+index b4e62441542..93eb85def91 100644
+--- a/libcli/security/access_check.c
++++ b/libcli/security/access_check.c
+@@ -392,32 +392,46 @@ static NTSTATUS check_object_specific_access(struct security_ace *ace,
+
+ *grant_access = false;
+
+- /*
+- * check only in case we have provided a tree,
+- * the ACE has an object type and that type
+- * is in the tree
+- */
+- type = get_ace_object_type(ace);
+-
++ /* if no tree was supplied, we can't do object-specific access checks */
+ if (!tree) {
+ return NT_STATUS_OK;
+ }
+
++ /* Get the ObjectType GUID this ACE applies to */
++ type = get_ace_object_type(ace);
++
++ /*
++ * If the ACE doesn't have a type, then apply it to the whole tree, i.e.
++ * treat 'OA' ACEs as 'A' and 'OD' as 'D'
++ */
+ if (!type) {
+ node = tree;
+ } else {
+- if (!(node = get_object_tree_by_GUID(tree, type))) {
++
++ /* skip it if the ACE's ObjectType GUID is not in the tree */
++ node = get_object_tree_by_GUID(tree, type);
++ if (!node) {
+ return NT_STATUS_OK;
+ }
+ }
+
+ if (ace->type == SEC_ACE_TYPE_ACCESS_ALLOWED_OBJECT) {
++
++ /* apply the access rights to this node, and any children */
+ object_tree_modify_access(node, ace->access_mask);
++
++ /*
++ * Currently all nodes in the tree request the same access mask,
++ * so we can use any node to check if processing this ACE now
++ * means the requested access has been granted
++ */
+ if (node->remaining_access == 0) {
+ *grant_access = true;
+ return NT_STATUS_OK;
+ }
+ } else {
++
++ /* this ACE denies access to the requested object/attribute */
+ if (node->remaining_access & ace->access_mask){
+ return NT_STATUS_ACCESS_DENIED;
+ }
+--
+2.18.0
+
diff --git a/main/samba/0002-CVE-2018-1139-libcli-auth-fix-debug-messages-in-hash.patch b/main/samba/0002-CVE-2018-1139-libcli-auth-fix-debug-messages-in-hash.patch
new file mode 100644
index 0000000000..fa0de101fa
--- /dev/null
+++ b/main/samba/0002-CVE-2018-1139-libcli-auth-fix-debug-messages-in-hash.patch
@@ -0,0 +1,62 @@
+From 1d89fe91a7336950b1ba84b8680f015e228047f5 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?G=C3=BCnther=20Deschner?= <gd@samba.org>
+Date: Wed, 14 Mar 2018 15:36:05 +0100
+Subject: [PATCH 1/1] CVE-2018-1139 libcli/auth: fix debug messages in
+ hash_password_check()
+
+BUG: https://bugzilla.samba.org/show_bug.cgi?id=13360
+
+CVE-2018-1139: Weak authentication protocol allowed.
+
+Guenther
+
+Signed-off-by: Guenther Deschner <gd@samba.org>
+Reviewed-by: Andreas Schneider <asn@samba.org>
+Reviewed-by: Jeremy Allison <jra@samba.org>
+Reviewed-by: Gary Lockyer <gary@catalyst.net.nz>
+---
+ libcli/auth/ntlm_check.c | 8 ++++----
+ 1 file changed, 4 insertions(+), 4 deletions(-)
+
+diff --git a/libcli/auth/ntlm_check.c b/libcli/auth/ntlm_check.c
+index 3b02adc1d48..1c6499bd210 100644
+--- a/libcli/auth/ntlm_check.c
++++ b/libcli/auth/ntlm_check.c
+@@ -224,7 +224,7 @@ NTSTATUS hash_password_check(TALLOC_CTX *mem_ctx,
+ const struct samr_Password *stored_nt)
+ {
+ if (stored_nt == NULL) {
+- DEBUG(3,("ntlm_password_check: NO NT password stored for user %s.\n",
++ DEBUG(3,("hash_password_check: NO NT password stored for user %s.\n",
+ username));
+ }
+
+@@ -232,14 +232,14 @@ NTSTATUS hash_password_check(TALLOC_CTX *mem_ctx,
+ if (memcmp(client_nt->hash, stored_nt->hash, sizeof(stored_nt->hash)) == 0) {
+ return NT_STATUS_OK;
+ } else {
+- DEBUG(3,("ntlm_password_check: Interactive logon: NT password check failed for user %s\n",
++ DEBUG(3,("hash_password_check: Interactive logon: NT password check failed for user %s\n",
+ username));
+ return NT_STATUS_WRONG_PASSWORD;
+ }
+
+ } else if (client_lanman && stored_lanman) {
+ if (!lanman_auth) {
+- DEBUG(3,("ntlm_password_check: Interactive logon: only LANMAN password supplied for user %s, and LM passwords are disabled!\n",
++ DEBUG(3,("hash_password_check: Interactive logon: only LANMAN password supplied for user %s, and LM passwords are disabled!\n",
+ username));
+ return NT_STATUS_WRONG_PASSWORD;
+ }
+@@ -250,7 +250,7 @@ NTSTATUS hash_password_check(TALLOC_CTX *mem_ctx,
+ if (memcmp(client_lanman->hash, stored_lanman->hash, sizeof(stored_lanman->hash)) == 0) {
+ return NT_STATUS_OK;
+ } else {
+- DEBUG(3,("ntlm_password_check: Interactive logon: LANMAN password check failed for user %s\n",
++ DEBUG(3,("hash_password_check: Interactive logon: LANMAN password check failed for user %s\n",
+ username));
+ return NT_STATUS_WRONG_PASSWORD;
+ }
+--
+2.18.0
+
diff --git a/main/samba/0003-CVE-2018-10919-tests-Add-tests-for-guessing-confiden.patch b/main/samba/0003-CVE-2018-10919-tests-Add-tests-for-guessing-confiden.patch
new file mode 100644
index 0000000000..9798e6e9fd
--- /dev/null
+++ b/main/samba/0003-CVE-2018-10919-tests-Add-tests-for-guessing-confiden.patch
@@ -0,0 +1,996 @@
+From 49920e7b218770433708cd5889bbf1f9b51d30c0 Mon Sep 17 00:00:00 2001
+From: Tim Beale <timbeale@catalyst.net.nz>
+Date: Mon, 9 Jul 2018 15:57:59 +1200
+Subject: [PATCH] CVE-2018-10919 tests: Add tests for guessing confidential
+ attributes
+
+Adds tests that assert that a confidential attribute cannot be guessed
+by an unprivileged user through wildcard DB searches.
+
+The tests basically consist of a set of DB searches/assertions that
+get run for:
+- basic searches against a confidential attribute
+- confidential attributes that get overridden by giving access to the
+ user via an ACE (run against a variety of ACEs)
+- protecting a non-confidential attribute via an ACL that denies read-
+ access (run against a variety of ACEs)
+- querying confidential attributes via the dirsync controls
+
+These tests all pass when run against a Windows Dc and all fail against
+a Samba DC.
+
+BUG: https://bugzilla.samba.org/show_bug.cgi?id=13434
+
+Signed-off-by: Tim Beale <timbeale@catalyst.net.nz>
+---
+ selftest/knownfail.d/confidential_attr | 15 +
+ .../dsdb/tests/python/confidential_attr.py | 920 ++++++++++++++++++
+ source4/selftest/tests.py | 3 +
+ 3 files changed, 938 insertions(+)
+ create mode 100644 selftest/knownfail.d/confidential_attr
+ create mode 100755 source4/dsdb/tests/python/confidential_attr.py
+
+diff --git a/selftest/knownfail.d/confidential_attr b/selftest/knownfail.d/confidential_attr
+new file mode 100644
+index 00000000000..7a2f2aada57
+--- /dev/null
++++ b/selftest/knownfail.d/confidential_attr
+@@ -0,0 +1,15 @@
++samba4.ldap.confidential_attr.python\(ad_dc_ntvfs\).__main__.ConfidentialAttrTest.test_basic_search\(ad_dc_ntvfs\)
++samba4.ldap.confidential_attr.python\(ad_dc_ntvfs\).__main__.ConfidentialAttrTest.test_search_with_acl_override\(ad_dc_ntvfs\)
++samba4.ldap.confidential_attr.python\(ad_dc_ntvfs\).__main__.ConfidentialAttrTest.test_search_with_attr_acl_override\(ad_dc_ntvfs\)
++samba4.ldap.confidential_attr.python\(ad_dc_ntvfs\).__main__.ConfidentialAttrTest.test_search_with_blanket_oa_acl\(ad_dc_ntvfs\)
++samba4.ldap.confidential_attr.python\(ad_dc_ntvfs\).__main__.ConfidentialAttrTest.test_search_with_neutral_acl\(ad_dc_ntvfs\)
++samba4.ldap.confidential_attr.python\(ad_dc_ntvfs\).__main__.ConfidentialAttrTest.test_search_with_neutral_acl\(ad_dc_ntvfs\)
++samba4.ldap.confidential_attr.python\(ad_dc_ntvfs\).__main__.ConfidentialAttrTest.test_search_with_neutral_attr_acl\(ad_dc_ntvfs\)
++samba4.ldap.confidential_attr.python\(ad_dc_ntvfs\).__main__.ConfidentialAttrTest.test_search_with_neutral_cr_acl\(ad_dc_ntvfs\)
++samba4.ldap.confidential_attr.python\(ad_dc_ntvfs\).__main__.ConfidentialAttrTest.test_search_with_propset_acl_override\(ad_dc_ntvfs\)
++samba4.ldap.confidential_attr.python\(ad_dc_ntvfs\).__main__.ConfidentialAttrTestDenyAcl.test_search_with_blanket_oa_deny_acl\(ad_dc_ntvfs\)
++samba4.ldap.confidential_attr.python\(ad_dc_ntvfs\).__main__.ConfidentialAttrTestDenyAcl.test_search_with_deny_acl\(ad_dc_ntvfs\)
++samba4.ldap.confidential_attr.python\(ad_dc_ntvfs\).__main__.ConfidentialAttrTestDenyAcl.test_search_with_deny_attr_acl\(ad_dc_ntvfs\)
++samba4.ldap.confidential_attr.python\(ad_dc_ntvfs\).__main__.ConfidentialAttrTestDenyAcl.test_search_with_deny_propset_acl\(ad_dc_ntvfs\)
++samba4.ldap.confidential_attr.python\(ad_dc_ntvfs\).__main__.ConfidentialAttrTestDirsync.test_search_with_dirsync\(ad_dc_ntvfs\)
++
+diff --git a/source4/dsdb/tests/python/confidential_attr.py b/source4/dsdb/tests/python/confidential_attr.py
+new file mode 100755
+index 00000000000..c3909d5fc2e
+--- /dev/null
++++ b/source4/dsdb/tests/python/confidential_attr.py
+@@ -0,0 +1,920 @@
++#!/usr/bin/env python
++# -*- coding: utf-8 -*-
++#
++# Tests that confidential attributes (or attributes protected by a ACL that
++# denies read access) cannot be guessed through wildcard DB searches.
++#
++# Copyright (C) Catalyst.Net Ltd
++#
++# 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
++sys.path.insert(0, "bin/python")
++
++import samba
++import os
++from samba.tests.subunitrun import SubunitOptions, TestProgram
++import samba.getopt as options
++from ldb import SCOPE_BASE, SCOPE_SUBTREE
++from samba.dsdb import SEARCH_FLAG_CONFIDENTIAL
++from ldb import Message, MessageElement, Dn
++from ldb import FLAG_MOD_REPLACE, FLAG_MOD_ADD
++from samba.auth import system_session
++from samba import gensec, sd_utils
++from samba.samdb import SamDB
++from samba.credentials import Credentials, DONT_USE_KERBEROS
++import samba.tests
++from samba.tests import delete_force
++import samba.dsdb
++
++parser = optparse.OptionParser("confidential_attr.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)
++
++host = args[0]
++if "://" not in host:
++ ldaphost = "ldap://%s" % host
++else:
++ ldaphost = host
++ start = host.rindex("://")
++ host = host.lstrip(start + 3)
++
++lp = sambaopts.get_loadparm()
++creds = credopts.get_credentials(lp)
++creds.set_gensec_features(creds.get_gensec_features() | gensec.FEATURE_SEAL)
++
++# When a user does not have access rights to view the objects' attributes,
++# Windows and Samba behave slightly differently.
++# A windows DC will always act as if the hidden attribute doesn't exist AT ALL
++# (for an unprivileged user). So, even for a user that lacks access rights,
++# the inverse/'!' queries should return ALL objects. This is similar to the
++# kludgeaclredacted behaviour on Samba.
++# However, on Samba (for implementation simplicity) we never return a matching
++# result for an unprivileged user.
++# Either approach is OK, so long as it gets applied consistently and we don't
++# disclose any sensitive details by varying what gets returned by the search.
++DC_MODE_RETURN_NONE = 0
++DC_MODE_RETURN_ALL = 1
++
++#
++# Tests start here
++#
++class ConfidentialAttrCommon(samba.tests.TestCase):
++
++ def setUp(self):
++ super(ConfidentialAttrCommon, self).setUp()
++
++ self.ldb_admin = SamDB(ldaphost, credentials=creds,
++ session_info=system_session(lp), lp=lp)
++ self.user_pass = "samba123@"
++ self.base_dn = self.ldb_admin.domain_dn()
++ self.schema_dn = self.ldb_admin.get_schema_basedn()
++ self.sd_utils = sd_utils.SDUtils(self.ldb_admin)
++
++ # the tests work by setting the 'Confidential' bit in the searchFlags
++ # for an existing schema attribute. This only works against Windows if
++ # the systemFlags does not have FLAG_SCHEMA_BASE_OBJECT set for the
++ # schema attribute being modified. There are only a few attributes that
++ # meet this criteria (most of which only apply to 'user' objects)
++ self.conf_attr = "homePostalAddress"
++ self.conf_attr_cn = "CN=Address-Home"
++ # schemaIdGuid for homePostalAddress:
++ self.conf_attr_guid = "16775781-47f3-11d1-a9c3-0000f80367c1"
++ self.conf_attr_sec_guid = "77b5b886-944a-11d1-aebd-0000f80367c1"
++ self.attr_dn = "{},{}".format(self.conf_attr_cn, self.schema_dn)
++
++ userou = "OU=conf-attr-test"
++ self.ou = "{},{}".format(userou, self.base_dn)
++ self.ldb_admin.create_ou(self.ou)
++
++ # use a common username prefix, so we can use sAMAccountName=CATC-* as
++ # a search filter to only return the users we're interested in
++ self.user_prefix = "catc-"
++
++ # add a test object with this attribute set
++ self.conf_value = "abcdef"
++ self.conf_user = "{}conf-user".format(self.user_prefix)
++ self.ldb_admin.newuser(self.conf_user, self.user_pass, userou=userou)
++ self.conf_dn = self.get_user_dn(self.conf_user)
++ self.add_attr(self.conf_dn, self.conf_attr, self.conf_value)
++
++ # add a sneaky user that will try to steal our secrets
++ self.user = "{}sneaky-user".format(self.user_prefix)
++ self.ldb_admin.newuser(self.user, self.user_pass, userou=userou)
++ self.ldb_user = self.get_ldb_connection(self.user, self.user_pass)
++
++ self.all_users = [self.user, self.conf_user]
++
++ # add some other users that also have confidential attributes, so we can
++ # check we don't disclose their details, particularly in '!' searches
++ for i in range(1, 3):
++ username = "{}other-user{}".format(self.user_prefix, i)
++ self.ldb_admin.newuser(username, self.user_pass, userou=userou)
++ userdn = self.get_user_dn(username)
++ self.add_attr(userdn, self.conf_attr, "xyz{}".format(i))
++ self.all_users.append(username)
++
++ # there are 4 users in the OU, plus the OU itself
++ self.test_dn = self.ou
++ self.total_objects = len(self.all_users) + 1
++ self.objects_with_attr = 3
++
++ # sanity-check the flag is not already set (this'll cause problems if
++ # previous test run didn't clean up properly)
++ search_flags = self.get_attr_search_flags(self.attr_dn)
++ self.assertTrue(int(search_flags) & SEARCH_FLAG_CONFIDENTIAL == 0,
++ "{} searchFlags already {}".format(self.conf_attr,
++ search_flags))
++
++ def tearDown(self):
++ super(ConfidentialAttrCommon, self).tearDown()
++ self.ldb_admin.delete(self.ou, ["tree_delete:1"])
++
++ def add_attr(self, dn, attr, value):
++ m = Message()
++ m.dn = Dn(self.ldb_admin, dn)
++ m[attr] = MessageElement(value, FLAG_MOD_ADD, attr)
++ self.ldb_admin.modify(m)
++
++ def set_schema_update_now(self):
++ ldif = """
++dn:
++changetype: modify
++add: schemaUpdateNow
++schemaUpdateNow: 1
++"""
++ self.ldb_admin.modify_ldif(ldif)
++
++ def set_attr_search_flags(self, attr_dn, flags):
++ """Modifies the searchFlags for an object in the schema"""
++ m = Message()
++ m.dn = Dn(self.ldb_admin, attr_dn)
++ m['searchFlags'] = MessageElement(flags, FLAG_MOD_REPLACE,
++ 'searchFlags')
++ self.ldb_admin.modify(m)
++
++ # note we have to update the schema for this change to take effect (on
++ # Windows, at least)
++ self.set_schema_update_now()
++
++ def get_attr_search_flags(self, attr_dn):
++ """Marks the attribute under test as being confidential"""
++ res = self.ldb_admin.search(attr_dn, scope=SCOPE_BASE,
++ attrs=['searchFlags'])
++ return res[0]['searchFlags'][0]
++
++ def make_attr_confidential(self):
++ """Marks the attribute under test as being confidential"""
++
++ # work out the original 'searchFlags' value before we overwrite it
++ old_value = self.get_attr_search_flags(self.attr_dn)
++
++ self.set_attr_search_flags(self.attr_dn, str(SEARCH_FLAG_CONFIDENTIAL))
++
++ # reset the value after the test completes
++ self.addCleanup(self.set_attr_search_flags, self.attr_dn, old_value)
++
++ # The behaviour of the DC can differ in some cases, depending on whether
++ # we're talking to a Windows DC or a Samba DC
++ def guess_dc_mode(self):
++ # if we're in selftest, we can be pretty sure it's a Samba DC
++ if os.environ.get('SAMBA_SELFTEST') == '1':
++ return DC_MODE_RETURN_NONE
++
++ searches = self.get_negative_match_all_searches()
++ res = self.ldb_user.search(self.test_dn, expression=searches[0],
++ scope=SCOPE_SUBTREE)
++
++ # we default to DC_MODE_RETURN_NONE (samba).Update this if it
++ # looks like we're talking to a Windows DC
++ if len(res) == self.total_objects:
++ return DC_MODE_RETURN_ALL
++
++ # otherwise assume samba DC behaviour
++ return DC_MODE_RETURN_NONE
++
++ def get_user_dn(self, name):
++ return "CN={},{}".format(name, self.ou)
++
++ def get_user_sid_string(self, username):
++ user_dn = self.get_user_dn(username)
++ user_sid = self.sd_utils.get_object_sid(user_dn)
++ return str(user_sid)
++
++ def get_ldb_connection(self, target_username, target_password):
++ creds_tmp = Credentials()
++ creds_tmp.set_username(target_username)
++ creds_tmp.set_password(target_password)
++ creds_tmp.set_domain(creds.get_domain())
++ creds_tmp.set_realm(creds.get_realm())
++ creds_tmp.set_workstation(creds.get_workstation())
++ features = creds_tmp.get_gensec_features() | gensec.FEATURE_SEAL
++ creds_tmp.set_gensec_features(features)
++ creds_tmp.set_kerberos_state(DONT_USE_KERBEROS)
++ ldb_target = SamDB(url=ldaphost, credentials=creds_tmp, lp=lp)
++ return ldb_target
++
++ def assert_not_in_result(self, res, exclude_dn):
++ for msg in res:
++ self.assertNotEqual(msg.dn, exclude_dn,
++ "Search revealed object {}".format(exclude_dn))
++
++ def assert_search_result(self, expected_num, expr, samdb):
++
++ # try asking for different attributes back: None/all, the confidential
++ # attribute itself, and a random unrelated attribute
++ attr_filters = [None, ["*"], [self.conf_attr], ['name']]
++ for attr in attr_filters:
++ res = samdb.search(self.test_dn, expression=expr,
++ scope=SCOPE_SUBTREE, attrs=attr)
++ self.assertTrue(len(res) == expected_num,
++ "%u results, not %u for search %s, attr %s" %
++ (len(res), expected_num, expr, str(attr)))
++
++ # return a selection of searches that match exactly against the test object
++ def get_exact_match_searches(self):
++ first_char = self.conf_value[:1]
++ last_char = self.conf_value[-1:]
++ test_attr = self.conf_attr
++
++ searches = [
++ # search for the attribute using a sub-string wildcard
++ # (which could reveal the attribute's actual value)
++ "({}={}*)".format(test_attr, first_char),
++ "({}=*{})".format(test_attr, last_char),
++
++ # sanity-check equality against an exact match on value
++ "({}={})".format(test_attr, self.conf_value),
++
++ # '~=' searches don't work against Samba
++ # sanity-check an approx search against an exact match on value
++ # "({}~={})".format(test_attr, self.conf_value),
++
++ # check wildcard in an AND search...
++ "(&({}={}*)(objectclass=*))".format(test_attr, first_char),
++
++ # ...an OR search (against another term that will never match)
++ "(|({}={}*)(objectclass=banana))".format(test_attr, first_char)]
++
++ return searches
++
++ # return searches that match any object with the attribute under test
++ def get_match_all_searches(self):
++ searches = [
++ # check a full wildcard against the confidential attribute
++ # (which could reveal the attribute's presence/absence)
++ "({}=*)".format(self.conf_attr),
++
++ # check wildcard in an AND search...
++ "(&(objectclass=*)({}=*))".format(self.conf_attr),
++
++ # ...an OR search (against another term that will never match)
++ "(|(objectclass=banana)({}=*))".format(self.conf_attr),
++
++ # check <=, and >= expressions that would normally find a match
++ "({}>=0)".format(self.conf_attr),
++ "({}<=ZZZZZZZZZZZ)".format(self.conf_attr)]
++
++ return searches
++
++ def assert_conf_attr_searches(self, has_rights_to=0, samdb=None):
++ """Check searches against the attribute under test work as expected"""
++
++ if samdb is None:
++ samdb = self.ldb_user
++
++ if has_rights_to == "all":
++ has_rights_to = self.objects_with_attr
++
++ # these first few searches we just expect to match against the one
++ # object under test that we're trying to guess the value of
++ expected_num = 1 if has_rights_to > 0 else 0
++ for search in self.get_exact_match_searches():
++ self.assert_search_result(expected_num, search, samdb)
++
++ # these next searches will match any objects we have rights to see
++ expected_num = has_rights_to
++ for search in self.get_match_all_searches():
++ self.assert_search_result(expected_num, search, samdb)
++
++ # The following are double negative searches (i.e. NOT non-matching-
++ # condition) which will therefore match ALL objects, including the test
++ # object(s).
++ def get_negative_match_all_searches(self):
++ first_char = self.conf_value[:1]
++ last_char = self.conf_value[-1:]
++ not_first_char = chr(ord(first_char) + 1)
++ not_last_char = chr(ord(last_char) + 1)
++
++ searches = [
++ "(!({}={}*))".format(self.conf_attr, not_first_char),
++ "(!({}=*{}))".format(self.conf_attr, not_last_char)]
++ return searches
++
++ # the following searches will not match against the test object(s). So
++ # a user with sufficient rights will see an inverse sub-set of objects.
++ # (An unprivileged user would either see all objects on Windows, or no
++ # objects on Samba)
++ def get_inverse_match_searches(self):
++ first_char = self.conf_value[:1]
++ last_char = self.conf_value[-1:]
++ searches = [
++ "(!({}={}*))".format(self.conf_attr, first_char),
++ "(!({}=*{}))".format(self.conf_attr, last_char)]
++ return searches
++
++ def negative_searches_all_rights(self, total_objects=None):
++ expected_results = {}
++
++ if total_objects is None:
++ total_objects = self.total_objects
++
++ # these searches should match ALL objects (including the OU)
++ for search in self.get_negative_match_all_searches():
++ expected_results[search] = total_objects
++
++ # a ! wildcard should only match the objects without the attribute
++ search = "(!({}=*))".format(self.conf_attr)
++ expected_results[search] = total_objects - self.objects_with_attr
++
++ # whereas the inverse searches should match all objects *except* the
++ # one under test
++ for search in self.get_inverse_match_searches():
++ expected_results[search] = total_objects - 1
++
++ return expected_results
++
++ # Returns the expected negative (i.e. '!') search behaviour when talking to
++ # a DC with DC_MODE_RETURN_ALL behaviour, i.e. we assert that users
++ # without rights always see ALL objects in '!' searches
++ def negative_searches_return_all(self, has_rights_to=0,
++ total_objects=None):
++ """Asserts user without rights cannot see objects in '!' searches"""
++ expected_results = {}
++
++ if total_objects is None:
++ total_objects = self.total_objects
++
++ # Windows 'hides' objects by always returning all of them, so negative
++ # searches that match all objects will simply return all objects
++ for search in self.get_negative_match_all_searches():
++ expected_results[search] = total_objects
++
++ # if the search is matching on an inverse subset (everything except the
++ # object under test), the
++ inverse_searches = self.get_inverse_match_searches()
++ inverse_searches += ["(!({}=*))".format(self.conf_attr)]
++
++ for search in inverse_searches:
++ expected_results[search] = total_objects - has_rights_to
++
++ return expected_results
++
++ # Returns the expected negative (i.e. '!') search behaviour when talking to
++ # a DC with DC_MODE_RETURN_NONE behaviour, i.e. we assert that users
++ # without rights cannot see objects in '!' searches at all
++ def negative_searches_return_none(self, has_rights_to=0):
++ expected_results = {}
++
++ # the 'match-all' searches should only return the objects we have
++ # access rights to (if any)
++ for search in self.get_negative_match_all_searches():
++ expected_results[search] = has_rights_to
++
++ # for inverse matches, we should NOT be told about any objects at all
++ inverse_searches = self.get_inverse_match_searches()
++ inverse_searches += ["(!({}=*))".format(self.conf_attr)]
++ for search in inverse_searches:
++ expected_results[search] = 0
++
++ return expected_results
++
++ # Returns the expected negative (i.e. '!') search behaviour. This varies
++ # depending on what type of DC we're talking to (i.e. Windows or Samba)
++ # and what access rights the user has
++ def negative_search_expected_results(self, has_rights_to, dc_mode,
++ total_objects=None):
++
++ if has_rights_to == "all":
++ expect_results = self.negative_searches_all_rights(total_objects)
++
++ # if it's a Samba DC, we only expect the 'match-all' searches to return
++ # the objects that we have access rights to (all others are hidden).
++ # Whereas Windows 'hides' the objects by always returning all of them
++ elif dc_mode == DC_MODE_RETURN_NONE:
++ expect_results = self.negative_searches_return_none(has_rights_to)
++ else:
++ expect_results = self.negative_searches_return_all(has_rights_to,
++ total_objects)
++ return expect_results
++
++ def assert_negative_searches(self, has_rights_to=0,
++ dc_mode=DC_MODE_RETURN_NONE, samdb=None):
++ """Asserts user without rights cannot see objects in '!' searches"""
++
++ if samdb is None:
++ samdb = self.ldb_user
++
++ # build a dictionary of key=search-expr, value=expected_num assertions
++ expected_results = self.negative_search_expected_results(has_rights_to,
++ dc_mode)
++
++ for search, expected_num in expected_results.items():
++ self.assert_search_result(expected_num, search, samdb)
++
++ def assert_attr_returned(self, expect_attr, samdb, attrs):
++ # does a query that should always return a successful result, and
++ # checks whether the confidential attribute is present
++ res = samdb.search(self.conf_dn, expression="(objectClass=*)",
++ scope=SCOPE_SUBTREE, attrs=attrs)
++ self.assertTrue(len(res) == 1)
++
++ attr_returned = False
++ for msg in res:
++ if self.conf_attr in msg:
++ attr_returned = True
++ self.assertEqual(expect_attr, attr_returned)
++
++ def assert_attr_visible(self, expect_attr, samdb=None):
++ if samdb is None:
++ samdb = self.ldb_user
++
++ # sanity-check confidential attribute is/isn't returned as expected
++ # based on the filter attributes we ask for
++ self.assert_attr_returned(expect_attr, samdb, attrs=None)
++ self.assert_attr_returned(expect_attr, samdb, attrs=["*"])
++ self.assert_attr_returned(expect_attr, samdb, attrs=[self.conf_attr])
++
++ # filtering on a different attribute should never return the conf_attr
++ self.assert_attr_returned(expect_attr=False, samdb=samdb,
++ attrs=['name'])
++
++ def assert_attr_visible_to_admin(self):
++ # sanity-check the admin user can always see the confidential attribute
++ self.assert_conf_attr_searches(has_rights_to="all", samdb=self.ldb_admin)
++ self.assert_negative_searches(has_rights_to="all", samdb=self.ldb_admin)
++ self.assert_attr_visible(expect_attr=True, samdb=self.ldb_admin)
++
++
++class ConfidentialAttrTest(ConfidentialAttrCommon):
++ def test_basic_search(self):
++ """Basic test confidential attributes aren't disclosed via searches"""
++
++ # check we can see a non-confidential attribute in a basic searches
++ self.assert_conf_attr_searches(has_rights_to="all")
++ self.assert_negative_searches(has_rights_to="all")
++ self.assert_attr_visible(expect_attr=True)
++
++ # now make the attribute confidential. Repeat the tests and check that
++ # an ordinary user can't see the attribute, or indirectly match on the
++ # attribute via the search expression
++ self.make_attr_confidential()
++
++ self.assert_conf_attr_searches(has_rights_to=0)
++ dc_mode = self.guess_dc_mode()
++ self.assert_negative_searches(has_rights_to=0, dc_mode=dc_mode)
++ self.assert_attr_visible(expect_attr=False)
++
++ # sanity-check we haven't hidden the attribute from the admin as well
++ self.assert_attr_visible_to_admin()
++
++ def _test_search_with_allow_acl(self, allow_ace):
++ """Checks a ACE with 'CR' rights can override a confidential attr"""
++ # make the test attribute confidential and check user can't see it
++ self.make_attr_confidential()
++
++ self.assert_conf_attr_searches(has_rights_to=0)
++ dc_mode = self.guess_dc_mode()
++ self.assert_negative_searches(has_rights_to=0, dc_mode=dc_mode)
++ self.assert_attr_visible(expect_attr=False)
++
++ # apply the allow ACE to the object under test
++ self.sd_utils.dacl_add_ace(self.conf_dn, allow_ace)
++
++ # the user should now be able to see the attribute for the one object
++ # we gave it rights to
++ self.assert_conf_attr_searches(has_rights_to=1)
++ self.assert_negative_searches(has_rights_to=1, dc_mode=dc_mode)
++ self.assert_attr_visible(expect_attr=True)
++
++ # sanity-check the admin can still see the attribute
++ self.assert_attr_visible_to_admin()
++
++ def test_search_with_attr_acl_override(self):
++ """Make the confidential attr visible via an OA attr ACE"""
++
++ # set the SEC_ADS_CONTROL_ACCESS bit ('CR') for the user for the
++ # attribute under test, so the user can see it once more
++ user_sid = self.get_user_sid_string(self.user)
++ ace = "(OA;;CR;{};;{})".format(self.conf_attr_guid, user_sid)
++
++ self._test_search_with_allow_acl(ace)
++
++ def test_search_with_propset_acl_override(self):
++ """Make the confidential attr visible via a Property-set ACE"""
++
++ # set the SEC_ADS_CONTROL_ACCESS bit ('CR') for the user for the
++ # property-set containing the attribute under test (i.e. the
++ # attributeSecurityGuid), so the user can see it once more
++ user_sid = self.get_user_sid_string(self.user)
++ ace = "(OA;;CR;{};;{})".format(self.conf_attr_sec_guid, user_sid)
++
++ self._test_search_with_allow_acl(ace)
++
++ def test_search_with_acl_override(self):
++ """Make the confidential attr visible via a general 'allow' ACE"""
++
++ # set the allow SEC_ADS_CONTROL_ACCESS bit ('CR') for the user
++ user_sid = self.get_user_sid_string(self.user)
++ ace = "(A;;CR;;;{})".format(user_sid)
++
++ self._test_search_with_allow_acl(ace)
++
++ def test_search_with_blanket_oa_acl(self):
++ """Make the confidential attr visible via a non-specific OA ACE"""
++
++ # this just checks that an Object Access (OA) ACE without a GUID
++ # specified will work the same as an 'Access' (A) ACE
++ user_sid = self.get_user_sid_string(self.user)
++ ace = "(OA;;CR;;;{})".format(user_sid)
++
++ self._test_search_with_allow_acl(ace)
++
++ def _test_search_with_neutral_acl(self, neutral_ace):
++ """Checks that a user does NOT gain access via an unrelated ACE"""
++
++ # make the test attribute confidential and check user can't see it
++ self.make_attr_confidential()
++
++ self.assert_conf_attr_searches(has_rights_to=0)
++ dc_mode = self.guess_dc_mode()
++ self.assert_negative_searches(has_rights_to=0, dc_mode=dc_mode)
++ self.assert_attr_visible(expect_attr=False)
++
++ # apply the ACE to the object under test
++ self.sd_utils.dacl_add_ace(self.conf_dn, neutral_ace)
++
++ # this should make no difference to the user's ability to see the attr
++ self.assert_conf_attr_searches(has_rights_to=0)
++ self.assert_negative_searches(has_rights_to=0, dc_mode=dc_mode)
++ self.assert_attr_visible(expect_attr=False)
++
++ # sanity-check the admin can still see the attribute
++ self.assert_attr_visible_to_admin()
++
++ def test_search_with_neutral_acl(self):
++ """Give the user all rights *except* CR for any attributes"""
++
++ # give the user all rights *except* CR and check it makes no difference
++ user_sid = self.get_user_sid_string(self.user)
++ ace = "(A;;RPWPCCDCLCLORCWOWDSDDTSW;;;{})".format(user_sid)
++ self._test_search_with_neutral_acl(ace)
++
++ def test_search_with_neutral_attr_acl(self):
++ """Give the user all rights *except* CR for the attribute under test"""
++
++ # giving user all OA rights *except* CR should make no difference
++ user_sid = self.get_user_sid_string(self.user)
++ rights = "RPWPCCDCLCLORCWOWDSDDTSW"
++ ace = "(OA;;{};{};;{})".format(rights, self.conf_attr_guid, user_sid)
++ self._test_search_with_neutral_acl(ace)
++
++ def test_search_with_neutral_cr_acl(self):
++ """Give the user CR rights for *another* unrelated attribute"""
++
++ # giving user object-access CR rights to an unrelated attribute
++ user_sid = self.get_user_sid_string(self.user)
++ # use the GUID for sAMAccountName here (for no particular reason)
++ unrelated_attr = "3e0abfd0-126a-11d0-a060-00aa006c33ed"
++ ace = "(OA;;CR;{};;{})".format(unrelated_attr, user_sid)
++ self._test_search_with_neutral_acl(ace)
++
++
++# Check that a Deny ACL on an attribute doesn't reveal confidential info
++class ConfidentialAttrTestDenyAcl(ConfidentialAttrCommon):
++
++ def assert_not_in_result(self, res, exclude_dn):
++ for msg in res:
++ self.assertNotEqual(msg.dn, exclude_dn,
++ "Search revealed object {}".format(exclude_dn))
++
++ # deny ACL tests are slightly different as we are only denying access to
++ # the one object under test (rather than any objects with that attribute).
++ # Therefore we need an extra check that we don't reveal the test object
++ # in the search, if we're not supposed to
++ def assert_search_result(self, expected_num, expr, samdb,
++ excl_testobj=False):
++
++ # try asking for different attributes back: None/all, the confidential
++ # attribute itself, and a random unrelated attribute
++ attr_filters = [None, ["*"], [self.conf_attr], ['name']]
++ for attr in attr_filters:
++ res = samdb.search(self.test_dn, expression=expr,
++ scope=SCOPE_SUBTREE, attrs=attr)
++ self.assertTrue(len(res) == expected_num,
++ "%u results, not %u for search %s, attr %s" %
++ (len(res), expected_num, expr, str(attr)))
++
++ # assert we haven't revealed the hidden test-object
++ if excl_testobj:
++ self.assert_not_in_result(res, exclude_dn=self.conf_dn)
++
++ # we make a few tweaks to the regular version of this function to cater to
++ # denying specifically one object via an ACE
++ def assert_conf_attr_searches(self, has_rights_to=0, samdb=None):
++ """Check searches against the attribute under test work as expected"""
++
++ if samdb is None:
++ samdb = self.ldb_user
++
++ # make sure the test object is not returned if we've been denied rights
++ # to it via an ACE
++ excl_testobj = True if has_rights_to == "deny-one" else False
++
++ # these first few searches we just expect to match against the one
++ # object under test that we're trying to guess the value of
++ expected_num = 1 if has_rights_to == "all" else 0
++
++ for search in self.get_exact_match_searches():
++ self.assert_search_result(expected_num, search, samdb,
++ excl_testobj)
++
++ # these next searches will match any objects with the attribute that
++ # we have rights to see (i.e. all except the object under test)
++ if has_rights_to == "all":
++ expected_num = self.objects_with_attr
++ elif has_rights_to == "deny-one":
++ expected_num = self.objects_with_attr - 1
++
++ for search in self.get_match_all_searches():
++ self.assert_search_result(expected_num, search, samdb,
++ excl_testobj)
++
++ def negative_searches_return_none(self, has_rights_to=0):
++ expected_results = {}
++
++ # on Samba we will see the objects we have rights to, but the one we
++ # are denied access to will be hidden
++ searches = self.get_negative_match_all_searches()
++ searches += self.get_inverse_match_searches()
++ for search in searches:
++ expected_results[search] = self.total_objects - 1
++
++ # The wildcard returns the objects without this attribute as normal.
++ search = "(!({}=*))".format(self.conf_attr)
++ expected_results[search] = self.total_objects - self.objects_with_attr
++ return expected_results
++
++ def negative_searches_return_all(self, has_rights_to=0,
++ total_objects=None):
++ expected_results = {}
++
++ # When a user lacks access rights to an object, Windows 'hides' it in
++ # '!' searches by always returning it, regardless of whether it matches
++ searches = self.get_negative_match_all_searches()
++ searches += self.get_inverse_match_searches()
++ for search in searches:
++ expected_results[search] = self.total_objects
++
++ # in the wildcard case, the one object we don't have rights to gets
++ # bundled in with the objects that don't have the attribute at all
++ search = "(!({}=*))".format(self.conf_attr)
++ has_rights_to = self.objects_with_attr - 1
++ expected_results[search] = self.total_objects - has_rights_to
++ return expected_results
++
++ def assert_negative_searches(self, has_rights_to=0,
++ dc_mode=DC_MODE_RETURN_NONE, samdb=None):
++ """Asserts user without rights cannot see objects in '!' searches"""
++
++ if samdb is None:
++ samdb = self.ldb_user
++
++ # As the deny ACL is only denying access to one particular object, add
++ # an extra check that the denied object is not returned. (We can only
++ # assert this if the '!'/negative search behaviour is to suppress any
++ # objects we don't have access rights to)
++ excl_testobj = False
++ if has_rights_to != "all" and dc_mode == DC_MODE_RETURN_NONE:
++ excl_testobj = True
++
++ # build a dictionary of key=search-expr, value=expected_num assertions
++ expected_results = self.negative_search_expected_results(has_rights_to,
++ dc_mode)
++
++ for search, expected_num in expected_results.items():
++ self.assert_search_result(expected_num, search, samdb,
++ excl_testobj=excl_testobj)
++
++ def _test_search_with_deny_acl(self, ace):
++ # check the user can see the attribute initially
++ self.assert_conf_attr_searches(has_rights_to="all")
++ self.assert_negative_searches(has_rights_to="all")
++ self.assert_attr_visible(expect_attr=True)
++
++ # add the ACE that denies access to the attr under test
++ self.sd_utils.dacl_add_ace(self.conf_dn, ace)
++
++ # the user shouldn't be able to see the attribute anymore
++ self.assert_conf_attr_searches(has_rights_to="deny-one")
++ dc_mode = self.guess_dc_mode()
++ self.assert_negative_searches(has_rights_to="deny-one",
++ dc_mode=dc_mode)
++ self.assert_attr_visible(expect_attr=False)
++
++ # sanity-check we haven't hidden the attribute from the admin as well
++ self.assert_attr_visible_to_admin()
++
++ def test_search_with_deny_attr_acl(self):
++ """Checks a deny ACE works the same way as a confidential attribute"""
++
++ # add an ACE that denies the user Read Property (RP) access to the attr
++ # (which is similar to making the attribute confidential)
++ user_sid = self.get_user_sid_string(self.user)
++ ace = "(OD;;RP;{};;{})".format(self.conf_attr_guid, user_sid)
++
++ # check the user cannot see the attribute anymore
++ self._test_search_with_deny_acl(ace)
++
++ def test_search_with_deny_acl(self):
++ """Checks a blanket deny ACE denies access to an object's attributes"""
++
++ # add an blanket deny ACE for Read Property (RP) rights
++ user_dn = self.get_user_dn(self.user)
++ user_sid = self.sd_utils.get_object_sid(user_dn)
++ ace = "(D;;RP;;;{})".format(str(user_sid))
++
++ # check the user cannot see the attribute anymore
++ self._test_search_with_deny_acl(ace)
++
++ def test_search_with_deny_propset_acl(self):
++ """Checks a deny ACE on the attribute's Property-Set"""
++
++ # add an blanket deny ACE for Read Property (RP) rights
++ user_sid = self.get_user_sid_string(self.user)
++ ace = "(OD;;RP;{};;{})".format(self.conf_attr_sec_guid, user_sid)
++
++ # check the user cannot see the attribute anymore
++ self._test_search_with_deny_acl(ace)
++
++ def test_search_with_blanket_oa_deny_acl(self):
++ """Checks a non-specific 'OD' ACE works the same as a 'D' ACE"""
++
++ # this just checks that adding a 'Object Deny' (OD) ACE without
++ # specifying a GUID will work the same way as a 'Deny' (D) ACE
++ user_sid = self.get_user_sid_string(self.user)
++ ace = "(OD;;RP;;;{})".format(user_sid)
++
++ # check the user cannot see the attribute anymore
++ self._test_search_with_deny_acl(ace)
++
++
++# Check that using the dirsync controls doesn't reveal confidential attributes
++class ConfidentialAttrTestDirsync(ConfidentialAttrCommon):
++
++ def setUp(self):
++ super(ConfidentialAttrTestDirsync, self).setUp()
++ self.dirsync = ["dirsync:1:1:1000"]
++
++ def assert_search_result(self, expected_num, expr, samdb, base_dn=None):
++
++ # Note dirsync must always search on the partition base DN
++ if base_dn is None:
++ base_dn = self.base_dn
++
++ attr_filters = [None, ["*"], ["name"]]
++
++ # Note dirsync behaviour is slighty different for the attribute under
++ # test - when you have full access rights, it only returns the objects
++ # that actually have this attribute (i.e. it doesn't return an empty
++ # message with just the DN). So we add the 'name' attribute into the
++ # attribute filter to avoid complicating our assertions further
++ attr_filters += [[self.conf_attr, "name"]]
++ for attr in attr_filters:
++ res = samdb.search(base_dn, expression=expr, scope=SCOPE_SUBTREE,
++ attrs=attr, controls=self.dirsync)
++
++ self.assertTrue(len(res) == expected_num,
++ "%u results, not %u for search %s, attr %s" %
++ (len(res), expected_num, expr, str(attr)))
++
++
++ def assert_attr_returned(self, expect_attr, samdb, attrs,
++ no_result_ok=False):
++
++ # When using dirsync, the base DN we search on needs to be a naming
++ # context. Add an extra filter to ignore all the objects we aren't
++ # interested in
++ expr = "(&(samaccountname={})(!(isDeleted=*)))".format(self.conf_user)
++ res = samdb.search(self.base_dn, expression=expr, scope=SCOPE_SUBTREE,
++ attrs=attrs, controls=self.dirsync)
++ self.assertTrue(len(res) == 1 or no_result_ok)
++
++ attr_returned = False
++ for msg in res:
++ if self.conf_attr in msg and len(msg[self.conf_attr]) > 0:
++ attr_returned = True
++ self.assertEqual(expect_attr, attr_returned)
++
++ def assert_attr_visible(self, expect_attr, samdb=None):
++ if samdb is None:
++ samdb = self.ldb_user
++
++ # sanity-check confidential attribute is/isn't returned as expected
++ # based on the filter attributes we ask for
++ self.assert_attr_returned(expect_attr, samdb, attrs=None)
++ self.assert_attr_returned(expect_attr, samdb, attrs=["*"])
++
++ if expect_attr:
++ self.assert_attr_returned(expect_attr, samdb,
++ attrs=[self.conf_attr])
++ else:
++ # The behaviour with dirsync when asking solely for an attribute
++ # that you don't have rights to is a bit strange. Samba returns
++ # no result rather than an empty message with just the DN.
++ # Presumably this is due to dirsync module behaviour. It's not
++ # disclosive in that the DC behaves the same way as if you asked
++ # for a garbage/non-existent attribute
++ self.assert_attr_returned(expect_attr, samdb,
++ attrs=[self.conf_attr],
++ no_result_ok=True)
++ self.assert_attr_returned(expect_attr, samdb,
++ attrs=["garbage"], no_result_ok=True)
++
++ # filtering on a different attribute should never return the conf_attr
++ self.assert_attr_returned(expect_attr=False, samdb=samdb,
++ attrs=['name'])
++
++ def assert_negative_searches(self, has_rights_to=0,
++ dc_mode=DC_MODE_RETURN_NONE, samdb=None):
++ """Asserts user without rights cannot see objects in '!' searches"""
++
++ if samdb is None:
++ samdb = self.ldb_user
++
++ # because we need to search on the base DN when using the dirsync
++ # controls, we need an extra filter for the inverse ('!') search,
++ # so we don't get thousands of objects returned
++ extra_filter = \
++ "(samaccountname={}*)(!(isDeleted=*))".format(self.user_prefix)
++
++ # because of this extra filter, the total objects we expect here only
++ # includes the user objects (not the parent OU)
++ total_objects = len(self.all_users)
++ expected_results = self.negative_search_expected_results(has_rights_to,
++ dc_mode,
++ total_objects)
++
++ for search, expected_num in expected_results.items():
++ search = "(&{}{})".format(search, extra_filter)
++ self.assert_search_result(expected_num, search, samdb)
++
++ def test_search_with_dirsync(self):
++ """Checks dirsync controls don't reveal confidential attributes"""
++
++ self.assert_conf_attr_searches(has_rights_to="all")
++ self.assert_attr_visible(expect_attr=True)
++ self.assert_negative_searches(has_rights_to="all")
++
++ # make the test attribute confidential and check user can't see it,
++ # even if they use the dirsync controls
++ self.make_attr_confidential()
++
++ self.assert_conf_attr_searches(has_rights_to=0)
++ self.assert_attr_visible(expect_attr=False)
++ dc_mode = self.guess_dc_mode()
++ self.assert_negative_searches(has_rights_to=0, dc_mode=dc_mode)
++
++ # as a final sanity-check, make sure the admin can still see the attr
++ self.assert_conf_attr_searches(has_rights_to="all",
++ samdb=self.ldb_admin)
++ self.assert_attr_visible(expect_attr=True, samdb=self.ldb_admin)
++ self.assert_negative_searches(has_rights_to="all",
++ samdb=self.ldb_admin)
++
++TestProgram(module=__name__, opts=subunitopts)
+diff --git a/source4/selftest/tests.py b/source4/selftest/tests.py
+index 4efe9630f61..9f2dff0f40c 100755
+--- a/source4/selftest/tests.py
++++ b/source4/selftest/tests.py
+@@ -740,6 +740,9 @@ for env in ["ad_dc_ntvfs", "fl2000dc", "fl2003dc", "fl2008r2dc"]:
+ # therefore skip it in that configuration
+ plantestsuite_loadlist("samba4.ldap.passwords.python(%s)" % env, env, [python, os.path.join(samba4srcdir, "dsdb/tests/python/passwords.py"), "$SERVER", '-U"$USERNAME%$PASSWORD"', "-W$DOMAIN", '$LOADLIST', '$LISTOPT'])
+
++env = "ad_dc_ntvfs"
++plantestsuite_loadlist("samba4.ldap.confidential_attr.python(%s)" % env, env, [python, os.path.join(samba4srcdir, "dsdb/tests/python/confidential_attr.py"), '$SERVER', '-U"$USERNAME%$PASSWORD"', '--workgroup=$DOMAIN', '$LOADLIST', '$LISTOPT'])
++
+ for env in ["ad_dc_ntvfs"]:
+ # This test takes a lot of time, so we run it against a minimum of
+ # environments, please only add new ones if there's really a
+--
+2.18.0
+
diff --git a/main/samba/0003-CVE-2018-1139-s3-utils-use-enum-ntlm_auth_level-in-n.patch b/main/samba/0003-CVE-2018-1139-s3-utils-use-enum-ntlm_auth_level-in-n.patch
new file mode 100644
index 0000000000..629587d027
--- /dev/null
+++ b/main/samba/0003-CVE-2018-1139-s3-utils-use-enum-ntlm_auth_level-in-n.patch
@@ -0,0 +1,47 @@
+From 947cf385978d9cae43fe7f59a6dcf3b28dc036f1 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?G=C3=BCnther=20Deschner?= <gd@samba.org>
+Date: Wed, 14 Mar 2018 15:35:01 +0100
+Subject: [PATCH] CVE-2018-1139 s3-utils: use enum ntlm_auth_level in
+ ntlm_password_check().
+
+BUG: https://bugzilla.samba.org/show_bug.cgi?id=13360
+
+CVE-2018-1139: Weak authentication protocol allowed.
+
+Guenther
+
+Signed-off-by: Guenther Deschner <gd@samba.org>
+Reviewed-by: Andreas Schneider <asn@samba.org>
+Reviewed-by: Jeremy Allison <jra@samba.org>
+Reviewed-by: Gary Lockyer <gary@catalyst.net.nz>
+---
+ source3/utils/ntlm_auth.c | 6 ++++--
+ 1 file changed, 4 insertions(+), 2 deletions(-)
+
+diff --git a/source3/utils/ntlm_auth.c b/source3/utils/ntlm_auth.c
+index 2409ef8d130..7d27712980b 100644
+--- a/source3/utils/ntlm_auth.c
++++ b/source3/utils/ntlm_auth.c
+@@ -1014,7 +1014,7 @@ static NTSTATUS local_pw_check(struct auth4_context *auth4_context,
+ *pauthoritative = 1;
+
+ nt_status = ntlm_password_check(mem_ctx,
+- true, true, 0,
++ true, NTLM_AUTH_ON, 0,
+ &auth4_context->challenge.data,
+ &user_info->password.response.lanman,
+ &user_info->password.response.nt,
+@@ -1735,7 +1735,9 @@ static void manage_ntlm_server_1_request(enum stdio_helper_mode stdio_helper_mod
+
+ nt_lm_owf_gen (opt_password, nt_pw.hash, lm_pw.hash);
+ nt_status = ntlm_password_check(mem_ctx,
+- true, true, 0,
++ true,
++ NTLM_AUTH_ON,
++ 0,
+ &challenge,
+ &lm_response,
+ &nt_response,
+--
+2.18.0
+
diff --git a/main/samba/0004-CVE-2018-10919-tests-Add-test-case-for-object-visibi.patch b/main/samba/0004-CVE-2018-10919-tests-Add-test-case-for-object-visibi.patch
new file mode 100644
index 0000000000..3f1bf64308
--- /dev/null
+++ b/main/samba/0004-CVE-2018-10919-tests-Add-test-case-for-object-visibi.patch
@@ -0,0 +1,117 @@
+From 938a55cf348bd95a5a9d940e1894d5a6df3251db Mon Sep 17 00:00:00 2001
+From: Tim Beale <timbeale@catalyst.net.nz>
+Date: Tue, 31 Jul 2018 14:14:20 +1200
+Subject: [PATCH] CVE-2018-10919 tests: Add test case for object visibility
+ with limited rights
+
+Currently Samba is a bit disclosive with LDB_OP_PRESENT (i.e.
+attribute=*) searches compared to Windows.
+
+All the acl.py tests are based on objectClass=* searches, where Windows
+will happily tell a user about objects they have List Contents rights,
+but not Read Property rights for. However, if you change the attribute
+being searched for, suddenly the objects are no longer visible on
+Windows (whereas they are on Samba).
+
+This is a problem, because Samba can tell you about which objects have
+confidential attributes, which in itself could be disclosive.
+
+This patch adds a acl.py test-case that highlights this behaviour. The
+test passes against Windows but fails against Samba.
+
+Signed-off-by: Tim Beale <timbeale@catalyst.net.nz>
+---
+ selftest/knownfail.d/acl | 1 +
+ source4/dsdb/tests/python/acl.py | 68 ++++++++++++++++++++++++++++++++
+ 2 files changed, 69 insertions(+)
+ create mode 100644 selftest/knownfail.d/acl
+
+diff --git a/selftest/knownfail.d/acl b/selftest/knownfail.d/acl
+new file mode 100644
+index 00000000000..6772ea1f943
+--- /dev/null
++++ b/selftest/knownfail.d/acl
+@@ -0,0 +1 @@
++^samba4.ldap.acl.python.*test_search7
+diff --git a/source4/dsdb/tests/python/acl.py b/source4/dsdb/tests/python/acl.py
+index ec042eeec6b..2038dad586d 100755
+--- a/source4/dsdb/tests/python/acl.py
++++ b/source4/dsdb/tests/python/acl.py
+@@ -981,6 +981,74 @@ class AclSearchTests(AclTests):
+ res_list = res[0].keys()
+ self.assertEquals(sorted(res_list), sorted(ok_list))
+
++ def assert_search_on_attr(self, dn, samdb, attr, expected_list):
++
++ expected_num = len(expected_list)
++ res = samdb.search(dn, expression="(%s=*)" % attr, scope=SCOPE_SUBTREE)
++ self.assertEquals(len(res), expected_num)
++
++ res_list = [ x["dn"] for x in res if x["dn"] in expected_list ]
++ self.assertEquals(sorted(res_list), sorted(expected_list))
++
++ def test_search7(self):
++ """Checks object search visibility when users don't have full rights"""
++ self.create_clean_ou("OU=ou1," + self.base_dn)
++ mod = "(A;;LC;;;%s)(A;;LC;;;%s)" % (str(self.user_sid),
++ str(self.group_sid))
++ self.sd_utils.dacl_add_ace("OU=ou1," + self.base_dn, mod)
++ tmp_desc = security.descriptor.from_sddl("D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)" + mod,
++ self.domain_sid)
++ self.ldb_admin.create_ou("OU=ou2,OU=ou1," + self.base_dn, sd=tmp_desc)
++ self.ldb_admin.create_ou("OU=ou3,OU=ou2,OU=ou1," + self.base_dn,
++ sd=tmp_desc)
++ self.ldb_admin.create_ou("OU=ou4,OU=ou2,OU=ou1," + self.base_dn,
++ sd=tmp_desc)
++ self.ldb_admin.create_ou("OU=ou5,OU=ou3,OU=ou2,OU=ou1," + self.base_dn,
++ sd=tmp_desc)
++ self.ldb_admin.create_ou("OU=ou6,OU=ou4,OU=ou2,OU=ou1," + self.base_dn,
++ sd=tmp_desc)
++
++ ou2_dn = Dn(self.ldb_admin, "OU=ou2,OU=ou1," + self.base_dn)
++ ou1_dn = Dn(self.ldb_admin, "OU=ou1," + self.base_dn)
++
++ # even though unprivileged users can't read these attributes for OU2,
++ # the object should still be visible in searches, because they have
++ # 'List Contents' rights still. This isn't really disclosive because
++ # ALL objects have these attributes
++ visible_attrs = ["objectClass", "distinguishedName", "name",
++ "objectGUID"]
++ two_objects = [ou2_dn, ou1_dn]
++
++ for attr in visible_attrs:
++ # a regular user should just see the 2 objects
++ self.assert_search_on_attr(str(ou1_dn), self.ldb_user3, attr,
++ expected_list=two_objects)
++
++ # whereas the following users have LC rights for all the objects,
++ # so they should see them all
++ self.assert_search_on_attr(str(ou1_dn), self.ldb_user, attr,
++ expected_list=self.full_list)
++ self.assert_search_on_attr(str(ou1_dn), self.ldb_user2, attr,
++ expected_list=self.full_list)
++
++ # however when searching on the following attributes, objects will not
++ # be visible unless the user has Read Property rights
++ hidden_attrs = ["objectCategory", "instanceType", "ou", "uSNChanged",
++ "uSNCreated", "whenCreated"]
++ one_object = [ou1_dn]
++
++ for attr in hidden_attrs:
++ self.assert_search_on_attr(str(ou1_dn), self.ldb_user3, attr,
++ expected_list=one_object)
++ self.assert_search_on_attr(str(ou1_dn), self.ldb_user, attr,
++ expected_list=one_object)
++ self.assert_search_on_attr(str(ou1_dn), self.ldb_user2, attr,
++ expected_list=one_object)
++
++ # admin has RP rights so can still see all the objects
++ self.assert_search_on_attr(str(ou1_dn), self.ldb_admin, attr,
++ expected_list=self.full_list)
++
+ #tests on ldap delete operations
+ class AclDeleteTests(AclTests):
+
+--
+2.18.0
+
diff --git a/main/samba/0004-CVE-2018-1139-selftest-verify-whether-ntlmv1-can-be-.patch b/main/samba/0004-CVE-2018-1139-selftest-verify-whether-ntlmv1-can-be-.patch
new file mode 100644
index 0000000000..6e09d30518
--- /dev/null
+++ b/main/samba/0004-CVE-2018-1139-selftest-verify-whether-ntlmv1-can-be-.patch
@@ -0,0 +1,38 @@
+From c88f7795b34eef6d2432f8e9c2fffc14f419dfcd Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?G=C3=BCnther=20Deschner?= <gd@samba.org>
+Date: Fri, 16 Mar 2018 17:25:12 +0100
+Subject: [PATCH] CVE-2018-1139 selftest: verify whether ntlmv1 can be used via
+ SMB1 when it is disabled.
+
+Right now, this test will succeed.
+
+BUG: https://bugzilla.samba.org/show_bug.cgi?id=13360
+
+CVE-2018-1139: Weak authentication protocol allowed.
+
+Guenther
+
+Signed-off-by: Guenther Deschner <gd@samba.org>
+Reviewed-by: Andreas Schneider <asn@samba.org>
+Reviewed-by: Jeremy Allison <jra@samba.org>
+Reviewed-by: Gary Lockyer <gary@catalyst.net.nz>
+---
+ source3/selftest/tests.py | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/source3/selftest/tests.py b/source3/selftest/tests.py
+index d3c54870377..2d7274c164a 100755
+--- a/source3/selftest/tests.py
++++ b/source3/selftest/tests.py
+@@ -204,7 +204,7 @@ for env in ["nt4_dc", "nt4_member", "ad_member", "ad_dc", "ad_dc_ntvfs", "s4memb
+ plantestsuite("samba3.blackbox.smbclient_machine_auth.plain (%s:local)" % env, "%s:local" % env, [os.path.join(samba3srcdir, "script/tests/test_smbclient_machine_auth.sh"), '$SERVER', smbclient3, configuration])
+ plantestsuite("samba3.blackbox.smbclient_ntlm.plain (%s)" % env, env, [os.path.join(samba3srcdir, "script/tests/test_smbclient_ntlm.sh"), '$SERVER', '$DC_USERNAME', '$DC_PASSWORD', "never", smbclient3, configuration])
+
+-for options in ["--option=clientntlmv2auth=no", "--option=clientusespnego=no --option=clientntlmv2auth=no", ""]:
++for options in ["--option=clientntlmv2auth=no", "--option=clientusespnego=no --option=clientntlmv2auth=no", "--option=clientusespnego=no --option=clientntlmv2auth=no -mNT1", ""]:
+ for env in ["nt4_member", "ad_member"]:
+ plantestsuite("samba3.blackbox.smbclient_auth.plain (%s) %s" % (env, options), env, [os.path.join(samba3srcdir, "script/tests/test_smbclient_auth.sh"), '$SERVER', '$SERVER_IP', '$DC_USERNAME', '$DC_PASSWORD', smbclient3, configuration, options])
+ plantestsuite("samba3.blackbox.smbclient_auth.plain (%s) %s member creds" % (env, options), env, [os.path.join(samba3srcdir, "script/tests/test_smbclient_auth.sh"), '$SERVER', '$SERVER_IP', '$SERVER/$USERNAME', '$PASSWORD', smbclient3, configuration, options])
+--
+2.18.0
+
diff --git a/main/samba/0005-CVE-2018-10919-security-Fix-checking-of-object-speci.patch b/main/samba/0005-CVE-2018-10919-security-Fix-checking-of-object-speci.patch
new file mode 100644
index 0000000000..abb6d32dcb
--- /dev/null
+++ b/main/samba/0005-CVE-2018-10919-security-Fix-checking-of-object-speci.patch
@@ -0,0 +1,58 @@
+From 1594cade555d96461b5b9db9965d8cdf9f5e45e0 Mon Sep 17 00:00:00 2001
+From: Tim Beale <timbeale@catalyst.net.nz>
+Date: Fri, 20 Jul 2018 13:01:00 +1200
+Subject: [PATCH] CVE-2018-10919 security: Fix checking of object-specific
+ CONTROL_ACCESS rights
+
+An 'Object Access Allowed' ACE that assigned 'Control Access' (CR)
+rights to a specific attribute would not actually grant access.
+
+What was happening was the remaining_access mask for the object_tree
+nodes would be Read Property (RP) + Control Access (CR). The ACE mapped
+to the schemaIDGUID for a given attribute, which would end up being a
+child node in the tree. So the CR bit was cleared for a child node, but
+not the rest of the tree. We would then check the user had the RP access
+right, which it did. However, the RP right was cleared for another node
+in the tree, which still had the CR bit set in its remaining_access
+bitmap, so Samba would not grant access.
+
+Generally, the remaining_access only ever has one bit set, which means
+this isn't a problem normally. However, in the Control Access case there
+are 2 separate bits being checked, i.e. RP + CR.
+
+One option to fix this problem would be to clear the remaining_access
+for the tree instead of just the node. However, the Windows spec is
+actually pretty clear on this: if the ACE has a CR right present, then
+you can stop any further access checks.
+
+BUG: https://bugzilla.samba.org/show_bug.cgi?id=13434
+
+Signed-off-by: Tim Beale <timbeale@catalyst.net.nz>
+---
+ libcli/security/access_check.c | 10 ++++++++++
+ 1 file changed, 10 insertions(+)
+
+diff --git a/libcli/security/access_check.c b/libcli/security/access_check.c
+index 93eb85def91..03a7dca4adf 100644
+--- a/libcli/security/access_check.c
++++ b/libcli/security/access_check.c
+@@ -429,6 +429,16 @@ static NTSTATUS check_object_specific_access(struct security_ace *ace,
+ *grant_access = true;
+ return NT_STATUS_OK;
+ }
++
++ /*
++ * As per 5.1.3.3.4 Checking Control Access Right-Based Access,
++ * if the CONTROL_ACCESS right is present, then we can grant
++ * access and stop any further access checks
++ */
++ if (ace->access_mask & SEC_ADS_CONTROL_ACCESS) {
++ *grant_access = true;
++ return NT_STATUS_OK;
++ }
+ } else {
+
+ /* this ACE denies access to the requested object/attribute */
+--
+2.18.0
+
diff --git a/main/samba/0005-CVE-2018-1139-libcli-auth-Add-initial-tests-for-ntlm.patch b/main/samba/0005-CVE-2018-1139-libcli-auth-Add-initial-tests-for-ntlm.patch
new file mode 100644
index 0000000000..1fee2a084f
--- /dev/null
+++ b/main/samba/0005-CVE-2018-1139-libcli-auth-Add-initial-tests-for-ntlm.patch
@@ -0,0 +1,472 @@
+From 5edcaece4130a00d8cdd8e8b10c2c2d86a74afe0 Mon Sep 17 00:00:00 2001
+From: Andrew Bartlett <abartlet@samba.org>
+Date: Fri, 27 Jul 2018 08:44:24 +1200
+Subject: [PATCH] CVE-2018-1139 libcli/auth: Add initial tests for
+ ntlm_password_check()
+
+BUG: https://bugzilla.samba.org/show_bug.cgi?id=13360
+
+Signed-off-by: Andrew Bartlett <abartlet@samba.org>
+Reviewed-by: Jeremy Allison <jra@samba.org>
+Reviewed-by: Gary Lockyer <gary@catalyst.net.nz>
+---
+ libcli/auth/tests/ntlm_check.c | 413 +++++++++++++++++++++++++++++++++
+ libcli/auth/wscript_build | 13 ++
+ selftest/knownfail.d/ntlm | 2 +
+ selftest/tests.py | 2 +
+ 4 files changed, 430 insertions(+)
+ create mode 100644 libcli/auth/tests/ntlm_check.c
+ create mode 100644 selftest/knownfail.d/ntlm
+
+diff --git a/libcli/auth/tests/ntlm_check.c b/libcli/auth/tests/ntlm_check.c
+new file mode 100644
+index 00000000000..e87a0a276d4
+--- /dev/null
++++ b/libcli/auth/tests/ntlm_check.c
+@@ -0,0 +1,413 @@
++/*
++ * Unit tests for the ntlm_check password hash check library.
++ *
++ * Copyright (C) Andrew Bartlett <abartlet@samba.org> 2018
++ *
++ * 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/>.
++ *
++ */
++
++/*
++ * from cmocka.c:
++ * These headers or their equivalents should be included prior to
++ * including
++ * this header file.
++ *
++ * #include <stdarg.h>
++ * #include <stddef.h>
++ * #include <setjmp.h>
++ *
++ * This allows test applications to use custom definitions of C standard
++ * library functions and types.
++ *
++ */
++
++/*
++ * Note that the messaging routines (audit_message_send and get_event_server)
++ * are not tested by these unit tests. Currently they are for integration
++ * test support, and as such are exercised by the integration tests.
++ */
++#include <stdarg.h>
++#include <stddef.h>
++#include <setjmp.h>
++#include <cmocka.h>
++
++#include "includes.h"
++#include "../lib/crypto/crypto.h"
++#include "librpc/gen_ndr/netlogon.h"
++#include "libcli/auth/libcli_auth.h"
++#include "auth/credentials/credentials.h"
++
++struct ntlm_state {
++ const char *username;
++ const char *domain;
++ DATA_BLOB challenge;
++ DATA_BLOB ntlm;
++ DATA_BLOB lm;
++ DATA_BLOB ntlm_key;
++ DATA_BLOB lm_key;
++ const struct samr_Password *nt_hash;
++};
++
++static int test_ntlm_setup_with_options(void **state,
++ int flags, bool upn)
++{
++ NTSTATUS status;
++ DATA_BLOB challenge = {
++ .data = discard_const_p(uint8_t, "I am a teapot"),
++ .length = 8
++ };
++ struct ntlm_state *ntlm_state = talloc(NULL, struct ntlm_state);
++ DATA_BLOB target_info = NTLMv2_generate_names_blob(ntlm_state,
++ NULL,
++ "serverdom");
++ struct cli_credentials *creds = cli_credentials_init(ntlm_state);
++ cli_credentials_set_username(creds,
++ "testuser",
++ CRED_SPECIFIED);
++ cli_credentials_set_domain(creds,
++ "testdom",
++ CRED_SPECIFIED);
++ cli_credentials_set_workstation(creds,
++ "testwksta",
++ CRED_SPECIFIED);
++ cli_credentials_set_password(creds,
++ "testpass",
++ CRED_SPECIFIED);
++
++ if (upn) {
++ cli_credentials_set_principal(creds,
++ "testuser@samba.org",
++ CRED_SPECIFIED);
++ }
++
++ cli_credentials_get_ntlm_username_domain(creds,
++ ntlm_state,
++ &ntlm_state->username,
++ &ntlm_state->domain);
++
++ status = cli_credentials_get_ntlm_response(creds,
++ ntlm_state,
++ &flags,
++ challenge,
++ NULL,
++ target_info,
++ &ntlm_state->lm,
++ &ntlm_state->ntlm,
++ &ntlm_state->lm_key,
++ &ntlm_state->ntlm_key);
++ ntlm_state->challenge = challenge;
++
++ ntlm_state->nt_hash = cli_credentials_get_nt_hash(creds,
++ ntlm_state);
++
++ if (!NT_STATUS_IS_OK(status)) {
++ return -1;
++ }
++
++ *state = ntlm_state;
++ return 0;
++}
++
++static int test_ntlm_setup(void **state) {
++ return test_ntlm_setup_with_options(state, 0, false);
++}
++
++static int test_ntlm_and_lm_setup(void **state) {
++ return test_ntlm_setup_with_options(state,
++ CLI_CRED_LANMAN_AUTH,
++ false);
++}
++
++static int test_ntlm2_setup(void **state) {
++ return test_ntlm_setup_with_options(state,
++ CLI_CRED_NTLM2,
++ false);
++}
++
++static int test_ntlmv2_setup(void **state) {
++ return test_ntlm_setup_with_options(state,
++ CLI_CRED_NTLMv2_AUTH,
++ false);
++}
++
++static int test_ntlm_teardown(void **state)
++{
++ struct ntlm_state *ntlm_state
++ = talloc_get_type_abort(*state,
++ struct ntlm_state);
++ TALLOC_FREE(ntlm_state);
++ *state = NULL;
++ return 0;
++}
++
++static void test_ntlm_allowed(void **state)
++{
++ DATA_BLOB user_sess_key, lm_sess_key;
++ struct ntlm_state *ntlm_state
++ = talloc_get_type_abort(*state,
++ struct ntlm_state);
++ NTSTATUS status;
++ status = ntlm_password_check(ntlm_state,
++ false,
++ NTLM_AUTH_ON,
++ 0,
++ &ntlm_state->challenge,
++ &ntlm_state->lm,
++ &ntlm_state->ntlm,
++ ntlm_state->username,
++ ntlm_state->username,
++ ntlm_state->domain,
++ NULL,
++ ntlm_state->nt_hash,
++ &user_sess_key,
++ &lm_sess_key);
++
++ assert_int_equal(NT_STATUS_V(status), NT_STATUS_V(NT_STATUS_OK));
++}
++
++static void test_ntlm_allowed_lm_supplied(void **state)
++{
++ return test_ntlm_allowed(state);
++}
++
++static void test_ntlm_disabled(void **state)
++{
++ DATA_BLOB user_sess_key, lm_sess_key;
++ struct ntlm_state *ntlm_state
++ = talloc_get_type_abort(*state,
++ struct ntlm_state);
++ NTSTATUS status;
++ status = ntlm_password_check(ntlm_state,
++ false,
++ NTLM_AUTH_DISABLED,
++ 0,
++ &ntlm_state->challenge,
++ &ntlm_state->lm,
++ &ntlm_state->ntlm,
++ ntlm_state->username,
++ ntlm_state->username,
++ ntlm_state->domain,
++ NULL,
++ ntlm_state->nt_hash,
++ &user_sess_key,
++ &lm_sess_key);
++
++ assert_int_equal(NT_STATUS_V(status), NT_STATUS_V(NT_STATUS_NTLM_BLOCKED));
++}
++
++static void test_ntlm2(void **state)
++{
++ DATA_BLOB user_sess_key, lm_sess_key;
++ struct ntlm_state *ntlm_state
++ = talloc_get_type_abort(*state,
++ struct ntlm_state);
++ NTSTATUS status;
++ status = ntlm_password_check(ntlm_state,
++ false,
++ NTLM_AUTH_ON,
++ 0,
++ &ntlm_state->challenge,
++ &ntlm_state->lm,
++ &ntlm_state->ntlm,
++ ntlm_state->username,
++ ntlm_state->username,
++ ntlm_state->domain,
++ NULL,
++ ntlm_state->nt_hash,
++ &user_sess_key,
++ &lm_sess_key);
++
++ /*
++ * NTLM2 session security (where the real challenge is the
++ * MD5(challenge, client-challenge) (in the first 8 bytes of
++ * the lm) isn't decoded by ntlm_password_check(), it must
++ * first be converted back into normal NTLM by the NTLMSSP
++ * layer
++ */
++ assert_int_equal(NT_STATUS_V(status),
++ NT_STATUS_V(NT_STATUS_WRONG_PASSWORD));
++}
++
++static void test_ntlm_mschapv2_only_allowed(void **state)
++{
++ DATA_BLOB user_sess_key, lm_sess_key;
++ struct ntlm_state *ntlm_state
++ = talloc_get_type_abort(*state,
++ struct ntlm_state);
++ NTSTATUS status;
++ status = ntlm_password_check(ntlm_state,
++ false,
++ NTLM_AUTH_MSCHAPv2_NTLMV2_ONLY,
++ MSV1_0_ALLOW_MSVCHAPV2,
++ &ntlm_state->challenge,
++ &ntlm_state->lm,
++ &ntlm_state->ntlm,
++ ntlm_state->username,
++ ntlm_state->username,
++ ntlm_state->domain,
++ NULL,
++ ntlm_state->nt_hash,
++ &user_sess_key,
++ &lm_sess_key);
++
++ assert_int_equal(NT_STATUS_V(status), NT_STATUS_V(NT_STATUS_OK));
++}
++
++static void test_ntlm_mschapv2_only_denied(void **state)
++{
++ DATA_BLOB user_sess_key, lm_sess_key;
++ struct ntlm_state *ntlm_state
++ = talloc_get_type_abort(*state,
++ struct ntlm_state);
++ NTSTATUS status;
++ status = ntlm_password_check(ntlm_state,
++ false,
++ NTLM_AUTH_MSCHAPv2_NTLMV2_ONLY,
++ 0,
++ &ntlm_state->challenge,
++ &ntlm_state->lm,
++ &ntlm_state->ntlm,
++ ntlm_state->username,
++ ntlm_state->username,
++ ntlm_state->domain,
++ NULL,
++ ntlm_state->nt_hash,
++ &user_sess_key,
++ &lm_sess_key);
++
++ assert_int_equal(NT_STATUS_V(status),
++ NT_STATUS_V(NT_STATUS_WRONG_PASSWORD));
++}
++
++static void test_ntlmv2_only_ntlmv2(void **state)
++{
++ DATA_BLOB user_sess_key, lm_sess_key;
++ struct ntlm_state *ntlm_state
++ = talloc_get_type_abort(*state,
++ struct ntlm_state);
++ NTSTATUS status;
++ status = ntlm_password_check(ntlm_state,
++ false,
++ NTLM_AUTH_NTLMV2_ONLY,
++ 0,
++ &ntlm_state->challenge,
++ &ntlm_state->lm,
++ &ntlm_state->ntlm,
++ ntlm_state->username,
++ ntlm_state->username,
++ ntlm_state->domain,
++ NULL,
++ ntlm_state->nt_hash,
++ &user_sess_key,
++ &lm_sess_key);
++
++ assert_int_equal(NT_STATUS_V(status), NT_STATUS_V(NT_STATUS_OK));
++}
++
++static void test_ntlmv2_only_ntlm(void **state)
++{
++ DATA_BLOB user_sess_key, lm_sess_key;
++ struct ntlm_state *ntlm_state
++ = talloc_get_type_abort(*state,
++ struct ntlm_state);
++ NTSTATUS status;
++ status = ntlm_password_check(ntlm_state,
++ false,
++ NTLM_AUTH_NTLMV2_ONLY,
++ 0,
++ &ntlm_state->challenge,
++ &ntlm_state->lm,
++ &ntlm_state->ntlm,
++ ntlm_state->username,
++ ntlm_state->username,
++ ntlm_state->domain,
++ NULL,
++ ntlm_state->nt_hash,
++ &user_sess_key,
++ &lm_sess_key);
++
++ assert_int_equal(NT_STATUS_V(status),
++ NT_STATUS_V(NT_STATUS_WRONG_PASSWORD));
++}
++
++static void test_ntlmv2_only_ntlm_and_lanman(void **state)
++{
++ return test_ntlmv2_only_ntlm(state);
++}
++
++static void test_ntlmv2_only_ntlm_once(void **state)
++{
++ DATA_BLOB user_sess_key, lm_sess_key;
++ struct ntlm_state *ntlm_state
++ = talloc_get_type_abort(*state,
++ struct ntlm_state);
++ NTSTATUS status;
++ status = ntlm_password_check(ntlm_state,
++ false,
++ NTLM_AUTH_NTLMV2_ONLY,
++ 0,
++ &ntlm_state->challenge,
++ &data_blob_null,
++ &ntlm_state->ntlm,
++ ntlm_state->username,
++ ntlm_state->username,
++ ntlm_state->domain,
++ NULL,
++ ntlm_state->nt_hash,
++ &user_sess_key,
++ &lm_sess_key);
++
++ assert_int_equal(NT_STATUS_V(status),
++ NT_STATUS_V(NT_STATUS_WRONG_PASSWORD));
++}
++
++int main(int argc, const char **argv)
++{
++ const struct CMUnitTest tests[] = {
++ cmocka_unit_test_setup_teardown(test_ntlm_allowed,
++ test_ntlm_setup,
++ test_ntlm_teardown),
++ cmocka_unit_test_setup_teardown(test_ntlm_allowed_lm_supplied,
++ test_ntlm_and_lm_setup,
++ test_ntlm_teardown),
++ cmocka_unit_test_setup_teardown(test_ntlm_disabled,
++ test_ntlm_setup,
++ test_ntlm_teardown),
++ cmocka_unit_test_setup_teardown(test_ntlm2,
++ test_ntlm2_setup,
++ test_ntlm_teardown),
++ cmocka_unit_test_setup_teardown(test_ntlm_mschapv2_only_allowed,
++ test_ntlm_setup,
++ test_ntlm_teardown),
++ cmocka_unit_test_setup_teardown(test_ntlm_mschapv2_only_denied,
++ test_ntlm_setup,
++ test_ntlm_teardown),
++ cmocka_unit_test_setup_teardown(test_ntlmv2_only_ntlm,
++ test_ntlm_setup,
++ test_ntlm_teardown),
++ cmocka_unit_test_setup_teardown(test_ntlmv2_only_ntlm_and_lanman,
++ test_ntlm_and_lm_setup,
++ test_ntlm_teardown),
++ cmocka_unit_test_setup_teardown(test_ntlmv2_only_ntlm_once,
++ test_ntlm_setup,
++ test_ntlm_teardown),
++ cmocka_unit_test_setup_teardown(test_ntlmv2_only_ntlmv2,
++ test_ntlmv2_setup,
++ test_ntlm_teardown)
++ };
++
++ cmocka_set_message_output(CM_OUTPUT_SUBUNIT);
++ return cmocka_run_group_tests(tests, NULL, NULL);
++}
+diff --git a/libcli/auth/wscript_build b/libcli/auth/wscript_build
+index 475b7d69406..d319d9b879e 100644
+--- a/libcli/auth/wscript_build
++++ b/libcli/auth/wscript_build
+@@ -41,3 +41,16 @@ bld.SAMBA_SUBSYSTEM('PAM_ERRORS',
+ bld.SAMBA_SUBSYSTEM('SPNEGO_PARSE',
+ source='spnego_parse.c',
+ deps='asn1util')
++
++bld.SAMBA_BINARY(
++ 'test_ntlm_check',
++ source='tests/ntlm_check.c',
++ deps='''
++ NTLM_CHECK
++ CREDENTIALS_NTLM
++ samba-credentials
++ cmocka
++ talloc
++ ''',
++ install=False
++ )
+diff --git a/selftest/knownfail.d/ntlm b/selftest/knownfail.d/ntlm
+new file mode 100644
+index 00000000000..c6e6a3739ba
+--- /dev/null
++++ b/selftest/knownfail.d/ntlm
+@@ -0,0 +1,2 @@
++^samba.unittests.ntlm_check.test_ntlm_mschapv2_only_denied
++^samba.unittests.ntlm_check.test_ntlmv2_only_ntlm\(
++ [os.path.join(bindir(), "default/libcli/auth/test_ntlm_check")])
+--
+2.18.0
+
diff --git a/main/samba/0006-CVE-2018-10919-tests-test-ldap-searches-for-non-exis.patch b/main/samba/0006-CVE-2018-10919-tests-test-ldap-searches-for-non-exis.patch
new file mode 100644
index 0000000000..596e9cd79b
--- /dev/null
+++ b/main/samba/0006-CVE-2018-10919-tests-test-ldap-searches-for-non-exis.patch
@@ -0,0 +1,39 @@
+From ddd6279e122405e87770db173234a26c5d81a616 Mon Sep 17 00:00:00 2001
+From: Gary Lockyer <gary@catalyst.net.nz>
+Date: Fri, 3 Aug 2018 15:51:28 +1200
+Subject: [PATCH] CVE-2018-10919 tests: test ldap searches for non-existent
+ attributes.
+
+It is perfectly legal to search LDAP for an attribute that is not part
+of the schema. That part of the query should simply not match.
+
+BUG: https://bugzilla.samba.org/show_bug.cgi?id=13434
+
+Signed-off-by: Gary Lockyer <gary@catalyst.net.nz>
+---
+ source4/dsdb/tests/python/ldap.py | 9 +++++++++
+ 1 file changed, 9 insertions(+)
+
+diff --git a/source4/dsdb/tests/python/ldap.py b/source4/dsdb/tests/python/ldap.py
+index 4235541fdbe..2514d0a9d72 100755
+--- a/source4/dsdb/tests/python/ldap.py
++++ b/source4/dsdb/tests/python/ldap.py
+@@ -599,6 +599,15 @@ class BasicTests(samba.tests.TestCase):
+ except LdbError, (num, _):
+ self.assertEquals(num, ERR_NO_SUCH_ATTRIBUTE)
+
++ #
++ # When searching the unknown attribute should be ignored
++ expr = "(|(cn=ldaptestgroup)(thisdoesnotexist=x))"
++ res = ldb.search(base=self.base_dn,
++ expression=expr,
++ scope=SCOPE_SUBTREE)
++ self.assertTrue(len(res) == 1,
++ "Search including unknown attribute failed")
++
+ delete_force(self.ldb, "cn=ldaptestgroup,cn=users," + self.base_dn)
+
+ # attributes not in objectclasses and mandatory attributes missing test
+--
+2.18.0
+
diff --git a/main/samba/0007-CVE-2018-10919-acl_read-Split-access_mask-logic-out-.patch b/main/samba/0007-CVE-2018-10919-acl_read-Split-access_mask-logic-out-.patch
new file mode 100644
index 0000000000..41a3ac8f44
--- /dev/null
+++ b/main/samba/0007-CVE-2018-10919-acl_read-Split-access_mask-logic-out-.patch
@@ -0,0 +1,91 @@
+From e95c621a7f243058a24f00a02e25d5edde35565d Mon Sep 17 00:00:00 2001
+From: Tim Beale <timbeale@catalyst.net.nz>
+Date: Fri, 20 Jul 2018 13:52:24 +1200
+Subject: [PATCH] CVE-2018-10919 acl_read: Split access_mask logic out into
+ helper function
+
+So we can re-use the same logic laster for checking the search-ops.
+
+BUG: https://bugzilla.samba.org/show_bug.cgi?id=13434
+
+Signed-off-by: Tim Beale <timbeale@catalyst.net.nz>
+---
+ source4/dsdb/samdb/ldb_modules/acl_read.c | 54 +++++++++++++++--------
+ 1 file changed, 35 insertions(+), 19 deletions(-)
+
+diff --git a/source4/dsdb/samdb/ldb_modules/acl_read.c b/source4/dsdb/samdb/ldb_modules/acl_read.c
+index 3c9cf7c0672..f42b131948c 100644
+--- a/source4/dsdb/samdb/ldb_modules/acl_read.c
++++ b/source4/dsdb/samdb/ldb_modules/acl_read.c
+@@ -227,6 +227,40 @@ static int aclread_get_sd_from_ldb_message(struct aclread_context *ac,
+ return LDB_SUCCESS;
+ }
+
++/*
++ * Returns the access mask required to read a given attribute
++ */
++static uint32_t get_attr_access_mask(const struct dsdb_attribute *attr,
++ uint32_t sd_flags)
++{
++
++ uint32_t access_mask = 0;
++ bool is_sd;
++
++ /* nTSecurityDescriptor is a special case */
++ is_sd = (ldb_attr_cmp("nTSecurityDescriptor",
++ attr->lDAPDisplayName) == 0);
++
++ if (is_sd) {
++ if (sd_flags & (SECINFO_OWNER|SECINFO_GROUP)) {
++ access_mask |= SEC_STD_READ_CONTROL;
++ }
++ if (sd_flags & SECINFO_DACL) {
++ access_mask |= SEC_STD_READ_CONTROL;
++ }
++ if (sd_flags & SECINFO_SACL) {
++ access_mask |= SEC_FLAG_SYSTEM_SECURITY;
++ }
++ } else {
++ access_mask = SEC_ADS_READ_PROP;
++ }
++
++ if (attr->searchFlags & SEARCH_FLAG_CONFIDENTIAL) {
++ access_mask |= SEC_ADS_CONTROL_ACCESS;
++ }
++
++ return access_mask;
++}
+
+ static int aclread_callback(struct ldb_request *req, struct ldb_reply *ares)
+ {
+@@ -342,26 +376,8 @@ static int aclread_callback(struct ldb_request *req, struct ldb_reply *ares)
+ aclread_mark_inaccesslible(&msg->elements[i]);
+ continue;
+ }
+- /* nTSecurityDescriptor is a special case */
+- if (is_sd) {
+- access_mask = 0;
+-
+- if (ac->sd_flags & (SECINFO_OWNER|SECINFO_GROUP)) {
+- access_mask |= SEC_STD_READ_CONTROL;
+- }
+- if (ac->sd_flags & SECINFO_DACL) {
+- access_mask |= SEC_STD_READ_CONTROL;
+- }
+- if (ac->sd_flags & SECINFO_SACL) {
+- access_mask |= SEC_FLAG_SYSTEM_SECURITY;
+- }
+- } else {
+- access_mask = SEC_ADS_READ_PROP;
+- }
+
+- if (attr->searchFlags & SEARCH_FLAG_CONFIDENTIAL) {
+- access_mask |= SEC_ADS_CONTROL_ACCESS;
+- }
++ access_mask = get_attr_access_mask(attr, ac->sd_flags);
+
+ if (access_mask == 0) {
+ aclread_mark_inaccesslible(&msg->elements[i]);
+--
+2.18.0
+
diff --git a/main/samba/0008-CVE-2018-10919-acl_read-Small-refactor-to-aclread_ca.patch b/main/samba/0008-CVE-2018-10919-acl_read-Small-refactor-to-aclread_ca.patch
new file mode 100644
index 0000000000..bd836bd66a
--- /dev/null
+++ b/main/samba/0008-CVE-2018-10919-acl_read-Small-refactor-to-aclread_ca.patch
@@ -0,0 +1,72 @@
+From df6c1dbeb27ab4c7dedc2461a9d20a6b67ffdda4 Mon Sep 17 00:00:00 2001
+From: Tim Beale <timbeale@catalyst.net.nz>
+Date: Thu, 26 Jul 2018 12:20:49 +1200
+Subject: [PATCH] CVE-2018-10919 acl_read: Small refactor to aclread_callback()
+
+Flip the dirsync check (to avoid a double negative), and use a helper
+boolean variable.
+
+BUG: https://bugzilla.samba.org/show_bug.cgi?id=13434
+
+Signed-off-by: Tim Beale <timbeale@catalyst.net.nz>
+---
+ source4/dsdb/samdb/ldb_modules/acl_read.c | 31 +++++++++++++----------
+ 1 file changed, 17 insertions(+), 14 deletions(-)
+
+diff --git a/source4/dsdb/samdb/ldb_modules/acl_read.c b/source4/dsdb/samdb/ldb_modules/acl_read.c
+index f42b131948c..17d6492cd35 100644
+--- a/source4/dsdb/samdb/ldb_modules/acl_read.c
++++ b/source4/dsdb/samdb/ldb_modules/acl_read.c
+@@ -398,18 +398,12 @@ static int aclread_callback(struct ldb_request *req, struct ldb_reply *ares)
+ * in anycase.
+ */
+ if (ret == LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS) {
+- if (!ac->indirsync) {
+- /*
+- * do not return this entry if attribute is
+- * part of the search filter
+- */
+- if (dsdb_attr_in_parse_tree(ac->req->op.search.tree,
+- msg->elements[i].name)) {
+- talloc_free(tmp_ctx);
+- return LDB_SUCCESS;
+- }
+- aclread_mark_inaccesslible(&msg->elements[i]);
+- } else {
++ bool in_search_filter;
++
++ in_search_filter = dsdb_attr_in_parse_tree(ac->req->op.search.tree,
++ msg->elements[i].name);
++
++ if (ac->indirsync) {
+ /*
+ * We are doing dirysnc answers
+ * and the object shouldn't be returned (normally)
+@@ -418,13 +412,22 @@ static int aclread_callback(struct ldb_request *req, struct ldb_reply *ares)
+ * (remove the object if it is not deleted, or return
+ * just the objectGUID if it's deleted).
+ */
+- if (dsdb_attr_in_parse_tree(ac->req->op.search.tree,
+- msg->elements[i].name)) {
++ if (in_search_filter) {
+ ldb_msg_remove_attr(msg, "replPropertyMetaData");
+ break;
+ } else {
+ aclread_mark_inaccesslible(&msg->elements[i]);
+ }
++ } else {
++ /*
++ * do not return this entry if attribute is
++ * part of the search filter
++ */
++ if (in_search_filter) {
++ talloc_free(tmp_ctx);
++ return LDB_SUCCESS;
++ }
++ aclread_mark_inaccesslible(&msg->elements[i]);
+ }
+ } else if (ret != LDB_SUCCESS) {
+ ldb_debug_set(ldb, LDB_DEBUG_FATAL,
+--
+2.18.0
+
diff --git a/main/samba/0009-CVE-2018-10919-acl_read-Flip-the-logic-in-the-dirsyn.patch b/main/samba/0009-CVE-2018-10919-acl_read-Flip-the-logic-in-the-dirsyn.patch
new file mode 100644
index 0000000000..9fa5626880
--- /dev/null
+++ b/main/samba/0009-CVE-2018-10919-acl_read-Flip-the-logic-in-the-dirsyn.patch
@@ -0,0 +1,62 @@
+From 717bde3288704d501368ca650963e2648d005c55 Mon Sep 17 00:00:00 2001
+From: Tim Beale <timbeale@catalyst.net.nz>
+Date: Mon, 30 Jul 2018 16:00:15 +1200
+Subject: [PATCH] CVE-2018-10919 acl_read: Flip the logic in the dirsync check
+
+This better reflects the special case we're making for dirsync, and gets
+rid of a 'if-else' clause.
+
+BUG: https://bugzilla.samba.org/show_bug.cgi?id=13434
+
+Signed-off-by: Tim Beale <timbeale@catalyst.net.nz>
+---
+ source4/dsdb/samdb/ldb_modules/acl_read.c | 17 +++++++----------
+ 1 file changed, 7 insertions(+), 10 deletions(-)
+
+diff --git a/source4/dsdb/samdb/ldb_modules/acl_read.c b/source4/dsdb/samdb/ldb_modules/acl_read.c
+index 17d6492cd35..9607ed05ee7 100644
+--- a/source4/dsdb/samdb/ldb_modules/acl_read.c
++++ b/source4/dsdb/samdb/ldb_modules/acl_read.c
+@@ -400,10 +400,12 @@ static int aclread_callback(struct ldb_request *req, struct ldb_reply *ares)
+ if (ret == LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS) {
+ bool in_search_filter;
+
++ /* check if attr is part of the search filter */
+ in_search_filter = dsdb_attr_in_parse_tree(ac->req->op.search.tree,
+ msg->elements[i].name);
+
+- if (ac->indirsync) {
++ if (in_search_filter) {
++
+ /*
+ * We are doing dirysnc answers
+ * and the object shouldn't be returned (normally)
+@@ -412,21 +414,16 @@ static int aclread_callback(struct ldb_request *req, struct ldb_reply *ares)
+ * (remove the object if it is not deleted, or return
+ * just the objectGUID if it's deleted).
+ */
+- if (in_search_filter) {
++ if (ac->indirsync) {
+ ldb_msg_remove_attr(msg, "replPropertyMetaData");
+ break;
+ } else {
+- aclread_mark_inaccesslible(&msg->elements[i]);
+- }
+- } else {
+- /*
+- * do not return this entry if attribute is
+- * part of the search filter
+- */
+- if (in_search_filter) {
++
++ /* do not return this entry */
+ talloc_free(tmp_ctx);
+ return LDB_SUCCESS;
+ }
++ } else {
+ aclread_mark_inaccesslible(&msg->elements[i]);
+ }
+ } else if (ret != LDB_SUCCESS) {
+--
+2.18.0
+
diff --git a/main/samba/0010-CVE-2018-10919-acl_read-Fix-unauthorized-attribute-a.patch b/main/samba/0010-CVE-2018-10919-acl_read-Fix-unauthorized-attribute-a.patch
new file mode 100644
index 0000000000..2a647fa024
--- /dev/null
+++ b/main/samba/0010-CVE-2018-10919-acl_read-Fix-unauthorized-attribute-a.patch
@@ -0,0 +1,351 @@
+From 9b17ce9a1f46e8519302eb6ec72f1104560bf953 Mon Sep 17 00:00:00 2001
+From: Tim Beale <timbeale@catalyst.net.nz>
+Date: Fri, 20 Jul 2018 15:42:36 +1200
+Subject: [PATCH] CVE-2018-10919 acl_read: Fix unauthorized attribute access
+ via searches
+
+A user that doesn't have access to view an attribute can still guess the
+attribute's value via repeated LDAP searches. This affects confidential
+attributes, as well as ACLs applied to an object/attribute to deny
+access.
+
+Currently the code will hide objects if the attribute filter contains an
+attribute they are not authorized to see. However, the code still
+returns objects as results if confidential attribute is in the search
+expression itself, but not in the attribute filter.
+
+To fix this problem we have to check the access rights on the attributes
+in the search-tree, as well as the attributes returned in the message.
+
+Points of note:
+- I've preserved the existing dirsync logic (the dirsync module code
+ suppresses the result as long as the replPropertyMetaData attribute is
+ removed). However, there doesn't appear to be any test that highlights
+ that this functionality is required for dirsync.
+- To avoid this fix breaking the acl.py tests, we need to still permit
+ searches like 'objectClass=*', even though we don't have Read Property
+ access rights for the objectClass attribute. The logic that Windows
+ uses does not appear to be clearly documented, so I've made a best
+ guess that seems to mirror Windows behaviour.
+
+BUG: https://bugzilla.samba.org/show_bug.cgi?id=13434
+
+Signed-off-by: Tim Beale <timbeale@catalyst.net.nz>
+---
+ selftest/knownfail.d/acl | 1 -
+ selftest/knownfail.d/confidential_attr | 15 --
+ source4/dsdb/samdb/ldb_modules/acl_read.c | 247 ++++++++++++++++++++++
+ 3 files changed, 247 insertions(+), 16 deletions(-)
+ delete mode 100644 selftest/knownfail.d/acl
+ delete mode 100644 selftest/knownfail.d/confidential_attr
+
+diff --git a/selftest/knownfail.d/acl b/selftest/knownfail.d/acl
+deleted file mode 100644
+index 6772ea1f943..00000000000
+--- a/selftest/knownfail.d/acl
++++ /dev/null
+@@ -1 +0,0 @@
+-^samba4.ldap.acl.python.*test_search7
+diff --git a/selftest/knownfail.d/confidential_attr b/selftest/knownfail.d/confidential_attr
+deleted file mode 100644
+index 7a2f2aada57..00000000000
+--- a/selftest/knownfail.d/confidential_attr
++++ /dev/null
+@@ -1,15 +0,0 @@
+-samba4.ldap.confidential_attr.python\(ad_dc_ntvfs\).__main__.ConfidentialAttrTest.test_basic_search\(ad_dc_ntvfs\)
+-samba4.ldap.confidential_attr.python\(ad_dc_ntvfs\).__main__.ConfidentialAttrTest.test_search_with_acl_override\(ad_dc_ntvfs\)
+-samba4.ldap.confidential_attr.python\(ad_dc_ntvfs\).__main__.ConfidentialAttrTest.test_search_with_attr_acl_override\(ad_dc_ntvfs\)
+-samba4.ldap.confidential_attr.python\(ad_dc_ntvfs\).__main__.ConfidentialAttrTest.test_search_with_blanket_oa_acl\(ad_dc_ntvfs\)
+-samba4.ldap.confidential_attr.python\(ad_dc_ntvfs\).__main__.ConfidentialAttrTest.test_search_with_neutral_acl\(ad_dc_ntvfs\)
+-samba4.ldap.confidential_attr.python\(ad_dc_ntvfs\).__main__.ConfidentialAttrTest.test_search_with_neutral_acl\(ad_dc_ntvfs\)
+-samba4.ldap.confidential_attr.python\(ad_dc_ntvfs\).__main__.ConfidentialAttrTest.test_search_with_neutral_attr_acl\(ad_dc_ntvfs\)
+-samba4.ldap.confidential_attr.python\(ad_dc_ntvfs\).__main__.ConfidentialAttrTest.test_search_with_neutral_cr_acl\(ad_dc_ntvfs\)
+-samba4.ldap.confidential_attr.python\(ad_dc_ntvfs\).__main__.ConfidentialAttrTest.test_search_with_propset_acl_override\(ad_dc_ntvfs\)
+-samba4.ldap.confidential_attr.python\(ad_dc_ntvfs\).__main__.ConfidentialAttrTestDenyAcl.test_search_with_blanket_oa_deny_acl\(ad_dc_ntvfs\)
+-samba4.ldap.confidential_attr.python\(ad_dc_ntvfs\).__main__.ConfidentialAttrTestDenyAcl.test_search_with_deny_acl\(ad_dc_ntvfs\)
+-samba4.ldap.confidential_attr.python\(ad_dc_ntvfs\).__main__.ConfidentialAttrTestDenyAcl.test_search_with_deny_attr_acl\(ad_dc_ntvfs\)
+-samba4.ldap.confidential_attr.python\(ad_dc_ntvfs\).__main__.ConfidentialAttrTestDenyAcl.test_search_with_deny_propset_acl\(ad_dc_ntvfs\)
+-samba4.ldap.confidential_attr.python\(ad_dc_ntvfs\).__main__.ConfidentialAttrTestDirsync.test_search_with_dirsync\(ad_dc_ntvfs\)
+-
+diff --git a/source4/dsdb/samdb/ldb_modules/acl_read.c b/source4/dsdb/samdb/ldb_modules/acl_read.c
+index 9607ed05ee7..280845a47a5 100644
+--- a/source4/dsdb/samdb/ldb_modules/acl_read.c
++++ b/source4/dsdb/samdb/ldb_modules/acl_read.c
+@@ -38,6 +38,8 @@
+ #include "param/param.h"
+ #include "dsdb/samdb/ldb_modules/util.h"
+
++/* The attributeSecurityGuid for the Public Information Property-Set */
++#define PUBLIC_INFO_PROPERTY_SET "e48d0154-bcf8-11d1-8702-00c04fb96050"
+
+ struct aclread_context {
+ struct ldb_module *module;
+@@ -262,6 +264,219 @@ static uint32_t get_attr_access_mask(const struct dsdb_attribute *attr,
+ return access_mask;
+ }
+
++/* helper struct for traversing the attributes in the search-tree */
++struct parse_tree_aclread_ctx {
++ struct aclread_context *ac;
++ TALLOC_CTX *mem_ctx;
++ struct dom_sid *sid;
++ struct ldb_dn *dn;
++ struct security_descriptor *sd;
++ const struct dsdb_class *objectclass;
++ bool suppress_result;
++};
++
++/*
++ * Checks that the user has sufficient access rights to view an attribute
++ */
++static int check_attr_access_rights(TALLOC_CTX *mem_ctx, const char *attr_name,
++ struct aclread_context *ac,
++ struct security_descriptor *sd,
++ const struct dsdb_class *objectclass,
++ struct dom_sid *sid, struct ldb_dn *dn,
++ bool *is_public_info)
++{
++ int ret;
++ const struct dsdb_attribute *attr = NULL;
++ uint32_t access_mask;
++ struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
++
++ *is_public_info = false;
++
++ attr = dsdb_attribute_by_lDAPDisplayName(ac->schema, attr_name);
++ if (!attr) {
++ ldb_debug_set(ldb,
++ LDB_DEBUG_TRACE,
++ "acl_read: %s cannot find attr[%s] in schema,"
++ "ignoring\n",
++ ldb_dn_get_linearized(dn), attr_name);
++ return LDB_SUCCESS;
++ }
++
++ /*
++ * If we have no Read Property (RP) rights for a child object, it should
++ * still appear as a visible object in 'objectClass=*' searches,
++ * as long as we have List Contents (LC) rights for it.
++ * This is needed for the acl.py tests (e.g. test_search1()).
++ * I couldn't find the Windows behaviour documented in the specs, so
++ * this is a guess, but it seems to only apply to attributes in the
++ * Public Information Property Set that have the systemOnly flag set to
++ * TRUE. (This makes sense in a way, as it's not disclosive to find out
++ * that a child object has a 'objectClass' or 'name' attribute, as every
++ * object has these attributes).
++ */
++ if (attr->systemOnly) {
++ struct GUID public_info_guid;
++ NTSTATUS status;
++
++ status = GUID_from_string(PUBLIC_INFO_PROPERTY_SET,
++ &public_info_guid);
++ if (!NT_STATUS_IS_OK(status)) {
++ ldb_set_errstring(ldb, "Public Info GUID parse error");
++ return LDB_ERR_OPERATIONS_ERROR;
++ }
++
++ if (GUID_compare(&attr->attributeSecurityGUID,
++ &public_info_guid) == 0) {
++ *is_public_info = true;
++ }
++ }
++
++ access_mask = get_attr_access_mask(attr, ac->sd_flags);
++
++ /* the access-mask should be non-zero. Skip attribute otherwise */
++ if (access_mask == 0) {
++ DBG_ERR("Could not determine access mask for attribute %s\n",
++ attr_name);
++ return LDB_SUCCESS;
++ }
++
++ ret = acl_check_access_on_attribute(ac->module, mem_ctx, sd, sid,
++ access_mask, attr, objectclass);
++
++ if (ret == LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS) {
++ return ret;
++ }
++
++ if (ret != LDB_SUCCESS) {
++ ldb_debug_set(ldb, LDB_DEBUG_FATAL,
++ "acl_read: %s check attr[%s] gives %s - %s\n",
++ ldb_dn_get_linearized(dn), attr_name,
++ ldb_strerror(ret), ldb_errstring(ldb));
++ return ret;
++ }
++
++ return LDB_SUCCESS;
++}
++
++/*
++ * Returns the attribute name for this particular level of a search operation
++ * parse-tree.
++ */
++static const char * parse_tree_get_attr(struct ldb_parse_tree *tree)
++{
++ const char *attr = NULL;
++
++ switch (tree->operation) {
++ case LDB_OP_EQUALITY:
++ case LDB_OP_GREATER:
++ case LDB_OP_LESS:
++ case LDB_OP_APPROX:
++ attr = tree->u.equality.attr;
++ break;
++ case LDB_OP_SUBSTRING:
++ attr = tree->u.substring.attr;
++ break;
++ case LDB_OP_PRESENT:
++ attr = tree->u.present.attr;
++ break;
++ case LDB_OP_EXTENDED:
++ attr = tree->u.extended.attr;
++ break;
++
++ /* we'll check LDB_OP_AND/_OR/_NOT children later on in the walk */
++ default:
++ break;
++ }
++ return attr;
++}
++
++/*
++ * Checks a single attribute in the search parse-tree to make sure the user has
++ * sufficient rights to view it.
++ */
++static int parse_tree_check_attr_access(struct ldb_parse_tree *tree,
++ void *private_context)
++{
++ struct parse_tree_aclread_ctx *ctx = NULL;
++ const char *attr_name = NULL;
++ bool is_public_info = false;
++ int ret;
++
++ ctx = (struct parse_tree_aclread_ctx *)private_context;
++
++ /*
++ * we can skip any further checking if we already know that this object
++ * shouldn't be visible in this user's search
++ */
++ if (ctx->suppress_result) {
++ return LDB_SUCCESS;
++ }
++
++ /* skip this level of the search-tree if it has no attribute to check */
++ attr_name = parse_tree_get_attr(tree);
++ if (attr_name == NULL) {
++ return LDB_SUCCESS;
++ }
++
++ ret = check_attr_access_rights(ctx->mem_ctx, attr_name, ctx->ac,
++ ctx->sd, ctx->objectclass, ctx->sid,
++ ctx->dn, &is_public_info);
++
++ /*
++ * if the user does not have the rights to view this attribute, then we
++ * should not return the object as a search result, i.e. act as if the
++ * object doesn't exist (for this particular user, at least)
++ */
++ if (ret == LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS) {
++
++ /*
++ * We make an exception for attribute=* searches involving the
++ * Public Information property-set. This allows searches like
++ * objectClass=* to return visible objects, even if the user
++ * doesn't have Read Property rights on the attribute
++ */
++ if (tree->operation == LDB_OP_PRESENT && is_public_info) {
++ return LDB_SUCCESS;
++ }
++
++ ctx->suppress_result = true;
++ return LDB_SUCCESS;
++ }
++
++ return ret;
++}
++
++/*
++ * Traverse the search-tree to check that the user has sufficient access rights
++ * to view all the attributes.
++ */
++static int check_search_ops_access(struct aclread_context *ac,
++ TALLOC_CTX *mem_ctx,
++ struct security_descriptor *sd,
++ const struct dsdb_class *objectclass,
++ struct dom_sid *sid, struct ldb_dn *dn,
++ bool *suppress_result)
++{
++ int ret;
++ struct parse_tree_aclread_ctx ctx = { 0 };
++ struct ldb_parse_tree *tree = ac->req->op.search.tree;
++
++ ctx.ac = ac;
++ ctx.mem_ctx = mem_ctx;
++ ctx.suppress_result = false;
++ ctx.sid = sid;
++ ctx.dn = dn;
++ ctx.sd = sd;
++ ctx.objectclass = objectclass;
++
++ /* walk the search tree, checking each attribute as we go */
++ ret = ldb_parse_tree_walk(tree, parse_tree_check_attr_access, &ctx);
++
++ /* return whether this search result should be hidden to this user */
++ *suppress_result = ctx.suppress_result;
++ return ret;
++}
++
+ static int aclread_callback(struct ldb_request *req, struct ldb_reply *ares)
+ {
+ struct ldb_context *ldb;
+@@ -275,6 +490,7 @@ static int aclread_callback(struct ldb_request *req, struct ldb_reply *ares)
+ TALLOC_CTX *tmp_ctx;
+ uint32_t instanceType;
+ const struct dsdb_class *objectclass;
++ bool suppress_result = false;
+
+ ac = talloc_get_type(req->context, struct aclread_context);
+ ldb = ldb_module_get_ctx(ac->module);
+@@ -436,6 +652,37 @@ static int aclread_callback(struct ldb_request *req, struct ldb_reply *ares)
+ goto fail;
+ }
+ }
++
++ /*
++ * check access rights for the search attributes, as well as the
++ * attribute values actually being returned
++ */
++ ret = check_search_ops_access(ac, tmp_ctx, sd, objectclass, sid,
++ msg->dn, &suppress_result);
++ if (ret != LDB_SUCCESS) {
++ ldb_debug_set(ldb, LDB_DEBUG_FATAL,
++ "acl_read: %s check search ops %s - %s\n",
++ ldb_dn_get_linearized(msg->dn),
++ ldb_strerror(ret), ldb_errstring(ldb));
++ goto fail;
++ }
++
++ if (suppress_result) {
++
++ /*
++ * As per the above logic, we strip replPropertyMetaData
++ * out of the msg so that the dirysync module will do
++ * what is needed (return just the objectGUID if it's,
++ * deleted, or remove the object if it is not).
++ */
++ if (ac->indirsync) {
++ ldb_msg_remove_attr(msg, "replPropertyMetaData");
++ } else {
++ talloc_free(tmp_ctx);
++ return LDB_SUCCESS;
++ }
++ }
++
+ for (i=0; i < msg->num_elements; i++) {
+ if (!aclread_is_inaccessible(&msg->elements[i])) {
+ num_of_attrs++;
+--
+2.18.0
+
diff --git a/main/samba/0011-CVE-2018-10919-tests-Add-extra-test-for-dirsync-dele.patch b/main/samba/0011-CVE-2018-10919-tests-Add-extra-test-for-dirsync-dele.patch
new file mode 100644
index 0000000000..cf477171b8
--- /dev/null
+++ b/main/samba/0011-CVE-2018-10919-tests-Add-extra-test-for-dirsync-dele.patch
@@ -0,0 +1,261 @@
+From e0bb0b6f74e32a7a0ddd7251f1c305eb38363359 Mon Sep 17 00:00:00 2001
+From: Tim Beale <timbeale@catalyst.net.nz>
+Date: Wed, 1 Aug 2018 13:51:42 +1200
+Subject: [PATCH] CVE-2018-10919 tests: Add extra test for dirsync deleted
+ object corner-case
+
+The acl_read.c code contains a special case to allow dirsync to
+work-around having insufficient access rights. We had a concern that
+the dirsync module could leak sensitive information for deleted objects.
+This patch adds a test-case to prove whether or not this is happening.
+
+The new test case is similar to the existing dirsync test except:
+- We make the confidential attribute also preserve-on-delete, so it
+ hangs around for deleted objcts. Because the attributes now persist
+ across test case runs, I've used a different attribute to normal.
+ (Technically, the dirsync search expressions are now specific enough
+ that the regular attribute could be used, but it would make things
+ quite fragile if someone tried to add a new test case).
+- To handle searching for deleted objects, the search expressions are
+ now more complicated. Currently dirsync adds an extra-filter to the
+ '!' searches to exclude deleted objects, i.e. samaccountname matches
+ the test-objects AND the object is not deleted. We now extend this to
+ include deleted objects with lastKnownParent equal to the test OU.
+ The search expression matches either case so that we can use the same
+ expression throughout the test (regardless of whether the object is
+ deleted yet or not).
+
+This test proves that the dirsync corner-case does not actually leak
+sensitive information on Samba. This is due to a bug in the dirsync
+code - when the buggy line is removed, this new test promptly fails.
+Test also passes against Windows.
+
+BUG: https://bugzilla.samba.org/show_bug.cgi?id=13434
+
+Signed-off-by: Tim Beale <timbeale@catalyst.net.nz>
+---
+ .../dsdb/tests/python/confidential_attr.py | 157 +++++++++++++++---
+ 1 file changed, 131 insertions(+), 26 deletions(-)
+
+diff --git a/source4/dsdb/tests/python/confidential_attr.py b/source4/dsdb/tests/python/confidential_attr.py
+index c3909d5fc2e..1e1cf6c48f8 100755
+--- a/source4/dsdb/tests/python/confidential_attr.py
++++ b/source4/dsdb/tests/python/confidential_attr.py
+@@ -28,7 +28,7 @@ import os
+ from samba.tests.subunitrun import SubunitOptions, TestProgram
+ import samba.getopt as options
+ from ldb import SCOPE_BASE, SCOPE_SUBTREE
+-from samba.dsdb import SEARCH_FLAG_CONFIDENTIAL
++from samba.dsdb import SEARCH_FLAG_CONFIDENTIAL, SEARCH_FLAG_PRESERVEONDELETE
+ from ldb import Message, MessageElement, Dn
+ from ldb import FLAG_MOD_REPLACE, FLAG_MOD_ADD
+ from samba.auth import system_session
+@@ -102,11 +102,11 @@ class ConfidentialAttrCommon(samba.tests.TestCase):
+ # schema attribute being modified. There are only a few attributes that
+ # meet this criteria (most of which only apply to 'user' objects)
+ self.conf_attr = "homePostalAddress"
+- self.conf_attr_cn = "CN=Address-Home"
+- # schemaIdGuid for homePostalAddress:
++ attr_cn = "CN=Address-Home"
++ # schemaIdGuid for homePostalAddress (used for ACE tests)
+ self.conf_attr_guid = "16775781-47f3-11d1-a9c3-0000f80367c1"
+ self.conf_attr_sec_guid = "77b5b886-944a-11d1-aebd-0000f80367c1"
+- self.attr_dn = "{},{}".format(self.conf_attr_cn, self.schema_dn)
++ self.attr_dn = "{},{}".format(attr_cn, self.schema_dn)
+
+ userou = "OU=conf-attr-test"
+ self.ou = "{},{}".format(userou, self.base_dn)
+@@ -801,28 +801,42 @@ class ConfidentialAttrTestDirsync(ConfidentialAttrCommon):
+ super(ConfidentialAttrTestDirsync, self).setUp()
+ self.dirsync = ["dirsync:1:1:1000"]
+
+- def assert_search_result(self, expected_num, expr, samdb, base_dn=None):
+-
+- # Note dirsync must always search on the partition base DN
+- if base_dn is None:
+- base_dn = self.base_dn
++ # because we need to search on the base DN when using the dirsync
++ # controls, we need an extra filter for the inverse ('!') search,
++ # so we don't get thousands of objects returned
++ self.extra_filter = \
++ "(&(samaccountname={}*)(!(isDeleted=*)))".format(self.user_prefix)
++ self.single_obj_filter = \
++ "(&(samaccountname={})(!(isDeleted=*)))".format(self.conf_user)
+
+- attr_filters = [None, ["*"], ["name"]]
++ self.attr_filters = [None, ["*"], ["name"]]
+
+ # Note dirsync behaviour is slighty different for the attribute under
+ # test - when you have full access rights, it only returns the objects
+ # that actually have this attribute (i.e. it doesn't return an empty
+ # message with just the DN). So we add the 'name' attribute into the
+ # attribute filter to avoid complicating our assertions further
+- attr_filters += [[self.conf_attr, "name"]]
+- for attr in attr_filters:
+- res = samdb.search(base_dn, expression=expr, scope=SCOPE_SUBTREE,
+- attrs=attr, controls=self.dirsync)
++ self.attr_filters += [[self.conf_attr, "name"]]
++
++ def assert_search_result(self, expected_num, expr, samdb, base_dn=None):
+
++ # Note dirsync must always search on the partition base DN
++ if base_dn is None:
++ base_dn = self.base_dn
++
++ # we need an extra filter for dirsync because:
++ # - we search on the base DN, so otherwise the '!' searches return
++ # thousands of unrelated results, and
++ # - we make the test attribute preserve-on-delete in one case, so we
++ # want to weed out results from any previous test runs
++ search = "(&{}{})".format(expr, self.extra_filter)
++
++ for attr in self.attr_filters:
++ res = samdb.search(base_dn, expression=search, scope=SCOPE_SUBTREE,
++ attrs=attr, controls=self.dirsync)
+ self.assertTrue(len(res) == expected_num,
+ "%u results, not %u for search %s, attr %s" %
+- (len(res), expected_num, expr, str(attr)))
+-
++ (len(res), expected_num, search, str(attr)))
+
+ def assert_attr_returned(self, expect_attr, samdb, attrs,
+ no_result_ok=False):
+@@ -830,7 +844,7 @@ class ConfidentialAttrTestDirsync(ConfidentialAttrCommon):
+ # When using dirsync, the base DN we search on needs to be a naming
+ # context. Add an extra filter to ignore all the objects we aren't
+ # interested in
+- expr = "(&(samaccountname={})(!(isDeleted=*)))".format(self.conf_user)
++ expr = self.single_obj_filter
+ res = samdb.search(self.base_dn, expression=expr, scope=SCOPE_SUBTREE,
+ attrs=attrs, controls=self.dirsync)
+ self.assertTrue(len(res) == 1 or no_result_ok)
+@@ -877,21 +891,14 @@ class ConfidentialAttrTestDirsync(ConfidentialAttrCommon):
+ if samdb is None:
+ samdb = self.ldb_user
+
+- # because we need to search on the base DN when using the dirsync
+- # controls, we need an extra filter for the inverse ('!') search,
+- # so we don't get thousands of objects returned
+- extra_filter = \
+- "(samaccountname={}*)(!(isDeleted=*))".format(self.user_prefix)
+-
+- # because of this extra filter, the total objects we expect here only
+- # includes the user objects (not the parent OU)
++ # because dirsync uses an extra filter, the total objects we expect
++ # here only includes the user objects (not the parent OU)
+ total_objects = len(self.all_users)
+ expected_results = self.negative_search_expected_results(has_rights_to,
+ dc_mode,
+ total_objects)
+
+ for search, expected_num in expected_results.items():
+- search = "(&{}{})".format(search, extra_filter)
+ self.assert_search_result(expected_num, search, samdb)
+
+ def test_search_with_dirsync(self):
+@@ -917,4 +924,102 @@ class ConfidentialAttrTestDirsync(ConfidentialAttrCommon):
+ self.assert_negative_searches(has_rights_to="all",
+ samdb=self.ldb_admin)
+
++ def get_guid(self, dn):
++ """Returns an object's GUID (in string format)"""
++ res = self.ldb_admin.search(base=dn, attrs=["objectGUID"],
++ scope=SCOPE_BASE)
++ guid = res[0]['objectGUID'][0]
++ return self.ldb_admin.schema_format_value("objectGUID", guid)
++
++ def make_attr_preserve_on_delete(self):
++ """Marks the attribute under test as being preserve on delete"""
++
++ # work out the original 'searchFlags' value before we overwrite it
++ search_flags = int(self.get_attr_search_flags(self.attr_dn))
++
++ # check we've already set the confidential flag
++ self.assertTrue(search_flags & SEARCH_FLAG_CONFIDENTIAL != 0)
++ search_flags |= SEARCH_FLAG_PRESERVEONDELETE
++
++ self.set_attr_search_flags(self.attr_dn, str(search_flags))
++
++ def change_attr_under_test(self, attr_name, attr_cn):
++ # change the attribute that the test code uses
++ self.conf_attr = attr_name
++ self.attr_dn = "{},{}".format(attr_cn, self.schema_dn)
++
++ # set the new attribute for the user-under-test
++ self.add_attr(self.conf_dn, self.conf_attr, self.conf_value)
++
++ # 2 other users also have the attribute-under-test set (to a randomish
++ # value). Set the new attribute for them now (normally this gets done
++ # in the setUp())
++ for username in self.all_users:
++ if "other-user" in username:
++ dn = self.get_user_dn(username)
++ self.add_attr(dn, self.conf_attr, "xyz-blah")
++
++ def test_search_with_dirsync_deleted_objects(self):
++ """Checks dirsync doesn't reveal confidential info for deleted objs"""
++
++ # change the attribute we're testing (we'll preserve on delete for this
++ # test case, which means the attribute-under-test hangs around after
++ # the test case finishes, and would interfere with the searches for
++ # subsequent other test cases)
++ self.change_attr_under_test("carLicense", "CN=carLicense")
++
++ # Windows dirsync behaviour is a little strange when you request
++ # attributes that deleted objects no longer have, so just request 'all
++ # attributes' to simplify the test logic
++ self.attr_filters = [None, ["*"]]
++
++ # normally dirsync uses extra filters to exclude deleted objects that
++ # we're not interested in. Override these filters so they WILL include
++ # deleted objects, but only from this particular test run. We can do
++ # this by matching lastKnownParent against this test case's OU, which
++ # will match any deleted child objects.
++ ou_guid = self.get_guid(self.ou)
++ deleted_filter = "(lastKnownParent=<GUID={}>)".format(ou_guid)
++
++ # the extra-filter will get combined via AND with the search expression
++ # we're testing, i.e. filter on the confidential attribute AND only
++ # include non-deleted objects, OR deleted objects from this test run
++ exclude_deleted_objs_filter = self.extra_filter
++ self.extra_filter = "(|{}{})".format(exclude_deleted_objs_filter,
++ deleted_filter)
++
++ # for matching on a single object, the search expresseion becomes:
++ # match exactly by account-name AND either a non-deleted object OR a
++ # deleted object from this test run
++ match_by_name = "(samaccountname={})".format(self.conf_user)
++ not_deleted = "(!(isDeleted=*))"
++ self.single_obj_filter = "(&{}(|{}{}))".format(match_by_name,
++ not_deleted,
++ deleted_filter)
++
++ # check that the search filters work as expected
++ self.assert_conf_attr_searches(has_rights_to="all")
++ self.assert_attr_visible(expect_attr=True)
++ self.assert_negative_searches(has_rights_to="all")
++
++ # make the test attribute confidential *and* preserve on delete.
++ self.make_attr_confidential()
++ self.make_attr_preserve_on_delete()
++
++ # check we can't see the objects now, even with using dirsync controls
++ self.assert_conf_attr_searches(has_rights_to=0)
++ self.assert_attr_visible(expect_attr=False)
++ dc_mode = self.guess_dc_mode()
++ self.assert_negative_searches(has_rights_to=0, dc_mode=dc_mode)
++
++ # now delete the users (except for the user whose LDB connection
++ # we're currently using)
++ for user in self.all_users:
++ if user != self.user:
++ self.ldb_admin.delete(self.get_user_dn(user))
++
++ # check we still can't see the objects
++ self.assert_conf_attr_searches(has_rights_to=0)
++ self.assert_negative_searches(has_rights_to=0, dc_mode=dc_mode)
++
+ TestProgram(module=__name__, opts=subunitopts)
+--
+2.18.0
+
diff --git a/main/samba/APKBUILD b/main/samba/APKBUILD
index a9efcd5780..2301bbde88 100644
--- a/main/samba/APKBUILD
+++ b/main/samba/APKBUILD
@@ -1,7 +1,7 @@
# Maintainer: Natanael Copa <ncopa@alpinelinux.org>
pkgname=samba
pkgver=4.7.6
-pkgrel=0
+pkgrel=1
pkgdesc="Tools to access a server's filespace and printers via SMB"
url="http://www.samba.org"
arch="all"
@@ -50,6 +50,25 @@ source="https://us1.samba.org/samba/ftp/stable/$pkgname-$pkgver.tar.gz
musl_uintptr.patch
netdb-defines.patch
netapp.patch
+ 0001-CVE-2018-1139-libcli-auth-Do-not-allow-ntlmv1-over-S.patch
+ 0002-CVE-2018-1139-libcli-auth-fix-debug-messages-in-hash.patch
+ 0003-CVE-2018-1139-s3-utils-use-enum-ntlm_auth_level-in-n.patch
+ 0004-CVE-2018-1139-selftest-verify-whether-ntlmv1-can-be-.patch
+ 0005-CVE-2018-1139-libcli-auth-Add-initial-tests-for-ntlm.patch
+ 0001-CVE-2018-10858-libsmb-Ensure-smbc_urlencode-can-t-ov.patch
+ 0002-CVE-2018-10858-libsmb-Harden-smbc_readdir_internal-a.patch
+ 0001-CVE-2018-10918-cracknames-Fix-DoS-NULL-pointer-de-re.patch
+ 0001-CVE-2018-10919-security-Move-object-specific-access-.patch
+ 0002-CVE-2018-10919-security-Add-more-comments-to-the-obj.patch
+ 0003-CVE-2018-10919-tests-Add-tests-for-guessing-confiden.patch
+ 0004-CVE-2018-10919-tests-Add-test-case-for-object-visibi.patch
+ 0005-CVE-2018-10919-security-Fix-checking-of-object-speci.patch
+ 0006-CVE-2018-10919-tests-test-ldap-searches-for-non-exis.patch
+ 0007-CVE-2018-10919-acl_read-Split-access_mask-logic-out-.patch
+ 0008-CVE-2018-10919-acl_read-Small-refactor-to-aclread_ca.patch
+ 0009-CVE-2018-10919-acl_read-Flip-the-logic-in-the-dirsyn.patch
+ 0010-CVE-2018-10919-acl_read-Fix-unauthorized-attribute-a.patch
+ 0011-CVE-2018-10919-tests-Add-extra-test-for-dirsync-dele.patch
$pkgname.initd
$pkgname.confd
@@ -59,6 +78,11 @@ pkggroups="winbind"
builddir="$srcdir/$pkgname-$pkgver"
# secfixes:
+# 4.7.6-r1:
+# - CVE-2018-1139
+# - CVE-2018-10858
+# - CVE-2018-10918
+# - CVE-2018-10919
# 4.7.6-r0:
# - CVE-2018-1050
# - CVE-2018-1057
@@ -518,6 +542,25 @@ b43809d7ecbf3968f5154c2ded6ed47dae36921f1895ea98bcce50557eb2ad39b736345ffb421465
a99e771f28d787dc22e832b97aa48a1c5e13ddc0c030c501a3c12819ff6e62800ef084b62930abe88c6767d785d5c37e2e9f18a4f9a24f2ee1f5d9650320c556 musl_uintptr.patch
1854577d0e4457e27da367a6c7ec0fb5cfd63cefea0a39181c9d6e78cf8d3eb50878cdddeea3daeec955d00263151c2f86ea754ff4276ef98bc52c0276d9ffe8 netdb-defines.patch
202667cb0383414d9289cd67574f5e1140c9a0ff63bb82a746a59b2397a00db15654bfb30cb5ec1cd68a097899be0f849d9aab4c0d210152386c9e66c640f0c0 netapp.patch
+4ab899af4dd7de4f95601a41d30786d2236bbbae1f8f8e1b8cb4e81a65d2548f433420947611cd0c79b4eabb45567c215f07ab6fc71fa4ad92b803e32c127b44 0001-CVE-2018-1139-libcli-auth-Do-not-allow-ntlmv1-over-S.patch
+89f43f3222bcd935fff63b73b140bdb6ec6b89fa67d25ffd1278a70355f89e8ee045902f4c11330ea853cff5e86ae493e39867a75cbfac4222453b251fd4f4f3 0002-CVE-2018-1139-libcli-auth-fix-debug-messages-in-hash.patch
+393a46570e402218050b116238af892e17db19ff305c32dddc9f01b9584cdcbce26e15699d30d79f7c9a5eeba77faa1d8100e6b89f33c8a921202bd5e674bd77 0003-CVE-2018-1139-s3-utils-use-enum-ntlm_auth_level-in-n.patch
+d9399568976324f24e5886185c533461f8fbd14cd3494a1ca02acf9d624ced2206669d413c0676f85e24e1d84f80d095cf894ddef8e3b698d8a9eccf784d206f 0004-CVE-2018-1139-selftest-verify-whether-ntlmv1-can-be-.patch
+541b8bc3b7b52a25f0cc6a5b5e9564bcd4c69c17c5d79dc60de5e85d7312cb0015ff13fb85f08f40149561f0d1fc4b1d599cfcb4962a286e7e00a0366cc88d97 0005-CVE-2018-1139-libcli-auth-Add-initial-tests-for-ntlm.patch
+1d817f4253f4081da69acf2443372f264b4d59486efb9f99bf4e355fe61c97f562d49ef62f429483aa88d55811974f29f6155c55a91bd51ae1105c7ae062d14a 0001-CVE-2018-10858-libsmb-Ensure-smbc_urlencode-can-t-ov.patch
+076967a0d311dde58fff9b2b295728af8df60c9aac426771ade60dd0dd47eff8dbceb2ed07ba70701cc11a3ed63b681e85c511962f0d956a1d7ea736206b2032 0002-CVE-2018-10858-libsmb-Harden-smbc_readdir_internal-a.patch
+fb5500c2bd10f2a7beb19004e97d2360d8980b3d64524a43debb49fd40143f8e1c3477349b4c2f52f0caa99335f8f429b771d76b29e54715cef3ff3ca28f8789 0001-CVE-2018-10918-cracknames-Fix-DoS-NULL-pointer-de-re.patch
+fb239ab76d9cb818dc02218f0b455678893b550deb7923588b1be7e6214d733cfbbd6833b638606bbc0bbf6f9716f8d972732d8b4caacee5ea2ee9c3c4e0ba3b 0001-CVE-2018-10919-security-Move-object-specific-access-.patch
+74989701e3f8c037b1fa0183d382707deded90c2870a084f62c6bf22845c0bf81d1c9ab847a1a8820b7fee6bfb2f29c99e6066399c054f6444dbe83b2a0feb77 0002-CVE-2018-10919-security-Add-more-comments-to-the-obj.patch
+31a2b079f7e3043ce4cfa96336f7eecb8a616881ddb267ab92b8009e19a2b342568ab70e84202637b9dbf5d39ff39d76bbfed5b1b934f87af7a8fef064d9b9a8 0003-CVE-2018-10919-tests-Add-tests-for-guessing-confiden.patch
+c81d508f6fdffc8830c3d4d69e3ce1f03070cd6a19cc59c44b0ade1bd7038a972b039911f19e385e282d08763f5306d1c6d164c7c0a1625bfb6b0fb2e03e32e5 0004-CVE-2018-10919-tests-Add-test-case-for-object-visibi.patch
+b5e73fa0e114a8c388b56b46d373dd821aecab942c409421afaca26aeeb85f14ca42db9fda88e03dcbdb8a016e92723a7526029fc1f392a894ac543a586a63f9 0005-CVE-2018-10919-security-Fix-checking-of-object-speci.patch
+9547e2cc039845c9d2679e4028a9a23ddd9c809e8ec28d9da60449f2d417ba3a192101789bd9f72a8ce6e5215bb0f5a562c732a710b0b4fa38add37a4fb475e3 0006-CVE-2018-10919-tests-test-ldap-searches-for-non-exis.patch
+5d2bcb47aa0840a1e18d3490b4e366a8d7ff67b6ac410f7d9c96b142d874f3763ae1a39a417283819be8de84c0d92f4ba31e5485417ce7e3717997888bcb8f22 0007-CVE-2018-10919-acl_read-Split-access_mask-logic-out-.patch
+54bf2a43eb760b735eeefdce969a4e8fb30dd56d7c816dd7bb494feb8af648a686fa18eabe33db1a15bfbff4b3c98ae339752050a6da70c201bc2acbf0f41c57 0008-CVE-2018-10919-acl_read-Small-refactor-to-aclread_ca.patch
+c317f5338a612906a3765ee69e3f487f755d4aa61670543aefec71cc4eb7fed12f1aa21027f80661a25a2cc550ddbceb3988a1d3fa624925763b9164b9153b89 0009-CVE-2018-10919-acl_read-Flip-the-logic-in-the-dirsyn.patch
+f59421fc880c864d36da28d9a6b41fb8be912c8a0411301cc80c8682210d17fc111ea956a9d2ad01ebb58f4737a3b8d8fa8d8e2a897d32a8b30240eacf23b2e2 0010-CVE-2018-10919-acl_read-Fix-unauthorized-attribute-a.patch
+8c284d9faa975342092c94193c6993d1dc11e429f108bf2a152a631ed2f5ae85c1f5db5ef51828682357aac015c888cc1080f705fbc529b6d07112a0039d3bd6 0011-CVE-2018-10919-tests-Add-extra-test-for-dirsync-dele.patch
6bee83aab500f27248b315d8a5f567940d7232269b021d801b3d51c20ed9e4aad513ee0117f356fb388014a63a145beacb55307ef9addbf7997987304b548fcf samba.initd
4faf581ecef3ec38319e3c4ab6d3995c51fd7ba83180dc5553a2ff4dfb92efadb43030c543292130c4ed0c281dc0972c6973d52d48062c5edb39bb1c4bbb6dd6 samba.confd
f88ebe59ca3a9e9b77dd5993c13ef3e73a838efb8ed858088b464a330132d662f33e25c27819e38835389dee23057a3951de11bae1eef55db8ff5e1ec6760053 samba.logrotate"