[Pkg-samba-maint] Bug#1119136: trixie-pu: package samba/2:4.22.6+dfsg-0+deb13u1

Michael Tokarev mjt at tls.msk.ru
Mon Oct 27 12:15:18 GMT 2025


Package: release.debian.org
Severity: normal
Tags: trixie
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 has been 2 upstream point releases since the last version
of samba in debian stable (trixie) - 4.22.5 and 4.22.6.  The first
one contains 2 security fixes, and the second one has a number of
non-security bugfixes all over the places.

The security fixes are CVE-2025-9640 and CVE-2025-10230:
https://www.samba.org/samba/security/CVE-2025-9640.html
https://www.samba.org/samba/security/CVE-2025-10230.html

[ Tests ]
This is an upstream release, with usual upstream testsuite which is
used before the release.  There's no additional debian changes. Also,
this debian package is already used by our site at production with no
ill effects, so basic AD-DC and file/print server functionality is
definitely working.

[ Risks ]
The changes are quite small and focused.  I don't expect any breakage
to happen.

[ 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

[ Changes ]
The debian/changelog is the first part of the debdiff below.
Everything is taken from the upstream WHATSNEW.txt file, which
is also below as part of the debdiff.

All the individual upstream commits can be seen on salsa:
https://salsa.debian.org/samba-team/samba/-/commits/samba-4.22.6
up to samba-4.22.4 tag (from which point it's already in trixie).

The debdiff is below.

Thanks,

/mjt

diff -Nru samba-4.22.4+dfsg/debian/changelog samba-4.22.6+dfsg/debian/changelog
--- samba-4.22.4+dfsg/debian/changelog	2025-08-21 20:37:38.000000000 +0300
+++ samba-4.22.6+dfsg/debian/changelog	2025-10-16 19:19:45.000000000 +0300
@@ -1,3 +1,34 @@
+samba (2:4.22.6+dfsg-0+deb13u1) trixie; urgency=medium
+
+  * new upstream stable/security release:
+   - https://bugzilla.samba.org/show_bug.cgi?id=15843:
+     macOS Finder client DFS broken on 4.22.0
+   - https://bugzilla.samba.org/show_bug.cgi?id=15900:
+     'net ads group' failed to list domain groups
+   - https://bugzilla.samba.org/show_bug.cgi?id=15905:
+     samba-4.21 fails to join AD when multiple DCs are returned
+   - https://bugzilla.samba.org/show_bug.cgi?id=15919:
+     vfs_ceph_new should not use ceph_ll_nonblocking_readv_writev for fsync_send
+   - https://bugzilla.samba.org/show_bug.cgi?id=15921:
+     CTDB_SOCKET can be used even when CTDB_TEST_MODE is not set
+   - https://bugzilla.samba.org/show_bug.cgi?id=15926:
+     Samba 4.22 breaks Time Machine
+   - https://bugzilla.samba.org/show_bug.cgi?id=15927:
+     Spotlight search restriction for shares incomplete and default search
+     searches in too many attributes
+   - https://bugzilla.samba.org/show_bug.cgi?id=15931:
+     rpcd_mdssvc may crash because name mangling is not initialized
+   - https://bugzilla.samba.org/show_bug.cgi?id=15933:
+     Only increment lease epoch if a lease was granted
+
+  * new upstream security release:
+   - CVE-2025-9640: Uninitialized memory disclosure via vfs_streams_xattr
+     https://www.samba.org/samba/security/CVE-2025-9640.html
+   - CVE-2025-10230: Command injection via WINS server hook script
+     https://www.samba.org/samba/security/CVE-2025-10230.html
+
+ -- Michael Tokarev <mjt at tls.msk.ru>  Thu, 16 Oct 2025 19:19:45 +0300
+
 samba (2:4.22.4+dfsg-1~deb13u1) trixie; urgency=medium
 
   * new upstream stable/bugfix release:
diff -Nru samba-4.22.4+dfsg/WHATSNEW.txt samba-4.22.6+dfsg/WHATSNEW.txt
--- samba-4.22.4+dfsg/WHATSNEW.txt	2025-08-21 18:22:56.187811600 +0300
+++ samba-4.22.6+dfsg/WHATSNEW.txt	2025-10-16 17:34:01.621333100 +0300
@@ -1,4 +1,114 @@
                    ==============================
+                   Release Notes for Samba 4.22.6
+                          October 16, 2025
+                   ==============================
+
+
+This is the latest stable release of the Samba 4.22 release series.
+
+
+Changes since 4.22.5
+--------------------
+
+o  Ralph Boehme <slow at samba.org>
+   * BUG 15843: macOS Finder client DFS broken on 4.22.0.
+   * BUG 15926: Samba 4.22 breaks Time Machine
+   * BUG 15927: Spotlight search restriction for shares incomplete and default
+     search searches in too many attributes
+   * BUG 15931: rpcd_mdssvc may crash because name mangling is not initialized
+   * BUG 15933: Only increment lease epoch if a lease was granted
+
+o Pavel Filipenský <pfilipensky at samba.org>
+   * BUG 15905: samba-4.21 fails to join AD when multiple DCs are returned
+
+o MikeLiu <mikeliu at qnap.com>
+   * BUG 15900: 'net ads group' failed to list domain groups.
+
+o Anoop C S <anoopcs at samba.org>
+   * BUG 15919: vfs_ceph_new should not use ceph_ll_nonblocking_readv_writev for
+     fsync_send.
+
+o Shachar Sharon <ssharon at redhat.com>
+   * BUG 15919: vfs_ceph_new should not use ceph_ll_nonblocking_readv_writev for
+     fsync_send.
+
+o Andreas Schneider <asn at samba.org>
+   * BUG 15905: samba-4.21 fails to join AD when multiple DCs are returned
+
+o Martin Schwenke <mschwenke at ddn.com>
+   * BUG 15921: CTDB_SOCKET can be used even when CTDB_TEST_MODE is not set.
+
+#######################################
+Reporting bugs & Development Discussion
+#######################################
+
+Please discuss this release on the samba-technical mailing list or by
+joining the #samba-technical:matrix.org matrix room, or
+#samba-technical IRC channel on irc.libera.chat.
+
+If you do report problems then please try to send high quality
+feedback. If you don't provide vital information to help us track down
+the problem then you will probably be ignored.  All bug reports should
+be filed under the Samba 4.1 and newer product in the project's Bugzilla
+database (https://bugzilla.samba.org/).
+
+
+======================================================================
+== Our Code, Our Bugs, Our Responsibility.
+== The Samba Team
+======================================================================
+
+
+Release notes for older releases follow:
+----------------------------------------
+                   ==============================
+                   Release Notes for Samba 4.22.5
+                          October 15, 2025
+                   ==============================
+
+
+This is a security release in order to address the following defects:
+
+o CVE-2025-9640:  Uninitialized memory disclosure via vfs_streams_xattr.
+                  https://www.samba.org/samba/security/CVE-2025-9640.html
+
+o CVE-2025-10230: Command injection via WINS server hook script.
+                  https://www.samba.org/samba/security/CVE-2025-10230.html
+
+
+Changes since 4.22.4
+--------------------
+
+o  Douglas Bagnall <douglas.bagnall at catalyst.net.nz>
+   * BUG 15903: CVE-2025-10230.
+
+o  Andrew Walker <andrew.walker at truenas.com>
+   * BUG 15885: CVE-2025-9640.
+
+
+#######################################
+Reporting bugs & Development Discussion
+#######################################
+
+Please discuss this release on the samba-technical mailing list or by
+joining the #samba-technical:matrix.org matrix room, or
+#samba-technical IRC channel on irc.libera.chat.
+
+If you do report problems then please try to send high quality
+feedback. If you don't provide vital information to help us track down
+the problem then you will probably be ignored.  All bug reports should
+be filed under the Samba 4.1 and newer product in the project's Bugzilla
+database (https://bugzilla.samba.org/).
+
+
+======================================================================
+== Our Code, Our Bugs, Our Responsibility.
+== The Samba Team
+======================================================================
+
+
+----------------------------------------------------------------------
+                   ==============================
                    Release Notes for Samba 4.22.4
                           August 21, 2025
                    ==============================
@@ -66,8 +176,7 @@
 ======================================================================
 
 
-Release notes for older releases follow:
-----------------------------------------
+----------------------------------------------------------------------
                    ==============================
                    Release Notes for Samba 4.22.3
                            July 07, 2025
diff -Nru samba-4.22.4+dfsg/VERSION samba-4.22.6+dfsg/VERSION
--- samba-4.22.4+dfsg/VERSION	2025-08-21 18:22:56.183811400 +0300
+++ samba-4.22.6+dfsg/VERSION	2025-10-16 17:34:01.621333100 +0300
@@ -27,7 +27,7 @@
 ########################################################
 SAMBA_VERSION_MAJOR=4
 SAMBA_VERSION_MINOR=22
-SAMBA_VERSION_RELEASE=4
+SAMBA_VERSION_RELEASE=6
 
 ########################################################
 # If a official release has a serious bug              #
@@ -101,7 +101,7 @@
 # e.g. SAMBA_VERSION_IS_SVN_SNAPSHOT=yes               #
 #  ->  "3.0.0-SVN-build-199"                           #
 ########################################################
-SAMBA_VERSION_IS_GIT_SNAPSHOT=no
+SAMBA_VERSION_IS_GIT_SNAPSHOT=no 
 
 ########################################################
 # This is for specifying a release nickname            #
diff -Nru samba-4.22.4+dfsg/ctdb/common/path.c samba-4.22.6+dfsg/ctdb/common/path.c
--- samba-4.22.4+dfsg/ctdb/common/path.c	2025-02-06 13:31:53.704143800 +0300
+++ samba-4.22.6+dfsg/ctdb/common/path.c	2025-10-16 17:34:01.625333300 +0300
@@ -45,16 +45,30 @@
 	.vardir = CTDB_VARDIR,
 };
 
-static void path_set_basedir(void)
+static void path_set_test_mode(void)
 {
-	const char *t;
+	const char *t = NULL;
 
+	/*
+	 * Do not use CTDB_TEST_MODE outside a test environment to
+	 * attempt to (for example) improve installation flexibility.
+	 * This is unsupported, may cause unwanted security issues and
+	 * may break in future releases.
+	 */
 	t = getenv("CTDB_TEST_MODE");
 	if (t == NULL) {
-		goto done;
+		return;
 	}
 
 	ctdb_paths.test_mode = true;
+}
+
+static void path_set_basedir(void)
+{
+	path_set_test_mode();
+	if (!ctdb_paths.test_mode) {
+		goto done;
+	}
 
 	ctdb_paths.basedir = getenv("CTDB_BASE");
 	if (ctdb_paths.basedir == NULL) {
@@ -188,11 +202,14 @@
 
 char *path_socket(TALLOC_CTX *mem_ctx, const char *daemon)
 {
-	if (strcmp(daemon, "ctdbd") == 0) {
-		const char *t = getenv("CTDB_SOCKET");
-
-		if (t != NULL) {
-			return talloc_strdup(mem_ctx, t);
+	path_set_test_mode();
+	if (ctdb_paths.test_mode) {
+		if (strcmp(daemon, "ctdbd") == 0) {
+			const char *t = getenv("CTDB_SOCKET");
+
+			if (t != NULL) {
+				return talloc_strdup(mem_ctx, t);
+			}
 		}
 	}
 
diff -Nru samba-4.22.4+dfsg/ctdb/server/ctdbd.c samba-4.22.6+dfsg/ctdb/server/ctdbd.c
--- samba-4.22.4+dfsg/ctdb/server/ctdbd.c	2025-02-06 13:31:53.736144000 +0300
+++ samba-4.22.6+dfsg/ctdb/server/ctdbd.c	2025-10-16 17:34:01.629333300 +0300
@@ -241,6 +241,13 @@
 	 * Logging setup/options
 	 */
 
+
+	/*
+	 * Do not use CTDB_TEST_MODE outside a test environment to
+	 * attempt to (for example) improve installation flexibility.
+	 * This is unsupported, may cause unwanted security issues and
+	 * may break in future releases.
+	 */
 	test_mode = getenv("CTDB_TEST_MODE");
 
 	/* Log to stderr (ignoring configuration) when running as interactive */
diff -Nru samba-4.22.4+dfsg/ctdb/tests/README samba-4.22.6+dfsg/ctdb/tests/README
--- samba-4.22.4+dfsg/ctdb/tests/README	2025-02-06 13:31:53.744144000 +0300
+++ samba-4.22.6+dfsg/ctdb/tests/README	2025-10-16 17:34:01.633333200 +0300
@@ -98,7 +98,7 @@
 	   PID file relative to CTDB_BASE.
 
 	   When testing with multiple local daemons on a single
-	   machine this does 3 extra things:
+	   machine this does some extra things:
 
 	   * Disables checks related to public IP addresses
 
@@ -107,6 +107,14 @@
 
 	   * Disables real-time scheduling
 
+	   * Allows the CTDB_SOCKET environment variable to be used to
+	     specify ctdbd's Unix domain socket location.
+
+	   Do not use this variable outside a test environment to
+	   attempt to (for example) improve installation flexibility.
+	   This is unsupported, may cause unwanted security issues and
+	   may break in future releases.
+
        CTDB_DEBUG_HUNG_SCRIPT_LOGFILE=FILENAME
 	   FILENAME specifies where log messages should go when
 	   debugging hung eventscripts. This is a testing option. See
diff -Nru samba-4.22.4+dfsg/ctdb/utils/pmda/pmda_ctdb.c samba-4.22.6+dfsg/ctdb/utils/pmda/pmda_ctdb.c
--- samba-4.22.4+dfsg/ctdb/utils/pmda/pmda_ctdb.c	2025-03-06 17:02:27.541217300 +0300
+++ samba-4.22.6+dfsg/ctdb/utils/pmda/pmda_ctdb.c	2025-10-16 17:34:01.633333200 +0300
@@ -28,6 +28,8 @@
 #include "lib/util/time.h"
 #include "lib/util/blocking.h"
 
+#include "common/path.h"
+
 #include "client/client.h"
 #include "client/client_sync.h"
 
@@ -49,9 +51,7 @@
  * CTDB PMDA
  *
  * This PMDA connects to the locally running ctdbd daemon and pulls
- * statistics for export via PCP. The ctdbd Unix domain socket path can be
- * specified with the CTDB_SOCKET environment variable, otherwise the default
- * path is used.
+ * statistics for export via PCP.
  */
 
 /*
@@ -191,7 +191,7 @@
 static int
 pmda_ctdb_daemon_connect(void)
 {
-	const char *socket_name;
+	char *socket_name = NULL;
 	int ret;
 
 	ev = tevent_context_init(NULL);
@@ -200,9 +200,9 @@
 		return -1;
 	}
 
-	socket_name = getenv("CTDB_SOCKET");
+	socket_name = path_socket(ev, "ctdbd");
 	if (socket_name == NULL) {
-		socket_name = CTDB_SOCKET;
+		goto err_ev;
 	}
 
 	ret = ctdb_client_init(ev, ev, socket_name, &client);
@@ -215,6 +215,7 @@
 	ctdb_client_set_disconnect_callback(client, pmda_ctdb_disconnected,
 					    NULL);
 
+	talloc_free(socket_name);
 	return 0;
 
 err_ev:
diff -Nru samba-4.22.4+dfsg/docs-xml/manpages/vfs_fruit.8.xml samba-4.22.6+dfsg/docs-xml/manpages/vfs_fruit.8.xml
--- samba-4.22.4+dfsg/docs-xml/manpages/vfs_fruit.8.xml	2025-02-06 13:31:53.892145000 +0300
+++ samba-4.22.6+dfsg/docs-xml/manpages/vfs_fruit.8.xml	2025-10-16 17:34:01.633333200 +0300
@@ -426,6 +426,39 @@
 	    </listitem>
 	  </varlistentry>
 
+	  <varlistentry>
+	    <term>fruit:posix_opens = yes | no</term>
+	    <listitem>
+
+	      <para>When <parameter>fruit:posix_opens</parameter> is set to
+	      <parameter>yes</parameter>, vfs_fruit will internally translate
+	      all filesystem semantics to use POSIX behaviour instead of Windows
+	      behaviour. As Macs are closer to POSIX than Windows with regard
+	      to filesystem semantics, this improves access semantics for
+	      a lot of corner cases.</para>
+	      <para>The default is <emphasis>yes</emphasis>.</para>
+
+            </listitem>
+	  </varlistentry>
+
+	  <varlistentry>
+	    <term>fruit:ignore_zero_aces = yes | no</term>
+	    <listitem>
+
+	      <para>When <parameter>fruit:ignore_zero_aces</parameter> is
+	      enabled, attempts to modify filesystem permissions fail if the ACL
+	      sent over the wire contains no ACEs. This is completely valid
+	      client behaviour, but it means subsequently no further access is
+	      possible to the file, unless permissions get fixed by an
+	      administrator.</para>
+	      <para>This problematic behaviour has been reported for latest
+	      macOS versions and this new option allows to work around
+	      it.</para>
+	      <para>The default is <emphasis>yes</emphasis>.</para>
+
+            </listitem>
+	  </varlistentry>
+
 	</variablelist>
 </refsect1>
 
diff -Nru samba-4.22.4+dfsg/docs-xml/smbdotconf/misc/elasticsearchdefaultfields.xml samba-4.22.6+dfsg/docs-xml/smbdotconf/misc/elasticsearchdefaultfields.xml
--- samba-4.22.4+dfsg/docs-xml/smbdotconf/misc/elasticsearchdefaultfields.xml	1970-01-01 03:00:00.000000000 +0300
+++ samba-4.22.6+dfsg/docs-xml/smbdotconf/misc/elasticsearchdefaultfields.xml	2025-10-16 17:34:01.633333200 +0300
@@ -0,0 +1,19 @@
+<samba:parameter name="elasticsearch:default_fields"
+                 context="G"
+                 type="string"
+                 xmlns:samba="http://www.samba.org/samba/DTD/samba-doc">
+  <description>
+    <para>
+      Default attributes in Elasticsearch to query when receiving a Spotlight
+      query that searches in the special attribute "*". This is the default used
+      by macOS clients when searching from the Finder.
+    </para>
+    <para>
+      This option expects a list of Elasticsearch attributes separated by
+      comma where each attributes must be enclosed in double quotes.
+    </para>
+  </description>
+
+  <value type="default">"file.filename", "content"</value>
+  <value type="example">"foo", "bar"</value>
+</samba:parameter>
diff -Nru samba-4.22.4+dfsg/python/samba/tests/blackbox/mdsearch.py samba-4.22.6+dfsg/python/samba/tests/blackbox/mdsearch.py
--- samba-4.22.4+dfsg/python/samba/tests/blackbox/mdsearch.py	2025-02-06 13:31:54.320147500 +0300
+++ samba-4.22.6+dfsg/python/samba/tests/blackbox/mdsearch.py	2025-10-16 17:34:01.633333200 +0300
@@ -102,8 +102,22 @@
         json_in = r'''{
           "from": 0, "size": 50, "_source": ["path.real"],
           "query": {
-            "query_string": {
-              "query": "(samba*) AND path.real.fulltext:\"%BASEPATH%\""
+            "bool": {
+              "filter": [
+                {
+                  "prefix": {
+                    "path.real": "%BASEPATH%/"
+                  }
+                }
+              ],
+              "must": [
+                {
+                  "query_string": {
+                    "query": "samba*",
+                    "fields": ["file.filename", "content"]
+                  }
+                }
+              ]
             }
           }
         }'''
diff -Nru samba-4.22.4+dfsg/python/samba/tests/dcerpc/dfs.py samba-4.22.6+dfsg/python/samba/tests/dcerpc/dfs.py
--- samba-4.22.4+dfsg/python/samba/tests/dcerpc/dfs.py	1970-01-01 03:00:00.000000000 +0300
+++ samba-4.22.6+dfsg/python/samba/tests/dcerpc/dfs.py	2025-10-16 17:34:01.633333200 +0300
@@ -0,0 +1,48 @@
+#
+# Unix SMB/CIFS implementation.
+# Copyright Ralph Boehme <slow at samba.org> 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/>.
+#
+
+"""Tests for samba.dcerpc.dfs"""
+
+import os
+import logging
+import samba
+from samba.dcerpc import dfs
+from samba.tests import RpcInterfaceTestCase
+from samba.logger import get_samba_logger
+from samba.credentials import Credentials
+from samba.samba3 import libsmb_samba_internal as libsmb
+import samba.tests.libsmb
+from samba.samba3 import param as s3param
+
+logger = get_samba_logger(name=__name__)
+
+class DfsTests(samba.tests.libsmb.LibsmbTests):
+    def setUp(self):
+        super().setUp()
+        self.dfs = dfs.netdfs('ncacn_np:%s[/pipe/netdfs]' % self.server, self.lp, self.creds)
+        self.c = libsmb.Conn(self.server_ip, "msdfs-share", self.lp, self.creds)
+
+    def tearDown(self):
+        super().tearDown()
+
+    def test_dfs_reparse_tag(self):
+        self.dfs.Add('\\\\%s\\msdfs-share\\dfslink' % self.server, self.server, 'tmp', 'comment', 0)
+        l = self.c.list('', info_level=libsmb.SMB2_FIND_ID_BOTH_DIRECTORY_INFO)
+        files = {i['name']: i for i in l}
+        self.assertEqual(files['dfslink']['reparse_tag'], libsmb.IO_REPARSE_TAG_DFS)
+        self.dfs.Remove('\\\\%s\\msdfs-share\\dfslink' % self.server, self.server, 'tmp')
diff -Nru samba-4.22.4+dfsg/python/samba/tests/dcerpc/mdssvc.py samba-4.22.6+dfsg/python/samba/tests/dcerpc/mdssvc.py
--- samba-4.22.4+dfsg/python/samba/tests/dcerpc/mdssvc.py	2025-02-06 13:31:54.324147500 +0300
+++ samba-4.22.6+dfsg/python/samba/tests/dcerpc/mdssvc.py	2025-10-16 17:34:01.633333200 +0300
@@ -133,8 +133,22 @@
         exp_json_query = r'''{
           "from": 0, "size": 50, "_source": ["path.real"],
           "query": {
-            "query_string": {
-              "query": "(samba*) AND path.real.fulltext:\"%BASEPATH%\""
+            "bool": {
+              "filter": [
+                {
+                  "prefix": {
+                    "path.real": "%BASEPATH%/"
+                  }
+                }
+              ],
+              "must": [
+                {
+                  "query_string": {
+                    "query": "samba*",
+                    "fields": ["file.filename", "content"]
+                  }
+                }
+              ]
             }
           }
         }'''
@@ -165,8 +179,22 @@
         exp_json_query = r'''{
           "from": 0, "size": 50, "_source": ["path.real"],
           "query": {
-            "query_string": {
-              "query": "(file.filename:x\\+x OR file.filename:x\\*x OR file.filename:x=x OR file.filename:x'x OR file.filename:x\\?x OR file.filename:x\\ x OR file.filename:x\\(x OR file.filename:x\\\"x OR file.filename:x\\\\x) AND path.real.fulltext:\"%BASEPATH%\""
+            "bool": {
+              "filter": [
+                {
+                  "prefix": {
+                    "path.real": "%BASEPATH%/"
+                  }
+                }
+              ],
+              "must": [
+                {
+                  "query_string": {
+                    "query": "file.filename:x\\+x OR file.filename:x\\*x OR file.filename:x=x OR file.filename:x'x OR file.filename:x\\?x OR file.filename:x\\ x OR file.filename:x\\(x OR file.filename:x\\\"x OR file.filename:x\\\\x",
+                    "fields": ["file.filename", "content"]
+                  }
+                }
+              ]
             }
           }
         }'''
@@ -207,8 +235,22 @@
         exp_json_query = r'''{
           "from": 0, "size": 50, "_source": ["path.real"],
           "query": {
-            "query_string": {
-              "query": "(*samba*) AND path.real.fulltext:\"%BASEPATH%\""
+            "bool": {
+              "filter": [
+                {
+                  "prefix": {
+                    "path.real": "%BASEPATH%/"
+                  }
+                }
+              ],
+              "must": [
+                {
+                  "query_string": {
+                    "query": "*samba*",
+                    "fields": ["file.filename", "content"]
+                  }
+                }
+              ]
             }
           }
         }'''
diff -Nru samba-4.22.4+dfsg/python/samba/tests/libsmb.py samba-4.22.6+dfsg/python/samba/tests/libsmb.py
--- samba-4.22.4+dfsg/python/samba/tests/libsmb.py	2025-02-06 13:31:54.352147600 +0300
+++ samba-4.22.6+dfsg/python/samba/tests/libsmb.py	2025-10-16 17:34:01.633333200 +0300
@@ -43,6 +43,7 @@
         server_conf_dir = os.path.dirname(server_conf)
         self.global_inject = os.path.join(server_conf_dir, "global_inject.conf")
 
+        self.server = samba.tests.env_get_var_value("SERVER")
         self.server_ip = samba.tests.env_get_var_value("SERVER_IP")
 
     def clean_file(self, conn, filename):
diff -Nru samba-4.22.4+dfsg/python/samba/tests/usage.py samba-4.22.6+dfsg/python/samba/tests/usage.py
--- samba-4.22.4+dfsg/python/samba/tests/usage.py	2025-02-06 13:31:54.368147600 +0300
+++ samba-4.22.6+dfsg/python/samba/tests/usage.py	2025-10-15 15:19:02.310114900 +0300
@@ -73,6 +73,7 @@
     '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',
@@ -89,6 +90,7 @@
     'selftest/tap2subunit',
     'wintest/test-s3.py',
     'wintest/test-s4-howto.py',
+    'testprogs/blackbox/wins_hook_test',
 }
 
 
diff -Nru samba-4.22.4+dfsg/selftest/target/Samba.pm samba-4.22.6+dfsg/selftest/target/Samba.pm
--- samba-4.22.4+dfsg/selftest/target/Samba.pm	2025-02-06 13:31:54.400148000 +0300
+++ samba-4.22.6+dfsg/selftest/target/Samba.pm	2025-10-16 17:34:01.641333300 +0300
@@ -1017,6 +1017,7 @@
 	"RESOLV_WRAPPER_HOSTS",
 
 	# ctdb stuff
+	"CTDB_TEST_MODE",
 	"CTDB_PREFIX",
 	"NUM_NODES",
 	"CTDB_BASE",
diff -Nru samba-4.22.4+dfsg/selftest/target/Samba3.pm samba-4.22.6+dfsg/selftest/target/Samba3.pm
--- samba-4.22.4+dfsg/selftest/target/Samba3.pm	2025-02-20 15:58:50.529505000 +0300
+++ samba-4.22.6+dfsg/selftest/target/Samba3.pm	2025-10-16 17:34:01.645333300 +0300
@@ -4328,6 +4328,7 @@
 		$ret{"CTDB_IFACE_IP_NODE${i}"} = $ip;
 	}
 
+	$ret{CTDB_TEST_MODE} = "yes";
 	$ret{CTDB_BASE} = $ret{CTDB_BASE_NODE0};
 	$ret{CTDB_SOCKET} = $ret{CTDB_SOCKET_NODE0};
 	$ret{CTDB_SERVER_NAME} = $ret{CTDB_SERVER_NAME_NODE0};
diff -Nru samba-4.22.4+dfsg/selftest/target/Samba4.pm samba-4.22.6+dfsg/selftest/target/Samba4.pm
--- samba-4.22.4+dfsg/selftest/target/Samba4.pm	2025-02-06 13:31:54.400148000 +0300
+++ samba-4.22.6+dfsg/selftest/target/Samba4.pm	2025-10-16 17:34:01.649333500 +0300
@@ -878,7 +878,7 @@
 
 	my $hostname = lc($ctx->{hostname});
 	open(HOSTS, ">>$ctx->{nsswrap_hosts}");
-	if ($hostname eq "localdc") {
+	if ($hostname eq "localdc" || $hostname eq "localvampiredc") {
 		print HOSTS "$ctx->{ipv4} ${hostname}.$ctx->{dnsname} $ctx->{dnsname} ${hostname}\n";
 		print HOSTS "$ctx->{ipv6} ${hostname}.$ctx->{dnsname} $ctx->{dnsname} ${hostname}\n";
 	} else {
@@ -1637,6 +1637,7 @@
 	ldap server require strong auth = allow_sasl_without_tls_channel_bindings
 	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 -Nru samba-4.22.4+dfsg/source3/include/secrets.h samba-4.22.6+dfsg/source3/include/secrets.h
--- samba-4.22.4+dfsg/source3/include/secrets.h	2025-02-06 13:31:54.412148000 +0300
+++ samba-4.22.6+dfsg/source3/include/secrets.h	2025-10-16 17:34:01.653333400 +0300
@@ -125,12 +125,15 @@
 NTSTATUS secrets_fetch_or_upgrade_domain_info(const char *domain,
 					TALLOC_CTX *mem_ctx,
 					struct secrets_domain_info1 **pinfo);
-NTSTATUS secrets_prepare_password_change(const char *domain, const char *dcname,
-					 const char *cleartext_unix,
-					 TALLOC_CTX *mem_ctx,
-					 struct secrets_domain_info1 **pinfo,
-					 struct secrets_domain_info1_change **pprev,
-					 NTSTATUS (*sync_pw2keytabs_fn)(void));
+NTSTATUS secrets_prepare_password_change(
+	const char *domain,
+	const char *dcname,
+	const char *cleartext_unix,
+	TALLOC_CTX *mem_ctx,
+	struct secrets_domain_info1 **pinfo,
+	struct secrets_domain_info1_change **pprev,
+	NTSTATUS (*sync_pw2keytabs_fn)(const char *),
+	const char *opt_host);
 NTSTATUS secrets_failed_password_change(const char *change_server,
 					NTSTATUS local_status,
 					NTSTATUS remote_status,
@@ -139,10 +142,12 @@
 				       NTSTATUS local_status,
 				       NTSTATUS remote_status,
 				       const struct secrets_domain_info1 *info);
-NTSTATUS secrets_finish_password_change(const char *change_server,
-					NTTIME change_time,
-					const struct secrets_domain_info1 *info,
-					NTSTATUS (*sync_pw2keytabs_fn)(void));
+NTSTATUS secrets_finish_password_change(
+	const char *change_server,
+	NTTIME change_time,
+	const struct secrets_domain_info1 *info,
+	NTSTATUS (*sync_pw2keytabs_fn)(const char *),
+	const char *prefer_dc);
 bool secrets_delete_machine_password_ex(const char *domain, const char *realm);
 bool secrets_delete_domain_sid(const char *domain);
 char *secrets_fetch_prev_machine_password(const char *domain);
diff -Nru samba-4.22.4+dfsg/source3/include/vfs.h samba-4.22.6+dfsg/source3/include/vfs.h
--- samba-4.22.4+dfsg/source3/include/vfs.h	2025-02-06 13:31:54.416148000 +0300
+++ samba-4.22.6+dfsg/source3/include/vfs.h	2025-10-16 17:34:01.657333400 +0300
@@ -462,6 +462,15 @@
 		bool lock_failure_seen : 1;
 		bool encryption_required : 1;
 		bool fstat_before_close : 1;
+		/*
+		 * For POSIX clients struct files_struct.fsp_flags.posix_open
+		 * and struct smb_filename.flags SMB_FILENAME_POSIX_PATH will
+		 * always be set to the same value.
+		 *
+		 * For macOS clients vfs_fruit with fruit:posix_open=yes, we
+		 * deliberately set both flags to fsp_flags.posix_open=true
+		 * while SMB_FILENAME_POSIX_PATH will not be set.
+		 */
 		bool posix_open : 1;
 		bool posix_append : 1;
 	} fsp_flags;
@@ -886,6 +895,15 @@
 	struct fsp_smb_fname_link *fsp_link;
 };
 
+/*
+ * For POSIX clients struct files_struct.fsp_flags.posix_open
+ * and struct smb_filename.flags SMB_FILENAME_POSIX_PATH will
+ * always be set to the same value.
+ *
+ * For macOS clients vfs_fruit with fruit:posix_open=yes, we
+ * deliberately set both flags to fsp_flags.posix_open=true
+ * while SMB_FILENAME_POSIX_PATH will not be set.
+ */
 #define SMB_FILENAME_POSIX_PATH		0x01
 
 enum vfs_translate_direction {
diff -Nru samba-4.22.4+dfsg/source3/libads/ads_proto.h samba-4.22.6+dfsg/source3/libads/ads_proto.h
--- samba-4.22.4+dfsg/source3/libads/ads_proto.h	2025-02-06 13:31:54.444148000 +0300
+++ samba-4.22.6+dfsg/source3/libads/ads_proto.h	2025-10-16 17:34:01.657333400 +0300
@@ -230,6 +230,6 @@
 /* parse a windows style SPN, returns NULL if parsing fails */
 struct spn_struct *parse_spn(TALLOC_CTX *ctx, const char *srvprinc);
 
-NTSTATUS sync_pw2keytabs(void);
+NTSTATUS sync_pw2keytabs(const char *prefer_dc);
 
 #endif /* _LIBADS_ADS_PROTO_H_ */
diff -Nru samba-4.22.4+dfsg/source3/libads/kerberos_keytab.c samba-4.22.6+dfsg/source3/libads/kerberos_keytab.c
--- samba-4.22.4+dfsg/source3/libads/kerberos_keytab.c	2025-04-17 20:12:25.658450400 +0300
+++ samba-4.22.6+dfsg/source3/libads/kerberos_keytab.c	2025-10-16 17:34:01.657333400 +0300
@@ -84,6 +84,7 @@
 	char *ad_upn;
 	char *ad_sam_account;
 	char **ad_spn_array;
+	const char *prefer_dc;
 	size_t ad_num_spns;
 	/* This is from secrets.db */
 	struct secrets_domain_info1 *info;
@@ -869,8 +870,11 @@
 	int count;
 	bool ok;
 	TALLOC_CTX *tmp_ctx = talloc_stackframe();
-	ADS_STRUCT *ads = ads_init(
-		tmp_ctx, lp_realm(), lp_workgroup(), NULL, ADS_SASL_SIGN);
+	ADS_STRUCT *ads = ads_init(tmp_ctx,
+				   lp_realm(),
+				   lp_workgroup(),
+				   state->prefer_dc,
+				   ADS_SASL_SIGN);
 
 	if (ads == NULL) {
 		DBG_ERR("ads_init() failed\n");
@@ -1029,7 +1033,20 @@
 	return true;
 }
 
-NTSTATUS sync_pw2keytabs(void)
+/**
+ * @internal
+ *
+ * @brief Sync machine password from secrets to keytab
+ *
+ * @param prefer_dc  The DC we should talk to. This is especially important
+ *                   during domain join. Pass NULL if we should pick a random
+ *                   one.
+ *
+ * @return An NTSTATUS error code.
+ *
+ * @see NT_STATUS_IS_OK()
+ */
+NTSTATUS sync_pw2keytabs(const char *prefer_dc)
 {
 	TALLOC_CTX *frame = talloc_stackframe();
 	const struct loadparm_substitution *lp_sub =
@@ -1055,6 +1072,7 @@
 		TALLOC_FREE(frame);
 		return NT_STATUS_NO_MEMORY;
 	}
+	state->prefer_dc = prefer_dc;
 
 	lp_ptr = lp_sync_machine_password_to_keytab();
 	if (lp_ptr == NULL) {
diff -Nru samba-4.22.4+dfsg/source3/libads/trusts_util.c samba-4.22.6+dfsg/source3/libads/trusts_util.c
--- samba-4.22.4+dfsg/source3/libads/trusts_util.c	2025-02-06 13:31:54.448148300 +0300
+++ samba-4.22.6+dfsg/source3/libads/trusts_util.c	2025-10-16 17:34:01.661333600 +0300
@@ -360,10 +360,11 @@
 							 &info,
 							 &prev,
 #ifdef HAVE_ADS
-							 sync_pw2keytabs);
+							 sync_pw2keytabs,
 #else
-							 NULL);
+							 NULL,
 #endif
+							 NULL /* opt_host */);
 		if (!NT_STATUS_IS_OK(status)) {
 			DEBUG(0, ("secrets_prepare_password_change() failed for domain %s!\n",
 				  domain));
@@ -610,10 +611,11 @@
 			prev->password->change_time,
 			info,
 #ifdef HAVE_ADS
-			sync_pw2keytabs);
+			sync_pw2keytabs,
 #else
-			NULL);
+			NULL,
 #endif
+			prev->password->change_server);
 		if (!NT_STATUS_IS_OK(status)) {
 			DEBUG(0, ("secrets_prepare_password_change() failed for domain %s!\n",
 				  domain));
@@ -759,10 +761,11 @@
 			info->next_change->change_time,
 			info,
 #ifdef HAVE_ADS
-			sync_pw2keytabs);
+			sync_pw2keytabs,
 #else
-			NULL);
+			NULL,
 #endif
+			info->next_change->change_server);
 		if (!NT_STATUS_IS_OK(status)) {
 			DEBUG(0, ("secrets_finish_password_change() failed for domain %s!\n",
 				  domain));
diff -Nru samba-4.22.4+dfsg/source3/libads/util.c samba-4.22.6+dfsg/source3/libads/util.c
--- samba-4.22.4+dfsg/source3/libads/util.c	2025-02-06 13:31:54.448148300 +0300
+++ samba-4.22.6+dfsg/source3/libads/util.c	2025-10-16 17:34:01.661333600 +0300
@@ -59,10 +59,11 @@
 						 &info,
 						 &prev,
 #ifdef HAVE_ADS
-						 sync_pw2keytabs);
+						 sync_pw2keytabs,
 #else
-						 NULL);
+						 NULL,
 #endif
