diff options
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" |