From c543ef453ff42cc10d03c66e2d0ee6a567820c7b Mon Sep 17 00:00:00 2001
From: Pavel Kohout <pavel@aisle.com>
Date: Fri, 13 Feb 2026 15:51:41 +1300
Subject: [PATCH 17/38] CVE-2026-2340: vfs_worm: Check destination WORM status
 in rename

vfs_worm_renameat() only checked if the source file was WORM-protected,
but not the destination. This allowed overwriting immutable files via
SMB2 rename with ReplaceIfExists=1, bypassing WORM protection.

Add destination check using FSTATAT on the destination dirfsp, as
suggested by the maintainer.

CWE-284 (Improper Access Control)

Reported-by: Pavel Kohout, Aisle Research, www.aisle.com

BUG: https://bugzilla.samba.org/show_bug.cgi?id=15997

To backport to 4.21 we change:

 1. the name of dst_dirfsp and src_dirfsp to
    dstfsp and srcfsp, respectively (accounting for
    76796180cf3af3252db2c29d0e95282a498a8527 in 4.24/master).

 2. remove 'how' argumuent to SMB_VFS_RENAMEAT() (accounting for
    5b305d1fbb2778f8c8dd2873e79f28a27d8ce581 in 4.22+).

Signed-off-by: Pavel Kohout <pavel.kohout@aisle.com>
Reviewed-by: Volker Lendecke <vl@samba.org>
Reviewed-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
---
 selftest/knownfail.d/vfs-worm |  2 --
 source3/modules/vfs_worm.c    | 25 +++++++++++++++++++++++--
 2 files changed, 23 insertions(+), 4 deletions(-)
 delete mode 100644 selftest/knownfail.d/vfs-worm

diff --git a/selftest/knownfail.d/vfs-worm b/selftest/knownfail.d/vfs-worm
deleted file mode 100644
index f4a330c744b..00000000000
--- a/selftest/knownfail.d/vfs-worm
+++ /dev/null
@@ -1,2 +0,0 @@
-^samba3.blackbox.worm.SMB3
-^samba3.blackbox.worm.NT1
diff --git a/source3/modules/vfs_worm.c b/source3/modules/vfs_worm.c
index 5c1bc6da650..bd2fdbc9a80 100644
--- a/source3/modules/vfs_worm.c
+++ b/source3/modules/vfs_worm.c
@@ -217,13 +217,34 @@ static int vfs_worm_renameat(vfs_handle_struct *handle,
 			     files_struct *dstfsp,
 			     const struct smb_filename *smb_fname_dst)
 {
+	struct stat_ex dst_st;
+	int ret;
+
 	if (is_readonly(handle, smb_fname_src)) {
 		errno = EACCES;
 		return -1;
 	}
 
-	return SMB_VFS_NEXT_RENAMEAT(
-		handle, srcfsp, smb_fname_src, dstfsp, smb_fname_dst);
+	/* Check if destination is WORM-protected (fixes CVE-2026-2340) */
+	ret = SMB_VFS_FSTATAT(handle->conn,
+			      dstfsp,
+			      smb_fname_dst,
+			      &dst_st,
+			      AT_SYMLINK_NOFOLLOW);
+	if (ret == 0) {
+		struct smb_filename dst_with_stat = *smb_fname_dst;
+		dst_with_stat.st = dst_st;
+		if (is_readonly(handle, &dst_with_stat)) {
+			errno = EACCES;
+			return -1;
+		}
+	}
+
+	return SMB_VFS_NEXT_RENAMEAT(handle,
+				     srcfsp,
+				     smb_fname_src,
+				     dstfsp,
+				     smb_fname_dst);
 }
 
 static int vfs_worm_fsetxattr(struct vfs_handle_struct *handle,
-- 
2.43.0