+						 ads->auth.kdc_server);
 	if (!NT_STATUS_IS_OK(status)) {
 		return ADS_ERROR_NT(status);
 	}
@@ -138,10 +139,11 @@
 						now,
 						info,
 #ifdef HAVE_ADS
-						sync_pw2keytabs);
+						sync_pw2keytabs,
 #else
-						NULL);
+						NULL,
 #endif
+						ads->auth.kdc_server);
 	if (!NT_STATUS_IS_OK(status)) {
 		DEBUG(1,("Failed to save machine password\n"));
 		return ADS_ERROR_NT(status);
diff -Nru samba-4.22.4+dfsg/source3/libnet/libnet_join.c samba-4.22.6+dfsg/source3/libnet/libnet_join.c
--- samba-4.22.4+dfsg/source3/libnet/libnet_join.c	2025-02-06 13:31:54.452148200 +0300
+++ samba-4.22.6+dfsg/source3/libnet/libnet_join.c	2025-10-16 17:34:01.661333600 +0300
@@ -867,7 +867,7 @@
 static bool libnet_join_create_keytab(TALLOC_CTX *mem_ctx,
 				      struct libnet_JoinCtx *r)
 {
-	NTSTATUS ntstatus = sync_pw2keytabs();
+	NTSTATUS ntstatus = sync_pw2keytabs(r->in.dc_name);
 
 	return NT_STATUS_IS_OK(ntstatus);
 }
diff -Nru samba-4.22.4+dfsg/source3/libsmb/pylibsmb.c samba-4.22.6+dfsg/source3/libsmb/pylibsmb.c
--- samba-4.22.4+dfsg/source3/libsmb/pylibsmb.c	2025-02-06 13:31:54.464148300 +0300
+++ samba-4.22.6+dfsg/source3/libsmb/pylibsmb.c	2025-10-16 17:34:01.665333500 +0300
@@ -3448,6 +3448,7 @@
 	ADD_STRING(SMB2_CREATE_TAG_APP_INSTANCE_ID);
 	ADD_STRING(SVHDX_OPEN_DEVICE_CONTEXT);
 	ADD_STRING(SMB2_CREATE_TAG_POSIX);
+	ADD_FLAGS(SMB2_FIND_ID_BOTH_DIRECTORY_INFO);
 	ADD_FLAGS(SMB2_FIND_POSIX_INFORMATION);
 	ADD_FLAGS(FILE_SUPERSEDE);
 	ADD_FLAGS(FILE_OPEN);
diff -Nru samba-4.22.4+dfsg/source3/modules/vfs_ceph_new.c samba-4.22.6+dfsg/source3/modules/vfs_ceph_new.c
--- samba-4.22.4+dfsg/source3/modules/vfs_ceph_new.c	2025-04-17 20:12:25.658450400 +0300
+++ samba-4.22.6+dfsg/source3/modules/vfs_ceph_new.c	2025-10-16 17:34:01.669333500 +0300
@@ -2972,18 +2972,8 @@
 				     0);
 	SMBPROFILE_BYTES_ASYNC_SET_IDLE(state->profile_bytes);
 
