The branch, v4-22-test has been updated
       via  6014ff20ce9 VERSION: Bump version up to Samba 4.22.6...
       via  463d7a0e3f6 Merge tag 'samba-4.22.5' into v4-22-test
       via  9f4a4c030de VERSION: Disable GIT_SNAPSHOT for the 4.22.5 release.
       via  5a70240cef7 WHATSNEW: Add release notes for Samba 4.22.5.
       via  06bc23b5977 CVE-2025-9640: s3/modules/vfs_streams_xattr fix 
unitialized write
       via  44d71234dff CVE-2025-9640: Add torture test for inserting hole in 
stream
       via  540197b92d0 CVE-2025-10230: s4:wins: restrict names fed to shell
       via  af58459f951 CVE-2025-10230: s4/tests: check that wins hook 
sanitizes names
       via  16e212a85c1 VERSION: Bump version up to Samba 4.22.5...
      from  ad38c984950 ctdb-common: Only respect CTDB_SOCKET in CTDB_TEST_MODE

https://git.samba.org/?p=samba.git;a=shortlog;h=v4-22-test


- Log -----------------------------------------------------------------
commit 6014ff20ce973d5a8475cfb27e1b374ac836838b
Author: Jule Anger <[email protected]>
Date:   Wed Oct 15 15:09:04 2025 +0200

    VERSION: Bump version up to Samba 4.22.6...
    
    and re-enable GIT_SNAPSHOT.
    
    Signed-off-by: Jule Anger <[email protected]>

commit 463d7a0e3f6f6c4e26233aaf11a397bc2a5c0068
Merge: ad38c984950 9f4a4c030de
Author: Jule Anger <[email protected]>
Date:   Wed Oct 15 15:03:17 2025 +0200

    Merge tag 'samba-4.22.5' into v4-22-test
    
    samba: tag release samba-4.22.5

-----------------------------------------------------------------------

Summary of changes:
 VERSION                             |   4 +-
 WHATSNEW.txt                        |  51 ++++++++-
 python/samba/tests/usage.py         |   2 +
 selftest/target/Samba4.pm           |   1 +
 source3/modules/vfs_streams_xattr.c |   5 +-
 source3/selftest/tests.py           |   3 +
 source4/nbt_server/wins/wins_hook.c |   9 ++
 source4/torture/nbt/wins.c          | 136 ++++++++++++++++++++++-
 source4/torture/vfs/streams_xattr.c | 211 ++++++++++++++++++++++++++++++++++++
 source4/torture/vfs/vfs.c           |   1 +
 source4/torture/wscript_build       |   2 +-
 testprogs/blackbox/wins_hook_test   |  15 +++
 12 files changed, 431 insertions(+), 9 deletions(-)
 create mode 100644 source4/torture/vfs/streams_xattr.c
 create mode 100755 testprogs/blackbox/wins_hook_test


Changeset truncated at 500 lines:

diff --git a/VERSION b/VERSION
index 94a1bac2237..638939edd54 100644
--- a/VERSION
+++ b/VERSION
@@ -27,7 +27,7 @@ SAMBA_COPYRIGHT_STRING="Copyright Andrew Tridgell and the 
Samba Team 1992-2025"
 ########################################################
 SAMBA_VERSION_MAJOR=4
 SAMBA_VERSION_MINOR=22
-SAMBA_VERSION_RELEASE=5
+SAMBA_VERSION_RELEASE=6
 
 ########################################################
 # If a official release has a serious bug              #
@@ -101,7 +101,7 @@ SAMBA_VERSION_RC_RELEASE=
 # e.g. SAMBA_VERSION_IS_SVN_SNAPSHOT=yes               #
 #  ->  "3.0.0-SVN-build-199"                           #
 ########################################################
-SAMBA_VERSION_IS_GIT_SNAPSHOT=yes
+SAMBA_VERSION_IS_GIT_SNAPSHOT=no
 
 ########################################################
 # This is for specifying a release nickname            #
