From af58459f9519a9b03ada8ec6a3d85331e1a94a7a Mon Sep 17 00:00:00 2001 From: Douglas Bagnall Date: Tue, 9 Sep 2025 13:36:16 +1200 Subject: [PATCH 1/4] CVE-2025-10230: s4/tests: check that wins hook sanitizes names An smb.conf can contain a 'wins hook' parameter, which names a script to run when a WINS name is changed. The man page says The second argument is the NetBIOS name. If the name is not a legal name then the wins hook is not called. Legal names contain only letters, digits, hyphens, underscores and periods. but it turns out the legality check is not performed if the WINS server in question is the source4 nbt one. It is not expected that people will run this server, but they can. This is bad because the name is passed unescaped into a shell command line, allowing command injection. For this test we don't care whether the WINS server is returning an error code, just whether it is running the wins hook. The tests show it often runs the hook it shouldn't, though some characters are incidentally blocked because the name has to fit in a DN before it gets to the hook, and DNs have a few syntactic restrictions (e.g., blocking '<', '>', and ';'). The source3 WINS server that is used by Samba when not run as a DC is not affected and not here tested. BUG: https://bugzilla.samba.org/show_bug.cgi?id=15903 Signed-off-by: Douglas Bagnall Reviewed-by: Gary Lockyer --- python/samba/tests/usage.py | 2 + .../samba4.nbt.wins.wins_bad_names | 1 + selftest/target/Samba4.pm | 1 + source4/torture/nbt/wins.c | 136 +++++++++++++++++- testprogs/blackbox/wins_hook_test | 15 ++ 5 files changed, 152 insertions(+), 3 deletions(-) create mode 100644 selftest/knownfail.d/samba4.nbt.wins.wins_bad_names create mode 100755 testprogs/blackbox/wins_hook_test diff --git a/python/samba/tests/usage.py b/python/samba/tests/usage.py index 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/knownfail.d/samba4.nbt.wins.wins_bad_names b/selftest/knownfail.d/samba4.nbt.wins.wins_bad_names new file mode 100644 index 00000000000..52388ce5749 --- /dev/null +++ b/selftest/knownfail.d/samba4.nbt.wins.wins_bad_names @@ -0,0 +1 @@ +samba4.nbt.wins.wins_bad_names diff --git a/selftest/target/Samba4.pm b/selftest/target/Samba4.pm index 9da339f6239..fe557ca655a 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/source4/torture/nbt/wins.c b/source4/torture/nbt/wins.c index 8c847b5ac50..7d7321752d6 100644 --- a/source4/torture/nbt/wins.c +++ b/source4/torture/nbt/wins.c @@ -31,6 +31,10 @@ #include "torture/nbt/proto.h" #include "param/param.h" +/* rcode used when you don't want to check the rcode */ +#define WINS_TEST_RCODE_WE_DONT_CARE 255 + + #define CHECK_VALUE(tctx, v, correct) \ torture_assert_int_equal(tctx, v, correct, "Incorrect value") @@ -137,7 +141,9 @@ static bool nbt_test_wins_name(struct torture_context *tctx, const char *address address)); CHECK_STRING(tctx, io.out.wins_server, address); - CHECK_VALUE(tctx, io.out.rcode, 0); + if (register_rcode != WINS_TEST_RCODE_WE_DONT_CARE) { + CHECK_VALUE(tctx, io.out.rcode, 0); + } torture_comment(tctx, "register the name correct address\n"); name_register.in.name = *name; @@ -185,7 +191,9 @@ static bool nbt_test_wins_name(struct torture_context *tctx, const char *address talloc_asprintf(tctx, "Bad response from %s for name register\n", address)); - CHECK_VALUE(tctx, name_register.out.rcode, 0); + if (register_rcode != WINS_TEST_RCODE_WE_DONT_CARE) { + CHECK_VALUE(tctx, name_register.out.rcode, 0); + } CHECK_STRING(tctx, name_register.out.reply_addr, myaddress); } @@ -203,7 +211,9 @@ static bool nbt_test_wins_name(struct torture_context *tctx, const char *address torture_assert_ntstatus_ok(tctx, status, talloc_asprintf(tctx, "Bad response from %s for name register", address)); CHECK_STRING(tctx, io.out.wins_server, address); - CHECK_VALUE(tctx, io.out.rcode, register_rcode); + if (register_rcode != WINS_TEST_RCODE_WE_DONT_CARE) { + CHECK_VALUE(tctx, io.out.rcode, register_rcode); + } if (register_rcode != NBT_RCODE_OK) { return true; @@ -532,6 +542,124 @@ static bool nbt_test_wins(struct torture_context *tctx) return ret; } +/* + * Test that the WINS server does not call 'wins hook' when the name + * contains dodgy characters. + */ +static bool nbt_test_wins_bad_names(struct torture_context *tctx) +{ + const char *address = NULL; + const char *wins_hook_file = NULL; + bool ret = true; + int err; + bool ok; + struct nbt_name name = {}; + size_t i, j; + FILE *fh = NULL; + + struct { + const char *name; + bool should_succeed; + } test_cases[] = { + {"NORMAL", true}, + {"|look|", false}, + {"look&true", false}, + {"look\\;false", false}, + {"&ls>foo", false}, /* already fails due to DN syntax */ + {"has spaces", false}, + {"hyphen-dot.0", true}, + }; + + wins_hook_file = talloc_asprintf(tctx, "%s/wins_hook_writes_here", + getenv("SELFTEST_TMPDIR")); + + if (!torture_nbt_get_name(tctx, &name, &address)) { + return false; + } + + for (i = 0; i < ARRAY_SIZE(test_cases); i++) { + err = unlink(wins_hook_file); + if (err != 0 && errno != ENOENT) { + /* we expect ENOENT, but nothing else */ + torture_comment(tctx, + "unlink %zu of '%s' failed\n", + i, wins_hook_file); + } + + name.name = test_cases[i].name; + name.type = NBT_NAME_CLIENT; + ok = nbt_test_wins_name(tctx, address, + &name, + NBT_NODE_H, + true, + WINS_TEST_RCODE_WE_DONT_CARE + ); + if (ok == false) { + /* + * This happens when the name interferes with + * the DN syntax when it is put in winsdb. + * + * The wins hook will not be reached. + */ + torture_comment(tctx, "tests for '%s' failed\n", + name.name); + } + + /* + * poll for the file being created by the wins hook. + */ + for (j = 0; j < 10; j++) { + usleep(200000); + fh = fopen(wins_hook_file, "r"); + if (fh != NULL) { + break; + } + } + + if (fh == NULL) { + if (errno == ENOENT) { + if (test_cases[i].should_succeed) { + torture_comment( + tctx, + "wins hook for '%s' failed\n", + test_cases[i].name); + ret = false; + } + } else { + torture_comment( + tctx, + "wins hook for '%s' unexpectedly failed with %d\n", + test_cases[i].name, + errno); + ret = false; + } + } else { + char readback[17] = {0}; + size_t n = fread(readback, 1, 16, fh); + torture_comment(tctx, + "wins hook wrote '%s' read '%.*s'\n", + test_cases[i].name, + (int)n, readback); + + if (! test_cases[i].should_succeed) { + torture_comment(tctx, + "wins hook for '%s' should fail\n", + test_cases[i].name); + ret = false; + } + fclose(fh); + } + } + err = unlink(wins_hook_file); + if (err != 0 && errno != ENOENT) { + torture_comment(tctx, "final unlink of '%s' failed\n", + wins_hook_file); + } + torture_assert(tctx, ret, "wins hook failure\n"); + return ret; +} + + /* test WINS operations */ @@ -540,6 +668,8 @@ struct torture_suite *torture_nbt_wins(TALLOC_CTX *mem_ctx) struct torture_suite *suite = torture_suite_create(mem_ctx, "wins"); torture_suite_add_simple_test(suite, "wins", nbt_test_wins); + torture_suite_add_simple_test(suite, "wins_bad_names", + nbt_test_wins_bad_names); return suite; } diff --git a/testprogs/blackbox/wins_hook_test b/testprogs/blackbox/wins_hook_test new file mode 100755 index 00000000000..f15379c28ca --- /dev/null +++ b/testprogs/blackbox/wins_hook_test @@ -0,0 +1,15 @@ +#!/usr/bin/python3 + +import os +import sys + +filename = f"{os.environ['SELFTEST_TMPDIR']}/wins_hook_writes_here" + +f = open(filename, 'wb') + +# Some names may truncate argv (e.g. '&'), for which we leave the file +# empty. +if len(sys.argv) > 2: + f.write(os.fsencode(sys.argv[2])) + +f.close() -- 2.34.1 From 540197b92d096b3fdfc66c5103e1a691866d7a6d Mon Sep 17 00:00:00 2001 From: Douglas Bagnall Date: Wed, 3 Sep 2025 14:20:24 +1200 Subject: [PATCH 2/4] CVE-2025-10230: s4:wins: restrict names fed to shell If the "wins hook" smb.conf parameter is set, the WINS server will attempt to execute that value in a shell command line when a client asks to modify a name. The WINS system is a trusting one, and clients can claim any NETBIOS name they wish. With the source3 nmbd WINS server (since the 1999 commit now called 3db52feb1f3b2c07ce0b06ad4a7099fa6efe3fc7) the wins hook will not be run for names that contain shell metacharacters. This restriction has not been present on the source4 nbt WINS server, which is the WINS server that will be used in the event that an Active Directory Domain Controller is also running WINS. This allowed an unauthenticated client to execute arbitrary commands on the server. This commit brings the nmbd check into the nbt WINS server, so that the wins hook will only be run for names that contain only letters, digits, hyphens, underscores and periods. This matches the behaviour described in the smb.conf man page. The source3 nmbd WINS server has another layer of protection, in that it uses the smb_run() exec wrapper that tries to escape arguments. We don't do that here. BUG: https://bugzilla.samba.org/show_bug.cgi?id=15903 Signed-off-by: Douglas Bagnall Reviewed-by: Gary Lockyer --- selftest/knownfail.d/samba4.nbt.wins.wins_bad_names | 1 - source4/nbt_server/wins/wins_hook.c | 9 +++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) delete mode 100644 selftest/knownfail.d/samba4.nbt.wins.wins_bad_names diff --git a/selftest/knownfail.d/samba4.nbt.wins.wins_bad_names b/selftest/knownfail.d/samba4.nbt.wins.wins_bad_names deleted file mode 100644 index 52388ce5749..00000000000 --- a/selftest/knownfail.d/samba4.nbt.wins.wins_bad_names +++ /dev/null @@ -1 +0,0 @@ -samba4.nbt.wins.wins_bad_names diff --git a/source4/nbt_server/wins/wins_hook.c b/source4/nbt_server/wins/wins_hook.c index 1af471b15bc..442141fecdd 100644 --- a/source4/nbt_server/wins/wins_hook.c +++ b/source4/nbt_server/wins/wins_hook.c @@ -43,9 +43,18 @@ void wins_hook(struct winsdb_handle *h, const struct winsdb_record *rec, int child; char *cmd = NULL; TALLOC_CTX *tmp_mem = NULL; + const char *p = NULL; if (!wins_hook_script || !wins_hook_script[0]) return; + for (p = rec->name->name; *p; p++) { + if (!(isalnum((int)*p) || strchr_m("._-", *p))) { + DBG_ERR("not calling wins hook for invalid name %s\n", + rec->name->name); + return; + } + } + tmp_mem = talloc_new(h); if (!tmp_mem) goto failed; -- 2.34.1 From 44d71234dfffa52ad2579924813b67c9d8a37822 Mon Sep 17 00:00:00 2001 From: Andrew Walker Date: Thu, 28 Aug 2025 19:39:34 +0000 Subject: [PATCH 3/4] CVE-2025-9640: Add torture test for inserting hole in stream This commit adds an smb torture test for inserting a hole into an alternate data stream and then verifying that hole contains null bytes. BUG: https://bugzilla.samba.org/show_bug.cgi?id=15885 Signed-off-by: Andrew Walker Reviewed-by: Volker Lendecke --- source3/selftest/tests.py | 3 + source4/torture/vfs/streams_xattr.c | 211 ++++++++++++++++++++++++++++ source4/torture/vfs/vfs.c | 1 + source4/torture/wscript_build | 2 +- 4 files changed, 216 insertions(+), 1 deletion(-) create mode 100644 source4/torture/vfs/streams_xattr.c diff --git a/source3/selftest/tests.py b/source3/selftest/tests.py index 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/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 . +*/ + +#include "includes.h" +#include "lib/cmdline/cmdline.h" +#include "libcli/smb2/smb2.h" +#include "libcli/smb2/smb2_calls.h" +#include "libcli/smb/smbXcli_base.h" +#include "torture/torture.h" +#include "torture/vfs/proto.h" +#include "libcli/resolve/resolve.h" +#include "torture/util.h" +#include "torture/smb2/proto.h" +#include "lib/param/param.h" + +#define BASEDIR "smb2-testads" + + +static bool get_stream_handle(struct torture_context *tctx, + struct smb2_tree *tree, + const char *dname, + const char *fname, + const char *sname, + struct smb2_handle *hdl_in) +{ + bool ret = true; + NTSTATUS status; + struct smb2_handle fhandle = {{0}}; + struct smb2_handle dhandle = {{0}}; + + torture_comment(tctx, "Create dir\n"); + + status = torture_smb2_testdir(tree, dname, &dhandle); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "torture_smb2_testdir\n"); + + torture_comment(tctx, "Create file\n"); + + status = torture_smb2_testfile(tree, fname, &fhandle); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "torture_smb2_testfile\n"); + + status = torture_smb2_testfile(tree, sname, hdl_in); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "torture_smb2_testfile\n"); + +done: + if (!smb2_util_handle_empty(fhandle)) { + smb2_util_close(tree, fhandle); + } + if (!smb2_util_handle_empty(dhandle)) { + smb2_util_close(tree, dhandle); + } + return ret; +} + +static bool read_stream(struct torture_context *tctx, + TALLOC_CTX *mem_ctx, + struct smb2_tree *tree, + struct smb2_handle *stream_hdl, + off_t read_offset, + size_t read_count, + char **data_out, + size_t *data_out_sz) +{ + NTSTATUS status; + struct smb2_read r; + bool ret = true; + + ZERO_STRUCT(r); + r.in.file.handle = *stream_hdl; + r.in.length = read_count; + r.in.offset = read_offset; + + status = smb2_read(tree, mem_ctx, &r); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "stream read\n"); + + *data_out = (char *)r.out.data.data; + *data_out_sz = r.out.data.length; + +done: + return ret; +} + + +#define WRITE_PAYLOAD "canary" +#define ADS_LEN 1024 +#define ADS_OFF_TAIL ADS_LEN - sizeof(WRITE_PAYLOAD) + +static bool test_streams_pwrite_hole(struct torture_context *tctx, + struct smb2_tree *tree) +{ + NTSTATUS status; + bool ok; + bool ret = true; + const char *dname = BASEDIR "\\testdir"; + const char *fname = BASEDIR "\\testdir\\testfile"; + const char *sname = BASEDIR "\\testdir\\testfile:test_stream"; + const char *canary = "canary"; + struct smb2_handle shandle = {{0}}; + TALLOC_CTX *tmp_ctx = NULL; + char *data = NULL; + size_t data_sz, i; + + ok = smb2_util_setup_dir(tctx, tree, BASEDIR); + torture_assert_goto(tctx, ok == true, ret, done, "Unable to setup testdir\n"); + + tmp_ctx = talloc_new(tree); + torture_assert_goto(tctx, tmp_ctx != NULL, ret, done, "Memory failure\n"); + + ok = get_stream_handle(tctx, tree, dname, fname, sname, &shandle); + if (!ok) { + // torture assert already set + goto done; + } + + /* + * We're going to write a string at the beginning at the ADS, then write the same + * string at a later offset, introducing a hole in the file + */ + torture_comment(tctx, "writing at varying offsets to create hole\n"); + status = smb2_util_write(tree, shandle, WRITE_PAYLOAD, 0, sizeof(WRITE_PAYLOAD)); + if (!NT_STATUS_IS_OK(status)) { + torture_comment(tctx, "Failed to write %zu bytes to " + "stream at offset 0\n", sizeof(canary)); + return false; + } + + status = smb2_util_write(tree, shandle, WRITE_PAYLOAD, ADS_OFF_TAIL, sizeof(WRITE_PAYLOAD)); + if (!NT_STATUS_IS_OK(status)) { + torture_comment(tctx, "Failed to write %zu bytes to " + "stream at offset 1018\n", sizeof(canary)); + return false; + } + + /* Now we'll read the stream contents */ + torture_comment(tctx, "Read stream data\n"); + ok = read_stream(tctx, tmp_ctx, tree, &shandle, 0, ADS_LEN, &data, &data_sz); + if (!ok) { + // torture assert already set + goto done; + } + + torture_assert_goto(tctx, data_sz == ADS_LEN, ret, done, "Short read on ADS\n"); + + /* Make sure our strings actually got written */ + if (strncmp(data, WRITE_PAYLOAD, sizeof(WRITE_PAYLOAD)) != 0) { + torture_result(tctx, TORTURE_FAIL, + "Payload write at beginning of file failed"); + ret = false; + goto done; + } + + if (strncmp(data + ADS_OFF_TAIL, WRITE_PAYLOAD, sizeof(WRITE_PAYLOAD)) != 0) { + torture_result(tctx, TORTURE_FAIL, + "Payload write at end of file failed"); + ret = false; + goto done; + } + + /* Now we'll check that the hole is full of null bytes */ + for (i = sizeof(WRITE_PAYLOAD); i < ADS_OFF_TAIL; i++) { + if (data[i] != '\0') { + torture_comment(tctx, "idx: %zu, got 0x%02x when expected 0x00\n", + i, (uint8_t)data[i]); + torture_result(tctx, TORTURE_FAIL, + "0x%08x: unexpected non-null byte in ADS read\n", + data[i]); + ret = false; + goto done; + } + } + +done: + talloc_free(tmp_ctx); + + if (!smb2_util_handle_empty(shandle)) { + smb2_util_close(tree, shandle); + } + + smb2_deltree(tree, BASEDIR); + + return ret; +} + +/* + basic testing of vfs_streams_xattr +*/ +struct torture_suite *torture_vfs_streams_xattr(TALLOC_CTX *ctx) +{ + struct torture_suite *suite = torture_suite_create(ctx, "streams_xattr"); + + torture_suite_add_1smb2_test(suite, "streams-pwrite-hole", test_streams_pwrite_hole); + + suite->description = talloc_strdup(suite, "vfs_streams_xattr tests"); + + return suite; +} diff --git a/source4/torture/vfs/vfs.c b/source4/torture/vfs/vfs.c index 3d402eeee0d..19dbaa0775c 100644 --- a/source4/torture/vfs/vfs.c +++ b/source4/torture/vfs/vfs.c @@ -115,6 +115,7 @@ NTSTATUS torture_vfs_init(TALLOC_CTX *ctx) torture_suite_add_suite(suite, torture_vfs_fruit_timemachine(suite)); torture_suite_add_suite(suite, torture_vfs_fruit_conversion(suite)); torture_suite_add_suite(suite, torture_vfs_fruit_unfruit(suite)); + torture_suite_add_suite(suite, torture_vfs_streams_xattr(suite)); torture_suite_add_1smb2_test(suite, "fruit_validate_afpinfo", test_fruit_validate_afpinfo); torture_register_suite(ctx, suite); diff --git a/source4/torture/wscript_build b/source4/torture/wscript_build index b38a30c98da..cae558398a3 100644 --- a/source4/torture/wscript_build +++ b/source4/torture/wscript_build @@ -301,7 +301,7 @@ bld.SAMBA_MODULE('TORTURE_NTP', ) bld.SAMBA_MODULE('TORTURE_VFS', - source='vfs/vfs.c vfs/fruit.c vfs/acl_xattr.c', + source='vfs/vfs.c vfs/fruit.c vfs/acl_xattr.c vfs/streams_xattr.c', subsystem='smbtorture', deps='LIBCLI_SMB TORTURE_UTIL smbclient-raw TORTURE_RAW', internal_module=True, -- 2.34.1 From 06bc23b5977f564fef31285a92ec28dc80b68edb Mon Sep 17 00:00:00 2001 From: Andrew Walker Date: Thu, 28 Aug 2025 19:36:19 +0000 Subject: [PATCH 4/4] CVE-2025-9640: s3/modules/vfs_streams_xattr fix unitialized write This commit fixes a situation in which vfs_streams_xattr could write unitialized memory into alternate data streams if the user writes to an offset that is beyond the current end of file to insert a hole in it. BUG: https://bugzilla.samba.org/show_bug.cgi?id=15885 Signed-off-by: Andrew Walker Reviewed-by: Volker Lendecke --- source3/modules/vfs_streams_xattr.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/source3/modules/vfs_streams_xattr.c b/source3/modules/vfs_streams_xattr.c index 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; -- 2.34.1