-#if HAVE_CEPH_ASYNCIO
-	state->req = req;
-	state->data = NULL;
-	state->len = 0;
-	state->off = 0;
-	state->fsync = true;
-	vfs_ceph_aio_submit(handle, req, ev);
-	return req;
-#endif
-
 	vfs_ceph_aio_start(state);
-	ret = vfs_ceph_ll_fsync(handle, state->cfh, false);
+	ret = vfs_ceph_ll_fsync(handle, state->cfh, 0);
 	vfs_ceph_aio_finish(state, ret);
 	if (ret != 0) {
 		/* ceph_fsync returns -errno on error. */
diff -Nru samba-4.22.4+dfsg/source3/modules/vfs_fruit.c samba-4.22.6+dfsg/source3/modules/vfs_fruit.c
--- samba-4.22.4+dfsg/source3/modules/vfs_fruit.c	2025-04-17 20:12:25.666450500 +0300
+++ samba-4.22.6+dfsg/source3/modules/vfs_fruit.c	2025-10-16 17:34:01.669333500 +0300
@@ -125,6 +125,7 @@
 	bool use_aapl;		/* config from smb.conf */
 	bool use_copyfile;
 	bool readdir_attr_enabled;
+	bool posix_opens;
 	bool unix_info_enabled;
 	bool copyfile_enabled;
 	bool veto_appledouble;
@@ -136,6 +137,7 @@
 	bool wipe_intentionally_left_blank_rfork;
 	bool delete_empty_adfiles;
 	bool validate_afpinfo;
+	bool ignore_zero_aces;
 
 	/*
 	 * Additional options, all enabled by default,
@@ -339,6 +341,14 @@
 	config->use_copyfile = lp_parm_bool(-1, FRUIT_PARAM_TYPE_NAME,
 					   "copyfile", false);
 
+	config->posix_opens = lp_parm_bool(
+		SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME, "posix_opens", true);
+
+	config->ignore_zero_aces = lp_parm_bool(SNUM(handle->conn),
+						FRUIT_PARAM_TYPE_NAME,
+						"ignore_zero_aces",
+						true);
+
 	config->aapl_zero_file_id =
 	    lp_parm_bool(SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
 			 "zero_file_id", true);
@@ -1754,16 +1764,27 @@
 			files_struct *fsp,
 			const struct vfs_open_how *how)
 {
+	struct fruit_config_data *config = NULL;
 	int fd;
 
+	SMB_VFS_HANDLE_GET_DATA(handle, config,
+				struct fruit_config_data, return -1);
+
 	DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
 
 	if (!is_named_stream(smb_fname)) {
-		return SMB_VFS_NEXT_OPENAT(handle,
-					   dirfsp,
-					   smb_fname,
-					   fsp,
-					   how);
+		fd = SMB_VFS_NEXT_OPENAT(handle,
+					 dirfsp,
+					 smb_fname,
+					 fsp,
+					 how);
+		if (fd == -1) {
+			return -1;
+		}
+		if (config->posix_opens && global_fruit_config.nego_aapl) {
+			fsp->fsp_flags.posix_open = true;
+		}
+		return fd;
 	}
 
 	if (how->resolve != 0) {
@@ -1798,7 +1819,13 @@
 	DBG_DEBUG("Path [%s] fd [%d]\n", smb_fname_str_dbg(smb_fname), fd);
 
 	/* Prevent reopen optimisation */
+	if (fd == -1) {
+		return -1;
+	}
 	fsp->fsp_flags.have_proc_fds = false;
+	if (config->posix_opens && global_fruit_config.nego_aapl) {
+		fsp->fsp_flags.posix_open = true;
+	}
 	return fd;
 }
 