diff --git a/WHATSNEW.txt b/WHATSNEW.txt
index fcbc3a8b524..54235056f10 100644
--- a/WHATSNEW.txt
+++ b/WHATSNEW.txt
@@ -1,3 +1,51 @@
+                   ==============================
+                   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 <[email protected]>
+   * BUG 15903: CVE-2025-10230.
+
+o  Andrew Walker <[email protected]>
+   * 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 older releases follow:
+----------------------------------------
                    ==============================
                    Release Notes for Samba 4.22.4
                           August 21, 2025
@@ -66,8 +114,7 @@ database (https://bugzilla.samba.org/).
 ======================================================================
 
 
-Release notes for older releases follow:
-----------------------------------------
+----------------------------------------------------------------------
                    ==============================
                    Release Notes for Samba 4.22.3
                            July 07, 2025
diff --git a/python/samba/tests/usage.py b/python/samba/tests/usage.py
index eb43bba64f4..dae71ecfda8 100644
--- a/python/samba/tests/usage.py
+++ b/python/samba/tests/usage.py
@@ -73,6 +73,7 @@ EXCLUDE_USAGE = {
     'lib/ldb/tests/python/api.py',
     'source4/selftest/tests.py',
     'buildtools/bin/waf',
+    'testprogs/blackbox/wins_hook_test',
     'selftest/tap2subunit',
     'script/show_test_time',
     'source4/scripting/bin/subunitrun',
@@ -89,6 +90,7 @@ EXCLUDE_HELP = {
     'selftest/tap2subunit',
     'wintest/test-s3.py',
     'wintest/test-s4-howto.py',
+    'testprogs/blackbox/wins_hook_test',
 }
 
 
diff --git a/selftest/target/Samba4.pm b/selftest/target/Samba4.pm
index af0434a8e6b..c63ad093681 100755
--- a/selftest/target/Samba4.pm
+++ b/selftest/target/Samba4.pm
@@ -1637,6 +1637,7 @@ sub provision_ad_dc_ntvfs($$$)
        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 --git a/source3/modules/vfs_streams_xattr.c 
b/source3/modules/vfs_streams_xattr.c
index 7601e744198..3d3ef7edb1d 100644
--- a/source3/modules/vfs_streams_xattr.c
+++ b/source3/modules/vfs_streams_xattr.c
@@ -1047,15 +1047,18 @@ static ssize_t streams_xattr_pwrite(vfs_handle_struct 
*handle,
 
         if ((offset + n) > ea.value.length-1) {
                uint8_t *tmp;
+               size_t new_sz = offset + n + 1;
 
                tmp = talloc_realloc(talloc_tos(), ea.value.data, uint8_t,
-                                          offset + n + 1);
+                                          new_sz);
 
                if (tmp == NULL) {
                        TALLOC_FREE(ea.value.data);
                         errno = ENOMEM;
                         return -1;
                 }
+
+               memset(tmp + ea.value.length, 0, new_sz - ea.value.length);
                ea.value.data = tmp;
                ea.value.length = offset + n + 1;
                ea.value.data[offset+n] = 0;
diff --git a/source3/selftest/tests.py b/source3/selftest/tests.py
index e4c897cd1da..db549f4cf1b 100755
--- a/source3/selftest/tests.py
+++ b/source3/selftest/tests.py
@@ -1152,6 +1152,7 @@ nbt = ["nbt.dgram"]
 vfs = [
     "vfs.fruit",
     "vfs.acl_xattr",
+    "vfs.streams_xattr",
     "vfs.fruit_netatalk",
     "vfs.fruit_file_id",
     "vfs.fruit_timemachine",
@@ -1347,6 +1348,8 @@ for t in tests:
             plansmbtorture4testsuite(t, "fileserver", '//$SERVER_IP/tmp 
-U$USERNAME%$PASSWORD')
     elif t == "vfs.acl_xattr":
         plansmbtorture4testsuite(t, "nt4_dc", '//$SERVER_IP/tmp 
-U$USERNAME%$PASSWORD')
+    elif t == "vfs.streams_xattr":
+        plansmbtorture4testsuite(t, "nt4_dc", '//$SERVER_IP/vfs_wo_fruit 
-U$USERNAME%$PASSWORD')
     elif t == "smb2.compound_find":
         plansmbtorture4testsuite(t, "fileserver", '//$SERVER/compound_find 
-U$USERNAME%$PASSWORD')
         plansmbtorture4testsuite(t, "fileserver", '//$SERVER_IP/tmp 
-U$USERNAME%$PASSWORD')
diff --git a/source4/nbt_server/wins/wins_hook.c 
b/source4/nbt_server/wins/wins_hook.c
index 1af471b15bc..442141fecdd 100644
--- a/source4/nbt_server/wins/wins_hook.c
+++ b/source4/nbt_server/wins/wins_hook.c
@@ -43,9 +43,18 @@ void wins_hook(struct winsdb_handle *h, const struct 
winsdb_record *rec,
        int child;
        char *cmd = NULL;
        TALLOC_CTX *tmp_mem = NULL;
+       const char *p = NULL;
 
        if (!wins_hook_script || !wins_hook_script[0]) return;
 
+       for (p = rec->name->name; *p; p++) {
+               if (!(isalnum((int)*p) || strchr_m("._-", *p))) {
+                       DBG_ERR("not calling wins hook for invalid name %s\n",
+                               rec->name->name);
+                       return;
+               }
+       }
+
        tmp_mem = talloc_new(h);
        if (!tmp_mem) goto failed;
 
diff --git a/source4/torture/nbt/wins.c b/source4/torture/nbt/wins.c
index 8c847b5ac50..7d7321752d6 100644
--- a/source4/torture/nbt/wins.c
+++ b/source4/torture/nbt/wins.c
@@ -31,6 +31,10 @@
 #include "torture/nbt/proto.h"
 #include "param/param.h"
 
+/* rcode used when you don't want to check the rcode */
+#define WINS_TEST_RCODE_WE_DONT_CARE 255
+
+
 #define CHECK_VALUE(tctx, v, correct) \
        torture_assert_int_equal(tctx, v, correct, "Incorrect value")
 
@@ -137,7 +141,9 @@ static bool nbt_test_wins_name(struct torture_context 
*tctx, const char *address
                                        address));
 
                CHECK_STRING(tctx, io.out.wins_server, address);
-               CHECK_VALUE(tctx, io.out.rcode, 0);
+               if (register_rcode != WINS_TEST_RCODE_WE_DONT_CARE) {
+                       CHECK_VALUE(tctx, io.out.rcode, 0);
+               }
 
                torture_comment(tctx, "register the name correct address\n");
                name_register.in.name           = *name;
@@ -185,7 +191,9 @@ static bool nbt_test_wins_name(struct torture_context 
*tctx, const char *address
                        talloc_asprintf(tctx, "Bad response from %s for name 
register\n",
                                        address));
 
-               CHECK_VALUE(tctx, name_register.out.rcode, 0);
+               if (register_rcode != WINS_TEST_RCODE_WE_DONT_CARE) {
+                       CHECK_VALUE(tctx, name_register.out.rcode, 0);
+               }
                CHECK_STRING(tctx, name_register.out.reply_addr, myaddress);
        }
 
@@ -203,7 +211,9 @@ static bool nbt_test_wins_name(struct torture_context 
*tctx, const char *address
        torture_assert_ntstatus_ok(tctx, status, talloc_asprintf(tctx, "Bad 
response from %s for name register", address));
        
        CHECK_STRING(tctx, io.out.wins_server, address);
-       CHECK_VALUE(tctx, io.out.rcode, register_rcode);
+       if (register_rcode != WINS_TEST_RCODE_WE_DONT_CARE) {
+               CHECK_VALUE(tctx, io.out.rcode, register_rcode);
+       }
 
        if (register_rcode != NBT_RCODE_OK) {
                return true;
@@ -532,6 +542,124 @@ static bool nbt_test_wins(struct torture_context *tctx)
        return ret;
 }
 
+/*
+ * Test that the WINS server does not call 'wins hook' when the name
+ * contains dodgy characters.
+ */
+static bool nbt_test_wins_bad_names(struct torture_context *tctx)
+{
+       const char *address = NULL;
+       const char *wins_hook_file = NULL;
+       bool ret = true;
+       int err;
+       bool ok;
+       struct nbt_name name = {};
+       size_t i, j;
+       FILE *fh = NULL;
+
+       struct {
+               const char *name;
+               bool should_succeed;
+       } test_cases[] = {
+               {"NORMAL", true},
+               {"|look|", false},
+               {"look&true", false},
+               {"look\\;false", false},
+               {"&ls>foo", false},  /* already fails due to DN syntax */
+               {"has spaces", false},
+               {"hyphen-dot.0", true},
+       };
+
+       wins_hook_file = talloc_asprintf(tctx, "%s/wins_hook_writes_here",
+                                        getenv("SELFTEST_TMPDIR"));
+
+       if (!torture_nbt_get_name(tctx, &name, &address)) {
+               return false;
+       }
+
+       for (i = 0; i < ARRAY_SIZE(test_cases); i++) {
+               err =  unlink(wins_hook_file);
+               if (err != 0 && errno != ENOENT) {
+                       /* we expect ENOENT, but nothing else */
+                       torture_comment(tctx,
+                                       "unlink %zu of '%s' failed\n",
+                                       i, wins_hook_file);
+               }
+
+               name.name = test_cases[i].name;
+               name.type = NBT_NAME_CLIENT;
+               ok = nbt_test_wins_name(tctx, address,
+                                       &name,
+                                       NBT_NODE_H,
+                                       true,
+                                       WINS_TEST_RCODE_WE_DONT_CARE
+                       );
+               if (ok == false) {
+                       /*
+                        * This happens when the name interferes with
+                        * the DN syntax when it is put in winsdb.
+                        *
+                        * The wins hook will not be reached.
+                        */
+                       torture_comment(tctx, "tests for '%s' failed\n",
+                                       name.name);
+               }
+
+               /*
+                * poll for the file being created by the wins hook.
+                */
+               for (j = 0; j < 10; j++) {
+                       usleep(200000);
+                       fh = fopen(wins_hook_file, "r");
+                       if (fh != NULL) {
+                               break;
+                       }
+               }
+
+               if (fh == NULL) {
+                       if (errno == ENOENT) {
+                               if (test_cases[i].should_succeed) {
+                                       torture_comment(
+                                               tctx,
+                                               "wins hook for '%s' failed\n",
+                                               test_cases[i].name);
+                                       ret = false;
+                               }
+                       } else {
+                               torture_comment(
+                                       tctx,
+                                       "wins hook for '%s' unexpectedly failed 
with %d\n",
+                                       test_cases[i].name,
+                                       errno);
+                               ret = false;
+                       }
+               } else {
+                       char readback[17] = {0};
+                       size_t n = fread(readback, 1, 16, fh);
+                       torture_comment(tctx,
+                                       "wins hook wrote '%s' read '%.*s'\n",
+                                       test_cases[i].name,
+                                       (int)n, readback);
+
+                       if (! test_cases[i].should_succeed) {
+                               torture_comment(tctx,
+                                               "wins hook for '%s' should 
fail\n",
+                                               test_cases[i].name);
+                               ret = false;
+                       }
+                       fclose(fh);
+               }
+       }
+       err = unlink(wins_hook_file);
+       if (err != 0 && errno != ENOENT) {
+               torture_comment(tctx, "final unlink of '%s' failed\n",
+                               wins_hook_file);
+       }
+       torture_assert(tctx, ret, "wins hook failure\n");
+       return ret;
+}
+
+
 /*
   test WINS operations
 */
@@ -540,6 +668,8 @@ struct torture_suite *torture_nbt_wins(TALLOC_CTX *mem_ctx)
        struct torture_suite *suite = torture_suite_create(mem_ctx, "wins");
 
        torture_suite_add_simple_test(suite, "wins", nbt_test_wins);
+       torture_suite_add_simple_test(suite, "wins_bad_names",
+                                     nbt_test_wins_bad_names);
 
        return suite;
 }
diff --git a/source4/torture/vfs/streams_xattr.c 
b/source4/torture/vfs/streams_xattr.c
new file mode 100644
index 00000000000..0eb83e092e7
--- /dev/null
+++ b/source4/torture/vfs/streams_xattr.c
@@ -0,0 +1,211 @@
+/*
+   Unix SMB/CIFS implementation.
+
+   Copyright (C) Andrew Walker (2025)
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "lib/cmdline/cmdline.h"
+#include "libcli/smb2/smb2.h"
+#include "libcli/smb2/smb2_calls.h"
+#include "libcli/smb/smbXcli_base.h"
+#include "torture/torture.h"
+#include "torture/vfs/proto.h"
+#include "libcli/resolve/resolve.h"
+#include "torture/util.h"
+#include "torture/smb2/proto.h"
+#include "lib/param/param.h"
+
+#define BASEDIR "smb2-testads"
+
+
+static bool get_stream_handle(struct torture_context *tctx,
+                             struct smb2_tree *tree,
+                             const char *dname,
+                             const char *fname,
+                             const char *sname,
+                             struct smb2_handle *hdl_in)
+{
+       bool ret = true;
+       NTSTATUS status;
+       struct smb2_handle fhandle = {{0}};
+       struct smb2_handle dhandle = {{0}};
+
+       torture_comment(tctx, "Create dir\n");
+
+       status = torture_smb2_testdir(tree, dname, &dhandle);
+       torture_assert_ntstatus_ok_goto(tctx, status, ret, done, 
"torture_smb2_testdir\n");
+
+       torture_comment(tctx, "Create file\n");
+
+       status = torture_smb2_testfile(tree, fname, &fhandle);
+       torture_assert_ntstatus_ok_goto(tctx, status, ret, done, 
"torture_smb2_testfile\n");
+
+       status = torture_smb2_testfile(tree, sname, hdl_in);
+       torture_assert_ntstatus_ok_goto(tctx, status, ret, done, 
"torture_smb2_testfile\n");
+
+done:
+       if (!smb2_util_handle_empty(fhandle)) {
+               smb2_util_close(tree, fhandle);
+       }
+       if (!smb2_util_handle_empty(dhandle)) {
+               smb2_util_close(tree, dhandle);
+       }
+       return ret;
+}
+
+static bool read_stream(struct torture_context *tctx,
+                       TALLOC_CTX *mem_ctx,
+                       struct smb2_tree *tree,
+                       struct smb2_handle *stream_hdl,
+                       off_t read_offset,
+                       size_t read_count,
+                       char **data_out,
+                       size_t *data_out_sz)
+{
+       NTSTATUS status;
+       struct smb2_read r;
+       bool ret = true;
+
+       ZERO_STRUCT(r);
+       r.in.file.handle = *stream_hdl;
+       r.in.length = read_count;
+       r.in.offset = read_offset;
+
+       status = smb2_read(tree, mem_ctx, &r);
+       torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "stream 
read\n");
+
+       *data_out = (char *)r.out.data.data;
+       *data_out_sz = r.out.data.length;
+
+done:
+       return ret;
+}
+
+
+#define WRITE_PAYLOAD "canary"
+#define ADS_LEN 1024
+#define ADS_OFF_TAIL ADS_LEN - sizeof(WRITE_PAYLOAD)
+
+static bool test_streams_pwrite_hole(struct torture_context *tctx,
+                                    struct smb2_tree *tree)
+{
+       NTSTATUS status;
+       bool ok;
+       bool ret = true;
+       const char *dname = BASEDIR "\\testdir";
+       const char *fname = BASEDIR "\\testdir\\testfile";
+       const char *sname = BASEDIR "\\testdir\\testfile:test_stream";
+       const char *canary = "canary";
+       struct smb2_handle shandle = {{0}};
+       TALLOC_CTX *tmp_ctx = NULL;
+       char *data = NULL;
+       size_t data_sz, i;
+
+       ok = smb2_util_setup_dir(tctx, tree, BASEDIR);
+       torture_assert_goto(tctx, ok == true, ret, done, "Unable to setup 
testdir\n");
+
+       tmp_ctx = talloc_new(tree);
+       torture_assert_goto(tctx, tmp_ctx != NULL, ret, done, "Memory 
failure\n");
+
+       ok = get_stream_handle(tctx, tree, dname, fname, sname, &shandle);


-- 
Samba Shared Repository

Reply via email to