Package: release.debian.org
Severity: normal
Tags: bookworm
X-Debbugs-Cc: [email protected]
Control: affects -1 + src:samba
User: [email protected]
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 <[email protected]>  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 <[email protected]>
+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 <[email protected]>
+Reviewed-by: Andrew Bartlett <[email protected]>
+(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 <[email protected]>
+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 <[email protected]>
+Reviewed-by: Andrew Bartlett <[email protected]>
+(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 <[email protected]>
+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 <[email protected]>
+Reviewed-by: Andrew Bartlett <[email protected]>
+(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 <[email protected]>
+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 <[email protected]>
+Reviewed-by: Andrew Bartlett <[email protected]>
+(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 <[email protected]>
+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 <[email protected]>
+Reviewed-by: Andrew Bartlett <[email protected]>
+(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 <[email protected]>
+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 <[email protected]>
+Reviewed-by: Andrew Bartlett <[email protected]>
+(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 <[email protected]>
+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 <[email protected]>
+Reviewed-by: Gary Lockyer <[email protected]>
+---
+ 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 <[email protected]>
+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 <[email protected]>
+Reviewed-by: Gary Lockyer <[email protected]>
+---
+ 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 <[email protected]>
+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 <[email protected]>
+Reviewed-by: Volker Lendecke <[email protected]>
+---
+ 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 <[email protected]>
+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 <[email protected]>
+Reviewed-by: Volker Lendecke <[email protected]>
+---
+ 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

Reply via email to