@@ -4605,6 +4632,7 @@
 				  uint32_t security_info_sent,
 				  const struct security_descriptor *orig_psd)
 {
+	struct fruit_config_data *config = NULL;
 	NTSTATUS status;
 	bool do_chmod;
 	mode_t ms_nfs_mode = 0;
@@ -4612,6 +4640,10 @@
 	struct security_descriptor *psd = NULL;
 	uint32_t orig_num_aces = 0;
 
+	SMB_VFS_HANDLE_GET_DATA(handle, config,
+				struct fruit_config_data,
+				return NT_STATUS_UNSUCCESSFUL);
+
 	if (orig_psd->dacl != NULL) {
 		orig_num_aces = orig_psd->dacl->num_aces;
 	}
@@ -4623,6 +4655,13 @@
 
 	DBG_DEBUG("%s\n", fsp_str_dbg(fsp));
 
+	if (config->ignore_zero_aces && (psd->dacl->num_aces == 0)) {
+		/*
+		 * Just ignore Set-ACL requests with zero ACEs.
+		 */
+		return NT_STATUS_OK;
+	}
+
 	status = check_ms_nfs(handle, fsp, psd, &ms_nfs_mode, &do_chmod);
 	if (!NT_STATUS_IS_OK(status)) {
 		DEBUG(1, ("fruit_fset_nt_acl: check_ms_nfs failed%s\n", fsp_str_dbg(fsp)));
diff -Nru samba-4.22.4+dfsg/source3/modules/vfs_streams_xattr.c samba-4.22.6+dfsg/source3/modules/vfs_streams_xattr.c
--- samba-4.22.4+dfsg/source3/modules/vfs_streams_xattr.c	2025-02-06 13:31:54.488148500 +0300
+++ samba-4.22.6+dfsg/source3/modules/vfs_streams_xattr.c	2025-10-15 15:19:02.314114800 +0300
@@ -1047,15 +1047,18 @@
 
         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;
diff -Nru samba-4.22.4+dfsg/source3/modules/vfs_xattr_tdb.c samba-4.22.6+dfsg/source3/modules/vfs_xattr_tdb.c
--- samba-4.22.4+dfsg/source3/modules/vfs_xattr_tdb.c	2025-02-06 13:31:54.488148500 +0300
+++ samba-4.22.6+dfsg/source3/modules/vfs_xattr_tdb.c	2025-10-16 17:34:01.669333500 +0300
@@ -604,13 +604,12 @@
 	} else {
 		ret = SMB_VFS_NEXT_STAT(handle, full_fname);
 		if (ret == -1 && (errno == ENOENT || errno == ELOOP)) {
-			if (VALID_STAT(smb_fname->st) &&
-					S_ISLNK(smb_fname->st.st_ex_mode)) {
-				/*
-				 * Original name was a link - Could be
-				 * trying to remove a dangling symlink.
-				 */
-				ret = SMB_VFS_NEXT_LSTAT(handle, full_fname);
+			/*
+			 * Could be trying to remove a dangling symlink.
+			 */
+			ret = SMB_VFS_NEXT_LSTAT(handle, full_fname);
+			if (ret == 0 && !S_ISLNK(full_fname->st.st_ex_mode)) {
+				ret = -1;
 			}
 		}
 	}
diff -Nru samba-4.22.4+dfsg/source3/passdb/machine_account_secrets.c samba-4.22.6+dfsg/source3/passdb/machine_account_secrets.c
--- samba-4.22.4+dfsg/source3/passdb/machine_account_secrets.c	2025-02-06 13:31:54.500148500 +0300
+++ samba-4.22.6+dfsg/source3/passdb/machine_account_secrets.c	2025-10-16 17:34:01.669333500 +0300
@@ -1674,7 +1674,8 @@
 					 TALLOC_CTX *mem_ctx,
 					 struct secrets_domain_info1 **pinfo,
 					 struct secrets_domain_info1_change **pprev,
-					 NTSTATUS (*sync_pw2keytabs_fn)(void))
+					 NTSTATUS (*sync_pw2keytabs_fn)(const char *),
+					 const char *opt_host)
 {
 	TALLOC_CTX *frame = talloc_stackframe();
 	struct db_context *db = NULL;
@@ -1770,7 +1771,7 @@
 	}
 
 	if (prev == NULL && sync_pw2keytabs_fn != NULL) {
-		status = sync_pw2keytabs_fn();
+		status = sync_pw2keytabs_fn(opt_host);
 		if (!NT_STATUS_IS_OK(status)) {
 			DBG_ERR("Sync of machine password failed.\n");
 			dbwrap_transaction_cancel(db);
@@ -2023,7 +2024,8 @@
 NTSTATUS secrets_finish_password_change(const char *change_server,
 					NTTIME change_time,
 					const struct secrets_domain_info1 *cookie,
-					NTSTATUS (*sync_pw2keytabs_fn)(void))
+					NTSTATUS (*sync_pw2keytabs_fn)(const char *),
+					const char *prefer_dc)
 {
 	const char *domain = cookie->domain_info.name.string;
 	TALLOC_CTX *frame = talloc_stackframe();
@@ -2102,7 +2104,7 @@
 	}
 
 	if (sync_pw2keytabs_fn != NULL) {
-		status = sync_pw2keytabs_fn();
+		status = sync_pw2keytabs_fn(prefer_dc);
 		if (!NT_STATUS_IS_OK(status)) {
 			DBG_ERR("Sync of machine password failed.\n");
 			TALLOC_FREE(frame);
diff -Nru samba-4.22.4+dfsg/source3/rpc_server/dfs/srv_dfs_nt.c samba-4.22.6+dfsg/source3/rpc_server/dfs/srv_dfs_nt.c
--- samba-4.22.4+dfsg/source3/rpc_server/dfs/srv_dfs_nt.c	2025-02-06 13:31:54.520148500 +0300
+++ samba-4.22.6+dfsg/source3/rpc_server/dfs/srv_dfs_nt.c	2025-10-16 17:34:01.673333600 +0300
@@ -97,7 +97,9 @@
 				   remote_address,
 				   local_address,
 				   jn, &consumedcnt, &self_ref);
-	if(!NT_STATUS_IS_OK(status)) {
+	if(!NT_STATUS_IS_OK(status) &&
+	   !NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_PATH_NOT_FOUND))
+	{
 		return ntstatus_to_werror(status);
 	}
 
diff -Nru samba-4.22.4+dfsg/source3/rpc_server/mdssvc/es_parser.y samba-4.22.6+dfsg/source3/rpc_server/mdssvc/es_parser.y
--- samba-4.22.4+dfsg/source3/rpc_server/mdssvc/es_parser.y	2025-02-06 13:31:54.524148700 +0300
+++ samba-4.22.6+dfsg/source3/rpc_server/mdssvc/es_parser.y	2025-10-16 17:34:01.673333600 +0300
@@ -85,7 +85,6 @@
 	int mdsyylwrap(void);
 	bool map_spotlight_to_es_query(TALLOC_CTX *mem_ctx,
 				       json_t *mappings,
-				       const char *path_scope,
 				       const char *query_string,
 				       char **_es_query);
 }
@@ -640,7 +639,6 @@
  **/
 bool map_spotlight_to_es_query(TALLOC_CTX *mem_ctx,
 			       json_t *mappings,
-			       const char *path_scope,
 			       const char *query_string,
 			       char **_es_query)
 {
@@ -691,13 +689,11 @@
 		return false;
 	}
 
-	es_query = talloc_asprintf(mem_ctx,
-				   "(%s) AND path.real.fulltext:\\\"%s\\\"",
-				   s.result, path_scope);
-	TALLOC_FREE(s.frame);
+	es_query = talloc_strdup(mem_ctx, s.result);
 	if (es_query == NULL) {
 		return false;
 	}
+	TALLOC_FREE(s.frame);
 
 	*_es_query = es_query;
 	return true;
diff -Nru samba-4.22.4+dfsg/source3/rpc_server/mdssvc/es_parser_test.c samba-4.22.6+dfsg/source3/rpc_server/mdssvc/es_parser_test.c
--- samba-4.22.4+dfsg/source3/rpc_server/mdssvc/es_parser_test.c	2025-02-06 13:31:54.524148700 +0300
+++ samba-4.22.6+dfsg/source3/rpc_server/mdssvc/es_parser_test.c	2025-10-16 17:34:01.673333600 +0300
@@ -41,7 +41,6 @@
 	char *default_path = NULL;
 	const char *path = NULL;
 	const char *query_string = NULL;
-	const char *path_scope = NULL;
 	char *es_query = NULL;
 	bool ok;
 
@@ -50,7 +49,6 @@
 		return 1;
 	}
 	query_string = argv[1];
