The branch, v4-23-test has been updated
via 97002261e50 VERSION: Bump version up to Samba 4.23.3...
via beb9001eecf VERSION: Disable GIT_SNAPSHOT for the 4.23.2 release.
via ea4dc4bde89 WHATSNEW: Add release notes for Samba 4.23.2.
via 26b3ff3752e CVE-2025-9640: s3/modules/vfs_streams_xattr fix
unitialized write
via 25cad91bcf1 CVE-2025-9640: Add torture test for inserting hole in
stream
via 0d8929e1544 CVE-2025-10230: s4:wins: restrict names fed to shell
via 8a92384c09d CVE-2025-10230: s4/tests: check that wins hook
sanitizes names
from cf1397c210b VERSION: Bump version up to Samba 4.23.2...
https://git.samba.org/?p=samba.git;a=shortlog;h=v4-23-test
- Log -----------------------------------------------------------------
commit 97002261e50044ebb9b36ea5e851dc7e913e1da0
Author: Jule Anger <[email protected]>
Date: Wed Oct 15 14:59:30 2025 +0200
VERSION: Bump version up to Samba 4.23.3...
and re-enable GIT_SNAPSHOT.
Signed-off-by: Jule Anger <[email protected]>
-----------------------------------------------------------------------
Summary of changes:
VERSION | 2 +-
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, 430 insertions(+), 8 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 0137426f3d8..2d0f4d04f04 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=23
-SAMBA_VERSION_RELEASE=2
+SAMBA_VERSION_RELEASE=3
########################################################
# If a official release has a serious bug #
diff --git a/WHATSNEW.txt b/WHATSNEW.txt
index 0546513b94f..4d729e939a3 100644
--- a/WHATSNEW.txt
+++ b/WHATSNEW.txt
@@ -1,3 +1,51 @@
+ ==============================
+ Release Notes for Samba 4.23.2
+ 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.23.1
+--------------------
+
+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.23.1
September 26, 2025
@@ -52,8 +100,7 @@ database (https://bugzilla.samba.org/).
======================================================================
-Release notes for older releases follow:
-----------------------------------------
+----------------------------------------------------------------------
==============================
Release Notes for Samba 4.23.0
September 12, 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 8d30fefbab2..5930877aa2a 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 ac01cc46043..58e5b1eb189 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 dad58fca5f2..efba899a920 100755
--- a/source3/selftest/tests.py
+++ b/source3/selftest/tests.py
@@ -1163,6 +1163,7 @@ nbt = ["nbt.dgram"]
vfs = [
"vfs.fruit",
"vfs.acl_xattr",
+ "vfs.streams_xattr",
"vfs.fruit_netatalk",
"vfs.fruit_file_id",
"vfs.fruit_timemachine",
@@ -1359,6 +1360,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);
+ 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
+ */
--
Samba Shared Repository