[Pkg-samba-maint] Bug#1121733: bookworm-pu: package samba/2:4.17.12+dfsg-0+deb12u3

Michael Tokarev mjt at tls.msk.ru
Mon Dec 1 13:22:28 GMT 2025


Package: release.debian.org
Severity: normal
Tags: bookworm
X-Debbugs-Cc: samba at packages.debian.org
Control: affects -1 + src:samba
User: release.debian.org at packages.debian.org
Usertags: pu

[ Reason ]
There are 3 known security hole exists in bookworm version
of samba.  These holes has been fixed in more recent versions
of the package, including trixie version, and the fixes has
been back-ported to earlier releases by the LTS samba community
(https://gitlab.com/samba-team/lts-community and the git tree
in there).

The vulnerabilities are:

 CVE-2018-14628: Unprivileged read of deleted object tombstones
    in AD LDAP server (#1034803)
 CVE-2025-10230: Command injection via WINS server hook script
 CVE-2025-9640: Uninitialized memory disclosure via vfs_streams_xattr

These aren't really huge holes, but it is still good to be able
to fix these.

[ Tests ]
There aren't much testing done for this release, because I don't
have the necessary testing environment.  The fixes are rather
targetted and has been well-tested in more recent versions.

[ Risks ]
I don't see much risks from these changes.  While the amount of
changes needed for CVE-2018-14628 fix is rather large, it is also
very well tested.

It would help still, if this release is available in proposed-updates
for a while.

[ Checklist ]
  [x] *all* changes are documented in the d/changelog
  [x] I reviewed all changes and I approve them
  [x] attach debdiff against the package in (old)stable
  [x] the issue is verified as fixed in unstable

[ Other info ]
4.17 series of samba reached its end-of-line wrt the upstream
support, however it is still maintained by the lts community.
With no official releases, - just as a collection of fixes in
the git tree.  This is the source of the changes.

Thanks,

/mjt

diff -Nru samba-4.17.12+dfsg/debian/changelog samba-4.17.12+dfsg/debian/changelog
--- samba-4.17.12+dfsg/debian/changelog	2025-07-11 11:21:51.000000000 +0300
+++ samba-4.17.12+dfsg/debian/changelog	2025-11-30 11:35:04.000000000 +0300
@@ -1,3 +1,12 @@
+samba (2:4.17.12+dfsg-0+deb12u3) bookworm; urgency=medium
+
+  * CVE-2018-14628: Unprivileged read of deleted object tombstones
+    in AD LDAP server.  Closes: #1034803
+  * CVE-2025-10230: Command injection via WINS server hook script
+  * CVE-2025-9640: Uninitialized memory disclosure via vfs_streams_xattr
+
+ -- Michael Tokarev <mjt at tls.msk.ru>  Sun, 30 Nov 2025 11:35:04 +0300
+
 samba (2:4.17.12+dfsg-0+deb12u2) bookworm; urgency=medium
 
   [ Salvatore Bonaccorso ]
diff -Nru samba-4.17.12+dfsg/debian/patches/CVE-2018-14628/01-python-descriptor-add-get_deletedobjects_descriptor.patch samba-4.17.12+dfsg/debian/patches/CVE-2018-14628/01-python-descriptor-add-get_deletedobjects_descriptor.patch
--- samba-4.17.12+dfsg/debian/patches/CVE-2018-14628/01-python-descriptor-add-get_deletedobjects_descriptor.patch	1970-01-01 03:00:00.000000000 +0300
+++ samba-4.17.12+dfsg/debian/patches/CVE-2018-14628/01-python-descriptor-add-get_deletedobjects_descriptor.patch	2025-11-30 11:35:04.000000000 +0300
@@ -0,0 +1,43 @@
+From: Stefan Metzmacher <metze at samba.org>
+Date: Fri, 29 Jan 2016 23:30:59 +0100
+Subject: CVE-2018-14628: python:descriptor: add get_deletedobjects_descriptor()
+
+samba-tool drs clone-dc-database was quite useful to find
+the true value of nTSecurityDescriptor of the CN=Delete Objects
+containers.
+
+Only the auto inherited SACL is available via a ldap search.
+
+BUG: https://bugzilla.samba.org/show_bug.cgi?id=13595
+
+Signed-off-by: Stefan Metzmacher <metze at samba.org>
+Reviewed-by: Andrew Bartlett <abartlet at samba.org>
+(cherry picked from commit 3be190dcf7153e479383f7f3d29ddca43fe121b8)
+---
+ python/samba/descriptor.py | 10 ++++++++++
+ 1 file changed, 10 insertions(+)
+
+diff --git a/python/samba/descriptor.py b/python/samba/descriptor.py
+index ac4c7e3273d..08c7518f56a 100644
+--- a/python/samba/descriptor.py
++++ b/python/samba/descriptor.py
+@@ -52,6 +52,16 @@ def get_empty_descriptor(domain_sid, name_map={}):
+ # "get_schema_descriptor" is located in "schema.py"
+ 
+ 
++def get_deletedobjects_descriptor(domain_sid, name_map=None):
++    if name_map is None:
++        name_map = {}
++
++    sddl = "O:SYG:SYD:PAI" \
++        "(A;;RPWPCCDCLCRCWOWDSDSW;;;SY)" \
++        "(A;;RPLC;;;BA)"
++    return sddl2binary(sddl, domain_sid, name_map)
++
++
+ def get_config_descriptor(domain_sid, name_map={}):
+     sddl = "O:EAG:EAD:(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
+            "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
+-- 
+2.47.3
+
diff -Nru samba-4.17.12+dfsg/debian/patches/CVE-2018-14628/02-python-provision-make-DELETEDOBJECTS_DESCRIPTOR-availab.patch samba-4.17.12+dfsg/debian/patches/CVE-2018-14628/02-python-provision-make-DELETEDOBJECTS_DESCRIPTOR-availab.patch
--- samba-4.17.12+dfsg/debian/patches/CVE-2018-14628/02-python-provision-make-DELETEDOBJECTS_DESCRIPTOR-availab.patch	1970-01-01 03:00:00.000000000 +0300
+++ samba-4.17.12+dfsg/debian/patches/CVE-2018-14628/02-python-provision-make-DELETEDOBJECTS_DESCRIPTOR-availab.patch	2025-11-30 11:35:04.000000000 +0300
@@ -0,0 +1,91 @@
+From: Stefan Metzmacher <metze at samba.org>
+Date: Fri, 29 Jan 2016 23:33:37 +0100
+Subject: CVE-2018-14628: python:provision: make DELETEDOBJECTS_DESCRIPTOR
+ available in the ldif files
+
+BUG: https://bugzilla.samba.org/show_bug.cgi?id=13595
+
+Signed-off-by: Stefan Metzmacher <metze at samba.org>
+Reviewed-by: Andrew Bartlett <abartlet at samba.org>
+(cherry picked from commit 0c329a0fda37d87ed737e4b579b6d04ec907604c)
+---
+ python/samba/provision/__init__.py | 5 +++++
+ python/samba/provision/sambadns.py | 4 ++++
+ 2 files changed, 9 insertions(+)
+
+diff --git a/python/samba/provision/__init__.py b/python/samba/provision/__init__.py
+index ff9b8fac916..f7d7468e4fa 100644
+--- a/python/samba/provision/__init__.py
++++ b/python/samba/provision/__init__.py
+@@ -78,6 +78,7 @@ from samba.provision.backend import (
+     LDBBackend,
+ )
+ from samba.descriptor import (
++    get_deletedobjects_descriptor,
+     get_empty_descriptor,
+     get_config_descriptor,
+     get_config_partitions_descriptor,
+@@ -1441,6 +1442,8 @@ def fill_samdb(samdb, lp, names, logger, policyguid,
+     msg["subRefs"] = ldb.MessageElement(names.configdn, ldb.FLAG_MOD_ADD,
+                                         "subRefs")
+ 
++    deletedobjects_descr = b64encode(get_deletedobjects_descriptor(names.domainsid)).decode('utf8')
++
+     samdb.invocation_id = invocationid
+ 
+     # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
+@@ -1472,6 +1475,7 @@ def fill_samdb(samdb, lp, names, logger, policyguid,
+                 "FOREST_FUNCTIONALITY": str(forestFunctionality),
+                 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
+                 "NTDSQUOTAS_DESCRIPTOR": ntdsquotas_descr,
++                "DELETEDOBJECTS_DESCRIPTOR": deletedobjects_descr,
+                 "LOSTANDFOUND_DESCRIPTOR": protected1wd_descr,
+                 "SERVICES_DESCRIPTOR": protected1_descr,
+                 "PHYSICALLOCATIONS_DESCRIPTOR": protected1wd_descr,
+@@ -1536,6 +1540,7 @@ def fill_samdb(samdb, lp, names, logger, policyguid,
+         "RIDAVAILABLESTART": str(next_rid + 600),
+         "POLICYGUID_DC": policyguid_dc,
+         "INFRASTRUCTURE_DESCRIPTOR": infrastructure_desc,
++        "DELETEDOBJECTS_DESCRIPTOR": deletedobjects_descr,
+         "LOSTANDFOUND_DESCRIPTOR": lostandfound_desc,
+         "SYSTEM_DESCRIPTOR": system_desc,
+         "BUILTIN_DESCRIPTOR": builtin_desc,
+diff --git a/python/samba/provision/sambadns.py b/python/samba/provision/sambadns.py
+index 9184711a764..d057b7830ad 100644
+--- a/python/samba/provision/sambadns.py
++++ b/python/samba/provision/sambadns.py
+@@ -42,6 +42,7 @@ from samba.dsdb import (
+     DS_GUID_USERS_CONTAINER
+ )
+ from samba.descriptor import (
++    get_deletedobjects_descriptor,
+     get_domain_descriptor,
+     get_domain_delete_protected1_descriptor,
+     get_domain_delete_protected2_descriptor,
+@@ -256,6 +257,7 @@ def setup_dns_partitions(samdb, domainsid, domaindn, forestdn, configdn,
+     domainzone_dn = "DC=DomainDnsZones,%s" % domaindn
+     forestzone_dn = "DC=ForestDnsZones,%s" % forestdn
+     descriptor = get_dns_partition_descriptor(domainsid)
++    deletedobjects_desc = get_deletedobjects_descriptor(domainsid)
+ 
+     setup_add_ldif(samdb, setup_path("provision_dnszones_partitions.ldif"), {
+         "ZONE_DN": domainzone_dn,
+@@ -278,6 +280,7 @@ def setup_dns_partitions(samdb, domainsid, domaindn, forestdn, configdn,
+         "ZONE_DNS": domainzone_dns,
+         "CONFIGDN": configdn,
+         "SERVERDN": serverdn,
++        "DELETEDOBJECTS_DESCRIPTOR": b64encode(deletedobjects_desc).decode('utf8'),
+         "LOSTANDFOUND_DESCRIPTOR": b64encode(protected2_desc).decode('utf8'),
+         "INFRASTRUCTURE_DESCRIPTOR": b64encode(protected1_desc).decode('utf8'),
+     })
+@@ -297,6 +300,7 @@ def setup_dns_partitions(samdb, domainsid, domaindn, forestdn, configdn,
+             "ZONE_DNS": forestzone_dns,
+             "CONFIGDN": configdn,
+             "SERVERDN": serverdn,
++            "DELETEDOBJECTS_DESCRIPTOR": b64encode(deletedobjects_desc).decode('utf8'),
+             "LOSTANDFOUND_DESCRIPTOR": b64encode(protected2_desc).decode('utf8'),
+             "INFRASTRUCTURE_DESCRIPTOR": b64encode(protected1_desc).decode('utf8'),
+         })
+-- 
+2.47.3
+
diff -Nru samba-4.17.12+dfsg/debian/patches/CVE-2018-14628/03-s4-setup-set-the-correct-nTSecurityDescriptor-on-the-CN.patch samba-4.17.12+dfsg/debian/patches/CVE-2018-14628/03-s4-setup-set-the-correct-nTSecurityDescriptor-on-the-CN.patch
--- samba-4.17.12+dfsg/debian/patches/CVE-2018-14628/03-s4-setup-set-the-correct-nTSecurityDescriptor-on-the-CN.patch	1970-01-01 03:00:00.000000000 +0300
+++ samba-4.17.12+dfsg/debian/patches/CVE-2018-14628/03-s4-setup-set-the-correct-nTSecurityDescriptor-on-the-CN.patch	2025-11-30 11:35:04.000000000 +0300
@@ -0,0 +1,67 @@
+From: Stefan Metzmacher <metze at samba.org>
+Date: Fri, 29 Jan 2016 23:34:15 +0100
+Subject: CVE-2018-14628: s4:setup: set the correct nTSecurityDescriptor on the
+ CN=Deleted Objects container
+
+This revealed a bug in our dirsync code, so we mark
+test_search_with_dirsync_deleted_objects as knownfail.
+
+BUG: https://bugzilla.samba.org/show_bug.cgi?id=13595
+
+Signed-off-by: Stefan Metzmacher <metze at samba.org>
+Reviewed-by: Andrew Bartlett <abartlet at samba.org>
+(cherry picked from commit 7f8b15faa76d05023c987fac2c4c31f9ac61bb47)
+---
+ selftest/knownfail.d/samba4.ldap.confidential_attr | 1 +
+ source4/setup/provision.ldif                       | 1 +
+ source4/setup/provision_configuration.ldif         | 1 +
+ source4/setup/provision_dnszones_add.ldif          | 1 +
+ 4 files changed, 4 insertions(+)
+ create mode 100644 selftest/knownfail.d/samba4.ldap.confidential_attr
+
+diff --git a/selftest/knownfail.d/samba4.ldap.confidential_attr b/selftest/knownfail.d/samba4.ldap.confidential_attr
+new file mode 100644
+index 00000000000..46a75ce928b
+--- /dev/null
++++ b/selftest/knownfail.d/samba4.ldap.confidential_attr
+@@ -0,0 +1 @@
++^samba4.ldap.confidential_attr.python.*.__main__.*.test_search_with_dirsync_deleted_objects
+diff --git a/source4/setup/provision.ldif b/source4/setup/provision.ldif
+index 5d9eba49f86..7f966fd57f8 100644
+--- a/source4/setup/provision.ldif
++++ b/source4/setup/provision.ldif
+@@ -34,6 +34,7 @@ isDeleted: TRUE
+ isCriticalSystemObject: TRUE
+ showInAdvancedViewOnly: TRUE
+ systemFlags: -1946157056
++nTSecurityDescriptor:: ${DELETEDOBJECTS_DESCRIPTOR}
+ 
+ # Computers located in "provision_computers*.ldif"
+ # Users/Groups located in "provision_users*.ldif"
+diff --git a/source4/setup/provision_configuration.ldif b/source4/setup/provision_configuration.ldif
+index 53c9c8536de..8fcbddbdae4 100644
+--- a/source4/setup/provision_configuration.ldif
++++ b/source4/setup/provision_configuration.ldif
+@@ -14,6 +14,7 @@ description: Container for deleted objects
+ isDeleted: TRUE
+ isCriticalSystemObject: TRUE
+ systemFlags: -1946157056
++nTSecurityDescriptor:: ${DELETEDOBJECTS_DESCRIPTOR}
+ 
+ # Extended rights
+ 
+diff --git a/source4/setup/provision_dnszones_add.ldif b/source4/setup/provision_dnszones_add.ldif
+index 860aa4b72b3..a2d6b6bab8f 100644
+--- a/source4/setup/provision_dnszones_add.ldif
++++ b/source4/setup/provision_dnszones_add.ldif
+@@ -8,6 +8,7 @@ description: Deleted objects
+ isDeleted: TRUE
+ isCriticalSystemObject: TRUE
+ systemFlags: -1946157056
++nTSecurityDescriptor:: ${DELETEDOBJECTS_DESCRIPTOR}
+ 
+ dn: CN=LostAndFound,${ZONE_DN}
+ objectClass: top
+-- 
+2.47.3
+
diff -Nru samba-4.17.12+dfsg/debian/patches/CVE-2018-14628/04-s4-dsdb-remove-unused-code-in-dirsync_filter_entry.patch samba-4.17.12+dfsg/debian/patches/CVE-2018-14628/04-s4-dsdb-remove-unused-code-in-dirsync_filter_entry.patch
--- samba-4.17.12+dfsg/debian/patches/CVE-2018-14628/04-s4-dsdb-remove-unused-code-in-dirsync_filter_entry.patch	1970-01-01 03:00:00.000000000 +0300
+++ samba-4.17.12+dfsg/debian/patches/CVE-2018-14628/04-s4-dsdb-remove-unused-code-in-dirsync_filter_entry.patch	2025-11-30 11:35:04.000000000 +0300
@@ -0,0 +1,99 @@
+From: Stefan Metzmacher <metze at samba.org>
+Date: Mon, 26 Jun 2023 15:14:24 +0200
+Subject: CVE-2018-14628: s4:dsdb: remove unused code in dirsync_filter_entry()
+
+This makes the next change easier to understand.
+
+BUG: https://bugzilla.samba.org/show_bug.cgi?id=13595
+
+Signed-off-by: Stefan Metzmacher <metze at samba.org>
+Reviewed-by: Andrew Bartlett <abartlet at samba.org>
+(cherry picked from commit 498542be0bbf4f26558573c1f87b77b8e3509371)
+---
+ source4/dsdb/samdb/ldb_modules/dirsync.c | 53 +++---------------------
+ 1 file changed, 5 insertions(+), 48 deletions(-)
+
+diff --git a/source4/dsdb/samdb/ldb_modules/dirsync.c b/source4/dsdb/samdb/ldb_modules/dirsync.c
+index fbb75790095..124cff25e39 100644
+--- a/source4/dsdb/samdb/ldb_modules/dirsync.c
++++ b/source4/dsdb/samdb/ldb_modules/dirsync.c
+@@ -151,10 +151,6 @@ static int dirsync_filter_entry(struct ldb_request *req,
+ 	 * list only the attribute that have been modified since last interogation
+ 	 *
+ 	 */
+-	newmsg = ldb_msg_new(dsc->req);
+-	if (newmsg == NULL) {
+-		return ldb_oom(ldb);
+-	}
+ 	for (i = msg->num_elements - 1; i >= 0; i--) {
+ 		if (ldb_attr_cmp(msg->elements[i].name, "uSNChanged") == 0) {
+ 			int error = 0;
+@@ -201,11 +197,6 @@ static int dirsync_filter_entry(struct ldb_request *req,
+ 			 */
+ 			return LDB_SUCCESS;
+ 		}
+-		newmsg->dn = ldb_dn_new(newmsg, ldb, "");
+-		if (newmsg->dn == NULL) {
+-			return ldb_oom(ldb);
+-		}
+-
+ 		el = ldb_msg_find_element(msg, "objectGUID");
+ 		if ( el != NULL) {
+ 			guidfound = true;
+@@ -216,48 +207,14 @@ static int dirsync_filter_entry(struct ldb_request *req,
+ 		 * well will uncomment the code bellow
+ 		 */
+ 		SMB_ASSERT(guidfound == true);
+-		/*
+-		if (guidfound == false) {
+-			struct GUID guid;
+-			struct ldb_val *new_val;
+-			DATA_BLOB guid_blob;
+-
+-			tmp[0] = '\0';
+-			txt = strrchr(txt, ':');
+-			if (txt == NULL) {
+-				return ldb_module_done(dsc->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
+-			}
+-			txt++;
+-
+-			status = GUID_from_string(txt, &guid);
+-			if (!NT_STATUS_IS_OK(status)) {
+-				return ldb_module_done(dsc->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
+-			}
+-
+-			status = GUID_to_ndr_blob(&guid, msg, &guid_blob);
+-			if (!NT_STATUS_IS_OK(status)) {
+-				return ldb_module_done(dsc->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
+-			}
+-
+-			new_val = talloc(msg, struct ldb_val);
+-			if (new_val == NULL) {
+-				return ldb_oom(ldb);
+-			}
+-			new_val->data = talloc_steal(new_val, guid_blob.data);
+-			new_val->length = guid_blob.length;
+-			if (ldb_msg_add_value(msg, "objectGUID", new_val, NULL) != 0) {
+-				return ldb_module_done(dsc->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
+-			}
+-		}
+-		*/
+-		ldb_msg_add(newmsg, el, LDB_FLAG_MOD_ADD);
+-		talloc_steal(newmsg->elements, el->name);
+-		talloc_steal(newmsg->elements, el->values);
+-
+-		talloc_steal(newmsg->elements, msg);
+ 		return ldb_module_send_entry(dsc->req, msg, controls);
+ 	}
+ 
++	newmsg = ldb_msg_new(dsc->req);
++	if (newmsg == NULL) {
++		return ldb_oom(ldb);
++	}
++
+ 	ndr_err = ndr_pull_struct_blob(replMetaData, dsc, &rmd,
+ 		(ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
+ 	if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+-- 
+2.47.3
+
diff -Nru samba-4.17.12+dfsg/debian/patches/CVE-2018-14628/05-dbchecker-use-get_deletedobjects_descriptor-for-missing.patch samba-4.17.12+dfsg/debian/patches/CVE-2018-14628/05-dbchecker-use-get_deletedobjects_descriptor-for-missing.patch
--- samba-4.17.12+dfsg/debian/patches/CVE-2018-14628/05-dbchecker-use-get_deletedobjects_descriptor-for-missing.patch	1970-01-01 03:00:00.000000000 +0300
+++ samba-4.17.12+dfsg/debian/patches/CVE-2018-14628/05-dbchecker-use-get_deletedobjects_descriptor-for-missing.patch	2025-11-30 11:35:04.000000000 +0300
@@ -0,0 +1,66 @@
+From: Stefan Metzmacher <metze at samba.org>
+Date: Wed, 7 Jun 2023 18:18:58 +0200
+Subject: CVE-2018-14628: dbchecker: use get_deletedobjects_descriptor for
+ missing deleted objects container
+
+BUG: https://bugzilla.samba.org/show_bug.cgi?id=13595
+
+Signed-off-by: Stefan Metzmacher <metze at samba.org>
+Reviewed-by: Andrew Bartlett <abartlet at samba.org>
+(cherry picked from commit 70586061128f90afa33f25e104d4570a1cf778db)
+---
+ python/samba/dbchecker.py | 17 ++++++++++++++---
+ 1 file changed, 14 insertions(+), 3 deletions(-)
+
+diff --git a/python/samba/dbchecker.py b/python/samba/dbchecker.py
+index 449b0a7d985..e124b1a0d67 100644
+--- a/python/samba/dbchecker.py
++++ b/python/samba/dbchecker.py
+@@ -20,7 +20,7 @@
+ import ldb
+ import samba
+ import time
+-from base64 import b64decode
++from base64 import b64decode, b64encode
+ from samba import dsdb
+ from samba import common
+ from samba.dcerpc import misc
+@@ -29,7 +29,11 @@ from samba.ndr import ndr_unpack, ndr_pack
+ from samba.dcerpc import drsblobs
+ from samba.samdb import dsdb_Dn
+ from samba.dcerpc import security
+-from samba.descriptor import get_wellknown_sds, get_diff_sds
++from samba.descriptor import (
++        get_wellknown_sds,
++        get_deletedobjects_descriptor,
++        get_diff_sds
++)
+ from samba.auth import system_session, admin_session
+ from samba.netcmd import CommandError
+ from samba.netcmd.fsmo import get_fsmo_roleowner
+@@ -341,6 +345,12 @@ class dbcheck(object):
+                 listwko.append('%s:%s' % (wko_prefix, dn))
+                 guid_suffix = ""
+ 
++
++            domain_sid = security.dom_sid(self.samdb.get_domain_sid())
++            sec_desc = get_deletedobjects_descriptor(domain_sid,
++                                                     name_map=self.name_map)
++            sec_desc_b64 = b64encode(sec_desc).decode('utf8')
++
+             # Insert a brand new Deleted Objects container
+             self.samdb.add_ldif("""dn: %s
+ objectClass: top
+@@ -349,7 +359,8 @@ description: Container for deleted objects
+ isDeleted: TRUE
+ isCriticalSystemObject: TRUE
+ showInAdvancedViewOnly: TRUE
+-systemFlags: -1946157056%s""" % (dn, guid_suffix),
++nTSecurityDescriptor:: %s
++systemFlags: -1946157056%s""" % (dn, sec_desc_b64, guid_suffix),
+                                 controls=["relax:0", "provision:0"])
+ 
+             delta = ldb.Message()
+-- 
+2.47.3
+
diff -Nru samba-4.17.12+dfsg/debian/patches/CVE-2018-14628/06-python-descriptor-let-samba-tool-dbch.patch samba-4.17.12+dfsg/debian/patches/CVE-2018-14628/06-python-descriptor-let-samba-tool-dbch.patch
--- samba-4.17.12+dfsg/debian/patches/CVE-2018-14628/06-python-descriptor-let-samba-tool-dbch.patch	1970-01-01 03:00:00.000000000 +0300
+++ samba-4.17.12+dfsg/debian/patches/CVE-2018-14628/06-python-descriptor-let-samba-tool-dbch.patch	2025-11-30 11:35:04.000000000 +0300
@@ -0,0 +1,138 @@
+From: Stefan Metzmacher <metze at samba.org>
+Date: Fri, 29 Jan 2016 23:35:31 +0100
+Subject: CVE-2018-14628: python:descriptor: let samba-tool dbcheck fix
+ the nTSecurityDescriptor on CN=Deleted Objects containers
+
+BUG: https://bugzilla.samba.org/show_bug.cgi?id=13595
+
+Signed-off-by: Stefan Metzmacher <metze at samba.org>
+Reviewed-by: Andrew Bartlett <abartlet at samba.org>
+(cherry picked from commit 97e4aab1a6e2feda7c6c6fdeaa7c3e1818c55566)
+---
+ python/samba/dbchecker.py                         | 10 ++++++++--
+ python/samba/descriptor.py                        | 15 ++++++++++++++-
+ testprogs/blackbox/dbcheck-links.sh               | 12 ++++++++++++
+ 3 files changed, 34 insertions(+), 3 deletions(-)
+
+diff --git a/python/samba/dbchecker.py b/python/samba/dbchecker.py
+index e124b1a0d67..28d99c01d04 100644
+--- a/python/samba/dbchecker.py
++++ b/python/samba/dbchecker.py
+@@ -2444,7 +2444,7 @@ newSuperior: %s""" % (str(from_dn), str(to_rdn), str(to_base)))
+                     error_count += 1
+                     continue
+ 
+-                if self.reset_well_known_acls:
++                if dn == deleted_objects_dn or self.reset_well_known_acls:
+                     try:
+                         well_known_sd = self.get_wellknown_sd(dn)
+                     except KeyError:
+@@ -2453,7 +2453,13 @@ newSuperior: %s""" % (str(from_dn), str(to_rdn), str(to_base)))
+                     current_sd = ndr_unpack(security.descriptor,
+                                             obj[attrname][0])
+ 
+-                    diff = get_diff_sds(well_known_sd, current_sd, security.dom_sid(self.samdb.get_domain_sid()))
++                    ignoreAdditionalACEs = False
++                    if not self.reset_well_known_acls:
++                        ignoreAdditionalACEs = True
++
++                    diff = get_diff_sds(well_known_sd, current_sd,
++                                        security.dom_sid(self.samdb.get_domain_sid()),
++                                        ignoreAdditionalACEs=ignoreAdditionalACEs)
+                     if diff != "":
+                         self.err_wrong_default_sd(dn, well_known_sd, diff)
+                         error_count += 1
+diff --git a/python/samba/descriptor.py b/python/samba/descriptor.py
+index 08c7518f56a..34877fa4814 100644
+--- a/python/samba/descriptor.py
++++ b/python/samba/descriptor.py
+@@ -417,6 +417,7 @@ def get_wellknown_sds(samdb):
+     # Then subcontainers
+     subcontainers = [
+         (ldb.Dn(samdb, "%s" % str(samdb.domain_dn())), get_domain_descriptor),
++        (ldb.Dn(samdb, "CN=Deleted Objects,%s" % str(samdb.domain_dn())), get_deletedobjects_descriptor),
+         (ldb.Dn(samdb, "CN=LostAndFound,%s" % str(samdb.domain_dn())), get_domain_delete_protected2_descriptor),
+         (ldb.Dn(samdb, "CN=System,%s" % str(samdb.domain_dn())), get_domain_delete_protected1_descriptor),
+         (ldb.Dn(samdb, "CN=Infrastructure,%s" % str(samdb.domain_dn())), get_domain_infrastructure_descriptor),
+@@ -427,6 +428,7 @@ def get_wellknown_sds(samdb):
+         (ldb.Dn(samdb, "CN=MicrosoftDNS,CN=System,%s" % str(samdb.domain_dn())), get_dns_domain_microsoft_dns_descriptor),
+ 
+         (ldb.Dn(samdb, "%s" % str(samdb.get_config_basedn())), get_config_descriptor),
++        (ldb.Dn(samdb, "CN=Deleted Objects,%s" % str(samdb.get_config_basedn())), get_deletedobjects_descriptor),
+         (ldb.Dn(samdb, "CN=NTDS Quotas,%s" % str(samdb.get_config_basedn())), get_config_ntds_quotas_descriptor),
+         (ldb.Dn(samdb, "CN=LostAndFoundConfig,%s" % str(samdb.get_config_basedn())), get_config_delete_protected1wd_descriptor),
+         (ldb.Dn(samdb, "CN=Services,%s" % str(samdb.get_config_basedn())), get_config_delete_protected1_descriptor),
+@@ -451,6 +453,9 @@ def get_wellknown_sds(samdb):
+         if ldb.Dn(samdb, nc.decode('utf8')) == dnsforestdn:
+             c = (ldb.Dn(samdb, "%s" % str(dnsforestdn)), get_dns_partition_descriptor)
+             subcontainers.append(c)
++            c = (ldb.Dn(samdb, "CN=Deleted Objects,%s" % str(dnsforestdn)),
++                 get_deletedobjects_descriptor)
++            subcontainers.append(c)
+             c = (ldb.Dn(samdb, "CN=Infrastructure,%s" % str(dnsforestdn)),
+                  get_domain_delete_protected1_descriptor)
+             subcontainers.append(c)
+@@ -466,6 +471,9 @@ def get_wellknown_sds(samdb):
+         if ldb.Dn(samdb, nc.decode('utf8')) == dnsdomaindn:
+             c = (ldb.Dn(samdb, "%s" % str(dnsdomaindn)), get_dns_partition_descriptor)
+             subcontainers.append(c)
++            c = (ldb.Dn(samdb, "CN=Deleted Objects,%s" % str(dnsdomaindn)),
++                 get_deletedobjects_descriptor)
++            subcontainers.append(c)
+             c = (ldb.Dn(samdb, "CN=Infrastructure,%s" % str(dnsdomaindn)),
+                  get_domain_delete_protected1_descriptor)
+             subcontainers.append(c)
+@@ -558,7 +566,8 @@ def get_clean_sd(sd):
+     return sd_clean
+ 
+ 
+-def get_diff_sds(refsd, cursd, domainsid, checkSacl=True):
++def get_diff_sds(refsd, cursd, domainsid, checkSacl=True,
++                 ignoreAdditionalACEs=False):
+     """Get the difference between 2 sd
+ 
+     This function split the textual representation of ACL into smaller
+@@ -613,6 +622,10 @@ def get_diff_sds(refsd, cursd, domainsid, checkSacl=True):
+                     h_ref.remove(k)
+ 
+             if len(h_cur) + len(h_ref) > 0:
++                if txt == "" and len(h_ref) == 0:
++                    if ignoreAdditionalACEs:
++                        return ""
++
+                 txt = "%s\tPart %s is different between reference" \
+                       " and current here is the detail:\n" % (txt, part)
+ 
+diff --git a/testprogs/blackbox/dbcheck-links.sh b/testprogs/blackbox/dbcheck-links.sh
+index 29fb5b85abc..a91ed00fb0f 100755
+--- a/testprogs/blackbox/dbcheck-links.sh
++++ b/testprogs/blackbox/dbcheck-links.sh
+@@ -59,6 +59,16 @@ dbcheck()
+ 	fi
+ }
+ 
++dbcheck_acl_reset()
++{
++	$PYTHON $BINDIR/samba-tool dbcheck -H tdb://$PREFIX_ABS/${RELEASE}/private/sam.ldb --cross-ncs --fix --yes --attrs=nTSecurityDescriptor
++}
++
++dbcheck_acl_clean()
++{
++	$PYTHON $BINDIR/samba-tool dbcheck -H tdb://$PREFIX_ABS/${RELEASE}/private/sam.ldb --cross-ncs --attrs=nTSecurityDescriptor
++}
++
+ dbcheck_dangling()
+ {
+ 	dbcheck "" "1" "--selftest-check-expired-tombstones"
+@@ -925,6 +935,8 @@ EOF
+ remove_directory $PREFIX_ABS/${RELEASE}
+ 
+ testit $RELEASE undump || failed=$(expr $failed + 1)
++testit_expect_failure "dbcheck_acl_reset" dbcheck_acl_reset || failed=$(expr $failed + 1)
++testit "dbcheck_acl_clean" dbcheck_acl_clean || failed=$(expr $failed + 1)
+ testit "add_two_more_users" add_two_more_users || failed=$(expr $failed + 1)
+ testit "add_four_more_links" add_four_more_links || failed=$(expr $failed + 1)
+ testit "remove_one_link" remove_one_link || failed=$(expr $failed + 1)
+-- 
+2.47.3
+
diff -Nru samba-4.17.12+dfsg/debian/patches/CVE-2025-10230/s4-tests-check-that-wins-hook-sanitizes-names.patch samba-4.17.12+dfsg/debian/patches/CVE-2025-10230/s4-tests-check-that-wins-hook-sanitizes-names.patch
--- samba-4.17.12+dfsg/debian/patches/CVE-2025-10230/s4-tests-check-that-wins-hook-sanitizes-names.patch	1970-01-01 03:00:00.000000000 +0300
+++ samba-4.17.12+dfsg/debian/patches/CVE-2025-10230/s4-tests-check-that-wins-hook-sanitizes-names.patch	2025-11-30 11:35:04.000000000 +0300
@@ -0,0 +1,286 @@
+From: Douglas Bagnall <douglas.bagnall at catalyst.net.nz>
+Date: Tue, 9 Sep 2025 13:36:16 +1200
+Subject: CVE-2025-10230: s4/tests: check that wins hook sanitizes names
+
+An smb.conf can contain a 'wins hook' parameter, which names a script
+to run when a WINS name is changed. The man page says
+
+    The second argument is the NetBIOS name. If the name is not a
+    legal name then the wins hook is not called. Legal names contain
+    only letters, digits, hyphens, underscores and periods.
+
+but it turns out the legality check is not performed if the WINS
+server in question is the source4 nbt one. It is not expected that
+people will run this server, but they can. This is bad because the
+name is passed unescaped into a shell command line, allowing command
+injection.
+
+For this test we don't care whether the WINS server is returning an
+error code, just whether it is running the wins hook. The tests show
+it often runs the hook it shouldn't, though some characters are
+incidentally blocked because the name has to fit in a DN before it
+gets to the hook, and DNs have a few syntactic restrictions (e.g.,
+blocking '<', '>', and ';').
+
+The source3 WINS server that is used by Samba when not run as a DC is
+not affected and not here tested.
+
+BUG: https://bugzilla.samba.org/show_bug.cgi?id=15903
+
+Signed-off-by: Douglas Bagnall <douglas.bagnall at catalyst.net.nz>
+Reviewed-by: Gary Lockyer <gary at catalyst.net.nz>
+---
+ python/samba/tests/usage.py                   |   2 +
+ .../samba4.nbt.wins.wins_bad_names            |   1 +
+ selftest/target/Samba4.pm                     |   1 +
+ source4/torture/nbt/wins.c                    | 136 +++++++++++++++++-
+ testprogs/blackbox/wins_hook_test             |  15 ++
+ 5 files changed, 152 insertions(+), 3 deletions(-)
+ create mode 100644 selftest/knownfail.d/samba4.nbt.wins.wins_bad_names
+ create mode 100755 testprogs/blackbox/wins_hook_test
+
+diff --git a/python/samba/tests/usage.py b/python/samba/tests/usage.py
+index 0286be15ec3..3dead1958a4 100644
+--- a/python/samba/tests/usage.py
++++ b/python/samba/tests/usage.py
+@@ -75,6 +75,7 @@ EXCLUDE_USAGE = {
+     'lib/ldb/tests/python/api.py',
+     'source4/selftest/tests.py',
+     'buildtools/bin/waf',
++    'testprogs/blackbox/wins_hook_test',
+     'selftest/tap2subunit',
+     'script/show_test_time',
+     'source4/scripting/bin/subunitrun',
+@@ -121,6 +122,7 @@ EXCLUDE_HELP = {
+     'selftest/tap2subunit',
+     'wintest/test-s3.py',
+     'wintest/test-s4-howto.py',
++    'testprogs/blackbox/wins_hook_test',
+ }
+ 
+ 
+diff --git a/selftest/knownfail.d/samba4.nbt.wins.wins_bad_names b/selftest/knownfail.d/samba4.nbt.wins.wins_bad_names
+new file mode 100644
+index 00000000000..52388ce5749
+--- /dev/null
++++ b/selftest/knownfail.d/samba4.nbt.wins.wins_bad_names
+@@ -0,0 +1 @@
++samba4.nbt.wins.wins_bad_names
+diff --git a/selftest/target/Samba4.pm b/selftest/target/Samba4.pm
+index 7033146f46a..8d93b50b1ee 100755
+--- a/selftest/target/Samba4.pm
++++ b/selftest/target/Samba4.pm
+@@ -1613,6 +1613,7 @@ sub provision_ad_dc_ntvfs($$$)
+ 	ldap server require strong auth = allow_sasl_over_tls
+ 	raw NTLMv2 auth = yes
+ 	lsa over netlogon = yes
++	wins hook = $ENV{SRCDIR_ABS}/testprogs/blackbox/wins_hook_test
+         rpc server port = 1027
+         auth event notification = true
+ 	dsdb event notification = true
+diff --git a/source4/torture/nbt/wins.c b/source4/torture/nbt/wins.c
+index 8c847b5ac50..7d7321752d6 100644
+--- a/source4/torture/nbt/wins.c
++++ b/source4/torture/nbt/wins.c
+@@ -31,6 +31,10 @@
+ #include "torture/nbt/proto.h"
+ #include "param/param.h"
+ 
++/* rcode used when you don't want to check the rcode */
++#define WINS_TEST_RCODE_WE_DONT_CARE 255
++
++
+ #define CHECK_VALUE(tctx, v, correct) \
+ 	torture_assert_int_equal(tctx, v, correct, "Incorrect value")
+ 
+@@ -137,7 +141,9 @@ static bool nbt_test_wins_name(struct torture_context *tctx, const char *address
+ 					address));
+ 
+ 		CHECK_STRING(tctx, io.out.wins_server, address);
+-		CHECK_VALUE(tctx, io.out.rcode, 0);
++		if (register_rcode != WINS_TEST_RCODE_WE_DONT_CARE) {
++			CHECK_VALUE(tctx, io.out.rcode, 0);
++		}
+ 
+ 		torture_comment(tctx, "register the name correct address\n");
+ 		name_register.in.name		= *name;
+@@ -185,7 +191,9 @@ static bool nbt_test_wins_name(struct torture_context *tctx, const char *address
+ 			talloc_asprintf(tctx, "Bad response from %s for name register\n",
+ 					address));
+ 
+-		CHECK_VALUE(tctx, name_register.out.rcode, 0);
++		if (register_rcode != WINS_TEST_RCODE_WE_DONT_CARE) {
++			CHECK_VALUE(tctx, name_register.out.rcode, 0);
++		}
+ 		CHECK_STRING(tctx, name_register.out.reply_addr, myaddress);
+ 	}
+ 
+@@ -203,7 +211,9 @@ static bool nbt_test_wins_name(struct torture_context *tctx, const char *address
+ 	torture_assert_ntstatus_ok(tctx, status, talloc_asprintf(tctx, "Bad response from %s for name register", address));
+ 	
+ 	CHECK_STRING(tctx, io.out.wins_server, address);
+-	CHECK_VALUE(tctx, io.out.rcode, register_rcode);
++	if (register_rcode != WINS_TEST_RCODE_WE_DONT_CARE) {
++		CHECK_VALUE(tctx, io.out.rcode, register_rcode);
++	}
+ 
+ 	if (register_rcode != NBT_RCODE_OK) {
+ 		return true;
+@@ -532,6 +542,124 @@ static bool nbt_test_wins(struct torture_context *tctx)
+ 	return ret;
+ }
+ 
++/*
++ * Test that the WINS server does not call 'wins hook' when the name
++ * contains dodgy characters.
++ */
++static bool nbt_test_wins_bad_names(struct torture_context *tctx)
++{
++	const char *address = NULL;
++	const char *wins_hook_file = NULL;
++	bool ret = true;
++	int err;
++	bool ok;
++	struct nbt_name name = {};
++	size_t i, j;
++	FILE *fh = NULL;
++
++	struct {
++		const char *name;
++		bool should_succeed;
++	} test_cases[] = {
++		{"NORMAL", true},
++		{"|look|", false},
++		{"look&true", false},
++		{"look\\;false", false},
++		{"&ls>foo", false},  /* already fails due to DN syntax */
++		{"has spaces", false},
++		{"hyphen-dot.0", true},
++	};
++
++	wins_hook_file = talloc_asprintf(tctx, "%s/wins_hook_writes_here",
++					 getenv("SELFTEST_TMPDIR"));
++
++	if (!torture_nbt_get_name(tctx, &name, &address)) {
++		return false;
++	}
++
++	for (i = 0; i < ARRAY_SIZE(test_cases); i++) {
++		err =  unlink(wins_hook_file);
++		if (err != 0 && errno != ENOENT) {
++			/* we expect ENOENT, but nothing else */
++			torture_comment(tctx,
++					"unlink %zu of '%s' failed\n",
++					i, wins_hook_file);
++		}
++
++		name.name = test_cases[i].name;
++		name.type = NBT_NAME_CLIENT;
++		ok = nbt_test_wins_name(tctx, address,
++					&name,
++					NBT_NODE_H,
++					true,
++					WINS_TEST_RCODE_WE_DONT_CARE
++			);
++		if (ok == false) {
++			/*
++			 * This happens when the name interferes with
++			 * the DN syntax when it is put in winsdb.
++			 *
++			 * The wins hook will not be reached.
++			 */
++			torture_comment(tctx, "tests for '%s' failed\n",
++					name.name);
++		}
++
++		/*
++		 * poll for the file being created by the wins hook.
++		 */
++		for (j = 0; j < 10; j++) {
++			usleep(200000);
++			fh = fopen(wins_hook_file, "r");
++			if (fh != NULL) {
++				break;
++			}
++		}
++
++		if (fh == NULL) {
++			if (errno == ENOENT) {
++				if (test_cases[i].should_succeed) {
++					torture_comment(
++						tctx,
++						"wins hook for '%s' failed\n",
++						test_cases[i].name);
++					ret = false;
++				}
++			} else {
++				torture_comment(
++					tctx,
++					"wins hook for '%s' unexpectedly failed with %d\n",
++					test_cases[i].name,
++					errno);
++				ret = false;
++			}
++		} else {
++			char readback[17] = {0};
++			size_t n = fread(readback, 1, 16, fh);
++			torture_comment(tctx,
++					"wins hook wrote '%s' read '%.*s'\n",
++					test_cases[i].name,
++					(int)n, readback);
++
++			if (! test_cases[i].should_succeed) {
++				torture_comment(tctx,
++						"wins hook for '%s' should fail\n",
++						test_cases[i].name);
++				ret = false;
++			}
++			fclose(fh);
++		}
++	}
++	err = unlink(wins_hook_file);
++	if (err != 0 && errno != ENOENT) {
++		torture_comment(tctx, "final unlink of '%s' failed\n",
++				wins_hook_file);
++	}
++	torture_assert(tctx, ret, "wins hook failure\n");
++	return ret;
++}
++
++
+ /*
+   test WINS operations
+ */
+@@ -540,6 +668,8 @@ struct torture_suite *torture_nbt_wins(TALLOC_CTX *mem_ctx)
+ 	struct torture_suite *suite = torture_suite_create(mem_ctx, "wins");
+ 
+ 	torture_suite_add_simple_test(suite, "wins", nbt_test_wins);
++	torture_suite_add_simple_test(suite, "wins_bad_names",
++				      nbt_test_wins_bad_names);
+ 
+ 	return suite;
+ }
+diff --git a/testprogs/blackbox/wins_hook_test b/testprogs/blackbox/wins_hook_test
+new file mode 100755
+index 00000000000..f15379c28ca
+--- /dev/null
++++ b/testprogs/blackbox/wins_hook_test
+@@ -0,0 +1,15 @@
++#!/usr/bin/python3
++
++import os
++import sys
++
++filename = f"{os.environ['SELFTEST_TMPDIR']}/wins_hook_writes_here"
++
++f = open(filename, 'wb')
++
++# Some names may truncate argv (e.g. '&'), for which we leave the file
++# empty.
++if len(sys.argv) > 2:
++    f.write(os.fsencode(sys.argv[2]))
++
++f.close()
+-- 
+2.47.3
+
diff -Nru samba-4.17.12+dfsg/debian/patches/CVE-2025-10230/s4-wins-restrict-names-fed-to-shell.patch samba-4.17.12+dfsg/debian/patches/CVE-2025-10230/s4-wins-restrict-names-fed-to-shell.patch
--- samba-4.17.12+dfsg/debian/patches/CVE-2025-10230/s4-wins-restrict-names-fed-to-shell.patch	1970-01-01 03:00:00.000000000 +0300
+++ samba-4.17.12+dfsg/debian/patches/CVE-2025-10230/s4-wins-restrict-names-fed-to-shell.patch	2025-11-30 11:35:04.000000000 +0300
@@ -0,0 +1,71 @@
+From: Douglas Bagnall <douglas.bagnall at catalyst.net.nz>
+Date: Wed, 3 Sep 2025 14:20:24 +1200
+Subject: CVE-2025-10230: s4:wins: restrict names fed to shell
+
+If the "wins hook" smb.conf parameter is set, the WINS server will
+attempt to execute that value in a shell command line when a client
+asks to modify a name. The WINS system is a trusting one, and clients
+can claim any NETBIOS name they wish.
+
+With the source3 nmbd WINS server (since the 1999 commit now called
+3db52feb1f3b2c07ce0b06ad4a7099fa6efe3fc7) the wins hook will not be
+run for names that contain shell metacharacters. This restriction has
+not been present on the source4 nbt WINS server, which is the WINS
+server that will be used in the event that an Active Directory Domain
+Controller is also running WINS.
+
+This allowed an unauthenticated client to execute arbitrary commands
+on the server.
+
+This commit brings the nmbd check into the nbt WINS server, so that
+the wins hook will only be run for names that contain only letters,
+digits, hyphens, underscores and periods. This matches the behaviour
+described in the smb.conf man page.
+
+The source3 nmbd WINS server has another layer of protection, in that
+it uses the smb_run() exec wrapper that tries to escape arguments. We
+don't do that here.
+
+BUG: https://bugzilla.samba.org/show_bug.cgi?id=15903
+
+Signed-off-by: Douglas Bagnall <douglas.bagnall at catalyst.net.nz>
+Reviewed-by: Gary Lockyer <gary at catalyst.net.nz>
+---
+ selftest/knownfail.d/samba4.nbt.wins.wins_bad_names | 1 -
+ source4/nbt_server/wins/wins_hook.c                 | 9 +++++++++
+ 2 files changed, 9 insertions(+), 1 deletion(-)
+ delete mode 100644 selftest/knownfail.d/samba4.nbt.wins.wins_bad_names
+
+diff --git a/selftest/knownfail.d/samba4.nbt.wins.wins_bad_names b/selftest/knownfail.d/samba4.nbt.wins.wins_bad_names
+deleted file mode 100644
+index 52388ce5749..00000000000
+--- a/selftest/knownfail.d/samba4.nbt.wins.wins_bad_names
++++ /dev/null
+@@ -1 +0,0 @@
+-samba4.nbt.wins.wins_bad_names
+diff --git a/source4/nbt_server/wins/wins_hook.c b/source4/nbt_server/wins/wins_hook.c
+index 1af471b15bc..442141fecdd 100644
+--- a/source4/nbt_server/wins/wins_hook.c
++++ b/source4/nbt_server/wins/wins_hook.c
+@@ -43,9 +43,18 @@ void wins_hook(struct winsdb_handle *h, const struct winsdb_record *rec,
+ 	int child;
+ 	char *cmd = NULL;
+ 	TALLOC_CTX *tmp_mem = NULL;
++	const char *p = NULL;
+ 
+ 	if (!wins_hook_script || !wins_hook_script[0]) return;
+ 
++	for (p = rec->name->name; *p; p++) {
++		if (!(isalnum((int)*p) || strchr_m("._-", *p))) {
++			DBG_ERR("not calling wins hook for invalid name %s\n",
++				rec->name->name);
++			return;
++		}
++	}
++
+ 	tmp_mem = talloc_new(h);
+ 	if (!tmp_mem) goto failed;
+ 
+-- 
+2.47.3
+
diff -Nru samba-4.17.12+dfsg/debian/patches/CVE-2025-9640/Add-torture-test-for-inserting-hole-in-stream.patch samba-4.17.12+dfsg/debian/patches/CVE-2025-9640/Add-torture-test-for-inserting-hole-in-stream.patch
--- samba-4.17.12+dfsg/debian/patches/CVE-2025-9640/Add-torture-test-for-inserting-hole-in-stream.patch	1970-01-01 03:00:00.000000000 +0300
+++ samba-4.17.12+dfsg/debian/patches/CVE-2025-9640/Add-torture-test-for-inserting-hole-in-stream.patch	2025-11-30 11:35:04.000000000 +0300
@@ -0,0 +1,286 @@
+From: Andrew Walker <andrew.walker at truenas.com>
+Date: Thu, 28 Aug 2025 19:39:34 +0000
+Subject: CVE-2025-9640: Add torture test for inserting hole in stream
+
+This commit adds an smb torture test for inserting a hole into
+an alternate data stream and then verifying that hole contains
+null bytes.
+
+BUG: https://bugzilla.samba.org/show_bug.cgi?id=15885
+
+Signed-off-by: Andrew Walker <andrew.walker at truenas.com>
+Reviewed-by: Volker Lendecke <vl at samba.org>
+---
+ source3/selftest/tests.py           |   3 +
+ source4/torture/vfs/streams_xattr.c | 211 ++++++++++++++++++++++++++++
+ source4/torture/vfs/vfs.c           |   1 +
+ source4/torture/wscript_build       |   2 +-
+ 4 files changed, 216 insertions(+), 1 deletion(-)
+ create mode 100644 source4/torture/vfs/streams_xattr.c
+
+diff --git a/source3/selftest/tests.py b/source3/selftest/tests.py
+index e93365e3db5..6d7c41c37d1 100755
+--- a/source3/selftest/tests.py
++++ b/source3/selftest/tests.py
+@@ -919,6 +919,7 @@ nbt = ["nbt.dgram"]
+ vfs = [
+     "vfs.fruit",
+     "vfs.acl_xattr",
++    "vfs.streams_xattr",
+     "vfs.fruit_netatalk",
+     "vfs.fruit_file_id",
+     "vfs.fruit_timemachine",
+@@ -1107,6 +1108,8 @@ for t in tests:
+             plansmbtorture4testsuite(t, "fileserver", '//$SERVER_IP/tmp -U$USERNAME%$PASSWORD')
+     elif t == "vfs.acl_xattr":
+         plansmbtorture4testsuite(t, "nt4_dc", '//$SERVER_IP/tmp -U$USERNAME%$PASSWORD')
++    elif t == "vfs.streams_xattr":
++        plansmbtorture4testsuite(t, "nt4_dc", '//$SERVER_IP/vfs_wo_fruit -U$USERNAME%$PASSWORD')
+     elif t == "smb2.compound_find":
+         plansmbtorture4testsuite(t, "fileserver", '//$SERVER/compound_find -U$USERNAME%$PASSWORD')
+         plansmbtorture4testsuite(t, "fileserver", '//$SERVER_IP/tmp -U$USERNAME%$PASSWORD')
+diff --git a/source4/torture/vfs/streams_xattr.c b/source4/torture/vfs/streams_xattr.c
+new file mode 100644
+index 00000000000..0eb83e092e7
+--- /dev/null
++++ b/source4/torture/vfs/streams_xattr.c
+@@ -0,0 +1,211 @@
++/*
++   Unix SMB/CIFS implementation.
++
++   Copyright (C) Andrew Walker (2025)
++
++   This program is free software; you can redistribute it and/or modify
++   it under the terms of the GNU General Public License as published by
++   the Free Software Foundation; either version 3 of the License, or
++   (at your option) any later version.
++
++   This program is distributed in the hope that it will be useful,
++   but WITHOUT ANY WARRANTY; without even the implied warranty of
++   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++   GNU General Public License for more details.
++
++   You should have received a copy of the GNU General Public License
++   along with this program.  If not, see <http://www.gnu.org/licenses/>.
++*/
++
++#include "includes.h"
++#include "lib/cmdline/cmdline.h"
++#include "libcli/smb2/smb2.h"
++#include "libcli/smb2/smb2_calls.h"
++#include "libcli/smb/smbXcli_base.h"
++#include "torture/torture.h"
++#include "torture/vfs/proto.h"
++#include "libcli/resolve/resolve.h"
++#include "torture/util.h"
++#include "torture/smb2/proto.h"
++#include "lib/param/param.h"
++
++#define BASEDIR "smb2-testads"
++
++
++static bool get_stream_handle(struct torture_context *tctx,
++			      struct smb2_tree *tree,
++			      const char *dname,
++			      const char *fname,
++			      const char *sname,
++			      struct smb2_handle *hdl_in)
++{
++	bool ret = true;
++	NTSTATUS status;
++	struct smb2_handle fhandle = {{0}};
++	struct smb2_handle dhandle = {{0}};
++
++	torture_comment(tctx, "Create dir\n");
++
++	status = torture_smb2_testdir(tree, dname, &dhandle);
++	torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "torture_smb2_testdir\n");
++
++	torture_comment(tctx, "Create file\n");
++
++	status = torture_smb2_testfile(tree, fname, &fhandle);
++	torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "torture_smb2_testfile\n");
++
++	status = torture_smb2_testfile(tree, sname, hdl_in);
++	torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "torture_smb2_testfile\n");
++
++done:
++	if (!smb2_util_handle_empty(fhandle)) {
++		smb2_util_close(tree, fhandle);
++	}
++	if (!smb2_util_handle_empty(dhandle)) {
++		smb2_util_close(tree, dhandle);
++	}
++	return ret;
++}
++
++static bool read_stream(struct torture_context *tctx,
++			TALLOC_CTX *mem_ctx,
++			struct smb2_tree *tree,
++			struct smb2_handle *stream_hdl,
++			off_t read_offset,
++			size_t read_count,
++			char **data_out,
++			size_t *data_out_sz)
++{
++	NTSTATUS status;
++	struct smb2_read r;
++	bool ret = true;
++
++	ZERO_STRUCT(r);
++	r.in.file.handle = *stream_hdl;
++	r.in.length = read_count;
++	r.in.offset = read_offset;
++
++	status = smb2_read(tree, mem_ctx, &r);
++	torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "stream read\n");
++
++	*data_out = (char *)r.out.data.data;
++	*data_out_sz = r.out.data.length;
++
++done:
++	return ret;
++}
++
++
++#define WRITE_PAYLOAD "canary"
++#define ADS_LEN 1024
++#define ADS_OFF_TAIL ADS_LEN - sizeof(WRITE_PAYLOAD)
++
++static bool test_streams_pwrite_hole(struct torture_context *tctx,
++				     struct smb2_tree *tree)
++{
++	NTSTATUS status;
++	bool ok;
++	bool ret = true;
++	const char *dname = BASEDIR "\\testdir";
++	const char *fname = BASEDIR "\\testdir\\testfile";
++	const char *sname = BASEDIR "\\testdir\\testfile:test_stream";
++	const char *canary = "canary";
++	struct smb2_handle shandle = {{0}};
++	TALLOC_CTX *tmp_ctx = NULL;
++	char *data = NULL;
++	size_t data_sz, i;
++
++	ok = smb2_util_setup_dir(tctx, tree, BASEDIR);
++	torture_assert_goto(tctx, ok == true, ret, done, "Unable to setup testdir\n");
++
++	tmp_ctx = talloc_new(tree);
++	torture_assert_goto(tctx, tmp_ctx != NULL, ret, done, "Memory failure\n");
++
++	ok = get_stream_handle(tctx, tree, dname, fname, sname, &shandle);
++	if (!ok) {
++		// torture assert already set
++		goto done;
++	}
++
++	/*
++	 * We're going to write a string at the beginning at the ADS, then write the same
++	 * string at a later offset, introducing a hole in the file
++	 */
++	torture_comment(tctx, "writing at varying offsets to create hole\n");
++	status = smb2_util_write(tree, shandle, WRITE_PAYLOAD, 0, sizeof(WRITE_PAYLOAD));
++	if (!NT_STATUS_IS_OK(status)) {
++		torture_comment(tctx, "Failed to write %zu bytes to "
++		    "stream at offset 0\n", sizeof(canary));
++		return false;
++	}
++
++	status = smb2_util_write(tree, shandle, WRITE_PAYLOAD, ADS_OFF_TAIL, sizeof(WRITE_PAYLOAD));
++	if (!NT_STATUS_IS_OK(status)) {
++		torture_comment(tctx, "Failed to write %zu bytes to "
++		    "stream at offset 1018\n", sizeof(canary));
++		return false;
++	}
++
++	/* Now we'll read the stream contents */
++	torture_comment(tctx, "Read stream data\n");
++	ok = read_stream(tctx, tmp_ctx, tree, &shandle, 0, ADS_LEN, &data, &data_sz);
++	if (!ok) {
++		// torture assert already set
++		goto done;
++	}
++
++	torture_assert_goto(tctx, data_sz == ADS_LEN, ret, done, "Short read on ADS\n");
++
++	/* Make sure our strings actually got written */
++	if (strncmp(data, WRITE_PAYLOAD, sizeof(WRITE_PAYLOAD)) != 0) {
++		torture_result(tctx, TORTURE_FAIL,
++			       "Payload write at beginning of file failed");
++		ret = false;
++		goto done;
++	}
++
++	if (strncmp(data + ADS_OFF_TAIL, WRITE_PAYLOAD, sizeof(WRITE_PAYLOAD)) != 0) {
++		torture_result(tctx, TORTURE_FAIL,
++			       "Payload write at end of file failed");
++		ret = false;
++		goto done;
++	}
++
++	/* Now we'll check that the hole is full of null bytes */
++	for (i = sizeof(WRITE_PAYLOAD); i < ADS_OFF_TAIL; i++) {
++		if (data[i] != '\0') {
++			torture_comment(tctx, "idx: %zu, got 0x%02x when expected 0x00\n",
++					i, (uint8_t)data[i]);
++			torture_result(tctx, TORTURE_FAIL,
++				       "0x%08x: unexpected non-null byte in ADS read\n",
++				       data[i]);
++			ret = false;
++			goto done;
++		}
++	}
++
++done:
++	talloc_free(tmp_ctx);
++
++	if (!smb2_util_handle_empty(shandle)) {
++		smb2_util_close(tree, shandle);
++	}
++
++	smb2_deltree(tree, BASEDIR);
++
++	return ret;
++}
++
++/*
++   basic testing of vfs_streams_xattr
++*/
++struct torture_suite *torture_vfs_streams_xattr(TALLOC_CTX *ctx)
++{
++	struct torture_suite *suite = torture_suite_create(ctx, "streams_xattr");
++
++	torture_suite_add_1smb2_test(suite, "streams-pwrite-hole", test_streams_pwrite_hole);
++
++	suite->description = talloc_strdup(suite, "vfs_streams_xattr tests");
++
++	return suite;
++}
+diff --git a/source4/torture/vfs/vfs.c b/source4/torture/vfs/vfs.c
+index 69da13f6d28..28e0b16ed95 100644
+--- a/source4/torture/vfs/vfs.c
++++ b/source4/torture/vfs/vfs.c
+@@ -115,6 +115,7 @@ NTSTATUS torture_vfs_init(TALLOC_CTX *ctx)
+ 	torture_suite_add_suite(suite, torture_vfs_fruit_timemachine(suite));
+ 	torture_suite_add_suite(suite, torture_vfs_fruit_conversion(suite));
+ 	torture_suite_add_suite(suite, torture_vfs_fruit_unfruit(suite));
++	torture_suite_add_suite(suite, torture_vfs_streams_xattr(suite));
+ 
+ 	torture_register_suite(ctx, suite);
+ 
+diff --git a/source4/torture/wscript_build b/source4/torture/wscript_build
+index 6bfc6aeae65..57e006dcaf8 100644
+--- a/source4/torture/wscript_build
++++ b/source4/torture/wscript_build
+@@ -305,7 +305,7 @@ bld.SAMBA_MODULE('TORTURE_NTP',
+ 	)
+ 
+ bld.SAMBA_MODULE('TORTURE_VFS',
+-	source='vfs/vfs.c vfs/fruit.c vfs/acl_xattr.c',
++	source='vfs/vfs.c vfs/fruit.c vfs/acl_xattr.c vfs/streams_xattr.c',
+ 	subsystem='smbtorture',
+ 	deps='LIBCLI_SMB TORTURE_UTIL smbclient-raw TORTURE_RAW',
+ 	internal_module=True,
+-- 
+2.47.3
+
diff -Nru samba-4.17.12+dfsg/debian/patches/CVE-2025-9640/s3-modules-vfs_streams_xattr-fix-unitialized-write.patch samba-4.17.12+dfsg/debian/patches/CVE-2025-9640/s3-modules-vfs_streams_xattr-fix-unitialized-write.patch
--- samba-4.17.12+dfsg/debian/patches/CVE-2025-9640/s3-modules-vfs_streams_xattr-fix-unitialized-write.patch	1970-01-01 03:00:00.000000000 +0300
+++ samba-4.17.12+dfsg/debian/patches/CVE-2025-9640/s3-modules-vfs_streams_xattr-fix-unitialized-write.patch	2025-11-30 11:35:04.000000000 +0300
@@ -0,0 +1,44 @@
+From: Andrew Walker <andrew.walker at truenas.com>
+Date: Thu, 28 Aug 2025 19:36:19 +0000
+Subject: CVE-2025-9640: s3/modules/vfs_streams_xattr fix unitialized write
+
+This commit fixes a situation in which vfs_streams_xattr could
+write unitialized memory into alternate data streams if the
+user writes to an offset that is beyond the current end of file
+to insert a hole in it.
+
+BUG: https://bugzilla.samba.org/show_bug.cgi?id=15885
+
+Signed-off-by: Andrew Walker <andrew.walker at truenas.com>
+Reviewed-by: Volker Lendecke <vl at samba.org>
+---
+ source3/modules/vfs_streams_xattr.c | 5 ++++-
+ 1 file changed, 4 insertions(+), 1 deletion(-)
+
+diff --git a/source3/modules/vfs_streams_xattr.c b/source3/modules/vfs_streams_xattr.c
+index f3371ca9b7e..5a419dbb705 100644
+--- a/source3/modules/vfs_streams_xattr.c
++++ b/source3/modules/vfs_streams_xattr.c
+@@ -963,15 +963,18 @@ static ssize_t streams_xattr_pwrite(vfs_handle_struct *handle,
+ 
+         if ((offset + n) > ea.value.length-1) {
+ 		uint8_t *tmp;
++		size_t new_sz = offset + n + 1;
+ 
+ 		tmp = talloc_realloc(talloc_tos(), ea.value.data, uint8_t,
+-					   offset + n + 1);
++					   new_sz);
+ 
+ 		if (tmp == NULL) {
+ 			TALLOC_FREE(ea.value.data);
+                         errno = ENOMEM;
+                         return -1;
+                 }
++
++		memset(tmp + ea.value.length, 0, new_sz - ea.value.length);
+ 		ea.value.data = tmp;
+ 		ea.value.length = offset + n + 1;
+ 		ea.value.data[offset+n] = 0;
+-- 
+2.47.3
+
diff -Nru samba-4.17.12+dfsg/debian/patches/series samba-4.17.12+dfsg/debian/patches/series
--- samba-4.17.12+dfsg/debian/patches/series	2025-07-10 16:02:07.000000000 +0300
+++ samba-4.17.12+dfsg/debian/patches/series	2025-11-30 11:35:04.000000000 +0300
@@ -30,3 +30,17 @@
 s3-winbindd-use-better-debug-messages-than-talloc_st.patch
 s3-winbindd-avoid-using-any-netlogon-call-to-get-a-d.patch
 s3-winbindd-Fix-internal-winbind-dsgetdcname-calls-w.patch
+# CVE-2018-14628: Unprivileged read of deleted object tombstones in AD LDAP server
+# https://gitlab.com/samba-team/lts-community/samba/-/merge_requests/3
+CVE-2018-14628/01-python-descriptor-add-get_deletedobjects_descriptor.patch
+CVE-2018-14628/02-python-provision-make-DELETEDOBJECTS_DESCRIPTOR-availab.patch
+CVE-2018-14628/03-s4-setup-set-the-correct-nTSecurityDescriptor-on-the-CN.patch
+CVE-2018-14628/04-s4-dsdb-remove-unused-code-in-dirsync_filter_entry.patch
+CVE-2018-14628/05-dbchecker-use-get_deletedobjects_descriptor-for-missing.patch
+CVE-2018-14628/06-python-descriptor-let-samba-tool-dbch.patch
+# CVE-2025-10230: Command injection via WINS server hook script
+CVE-2025-10230/s4-tests-check-that-wins-hook-sanitizes-names.patch
+CVE-2025-10230/s4-wins-restrict-names-fed-to-shell.patch
+# CVE-2025-9640: Uninitialized memory disclosure via vfs_streams_xattr
+CVE-2025-9640/Add-torture-test-for-inserting-hole-in-stream.patch
+CVE-2025-9640/s3-modules-vfs_streams_xattr-fix-unitialized-write.patch



More information about the Pkg-samba-maint mailing list