-	path_scope = "/foo/bar";
 
 	lp_load_global(get_dyn_CONFIGFILE());
 
@@ -86,7 +84,6 @@
 
 	ok = map_spotlight_to_es_query(mem_ctx,
 					   mappings,
-					   path_scope,
 					   query_string,
 					   &es_query);
 	printf("%s\n", ok ? es_query : "*mapping failed*");
diff -Nru samba-4.22.4+dfsg/source3/rpc_server/mdssvc/mdssvc_es.c samba-4.22.6+dfsg/source3/rpc_server/mdssvc/mdssvc_es.c
--- samba-4.22.4+dfsg/source3/rpc_server/mdssvc/mdssvc_es.c	2025-02-06 13:31:54.524148700 +0300
+++ samba-4.22.6+dfsg/source3/rpc_server/mdssvc/mdssvc_es.c	2025-10-16 17:34:01.673333600 +0300
@@ -36,17 +36,27 @@
 #undef DBGC_CLASS
 #define DBGC_CLASS DBGC_RPC_SRV
 
-#define MDSSVC_ELASTIC_QUERY_TEMPLATE	\
-	"{"				\
-	"    \"from\": %zu,"		\
-	"    \"size\": %zu,"		\
-	"    \"_source\": [%s],"	\
-	"    \"query\": {"		\
-        "        \"query_string\": {"	\
-	"            \"query\": \"%s\"" \
-	"        }"			\
-	"    }"				\
-	"}"
+#define MDSSVC_ELASTIC_QUERY_TEMPLATE			\
+	"{\n"						\
+	"    \"from\": %zu,\n"				\
+	"    \"size\": %zu,\n"				\
+	"    \"_source\": [%s],\n"			\
+	"    \"query\": {\n"				\
+	"        \"bool\": {\n"				\
+	"            \"filter\": [ {\n"			\
+	"                \"prefix\": {\n"		\
+	"                    \"path.real\": \"%s/\"\n"	\
+	"                }\n"				\
+	"            } ],\n"				\
+	"            \"must\": [ {\n"			\
+        "                \"query_string\": {\n"		\
+	"                    \"query\": \"%s\",\n"	\
+	"                    \"fields\": [%s]\n"	\
+	"                }\n"				\
+	"            } ]\n"				\
+	"        }\n"					\
+	"    }\n"					\
+	"}\n"
 
 #define MDSSVC_ELASTIC_SOURCES \
 	"\"path.real\""
@@ -57,6 +67,7 @@
 	json_error_t json_error;
 	char *default_path = NULL;
 	const char *path = NULL;
+	const char *default_fields = "\"file.filename\", \"content\"";
 
 	mdssvc_es_ctx = talloc_zero(mdssvc_ctx, struct mdssvc_es_ctx);
 	if (mdssvc_es_ctx == NULL) {
@@ -97,6 +108,15 @@
 	}
 	TALLOC_FREE(default_path);
 
+	mdssvc_es_ctx->default_fields = lp_parm_const_string(GLOBAL_SECTION_SNUM,
+							     "elasticsearch",
+							     "default_fields",
+							     default_fields);
+	if (mdssvc_es_ctx->default_fields == NULL) {
+		TALLOC_FREE(mdssvc_es_ctx);
+		return false;
+	}
+
 	mdssvc_ctx->backend_private = mdssvc_es_ctx;
 	return true;
 }
@@ -413,7 +433,6 @@
 	ok = map_spotlight_to_es_query(
 		s,
 		mds_es_ctx->mdssvc_es_ctx->mappings,
-		slq->path_scope,
 		slq->query_string,
 		&s->es_query);
 	if (!ok) {
@@ -616,12 +635,15 @@
 		return tevent_req_post(req, ev);
 	}
 
-	elastic_query = talloc_asprintf(state,
-					MDSSVC_ELASTIC_QUERY_TEMPLATE,
-					s->from,
-					s->size,
-					MDSSVC_ELASTIC_SOURCES,
-					s->es_query);
+	elastic_query = talloc_asprintf(
+		state,
+		MDSSVC_ELASTIC_QUERY_TEMPLATE,
+		s->from,
+		s->size,
+		MDSSVC_ELASTIC_SOURCES,
+		s->slq->path_scope,
+		s->es_query,
+		s->mds_es_ctx->mdssvc_es_ctx->default_fields);
 	if (tevent_req_nomem(elastic_query, req)) {
 		return tevent_req_post(req, ev);
 	}
diff -Nru samba-4.22.4+dfsg/source3/rpc_server/mdssvc/mdssvc_es.h samba-4.22.6+dfsg/source3/rpc_server/mdssvc/mdssvc_es.h
--- samba-4.22.4+dfsg/source3/rpc_server/mdssvc/mdssvc_es.h	2025-02-06 13:31:54.524148700 +0300
+++ samba-4.22.6+dfsg/source3/rpc_server/mdssvc/mdssvc_es.h	2025-10-16 17:34:01.673333600 +0300
@@ -30,6 +30,7 @@
 	struct mdssvc_ctx *mdssvc_ctx;
 	struct cli_credentials *creds;
 	json_t *mappings;
