Version in base suite: 4.22.4+dfsg-1~deb13u1 Base version: samba_4.22.4+dfsg-1~deb13u1 Target version: samba_4.22.6+dfsg-0+deb13u1 Base file: /srv/ftp-master.debian.org/ftp/pool/main/s/samba/samba_4.22.4+dfsg-1~deb13u1.dsc Target file: /srv/ftp-master.debian.org/policy/pool/main/s/samba/samba_4.22.6+dfsg-0+deb13u1.dsc VERSION | 4 WHATSNEW.txt | 113 ++++++++ ctdb/common/path.c | 33 +- ctdb/server/ctdbd.c | 7 ctdb/tests/README | 10 ctdb/utils/pmda/pmda_ctdb.c | 13 debian/changelog | 31 ++ docs-xml/manpages/vfs_fruit.8.xml | 33 ++ docs-xml/smbdotconf/misc/elasticsearchdefaultfields.xml | 19 + python/samba/tests/blackbox/mdsearch.py | 18 + python/samba/tests/dcerpc/dfs.py | 48 +++ python/samba/tests/dcerpc/mdssvc.py | 54 +++- python/samba/tests/libsmb.py | 1 python/samba/tests/usage.py | 2 selftest/target/Samba.pm | 1 selftest/target/Samba3.pm | 1 selftest/target/Samba4.pm | 3 source3/include/secrets.h | 25 + source3/include/vfs.h | 18 + source3/libads/ads_proto.h | 2 source3/libads/kerberos_keytab.c | 24 + source3/libads/trusts_util.c | 15 - source3/libads/util.c | 10 source3/libnet/libnet_join.c | 2 source3/libsmb/pylibsmb.c | 1 source3/modules/vfs_ceph_new.c | 12 source3/modules/vfs_fruit.c | 49 +++ source3/modules/vfs_streams_xattr.c | 5 source3/modules/vfs_xattr_tdb.c | 13 source3/passdb/machine_account_secrets.c | 10 source3/rpc_server/dfs/srv_dfs_nt.c | 4 source3/rpc_server/mdssvc/es_parser.y | 8 source3/rpc_server/mdssvc/es_parser_test.c | 3 source3/rpc_server/mdssvc/mdssvc_es.c | 58 +++- source3/rpc_server/mdssvc/mdssvc_es.h | 1 source3/rpc_server/mdssvc/test_mdsparser_es.c | 115 ++++---- source3/rpc_server/rpcd_mdssvc.c | 3 source3/selftest/tests.py | 3 source3/smbd/dir.c | 4 source3/smbd/dosmode.c | 4 source3/smbd/open.c | 5 source3/smbd/smb2_lock.c | 16 + source3/utils/net.c | 10 source3/utils/net_ads.c | 4 source4/nbt_server/wins/wins_hook.c | 9 source4/selftest/tests.py | 2 source4/torture/nbt/wins.c | 136 ++++++++++ source4/torture/smb2/lease.c | 99 +++++++ source4/torture/vfs/fruit.c | 179 +++++++++++++ source4/torture/vfs/streams_xattr.c | 211 ++++++++++++++++ source4/torture/vfs/vfs.c | 1 source4/torture/wscript_build | 2 testprogs/blackbox/test_net_ads_join_to_preferred_dc.sh | 61 ++++ testprogs/blackbox/wins_hook_test | 15 + 54 files changed, 1349 insertions(+), 181 deletions(-) diff -Nru samba-4.22.4+dfsg/VERSION samba-4.22.6+dfsg/VERSION --- samba-4.22.4+dfsg/VERSION 2025-08-21 15:22:56.183811400 +0000 +++ samba-4.22.6+dfsg/VERSION 2025-10-16 14:34:01.621333100 +0000 @@ -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/WHATSNEW.txt samba-4.22.6+dfsg/WHATSNEW.txt --- samba-4.22.4+dfsg/WHATSNEW.txt 2025-08-21 15:22:56.187811600 +0000 +++ samba-4.22.6+dfsg/WHATSNEW.txt 2025-10-16 14:34:01.621333100 +0000 @@ -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 + * 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ý + * BUG 15905: samba-4.21 fails to join AD when multiple DCs are returned + +o MikeLiu + * BUG 15900: 'net ads group' failed to list domain groups. + +o Anoop C S + * BUG 15919: vfs_ceph_new should not use ceph_ll_nonblocking_readv_writev for + fsync_send. + +o Shachar Sharon + * BUG 15919: vfs_ceph_new should not use ceph_ll_nonblocking_readv_writev for + fsync_send. + +o Andreas Schneider + * BUG 15905: samba-4.21 fails to join AD when multiple DCs are returned + +o Martin Schwenke + * 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 + * BUG 15903: CVE-2025-10230. + +o Andrew Walker + * 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/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 10:31:53.704143800 +0000 +++ samba-4.22.6+dfsg/ctdb/common/path.c 2025-10-16 14:34:01.625333300 +0000 @@ -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 10:31:53.736144000 +0000 +++ samba-4.22.6+dfsg/ctdb/server/ctdbd.c 2025-10-16 14:34:01.629333300 +0000 @@ -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 10:31:53.744144000 +0000 +++ samba-4.22.6+dfsg/ctdb/tests/README 2025-10-16 14:34:01.633333200 +0000 @@ -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 14:02:27.541217300 +0000 +++ samba-4.22.6+dfsg/ctdb/utils/pmda/pmda_ctdb.c 2025-10-16 14:34:01.633333200 +0000 @@ -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/debian/changelog samba-4.22.6+dfsg/debian/changelog --- samba-4.22.4+dfsg/debian/changelog 2025-08-21 17:37:38.000000000 +0000 +++ samba-4.22.6+dfsg/debian/changelog 2025-10-16 16:19:45.000000000 +0000 @@ -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 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/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 10:31:53.892145000 +0000 +++ samba-4.22.6+dfsg/docs-xml/manpages/vfs_fruit.8.xml 2025-10-16 14:34:01.633333200 +0000 @@ -426,6 +426,39 @@ + + fruit:posix_opens = yes | no + + + When fruit:posix_opens is set to + yes, 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. + The default is yes. + + + + + + fruit:ignore_zero_aces = yes | no + + + When fruit:ignore_zero_aces 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. + This problematic behaviour has been reported for latest + macOS versions and this new option allows to work around + it. + The default is yes. + + + + 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 00:00:00.000000000 +0000 +++ samba-4.22.6+dfsg/docs-xml/smbdotconf/misc/elasticsearchdefaultfields.xml 2025-10-16 14:34:01.633333200 +0000 @@ -0,0 +1,19 @@ + + + + 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. + + + This option expects a list of Elasticsearch attributes separated by + comma where each attributes must be enclosed in double quotes. + + + + "file.filename", "content" + "foo", "bar" + 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 10:31:54.320147500 +0000 +++ samba-4.22.6+dfsg/python/samba/tests/blackbox/mdsearch.py 2025-10-16 14:34:01.633333200 +0000 @@ -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 00:00:00.000000000 +0000 +++ samba-4.22.6+dfsg/python/samba/tests/dcerpc/dfs.py 2025-10-16 14:34:01.633333200 +0000 @@ -0,0 +1,48 @@ +# +# Unix SMB/CIFS implementation. +# Copyright Ralph Boehme 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 . +# + +"""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 10:31:54.324147500 +0000 +++ samba-4.22.6+dfsg/python/samba/tests/dcerpc/mdssvc.py 2025-10-16 14:34:01.633333200 +0000 @@ -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 10:31:54.352147600 +0000 +++ samba-4.22.6+dfsg/python/samba/tests/libsmb.py 2025-10-16 14:34:01.633333200 +0000 @@ -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 10:31:54.368147600 +0000 +++ samba-4.22.6+dfsg/python/samba/tests/usage.py 2025-10-15 12:19:02.310114900 +0000 @@ -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 10:31:54.400148000 +0000 +++ samba-4.22.6+dfsg/selftest/target/Samba.pm 2025-10-16 14:34:01.641333300 +0000 @@ -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 12:58:50.529505000 +0000 +++ samba-4.22.6+dfsg/selftest/target/Samba3.pm 2025-10-16 14:34:01.645333300 +0000 @@ -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 10:31:54.400148000 +0000 +++ samba-4.22.6+dfsg/selftest/target/Samba4.pm 2025-10-16 14:34:01.649333500 +0000 @@ -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 10:31:54.412148000 +0000 +++ samba-4.22.6+dfsg/source3/include/secrets.h 2025-10-16 14:34:01.653333400 +0000 @@ -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 10:31:54.416148000 +0000 +++ samba-4.22.6+dfsg/source3/include/vfs.h 2025-10-16 14:34:01.657333400 +0000 @@ -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 10:31:54.444148000 +0000 +++ samba-4.22.6+dfsg/source3/libads/ads_proto.h 2025-10-16 14:34:01.657333400 +0000 @@ -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 17:12:25.658450400 +0000 +++ samba-4.22.6+dfsg/source3/libads/kerberos_keytab.c 2025-10-16 14:34:01.657333400 +0000 @@ -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 10:31:54.448148300 +0000 +++ samba-4.22.6+dfsg/source3/libads/trusts_util.c 2025-10-16 14:34:01.661333600 +0000 @@ -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 10:31:54.448148300 +0000 +++ samba-4.22.6+dfsg/source3/libads/util.c 2025-10-16 14:34:01.661333600 +0000 @@ -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 10:31:54.452148200 +0000 +++ samba-4.22.6+dfsg/source3/libnet/libnet_join.c 2025-10-16 14:34:01.661333600 +0000 @@ -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 10:31:54.464148300 +0000 +++ samba-4.22.6+dfsg/source3/libsmb/pylibsmb.c 2025-10-16 14:34:01.665333500 +0000 @@ -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 17:12:25.658450400 +0000 +++ samba-4.22.6+dfsg/source3/modules/vfs_ceph_new.c 2025-10-16 14:34:01.669333500 +0000 @@ -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 17:12:25.666450500 +0000 +++ samba-4.22.6+dfsg/source3/modules/vfs_fruit.c 2025-10-16 14:34:01.669333500 +0000 @@ -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 10:31:54.488148500 +0000 +++ samba-4.22.6+dfsg/source3/modules/vfs_streams_xattr.c 2025-10-15 12:19:02.314114800 +0000 @@ -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 10:31:54.488148500 +0000 +++ samba-4.22.6+dfsg/source3/modules/vfs_xattr_tdb.c 2025-10-16 14:34:01.669333500 +0000 @@ -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 10:31:54.500148500 +0000 +++ samba-4.22.6+dfsg/source3/passdb/machine_account_secrets.c 2025-10-16 14:34:01.669333500 +0000 @@ -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 10:31:54.520148500 +0000 +++ samba-4.22.6+dfsg/source3/rpc_server/dfs/srv_dfs_nt.c 2025-10-16 14:34:01.673333600 +0000 @@ -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 10:31:54.524148700 +0000 +++ samba-4.22.6+dfsg/source3/rpc_server/mdssvc/es_parser.y 2025-10-16 14:34:01.673333600 +0000 @@ -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 10:31:54.524148700 +0000 +++ samba-4.22.6+dfsg/source3/rpc_server/mdssvc/es_parser_test.c 2025-10-16 14:34:01.673333600 +0000 @@ -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 10:31:54.524148700 +0000 +++ samba-4.22.6+dfsg/source3/rpc_server/mdssvc/mdssvc_es.c 2025-10-16 14:34:01.673333600 +0000 @@ -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 10:31:54.524148700 +0000 +++ samba-4.22.6+dfsg/source3/rpc_server/mdssvc/mdssvc_es.h 2025-10-16 14:34:01.673333600 +0000 @@ -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 10:31:54.524148700 +0000 +++ samba-4.22.6+dfsg/source3/rpc_server/mdssvc/test_mdsparser_es.c 2025-10-16 14:34:01.673333600 +0000 @@ -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 10:31:54.528148700 +0000 +++ samba-4.22.6+dfsg/source3/rpc_server/rpcd_mdssvc.c 2025-10-16 14:34:01.673333600 +0000 @@ -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 15:22:16.467915800 +0000 +++ samba-4.22.6+dfsg/source3/selftest/tests.py 2025-10-15 12:19:02.314114800 +0000 @@ -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 10:31:54.560148700 +0000 +++ samba-4.22.6+dfsg/source3/smbd/dir.c 2025-10-16 14:34:01.673333600 +0000 @@ -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 10:31:54.560148700 +0000 +++ samba-4.22.6+dfsg/source3/smbd/dosmode.c 2025-10-16 14:34:01.673333600 +0000 @@ -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 15:22:16.471915700 +0000 +++ samba-4.22.6+dfsg/source3/smbd/open.c 2025-10-16 14:34:01.677333600 +0000 @@ -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 17:12:25.674450400 +0000 +++ samba-4.22.6+dfsg/source3/smbd/smb2_lock.c 2025-10-16 14:34:01.677333600 +0000 @@ -381,8 +381,22 @@ for (i=0; ifsp_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 15:22:16.471915700 +0000 +++ samba-4.22.6+dfsg/source3/utils/net.c 2025-10-16 14:34:01.677333600 +0000 @@ -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 15:22:16.471915700 +0000 +++ samba-4.22.6+dfsg/source3/utils/net_ads.c 2025-10-16 14:34:01.677333600 +0000 @@ -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 10:31:54.720149800 +0000 +++ samba-4.22.6+dfsg/source4/nbt_server/wins/wins_hook.c 2025-10-15 12:19:02.314114800 +0000 @@ -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 15:38:33.778581100 +0000 +++ samba-4.22.6+dfsg/source4/selftest/tests.py 2025-10-16 14:34:01.681333500 +0000 @@ -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 10:31:55.596155000 +0000 +++ samba-4.22.6+dfsg/source4/torture/nbt/wins.c 2025-10-15 12:19:02.314114800 +0000 @@ -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 15:38:33.782581000 +0000 +++ samba-4.22.6+dfsg/source4/torture/smb2/lease.c 2025-10-16 14:34:01.681333500 +0000 @@ -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 10:31:55.640155000 +0000 +++ samba-4.22.6+dfsg/source4/torture/vfs/fruit.c 2025-10-16 14:34:01.685333700 +0000 @@ -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 00:00:00.000000000 +0000 +++ samba-4.22.6+dfsg/source4/torture/vfs/streams_xattr.c 2025-10-15 12:19:02.314114800 +0000 @@ -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 . +*/ + +#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 10:31:55.640155000 +0000 +++ samba-4.22.6+dfsg/source4/torture/vfs/vfs.c 2025-10-15 12:19:02.314114800 +0000 @@ -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 10:31:55.640155000 +0000 +++ samba-4.22.6+dfsg/source4/torture/wscript_build 2025-10-15 12:19:02.314114800 +0000 @@ -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 00:00:00.000000000 +0000 +++ samba-4.22.6+dfsg/testprogs/blackbox/test_net_ads_join_to_preferred_dc.sh 2025-10-16 14:34:01.685333700 +0000 @@ -0,0 +1,61 @@ +if [ $# -lt 4 ]; then + cat </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 <>$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 00:00:00.000000000 +0000 +++ samba-4.22.6+dfsg/testprogs/blackbox/wins_hook_test 2025-10-15 12:19:02.314114800 +0000 @@ -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()