diff -Nru samba-4.15.13+dfsg/debian/changelog samba-4.15.13+dfsg/debian/changelog --- samba-4.15.13+dfsg/debian/changelog 2023-07-11 12:44:35.000000000 +0000 +++ samba-4.15.13+dfsg/debian/changelog 2023-10-04 12:38:27.000000000 +0000 @@ -1,3 +1,47 @@ +samba (2:4.15.13+dfsg-0ubuntu1.5) jammy-security; urgency=medium + + * SECURITY UPDATE: SMB clients can truncate files with read-only + permissions + - debian/patches/CVE-2023-4091-*.patch + - CVE-2023-4091 + * SECURITY UPDATE: Samba AD DC password exposure to privileged users and + RODCs + - debian/patches/CVE-2023-4154-*.patch + - CVE-2023-4154 + * SECURITY UPDATE: rpcecho development server allows Denial of Service + via sleep() call on AD DC + - debian/patches/CVE-2023-42669.patch + - CVE-2023-42669 + + -- Marc Deslauriers Wed, 04 Oct 2023 08:38:27 -0400 + +samba (2:4.15.13+dfsg-0ubuntu1.4) jammy; urgency=medium + + * d/p/issue-when-updating-old-passwd-containing-regex-metachars.patch: + Add changes to fix uncaught exception when updating old password + containing regex metacharacters by simplifying samba-tool password + redaction (LP: #2002949). + + -- Michal Maloszewski Fri, 18 Aug 2023 15:53:14 +0200 + +samba (2:4.15.13+dfsg-0ubuntu1.3) jammy; urgency=medium + + * d/p/secure-channel-faulty-kb5028166.patch: fix domain membership + after Windows KB5028166 update (LP: #2027716) + * Cherry pick samba AD DC provisioning DEP8 test from later Ubuntu + releases (LP: #1977746, LP: #2011745): + - d/t/control, d/t/util, d/t/samba-ad-dc-provisioning-internal-dns: + samba AD DC provisioning and domain join tests with internal DNS + + d/t/control: adjust package dependencies + + d/t/samba-ad-dc-provisioning-internal-dns: handle the case where + libnss-winbind does not automatically add winbind to + /etc/nsswitch.conf (that is done only in Lunar and later) + + d/t/samba-ad-dc-provisioning-internal-dns: use case insensitive + match when inspecting kerberos tickets, as the hostname may be + capitalized + + -- Andreas Hasenack Sun, 23 Jul 2023 17:09:59 -0300 + samba (2:4.15.13+dfsg-0ubuntu1.2) jammy-security; urgency=medium * SECURITY UPDATE: Out-Of-Bounds read in winbind AUTH_CRAP diff -Nru samba-4.15.13+dfsg/debian/patches/CVE-2023-4091-1.patch samba-4.15.13+dfsg/debian/patches/CVE-2023-4091-1.patch --- samba-4.15.13+dfsg/debian/patches/CVE-2023-4091-1.patch 1970-01-01 00:00:00.000000000 +0000 +++ samba-4.15.13+dfsg/debian/patches/CVE-2023-4091-1.patch 2023-10-04 12:22:33.000000000 +0000 @@ -0,0 +1,178 @@ +From 968f710e25841d09ba62ff9326b78489958c80b2 Mon Sep 17 00:00:00 2001 +From: Ralph Boehme +Date: Tue, 1 Aug 2023 12:30:00 +0200 +Subject: [PATCH 1/2] CVE-2023-4091: smbtorture: test overwrite dispositions on + read-only file + +BUG: https://bugzilla.samba.org/show_bug.cgi?id=15439 + +Signed-off-by: Ralph Boehme +--- + selftest/knownfail.d/samba3.smb2.acls | 1 + + source4/torture/smb2/acls.c | 143 ++++++++++++++++++++++++++ + 2 files changed, 144 insertions(+) + create mode 100644 selftest/knownfail.d/samba3.smb2.acls + +--- /dev/null ++++ b/selftest/knownfail.d/samba3.smb2.acls +@@ -0,0 +1 @@ ++^samba3.smb2.acls.OVERWRITE_READ_ONLY_FILE +--- a/source4/torture/smb2/acls.c ++++ b/source4/torture/smb2/acls.c +@@ -3023,6 +3023,148 @@ done: + return ret; + } + ++static bool test_overwrite_read_only_file(struct torture_context *tctx, ++ struct smb2_tree *tree) ++{ ++ NTSTATUS status; ++ struct smb2_create c; ++ const char *fname = BASEDIR "\\test_overwrite_read_only_file.txt"; ++ struct smb2_handle handle = {{0}}; ++ union smb_fileinfo q; ++ union smb_setfileinfo set; ++ struct security_descriptor *sd = NULL, *sd_orig = NULL; ++ const char *owner_sid = NULL; ++ int i; ++ bool ret = true; ++ ++ struct tcase { ++ int disposition; ++ const char *disposition_string; ++ NTSTATUS expected_status; ++ } tcases[] = { ++#define TCASE(d, s) { \ ++ .disposition = d, \ ++ .disposition_string = #d, \ ++ .expected_status = s, \ ++ } ++ TCASE(NTCREATEX_DISP_OPEN, NT_STATUS_OK), ++ TCASE(NTCREATEX_DISP_SUPERSEDE, NT_STATUS_ACCESS_DENIED), ++ TCASE(NTCREATEX_DISP_OVERWRITE, NT_STATUS_ACCESS_DENIED), ++ TCASE(NTCREATEX_DISP_OVERWRITE_IF, NT_STATUS_ACCESS_DENIED), ++ }; ++#undef TCASE ++ ++ ret = smb2_util_setup_dir(tctx, tree, BASEDIR); ++ torture_assert_goto(tctx, ret, ret, done, "smb2_util_setup_dir not ok"); ++ ++ c = (struct smb2_create) { ++ .in.desired_access = SEC_STD_READ_CONTROL | ++ SEC_STD_WRITE_DAC | ++ SEC_STD_WRITE_OWNER, ++ .in.file_attributes = FILE_ATTRIBUTE_NORMAL, ++ .in.share_access = NTCREATEX_SHARE_ACCESS_READ | ++ NTCREATEX_SHARE_ACCESS_WRITE, ++ .in.create_disposition = NTCREATEX_DISP_OPEN_IF, ++ .in.impersonation_level = NTCREATEX_IMPERSONATION_ANONYMOUS, ++ .in.fname = fname, ++ }; ++ ++ status = smb2_create(tree, tctx, &c); ++ torture_assert_ntstatus_ok_goto(tctx, status, ret, done, ++ "smb2_create failed\n"); ++ handle = c.out.file.handle; ++ ++ torture_comment(tctx, "get the original sd\n"); ++ ++ ZERO_STRUCT(q); ++ q.query_secdesc.level = RAW_FILEINFO_SEC_DESC; ++ q.query_secdesc.in.file.handle = handle; ++ q.query_secdesc.in.secinfo_flags = SECINFO_DACL | SECINFO_OWNER; ++ ++ status = smb2_getinfo_file(tree, tctx, &q); ++ torture_assert_ntstatus_ok_goto(tctx, status, ret, done, ++ "smb2_getinfo_file failed\n"); ++ sd_orig = q.query_secdesc.out.sd; ++ ++ owner_sid = dom_sid_string(tctx, sd_orig->owner_sid); ++ ++ sd = security_descriptor_dacl_create(tctx, ++ 0, NULL, NULL, ++ owner_sid, ++ SEC_ACE_TYPE_ACCESS_ALLOWED, ++ SEC_FILE_READ_DATA, ++ 0, ++ NULL); ++ ++ ZERO_STRUCT(set); ++ set.set_secdesc.level = RAW_SFILEINFO_SEC_DESC; ++ set.set_secdesc.in.file.handle = handle; ++ set.set_secdesc.in.secinfo_flags = SECINFO_DACL; ++ set.set_secdesc.in.sd = sd; ++ ++ status = smb2_setinfo_file(tree, &set); ++ torture_assert_ntstatus_ok_goto(tctx, status, ret, done, ++ "smb2_setinfo_file failed\n"); ++ ++ smb2_util_close(tree, handle); ++ ZERO_STRUCT(handle); ++ ++ for (i = 0; i < ARRAY_SIZE(tcases); i++) { ++ torture_comment(tctx, "Verify open with %s dispostion\n", ++ tcases[i].disposition_string); ++ ++ c = (struct smb2_create) { ++ .in.create_disposition = tcases[i].disposition, ++ .in.desired_access = SEC_FILE_READ_DATA, ++ .in.file_attributes = FILE_ATTRIBUTE_NORMAL, ++ .in.share_access = NTCREATEX_SHARE_ACCESS_MASK, ++ .in.impersonation_level = NTCREATEX_IMPERSONATION_ANONYMOUS, ++ .in.fname = fname, ++ }; ++ ++ status = smb2_create(tree, tctx, &c); ++ smb2_util_close(tree, c.out.file.handle); ++ torture_assert_ntstatus_equal_goto( ++ tctx, status, tcases[i].expected_status, ret, done, ++ "smb2_create failed\n"); ++ }; ++ ++ torture_comment(tctx, "put back original sd\n"); ++ ++ c = (struct smb2_create) { ++ .in.desired_access = SEC_STD_WRITE_DAC, ++ .in.file_attributes = FILE_ATTRIBUTE_NORMAL, ++ .in.share_access = NTCREATEX_SHARE_ACCESS_MASK, ++ .in.create_disposition = NTCREATEX_DISP_OPEN_IF, ++ .in.impersonation_level = NTCREATEX_IMPERSONATION_ANONYMOUS, ++ .in.fname = fname, ++ }; ++ ++ status = smb2_create(tree, tctx, &c); ++ torture_assert_ntstatus_ok_goto(tctx, status, ret, done, ++ "smb2_create failed\n"); ++ handle = c.out.file.handle; ++ ++ ZERO_STRUCT(set); ++ set.set_secdesc.level = RAW_SFILEINFO_SEC_DESC; ++ set.set_secdesc.in.file.handle = handle; ++ set.set_secdesc.in.secinfo_flags = SECINFO_DACL; ++ set.set_secdesc.in.sd = sd_orig; ++ ++ status = smb2_setinfo_file(tree, &set); ++ torture_assert_ntstatus_ok_goto(tctx, status, ret, done, ++ "smb2_setinfo_file failed\n"); ++ ++ smb2_util_close(tree, handle); ++ ZERO_STRUCT(handle); ++ ++done: ++ smb2_util_close(tree, handle); ++ smb2_util_unlink(tree, fname); ++ smb2_deltree(tree, BASEDIR); ++ return ret; ++} ++ + /* + basic testing of SMB2 ACLs + */ +@@ -3051,6 +3193,7 @@ struct torture_suite *torture_smb2_acls_ + test_deny1); + torture_suite_add_1smb2_test(suite, "MXAC-NOT-GRANTED", + test_mxac_not_granted); ++ torture_suite_add_1smb2_test(suite, "OVERWRITE_READ_ONLY_FILE", test_overwrite_read_only_file); + + suite->description = talloc_strdup(suite, "SMB2-ACLS tests"); + diff -Nru samba-4.15.13+dfsg/debian/patches/CVE-2023-4091-2.patch samba-4.15.13+dfsg/debian/patches/CVE-2023-4091-2.patch --- samba-4.15.13+dfsg/debian/patches/CVE-2023-4091-2.patch 1970-01-01 00:00:00.000000000 +0000 +++ samba-4.15.13+dfsg/debian/patches/CVE-2023-4091-2.patch 2023-10-04 12:22:38.000000000 +0000 @@ -0,0 +1,45 @@ +From d4183416f14ad68fa78228d72a506bba355017ae Mon Sep 17 00:00:00 2001 +From: Ralph Boehme +Date: Tue, 1 Aug 2023 13:04:36 +0200 +Subject: [PATCH 2/2] CVE-2023-4091: smbd: use open_access_mask for access + check in open_file() + +If the client requested FILE_OVERWRITE[_IF], we're implicitly adding +FILE_WRITE_DATA to the open_access_mask in open_file_ntcreate(), but for the +access check we're using access_mask which doesn't contain the additional +right, which means we can end up truncating a file for which the user has +only read-only access via an SD. + +BUG: https://bugzilla.samba.org/show_bug.cgi?id=15439 + +Signed-off-by: Ralph Boehme +--- + selftest/knownfail.d/samba3.smb2.acls | 1 - + source3/smbd/open.c | 4 ++-- + 2 files changed, 2 insertions(+), 3 deletions(-) + delete mode 100644 selftest/knownfail.d/samba3.smb2.acls + +--- a/selftest/knownfail.d/samba3.smb2.acls ++++ /dev/null +@@ -1 +0,0 @@ +-^samba3.smb2.acls.OVERWRITE_READ_ONLY_FILE +--- a/source3/smbd/open.c ++++ b/source3/smbd/open.c +@@ -1385,7 +1385,7 @@ static NTSTATUS open_file(files_struct * + parent_dir->fsp, + fsp, + false, +- access_mask); ++ open_access_mask); + + if (!NT_STATUS_IS_OK(status)) { + DBG_DEBUG("smbd_check_access_rights_fsp" +@@ -1574,7 +1574,7 @@ static NTSTATUS open_file(files_struct * + status = smbd_check_access_rights_fsp(parent_dir->fsp, + fsp, + false, +- access_mask); ++ open_access_mask); + + if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND) && + (fsp->posix_flags & FSP_POSIX_FLAGS_OPEN) && diff -Nru samba-4.15.13+dfsg/debian/patches/CVE-2023-4154-01.patch samba-4.15.13+dfsg/debian/patches/CVE-2023-4154-01.patch --- samba-4.15.13+dfsg/debian/patches/CVE-2023-4154-01.patch 1970-01-01 00:00:00.000000000 +0000 +++ samba-4.15.13+dfsg/debian/patches/CVE-2023-4154-01.patch 2023-10-04 12:23:15.000000000 +0000 @@ -0,0 +1,115 @@ +From eecdc5c4ede4e03c92b42e1ba4cc0c28cf33fd36 Mon Sep 17 00:00:00 2001 +From: Christian Merten +Date: Mon, 19 Sep 2022 22:47:10 +0200 +Subject: [PATCH 01/21] CVE-2023-4154 libcli security_descriptor: Add function + to delete a given ace from a security descriptor + +Two functions have been added to delete a given ace from the SACL or the DACL of a security descriptor. + +BUG: https://bugzilla.samba.org/show_bug.cgi?id=15424 + +Signed-off-by: Christian Merten +Reviewed-by: Douglas Bagnall +Reviewed-by: Jeremy Allison +(cherry picked from commit 7efe673fbdcd27ddd23f36281c5f5338681a68fe) +--- + libcli/security/security_descriptor.c | 66 +++++++++++++++++++++++++++ + libcli/security/security_descriptor.h | 4 ++ + 2 files changed, 70 insertions(+) + +diff --git a/libcli/security/security_descriptor.c b/libcli/security/security_descriptor.c +index ba142016389..64c2d027876 100644 +--- a/libcli/security/security_descriptor.c ++++ b/libcli/security/security_descriptor.c +@@ -419,6 +419,72 @@ NTSTATUS security_descriptor_sacl_del(struct security_descriptor *sd, + return security_descriptor_acl_del(sd, true, trustee); + } + ++/* ++ delete the given ACE in the SACL or DACL of a security_descriptor ++*/ ++static NTSTATUS security_descriptor_acl_del_ace(struct security_descriptor *sd, ++ bool sacl_del, ++ const struct security_ace *ace) ++{ ++ uint32_t i; ++ bool found = false; ++ struct security_acl *acl = NULL; ++ ++ if (sacl_del) { ++ acl = sd->sacl; ++ } else { ++ acl = sd->dacl; ++ } ++ ++ if (acl == NULL) { ++ return NT_STATUS_OBJECT_NAME_NOT_FOUND; ++ } ++ ++ for (i=0;inum_aces;i++) { ++ if (security_ace_equal(ace, &acl->aces[i])) { ++ ARRAY_DEL_ELEMENT(acl->aces, i, acl->num_aces); ++ acl->num_aces--; ++ if (acl->num_aces == 0) { ++ acl->aces = NULL; ++ } ++ found = true; ++ i--; ++ } ++ } ++ ++ if (!found) { ++ return NT_STATUS_OBJECT_NAME_NOT_FOUND; ++ } ++ ++ acl->revision = SECURITY_ACL_REVISION_NT4; ++ ++ for (i=0;inum_aces;i++) { ++ switch (acl->aces[i].type) { ++ case SEC_ACE_TYPE_ACCESS_ALLOWED_OBJECT: ++ case SEC_ACE_TYPE_ACCESS_DENIED_OBJECT: ++ case SEC_ACE_TYPE_SYSTEM_AUDIT_OBJECT: ++ case SEC_ACE_TYPE_SYSTEM_ALARM_OBJECT: ++ acl->revision = SECURITY_ACL_REVISION_ADS; ++ return NT_STATUS_OK; ++ default: ++ break; /* only for the switch statement */ ++ } ++ } ++ ++ return NT_STATUS_OK; ++} ++ ++NTSTATUS security_descriptor_dacl_del_ace(struct security_descriptor *sd, ++ const struct security_ace *ace) ++{ ++ return security_descriptor_acl_del_ace(sd, false, ace); ++} ++ ++NTSTATUS security_descriptor_sacl_del_ace(struct security_descriptor *sd, ++ const struct security_ace *ace) ++{ ++ return security_descriptor_acl_del_ace(sd, true, ace); ++} + /* + compare two security ace structures + */ +diff --git a/libcli/security/security_descriptor.h b/libcli/security/security_descriptor.h +index 7e6df87fefa..46545321d15 100644 +--- a/libcli/security/security_descriptor.h ++++ b/libcli/security/security_descriptor.h +@@ -39,6 +39,10 @@ NTSTATUS security_descriptor_dacl_del(struct security_descriptor *sd, + const struct dom_sid *trustee); + NTSTATUS security_descriptor_sacl_del(struct security_descriptor *sd, + const struct dom_sid *trustee); ++NTSTATUS security_descriptor_dacl_del_ace(struct security_descriptor *sd, ++ const struct security_ace *ace); ++NTSTATUS security_descriptor_sacl_del_ace(struct security_descriptor *sd, ++ const struct security_ace *ace); + bool security_ace_equal(const struct security_ace *ace1, + const struct security_ace *ace2); + bool security_acl_equal(const struct security_acl *acl1, +-- +2.25.1 + + diff -Nru samba-4.15.13+dfsg/debian/patches/CVE-2023-4154-02.patch samba-4.15.13+dfsg/debian/patches/CVE-2023-4154-02.patch --- samba-4.15.13+dfsg/debian/patches/CVE-2023-4154-02.patch 1970-01-01 00:00:00.000000000 +0000 +++ samba-4.15.13+dfsg/debian/patches/CVE-2023-4154-02.patch 2023-10-04 12:23:20.000000000 +0000 @@ -0,0 +1,93 @@ +From dad22546d000238bf047bf7d333400d9f598988b Mon Sep 17 00:00:00 2001 +From: Christian Merten +Date: Mon, 19 Sep 2022 23:01:34 +0200 +Subject: [PATCH 02/21] CVE-2023-4154 librpc ndr/py_security: Export ACE + deletion functions to python + +Exported security_descriptor_sacl_del and security_descriptor_dacl_del as new methods of the +security descriptor class to python. + +BUG: https://bugzilla.samba.org/show_bug.cgi?id=15424 + +Signed-off-by: Christian Merten +Reviewed-by: Douglas Bagnall +Reviewed-by: Jeremy Allison +(cherry picked from commit 84a54d2fa2b1590fdb4e2ea986ded9c39a82cf78) +--- + source4/librpc/ndr/py_security.c | 52 +++++++++++++++++++++++++++++++- + 1 file changed, 51 insertions(+), 1 deletion(-) + +diff --git a/source4/librpc/ndr/py_security.c b/source4/librpc/ndr/py_security.c +index e79e7170812..e61b994d7cb 100644 +--- a/source4/librpc/ndr/py_security.c ++++ b/source4/librpc/ndr/py_security.c +@@ -234,6 +234,52 @@ static PyObject *py_descriptor_sacl_del(PyObject *self, PyObject *args) + Py_RETURN_NONE; + } + ++static PyObject *py_descriptor_dacl_del_ace(PyObject *self, PyObject *args) ++{ ++ struct security_descriptor *desc = pytalloc_get_ptr(self); ++ NTSTATUS status; ++ struct security_ace *ace = NULL; ++ PyObject *py_ace = Py_None; ++ ++ if (!PyArg_ParseTuple(args, "O!", &security_ace_Type, &py_ace)) ++ return NULL; ++ ++ if (!PyObject_TypeCheck(py_ace, &security_ace_Type)) { ++ PyErr_SetString(PyExc_TypeError, ++ "expected security.security_ace " ++ "for first argument to .dacl_del_ace"); ++ return NULL; ++ } ++ ++ ace = pytalloc_get_ptr(py_ace); ++ status = security_descriptor_dacl_del_ace(desc, ace); ++ PyErr_NTSTATUS_IS_ERR_RAISE(status); ++ Py_RETURN_NONE; ++} ++ ++static PyObject *py_descriptor_sacl_del_ace(PyObject *self, PyObject *args) ++{ ++ struct security_descriptor *desc = pytalloc_get_ptr(self); ++ NTSTATUS status; ++ struct security_ace *ace = NULL; ++ PyObject *py_ace = Py_None; ++ ++ if (!PyArg_ParseTuple(args, "O!", &security_ace_Type, &py_ace)) ++ return NULL; ++ ++ if (!PyObject_TypeCheck(py_ace, &security_ace_Type)) { ++ PyErr_SetString(PyExc_TypeError, ++ "expected security.security_ace " ++ "for first argument to .sacl_del_ace"); ++ return NULL; ++ } ++ ++ ace = pytalloc_get_ptr(py_ace); ++ status = security_descriptor_sacl_del_ace(desc, ace); ++ PyErr_NTSTATUS_IS_ERR_RAISE(status); ++ Py_RETURN_NONE; ++} ++ + static PyObject *py_descriptor_new(PyTypeObject *self, PyObject *args, PyObject *kwargs) + { + return pytalloc_steal(self, security_descriptor_initialise(NULL)); +@@ -302,7 +348,11 @@ static PyMethodDef py_descriptor_extra_methods[] = { + NULL }, + { "sacl_del", (PyCFunction)py_descriptor_sacl_del, METH_VARARGS, + NULL }, +- { "from_sddl", (PyCFunction)py_descriptor_from_sddl, METH_VARARGS|METH_CLASS, ++ { "dacl_del_ace", (PyCFunction)py_descriptor_dacl_del_ace, METH_VARARGS, ++ NULL }, ++ { "sacl_del_ace", (PyCFunction)py_descriptor_sacl_del_ace, METH_VARARGS, ++ NULL }, ++ { "from_sddl", (PyCFunction)py_descriptor_from_sddl, METH_VARARGS|METH_CLASS, + NULL }, + { "as_sddl", (PyCFunction)py_descriptor_as_sddl, METH_VARARGS, + NULL }, +-- +2.25.1 + + diff -Nru samba-4.15.13+dfsg/debian/patches/CVE-2023-4154-03.patch samba-4.15.13+dfsg/debian/patches/CVE-2023-4154-03.patch --- samba-4.15.13+dfsg/debian/patches/CVE-2023-4154-03.patch 1970-01-01 00:00:00.000000000 +0000 +++ samba-4.15.13+dfsg/debian/patches/CVE-2023-4154-03.patch 2023-10-04 12:23:23.000000000 +0000 @@ -0,0 +1,248 @@ +From 4d2fcca878ff0b40ee4b3cd8ce55896132853dcd Mon Sep 17 00:00:00 2001 +From: Andrew Bartlett +Date: Wed, 1 Mar 2023 14:49:06 +1300 +Subject: [PATCH 03/21] CVE-2023-4154 dsdb: Remove remaining references to + DC_MODE_RETURN_NONE and DC_MODE_RETURN_ALL + +The confidential_attrs test no longer uses DC_MODE_RETURN_NONE we can now +remove the complexity. + +BUG: https://bugzilla.samba.org/show_bug.cgi?id=15424 + +Signed-off-by: Andrew Bartlett +Reviewed-by: Joseph Sutton +(cherry picked from commit 82d2ec786f7e75ff6f34eb3357964345b10de091) +--- + .../dsdb/tests/python/confidential_attr.py | 86 ++++--------------- + 1 file changed, 16 insertions(+), 70 deletions(-) + +diff --git a/source4/dsdb/tests/python/confidential_attr.py b/source4/dsdb/tests/python/confidential_attr.py +index 6889d5a5560..ac83f488061 100755 +--- a/source4/dsdb/tests/python/confidential_attr.py ++++ b/source4/dsdb/tests/python/confidential_attr.py +@@ -70,20 +70,6 @@ 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 + # +@@ -193,25 +179,6 @@ class ConfidentialAttrCommon(samba.tests.TestCase): + # 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={0},{1}".format(name, self.ou) + +@@ -359,7 +326,7 @@ class ConfidentialAttrCommon(samba.tests.TestCase): + 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 ++ # a DC, 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): +@@ -409,32 +376,24 @@ class ConfidentialAttrCommon(samba.tests.TestCase): + # and what access rights the user has. + # Note we only handle has_rights_to="all", 1 (the test object), or 0 (i.e. + # we don't have rights to any objects) +- def negative_search_expected_results(self, has_rights_to, dc_mode, +- total_objects=None): ++ def negative_search_expected_results(self, has_rights_to, 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): ++ def assert_negative_searches(self, has_rights_to=0, 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) ++ expected_results = self.negative_search_expected_results(has_rights_to) + + for search, expected_num in expected_results.items(): + self.assert_search_result(expected_num, search, samdb) +@@ -490,8 +449,7 @@ class ConfidentialAttrTest(ConfidentialAttrCommon): + self.make_attr_confidential() + + self.assert_conf_attr_searches(has_rights_to=0) +- dc_mode = DC_MODE_RETURN_ALL +- self.assert_negative_searches(has_rights_to=0, dc_mode=dc_mode) ++ self.assert_negative_searches(has_rights_to=0) + self.assert_attr_visible(expect_attr=False) + + # sanity-check we haven't hidden the attribute from the admin as well +@@ -503,8 +461,7 @@ class ConfidentialAttrTest(ConfidentialAttrCommon): + self.make_attr_confidential() + + self.assert_conf_attr_searches(has_rights_to=0) +- dc_mode = DC_MODE_RETURN_ALL +- self.assert_negative_searches(has_rights_to=0, dc_mode=dc_mode) ++ self.assert_negative_searches(has_rights_to=0) + self.assert_attr_visible(expect_attr=False) + + # apply the allow ACE to the object under test +@@ -513,7 +470,7 @@ class ConfidentialAttrTest(ConfidentialAttrCommon): + # 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_negative_searches(has_rights_to=1) + self.assert_attr_visible(expect_attr=True) + + # sanity-check the admin can still see the attribute +@@ -566,8 +523,7 @@ class ConfidentialAttrTest(ConfidentialAttrCommon): + self.make_attr_confidential() + + self.assert_conf_attr_searches(has_rights_to=0) +- dc_mode = DC_MODE_RETURN_ALL +- self.assert_negative_searches(has_rights_to=0, dc_mode=dc_mode) ++ self.assert_negative_searches(has_rights_to=0) + self.assert_attr_visible(expect_attr=False) + + # apply the ACE to the object under test +@@ -575,7 +531,7 @@ class ConfidentialAttrTest(ConfidentialAttrCommon): + + # 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_negative_searches(has_rights_to=0) + self.assert_attr_visible(expect_attr=False) + + # sanity-check the admin can still see the attribute +@@ -707,8 +663,7 @@ class ConfidentialAttrTestDenyAcl(ConfidentialAttrCommon): + return expected_results + + # override method specifically for deny ACL test cases +- def assert_negative_searches(self, has_rights_to=0, +- dc_mode=DC_MODE_RETURN_NONE, samdb=None): ++ def assert_negative_searches(self, has_rights_to=0, samdb=None): + """Asserts user without rights cannot see objects in '!' searches""" + + if samdb is None: +@@ -719,12 +674,9 @@ class ConfidentialAttrTestDenyAcl(ConfidentialAttrCommon): + # 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) ++ expected_results = self.negative_search_expected_results(has_rights_to) + + for search, expected_num in expected_results.items(): + self.assert_search_result(expected_num, search, samdb, +@@ -741,9 +693,7 @@ class ConfidentialAttrTestDenyAcl(ConfidentialAttrCommon): + + # the user shouldn't be able to see the attribute anymore + self.assert_conf_attr_searches(has_rights_to="deny-one") +- dc_mode = DC_MODE_RETURN_ALL +- self.assert_negative_searches(has_rights_to="deny-one", +- dc_mode=dc_mode) ++ self.assert_negative_searches(has_rights_to="deny-one") + self.assert_attr_visible(expect_attr=False) + + # sanity-check we haven't hidden the attribute from the admin as well +@@ -887,8 +837,7 @@ class ConfidentialAttrTestDirsync(ConfidentialAttrCommon): + attrs=['name']) + + # override method specifically for dirsync (total object count differs) +- def assert_negative_searches(self, has_rights_to=0, +- dc_mode=DC_MODE_RETURN_NONE, samdb=None): ++ def assert_negative_searches(self, has_rights_to=0, samdb=None): + """Asserts user without rights cannot see objects in '!' searches""" + + if samdb is None: +@@ -898,7 +847,6 @@ class ConfidentialAttrTestDirsync(ConfidentialAttrCommon): + # 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(): +@@ -917,8 +865,7 @@ class ConfidentialAttrTestDirsync(ConfidentialAttrCommon): + + self.assert_conf_attr_searches(has_rights_to=0) + self.assert_attr_visible(expect_attr=False) +- dc_mode = DC_MODE_RETURN_ALL +- self.assert_negative_searches(has_rights_to=0, dc_mode=dc_mode) ++ self.assert_negative_searches(has_rights_to=0) + + # as a final sanity-check, make sure the admin can still see the attr + self.assert_conf_attr_searches(has_rights_to="all", +@@ -1012,8 +959,7 @@ class ConfidentialAttrTestDirsync(ConfidentialAttrCommon): + # 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 = DC_MODE_RETURN_ALL +- self.assert_negative_searches(has_rights_to=0, dc_mode=dc_mode) ++ self.assert_negative_searches(has_rights_to=0) + + # now delete the users (except for the user whose LDB connection + # we're currently using) +@@ -1023,7 +969,7 @@ class ConfidentialAttrTestDirsync(ConfidentialAttrCommon): + + # 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) ++ self.assert_negative_searches(has_rights_to=0) + + def test_timing_attack(self): + # Create the machine account. +-- +2.25.1 + + diff -Nru samba-4.15.13+dfsg/debian/patches/CVE-2023-4154-04.patch samba-4.15.13+dfsg/debian/patches/CVE-2023-4154-04.patch --- samba-4.15.13+dfsg/debian/patches/CVE-2023-4154-04.patch 1970-01-01 00:00:00.000000000 +0000 +++ samba-4.15.13+dfsg/debian/patches/CVE-2023-4154-04.patch 2023-10-04 12:23:27.000000000 +0000 @@ -0,0 +1,179 @@ +From 4af1125bbb22d5f1acbf9542d2d04287a84848bf Mon Sep 17 00:00:00 2001 +From: Joseph Sutton +Date: Fri, 27 Jan 2023 07:43:40 +1300 +Subject: [PATCH 04/21] CVE-2023-4154 s4:dsdb:tests: Refactor confidential + attributes test + +Use more specific unittest methods, and remove unused code. + +BUG: https://bugzilla.samba.org/show_bug.cgi?id=15424 + +Signed-off-by: Joseph Sutton +Reviewed-by: Andrew Bartlett + +(cherry picked from commit 2e5d08c908b3fa48b9b374279a331061cb77bce3) +--- + .../dsdb/tests/python/confidential_attr.py | 69 +++++-------------- + 1 file changed, 16 insertions(+), 53 deletions(-) + +diff --git a/source4/dsdb/tests/python/confidential_attr.py b/source4/dsdb/tests/python/confidential_attr.py +index ac83f488061..eb75da6374f 100755 +--- a/source4/dsdb/tests/python/confidential_attr.py ++++ b/source4/dsdb/tests/python/confidential_attr.py +@@ -24,7 +24,6 @@ import sys + sys.path.insert(0, "bin/python") + + import samba +-import os + import random + import statistics + import time +@@ -136,9 +135,9 @@ class ConfidentialAttrCommon(samba.tests.TestCase): + # 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, +- "{0} searchFlags already {1}".format(self.conf_attr, +- search_flags)) ++ self.assertEqual(0, int(search_flags) & SEARCH_FLAG_CONFIDENTIAL, ++ "{0} searchFlags already {1}".format(self.conf_attr, ++ search_flags)) + + def tearDown(self): + super(ConfidentialAttrCommon, self).tearDown() +@@ -208,9 +207,9 @@ class ConfidentialAttrCommon(samba.tests.TestCase): + 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))) ++ self.assertEqual(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): +@@ -352,25 +351,6 @@ class ConfidentialAttrCommon(samba.tests.TestCase): + + 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 += ["(!({0}=*))".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. +@@ -403,7 +383,7 @@ class ConfidentialAttrCommon(samba.tests.TestCase): + # 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) ++ self.assertEqual(1, len(res)) + + attr_returned = False + for msg in res: +@@ -586,9 +566,9 @@ class ConfidentialAttrTestDenyAcl(ConfidentialAttrCommon): + 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))) ++ self.assertEqual(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: +@@ -604,7 +584,7 @@ class ConfidentialAttrTestDenyAcl(ConfidentialAttrCommon): + + # 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 ++ excl_testobj = has_rights_to == "deny-one" + + # these first few searches we just expect to match against the one + # object under test that we're trying to guess the value of +@@ -625,24 +605,6 @@ class ConfidentialAttrTestDenyAcl(ConfidentialAttrCommon): + self.assert_search_result(expected_num, search, samdb, + excl_testobj) + +- # override method specifically for deny ACL test cases. Instead of being +- # granted access to either no objects or only one, we are being denied +- # access to only one object (but can still access the rest). +- 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 = "(!({0}=*))".format(self.conf_attr) +- expected_results[search] = self.total_objects - self.objects_with_attr +- return expected_results +- + # override method specifically for deny ACL test cases + def negative_searches_return_all(self, has_rights_to=0, + total_objects=None): +@@ -783,7 +745,7 @@ class ConfidentialAttrTestDirsync(ConfidentialAttrCommon): + 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, ++ self.assertEqual(len(res), expected_num, + "%u results, not %u for search %s, attr %s" % + (len(res), expected_num, search, str(attr))) + +@@ -797,7 +759,8 @@ class ConfidentialAttrTestDirsync(ConfidentialAttrCommon): + 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) ++ if not no_result_ok: ++ self.assertEqual(1, len(res)) + + attr_returned = False + for msg in res: +@@ -888,7 +851,7 @@ class ConfidentialAttrTestDirsync(ConfidentialAttrCommon): + 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) ++ self.assertNotEqual(0, search_flags & SEARCH_FLAG_CONFIDENTIAL) + search_flags |= SEARCH_FLAG_PRESERVEONDELETE + + self.set_attr_search_flags(self.attr_dn, str(search_flags)) +@@ -964,7 +927,7 @@ class ConfidentialAttrTestDirsync(ConfidentialAttrCommon): + # 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: ++ if user is not self.user: + self.ldb_admin.delete(self.get_user_dn(user)) + + # check we still can't see the objects +-- +2.25.1 + + diff -Nru samba-4.15.13+dfsg/debian/patches/CVE-2023-4154-05.patch samba-4.15.13+dfsg/debian/patches/CVE-2023-4154-05.patch --- samba-4.15.13+dfsg/debian/patches/CVE-2023-4154-05.patch 1970-01-01 00:00:00.000000000 +0000 +++ samba-4.15.13+dfsg/debian/patches/CVE-2023-4154-05.patch 2023-10-04 12:23:30.000000000 +0000 @@ -0,0 +1,405 @@ +From 3174f3956d29ad638757770d81131c016314e54c Mon Sep 17 00:00:00 2001 +From: Andreas Schneider +Date: Wed, 2 Aug 2023 10:44:32 +0200 +Subject: [PATCH 05/21] CVE-2023-4154 s4:dsdb:tests: Fix code spelling + +BUG: https://bugzilla.samba.org/show_bug.cgi?id=15424 + +Signed-off-by: Andreas Schneider +Reviewed-by: Joseph Sutton + +[abartlet@samba.org adapted from commit b29793ffdee5d9b9c1c05830622e80f7faec7670 + but excluding changes to token_group.py due to conflicts in backporting to 4.16] +--- + source4/dsdb/tests/python/acl.py | 12 ++++++------ + .../dsdb/tests/python/ad_dc_search_performance.py | 2 +- + source4/dsdb/tests/python/confidential_attr.py | 2 +- + source4/dsdb/tests/python/dirsync.py | 8 ++++---- + source4/dsdb/tests/python/ldap.py | 14 +++++++------- + source4/dsdb/tests/python/ldap_modify_order.py | 4 ++-- + source4/dsdb/tests/python/ldap_syntaxes.py | 4 ++-- + source4/dsdb/tests/python/login_basics.py | 2 +- + source4/dsdb/tests/python/password_settings.py | 4 ++-- + source4/dsdb/tests/python/passwords.py | 4 ++-- + source4/dsdb/tests/python/sam.py | 2 +- + source4/dsdb/tests/python/sec_descriptor.py | 14 +++++++------- + source4/dsdb/tests/python/user_account_control.py | 2 +- + 13 files changed, 37 insertions(+), 37 deletions(-) + +diff --git a/source4/dsdb/tests/python/acl.py b/source4/dsdb/tests/python/acl.py +index 3e5f7a44a79..6d547a480a8 100755 +--- a/source4/dsdb/tests/python/acl.py ++++ b/source4/dsdb/tests/python/acl.py +@@ -174,7 +174,7 @@ class AclAddTests(AclTests): + self.assertEqual(len(res), 0) + + def test_add_u1(self): +- """Testing OU with the rights of Doman Admin not creator of the OU """ ++ """Testing OU with the rights of Domain Admin not creator of the OU """ + self.assert_top_ou_deleted() + # Change descriptor for top level OU + self.ldb_owner.create_ou("OU=test_add_ou1," + self.base_dn) +@@ -187,7 +187,7 @@ class AclAddTests(AclTests): + self.ldb_notowner.newgroup("test_add_group1", groupou="OU=test_add_ou2,OU=test_add_ou1", + grouptype=samba.dsdb.GTYPE_DISTRIBUTION_DOMAIN_LOCAL_GROUP) + # Make sure we HAVE created the two objects -- user and group +- # !!! We should not be able to do that, but however beacuse of ACE ordering our inherited Deny ACE ++ # !!! We should not be able to do that, but however because of ACE ordering our inherited Deny ACE + # !!! comes after explicit (A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA) that comes from somewhere + res = self.ldb_admin.search(self.base_dn, expression="(distinguishedName=%s,%s)" % ("CN=test_add_user1,OU=test_add_ou2,OU=test_add_ou1", self.base_dn)) + self.assertTrue(len(res) > 0) +@@ -248,7 +248,7 @@ class AclAddTests(AclTests): + self.assertEqual(len(res), 0) + + def test_add_u4(self): +- """ 4 Testing OU with the rights of Doman Admin creator of the OU""" ++ """ 4 Testing OU with the rights of Domain Admin creator of the OU""" + self.assert_top_ou_deleted() + self.ldb_owner.create_ou("OU=test_add_ou1," + self.base_dn) + self.ldb_owner.create_ou("OU=test_add_ou2,OU=test_add_ou1," + self.base_dn) +@@ -1133,7 +1133,7 @@ class AclDeleteTests(AclTests): + self.assertEqual(len(res), 0) + + def test_delete_u3(self): +- """User indentified by SID has RIGHT_DELETE to another User object""" ++ """User identified by SID has RIGHT_DELETE to another User object""" + user_dn = self.get_user_dn("test_delete_user1") + # Create user that we try to delete + self.ldb_admin.newuser("test_delete_user1", self.user_pass) +@@ -1424,7 +1424,7 @@ class AclCARTests(AclTests): + minPwdAge = self.ldb_admin.get_minPwdAge() + # Reset the "minPwdAge" as it was before + self.addCleanup(self.ldb_admin.set_minPwdAge, minPwdAge) +- # Set it temporarely to "0" ++ # Set it temporarily to "0" + self.ldb_admin.set_minPwdAge("0") + + self.user_with_wp = "acl_car_user1" +@@ -1880,7 +1880,7 @@ class AclUndeleteTests(AclTests): + self.sd_utils.dacl_add_ace(self.deleted_dn2, mod) + self.undelete_deleted(self.deleted_dn2, self.testuser2_dn) + +- # attempt undelete with simultanious addition of url, WP to which is denied ++ # attempt undelete with simultaneous addition of url, WP to which is denied + mod = "(OD;;WP;9a9a0221-4a5b-11d1-a9c3-0000f80367c1;;%s)" % str(self.sid) + self.sd_utils.dacl_add_ace(self.deleted_dn3, mod) + try: +diff --git a/source4/dsdb/tests/python/ad_dc_search_performance.py b/source4/dsdb/tests/python/ad_dc_search_performance.py +index 0afd7a2582e..44e468097d8 100644 +--- a/source4/dsdb/tests/python/ad_dc_search_performance.py ++++ b/source4/dsdb/tests/python/ad_dc_search_performance.py +@@ -180,7 +180,7 @@ class UserTests(samba.tests.TestCase): + maybe_not = ['!(', ''] + joiners = ['&', '|'] + +- # The number of permuations is 18432, which is not huge but ++ # The number of permutations is 18432, which is not huge but + # would take hours to search. So we take a sample. + all_permutations = list(itertools.product(joiners, + classes, classes, +diff --git a/source4/dsdb/tests/python/confidential_attr.py b/source4/dsdb/tests/python/confidential_attr.py +index eb75da6374f..8ca56bd1023 100755 +--- a/source4/dsdb/tests/python/confidential_attr.py ++++ b/source4/dsdb/tests/python/confidential_attr.py +@@ -722,7 +722,7 @@ class ConfidentialAttrTestDirsync(ConfidentialAttrCommon): + + self.attr_filters = [None, ["*"], ["name"]] + +- # Note dirsync behaviour is slighty different for the attribute under ++ # Note dirsync behaviour is slightly 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 +diff --git a/source4/dsdb/tests/python/dirsync.py b/source4/dsdb/tests/python/dirsync.py +index 1ac719e4332..ca0947e2d21 100755 +--- a/source4/dsdb/tests/python/dirsync.py ++++ b/source4/dsdb/tests/python/dirsync.py +@@ -338,7 +338,7 @@ class SimpleDirsyncTests(DirsyncBaseTests): + self.assertEqual(len(res.msgs[0]), 3) + + def test_dirsync_othernc(self): +- """Check that dirsync return information for entries that are normaly referrals (ie. other NCs)""" ++ """Check that dirsync return information for entries that are normally referrals (ie. other NCs)""" + res = self.ldb_admin.search(self.base_dn, + expression="(objectclass=configuration)", + attrs=["name"], +@@ -459,7 +459,7 @@ class SimpleDirsyncTests(DirsyncBaseTests): + delete_force(self.ldb_admin, ouname) + + def test_dirsync_linkedattributes(self): +- """Check that dirsync returnd deleted objects too""" ++ """Check that dirsync returned deleted objects too""" + # Let's search for members + self.ldb_simple = self.get_ldb_connection(self.simple_user, self.user_pass) + res = self.ldb_simple.search(self.base_dn, +@@ -541,7 +541,7 @@ class SimpleDirsyncTests(DirsyncBaseTests): + self.assertEqual(len(res[0].get("member")), 0) + + def test_dirsync_deleted_items(self): +- """Check that dirsync returnd deleted objects too""" ++ """Check that dirsync returned deleted objects too""" + # Let's create an OU + ouname = "OU=testou3,%s" % self.base_dn + self.ouname = ouname +@@ -712,7 +712,7 @@ class ExtendedDirsyncTests(SimpleDirsyncTests): + self.assertIn(b">; +Date: Tue, 14 Feb 2023 17:19:27 +1300 +Subject: [PATCH 06/21] CVE-2023-4154 s4-dsdb: Remove + DSDB_ACL_CHECKS_DIRSYNC_FLAG + +It's no longer used anywhere. + +BUG: https://bugzilla.samba.org/show_bug.cgi?id=15424 + +Signed-off-by: Joseph Sutton +Reviewed-by: Andrew Bartlett + +[abartlet@samba.org backported to Samba 4.16 from commit 8b4e6f7b3fb8018cb64deef9b8e1cbc2e5ba12cf] +--- + source4/dsdb/samdb/ldb_modules/dirsync.c | 11 ++--------- + source4/dsdb/samdb/samdb.h | 1 - + 2 files changed, 2 insertions(+), 10 deletions(-) + +diff --git a/source4/dsdb/samdb/ldb_modules/dirsync.c b/source4/dsdb/samdb/ldb_modules/dirsync.c +index fa57af49e8f..b3c463741c8 100644 +--- a/source4/dsdb/samdb/ldb_modules/dirsync.c ++++ b/source4/dsdb/samdb/ldb_modules/dirsync.c +@@ -1005,7 +1005,6 @@ static int dirsync_ldb_search(struct ldb_module *module, struct ldb_request *req + struct dirsync_context *dsc; + struct ldb_context *ldb; + struct ldb_parse_tree *new_tree = req->op.search.tree; +- uint32_t flags = 0; + enum ndr_err_code ndr_err; + DATA_BLOB blob; + const char **attrs; +@@ -1117,13 +1116,8 @@ static int dirsync_ldb_search(struct ldb_module *module, struct ldb_request *req + return ret; + } + talloc_free(acl_res); +- } else { +- flags |= DSDB_ACL_CHECKS_DIRSYNC_FLAG; +- +- if (ret != LDB_SUCCESS) { +- return ret; +- } +- ++ } else if (ret != LDB_SUCCESS) { ++ return ret; + } + + dsc->functional_level = dsdb_functional_level(ldb); +@@ -1394,7 +1388,6 @@ static int dirsync_ldb_search(struct ldb_module *module, struct ldb_request *req + req->controls, + dsc, dirsync_search_callback, + req); +- ldb_req_set_custom_flags(down_req, flags); + LDB_REQ_SET_LOCATION(down_req); + if (ret != LDB_SUCCESS) { + return ret; +diff --git a/source4/dsdb/samdb/samdb.h b/source4/dsdb/samdb/samdb.h +index 3c4c78822bf..e90e02609f3 100644 +--- a/source4/dsdb/samdb/samdb.h ++++ b/source4/dsdb/samdb/samdb.h +@@ -348,7 +348,6 @@ struct dsdb_extended_dn_store_format { + + #define DSDB_OPAQUE_PARTITION_MODULE_MSG_OPAQUE_NAME "DSDB_OPAQUE_PARTITION_MODULE_MSG" + +-#define DSDB_ACL_CHECKS_DIRSYNC_FLAG 0x1 + #define DSDB_SAMDB_MINIMUM_ALLOWED_RID 1000 + + #define DSDB_METADATA_SCHEMA_SEQ_NUM "SCHEMA_SEQ_NUM" +-- +2.25.1 + + diff -Nru samba-4.15.13+dfsg/debian/patches/CVE-2023-4154-07.patch samba-4.15.13+dfsg/debian/patches/CVE-2023-4154-07.patch --- samba-4.15.13+dfsg/debian/patches/CVE-2023-4154-07.patch 1970-01-01 00:00:00.000000000 +0000 +++ samba-4.15.13+dfsg/debian/patches/CVE-2023-4154-07.patch 2023-10-04 12:23:37.000000000 +0000 @@ -0,0 +1,170 @@ +From 4db59a437fdd2c10514173da0a3802f74eaf6a98 Mon Sep 17 00:00:00 2001 +From: Stefan Metzmacher +Date: Fri, 10 Mar 2023 18:25:18 +0100 +Subject: [PATCH 07/21] CVE-2023-4154 python:sd_utils: introduce + update_aces_in_dacl() helper + +This is a more generic api that can be re-used in other places +as well in future. It operates on a security descriptor object instead of +SDDL. + +BUG: https://bugzilla.samba.org/show_bug.cgi?id=15424 + +Signed-off-by: Stefan Metzmacher +Reviewed-by: Andrew Bartlett +Reviewed-by: Douglas Bagnall + +(cherry picked from commit 8411e6d302e25d10f1035ebbdcbde7308566e930) +--- + python/samba/sd_utils.py | 124 +++++++++++++++++++++++++++++++++++---- + 1 file changed, 112 insertions(+), 12 deletions(-) + +diff --git a/python/samba/sd_utils.py b/python/samba/sd_utils.py +index 26e80ee2f4a..52a78de5d09 100644 +--- a/python/samba/sd_utils.py ++++ b/python/samba/sd_utils.py +@@ -21,8 +21,11 @@ + import samba + from ldb import Message, MessageElement, Dn + from ldb import FLAG_MOD_REPLACE, SCOPE_BASE +-from samba.ndr import ndr_pack, ndr_unpack ++from samba.ndr import ndr_pack, ndr_unpack, ndr_deepcopy + from samba.dcerpc import security ++from samba.ntstatus import ( ++ NT_STATUS_OBJECT_NAME_NOT_FOUND, ++) + + + class SDUtils(object): +@@ -63,19 +66,116 @@ class SDUtils(object): + res = self.ldb.search(object_dn) + return ndr_unpack(security.dom_sid, res[0]["objectSid"][0]) + ++ def update_aces_in_dacl(self, dn, del_aces=None, add_aces=None, ++ sddl_attr=None, controls=None): ++ if del_aces is None: ++ del_aces=[] ++ if add_aces is None: ++ add_aces=[] ++ ++ def ace_from_sddl(ace_sddl): ++ ace_sd = security.descriptor.from_sddl("D:" + ace_sddl, self.domain_sid) ++ assert(len(ace_sd.dacl.aces)==1) ++ return ace_sd.dacl.aces[0] ++ ++ if sddl_attr is None: ++ if controls is None: ++ controls=["sd_flags:1:%d" % security.SECINFO_DACL] ++ sd = self.read_sd_on_dn(dn, controls=controls) ++ if not sd.type & security.SEC_DESC_DACL_PROTECTED: ++ # if the DACL is not protected remove all ++ # inherited aces, as they will be re-inherited ++ # on the server, we need a ndr_deepcopy in order ++ # to avoid reference problems while deleting ++ # the aces while looping over them ++ dacl_copy = ndr_deepcopy(sd.dacl) ++ for ace in dacl_copy.aces: ++ if ace.flags & security.SEC_ACE_FLAG_INHERITED_ACE: ++ try: ++ sd.dacl_del_ace(ace) ++ except samba.NTSTATUSError as err: ++ if err.args[0] != NT_STATUS_OBJECT_NAME_NOT_FOUND: ++ raise err ++ # dacl_del_ace may remove more than ++ # one ace, so we may not find it anymore ++ pass ++ else: ++ if controls is None: ++ controls=[] ++ res = self.ldb.search(dn, SCOPE_BASE, None, ++ [sddl_attr], controls=controls) ++ old_sddl = str(res[0][sddl_attr][0]) ++ sd = security.descriptor.from_sddl(old_sddl, self.domain_sid) ++ ++ num_changes = 0 ++ del_ignored = [] ++ add_ignored = [] ++ inherited_ignored = [] ++ ++ for ace in del_aces: ++ if isinstance(ace, str): ++ ace = ace_from_sddl(ace) ++ assert(isinstance(ace, security.ace)) ++ ++ if ace.flags & security.SEC_ACE_FLAG_INHERITED_ACE: ++ inherited_ignored.append(ace) ++ continue ++ ++ if ace not in sd.dacl.aces: ++ del_ignored.append(ace) ++ continue ++ ++ sd.dacl_del_ace(ace) ++ num_changes += 1 ++ ++ for ace in add_aces: ++ add_idx = -1 ++ if isinstance(ace, dict): ++ if "idx" in ace: ++ add_idx = ace["idx"] ++ ace = ace["ace"] ++ if isinstance(ace, str): ++ ace = ace_from_sddl(ace) ++ assert(isinstance(ace, security.ace)) ++ ++ if ace.flags & security.SEC_ACE_FLAG_INHERITED_ACE: ++ inherited_ignored.append(ace) ++ continue ++ ++ if ace in sd.dacl.aces: ++ add_ignored.append(ace) ++ continue ++ ++ sd.dacl_add(ace, add_idx) ++ num_changes += 1 ++ ++ if num_changes == 0: ++ return del_ignored, add_ignored, inherited_ignored ++ ++ if sddl_attr is None: ++ self.modify_sd_on_dn(dn, sd, controls=controls) ++ else: ++ new_sddl = sd.as_sddl(self.domain_sid) ++ m = Message() ++ m.dn = dn ++ m[sddl_attr] = MessageElement(new_sddl.encode('ascii'), ++ FLAG_MOD_REPLACE, ++ sddl_attr) ++ self.ldb.modify(m, controls=controls) ++ ++ return del_ignored, add_ignored, inherited_ignored ++ + def dacl_add_ace(self, object_dn, ace): +- """Add an ACE to an objects security descriptor ++ """Add an ACE (or more) to an objects security descriptor + """ +- desc = self.read_sd_on_dn(object_dn, ["show_deleted:1"]) +- desc_sddl = desc.as_sddl(self.domain_sid) +- if ace in desc_sddl: +- return +- if desc_sddl.find("(") >= 0: +- desc_sddl = (desc_sddl[:desc_sddl.index("(")] + ace + +- desc_sddl[desc_sddl.index("("):]) +- else: +- desc_sddl = desc_sddl + ace +- self.modify_sd_on_dn(object_dn, desc_sddl, ["show_deleted:1"]) ++ ace_sd = security.descriptor.from_sddl("D:" + ace, self.domain_sid) ++ add_aces = [] ++ add_idx = 0 ++ for ace in ace_sd.dacl.aces: ++ add_aces.append({"idx": add_idx, "ace": ace}) ++ add_idx += 1 ++ _,_,_ = self.update_aces_in_dacl(object_dn, add_aces=add_aces, ++ controls=["show_deleted:1"]) + + def get_sd_as_sddl(self, object_dn, controls=[]): + """Return object nTSecutiryDescriptor in SDDL format +-- +2.25.1 + + diff -Nru samba-4.15.13+dfsg/debian/patches/CVE-2023-4154-08.patch samba-4.15.13+dfsg/debian/patches/CVE-2023-4154-08.patch --- samba-4.15.13+dfsg/debian/patches/CVE-2023-4154-08.patch 1970-01-01 00:00:00.000000000 +0000 +++ samba-4.15.13+dfsg/debian/patches/CVE-2023-4154-08.patch 2023-10-04 12:23:41.000000000 +0000 @@ -0,0 +1,85 @@ +From 16d563646ab1622830ce43e539463d13121d04f1 Mon Sep 17 00:00:00 2001 +From: Stefan Metzmacher +Date: Thu, 16 Mar 2023 18:03:10 +0100 +Subject: [PATCH 08/21] CVE-2023-4154 python:sd_utils: add + dacl_{prepend,append,delete}_aces() helpers + +They better represent what they are doing, we keep dacl_add_ace() +as wrapper of dacl_prepend_aces() in order to let existing callers +work as before. + +In future it would be good to have a dacl_insert_aces() that +would canonicalize the ace order before storing, but that a task +for another day. + +BUG: https://bugzilla.samba.org/show_bug.cgi?id=15424 + +Signed-off-by: Stefan Metzmacher +Reviewed-by: Andrew Bartlett +Reviewed-by: Douglas Bagnall + +(cherry picked from commit a1109a9bf12e020636b8d66fc54984aac58bfe6b) +--- + python/samba/sd_utils.py | 39 ++++++++++++++++++++++++++++++++++----- + 1 file changed, 34 insertions(+), 5 deletions(-) + +diff --git a/python/samba/sd_utils.py b/python/samba/sd_utils.py +index 52a78de5d09..462bbfbaf18 100644 +--- a/python/samba/sd_utils.py ++++ b/python/samba/sd_utils.py +@@ -165,17 +165,46 @@ class SDUtils(object): + + return del_ignored, add_ignored, inherited_ignored + +- def dacl_add_ace(self, object_dn, ace): +- """Add an ACE (or more) to an objects security descriptor ++ def dacl_prepend_aces(self, object_dn, aces, controls=None): ++ """Prepend an ACE (or more) to an objects security descriptor + """ +- ace_sd = security.descriptor.from_sddl("D:" + ace, self.domain_sid) ++ ace_sd = security.descriptor.from_sddl("D:" + aces, self.domain_sid) + add_aces = [] + add_idx = 0 + for ace in ace_sd.dacl.aces: + add_aces.append({"idx": add_idx, "ace": ace}) + add_idx += 1 +- _,_,_ = self.update_aces_in_dacl(object_dn, add_aces=add_aces, +- controls=["show_deleted:1"]) ++ _,ai,ii = self.update_aces_in_dacl(object_dn, add_aces=add_aces, ++ controls=controls) ++ return ai, ii ++ ++ def dacl_add_ace(self, object_dn, ace): ++ """Add an ACE (or more) to an objects security descriptor ++ """ ++ _,_ = self.dacl_prepend_aces(object_dn, ace, ++ controls=["show_deleted:1"]) ++ ++ def dacl_append_aces(self, object_dn, aces, controls=None): ++ """Append an ACE (or more) to an objects security descriptor ++ """ ++ ace_sd = security.descriptor.from_sddl("D:" + aces, self.domain_sid) ++ add_aces = [] ++ for ace in ace_sd.dacl.aces: ++ add_aces.append(ace) ++ _,ai,ii = self.update_aces_in_dacl(object_dn, add_aces=add_aces, ++ controls=controls) ++ return ai, ii ++ ++ def dacl_delete_aces(self, object_dn, aces, controls=None): ++ """Delete an ACE (or more) to an objects security descriptor ++ """ ++ del_sd = security.descriptor.from_sddl("D:" + aces, self.domain_sid) ++ del_aces = [] ++ for ace in del_sd.dacl.aces: ++ del_aces.append(ace) ++ di,_,ii = self.update_aces_in_dacl(object_dn, del_aces=del_aces, ++ controls=controls) ++ return di, ii + + def get_sd_as_sddl(self, object_dn, controls=[]): + """Return object nTSecutiryDescriptor in SDDL format +-- +2.25.1 + + diff -Nru samba-4.15.13+dfsg/debian/patches/CVE-2023-4154-09.patch samba-4.15.13+dfsg/debian/patches/CVE-2023-4154-09.patch --- samba-4.15.13+dfsg/debian/patches/CVE-2023-4154-09.patch 1970-01-01 00:00:00.000000000 +0000 +++ samba-4.15.13+dfsg/debian/patches/CVE-2023-4154-09.patch 2023-10-04 12:23:45.000000000 +0000 @@ -0,0 +1,58 @@ +From 4f91be132478a47ce3e0099407946840ae40844d Mon Sep 17 00:00:00 2001 +From: Stefan Metzmacher +Date: Thu, 16 Mar 2023 10:11:05 +0100 +Subject: [PATCH 09/21] CVE-2023-4154 py_security: allow idx argument to + descriptor.[s|d]acl_add() + +BUG: https://bugzilla.samba.org/show_bug.cgi?id=15424 + +Signed-off-by: Stefan Metzmacher +Reviewed-by: Andrew Bartlett +Reviewed-by: Douglas Bagnall + +(cherry picked from commit 9ea06aaf9f57e3c7094553d9ac40fb73057a9b74) +--- + source4/librpc/ndr/py_security.c | 10 ++++++---- + 1 file changed, 6 insertions(+), 4 deletions(-) + +diff --git a/source4/librpc/ndr/py_security.c b/source4/librpc/ndr/py_security.c +index e61b994d7cb..4a8271a11db 100644 +--- a/source4/librpc/ndr/py_security.c ++++ b/source4/librpc/ndr/py_security.c +@@ -175,12 +175,13 @@ static PyObject *py_descriptor_sacl_add(PyObject *self, PyObject *args) + NTSTATUS status; + struct security_ace *ace; + PyObject *py_ace; ++ Py_ssize_t idx = -1; + +- if (!PyArg_ParseTuple(args, "O", &py_ace)) ++ if (!PyArg_ParseTuple(args, "O|n", &py_ace, &idx)) + return NULL; + + ace = pytalloc_get_ptr(py_ace); +- status = security_descriptor_sacl_add(desc, ace); ++ status = security_descriptor_sacl_insert(desc, ace, idx); + PyErr_NTSTATUS_IS_ERR_RAISE(status); + Py_RETURN_NONE; + } +@@ -191,13 +192,14 @@ static PyObject *py_descriptor_dacl_add(PyObject *self, PyObject *args) + NTSTATUS status; + struct security_ace *ace; + PyObject *py_ace; ++ Py_ssize_t idx = -1; + +- if (!PyArg_ParseTuple(args, "O", &py_ace)) ++ if (!PyArg_ParseTuple(args, "O|n", &py_ace, &idx)) + return NULL; + + ace = pytalloc_get_ptr(py_ace); + +- status = security_descriptor_dacl_add(desc, ace); ++ status = security_descriptor_dacl_insert(desc, ace, idx); + PyErr_NTSTATUS_IS_ERR_RAISE(status); + Py_RETURN_NONE; + } +-- +2.25.1 + + diff -Nru samba-4.15.13+dfsg/debian/patches/CVE-2023-4154-10.patch samba-4.15.13+dfsg/debian/patches/CVE-2023-4154-10.patch --- samba-4.15.13+dfsg/debian/patches/CVE-2023-4154-10.patch 1970-01-01 00:00:00.000000000 +0000 +++ samba-4.15.13+dfsg/debian/patches/CVE-2023-4154-10.patch 2023-10-04 12:23:48.000000000 +0000 @@ -0,0 +1,54 @@ +From dd6cf174bac5fb7a5d43d348814715dde985ea1b Mon Sep 17 00:00:00 2001 +From: Stefan Metzmacher +Date: Fri, 17 Mar 2023 14:08:34 +0100 +Subject: [PATCH 10/21] CVE-2023-4154 python/samba/ndr: add ndr_deepcopy() + helper + +This uses ndr_pack/unpack in order to create a deep copy +of the given object. + +BUG: https://bugzilla.samba.org/show_bug.cgi?id=15424 + +Signed-off-by: Stefan Metzmacher +Reviewed-by: Andrew Bartlett +Reviewed-by: Douglas Bagnall + +(cherry picked from commit 4627997ddae44265ad35b3234232eb74458c6c34) +--- + python/samba/ndr.py | 19 +++++++++++++++++++ + 1 file changed, 19 insertions(+) + +diff --git a/python/samba/ndr.py b/python/samba/ndr.py +index 35b2414e8ae..8369abfb2d0 100644 +--- a/python/samba/ndr.py ++++ b/python/samba/ndr.py +@@ -56,6 +56,25 @@ def ndr_print(object): + return ndr_print() + + ++def ndr_deepcopy(object): ++ """Create a deep copy of a NDR object, using pack/unpack ++ ++ :param object: Object to copy ++ :return: The object copy ++ """ ++ ndr_pack = getattr(object, "__ndr_pack__", None) ++ if ndr_pack is None: ++ raise TypeError("%r is not a NDR object" % object) ++ data = ndr_pack() ++ cls = type(object) ++ copy = cls() ++ ndr_unpack = getattr(copy, "__ndr_unpack__", None) ++ if ndr_unpack is None: ++ raise TypeError("%r is not a NDR object" % copy) ++ ndr_unpack(data, allow_remaining=False) ++ return copy ++ ++ + def ndr_pack_in(object, bigendian=False, ndr64=False): + """Pack the input of an NDR function object. + +-- +2.25.1 + + diff -Nru samba-4.15.13+dfsg/debian/patches/CVE-2023-4154-11.patch samba-4.15.13+dfsg/debian/patches/CVE-2023-4154-11.patch --- samba-4.15.13+dfsg/debian/patches/CVE-2023-4154-11.patch 1970-01-01 00:00:00.000000000 +0000 +++ samba-4.15.13+dfsg/debian/patches/CVE-2023-4154-11.patch 2023-10-04 12:23:56.000000000 +0000 @@ -0,0 +1,41 @@ +From 5f903cadbbc0b7276ff0494a58c39f4aed533dba Mon Sep 17 00:00:00 2001 +From: Stefan Metzmacher +Date: Thu, 16 Mar 2023 09:57:43 +0100 +Subject: [PATCH 11/21] CVE-2023-4154 replace: add ARRAY_INSERT_ELEMENT() + helper + +BUG: https://bugzilla.samba.org/show_bug.cgi?id=15424 + +Signed-off-by: Stefan Metzmacher +Reviewed-by: Andrew Bartlett +Reviewed-by: Douglas Bagnall + +(cherry picked from commit 9d8ff0d1e0b2ba7c84af36e1931f5bc99902a44b) +--- + lib/replace/replace.h | 15 +++++++++++++++ + 1 file changed, 15 insertions(+) + +--- a/lib/replace/replace.h ++++ b/lib/replace/replace.h +@@ -861,6 +861,21 @@ typedef unsigned long long ptrdiff_t ; + if((i)<((n)-1)){memmove(&((a)[(i)]),&((a)[(i)+1]),(sizeof(*(a))*((n)-(i)-1)));} + + /** ++ * Insert an array element by moving the rest one up ++ * ++ */ ++#define ARRAY_INSERT_ELEMENT(__array,__old_last_idx,__new_elem,__new_idx) do { \ ++ if ((__new_idx) < (__old_last_idx)) { \ ++ const void *__src = &((__array)[(__new_idx)]); \ ++ void *__dst = &((__array)[(__new_idx)+1]); \ ++ size_t __num = (__old_last_idx)-(__new_idx); \ ++ size_t __len = sizeof(*(__array)) * __num; \ ++ memmove(__dst, __src, __len); \ ++ } \ ++ (__array)[(__new_idx)] = (__new_elem); \ ++} while(0) ++ ++/** + * Pointer difference macro + */ + #define PTR_DIFF(p1,p2) ((ptrdiff_t)(((const char *)(p1)) - (const char *)(p2))) diff -Nru samba-4.15.13+dfsg/debian/patches/CVE-2023-4154-12.patch samba-4.15.13+dfsg/debian/patches/CVE-2023-4154-12.patch --- samba-4.15.13+dfsg/debian/patches/CVE-2023-4154-12.patch 1970-01-01 00:00:00.000000000 +0000 +++ samba-4.15.13+dfsg/debian/patches/CVE-2023-4154-12.patch 2023-10-04 12:23:59.000000000 +0000 @@ -0,0 +1,100 @@ +From 85794f8e33991fc935cd63d457b2e7678b0fdd26 Mon Sep 17 00:00:00 2001 +From: Stefan Metzmacher +Date: Thu, 16 Mar 2023 10:00:11 +0100 +Subject: [PATCH 12/21] CVE-2023-4154 libcli/security: prepare + security_descriptor_acl_add() to place the ace at a position + +Often it is important to insert an ace at a specific position in the +ACL. As a default we still append by default by using -1, which is the +generic version of passing the number of existing aces. + +BUG: https://bugzilla.samba.org/show_bug.cgi?id=15424 + +Signed-off-by: Stefan Metzmacher +Reviewed-by: Andrew Bartlett +Reviewed-by: Douglas Bagnall + +(cherry picked from commit c3cb915a67aff6739b72b86d7d139609df309ada) +--- + libcli/security/security_descriptor.c | 27 ++++++++++++++++++++------- + 1 file changed, 20 insertions(+), 7 deletions(-) + +diff --git a/libcli/security/security_descriptor.c b/libcli/security/security_descriptor.c +index 64c2d027876..8657c797364 100644 +--- a/libcli/security/security_descriptor.c ++++ b/libcli/security/security_descriptor.c +@@ -267,9 +267,11 @@ NTSTATUS security_descriptor_for_client(TALLOC_CTX *mem_ctx, + + static NTSTATUS security_descriptor_acl_add(struct security_descriptor *sd, + bool add_to_sacl, +- const struct security_ace *ace) ++ const struct security_ace *ace, ++ ssize_t _idx) + { + struct security_acl *acl = NULL; ++ ssize_t idx; + + if (add_to_sacl) { + acl = sd->sacl; +@@ -288,15 +290,28 @@ static NTSTATUS security_descriptor_acl_add(struct security_descriptor *sd, + acl->aces = NULL; + } + ++ if (_idx < 0) { ++ idx = (acl->num_aces + 1) + _idx; ++ } else { ++ idx = _idx; ++ } ++ ++ if (idx < 0) { ++ return NT_STATUS_ARRAY_BOUNDS_EXCEEDED; ++ } else if (idx > acl->num_aces) { ++ return NT_STATUS_ARRAY_BOUNDS_EXCEEDED; ++ } ++ + acl->aces = talloc_realloc(acl, acl->aces, + struct security_ace, acl->num_aces+1); + if (acl->aces == NULL) { + return NT_STATUS_NO_MEMORY; + } + +- acl->aces[acl->num_aces] = *ace; ++ ARRAY_INSERT_ELEMENT(acl->aces, acl->num_aces, *ace, idx); ++ acl->num_aces++; + +- switch (acl->aces[acl->num_aces].type) { ++ switch (acl->aces[idx].type) { + case SEC_ACE_TYPE_ACCESS_ALLOWED_OBJECT: + case SEC_ACE_TYPE_ACCESS_DENIED_OBJECT: + case SEC_ACE_TYPE_SYSTEM_AUDIT_OBJECT: +@@ -307,8 +322,6 @@ static NTSTATUS security_descriptor_acl_add(struct security_descriptor *sd, + break; + } + +- acl->num_aces++; +- + if (add_to_sacl) { + sd->sacl = acl; + sd->type |= SEC_DESC_SACL_PRESENT; +@@ -327,7 +340,7 @@ static NTSTATUS security_descriptor_acl_add(struct security_descriptor *sd, + NTSTATUS security_descriptor_sacl_add(struct security_descriptor *sd, + const struct security_ace *ace) + { +- return security_descriptor_acl_add(sd, true, ace); ++ return security_descriptor_acl_add(sd, true, ace, -1); + } + + /* +@@ -337,7 +350,7 @@ NTSTATUS security_descriptor_sacl_add(struct security_descriptor *sd, + NTSTATUS security_descriptor_dacl_add(struct security_descriptor *sd, + const struct security_ace *ace) + { +- return security_descriptor_acl_add(sd, false, ace); ++ return security_descriptor_acl_add(sd, false, ace, -1); + } + + /* +-- +2.25.1 + + diff -Nru samba-4.15.13+dfsg/debian/patches/CVE-2023-4154-13.patch samba-4.15.13+dfsg/debian/patches/CVE-2023-4154-13.patch --- samba-4.15.13+dfsg/debian/patches/CVE-2023-4154-13.patch 1970-01-01 00:00:00.000000000 +0000 +++ samba-4.15.13+dfsg/debian/patches/CVE-2023-4154-13.patch 2023-10-04 12:24:03.000000000 +0000 @@ -0,0 +1,87 @@ +From f16312845f5580791880f845feca75f1a2e5d3dc Mon Sep 17 00:00:00 2001 +From: Stefan Metzmacher +Date: Thu, 16 Mar 2023 10:03:44 +0100 +Subject: [PATCH 13/21] CVE-2023-4154 libcli/security: add + security_descriptor_[s|d]acl_insert() helpers + +BUG: https://bugzilla.samba.org/show_bug.cgi?id=15424 + +Signed-off-by: Stefan Metzmacher +Reviewed-by: Andrew Bartlett +Reviewed-by: Douglas Bagnall + +(cherry picked from commit 2c02378029fff6636b8f19e45af78b265f2210ed) +--- + libcli/security/security_descriptor.c | 28 +++++++++++++++++++++++++++ + libcli/security/security_descriptor.h | 6 ++++++ + 2 files changed, 34 insertions(+) + +diff --git a/libcli/security/security_descriptor.c b/libcli/security/security_descriptor.c +index 8657c797364..08f2cf19ee8 100644 +--- a/libcli/security/security_descriptor.c ++++ b/libcli/security/security_descriptor.c +@@ -343,6 +343,20 @@ NTSTATUS security_descriptor_sacl_add(struct security_descriptor *sd, + return security_descriptor_acl_add(sd, true, ace, -1); + } + ++/* ++ insert an ACE at a given index to the SACL of a security_descriptor ++ ++ idx can be negative, which means it's related to the new size from the ++ end, so -1 means the ace is appended at the end. ++*/ ++ ++NTSTATUS security_descriptor_sacl_insert(struct security_descriptor *sd, ++ const struct security_ace *ace, ++ ssize_t idx) ++{ ++ return security_descriptor_acl_add(sd, true, ace, idx); ++} ++ + /* + add an ACE to the DACL of a security_descriptor + */ +@@ -353,6 +367,20 @@ NTSTATUS security_descriptor_dacl_add(struct security_descriptor *sd, + return security_descriptor_acl_add(sd, false, ace, -1); + } + ++/* ++ insert an ACE at a given index to the DACL of a security_descriptor ++ ++ idx can be negative, which means it's related to the new size from the ++ end, so -1 means the ace is appended at the end. ++*/ ++ ++NTSTATUS security_descriptor_dacl_insert(struct security_descriptor *sd, ++ const struct security_ace *ace, ++ ssize_t idx) ++{ ++ return security_descriptor_acl_add(sd, false, ace, idx); ++} ++ + /* + delete the ACE corresponding to the given trustee in an ACL of a + security_descriptor +diff --git a/libcli/security/security_descriptor.h b/libcli/security/security_descriptor.h +index 46545321d15..354bc17e925 100644 +--- a/libcli/security/security_descriptor.h ++++ b/libcli/security/security_descriptor.h +@@ -33,8 +33,14 @@ NTSTATUS security_descriptor_for_client(TALLOC_CTX *mem_ctx, + struct security_descriptor **_csd); + NTSTATUS security_descriptor_sacl_add(struct security_descriptor *sd, + const struct security_ace *ace); ++NTSTATUS security_descriptor_sacl_insert(struct security_descriptor *sd, ++ const struct security_ace *ace, ++ ssize_t idx); + NTSTATUS security_descriptor_dacl_add(struct security_descriptor *sd, + const struct security_ace *ace); ++NTSTATUS security_descriptor_dacl_insert(struct security_descriptor *sd, ++ const struct security_ace *ace, ++ ssize_t idx); + NTSTATUS security_descriptor_dacl_del(struct security_descriptor *sd, + const struct dom_sid *trustee); + NTSTATUS security_descriptor_sacl_del(struct security_descriptor *sd, +-- +2.25.1 + + diff -Nru samba-4.15.13+dfsg/debian/patches/CVE-2023-4154-14.patch samba-4.15.13+dfsg/debian/patches/CVE-2023-4154-14.patch --- samba-4.15.13+dfsg/debian/patches/CVE-2023-4154-14.patch 1970-01-01 00:00:00.000000000 +0000 +++ samba-4.15.13+dfsg/debian/patches/CVE-2023-4154-14.patch 2023-10-04 12:24:14.000000000 +0000 @@ -0,0 +1,60 @@ +From 299845365be6ddc29238e0903d20463a3f509c8a Mon Sep 17 00:00:00 2001 +From: Andrew Bartlett +Date: Mon, 7 Aug 2023 11:55:55 +1200 +Subject: [PATCH 14/21] CVE-2023-4154 dsdb/tests: Do not run SimpleDirsyncTests + twice + +To re-use setup code, the super-class must have no test_*() methods +otherwise these will be run as well as the class-local tests. + +We rename tests that would otherwise have duplicate names + +BUG: https://bugzilla.samba.org/show_bug.cgi?id=15424 + +Signed-off-by: Andrew Bartlett +--- + selftest/knownfail | 2 +- + source4/dsdb/tests/python/dirsync.py | 7 ++----- + 2 files changed, 3 insertions(+), 6 deletions(-) + +--- a/selftest/knownfail ++++ b/selftest/knownfail +@@ -149,7 +149,7 @@ + ^samba4.smb2.acls.*.inheritflags + ^samba4.smb2.acls.*.owner + ^samba4.smb2.acls.*.ACCESSBASED +-^samba4.ldap.dirsync.python.ad_dc_ntvfs..__main__.ExtendedDirsyncTests.test_dirsync_deleted_items ++^samba4.ldap.dirsync.python.ad_dc_ntvfs..__main__.SimpleDirsyncTests.test_dirsync_deleted_items_OBJECT_SECURITY + #^samba4.ldap.dirsync.python.ad_dc_ntvfs..__main__.ExtendedDirsyncTests.* + ^samba4.libsmbclient.opendir.(NT1|SMB3).opendir # This requires netbios browsing + ^samba4.rpc.drsuapi.*.drsuapi.DsGetDomainControllerInfo\(.*\)$ +--- a/source4/dsdb/tests/python/dirsync.py ++++ b/source4/dsdb/tests/python/dirsync.py +@@ -458,7 +458,7 @@ class SimpleDirsyncTests(DirsyncBaseTest + self.assertTrue(res[0].get("name") is not None) + delete_force(self.ldb_admin, ouname) + +- def test_dirsync_linkedattributes(self): ++ def test_dirsync_linkedattributes_OBJECT_SECURITY(self): + """Check that dirsync returned deleted objects too""" + # Let's search for members + self.ldb_simple = self.get_ldb_connection(self.simple_user, self.user_pass) +@@ -585,9 +585,6 @@ class SimpleDirsyncTests(DirsyncBaseTest + expression="(&(objectClass=organizationalUnit)(!(isDeleted=*)))", + controls=controls) + +- +-class ExtendedDirsyncTests(SimpleDirsyncTests): +- + def test_dirsync_linkedattributes_range(self): + self.ldb_simple = self.get_ldb_connection(self.simple_user, self.user_pass) + res = self.ldb_admin.search(self.base_dn, +@@ -711,7 +708,7 @@ class ExtendedDirsyncTests(SimpleDirsync + self.assertIn(b"; +Date: Mon, 7 Aug 2023 13:15:40 +1200 +Subject: [PATCH 15/21] CVE-2023-4154 dsdb/tests: Use self.addCleanup() and + delete_force() + +Thie helps ensure this test is reliable even in spite of errors while +running. + +BUG: https://bugzilla.samba.org/show_bug.cgi?id=15424 + +Signed-off-by: Andrew Bartlett +--- + source4/dsdb/tests/python/confidential_attr.py | 6 ++---- + source4/dsdb/tests/python/dirsync.py | 6 +----- + 2 files changed, 3 insertions(+), 9 deletions(-) + +diff --git a/source4/dsdb/tests/python/confidential_attr.py b/source4/dsdb/tests/python/confidential_attr.py +index 8ca56bd1023..3997848f8f9 100755 +--- a/source4/dsdb/tests/python/confidential_attr.py ++++ b/source4/dsdb/tests/python/confidential_attr.py +@@ -98,7 +98,9 @@ class ConfidentialAttrCommon(samba.tests.TestCase): + + userou = "OU=conf-attr-test" + self.ou = "{0},{1}".format(userou, self.base_dn) ++ samba.tests.delete_force(self.ldb_admin, self.ou, controls=['tree_delete:1']) + self.ldb_admin.create_ou(self.ou) ++ self.addCleanup(samba.tests.delete_force, self.ldb_admin, self.ou, controls=['tree_delete:1']) + + # use a common username prefix, so we can use sAMAccountName=CATC-* as + # a search filter to only return the users we're interested in +@@ -139,10 +141,6 @@ class ConfidentialAttrCommon(samba.tests.TestCase): + "{0} searchFlags already {1}".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) +diff --git a/source4/dsdb/tests/python/dirsync.py b/source4/dsdb/tests/python/dirsync.py +index ad136b7efee..e06b85bc749 100755 +--- a/source4/dsdb/tests/python/dirsync.py ++++ b/source4/dsdb/tests/python/dirsync.py +@@ -137,10 +137,6 @@ class SimpleDirsyncTests(DirsyncBaseTests): + if self.ouname: + delete_force(self.ldb_admin, self.ouname) + self.sd_utils.modify_sd_on_dn(self.base_dn, self.desc_sddl) +- try: +- self.ldb_admin.deletegroup("testgroup") +- except Exception: +- pass + + # def test_dirsync_errors(self): + +@@ -499,6 +495,7 @@ class SimpleDirsyncTests(DirsyncBaseTests): + self.assertEqual(len(res[0].get("member")), size) + + self.ldb_admin.newgroup("testgroup") ++ self.addCleanup(self.ldb_admin.deletegroup, "testgroup") + self.ldb_admin.add_remove_group_members("testgroup", [self.simple_user], + add_members_operation=True) + +@@ -537,7 +534,6 @@ class SimpleDirsyncTests(DirsyncBaseTests): + attrs=["member"], + controls=[control1]) + +- self.ldb_admin.deletegroup("testgroup") + self.assertEqual(len(res[0].get("member")), 0) + + def test_dirsync_deleted_items(self): +-- +2.25.1 + + diff -Nru samba-4.15.13+dfsg/debian/patches/CVE-2023-4154-16.patch samba-4.15.13+dfsg/debian/patches/CVE-2023-4154-16.patch --- samba-4.15.13+dfsg/debian/patches/CVE-2023-4154-16.patch 1970-01-01 00:00:00.000000000 +0000 +++ samba-4.15.13+dfsg/debian/patches/CVE-2023-4154-16.patch 2023-10-04 12:24:21.000000000 +0000 @@ -0,0 +1,41 @@ +From e6cf59318b27efc9d129fc4aa51fb168762252d6 Mon Sep 17 00:00:00 2001 +From: Andrew Bartlett +Date: Mon, 7 Aug 2023 14:44:28 +1200 +Subject: [PATCH 16/21] CVE-2023-4154 dsdb/tests: Force the test attribute to + be not-confidential at the start + +Rather than fail, if the last run failed to reset things, just force +the DC into the required state. + +BUG: https://bugzilla.samba.org/show_bug.cgi?id=15424 + +Signed-off-by: Andrew Bartlett +--- + source4/dsdb/tests/python/confidential_attr.py | 10 ++++++---- + 1 file changed, 6 insertions(+), 4 deletions(-) + +diff --git a/source4/dsdb/tests/python/confidential_attr.py b/source4/dsdb/tests/python/confidential_attr.py +index 3997848f8f9..ee7f554a008 100755 +--- a/source4/dsdb/tests/python/confidential_attr.py ++++ b/source4/dsdb/tests/python/confidential_attr.py +@@ -136,10 +136,12 @@ class ConfidentialAttrCommon(samba.tests.TestCase): + + # 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.assertEqual(0, int(search_flags) & SEARCH_FLAG_CONFIDENTIAL, +- "{0} searchFlags already {1}".format(self.conf_attr, +- search_flags)) ++ search_flags = int(self.get_attr_search_flags(self.attr_dn)) ++ if search_flags & SEARCH_FLAG_CONFIDENTIAL: ++ self.set_attr_search_flags(self.attr_dn, str(search_flags &~ SEARCH_FLAG_CONFIDENTIAL)) ++ search_flags = int(self.get_attr_search_flags(self.attr_dn)) ++ self.assertEqual(0, search_flags & SEARCH_FLAG_CONFIDENTIAL, ++ f"{self.conf_attr} searchFlags did not reset to omit SEARCH_FLAG_CONFIDENTIAL ({search_flags})") + + def add_attr(self, dn, attr, value): + m = Message() +-- +2.25.1 + + diff -Nru samba-4.15.13+dfsg/debian/patches/CVE-2023-4154-17.patch samba-4.15.13+dfsg/debian/patches/CVE-2023-4154-17.patch --- samba-4.15.13+dfsg/debian/patches/CVE-2023-4154-17.patch 1970-01-01 00:00:00.000000000 +0000 +++ samba-4.15.13+dfsg/debian/patches/CVE-2023-4154-17.patch 2023-10-04 12:24:24.000000000 +0000 @@ -0,0 +1,50 @@ +From fb9bda913d7534207564f84edb41741177388ebc Mon Sep 17 00:00:00 2001 +From: Andrew Bartlett +Date: Mon, 7 Aug 2023 11:56:56 +1200 +Subject: [PATCH 17/21] CVE-2023-4154 dsdb/tests: Check that secret attributes + are not visible with DirSync ever. + +BUG: https://bugzilla.samba.org/show_bug.cgi?id=15424 + +Signed-off-by: Andrew Bartlett +--- + selftest/knownfail.d/dirsync | 1 + + source4/dsdb/tests/python/dirsync.py | 12 ++++++++++++ + 2 files changed, 13 insertions(+) + create mode 100644 selftest/knownfail.d/dirsync + +diff --git a/selftest/knownfail.d/dirsync b/selftest/knownfail.d/dirsync +new file mode 100644 +index 00000000000..9367f92e109 +--- /dev/null ++++ b/selftest/knownfail.d/dirsync +@@ -0,0 +1 @@ ++^samba4.ldap.dirsync.python\(.*\).__main__.SimpleDirsyncTests.test_dirsync_unicodePwd +\ No newline at end of file +diff --git a/source4/dsdb/tests/python/dirsync.py b/source4/dsdb/tests/python/dirsync.py +index e06b85bc749..2cacaf01251 100755 +--- a/source4/dsdb/tests/python/dirsync.py ++++ b/source4/dsdb/tests/python/dirsync.py +@@ -742,6 +742,18 @@ class SimpleDirsyncTests(DirsyncBaseTests): + self.assertEqual(guid2, guid) + self.assertEqual(str(res[0].dn), "") + ++ def test_dirsync_unicodePwd(self): ++ res = self.ldb_admin.search(self.base_dn, ++ attrs=["unicodePwd", "supplementalCredentials", "samAccountName"], ++ expression="(samAccountName=krbtgt)", ++ controls=["dirsync:1:0:0"]) ++ ++ self.assertTrue(len(res) == 1) ++ # This form ensures this is a case insensitive comparison ++ self.assertTrue("samAccountName" in res[0]) ++ self.assertTrue(res[0].get("samAccountName")) ++ self.assertTrue(res[0].get("unicodePwd") is None) ++ self.assertTrue(res[0].get("supplementalCredentials") is None) + + if not getattr(opts, "listtests", False): + lp = sambaopts.get_loadparm() +-- +2.25.1 + + diff -Nru samba-4.15.13+dfsg/debian/patches/CVE-2023-4154-18.patch samba-4.15.13+dfsg/debian/patches/CVE-2023-4154-18.patch --- samba-4.15.13+dfsg/debian/patches/CVE-2023-4154-18.patch 1970-01-01 00:00:00.000000000 +0000 +++ samba-4.15.13+dfsg/debian/patches/CVE-2023-4154-18.patch 2023-10-04 12:24:28.000000000 +0000 @@ -0,0 +1,40 @@ +From d309432e3668e5416f292d26a2b944dbff022209 Mon Sep 17 00:00:00 2001 +From: Andrew Bartlett +Date: Tue, 8 Aug 2023 11:18:46 +1200 +Subject: [PATCH 18/21] CVE-2023-4154 dsdb/tests: Speed up DirSync test by only + checking positive matches once + +When we (expect to) get back a result, do not waste time against a potentially +slow server confirming we also get back results for all the other attribute +combinations. + +BUG: https://bugzilla.samba.org/show_bug.cgi?id=15424 + +Signed-off-by: Andrew Bartlett +--- + source4/dsdb/tests/python/confidential_attr.py | 8 +++++++- + 1 file changed, 7 insertions(+), 1 deletion(-) + +diff --git a/source4/dsdb/tests/python/confidential_attr.py b/source4/dsdb/tests/python/confidential_attr.py +index ee7f554a008..678a5a82948 100755 +--- a/source4/dsdb/tests/python/confidential_attr.py ++++ b/source4/dsdb/tests/python/confidential_attr.py +@@ -742,7 +742,13 @@ class ConfidentialAttrTestDirsync(ConfidentialAttrCommon): + # want to weed out results from any previous test runs + search = "(&{0}{1})".format(expr, self.extra_filter) + +- for attr in self.attr_filters: ++ # If we expect to return multiple results, only check the first ++ if expected_num > 0: ++ attr_filters = [self.attr_filters[0]] ++ else: ++ attr_filters = self.attr_filters ++ ++ for attr in attr_filters: + res = samdb.search(base_dn, expression=search, scope=SCOPE_SUBTREE, + attrs=attr, controls=self.dirsync) + self.assertEqual(len(res), expected_num, +-- +2.25.1 + + diff -Nru samba-4.15.13+dfsg/debian/patches/CVE-2023-4154-19.patch samba-4.15.13+dfsg/debian/patches/CVE-2023-4154-19.patch --- samba-4.15.13+dfsg/debian/patches/CVE-2023-4154-19.patch 1970-01-01 00:00:00.000000000 +0000 +++ samba-4.15.13+dfsg/debian/patches/CVE-2023-4154-19.patch 2023-10-04 12:24:32.000000000 +0000 @@ -0,0 +1,98 @@ +From 22c08bc3087e1509f3ec05894d850c50c8a31dc8 Mon Sep 17 00:00:00 2001 +From: Andrew Bartlett +Date: Tue, 8 Aug 2023 14:30:19 +1200 +Subject: [PATCH 19/21] CVE-2023-4154 dsdb/tests: Add test for + SEARCH_FLAG_RODC_ATTRIBUTE behaviour + +SEARCH_FLAG_RODC_ATTRIBUTE should be like SEARCH_FLAG_CONFIDENTIAL, +but for DirSync and DRS replication. Accounts with +GUID_DRS_GET_CHANGES rights should not be able to read this +attribute. + +BUG: https://bugzilla.samba.org/show_bug.cgi?id=15424 + +Signed-off-by: Andrew Bartlett +--- + .../dsdb/tests/python/confidential_attr.py | 45 ++++++++++++++++--- + 1 file changed, 40 insertions(+), 5 deletions(-) + +diff --git a/source4/dsdb/tests/python/confidential_attr.py b/source4/dsdb/tests/python/confidential_attr.py +index 678a5a82948..edbc9593204 100755 +--- a/source4/dsdb/tests/python/confidential_attr.py ++++ b/source4/dsdb/tests/python/confidential_attr.py +@@ -30,13 +30,15 @@ import time + 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, SEARCH_FLAG_PRESERVEONDELETE ++from samba.dsdb import SEARCH_FLAG_CONFIDENTIAL, SEARCH_FLAG_RODC_ATTRIBUTE, SEARCH_FLAG_PRESERVEONDELETE + 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 ++from samba.dcerpc import security ++ + import samba.tests + import samba.dsdb + +@@ -137,11 +139,11 @@ class ConfidentialAttrCommon(samba.tests.TestCase): + # sanity-check the flag is not already set (this'll cause problems if + # previous test run didn't clean up properly) + search_flags = int(self.get_attr_search_flags(self.attr_dn)) +- if search_flags & SEARCH_FLAG_CONFIDENTIAL: +- self.set_attr_search_flags(self.attr_dn, str(search_flags &~ SEARCH_FLAG_CONFIDENTIAL)) ++ if search_flags & SEARCH_FLAG_CONFIDENTIAL|SEARCH_FLAG_RODC_ATTRIBUTE: ++ self.set_attr_search_flags(self.attr_dn, str(search_flags &~ (SEARCH_FLAG_CONFIDENTIAL|SEARCH_FLAG_RODC_ATTRIBUTE))) + search_flags = int(self.get_attr_search_flags(self.attr_dn)) +- self.assertEqual(0, search_flags & SEARCH_FLAG_CONFIDENTIAL, +- f"{self.conf_attr} searchFlags did not reset to omit SEARCH_FLAG_CONFIDENTIAL ({search_flags})") ++ self.assertEqual(0, search_flags & (SEARCH_FLAG_CONFIDENTIAL|SEARCH_FLAG_RODC_ATTRIBUTE), ++ f"{self.conf_attr} searchFlags did not reset to omit SEARCH_FLAG_CONFIDENTIAL and SEARCH_FLAG_RODC_ATTRIBUTE ({search_flags})") + + def add_attr(self, dn, attr, value): + m = Message() +@@ -1098,5 +1100,38 @@ class ConfidentialAttrTestDirsync(ConfidentialAttrCommon): + user_matching, user_non_matching = time_searches(self.ldb_user) + assertRangesOverlap(user_matching, user_non_matching) + ++# Check that using the dirsync controls doesn't reveal confidential ++# "RODC filtered attribute" values to users with only ++# GUID_DRS_GET_CHANGES. The tests is so similar to the Confidential ++# attribute test we base it on that. ++class RodcFilteredAttrDirsync(ConfidentialAttrTestDirsync): ++ ++ def setUp(self): ++ super().setUp() ++ self.dirsync = ["dirsync:1:0:1000"] ++ ++ user_sid = self.sd_utils.get_object_sid(self.get_user_dn(self.user)) ++ mod = "(OA;;CR;%s;;%s)" % (security.GUID_DRS_GET_CHANGES, ++ str(user_sid)) ++ self.sd_utils.dacl_add_ace(self.base_dn, mod) ++ ++ self.ldb_user = self.get_ldb_connection(self.user, self.user_pass) ++ ++ self.addCleanup(self.sd_utils.dacl_delete_aces, self.base_dn, mod) ++ ++ def make_attr_confidential(self): ++ """Marks the attribute under test as being confidential AND RODC ++ filtered (which should mean it is not visible with only ++ GUID_DRS_GET_CHANGES) ++ """ ++ ++ # 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_RODC_ATTRIBUTE|SEARCH_FLAG_CONFIDENTIAL)) ++ ++ # reset the value after the test completes ++ self.addCleanup(self.set_attr_search_flags, self.attr_dn, old_value) ++ + + TestProgram(module=__name__, opts=subunitopts) +-- +2.25.1 + + diff -Nru samba-4.15.13+dfsg/debian/patches/CVE-2023-4154-20.patch samba-4.15.13+dfsg/debian/patches/CVE-2023-4154-20.patch --- samba-4.15.13+dfsg/debian/patches/CVE-2023-4154-20.patch 1970-01-01 00:00:00.000000000 +0000 +++ samba-4.15.13+dfsg/debian/patches/CVE-2023-4154-20.patch 2023-10-04 12:24:35.000000000 +0000 @@ -0,0 +1,631 @@ +From 568fb2bd24098ce60dcffee5282ca90a574df6ad Mon Sep 17 00:00:00 2001 +From: Andrew Bartlett +Date: Tue, 22 Aug 2023 15:08:17 +1200 +Subject: [PATCH 20/21] CVE-2023-4154 dsdb/tests: Extend attribute read DirSync + tests + +The aim here is to document the expected (even if not implemented) +SEARCH_FLAG_RODC_ATTRIBUTE vs SEARCH_FLAG_CONFIDENTIAL, behaviour, so +that any change once CVE-2023-4154 is fixed can be noted. + +BUG: https://bugzilla.samba.org/show_bug.cgi?id=15424 + +Signed-off-by: Andrew Bartlett +--- + selftest/knownfail.d/dirsync | 15 +- + source4/dsdb/tests/python/dirsync.py | 456 +++++++++++++++++++++++---- + 2 files changed, 414 insertions(+), 57 deletions(-) + +diff --git a/selftest/knownfail.d/dirsync b/selftest/knownfail.d/dirsync +index 9367f92e109..db098549a08 100644 +--- a/selftest/knownfail.d/dirsync ++++ b/selftest/knownfail.d/dirsync +@@ -1 +1,14 @@ +-^samba4.ldap.dirsync.python\(.*\).__main__.SimpleDirsyncTests.test_dirsync_unicodePwd +\ No newline at end of file ++^samba4.ldap.dirsync.python\(.*\).__main__.ConfidentialDirsyncTests.test_dirsync_OBJECT_SECURITY_insist_on_empty_element\(.*\) ++^samba4.ldap.dirsync.python\(.*\).__main__.ConfidentialDirsyncTests.test_dirsync_unicodePwd_OBJ_SEC_insist_on_empty_element\(.*\) ++^samba4.ldap.dirsync.python\(.*\).__main__.ConfidentialDirsyncTests.test_dirsync_unicodePwd_with_GET_CHANGES\(.*\) ++^samba4.ldap.dirsync.python\(.*\).__main__.ConfidentialDirsyncTests.test_dirsync_unicodePwd_with_GET_CHANGES_OBJ_SEC_insist_on_empty_element\(.*\) ++^samba4.ldap.dirsync.python\(.*\).__main__.ConfidentialDirsyncTests.test_dirsync_unicodePwd_with_GET_CHANGES_insist_on_empty_element\(.*\) ++^samba4.ldap.dirsync.python\(.*\).__main__.ConfidentialDirsyncTests.test_dirsync_with_GET_CHANGES_OBJECT_SECURITY_insist_on_empty_element\(.*\) ++^samba4.ldap.dirsync.python\(.*\).__main__.ConfidentialFilteredDirsyncTests.test_dirsync_OBJECT_SECURITY_insist_on_empty_element\(.*\) ++^samba4.ldap.dirsync.python\(.*\).__main__.ConfidentialFilteredDirsyncTests.test_dirsync_OBJECT_SECURITY_with_GET_CHANGES_insist_on_empty_element\(.*\) ++^samba4.ldap.dirsync.python\(.*\).__main__.ConfidentialFilteredDirsyncTests.test_dirsync_with_GET_CHANGES\(.*\) ++^samba4.ldap.dirsync.python\(.*\).__main__.ConfidentialFilteredDirsyncTests.test_dirsync_with_GET_CHANGES_attr\(.*\) ++^samba4.ldap.dirsync.python\(.*\).__main__.ConfidentialFilteredDirsyncTests.test_dirsync_with_GET_CHANGES_insist_on_empty_element\(.*\) ++^samba4.ldap.dirsync.python\(.*\).__main__.FilteredDirsyncTests.test_dirsync_with_GET_CHANGES\(.*\) ++^samba4.ldap.dirsync.python\(.*\).__main__.FilteredDirsyncTests.test_dirsync_with_GET_CHANGES_attr\(.*\) ++^samba4.ldap.dirsync.python\(.*\).__main__.FilteredDirsyncTests.test_dirsync_with_GET_CHANGES_insist_on_empty_element\(.*\) +diff --git a/source4/dsdb/tests/python/dirsync.py b/source4/dsdb/tests/python/dirsync.py +index 2cacaf01251..a0691f0afe0 100755 +--- a/source4/dsdb/tests/python/dirsync.py ++++ b/source4/dsdb/tests/python/dirsync.py +@@ -3,6 +3,7 @@ + # Unit tests for dirsync control + # Copyright (C) Matthieu Patou 2011 + # Copyright (C) Jelmer Vernooij 2014 ++# 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 +@@ -30,7 +31,8 @@ import base64 + import ldb + from ldb import LdbError, SCOPE_BASE + from ldb import Message, MessageElement, Dn +-from ldb import FLAG_MOD_ADD, FLAG_MOD_DELETE ++from ldb import FLAG_MOD_ADD, FLAG_MOD_DELETE, FLAG_MOD_REPLACE ++from samba.dsdb import SEARCH_FLAG_CONFIDENTIAL, SEARCH_FLAG_RODC_ATTRIBUTE + from samba.dcerpc import security, misc, drsblobs + from samba.ndr import ndr_unpack, ndr_pack + +@@ -60,7 +62,6 @@ if len(args) < 1: + host = args.pop() + if "://" not in host: + ldaphost = "ldap://%s" % host +- ldapshost = "ldaps://%s" % host + else: + ldaphost = host + start = host.rindex("://") +@@ -77,8 +78,8 @@ creds = credopts.get_credentials(lp) + class DirsyncBaseTests(samba.tests.TestCase): + + def setUp(self): +- super(DirsyncBaseTests, self).setUp() +- self.ldb_admin = SamDB(ldapshost, credentials=creds, session_info=system_session(lp), lp=lp) ++ super().setUp() ++ self.ldb_admin = SamDB(ldaphost, credentials=creds, session_info=system_session(lp), lp=lp) + self.base_dn = self.ldb_admin.domain_dn() + self.domain_sid = security.dom_sid(self.ldb_admin.get_domain_sid()) + self.user_pass = samba.generate_random_password(12, 16) +@@ -87,63 +88,60 @@ class DirsyncBaseTests(samba.tests.TestCase): + # used for anonymous login + print("baseDN: %s" % self.base_dn) + +- def get_user_dn(self, name): +- return "CN=%s,CN=Users,%s" % (name, self.base_dn) ++ userou = "OU=dirsync-test" ++ self.ou = f"{userou},{self.base_dn}" ++ samba.tests.delete_force(self.ldb_admin, self.ou, controls=['tree_delete:1']) ++ self.ldb_admin.create_ou(self.ou) ++ self.addCleanup(samba.tests.delete_force, self.ldb_admin, self.ou, controls=['tree_delete:1']) + +- 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()) +- creds_tmp.set_gensec_features(creds_tmp.get_gensec_features() +- | gensec.FEATURE_SEAL) +- creds_tmp.set_kerberos_state(DONT_USE_KERBEROS) # kinit is too expensive to use in a tight loop +- ldb_target = SamDB(url=ldaphost, credentials=creds_tmp, lp=lp) +- return ldb_target +- +- +-# tests on ldap add operations +-class SimpleDirsyncTests(DirsyncBaseTests): +- +- def setUp(self): +- super(SimpleDirsyncTests, self).setUp() + # Regular user + self.dirsync_user = "test_dirsync_user" + self.simple_user = "test_simple_user" + self.admin_user = "test_admin_user" +- self.ouname = None ++ self.dirsync_pass = self.user_pass ++ self.simple_pass = self.user_pass ++ self.admin_pass = self.user_pass + +- self.ldb_admin.newuser(self.dirsync_user, self.user_pass) +- self.ldb_admin.newuser(self.simple_user, self.user_pass) +- self.ldb_admin.newuser(self.admin_user, self.user_pass) ++ self.ldb_admin.newuser(self.dirsync_user, self.dirsync_pass, userou=userou) ++ self.ldb_admin.newuser(self.simple_user, self.simple_pass, userou=userou) ++ self.ldb_admin.newuser(self.admin_user, self.admin_pass, userou=userou) + self.desc_sddl = self.sd_utils.get_sd_as_sddl(self.base_dn) + + user_sid = self.sd_utils.get_object_sid(self.get_user_dn(self.dirsync_user)) + mod = "(OA;;CR;%s;;%s)" % (security.GUID_DRS_GET_CHANGES, + str(user_sid)) + self.sd_utils.dacl_add_ace(self.base_dn, mod) ++ self.addCleanup(self.sd_utils.dacl_delete_aces, self.base_dn, mod) + + # add admins to the Domain Admins group + self.ldb_admin.add_remove_group_members("Domain Admins", [self.admin_user], + add_members_operation=True) + +- def tearDown(self): +- super(SimpleDirsyncTests, self).tearDown() +- delete_force(self.ldb_admin, self.get_user_dn(self.dirsync_user)) +- delete_force(self.ldb_admin, self.get_user_dn(self.simple_user)) +- delete_force(self.ldb_admin, self.get_user_dn(self.admin_user)) +- if self.ouname: +- delete_force(self.ldb_admin, self.ouname) +- self.sd_utils.modify_sd_on_dn(self.base_dn, self.desc_sddl) ++ def get_user_dn(self, name): ++ return ldb.Dn(self.ldb_admin, "CN={0},{1}".format(name, self.ou)) ++ ++ 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()) ++ creds_tmp.set_gensec_features(creds_tmp.get_gensec_features() ++ | gensec.FEATURE_SEAL) ++ creds_tmp.set_kerberos_state(DONT_USE_KERBEROS) # kinit is too expensive to use in a tight loop ++ ldb_target = SamDB(url=ldaphost, credentials=creds_tmp, lp=lp) ++ return ldb_target ++ ++# tests on ldap add operations ++class SimpleDirsyncTests(DirsyncBaseTests): + + # def test_dirsync_errors(self): + + def test_dirsync_supported(self): + """Test the basic of the dirsync is supported""" + self.ldb_dirsync = self.get_ldb_connection(self.dirsync_user, self.user_pass) +- self.ldb_simple = self.get_ldb_connection(self.simple_user, self.user_pass) ++ self.ldb_simple = self.get_ldb_connection(self.simple_user, self.simple_pass) + res = self.ldb_admin.search(self.base_dn, expression="samaccountname=*", controls=["dirsync:1:0:1"]) + res = self.ldb_dirsync.search(self.base_dn, expression="samaccountname=*", controls=["dirsync:1:0:1"]) + try: +@@ -169,8 +167,8 @@ class SimpleDirsyncTests(DirsyncBaseTests): + + def test_dirsync_errors(self): + """Test if dirsync returns the correct LDAP errors in case of pb""" +- self.ldb_simple = self.get_ldb_connection(self.simple_user, self.user_pass) +- self.ldb_dirsync = self.get_ldb_connection(self.dirsync_user, self.user_pass) ++ self.ldb_simple = self.get_ldb_connection(self.simple_user, self.simple_pass) ++ self.ldb_dirsync = self.get_ldb_connection(self.dirsync_user, self.dirsync_pass) + try: + self.ldb_simple.search(self.base_dn, + expression="samaccountname=*", +@@ -292,11 +290,11 @@ class SimpleDirsyncTests(DirsyncBaseTests): + attrs=["parentGUID"], + controls=["dirsync:1:0:1"]) + self.assertEqual(len(res.msgs), 0) +- ouname = "OU=testou,%s" % self.base_dn ++ ouname = "OU=testou,%s" % self.ou + self.ouname = ouname + self.ldb_admin.create_ou(ouname) + delta = Message() +- delta.dn = Dn(self.ldb_admin, str(ouname)) ++ delta.dn = Dn(self.ldb_admin, ouname) + delta["cn"] = MessageElement("test ou", + FLAG_MOD_ADD, + "cn") +@@ -457,7 +455,7 @@ class SimpleDirsyncTests(DirsyncBaseTests): + def test_dirsync_linkedattributes_OBJECT_SECURITY(self): + """Check that dirsync returned deleted objects too""" + # Let's search for members +- self.ldb_simple = self.get_ldb_connection(self.simple_user, self.user_pass) ++ self.ldb_simple = self.get_ldb_connection(self.simple_user, self.simple_pass) + res = self.ldb_simple.search(self.base_dn, + expression="(name=Administrators)", + controls=["dirsync:1:1:1"]) +@@ -582,7 +580,7 @@ class SimpleDirsyncTests(DirsyncBaseTests): + controls=controls) + + def test_dirsync_linkedattributes_range(self): +- self.ldb_simple = self.get_ldb_connection(self.simple_user, self.user_pass) ++ self.ldb_simple = self.get_ldb_connection(self.simple_user, self.simple_pass) + res = self.ldb_admin.search(self.base_dn, + attrs=["member;range=1-1"], + expression="(name=Administrators)", +@@ -594,7 +592,7 @@ class SimpleDirsyncTests(DirsyncBaseTests): + self.assertTrue(len(res[0].get("member")) > 0) + + def test_dirsync_linkedattributes_range_user(self): +- self.ldb_simple = self.get_ldb_connection(self.simple_user, self.user_pass) ++ self.ldb_simple = self.get_ldb_connection(self.simple_user, self.simple_pass) + try: + res = self.ldb_simple.search(self.base_dn, + attrs=["member;range=1-1"], +@@ -608,7 +606,7 @@ class SimpleDirsyncTests(DirsyncBaseTests): + + def test_dirsync_linkedattributes(self): + flag_incr_linked = 2147483648 +- self.ldb_simple = self.get_ldb_connection(self.simple_user, self.user_pass) ++ self.ldb_simple = self.get_ldb_connection(self.simple_user, self.simple_pass) + res = self.ldb_admin.search(self.base_dn, + attrs=["member"], + expression="(name=Administrators)", +@@ -676,7 +674,7 @@ class SimpleDirsyncTests(DirsyncBaseTests): + def test_dirsync_extended_dn(self): + """Check that dirsync works together with the extended_dn control""" + # Let's search for members +- self.ldb_simple = self.get_ldb_connection(self.simple_user, self.user_pass) ++ self.ldb_simple = self.get_ldb_connection(self.simple_user, self.simple_pass) + res = self.ldb_simple.search(self.base_dn, + expression="(name=Administrators)", + controls=["dirsync:1:1:1"]) +@@ -707,7 +705,7 @@ class SimpleDirsyncTests(DirsyncBaseTests): + def test_dirsync_deleted_items_OBJECT_SECURITY(self): + """Check that dirsync returned deleted objects too""" + # Let's create an OU +- self.ldb_simple = self.get_ldb_connection(self.simple_user, self.user_pass) ++ self.ldb_simple = self.get_ldb_connection(self.simple_user, self.simple_pass) + ouname = "OU=testou3,%s" % self.base_dn + self.ouname = ouname + self.ldb_admin.create_ou(ouname) +@@ -742,18 +740,364 @@ class SimpleDirsyncTests(DirsyncBaseTests): + self.assertEqual(guid2, guid) + self.assertEqual(str(res[0].dn), "") + +- def test_dirsync_unicodePwd(self): ++class SpecialDirsyncTests(DirsyncBaseTests): ++ ++ def setUp(self): ++ super().setUp() ++ ++ self.schema_dn = self.ldb_admin.get_schema_basedn() ++ ++ # the tests work by setting the 'Confidential' or 'RODC Filtered' 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" ++ attr_cn = "CN=Address-Home" ++ # schemaIdGuid for homePostalAddress (used for ACE tests) ++ self.attr_dn = f"{attr_cn},{self.schema_dn}" ++ ++ userou = "OU=conf-attr-test" ++ self.ou = "{0},{1}".format(userou, self.base_dn) ++ samba.tests.delete_force(self.ldb_admin, self.ou, controls=['tree_delete:1']) ++ self.ldb_admin.create_ou(self.ou) ++ self.addCleanup(samba.tests.delete_force, self.ldb_admin, self.ou, controls=['tree_delete:1']) ++ ++ # add a test object with this attribute set ++ self.conf_value = "abcdef" ++ self.conf_user = "conf-user" ++ 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) ++ ++ # sanity-check the flag is not already set (this'll cause problems if ++ # previous test run didn't clean up properly) ++ ++ search_flags = int(self.get_attr_search_flags(self.attr_dn)) ++ if search_flags & SEARCH_FLAG_CONFIDENTIAL|SEARCH_FLAG_RODC_ATTRIBUTE: ++ self.set_attr_search_flags(self.attr_dn, str(search_flags &~ (SEARCH_FLAG_CONFIDENTIAL|SEARCH_FLAG_RODC_ATTRIBUTE))) ++ search_flags = int(self.get_attr_search_flags(self.attr_dn)) ++ self.assertEqual(0, search_flags & (SEARCH_FLAG_CONFIDENTIAL|SEARCH_FLAG_RODC_ATTRIBUTE), ++ f"{self.conf_attr} searchFlags did not reset to omit SEARCH_FLAG_CONFIDENTIAL and SEARCH_FLAG_RODC_ATTRIBUTE ({search_flags})") ++ ++ # 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(self.flag_under_test)) ++ ++ # reset the value after the test completes ++ self.addCleanup(self.set_attr_search_flags, self.attr_dn, old_value) ++ ++ def add_attr(self, dn, attr, value): ++ m = Message() ++ m.dn = dn ++ m[attr] = MessageElement(value, FLAG_MOD_ADD, attr) ++ self.ldb_admin.modify(m) ++ ++ 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.ldb_admin.set_schema_update_now() ++ ++ def get_attr_search_flags(self, attr_dn): ++ res = self.ldb_admin.search(attr_dn, scope=SCOPE_BASE, ++ attrs=['searchFlags']) ++ return res[0]['searchFlags'][0] ++ ++ def find_under_current_ou(self, res): ++ for msg in res: ++ if msg.dn == self.conf_dn: ++ return msg ++ self.fail(f"Failed to find object {self.conf_dn} in {len(res)} results") ++ ++ ++class ConfidentialDirsyncTests(SpecialDirsyncTests): ++ ++ def setUp(self): ++ self.flag_under_test = SEARCH_FLAG_CONFIDENTIAL ++ super().setUp() ++ ++ def test_unicodePwd_normal(self): + res = self.ldb_admin.search(self.base_dn, + attrs=["unicodePwd", "supplementalCredentials", "samAccountName"], +- expression="(samAccountName=krbtgt)", +- controls=["dirsync:1:0:0"]) ++ expression=f"(samAccountName={self.conf_user})") ++ ++ msg = res[0] ++ ++ self.assertTrue("samAccountName" in msg) ++ # This form ensures this is a case insensitive comparison ++ self.assertTrue(msg.get("samAccountName")) ++ self.assertTrue(msg.get("unicodePwd") is None) ++ self.assertTrue(msg.get("supplementalCredentials") is None) ++ ++ def _test_dirsync_unicodePwd(self, ldb_conn, control=None, insist_on_empty_element=False): ++ res = ldb_conn.search(self.base_dn, ++ attrs=["unicodePwd", "supplementalCredentials", "samAccountName"], ++ expression=f"(samAccountName={self.conf_user})", ++ controls=[control]) ++ ++ msg = self.find_under_current_ou(res) + +- self.assertTrue(len(res) == 1) ++ self.assertTrue("samAccountName" in msg) + # This form ensures this is a case insensitive comparison +- self.assertTrue("samAccountName" in res[0]) +- self.assertTrue(res[0].get("samAccountName")) +- self.assertTrue(res[0].get("unicodePwd") is None) +- self.assertTrue(res[0].get("supplementalCredentials") is None) ++ self.assertTrue(msg.get("samAccountName")) ++ if insist_on_empty_element: ++ self.assertTrue(msg.get("unicodePwd") is not None) ++ self.assertEqual(len(msg.get("unicodePwd")), 0) ++ self.assertTrue(msg.get("supplementalCredentials") is not None) ++ self.assertEqual(len(msg.get("supplementalCredentials")), 0) ++ else: ++ self.assertTrue(msg.get("unicodePwd") is None ++ or len(msg.get("unicodePwd")) == 0) ++ self.assertTrue(msg.get("supplementalCredentials") is None ++ or len(msg.get("supplementalCredentials")) == 0) ++ ++ def test_dirsync_unicodePwd_OBJ_SEC(self): ++ ldb_conn = self.get_ldb_connection(self.simple_user, self.simple_pass) ++ self._test_dirsync_unicodePwd(ldb_conn, control="dirsync:1:1:0") ++ ++ def test_dirsync_unicodePwd_OBJ_SEC_insist_on_empty_element(self): ++ ldb_conn = self.get_ldb_connection(self.simple_user, self.simple_pass) ++ self._test_dirsync_unicodePwd(ldb_conn, control="dirsync:1:1:0", insist_on_empty_element=True) ++ ++ def test_dirsync_unicodePwd_with_GET_CHANGES_OBJ_SEC(self): ++ ldb_conn = self.get_ldb_connection(self.dirsync_user, self.dirsync_pass) ++ self._test_dirsync_unicodePwd(ldb_conn, control="dirsync:1:1:0") ++ ++ def test_dirsync_unicodePwd_with_GET_CHANGES_OBJ_SEC_insist_on_empty_element(self): ++ ldb_conn = self.get_ldb_connection(self.dirsync_user, self.dirsync_pass) ++ self._test_dirsync_unicodePwd(ldb_conn, control="dirsync:1:1:0", insist_on_empty_element=True) ++ ++ def test_dirsync_unicodePwd_with_GET_CHANGES(self): ++ ldb_conn = self.get_ldb_connection(self.dirsync_user, self.dirsync_pass) ++ self._test_dirsync_unicodePwd(ldb_conn, control="dirsync:1:0:0") ++ ++ def test_dirsync_unicodePwd_with_GET_CHANGES_insist_on_empty_element(self): ++ ldb_conn = self.get_ldb_connection(self.dirsync_user, self.dirsync_pass) ++ self._test_dirsync_unicodePwd(ldb_conn, control="dirsync:1:0:0", insist_on_empty_element=True) ++ ++ def test_normal(self): ++ ldb_conn = self.get_ldb_connection(self.simple_user, self.simple_pass) ++ res = ldb_conn.search(self.base_dn, ++ attrs=[self.conf_attr, "samAccountName"], ++ expression=f"(samAccountName={self.conf_user})") ++ ++ msg = res[0] ++ self.assertTrue("samAccountName" in msg) ++ # This form ensures this is a case insensitive comparison ++ self.assertTrue(msg.get("samAccountName")) ++ self.assertTrue(msg.get(self.conf_attr) is None) ++ ++ def _test_dirsync_OBJECT_SECURITY(self, ldb_conn, insist_on_empty_element=False): ++ res = ldb_conn.search(self.base_dn, ++ attrs=[self.conf_attr, "samAccountName"], ++ expression=f"(samAccountName={self.conf_user})", ++ controls=["dirsync:1:1:0"]) ++ ++ msg = self.find_under_current_ou(res) ++ self.assertTrue("samAccountName" in msg) ++ # This form ensures this is a case insensitive comparison ++ self.assertTrue(msg.get("samAccountName")) ++ if insist_on_empty_element: ++ self.assertTrue(msg.get(self.conf_attr) is not None) ++ self.assertEqual(len(msg.get(self.conf_attr)), 0) ++ else: ++ self.assertTrue(msg.get(self.conf_attr) is None ++ or len(msg.get(self.conf_attr)) == 0) ++ ++ def test_dirsync_OBJECT_SECURITY(self): ++ ldb_conn = self.get_ldb_connection(self.simple_user, self.simple_pass) ++ self._test_dirsync_OBJECT_SECURITY(ldb_conn) ++ ++ def test_dirsync_OBJECT_SECURITY_insist_on_empty_element(self): ++ ldb_conn = self.get_ldb_connection(self.simple_user, self.simple_pass) ++ self._test_dirsync_OBJECT_SECURITY(ldb_conn, insist_on_empty_element=True) ++ ++ def test_dirsync_with_GET_CHANGES(self): ++ ldb_conn = self.get_ldb_connection(self.dirsync_user, self.dirsync_pass) ++ res = ldb_conn.search(self.base_dn, ++ attrs=[self.conf_attr, "samAccountName"], ++ expression=f"(samAccountName={self.conf_user})", ++ controls=["dirsync:1:0:0"]) ++ ++ msg = self.find_under_current_ou(res) ++ # This form ensures this is a case insensitive comparison ++ self.assertTrue(msg.get("samAccountName")) ++ self.assertTrue(msg.get(self.conf_attr)) ++ self.assertEqual(len(msg.get(self.conf_attr)), 1) ++ ++ def test_dirsync_with_GET_CHANGES_OBJECT_SECURITY(self): ++ ldb_conn = self.get_ldb_connection(self.dirsync_user, self.dirsync_pass) ++ self._test_dirsync_OBJECT_SECURITY(ldb_conn) ++ ++ def test_dirsync_with_GET_CHANGES_OBJECT_SECURITY_insist_on_empty_element(self): ++ ldb_conn = self.get_ldb_connection(self.dirsync_user, self.dirsync_pass) ++ self._test_dirsync_OBJECT_SECURITY(ldb_conn, insist_on_empty_element=True) ++ ++class FilteredDirsyncTests(SpecialDirsyncTests): ++ ++ def setUp(self): ++ self.flag_under_test = SEARCH_FLAG_RODC_ATTRIBUTE ++ super().setUp() ++ ++ def test_attr(self): ++ ldb_conn = self.get_ldb_connection(self.simple_user, self.simple_pass) ++ res = ldb_conn.search(self.base_dn, ++ attrs=[self.conf_attr, "samAccountName"], ++ expression=f"(samAccountName={self.conf_user})") ++ ++ msg = res[0] ++ self.assertTrue("samAccountName" in msg) ++ # This form ensures this is a case insensitive comparison ++ self.assertTrue(msg.get("samAccountName")) ++ self.assertTrue(msg.get(self.conf_attr)) ++ self.assertEqual(len(msg.get(self.conf_attr)), 1) ++ ++ def _test_dirsync_OBJECT_SECURITY(self, ldb_conn): ++ res = ldb_conn.search(self.base_dn, ++ attrs=[self.conf_attr, "samAccountName"], ++ expression=f"(samAccountName={self.conf_user})", ++ controls=["dirsync:1:1:0"]) ++ ++ msg = self.find_under_current_ou(res) ++ self.assertTrue("samAccountName" in msg) ++ # This form ensures this is a case insensitive comparison ++ self.assertTrue(msg.get("samAccountName")) ++ self.assertTrue(msg.get(self.conf_attr)) ++ self.assertEqual(len(msg.get(self.conf_attr)), 1) ++ ++ def test_dirsync_OBJECT_SECURITY(self): ++ ldb_conn = self.get_ldb_connection(self.simple_user, self.simple_pass) ++ self._test_dirsync_OBJECT_SECURITY(ldb_conn) ++ ++ def test_dirsync_OBJECT_SECURITY_with_GET_CHANGES(self): ++ ldb_conn = self.get_ldb_connection(self.dirsync_user, self.dirsync_pass) ++ self._test_dirsync_OBJECT_SECURITY(ldb_conn) ++ ++ def _test_dirsync_with_GET_CHANGES(self, insist_on_empty_element=False): ++ ldb_conn = self.get_ldb_connection(self.dirsync_user, self.dirsync_pass) ++ res = ldb_conn.search(self.base_dn, ++ expression=f"(samAccountName={self.conf_user})", ++ controls=["dirsync:1:0:0"]) ++ ++ msg = self.find_under_current_ou(res) ++ # This form ensures this is a case insensitive comparison ++ self.assertTrue(msg.get("samAccountName")) ++ if insist_on_empty_element: ++ self.assertTrue(msg.get(self.conf_attr) is not None) ++ self.assertEqual(len(msg.get(self.conf_attr)), 0) ++ else: ++ self.assertTrue(msg.get(self.conf_attr) is None ++ or len(msg.get(self.conf_attr)) == 0) ++ ++ def test_dirsync_with_GET_CHANGES(self): ++ self._test_dirsync_with_GET_CHANGES() ++ ++ def test_dirsync_with_GET_CHANGES_insist_on_empty_element(self): ++ self._test_dirsync_with_GET_CHANGES(insist_on_empty_element=True) ++ ++ def test_dirsync_with_GET_CHANGES_attr(self): ++ ldb_conn = self.get_ldb_connection(self.dirsync_user, self.dirsync_pass) ++ try: ++ res = ldb_conn.search(self.base_dn, ++ attrs=[self.conf_attr, "samAccountName"], ++ expression=f"(samAccountName={self.conf_user})", ++ controls=["dirsync:1:0:0"]) ++ self.fail("ldb.search() should have failed with LDAP_INSUFFICIENT_ACCESS_RIGHTS") ++ except ldb.LdbError as e: ++ (errno, errstr) = e.args ++ self.assertEqual(errno, ldb.ERR_INSUFFICIENT_ACCESS_RIGHTS) ++ ++class ConfidentialFilteredDirsyncTests(SpecialDirsyncTests): ++ ++ def setUp(self): ++ self.flag_under_test = SEARCH_FLAG_RODC_ATTRIBUTE|SEARCH_FLAG_CONFIDENTIAL ++ super().setUp() ++ ++ def test_attr(self): ++ ldb_conn = self.get_ldb_connection(self.simple_user, self.simple_pass) ++ res = ldb_conn.search(self.base_dn, ++ attrs=["unicodePwd", "supplementalCredentials", "samAccountName"], ++ expression=f"(samAccountName={self.conf_user})") ++ ++ msg = res[0] ++ self.assertTrue(msg.get("samAccountName")) ++ self.assertTrue(msg.get(self.conf_attr) is None) ++ ++ def _test_dirsync_OBJECT_SECURITY(self, ldb_conn, insist_on_empty_element=False): ++ res = ldb_conn.search(self.base_dn, ++ attrs=[self.conf_attr, "samAccountName"], ++ expression=f"(samAccountName={self.conf_user})", ++ controls=["dirsync:1:1:0"]) ++ ++ msg = self.find_under_current_ou(res) ++ self.assertTrue("samAccountName" in msg) ++ # This form ensures this is a case insensitive comparison ++ self.assertTrue(msg.get("samAccountName")) ++ if insist_on_empty_element: ++ self.assertTrue(msg.get(self.conf_attr) is not None) ++ self.assertEqual(len(msg.get(self.conf_attr)), 0) ++ else: ++ self.assertTrue(msg.get(self.conf_attr) is None ++ or len(msg.get(self.conf_attr)) == 0) ++ ++ def test_dirsync_OBJECT_SECURITY(self): ++ ldb_conn = self.get_ldb_connection(self.simple_user, self.simple_pass) ++ self._test_dirsync_OBJECT_SECURITY(ldb_conn) ++ ++ def test_dirsync_OBJECT_SECURITY_insist_on_empty_element(self): ++ ldb_conn = self.get_ldb_connection(self.simple_user, self.simple_pass) ++ self._test_dirsync_OBJECT_SECURITY(ldb_conn, insist_on_empty_element=True) ++ ++ def test_dirsync_OBJECT_SECURITY_with_GET_CHANGES(self): ++ ldb_conn = self.get_ldb_connection(self.dirsync_user, self.dirsync_pass) ++ self._test_dirsync_OBJECT_SECURITY(ldb_conn) ++ ++ def test_dirsync_OBJECT_SECURITY_with_GET_CHANGES_insist_on_empty_element(self): ++ ldb_conn = self.get_ldb_connection(self.dirsync_user, self.dirsync_pass) ++ self._test_dirsync_OBJECT_SECURITY(ldb_conn, insist_on_empty_element=True) ++ ++ def _test_dirsync_with_GET_CHANGES(self, insist_on_empty_element=False): ++ ldb_conn = self.get_ldb_connection(self.dirsync_user, self.dirsync_pass) ++ res = ldb_conn.search(self.base_dn, ++ expression=f"(samAccountName={self.conf_user})", ++ controls=["dirsync:1:0:0"]) ++ ++ msg = self.find_under_current_ou(res) ++ # This form ensures this is a case insensitive comparison ++ self.assertTrue(msg.get("samAccountName")) ++ if insist_on_empty_element: ++ self.assertTrue(msg.get(self.conf_attr) is not None) ++ self.assertEqual(len(msg.get(self.conf_attr)), 0) ++ else: ++ self.assertTrue(msg.get(self.conf_attr) is None ++ or len(msg.get(self.conf_attr)) == 0) ++ ++ def test_dirsync_with_GET_CHANGES(self): ++ self._test_dirsync_with_GET_CHANGES() ++ ++ def test_dirsync_with_GET_CHANGES_insist_on_empty_element(self): ++ self._test_dirsync_with_GET_CHANGES(insist_on_empty_element=True) ++ ++ def test_dirsync_with_GET_CHANGES_attr(self): ++ ldb_conn = self.get_ldb_connection(self.dirsync_user, self.dirsync_pass) ++ try: ++ res = ldb_conn.search(self.base_dn, ++ attrs=[self.conf_attr, "samAccountName"], ++ expression=f"(samAccountName={self.conf_user})", ++ controls=["dirsync:1:0:0"]) ++ self.fail("ldb.search() should have failed with LDAP_INSUFFICIENT_ACCESS_RIGHTS") ++ except ldb.LdbError as e: ++ (errno, errstr) = e.args ++ self.assertEqual(errno, ldb.ERR_INSUFFICIENT_ACCESS_RIGHTS) ++ + + if not getattr(opts, "listtests", False): + lp = sambaopts.get_loadparm() +-- +2.25.1 + + diff -Nru samba-4.15.13+dfsg/debian/patches/CVE-2023-4154-21.patch samba-4.15.13+dfsg/debian/patches/CVE-2023-4154-21.patch --- samba-4.15.13+dfsg/debian/patches/CVE-2023-4154-21.patch 1970-01-01 00:00:00.000000000 +0000 +++ samba-4.15.13+dfsg/debian/patches/CVE-2023-4154-21.patch 2023-10-04 12:24:38.000000000 +0000 @@ -0,0 +1,105 @@ +From b5310cff753e8bcbc703dd4900b4fb9349d11d74 Mon Sep 17 00:00:00 2001 +From: Andrew Bartlett +Date: Tue, 8 Aug 2023 17:58:27 +1200 +Subject: [PATCH 21/21] CVE-2023-4154: Unimplement the original DirSync + behaviour without LDAP_DIRSYNC_OBJECT_SECURITY + +This makes LDAP_DIRSYNC_OBJECT_SECURITY the only behaviour provided by +Samba. + +Having a second access control system withing the LDAP stack is unsafe +and this layer is incomplete. + +The current system gives all accounts that have been given the +GUID_DRS_GET_CHANGES extended right SYSTEM access. Currently in Samba +this equates to full access to passwords as well as "RODC Filtered +attributes" (often used with confidential attributes). + +Rather than attempting to correctly filter for secrets (passwords) and +these filtered attributes, as well as preventing search expressions for +both, we leave this complexity to the acl_read module which has this +facility already well tested. + +The implication is that callers will only see and filter by attribute +in DirSync that they could without DirSync. + +BUG: https://bugzilla.samba.org/show_bug.cgi?id=15424 + +Signed-off-by: Andrew Bartlett +--- + selftest/knownfail.d/dirsync | 3 +-- + source4/dsdb/samdb/ldb_modules/dirsync.c | 22 +++++++++++----------- + 2 files changed, 12 insertions(+), 13 deletions(-) + +diff --git a/selftest/knownfail.d/dirsync b/selftest/knownfail.d/dirsync +index db098549a08..fcf4d469d6e 100644 +--- a/selftest/knownfail.d/dirsync ++++ b/selftest/knownfail.d/dirsync +@@ -1,12 +1,11 @@ + ^samba4.ldap.dirsync.python\(.*\).__main__.ConfidentialDirsyncTests.test_dirsync_OBJECT_SECURITY_insist_on_empty_element\(.*\) + ^samba4.ldap.dirsync.python\(.*\).__main__.ConfidentialDirsyncTests.test_dirsync_unicodePwd_OBJ_SEC_insist_on_empty_element\(.*\) +-^samba4.ldap.dirsync.python\(.*\).__main__.ConfidentialDirsyncTests.test_dirsync_unicodePwd_with_GET_CHANGES\(.*\) + ^samba4.ldap.dirsync.python\(.*\).__main__.ConfidentialDirsyncTests.test_dirsync_unicodePwd_with_GET_CHANGES_OBJ_SEC_insist_on_empty_element\(.*\) + ^samba4.ldap.dirsync.python\(.*\).__main__.ConfidentialDirsyncTests.test_dirsync_unicodePwd_with_GET_CHANGES_insist_on_empty_element\(.*\) + ^samba4.ldap.dirsync.python\(.*\).__main__.ConfidentialDirsyncTests.test_dirsync_with_GET_CHANGES_OBJECT_SECURITY_insist_on_empty_element\(.*\) ++^samba4.ldap.dirsync.python\(.*\).__main__.ConfidentialDirsyncTests.test_dirsync_with_GET_CHANGES\(.*\) + ^samba4.ldap.dirsync.python\(.*\).__main__.ConfidentialFilteredDirsyncTests.test_dirsync_OBJECT_SECURITY_insist_on_empty_element\(.*\) + ^samba4.ldap.dirsync.python\(.*\).__main__.ConfidentialFilteredDirsyncTests.test_dirsync_OBJECT_SECURITY_with_GET_CHANGES_insist_on_empty_element\(.*\) +-^samba4.ldap.dirsync.python\(.*\).__main__.ConfidentialFilteredDirsyncTests.test_dirsync_with_GET_CHANGES\(.*\) + ^samba4.ldap.dirsync.python\(.*\).__main__.ConfidentialFilteredDirsyncTests.test_dirsync_with_GET_CHANGES_attr\(.*\) + ^samba4.ldap.dirsync.python\(.*\).__main__.ConfidentialFilteredDirsyncTests.test_dirsync_with_GET_CHANGES_insist_on_empty_element\(.*\) + ^samba4.ldap.dirsync.python\(.*\).__main__.FilteredDirsyncTests.test_dirsync_with_GET_CHANGES\(.*\) +diff --git a/source4/dsdb/samdb/ldb_modules/dirsync.c b/source4/dsdb/samdb/ldb_modules/dirsync.c +index b3c463741c8..fbb75790095 100644 +--- a/source4/dsdb/samdb/ldb_modules/dirsync.c ++++ b/source4/dsdb/samdb/ldb_modules/dirsync.c +@@ -56,7 +56,6 @@ struct dirsync_context { + bool linkIncrVal; + bool localonly; + bool partial; +- bool assystem; + int functional_level; + const struct GUID *our_invocation_id; + const struct dsdb_schema *schema; +@@ -872,10 +871,6 @@ static int dirsync_search_callback(struct ldb_request *req, struct ldb_reply *ar + DSDB_SEARCH_SHOW_DELETED | + DSDB_SEARCH_SHOW_EXTENDED_DN; + +- if (dsc->assystem) { +- flags = flags | DSDB_FLAG_AS_SYSTEM; +- } +- + ret = dsdb_module_search_tree(dsc->module, dsc, &res, + dn, LDB_SCOPE_BASE, + req->op.search.tree, +@@ -1102,16 +1097,21 @@ static int dirsync_ldb_search(struct ldb_module *module, struct ldb_request *req + return LDB_ERR_OPERATIONS_ERROR; + } + objectclass = dsdb_get_structural_oc_from_msg(schema, acl_res->msgs[0]); ++ ++ /* ++ * While we never use the answer to this for access ++ * control (after CVE-2023-4154), we return a ++ * different error message depending on if the user ++ * was granted GUID_DRS_GET_CHANGES to provide a closer ++ * emulation and keep some tests passing. ++ * ++ * (Samba's ACL logic is not well suited to redacting ++ * only the secret and RODC filtered attributes). ++ */ + ret = acl_check_extended_right(dsc, module, req, objectclass, + sd, acl_user_token(module), + GUID_DRS_GET_CHANGES, SEC_ADS_CONTROL_ACCESS, sid); + +- if (ret == LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS) { +- return ret; +- } +- dsc->assystem = true; +- ret = ldb_request_add_control(req, LDB_CONTROL_AS_SYSTEM_OID, false, NULL); +- + if (ret != LDB_SUCCESS) { + return ret; + } +-- +2.25.1 + diff -Nru samba-4.15.13+dfsg/debian/patches/CVE-2023-42669.patch samba-4.15.13+dfsg/debian/patches/CVE-2023-42669.patch --- samba-4.15.13+dfsg/debian/patches/CVE-2023-42669.patch 1970-01-01 00:00:00.000000000 +0000 +++ samba-4.15.13+dfsg/debian/patches/CVE-2023-42669.patch 2023-10-04 12:26:09.000000000 +0000 @@ -0,0 +1,78 @@ +Backport of: + +From 3f62a590b02bf4c888a995017e2575d3b2ec6ac9 Mon Sep 17 00:00:00 2001 +From: Andrew Bartlett +Date: Tue, 12 Sep 2023 18:59:44 +1200 +Subject: [PATCH 1/2] CVE-2023-42669 s4-rpc_server: Disable rpcecho server by + default + +The rpcecho server is useful in development and testing, but should never +have been allowed into production, as it includes the facility to +do a blocking sleep() in the single-threaded rpc worker. + +BUG: https://bugzilla.samba.org/show_bug.cgi?id=15474 + +Signed-off-by: Andrew Bartlett +--- + docs-xml/smbdotconf/protocol/dcerpcendpointservers.xml | 2 +- + lib/param/loadparm.c | 2 +- + selftest/target/Samba4.pm | 2 +- + source3/param/loadparm.c | 2 +- + source4/rpc_server/wscript_build | 3 ++- + 5 files changed, 6 insertions(+), 5 deletions(-) + +--- a/docs-xml/smbdotconf/protocol/dcerpcendpointservers.xml ++++ b/docs-xml/smbdotconf/protocol/dcerpcendpointservers.xml +@@ -6,6 +6,6 @@ + Specifies which DCE/RPC endpoint servers should be run. + + +-epmapper, wkssvc, rpcecho, samr, netlogon, lsarpc, drsuapi, dssetup, unixinfo, browser, eventlog6, backupkey, dnsserver ++epmapper, wkssvc, samr, netlogon, lsarpc, drsuapi, dssetup, unixinfo, browser, eventlog6, backupkey, dnsserver + rpcecho + +--- a/lib/param/loadparm.c ++++ b/lib/param/loadparm.c +@@ -2729,7 +2729,7 @@ struct loadparm_context *loadparm_init(T + lpcfg_do_global_parameter(lp_ctx, "ntvfs handler", "unixuid default"); + lpcfg_do_global_parameter(lp_ctx, "max connections", "0"); + +- lpcfg_do_global_parameter(lp_ctx, "dcerpc endpoint servers", "epmapper wkssvc rpcecho samr netlogon lsarpc drsuapi dssetup unixinfo browser eventlog6 backupkey dnsserver"); ++ lpcfg_do_global_parameter(lp_ctx, "dcerpc endpoint servers", "epmapper wkssvc samr netlogon lsarpc drsuapi dssetup unixinfo browser eventlog6 backupkey dnsserver"); + lpcfg_do_global_parameter(lp_ctx, "server services", "s3fs rpc nbt wrepl ldap cldap kdc drepl winbindd ntp_signd kcc dnsupdate dns"); + lpcfg_do_global_parameter(lp_ctx, "kccsrv:samba_kcc", "true"); + /* the winbind method for domain controllers is for both RODC +--- a/selftest/target/Samba4.pm ++++ b/selftest/target/Samba4.pm +@@ -781,7 +781,7 @@ sub provision_raw_step1($$) + wins support = yes + server role = $ctx->{server_role} + server services = +echo $services +- dcerpc endpoint servers = +winreg +srvsvc ++ dcerpc endpoint servers = +winreg +srvsvc +rpcecho + notify:inotify = false + ldb:nosync = true + ldap server require strong auth = yes +--- a/source3/param/loadparm.c ++++ b/source3/param/loadparm.c +@@ -878,7 +878,7 @@ static void init_globals(struct loadparm + + Globals.server_services = str_list_make_v3_const(NULL, "s3fs rpc nbt wrepl ldap cldap kdc drepl winbindd ntp_signd kcc dnsupdate dns", NULL); + +- Globals.dcerpc_endpoint_servers = str_list_make_v3_const(NULL, "epmapper wkssvc rpcecho samr netlogon lsarpc drsuapi dssetup unixinfo browser eventlog6 backupkey dnsserver", NULL); ++ Globals.dcerpc_endpoint_servers = str_list_make_v3_const(NULL, "epmapper wkssvc samr netlogon lsarpc drsuapi dssetup unixinfo browser eventlog6 backupkey dnsserver", NULL); + + Globals.tls_enabled = true; + Globals.tls_verify_peer = TLS_VERIFY_PEER_AS_STRICT_AS_POSSIBLE; +--- a/source4/rpc_server/wscript_build ++++ b/source4/rpc_server/wscript_build +@@ -29,7 +29,8 @@ bld.SAMBA_MODULE('dcerpc_rpcecho', + source='echo/rpc_echo.c', + subsystem='dcerpc_server', + init_function='dcerpc_server_rpcecho_init', +- deps='ndr-standard events' ++ deps='ndr-standard events', ++ enabled=bld.CONFIG_GET('ENABLE_SELFTEST') + ) + + diff -Nru samba-4.15.13+dfsg/debian/patches/issue-when-updating-old-passwd-containing-regex-metachars.patch samba-4.15.13+dfsg/debian/patches/issue-when-updating-old-passwd-containing-regex-metachars.patch --- samba-4.15.13+dfsg/debian/patches/issue-when-updating-old-passwd-containing-regex-metachars.patch 1970-01-01 00:00:00.000000000 +0000 +++ samba-4.15.13+dfsg/debian/patches/issue-when-updating-old-passwd-containing-regex-metachars.patch 2023-08-18 13:53:14.000000000 +0000 @@ -0,0 +1,25 @@ +Description: Fix samba to allow updating old password + The patch here is one line long. It is created from the + 'suggested fix' in the reported bug in Ubuntu: + https://bugs.launchpad.net/ubuntu/+source/samba/+bug/2002949 + The trick is to replace '(opt_str, secret_data)' with re.escape that + are used to escape any special characters in 'opt_str' and + 'secret_data', ensuring that they are treated as literal characters in + the regex pattern. +Origin: other, https://bugs.launchpad.net/ubuntu/+source/samba/+bug/2002949 +Bug: https://bugzilla.samba.org/show_bug.cgi?id=15289 +Bug-Ubuntu: https://bugs.launchpad.net/ubuntu/+source/samba/+bug/2002949 +Last-Update: 2023-08-21 +--- +This patch header follows DEP-3: http://dep.debian.net/deps/dep3/ +--- a/python/samba/getopt.py ++++ b/python/samba/getopt.py +@@ -198,7 +198,7 @@ + # [= ] -> The option and the secret data might be separated by space + # or equal sign. + # [ ]*.* -> After the secret data might be one, many or no space. +- pass_opt_re_str = "(.*[ ]+)(%s[= ]%s)([ ]*.*)" % (opt_str, secret_data) ++ pass_opt_re_str = "(.*[ ]+)(%s[= ]%s)([ ]*.*)" % (re.escape(opt_str), re.escape(secret_data)) + pass_opt_re = re.compile(pass_opt_re_str) + # Get current proctitle. + cur_proctitle = setproctitle.getproctitle() diff -Nru samba-4.15.13+dfsg/debian/patches/secure-channel-faulty-kb5028166.patch samba-4.15.13+dfsg/debian/patches/secure-channel-faulty-kb5028166.patch --- samba-4.15.13+dfsg/debian/patches/secure-channel-faulty-kb5028166.patch 1970-01-01 00:00:00.000000000 +0000 +++ samba-4.15.13+dfsg/debian/patches/secure-channel-faulty-kb5028166.patch 2023-07-23 20:09:59.000000000 +0000 @@ -0,0 +1,215 @@ +From 2150e7f3dc409b415ca8b6a541729a49932c5073 Mon Sep 17 00:00:00 2001 +From: Stefan Metzmacher +Date: Sat, 15 Jul 2023 17:20:32 +0200 +Subject: [PATCH 1/4] netlogon.idl: add support for netr_LogonGetCapabilities + response level 2 + +We don't have any documentation about this yet, but tests against +a Windows Server 2022 patched with KB5028166 revealed that +the response for query_level=2 is exactly the same as +for querey_level=1. + +Until we know the reason for query_level=2 we won't +use it as client nor support it in the server, but +we want ndrdump to work. + +BUG: https://bugzilla.samba.org/show_bug.cgi?id=15418 + +Signed-off-by: Stefan Metzmacher +Reviewed-by: Andrew Bartlett +(cherry picked from commit 5f87888ed53320538cf773d64868390d8641a40e) +--- + librpc/idl/netlogon.idl | 1 + + 1 file changed, 1 insertion(+) + +Ubuntu patch note: removed the parts that changed the upstream test suite + +Origin: backport, https://bugzilla.samba.org/attachment.cgi?id=17987 +Bug: https://bugzilla.samba.org/show_bug.cgi?id=15418 +Bug-Ubuntu: https://bugs.launchpad.net/ubuntu/+source/samba/+bug/2027716 +Last-Update: 2023-07-17 + +diff --git a/librpc/idl/netlogon.idl b/librpc/idl/netlogon.idl +index d956a661fff7..b51767136d3c 100644 +--- a/librpc/idl/netlogon.idl ++++ b/librpc/idl/netlogon.idl +@@ -1241,6 +1241,7 @@ interface netlogon + /* Function 0x15 */ + typedef [switch_type(uint32)] union { + [case(1)] netr_NegotiateFlags server_capabilities; ++ [case(2)] netr_NegotiateFlags server_capabilities; + } netr_Capabilities; + + NTSTATUS netr_LogonGetCapabilities( +-- +2.34.1 + + +From fa71e7b4b027dc8224fda7125f1faaefa4e71eae Mon Sep 17 00:00:00 2001 +From: Stefan Metzmacher +Date: Sat, 15 Jul 2023 16:11:48 +0200 +Subject: [PATCH 3/4] s4:rpc_server:netlogon: generate FAULT_INVALID_TAG for + invalid netr_LogonGetCapabilities levels + +This is important as Windows clients with KB5028166 seem to +call netr_LogonGetCapabilities with query_level=2 after +a call with query_level=1. + +An unpatched Windows Server returns DCERPC_NCA_S_FAULT_INVALID_TAG +for query_level values other than 1. +While Samba tries to return NT_STATUS_NOT_SUPPORTED, but +later fails to marshall the response, which results +in DCERPC_FAULT_BAD_STUB_DATA instead. + +Because we don't have any documentation for level 2 yet, +we just try to behave like an unpatched server and +generate DCERPC_NCA_S_FAULT_INVALID_TAG instead of +DCERPC_FAULT_BAD_STUB_DATA. +Which allows patched Windows clients to keep working +against a Samba DC. + +BUG: https://bugzilla.samba.org/show_bug.cgi?id=15418 + +Signed-off-by: Stefan Metzmacher +Reviewed-by: Andrew Bartlett +(cherry picked from commit d5f1097b6220676d56ed5fc6707acf667b704518) +--- + .../knownfail.d/netr_LogonGetCapabilities | 2 -- + source4/rpc_server/netlogon/dcerpc_netlogon.c | 28 ++++++++++++++++--- + 2 files changed, 24 insertions(+), 6 deletions(-) + +diff --git a/source4/rpc_server/netlogon/dcerpc_netlogon.c b/source4/rpc_server/netlogon/dcerpc_netlogon.c +index 6a3e044eb9da..26be4f567513 100644 +--- a/source4/rpc_server/netlogon/dcerpc_netlogon.c ++++ b/source4/rpc_server/netlogon/dcerpc_netlogon.c +@@ -2399,6 +2399,30 @@ static NTSTATUS dcesrv_netr_LogonGetCapabilities(struct dcesrv_call_state *dce_c + struct netlogon_creds_CredentialState *creds; + NTSTATUS status; + ++ switch (r->in.query_level) { ++ case 1: ++ break; ++ case 2: ++ /* ++ * Until we know the details behind KB5028166 ++ * just return DCERPC_NCA_S_FAULT_INVALID_TAG ++ * like an unpatched Windows Server. ++ */ ++ FALL_THROUGH; ++ default: ++ /* ++ * There would not be a way to marshall the ++ * the response. Which would mean our final ++ * ndr_push would fail an we would return ++ * an RPC-level fault with DCERPC_FAULT_BAD_STUB_DATA. ++ * ++ * But it's important to match a Windows server ++ * especially before KB5028166, see also our bug #15418 ++ * Otherwise Windows client would stop talking to us. ++ */ ++ DCESRV_FAULT(DCERPC_NCA_S_FAULT_INVALID_TAG); ++ } ++ + status = dcesrv_netr_creds_server_step_check(dce_call, + mem_ctx, + r->in.computer_name, +@@ -2410,10 +2434,6 @@ static NTSTATUS dcesrv_netr_LogonGetCapabilities(struct dcesrv_call_state *dce_c + } + NT_STATUS_NOT_OK_RETURN(status); + +- if (r->in.query_level != 1) { +- return NT_STATUS_NOT_SUPPORTED; +- } +- + r->out.capabilities->server_capabilities = creds->negotiate_flags; + + return NT_STATUS_OK; +-- +2.34.1 + + +From 05f110e1a4d4b38bfbaaa3a92fda7a9127b3b456 Mon Sep 17 00:00:00 2001 +From: Stefan Metzmacher +Date: Sat, 15 Jul 2023 16:11:48 +0200 +Subject: [PATCH 4/4] s3:rpc_server:netlogon: generate FAULT_INVALID_TAG for + invalid netr_LogonGetCapabilities levels + +This is important as Windows clients with KB5028166 seem to +call netr_LogonGetCapabilities with query_level=2 after +a call with query_level=1. + +An unpatched Windows Server returns DCERPC_NCA_S_FAULT_INVALID_TAG +for query_level values other than 1. +While Samba tries to return NT_STATUS_NOT_SUPPORTED, but +later fails to marshall the response, which results +in DCERPC_FAULT_BAD_STUB_DATA instead. + +Because we don't have any documentation for level 2 yet, +we just try to behave like an unpatched server and +generate DCERPC_NCA_S_FAULT_INVALID_TAG instead of +DCERPC_FAULT_BAD_STUB_DATA. +Which allows patched Windows clients to keep working +against a Samba DC. + +BUG: https://bugzilla.samba.org/show_bug.cgi?id=15418 + +Signed-off-by: Stefan Metzmacher +Reviewed-by: Andrew Bartlett + +Autobuild-User(master): Stefan Metzmacher +Autobuild-Date(master): Mon Jul 17 07:35:09 UTC 2023 on atb-devel-224 + +(cherry picked from commit dfeabce44fbb78083fbbb2aa634fc4172cf83db9) +--- + .../knownfail.d/netr_LogonGetCapabilities | 1 - + source3/rpc_server/netlogon/srv_netlog_nt.c | 29 ++++++++++++++++--- + 2 files changed, 25 insertions(+), 5 deletions(-) + delete mode 100644 selftest/knownfail.d/netr_LogonGetCapabilities + +diff --git a/source3/rpc_server/netlogon/srv_netlog_nt.c b/source3/rpc_server/netlogon/srv_netlog_nt.c +index 5906464a9f3..35433ec6781 100644 +--- a/source3/rpc_server/netlogon/srv_netlog_nt.c ++++ b/source3/rpc_server/netlogon/srv_netlog_nt.c +@@ -2421,6 +2421,31 @@ NTSTATUS _netr_LogonGetCapabilities(struct pipes_struct *p, + struct netlogon_creds_CredentialState *creds; + NTSTATUS status; + ++ switch (r->in.query_level) { ++ case 1: ++ break; ++ case 2: ++ /* ++ * Until we know the details behind KB5028166 ++ * just return DCERPC_NCA_S_FAULT_INVALID_TAG ++ * like an unpatched Windows Server. ++ */ ++ FALL_THROUGH; ++ default: ++ /* ++ * There would not be a way to marshall the ++ * the response. Which would mean our final ++ * ndr_push would fail an we would return ++ * an RPC-level fault with DCERPC_FAULT_BAD_STUB_DATA. ++ * ++ * But it's important to match a Windows server ++ * especially before KB5028166, see also our bug #15418 ++ * Otherwise Windows client would stop talking to us. ++ */ ++ p->fault_state = DCERPC_NCA_S_FAULT_INVALID_TAG; ++ return NT_STATUS_NOT_SUPPORTED; ++ } ++ + become_root(); + status = netr_creds_server_step_check(p, p->mem_ctx, + r->in.computer_name, +@@ -2432,10 +2457,6 @@ NTSTATUS _netr_LogonGetCapabilities(struct pipes_struct *p, + return status; + } + +- if (r->in.query_level != 1) { +- return NT_STATUS_NOT_SUPPORTED; +- } +- + r->out.capabilities->server_capabilities = creds->negotiate_flags; + + return NT_STATUS_OK; diff -Nru samba-4.15.13+dfsg/debian/patches/series samba-4.15.13+dfsg/debian/patches/series --- samba-4.15.13+dfsg/debian/patches/series 2023-07-11 12:43:49.000000000 +0000 +++ samba-4.15.13+dfsg/debian/patches/series 2023-10-04 12:38:12.000000000 +0000 @@ -64,3 +64,29 @@ CVE-2023-34968-10.patch CVE-2023-34968-11.patch CVE-2023-34968-12.patch +secure-channel-faulty-kb5028166.patch +issue-when-updating-old-passwd-containing-regex-metachars.patch +CVE-2023-4091-1.patch +CVE-2023-4091-2.patch +CVE-2023-4154-01.patch +CVE-2023-4154-02.patch +CVE-2023-4154-03.patch +CVE-2023-4154-04.patch +CVE-2023-4154-05.patch +CVE-2023-4154-06.patch +CVE-2023-4154-07.patch +CVE-2023-4154-08.patch +CVE-2023-4154-09.patch +CVE-2023-4154-10.patch +CVE-2023-4154-11.patch +CVE-2023-4154-12.patch +CVE-2023-4154-13.patch +CVE-2023-4154-14.patch +CVE-2023-4154-15.patch +CVE-2023-4154-16.patch +CVE-2023-4154-17.patch +CVE-2023-4154-18.patch +CVE-2023-4154-19.patch +CVE-2023-4154-20.patch +CVE-2023-4154-21.patch +CVE-2023-42669.patch diff -Nru samba-4.15.13+dfsg/debian/tests/control samba-4.15.13+dfsg/debian/tests/control --- samba-4.15.13+dfsg/debian/tests/control 2022-11-08 13:59:27.000000000 +0000 +++ samba-4.15.13+dfsg/debian/tests/control 2023-07-23 20:09:59.000000000 +0000 @@ -24,3 +24,7 @@ Tests: smbclient-share-access-uring Depends: samba, samba-vfs-modules, smbclient, coreutils, systemd, passwd Restrictions: needs-root, allow-stderr, isolation-container, skippable + +Tests: samba-ad-dc-provisioning-internal-dns +Depends: samba, samba-dsdb-modules, samba-vfs-modules, winbind, smbclient, krb5-user, bind9-dnsutils, lxd | snapd, lsb-release, dctrl-tools +Restrictions: needs-root, isolation-machine, allow-stderr, breaks-testbed diff -Nru samba-4.15.13+dfsg/debian/tests/samba-ad-dc-provisioning-internal-dns samba-4.15.13+dfsg/debian/tests/samba-ad-dc-provisioning-internal-dns --- samba-4.15.13+dfsg/debian/tests/samba-ad-dc-provisioning-internal-dns 1970-01-01 00:00:00.000000000 +0000 +++ samba-4.15.13+dfsg/debian/tests/samba-ad-dc-provisioning-internal-dns 2023-07-23 20:09:59.000000000 +0000 @@ -0,0 +1,404 @@ +#!/bin/bash + +set -e +set -o pipefail + +source debian/tests/util + +declare -r domain="EXAMPLE" +declare -r realm="EXAMPLE.FAKE" +declare -r adminpass="Passw0rd" +declare -r test_user="test_user_${RANDOM}" +declare -r test_pw="test_user_secret_${RANDOM}" +declare -A user_pass +user_pass[Administrator]="${adminpass}" +user_pass[${test_user}]="${test_pw}" +declare -A join_method_deps +# Minimum set of deps: let realmd install the extra dependencies +# as needed, depending on the join method. +join_method_deps[realmd_sssd]="realmd krb5-user smbclient" +join_method_deps[realmd_winbind]="realmd krb5-user smbclient" + + +cleanup() { + rc=$? + set +e # so we don't exit midcleanup + if [ ${rc} -ne 0 ]; then + echo "## Something failed, gathering logs" + echo + echo "## smb.conf" + cat /etc/samba/smb.conf + echo + echo "## resolv.conf" + cat /etc/resolv.conf + echo + echo "## resolvectl status" + resolvectl status + echo "## journal for samba-ad-dc.service" + journalctl -u samba-ad-dc.service --lines 500 + echo + for log in /var/log/samba/log.*; do + # skip compressed logrotated files + if [ "${log%.gz}" != "${log}" ]; then + continue + fi + [ -s "${log}" ] || continue + echo "## $(basename ${log}):" + tail -n 500 "${log}" + echo + done + echo "## syslog" + tail -n 500 /var/log/syslog + fi +} + +trap cleanup EXIT + +assert_testparm() { + local parameter="${1}" + local expected_value="${2}" + local current_value="" + local -i retval=0 + + echo -n "Asserting ${parameter} is ${expected_value}: " + current_value=$(testparm -s --parameter-name "${parameter}" 2>/dev/null) || { + retval=$? + echo "FAIL" + return ${retval} + } + if [ "${current_value}" = "${expected_value}" ]; then + echo "OK" + return 0 + else + echo "FAIL" + return 1 + fi +} + +basic_config_tests() { + echo "## Basic config tests" + testparm -s > /dev/null + assert_testparm "realm" "${realm}" + assert_testparm "workgroup" "${domain}" + assert_testparm "server role" "active directory domain controller" + echo +} + +dns_tests() { + echo "## DNS tests" + echo "Obtaining administrator kerberos ticket" + echo "${adminpass}" | timeout --verbose 30 kinit Administrator + echo + echo "Querying server info" + samba-tool dns serverinfo "$(hostname)" + echo + echo "Checking we got a service ticket of type host/" + klist | grep -i "host/$(hostname)" + echo + echo "Checking specific DNS records" + for srv in _ldap._tcp _kerberos._tcp _kerberos._udp _kpasswd._udp; do + echo -n "${srv}.${realm,,}: " + dig @localhost +short -t SRV ${srv}.${realm,,} + echo + done + echo + echo -n "Checking that our hostname \"$(hostname)\" is in DNS: " + myip=$(dig @localhost +short -t A "$(hostname).${realm,,}") + echo "${myip}" + echo +} + +user_creation_tests() { + echo "## User creation tests" + samba-tool domain passwordsettings set --complexity=off + echo "Creating user \"${test_user}\" with password ${test_pw}" + samba-tool user add "${test_user}" "${test_pw}" + echo + echo "Attempting to obtain kerberos ticket for user \"${test_user}\"" + # just in case it ends up waiting at a prompt, we use "timeout" + echo "${test_pw}" | timeout --verbose 30 kinit "${test_user}" + echo "Ticket obtained" + klist + echo +} + +smbclient_tests() { + echo "## smbclient tests" + kdestroy || : + echo + echo "Obtaining a TGT for ${test_user}" + echo "${test_pw}" | timeout --verbose 30 kinit "${test_user}" + klist | grep krbtgt + echo + echo "Attempting password-less authentication with smbclient" + echo + echo "Listing shares" + smbclient -L "$(hostname)" --use-kerberos=required -k + echo + echo "Listing the sysvol share" + smbclient "//$(hostname)/sysvol" --use-kerberos=required -k -c "ls" + echo + echo "Listing policies" + # lowercase the ${realm} + smbclient "//$(hostname)/sysvol" --use-kerberos=required -k -c "ls ${realm,,}/Policies/*" + echo + echo "Checking that we have a ticket for the cifs service after all these commands" + klist | grep cifs/ + echo +} + +server_join_tests() { + local member_server + # the join methods are the keys of the join_method_deps dict + local -a methods=("${!join_method_deps[@]}") + local member_server="member-server" + + echo "## Server join tests" + echo "## Initializing lxd" + setup_lxd "${realm,,}" + + for method in "${methods[@]}"; do + echo "## Setting up member server to join a domain using method ${method}" + setup_member_server "${member_server}" "${method}" + echo "## Joining domain with method ${method}" + join_domain "${member_server}" "${method}" + echo + echo "## Verifying join with method ${method}" + verify_join "${member_server}" "${method}" + echo + echo "## Leaving domain with method ${method}" + leave_domain "${member_server}" "${method}" + echo + echo "## Destroying member server" + lxc delete --force "${member_server}" + done +} + +setup_member_server() { + local container_name="${1}" + local method="${2}" + local release + + release="$(lsb_release -cs)" + if [ -z "${join_method_deps[${method}]}" ]; then + echo "## INTERNAL ERROR, invalid join method: ${method}" + return 1 + fi + echo "## Got test dependencies: ${join_method_deps[${method}]}" + # can't use cloud-init here to install packages, because we first need to + # sync the apt config from the host to the container + echo "## Launching ${release} container" + lxc launch "ubuntu-daily:${release}" "${container_name}" -q + wait_container_ready "${container_name}" + send_apt_config "${container_name}" + copy_local_apt_files "${container_name}" + echo "## Installing dependencies in test container" + install_packages_in_container "${container_name}" ${join_method_deps[${method}]} +} + +join_domain_realmd_winbind() { + local server="${1}" + local discover_cmd="realm discover -v --membership-software=samba --client-software=winbind ${realm,,}" + local join_cmd="realm join -v --membership-software=samba --client-software=winbind ${realm,,}" + + echo "## Domain information" + lxc exec "${server}" -- ${discover_cmd} + echo + echo "## Running join command: ${join_cmd}" + echo "${adminpass}" | lxc exec "${server}" -- ${join_cmd} + # LP: #1980246 + # So far, only lunar and later automatically add winbind to /etc/nsswitch.conf. + lxc exec "${server}" -- sed -r -i \ + -e '/^(passwd|group):.*[[:space:]]winbind\b/b' \ + -e 's/^(passwd|group):.*/& winbind/' \ + /etc/nsswitch.conf +} + +verify_join_realmd_winbind() { + local server="${1}" + local member_domain + + echo -n "## Verifying member server joined domain name: " + member_domain=$(lxc exec "${server}" -- wbinfo --own-domain) + echo "${member_domain}" + if [ "${member_domain}" != "${domain}" ]; then + echo "ERROR: expected member server domain to match the joined domain:" + echo "member server domain: ${member_domain}" + echo "AD domain: ${domain}" + return 1 + fi + echo + # we just want to see the output, not parse it + echo "## Domain status in member server" + lxc exec "${server}" -- wbinfo --domain-info "${member_domain}" + echo + echo "## User status in member server" + for u in "${!user_pass[@]}"; do + echo "## User \"${u}@${realm}\" information:" + lxc exec "${server}" -- wbinfo --user-info "${u}@${realm}" + echo + echo "## id ${u}@${realm}" + lxc exec "${server}" -- id ${u}@${realm} + echo + echo "## kinit authentication check for user \"${u}@${realm}\" inside member server" + echo "${user_pass[${u}]}" | lxc exec "${server}" -- timeout --verbose 30 kinit "${u}@${realm}" + lxc exec "${server}" -- klist + echo + echo "## Listing shares with the obtained kerberos ticket" + lxc exec "${server}" -- smbclient -L "$(hostname)" --use-kerberos=required -k + lxc exec "${server}" -- kdestroy + echo + echo "## wbinfo authentication check for user \"${u}@${realm}\" inside member server" + # non-interactive format for username is user%password + lxc exec "${server}" -- wbinfo --authenticate="${u}@${realm}%${user_pass[${u}]}" + echo + echo "## wbinfo kerberos authentication check for user \"${u}@${realm}\" inside member server" + lxc exec "${server}" -- wbinfo --krb5auth="${u}@${realm}%${user_pass[${u}]}" + echo + echo "## Listing shares with the obtained kerberos ticket" + lxc exec "${server}" -- smbclient -L "$(hostname)" --use-kerberos=required -k + lxc exec "${server}" -- kdestroy + done +} + +leave_domain_realmd_winbind() { + local server="${1}" + local leave_cmd="realm leave -v --remove --client-software=winbind" + + echo "## Running leave command: ${leave_cmd}" + echo "${adminpass}" | lxc exec "${server}" -- ${leave_cmd} +} + +join_domain_realmd_sssd() { + local server="${1}" + local discover_cmd="realm discover -v --membership-software=adcli --client-software=sssd ${realm,,}" + local join_cmd="realm join -v --membership-software=adcli --client-software=sssd ${realm,,}" + + echo "## Domain information" + lxc exec "${server}" -- ${discover_cmd} + echo + echo "## Running join command: ${join_cmd}" + echo "${adminpass}" | lxc exec "${server}" -- ${join_cmd} + echo +} + +verify_join_realmd_sssd() { + local server="${1}" + local samba_domain + + echo -n "## Verifying member server joined domain name: " + samba_domain=$(lxc exec "${server}" -- sssctl domain-list) + echo "${samba_domain}" + if [ "${samba_domain}" != "${realm,,}" ]; then + echo "ERROR: expected member server domain to match the joined domain:" + echo "member server domain: ${samba_domain}" + echo "AD domain: ${realm,,}" + return 1 + fi + echo + # we just want to see the output, not parse it + echo "## Domain status in member server" + lxc exec "${server}" -- sssctl domain-status "${realm}" + echo + echo "## User status in member server" + for u in "${!user_pass[@]}"; do + echo "## User \"${u}@${realm}\" information:" + lxc exec "${server}" -- sssctl user-checks "${u}@${realm}" + echo + echo "## id ${u}@${realm}" + lxc exec "${server}" -- id "${u}@${realm}" + echo + echo "## kinit authentication check for user \"${u}@${realm}\" inside member server" + echo "${user_pass[${u}]}" | lxc exec "${server}" -- timeout --verbose 30 kinit "${u}@${realm}" + lxc exec "${server}" -- klist + echo + echo "## Listing shares with the obtained kerberos ticket" + lxc exec "${server}" -- smbclient -L "$(hostname)" --use-kerberos=required -k + lxc exec "${server}" -- kdestroy + done +} + +leave_domain_realmd_sssd() { + local server="${1}" + local leave_cmd="realm leave -v --remove --client-software=sssd" + + echo "## Running leave command: ${leave_cmd}" + echo "${adminpass}" | lxc exec "${server}" -- ${leave_cmd} +} + +join_domain() { + local server="${1}" + local m="${2}" + + join_domain_${m} "${server}" +} + +verify_join() { + local server="${1}" + local m="${2}" + + verify_join_${m} "${server}" +} + +leave_domain() { + local server="${1}" + local m="${2}" + + leave_domain_${m} "${server}" +} + +systemctl stop smbd nmbd winbind +systemctl disable smbd nmbd winbind +systemctl mask smbd nmbd winbind + +systemctl unmask samba-ad-dc +systemctl enable samba-ad-dc + +if [ -f /etc/samba/smb.conf ]; then + mv /etc/samba/smb.conf{,.orig} +fi + +# make sure we are starting fresh, as previous tests might left things around + +rm -rf /var/lib/samba/* /var/cache/samba/* /run/samba/* +kdestroy || : + +samba-tool domain provision \ + --domain="${domain}" \ + --realm="${realm}" \ + --adminpass="${adminpass}" \ + --server-role=dc \ + --use-rfc2307 \ + --dns-backend=SAMBA_INTERNAL + +current_dns=$(resolvectl status | grep "^Current DNS Server:" | awk '{print $4}') + +if [ -n "${current_dns}" ]; then + echo "## Setting dns forwarder to ${current_dns} in smb.conf" + sed -r -i "s,dns forwarder = .*,dns forwarder = ${current_dns}," \ + /etc/samba/smb.conf + unlink /etc/resolv.conf + echo "nameserver 127.0.0.1" > /etc/resolv.conf + # lowercase substitution + echo "search ${realm,,}" >> /etc/resolv.conf + systemctl stop systemd-resolved + systemctl disable systemd-resolved +else + echo "## Warning, couldn't detect the current DNS server to use as forwarder in smb.conf" + echo "## resolvectl status:" + resolvectl status + echo "## Continuing, and hoping for the best" +fi + +cp -f /var/lib/samba/private/krb5.conf /etc/krb5.conf + +systemctl start samba-ad-dc + +# give it some time, it's a lot of services to start +sleep 5s + +basic_config_tests +dns_tests +user_creation_tests +smbclient_tests +server_join_tests diff -Nru samba-4.15.13+dfsg/debian/tests/util samba-4.15.13+dfsg/debian/tests/util --- samba-4.15.13+dfsg/debian/tests/util 2022-11-08 13:59:27.000000000 +0000 +++ samba-4.15.13+dfsg/debian/tests/util 2023-07-23 20:09:59.000000000 +0000 @@ -16,7 +16,7 @@ if [ -n "${vfs}" ]; then echo "vfs objects = ${vfs}" >> /etc/samba/smb.conf fi - systemctl restart smbd.service + systemctl reload smbd.service else echo "Share [${share}] already exists, continuing" fi @@ -63,3 +63,113 @@ return 1 fi } + +wait_container_ready() { + local container="${1}" + local -i limit=120 # seconds + local -i i=0 + local -i result=0 + local ip + local output + + while /bin/true; do + ip=$(lxc list "${container}" -c 4 --format=compact | tail -1 | awk '{print $1}') + if [ -n "${ip}" ]; then + break + fi + i=$((i+1)) + if [ ${i} -ge ${limit} ]; then + return 1 + fi + sleep 1s + echo -n "." + done + while ! nc -z "${ip}" 22; do + echo -n "." + i=$((i+1)) + if [ ${i} -ge ${limit} ]; then + return 1 + fi + sleep 1s + done + # cloud-init might still be doing things... + # this call blocks, so wrap it in its own little timeout + output=$(lxc exec "${container}" -- timeout --verbose $((limit-i)) cloud-init status --wait) || { + result=$? + echo "cloud-init status --wait failed on container ${container}" + echo "${output}" + return ${result} + } + echo +} + +install_lxd() { + if ! command -v lxd > /dev/null 2>&1; then + # the test depends has "lxd | snapd", so if we don't have lxd, we must + # install the snap + snap list lxd > /dev/null 2>&1 || { + echo "Installing the LXD snap..." + snap install lxd + } + fi +} + +setup_lxd() { + local dns_domain="${1}" + local network + local nic + local dns_ip + + install_lxd + # Stop samba while lxd is setup, to avoid conflicts on lxdbr0:53 + systemctl stop samba-ad-dc + lxd init --auto + lxd waitready --timeout 600 + network=$(lxc network list --format=compact | grep -E "bridge.*YES.*CREATED") + nic=$(echo "${network}" | awk '{print $1}') + dns_ip=$(echo "${network}" | awk '{print $4}' | cut -d / -f 1) # strip the cidr + # port=0 effectively disables dnsmasq's DNS, so it doesn't conflict with samba's DNS + lxc network set "${nic:-lxdbr0}" ipv6.address=none dns.domain="${dns_domain}" raw.dnsmasq="$(echo -e port=0\\ndhcp-option=option:dns-server,${dns_ip})" + if [ -n "${http_proxy}" ]; then + lxc config set core.proxy_http "${http_proxy}" + fi + if [ -n "${https_proxy}" ]; then + lxc config set core.proxy_https "${https_proxy}" + fi + if [ -n "${noproxy}" ]; then + lxc config set core.proxy_ignore_hosts "${noproxy}" + fi + systemctl start samba-ad-dc + # give it some time, it's a lot of services to start + sleep 5s +} + +# Copy the local apt package archive over to the lxd container. +copy_local_apt_files() { + local container_name="${1:-docker}" + + for local_source in $(apt-get indextargets | grep-dctrl -F URI -e '^file:/' -sURI | awk '{print $2}'); do + local_source=${local_source#file:} + local_dir=$(dirname "${local_source}") + lxc exec "${container_name}" -- mkdir -p "${local_dir}" + tar -cC "${local_dir}" . | lxc exec "${container_name}" -- tar -xC "${local_dir}" + done +} + +send_apt_config() { + echo "Copying over /etc/apt to container ${1}" + lxc exec "${1}" -- rm -rf /etc/apt + lxc exec "${1}" -- mkdir -p /etc/apt + tar -cC /etc/apt . | lxc exec "${1}" -- tar -xC /etc/apt +} + +install_packages_in_container() { + local container="${1}" + shift + local packages="${*}" + + echo "### Installing dependencies in member server container: ${packages}" + lxc exec "${container}" --env DEBIAN_FRONTEND=noninteractive -- apt-get update -q + lxc exec "${container}" --env DEBIAN_FRONTEND=noninteractive -- apt-get dist-upgrade -q -y + lxc exec "${container}" --env DEBIAN_FRONTEND=noninteractive -- apt-get install -q -y ${packages} +}