+	const char *default_fields;
 };
 
 /*
diff -Nru samba-4.22.4+dfsg/source3/rpc_server/mdssvc/test_mdsparser_es.c samba-4.22.6+dfsg/source3/rpc_server/mdssvc/test_mdsparser_es.c
--- samba-4.22.4+dfsg/source3/rpc_server/mdssvc/test_mdsparser_es.c	2025-02-06 13:31:54.524148700 +0300
+++ samba-4.22.6+dfsg/source3/rpc_server/mdssvc/test_mdsparser_es.c	2025-10-16 17:34:01.673333600 +0300
@@ -28,136 +28,133 @@
 #include "lib/param/param.h"
 #include "rpc_server/mdssvc/es_parser.tab.h"
 
-#define PATH_QUERY_SUBEXPR \
-	" AND path.real.fulltext:\\\"/foo/bar\\\""
-
 static struct {
 	const char *mds;
 	const char *es;
 } map[] = {
 	{
 		"*==\"samba\"",
-		"(samba)" PATH_QUERY_SUBEXPR
+		"samba"
 	}, {
 		"kMDItemTextContent==\"samba\"",
-		"(content:samba)" PATH_QUERY_SUBEXPR
+		"content:samba"
 	}, {
 		"_kMDItemGroupId==\"11\"",
-		"(file.content_type:(application\\\\/pdf))" PATH_QUERY_SUBEXPR
+		"file.content_type:(application\\\\/pdf)"
 	}, {
 		"kMDItemContentType==\"1\"",
-		"(file.content_type:(message\\\\/rfc822))" PATH_QUERY_SUBEXPR
+		"file.content_type:(message\\\\/rfc822)"
 	}, {
 		"kMDItemContentType==\"public.content\"",
-		"(file.content_type:(message\\\\/rfc822 application\\\\/pdf application\\\\/vnd.oasis.opendocument.presentation image\\\\/* text\\\\/*))" PATH_QUERY_SUBEXPR
+		"file.content_type:(message\\\\/rfc822 application\\\\/pdf application\\\\/vnd.oasis.opendocument.presentation image\\\\/* text\\\\/*)"
 	}, {
 		"kMDItemContentTypeTree==\"1\"",
-		"(file.content_type:(message\\\\/rfc822))" PATH_QUERY_SUBEXPR
+		"file.content_type:(message\\\\/rfc822)"
 	}, {
 		"kMDItemFSContentChangeDate==$time.iso(2018-10-01T10:00:00Z)",
-		"(file.last_modified:2018\\\\-10\\\\-01T10\\\\:00\\\\:00Z)" PATH_QUERY_SUBEXPR
+		"file.last_modified:2018\\\\-10\\\\-01T10\\\\:00\\\\:00Z"
 	}, {
 		"kMDItemFSContentChangeDate==\"1\"",
-		"(file.last_modified:2001\\\\-01\\\\-01T00\\\\:00\\\\:01Z)" PATH_QUERY_SUBEXPR
+		"file.last_modified:2001\\\\-01\\\\-01T00\\\\:00\\\\:01Z"
 	}, {
 		"kMDItemFSCreationDate==\"1\"",
-		"(file.created:2001\\\\-01\\\\-01T00\\\\:00\\\\:01Z)" PATH_QUERY_SUBEXPR
+		"file.created:2001\\\\-01\\\\-01T00\\\\:00\\\\:01Z"
 	}, {
 		"kMDItemFSName==\"samba*\"",
-		"(file.filename:samba*)" PATH_QUERY_SUBEXPR
+		"file.filename:samba*"
 	}, {
 		"kMDItemFSOwnerGroupID==\"0\"",
-		"(attributes.owner:0)" PATH_QUERY_SUBEXPR
+		"attributes.owner:0"
 	}, {
 		"kMDItemFSOwnerUserID==\"0\"",
-		"(attributes.group:0)" PATH_QUERY_SUBEXPR
+		"attributes.group:0"
 	}, {
 		"kMDItemFSSize==\"1\"",
-		"(file.filesize:1)" PATH_QUERY_SUBEXPR
+		"file.filesize:1"
 	}, {
 		"kMDItemPath==\"/foo/bar\"",
-		"(path.real:\\\\/foo\\\\/bar)" PATH_QUERY_SUBEXPR
+		"path.real:\\\\/foo\\\\/bar"
 	}, {
 		"kMDItemAttributeChangeDate==\"1\"",
-		"(file.last_modified:2001\\\\-01\\\\-01T00\\\\:00\\\\:01Z)" PATH_QUERY_SUBEXPR
+		"file.last_modified:2001\\\\-01\\\\-01T00\\\\:00\\\\:01Z"
 	}, {
 		"kMDItemAuthors==\"Chouka\"",
-		"(meta.author:Chouka)" PATH_QUERY_SUBEXPR
+		"meta.author:Chouka"
 	}, {
 		"kMDItemContentCreationDate==\"1\"",
-		"(file.created:2001\\\\-01\\\\-01T00\\\\:00\\\\:01Z)" PATH_QUERY_SUBEXPR
+		"file.created:2001\\\\-01\\\\-01T00\\\\:00\\\\:01Z"
 	}, {
 		"kMDItemContentModificationDate==\"1\"",
-		"(file.last_modified:2001\\\\-01\\\\-01T00\\\\:00\\\\:01Z)" PATH_QUERY_SUBEXPR
+		"file.last_modified:2001\\\\-01\\\\-01T00\\\\:00\\\\:01Z"
 	}, {
 		"kMDItemCreator==\"Chouka\"",
-		"(meta.raw.creator:Chouka)" PATH_QUERY_SUBEXPR
+		"meta.raw.creator:Chouka"
 	}, {
 		"kMDItemDescription==\"Dog\"",
-		"(meta.raw.description:Dog)" PATH_QUERY_SUBEXPR
+		"meta.raw.description:Dog"
 	}, {
 		"kMDItemDisplayName==\"Samba\"",
-		"(file.filename:Samba)" PATH_QUERY_SUBEXPR
+		"file.filename:Samba"
 	}, {
 		"kMDItemDurationSeconds==\"1\"",
-		"(meta.raw.xmpDM\\\\:duration:1)" PATH_QUERY_SUBEXPR
+		"meta.raw.xmpDM\\\\:duration:1"
 	}, {
 		"kMDItemNumberOfPages==\"1\"",
-		"(meta.raw.xmpTPg\\\\:NPages:1)" PATH_QUERY_SUBEXPR
+		"meta.raw.xmpTPg\\\\:NPages:1"
 	}, {
 		"kMDItemTitle==\"Samba\"",
-		"(meta.title:Samba)" PATH_QUERY_SUBEXPR
+		"meta.title:Samba"
 	}, {
 		"kMDItemAlbum==\"Red Roses for Me\"",
-		"(meta.raw.xmpDM\\\\:album:Red\\\\ Roses\\\\ for\\\\ Me)" PATH_QUERY_SUBEXPR
+		"meta.raw.xmpDM\\\\:album:Red\\\\ Roses\\\\ for\\\\ Me"
 	}, {
 		"kMDItemBitsPerSample==\"1\"",
-		"(meta.raw.tiff\\\\:BitsPerSample:1)" PATH_QUERY_SUBEXPR
+		"meta.raw.tiff\\\\:BitsPerSample:1"
 	}, {
 		"kMDItemPixelHeight==\"1\"",
-		"(meta.raw.Image\\\\ Height:1)" PATH_QUERY_SUBEXPR
+		"meta.raw.Image\\\\ Height:1"
 	}, {
 		"kMDItemPixelWidth==\"1\"",
-		"(meta.raw.Image\\\\ Width:1)" PATH_QUERY_SUBEXPR
+		"meta.raw.Image\\\\ Width:1"
 	}, {
 		"kMDItemResolutionHeightDPI==\"72\"",
-		"(meta.raw.Y\\\\ Resolution:72)" PATH_QUERY_SUBEXPR
+		"meta.raw.Y\\\\ Resolution:72"
 	}, {
 		"kMDItemResolutionWidthDPI==\"72\"",
-		"(meta.raw.X\\\\ Resolution:72)" PATH_QUERY_SUBEXPR
+		"meta.raw.X\\\\ Resolution:72"
 	},{
 		"*!=\"samba\"",
-		"((NOT samba))" PATH_QUERY_SUBEXPR
+		"(NOT samba)"
 	}, {
 		"kMDItemFSSize!=\"1\"",
-		"((NOT file.filesize:1))" PATH_QUERY_SUBEXPR
+		"(NOT file.filesize:1)"
 	}, {
 		"kMDItemFSSize>\"1\"",
-		"(file.filesize:{1 TO *})" PATH_QUERY_SUBEXPR
+		"file.filesize:{1 TO *}"
 	}, {
 		"kMDItemFSSize<\"1\"",
-		"(file.filesize:{* TO 1})" PATH_QUERY_SUBEXPR
+		"file.filesize:{* TO 1}"
 	}, {
 		"kMDItemFSCreationDate!=\"1\"",
-		"((NOT file.created:2001\\\\-01\\\\-01T00\\\\:00\\\\:01Z))" PATH_QUERY_SUBEXPR
+		"(NOT file.created:2001\\\\-01\\\\-01T00\\\\:00\\\\:01Z)"
 	}, {
 		"kMDItemFSCreationDate>\"1\"",
-		"(file.created:{2001\\\\-01\\\\-01T00\\\\:00\\\\:01Z TO *})" PATH_QUERY_SUBEXPR
+		"file.created:{2001\\\\-01\\\\-01T00\\\\:00\\\\:01Z TO *}"
 	}, {
 		"kMDItemFSCreationDate<\"1\"",
-		"(file.created:{* TO 2001\\\\-01\\\\-01T00\\\\:00\\\\:01Z})" PATH_QUERY_SUBEXPR
+		"file.created:{* TO 2001\\\\-01\\\\-01T00\\\\:00\\\\:01Z}"
 	}, {
 		"kMDItemFSName==\"Samba\"||kMDItemTextContent==\"Samba\"",
-		"(file.filename:Samba OR content:Samba)" PATH_QUERY_SUBEXPR
+		"file.filename:Samba OR content:Samba"
 	}, {
 		"kMDItemFSName==\"Samba\"&&kMDItemTextContent==\"Samba\"",
-		"((file.filename:Samba) AND (content:Samba))" PATH_QUERY_SUBEXPR
+		"(file.filename:Samba) AND (content:Samba)"
 	}, {
 		"InRange(kMDItemFSCreationDate,1,2)",
-		"(file.created:[2001\\\\-01\\\\-01T00\\\\:00\\\\:01Z TO 2001\\\\-01\\\\-01T00\\\\:00\\\\:02Z])" PATH_QUERY_SUBEXPR
+		"file.created:[2001\\\\-01\\\\-01T00\\\\:00\\\\:01Z TO 2001\\\\-01\\\\-01T00\\\\:00\\\\:02Z]"
 	}, {
 		"InRange(kMDItemFSSize,1,2)",
-		"(file.filesize:[1 TO 2])" PATH_QUERY_SUBEXPR
+		"file.filesize:[1 TO 2]"
 	}
 };
 
@@ -167,49 +164,55 @@
 } map_ignore_failures[] = {
 	{
 		"*==\"Samba\"||foo==\"bar\"",
-		"(Samba)" PATH_QUERY_SUBEXPR
+		"Samba"
 	}, {
 		"*==\"Samba\"&&foo==\"bar\"",
-		"(Samba)" PATH_QUERY_SUBEXPR
+		"Samba"
 	}, {
 		"*==\"Samba\"||kMDItemContentType==\"666\"",
-		"(Samba)" PATH_QUERY_SUBEXPR
+		"Samba"
 	}, {
 		"*==\"Samba\"&&kMDItemContentType==\"666\"",
-		"(Samba)" PATH_QUERY_SUBEXPR
+		"Samba"
 	}, {
 		"*==\"Samba\"||foo==\"bar\"||kMDItemContentType==\"666\"",
-		"(Samba)" PATH_QUERY_SUBEXPR
+		"Samba"
 	}, {
 		"*==\"Samba\"&&foo==\"bar\"&&kMDItemContentType==\"666\"",
-		"(Samba)" PATH_QUERY_SUBEXPR
+		"Samba"
 	}, {
 		"foo==\"bar\"||kMDItemContentType==\"666\"||*==\"Samba\"||x!=\"6\"",
-		"(Samba)" PATH_QUERY_SUBEXPR
+		"Samba"
 	}, {
 		"*==\"Samba\"||InRange(foo,1,2)",
-		"(Samba)" PATH_QUERY_SUBEXPR
+		"Samba"
 	}, {
 		"*==\"Samba\"||foo==$time.iso(2018-10-01T10:00:00Z)",
-		"(Samba)" PATH_QUERY_SUBEXPR
+		"Samba"
 	}
 };
 
 static void test_mdsparser_es(void **state)
 {
 	TALLOC_CTX *frame = talloc_stackframe();
-	const char *path_scope = "/foo/bar";
 	char *es_query = NULL;
+	char *default_path = NULL;
 	const char *path = NULL;
 	json_t *mappings = NULL;
 	json_error_t json_error;
 	int i;
 	bool ok;
 
+	default_path = talloc_asprintf(
+		frame,
+		"%s/mdssvc/elasticsearch_mappings.json",
+		get_dyn_SAMBA_DATADIR());
+	assert_non_null(default_path);
+
 	path = lp_parm_const_string(GLOBAL_SECTION_SNUM,
 				    "elasticsearch",
 				    "mappings",
-				    NULL);
+				    default_path);
 	assert_non_null(path);
 
 	mappings = json_load_file(path, 0, &json_error);
@@ -219,7 +222,6 @@
 		DBG_DEBUG("Mapping: %s\n", map[i].mds);
 		ok = map_spotlight_to_es_query(frame,
 					       mappings,
-					       path_scope,
 					       map[i].mds,
 					       &es_query);
 		assert_true(ok);
@@ -238,7 +240,6 @@
 		DBG_DEBUG("Mapping: %s\n", map_ignore_failures[i].mds);
 		ok = map_spotlight_to_es_query(frame,
 					       mappings,
-					       path_scope,
 					       map_ignore_failures[i].mds,
 					       &es_query);
 		assert_true(ok);
diff -Nru samba-4.22.4+dfsg/source3/rpc_server/rpcd_mdssvc.c samba-4.22.6+dfsg/source3/rpc_server/rpcd_mdssvc.c
--- samba-4.22.4+dfsg/source3/rpc_server/rpcd_mdssvc.c	2025-02-06 13:31:54.528148700 +0300
+++ samba-4.22.6+dfsg/source3/rpc_server/rpcd_mdssvc.c	2025-10-16 17:34:01.673333600 +0300
@@ -17,6 +17,7 @@
 
 #include "includes.h"
 #include "source3/locking/proto.h"
+#include "source3/smbd/proto.h"
 #include "rpc_worker.h"
 #include "librpc/gen_ndr/ndr_mdssvc.h"
 #include "librpc/gen_ndr/ndr_mdssvc_scompat.h"
@@ -50,6 +51,8 @@
 		return NT_STATUS_INTERNAL_ERROR;
 	}
 
+	mangle_reset_cache();
+
 	ep_servers[0] = mdssvc_get_ep_server();
 
 	*_ep_servers = ep_servers;
diff -Nru samba-4.22.4+dfsg/source3/selftest/tests.py samba-4.22.6+dfsg/source3/selftest/tests.py
--- samba-4.22.4+dfsg/source3/selftest/tests.py	2025-08-21 18:22:16.467915800 +0300
+++ samba-4.22.6+dfsg/source3/selftest/tests.py	2025-10-15 15:19:02.314114800 +0300
@@ -1152,6 +1152,7 @@
 vfs = [
     "vfs.fruit",
     "vfs.acl_xattr",
+    "vfs.streams_xattr",
     "vfs.fruit_netatalk",
     "vfs.fruit_file_id",
     "vfs.fruit_timemachine",
@@ -1347,6 +1348,8 @@
             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 -Nru samba-4.22.4+dfsg/source3/smbd/dir.c samba-4.22.6+dfsg/source3/smbd/dir.c
--- samba-4.22.4+dfsg/source3/smbd/dir.c	2025-02-06 13:31:54.560148700 +0300
+++ samba-4.22.6+dfsg/source3/smbd/dir.c	2025-10-16 17:34:01.673333600 +0300
@@ -640,6 +640,8 @@
 			smb_fname->st.st_ex_mode = (smb_fname->st.st_ex_mode &
 						    ~S_IFMT) |
 						   S_IFDIR;
+			smb_fname->fsp->fsp_name->st.st_ex_mode =
+				smb_fname->st.st_ex_mode;
 
 			mode = dos_mode_msdfs(conn, dname, &smb_fname->st);
 			get_dosmode = false;
@@ -1171,7 +1173,7 @@
 		goto fail;
 	}
 	dir_hnd->fsp = fsp;
-	if (fsp->fsp_flags.posix_open) {
+	if (fsp->fsp_name->flags & SMB_FILENAME_POSIX_PATH) {
 		dir_hnd->case_sensitive = true;
 	} else {
 		dir_hnd->case_sensitive = conn->case_sensitive;
diff -Nru samba-4.22.4+dfsg/source3/smbd/dosmode.c samba-4.22.6+dfsg/source3/smbd/dosmode.c
--- samba-4.22.4+dfsg/source3/smbd/dosmode.c	2025-02-06 13:31:54.560148700 +0300
+++ samba-4.22.6+dfsg/source3/smbd/dosmode.c	2025-10-16 17:34:01.673333600 +0300
@@ -1279,6 +1279,10 @@
 {
 	bool ok;
 
+	if (fsp->fsp_flags.posix_open) {
+		return true;
+	}
+
 	if (is_omit_timespec(&mtime)) {
 		return true;
 	}
diff -Nru samba-4.22.4+dfsg/source3/smbd/open.c samba-4.22.6+dfsg/source3/smbd/open.c
--- samba-4.22.4+dfsg/source3/smbd/open.c	2025-08-21 18:22:16.471915700 +0300
+++ samba-4.22.6+dfsg/source3/smbd/open.c	2025-10-16 17:34:01.677333600 +0300
@@ -2057,7 +2057,10 @@
 	fsp->lease->lease.parent_lease_key = lease->parent_lease_key;
 	fsp->lease->lease.lease_flags = lease->lease_flags;
 	fsp->lease->lease.lease_state = granted;
-	fsp->lease->lease.lease_epoch = lease->lease_epoch + 1;
+	fsp->lease->lease.lease_epoch = lease->lease_epoch;
+	if (granted != 0) {
+		fsp->lease->lease.lease_epoch++;
+	}
 
 	status = leases_db_add(client_guid,
 			       &lease->lease_key,
diff -Nru samba-4.22.4+dfsg/source3/smbd/smb2_lock.c samba-4.22.6+dfsg/source3/smbd/smb2_lock.c
--- samba-4.22.4+dfsg/source3/smbd/smb2_lock.c	2025-04-17 20:12:25.674450400 +0300
+++ samba-4.22.6+dfsg/source3/smbd/smb2_lock.c	2025-10-16 17:34:01.677333600 +0300
@@ -381,8 +381,22 @@
 
 	for (i=0; i<in_lock_count; i++) {
 		bool invalid = false;
-		bool posix_handle = fsp->fsp_flags.posix_open;
+		bool posix_handle = fsp->fsp_name->flags &
+			SMB_FILENAME_POSIX_PATH;
 
+		/*
+		 * For POSIX clients struct files_struct.fsp_flags.posix_open
+		 * and struct smb_filename.flags SMB_FILENAME_POSIX_PATH will
+		 * always be set to the same value.
+		 *
+		 * For macOS clients vfs_fruit with fruit:posix_open=yes, we
+		 * deliberately set both flags to fsp_flags.posix_open=true
+		 * while SMB_FILENAME_POSIX_PATH will not be set.
+		 *
+		 * By deliberately checking the fsp_name flag here instead of
+		 * the fsp flag, Byterange Lock processing uses Windows
+		 * behaviour for macOS clients which is what we want.
+		 */
 		switch (in_locks[i].flags) {
 		case SMB2_LOCK_FLAG_SHARED:
 		case SMB2_LOCK_FLAG_EXCLUSIVE:
diff -Nru samba-4.22.4+dfsg/source3/utils/net.c samba-4.22.6+dfsg/source3/utils/net.c
--- samba-4.22.4+dfsg/source3/utils/net.c	2025-08-21 18:22:16.471915700 +0300
+++ samba-4.22.6+dfsg/source3/utils/net.c	2025-10-16 17:34:01.677333600 +0300
@@ -235,10 +235,11 @@
 							 &info,
 							 &prev,
 #ifdef HAVE_ADS
-							 sync_pw2keytabs);
+							 sync_pw2keytabs,
 #else
-							 NULL);
+							 NULL,
 #endif
+							 c->opt_host);
 		if (!NT_STATUS_IS_OK(status)) {
 			d_fprintf(stderr,
 			        _("Unable to write the machine account password in the secrets database"));
@@ -261,10 +262,11 @@
 							now,
 							info,
 #ifdef HAVE_ADS
-							sync_pw2keytabs);
+							sync_pw2keytabs,
 #else
-							NULL);
+							NULL,
 #endif
+							c->opt_host);
 		if (!NT_STATUS_IS_OK(status)) {
 			d_fprintf(stderr,
 			        _("Unable to write the machine account password in the secrets database"));
diff -Nru samba-4.22.4+dfsg/source3/utils/net_ads.c samba-4.22.6+dfsg/source3/utils/net_ads.c
--- samba-4.22.4+dfsg/source3/utils/net_ads.c	2025-08-21 18:22:16.471915700 +0300
+++ samba-4.22.6+dfsg/source3/utils/net_ads.c	2025-10-16 17:34:01.677333600 +0300
@@ -1359,7 +1359,7 @@
 	char *disp_fields[2] = {NULL, NULL};
 	int ret = -1;
 
-	if (argc >= 0) {
+	if (argc > 0) {
 		TALLOC_FREE(tmp_ctx);
 		return net_run_function(c, argc, argv, "net ads group", func);
 	}
@@ -2965,7 +2965,7 @@
 		net_use_krb_machine_account(c);
 	}
 
-	ntstatus = sync_pw2keytabs();
+	ntstatus = sync_pw2keytabs(c->opt_host);
 	ret = NT_STATUS_IS_OK(ntstatus) ? 0 : 1;
 	return ret;
 }
diff -Nru samba-4.22.4+dfsg/source4/nbt_server/wins/wins_hook.c samba-4.22.6+dfsg/source4/nbt_server/wins/wins_hook.c
--- samba-4.22.4+dfsg/source4/nbt_server/wins/wins_hook.c	2025-02-06 13:31:54.720149800 +0300
+++ samba-4.22.6+dfsg/source4/nbt_server/wins/wins_hook.c	2025-10-15 15:19:02.314114800 +0300
@@ -43,9 +43,18 @@
 	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;
 
diff -Nru samba-4.22.4+dfsg/source4/selftest/tests.py samba-4.22.6+dfsg/source4/selftest/tests.py
--- samba-4.22.4+dfsg/source4/selftest/tests.py	2025-06-05 18:38:33.778581100 +0300
+++ samba-4.22.6+dfsg/source4/selftest/tests.py	2025-10-16 17:34:01.681333500 +0300
@@ -902,6 +902,7 @@
 plantestsuite("samba4.blackbox.chgdcpass", "chgdcpass", [os.path.join(bbdir, "test_chgdcpass.sh"), '$SERVER', r"CHGDCPASS\$", '$REALM', '$DOMAIN', '$PREFIX/chgdcpass', "aes256-cts-hmac-sha1-96", '$PREFIX/chgdcpass', smbclient3])
 plantestsuite("samba4.blackbox.samba_upgradedns(chgdcpass:local)", "chgdcpass:local", [os.path.join(bbdir, "test_samba_upgradedns.sh"), '$SERVER', '$REALM', '$PREFIX', '$SELFTEST_PREFIX/chgdcpass'])
 plantestsuite("samba4.blackbox.net_ads", "ad_dc:client", [os.path.join(bbdir, "test_net_ads.sh"), '$DC_SERVER', '$DC_USERNAME', '$DC_PASSWORD', '$PREFIX_ABS'])
+plantestsuite("samba4.blackbox.net_ads_join", "vampire_dc:client", [os.path.join(bbdir, "test_net_ads_join_to_preferred_dc.sh"), '$DC_SERVER', '$DC_USERNAME', '$DC_PASSWORD', '$PREFIX'])
 plantestsuite("samba4.blackbox.net_offlinejoin", "ad_dc:client", [os.path.join(bbdir, "test_net_offline.sh"), '$DC_SERVER', '$DC_USERNAME', '$DC_PASSWORD', '$PREFIX_ABS'])
 plantestsuite("samba4.blackbox.client_etypes_all(ad_dc:client)", "ad_dc:client", [os.path.join(bbdir, "test_client_etypes.sh"), '$DC_SERVER', '$DC_USERNAME', '$DC_PASSWORD', '$PREFIX_ABS', 'all', '17_18_23'])
 plantestsuite("samba4.blackbox.client_etypes_legacy(ad_dc:client)", "ad_dc:client", [os.path.join(bbdir, "test_client_etypes.sh"), '$DC_SERVER', '$DC_USERNAME', '$DC_PASSWORD', '$PREFIX_ABS', 'legacy', '23'])
@@ -2237,6 +2238,7 @@
 
 planoldpythontestsuite("none", "samba.tests.usage")
 planpythontestsuite("fileserver", "samba.tests.dcerpc.mdssvc")
+planpythontestsuite("fileserver", "samba.tests.dcerpc.dfs")
 planoldpythontestsuite("none", "samba.tests.compression")
 planpythontestsuite("none", "samba.tests.security_descriptors")
 
diff -Nru samba-4.22.4+dfsg/source4/torture/nbt/wins.c samba-4.22.6+dfsg/source4/torture/nbt/wins.c
--- samba-4.22.4+dfsg/source4/torture/nbt/wins.c	2025-02-06 13:31:55.596155000 +0300
+++ samba-4.22.6+dfsg/source4/torture/nbt/wins.c	2025-10-15 15:19:02.314114800 +0300
@@ -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 @@
 					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 @@
 			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 @@
 	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;
@@ -533,6 +543,124 @@
 }
 
 /*
+ * 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
 */
 struct torture_suite *torture_nbt_wins(TALLOC_CTX *mem_ctx)
@@ -540,6 +668,8 @@
 	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 -Nru samba-4.22.4+dfsg/source4/torture/smb2/lease.c samba-4.22.6+dfsg/source4/torture/smb2/lease.c
--- samba-4.22.4+dfsg/source4/torture/smb2/lease.c	2025-06-05 18:38:33.782581000 +0300
+++ samba-4.22.6+dfsg/source4/torture/smb2/lease.c	2025-10-16 17:34:01.681333500 +0300
@@ -5835,6 +5835,104 @@
 	return ret;
 }
 
+/*
+ * Verifies the lease epoch is not incremented by the server (returns what the
+ * client sent in the request) if a lease was not granted ie lease_level=NONE.
+ */
+static bool test_lease_epoch(struct torture_context *tctx,
+			     struct smb2_tree *tree)
+{
+	struct smb2_create c;
+	struct smb2_lease ls1;
+	struct smb2_handle h1 = {};
+	struct smb2_write wr;
+	char dat = 'x';
+	DATA_BLOB data = (DATA_BLOB) {.data = (uint8_t *)&dat, .length = 1};
+	struct smb2_lock lck = {0};
+	struct smb2_lock_element el[1];
+	uint64_t lease1 = 1;
+	struct GUID create_guid = GUID_random();
+	char *fname = NULL;
+	NTSTATUS status;
+	bool ret = true;
+
+	fname = talloc_asprintf(tctx, "lease_break-%ld.dat", random());
+	torture_assert_not_null_goto(tctx, fname, ret, done,
+				     "talloc_asprintf failed\n");
+
+	c = (struct smb2_create) {
+		.in.desired_access = SEC_RIGHTS_FILE_ALL,
+		.in.share_access = NTCREATEX_SHARE_ACCESS_MASK,
+		.in.create_disposition = NTCREATEX_DISP_OPEN_IF,
+		.in.fname = fname,
+	};
+
+	status = smb2_create(tree, tctx, &c);
+	torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+					"smb2_create failed\n");
+	h1 = c.out.file.handle;
+
+	ZERO_STRUCT(wr);
+	wr.in.file.handle = h1;
+	wr.in.offset      = 0;
+	wr.in.data        = data;
+	status = smb2_write(tree, &wr);
+	torture_assert_ntstatus_ok(tctx, status, "smb2_write failed\n");
+
+	ZERO_ARRAY(el);
+	ZERO_STRUCT(lck);
+	el[0].offset = 0;
+	el[0].length = 1;
+	el[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE|SMB2_LOCK_FLAG_FAIL_IMMEDIATELY;
+	lck.in.locks = el;
+	lck.in.lock_count = 1;
+	lck.in.file.handle = h1;
+
+	status = smb2_lock(tree, &lck);
+	torture_assert_ntstatus_equal_goto(
+		tctx, status, NT_STATUS_OK,
+		ret, done, "smb2_lock failed\n");
+
+
+	smb2_lease_v2_create_share(&c,
+				   &ls1,
+				   false,
+				   fname,
+				   smb2_util_share_access("RWD"),
+				   lease1,
+				   NULL,
+				   smb2_util_lease_state("R"),
+				   100);
+	c.in.durable_open_v2 = true;
+	c.in.create_guid = create_guid;
+	status = smb2_create(tree, tree, &c);
+	torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+					"smb2_create failed\n");
+
+	torture_assert_int_equal_goto(
+		tctx,
+		c.out.oplock_level,
+		SMB2_OPLOCK_LEVEL_LEASE,
+		ret, done,
+		"Bad lease level\n");
+	torture_assert_int_equal_goto(
+		tctx,
+		c.out.lease_response_v2.lease_state,
+		0,
+		ret, done,
+		"Bad lease level\n");
+	torture_assert_int_equal_goto(
+		tctx,
+		c.out.lease_response_v2.lease_epoch,
+		100,
+		ret,
+		done,
+		"Bad lease epoch\n");
+
+done:
+	return ret;
+}
+
 struct torture_suite *torture_smb2_lease_init(TALLOC_CTX *ctx)
 {
 	struct torture_suite *suite =
@@ -5896,6 +5994,7 @@
 				     test_initial_delete_disconnect);
 	torture_suite_add_2smb2_test(suite, "rename_dir_openfile",
 				     torture_rename_dir_openfile);
+	torture_suite_add_1smb2_test(suite, "lease-epoch", test_lease_epoch);
 
 	suite->description = talloc_strdup(suite, "SMB2-LEASE tests");
 
diff -Nru samba-4.22.4+dfsg/source4/torture/vfs/fruit.c samba-4.22.6+dfsg/source4/torture/vfs/fruit.c
--- samba-4.22.4+dfsg/source4/torture/vfs/fruit.c	2025-02-06 13:31:55.640155000 +0300
+++ samba-4.22.6+dfsg/source4/torture/vfs/fruit.c	2025-10-16 17:34:01.685333700 +0300
@@ -7840,6 +7840,180 @@
 }
 
 /*
+  test exclusive byte range lock on read-only file
+*/
+static bool test_readonly_exclusive_lock(struct torture_context *tctx,
+					 struct smb2_tree *tree)
+{
+	NTSTATUS status;
+	bool ret = true;
+	struct smb2_handle h;
+	struct smb2_create create;
+	struct smb2_lock lock;
+	struct smb2_lock_element lock_element;
+	const char *fname = "readonly_lock_test.txt";
+
+	torture_comment(tctx, "Testing exclusive lock on read-only opened file\n");
+
+	ret = enable_aapl(tctx, tree);
+	torture_assert_goto(tctx, ret == true, ret, done, "enable_aapl failed");
+
+	/* Clean up any existing file */
+	smb2_util_unlink(tree, fname);
+
+	/* Create the file first with write access to ensure it exists */
+	ZERO_STRUCT(create);
+	create.in.desired_access = SEC_RIGHTS_FILE_ALL;
+	create.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
+	create.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
+				 NTCREATEX_SHARE_ACCESS_WRITE |
+				 NTCREATEX_SHARE_ACCESS_DELETE;
+	create.in.create_disposition = NTCREATEX_DISP_CREATE;
+	create.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS;
+	create.in.security_flags = 0;
+	create.in.fname = fname;
+
+	status = smb2_create(tree, tctx, &create);
+	CHECK_STATUS(status, NT_STATUS_OK);
+
+	/* Write some data to the file */
+	status = smb2_util_write(tree, create.out.file.handle, "test data", 0, 9);
+	CHECK_STATUS(status, NT_STATUS_OK);
+
+	/* Close the file */
+	status = smb2_util_close(tree, create.out.file.handle);
+	CHECK_STATUS(status, NT_STATUS_OK);
+
+	/* Now open the file read-only */
+	ZERO_STRUCT(create);
+	create.in.desired_access = SEC_FILE_READ_DATA | SEC_FILE_READ_ATTRIBUTE;
+	create.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
+	create.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
+				 NTCREATEX_SHARE_ACCESS_WRITE |
+				 NTCREATEX_SHARE_ACCESS_DELETE;
+	create.in.create_disposition = NTCREATEX_DISP_OPEN;
+	create.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS;
+	create.in.security_flags = 0;
+	create.in.fname = fname;
+
+	status = smb2_create(tree, tctx, &create);
+	CHECK_STATUS(status, NT_STATUS_OK);
+	h = create.out.file.handle;
+
+	torture_comment(tctx, "File opened read-only successfully\n");
+
+	/* Attempt to set an exclusive byte-range lock */
+	ZERO_STRUCT(lock);
+	ZERO_STRUCT(lock_element);
+
+	lock.in.lock_count = 1;
+	lock.in.lock_sequence = 0;
+	lock.in.file.handle = h;
+	lock.in.locks = &lock_element;
+
+	lock_element.offset = 0;
+	lock_element.length = 100;
+	lock_element.flags = SMB2_LOCK_FLAG_EXCLUSIVE | SMB2_LOCK_FLAG_FAIL_IMMEDIATELY;
+
+	torture_comment(tctx, "Attempting to set exclusive lock on read-only file\n");
+
+	status = smb2_lock(tree, &lock);
+	CHECK_STATUS(status, NT_STATUS_OK);
+
+done:
+	/* Close the file */
+	smb2_util_close(tree, h);
+
+	/* Clean up */
+	smb2_util_unlink(tree, fname);
+
+	return ret;
+}
+
+/*
+ * Test case-insensitive file finding with AAPL extensions
+ * Add this function to source4/torture/vfs/fruit.c
+ */
+
+static bool test_case_insensitive_find(struct torture_context *tctx,
+				       struct smb2_tree *tree)
+{
+	NTSTATUS status;
+	bool ret = true;
+	const char *fname = "TestFile.txt";
+	const char *fname_upper = "TESTFILE.TXT";
+	struct smb2_handle testdirh;
+	struct smb2_handle h1;
+	struct smb2_create create;
+	struct smb2_find f;
+	union smb_search_data *d;
+	uint_t count;
+
+	smb2_deltree(tree, BASEDIR);
+
+	status = torture_smb2_testdir(tree, BASEDIR, &testdirh);
+	torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+					"torture_smb2_testdir failed");
+
+	/* Enable AAPL extensions */
+	ret = enable_aapl(tctx, tree);
+	torture_assert_goto(tctx, ret, ret, done,
+			    "enable_aapl failed");
+
+	/* Create test file */
+	ZERO_STRUCT(create);
+	create.in.desired_access = SEC_RIGHTS_FILE_ALL;
+	create.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
+	create.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
+		NTCREATEX_SHARE_ACCESS_WRITE |
+		NTCREATEX_SHARE_ACCESS_DELETE;
+	create.in.create_disposition = NTCREATEX_DISP_CREATE;
+	create.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS;
+	create.in.fname = talloc_asprintf(tctx, "%s\\%s", BASEDIR, fname);
+
+	status = smb2_create(tree, tctx, &create);
+	torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+					talloc_asprintf(tctx, "failed to create %s", fname));
+	h1 = create.out.file.handle;
+
+	/* Close the file */
+	status = smb2_util_close(tree, h1);
+	torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+					"failed to close test file");
+
+	/* Search for file using different case */
+	f = (struct smb2_find) {
+		.in.file.handle = testdirh,
+		.in.pattern = fname_upper,
+		.in.max_response_size = 0x1000,
+		.in.level = SMB2_FIND_ID_BOTH_DIRECTORY_INFO,
+	};
+
+	status = smb2_find_level(tree, tctx, &f, &count, &d);
+	torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+					talloc_asprintf(tctx, "smb2_find_level failed searching for %s", fname_upper));
+
+	/* Verify we found exactly one file */
+	torture_assert_int_equal_goto(tctx, count, 1, ret, done,
+				      talloc_asprintf(tctx, "Expected 1 file, got %u", count));
+
+	/* Verify the filename matches our original file (case may differ) */
+	torture_assert_str_equal_goto(tctx,
+				      d[0].id_both_directory_info.name.s, fname, ret, done,
+				      talloc_asprintf(tctx, "Found file name '%s' doesn't match expected '%s'",
+						      d[0].directory_info.name.s, fname));
+
+	torture_comment(tctx, "Case-insensitive find test passed: "
+			"searched for '%s', found '%s'\n",
+			fname_upper, d[0].id_both_directory_info.name.s);
+
+done:
+	smb2_util_close(tree, testdirh);
+	smb2_deltree(tree, BASEDIR);
+	return ret;
+}
+
+/*
  * Note: This test depends on "vfs objects = catia fruit streams_xattr".  For
  * some tests torture must be run on the host it tests and takes an additional
  * argument with the local path to the share:
@@ -7885,7 +8059,8 @@
 	torture_suite_add_1smb2_test(suite, "empty_stream", test_empty_stream);
 	torture_suite_add_1smb2_test(suite, "writing_afpinfo", test_writing_afpinfo);
 	torture_suite_add_1smb2_test(suite, "delete_trigger_convert_sharing_violation", test_delete_trigger_convert_sharing_violation);
-
+	torture_suite_add_1smb2_test(suite, "readonly-exclusive-lock", test_readonly_exclusive_lock);
+	torture_suite_add_1smb2_test(suite, "case_insensitive_find", test_case_insensitive_find);
 	return suite;
 }
 
@@ -8002,7 +8177,7 @@
 
 	/* Add AD_FILELOCK_RSRC_DENY_WR lock. */
 	el = (struct smb2_lock_element) {
-		.offset = 0xfffffffffffffffc,
+		.offset = 0x7ffffffffffffffc,
 		.length = 1,
 		.flags = SMB2_LOCK_FLAG_EXCLUSIVE,
 	};
diff -Nru samba-4.22.4+dfsg/source4/torture/vfs/streams_xattr.c samba-4.22.6+dfsg/source4/torture/vfs/streams_xattr.c
--- samba-4.22.4+dfsg/source4/torture/vfs/streams_xattr.c	1970-01-01 03:00:00.000000000 +0300
+++ samba-4.22.6+dfsg/source4/torture/vfs/streams_xattr.c	2025-10-15 15:19:02.314114800 +0300
@@ -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 -Nru samba-4.22.4+dfsg/source4/torture/vfs/vfs.c samba-4.22.6+dfsg/source4/torture/vfs/vfs.c
--- samba-4.22.4+dfsg/source4/torture/vfs/vfs.c	2025-02-06 13:31:55.640155000 +0300
+++ samba-4.22.6+dfsg/source4/torture/vfs/vfs.c	2025-10-15 15:19:02.314114800 +0300
@@ -115,6 +115,7 @@
 	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_suite_add_1smb2_test(suite, "fruit_validate_afpinfo", test_fruit_validate_afpinfo);
 
 	torture_register_suite(ctx, suite);
diff -Nru samba-4.22.4+dfsg/source4/torture/wscript_build samba-4.22.6+dfsg/source4/torture/wscript_build
--- samba-4.22.4+dfsg/source4/torture/wscript_build	2025-02-06 13:31:55.640155000 +0300
+++ samba-4.22.6+dfsg/source4/torture/wscript_build	2025-10-15 15:19:02.314114800 +0300
@@ -301,7 +301,7 @@
 	)
 
 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,
diff -Nru samba-4.22.4+dfsg/testprogs/blackbox/test_net_ads_join_to_preferred_dc.sh samba-4.22.6+dfsg/testprogs/blackbox/test_net_ads_join_to_preferred_dc.sh
--- samba-4.22.4+dfsg/testprogs/blackbox/test_net_ads_join_to_preferred_dc.sh	1970-01-01 03:00:00.000000000 +0300
+++ samba-4.22.6+dfsg/testprogs/blackbox/test_net_ads_join_to_preferred_dc.sh	2025-10-16 17:34:01.685333700 +0300
@@ -0,0 +1,61 @@
+if [ $# -lt 4 ]; then
+	cat <<EOF
+Usage: test_net_ads.sh DC_SERVER DC_USERNAME DC_PASSWORD BASEDIR
+EOF
+	exit 1
+fi
+
+DC_SERVER=$1
+DC_USERNAME=$2
+DC_PASSWORD=$3
+BASEDIR=$4
+
+HOSTNAME=$(dd if=/dev/urandom bs=1 count=32 2>/dev/null | sha1sum | cut -b 1-10)
+
+RUNDIR=$(pwd)
+cd $BASEDIR
+WORKDIR=$(mktemp -d -p .)
+WORKDIR=$(basename $WORKDIR)
+cp -a client/* $WORKDIR/
+sed -ri "s@(dir|directory) = (.*)/client/@\1 = \2/$WORKDIR/@" $WORKDIR/client.conf
+sed -ri "s/netbios name = .*/netbios name = $HOSTNAME/" $WORKDIR/client.conf
+rm -f $WORKDIR/private/secrets.tdb
+cd $RUNDIR
+
+failed=0
+
+net_tool="$BINDIR/net --configfile=$BASEDIR/$WORKDIR/client.conf --option=security=ads"
+
+# Load test functions
+. $(dirname $0)/subunit.sh
+. "$(dirname "${0}")/common_test_fns.inc"
+
+# This test is run in environment with two DCs ('localdc' and 'localvampiredc')
+# The 'net ads join' has these two steps:
+#   1. create machine account at DC ('-S' points to 'localvampiredc')
+#   2. create keytab and sync the KVNO from a DC
+#
+# It must be ensured that in step #2 the keytab code contacts the same DC
+# ('localvampiredc'). The configuration below tries to break it.
+# We disable [SAF/DOMAIN/...] and [SAFJOIN/DOMAIN/...] by setting TTL to '-1'
+# And via setting 'password server' to 'localdc' we manage that
+# get_dc_list() returns 'localdc' instead of 'localvampiredc'
+#
+# As long as the keytab code is not explicitly told to use the same DC as join,
+# we get failure:
+# gensec_gse_client_prepare_ccache: Kinit for F0D26C71F6$@SAMBA.EXAMPLE.COM to access ldap/localdc.samba.example.com failed: Client not found in Kerberos database: NT_STATUS_LOGON_FAILURE
+
+cat <<EOF >>$BASEDIR/$WORKDIR/client.conf
+sync machine password to keytab = $BASEDIR/keytab:account_name:machine_password:sync_kvno
+password server = $DC_SERVER
+saf: join ttl = -1
+saf: ttl = -1
+EOF
+
+testit "join" $VALGRIND $net_tool ads join -S$SERVER -U$DC_USERNAME%$DC_PASSWORD || failed=$(expr $failed + 1)
+
+testit "leave" $VALGRIND $net_tool ads leave -U$DC_USERNAME%$DC_PASSWORD || failed=$(expr $failed + 1)
+
+rm -rf $BASEDIR/$WORKDIR
+
+exit $failed
diff -Nru samba-4.22.4+dfsg/testprogs/blackbox/wins_hook_test samba-4.22.6+dfsg/testprogs/blackbox/wins_hook_test
--- samba-4.22.4+dfsg/testprogs/blackbox/wins_hook_test	1970-01-01 03:00:00.000000000 +0300
+++ samba-4.22.6+dfsg/testprogs/blackbox/wins_hook_test	2025-10-15 15:19:02.314114800 +0300
@@ -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()


More information about the Pkg-samba-maint mailing list