From 8950bc16a64a9fb14a3e02275fb908583cea5db0 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn=20Jacke?= <bjacke@samba.org>
Date: Thu, 4 Jan 2024 12:48:59 +0100
Subject: [PATCH 12/38] vfs_worm: add some more vfs functions that worm needs
 to take care of

BUG: https://bugzilla.samba.org/show_bug.cgi?id=10430
BUG: https://bugzilla.samba.org/show_bug.cgi?id=15997
[backported for CVE-2026-2340]

Signed-off-by: Bjoern Jacke <bjacke@samba.org>
Reviewed-by: Volker Lendecke <vl@samba.org>
(cherry picked from commit 30fea0e959229ee583bc76490aff5e54bc7f5be8)
---
 source3/modules/vfs_worm.c | 203 +++++++++++++++++++++++++++++++++++++
 1 file changed, 203 insertions(+)

diff --git a/source3/modules/vfs_worm.c b/source3/modules/vfs_worm.c
index 0d17bbbc814..4abb25d12e8 100644
--- a/source3/modules/vfs_worm.c
+++ b/source3/modules/vfs_worm.c
@@ -82,6 +82,29 @@ static bool is_readonly(vfs_handle_struct *handle,
 		return true;
 	}
 
+out:
+	return false;
+}
+static bool fsp_is_readonly(vfs_handle_struct *handle, files_struct *fsp)
+{
+	double age;
+	struct worm_config_data *config = NULL;
+
+	SMB_VFS_HANDLE_GET_DATA(handle,
+				config,
+				struct worm_config_data,
+				return true);
+
+	if (!VALID_STAT(fsp->fsp_name->st)) {
+		goto out;
+	}
+
+	age = timespec_elapsed(&fsp->fsp_name->st.st_ex_ctime);
+
+	if (age > config->grace_period) {
+		return true;
+	}
+
 out:
 	return false;
 }
@@ -135,9 +158,189 @@ static NTSTATUS vfs_worm_create_file(vfs_handle_struct *handle,
 	return NT_STATUS_OK;
 }
 
+static int vfs_worm_openat(vfs_handle_struct *handle,
+			   const struct files_struct *dirfsp,
+			   const struct smb_filename *smb_fname,
+			   files_struct *fsp,
+			   const struct vfs_open_how *how)
+{
+	if (is_readonly(handle, smb_fname) &&
+	    (fsp->access_mask & write_access_flags)) {
+		errno = EACCES;
+		return -1;
+	}
+
+	return SMB_VFS_NEXT_OPENAT(handle, dirfsp, smb_fname, fsp, how);
+}
+
+static int vfs_worm_fntimes(vfs_handle_struct *handle,
+			    files_struct *fsp,
+			    struct smb_file_time *ft)
+{
+	if (fsp_is_readonly(handle, fsp)) {
+		errno = EACCES;
+		return -1;
+	}
+
+	return SMB_VFS_NEXT_FNTIMES(handle, fsp, ft);
+}
+
+static int vfs_worm_fchmod(vfs_handle_struct *handle,
+			   files_struct *fsp,
+			   mode_t mode)
+{
+	if (fsp_is_readonly(handle, fsp)) {
+		errno = EACCES;
+		return -1;
+	}
+
+	return SMB_VFS_NEXT_FCHMOD(handle, fsp, mode);
+}
+
+static int vfs_worm_fchown(vfs_handle_struct *handle,
+			   files_struct *fsp,
+			   uid_t uid,
+			   gid_t gid)
+{
+	if (fsp_is_readonly(handle, fsp)) {
+		errno = EACCES;
+		return -1;
+	}
+
+	return SMB_VFS_NEXT_FCHOWN(handle, fsp, uid, gid);
+}
+
+static int vfs_worm_renameat(vfs_handle_struct *handle,
+			     files_struct *srcfsp,
+			     const struct smb_filename *smb_fname_src,
+			     files_struct *dstfsp,
+			     const struct smb_filename *smb_fname_dst)
+{
+	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);
+}
+
+static int vfs_worm_fsetxattr(struct vfs_handle_struct *handle,
+			      struct files_struct *fsp,
+			      const char *name,
+			      const void *value,
+			      size_t size,
+			      int flags)
+{
+	if (fsp_is_readonly(handle, fsp)) {
+		errno = EACCES;
+		return -1;
+	}
+
+	return SMB_VFS_NEXT_FSETXATTR(handle, fsp, name, value, size, flags);
+}
+
+static int vfs_worm_fremotexattr(struct vfs_handle_struct *handle,
+				 struct files_struct *fsp,
+				 const char *name)
+{
+	if (fsp_is_readonly(handle, fsp)) {
+		errno = EACCES;
+		return -1;
+	}
+
+	return SMB_VFS_NEXT_FREMOVEXATTR(handle, fsp, name);
+}
+
+static int vfs_worm_unlinkat(vfs_handle_struct *handle,
+			     struct files_struct *dirfsp,
+			     const struct smb_filename *smb_fname,
+			     int flags)
+{
+	struct smb_filename *full_fname = NULL;
+	bool readonly;
+
+	full_fname = full_path_from_dirfsp_atname(talloc_tos(),
+						  dirfsp,
+						  smb_fname);
+	if (full_fname == NULL) {
+		return -1;
+	}
+
+	readonly = is_readonly(handle, full_fname);
+
+	TALLOC_FREE(full_fname);
+
+	if (readonly) {
+		errno = EACCES;
+		return -1;
+	}
+
+	return SMB_VFS_NEXT_UNLINKAT(handle, dirfsp, smb_fname, flags);
+}
+
+static NTSTATUS vfs_worm_fset_dos_attributes(struct vfs_handle_struct *handle,
+					     struct files_struct *fsp,
+					     uint32_t dosmode)
+{
+	if (fsp_is_readonly(handle, fsp)) {
+		return NT_STATUS_ACCESS_DENIED;
+	}
+
+	return SMB_VFS_NEXT_FSET_DOS_ATTRIBUTES(handle, fsp, dosmode);
+}
+
+static NTSTATUS vfs_worm_fset_nt_acl(vfs_handle_struct *handle,
+				     files_struct *fsp,
+				     uint32_t security_info_sent,
+				     const struct security_descriptor *psd)
+{
+	if (fsp_is_readonly(handle, fsp)) {
+		return NT_STATUS_ACCESS_DENIED;
+	}
+
+	return SMB_VFS_NEXT_FSET_NT_ACL(handle, fsp, security_info_sent, psd);
+}
+
+static int vfs_worm_sys_acl_set_fd(vfs_handle_struct *handle,
+				   struct files_struct *fsp,
+				   SMB_ACL_TYPE_T type,
+				   SMB_ACL_T theacl)
+{
+	if (fsp_is_readonly(handle, fsp)) {
+		errno = EACCES;
+		return -1;
+	}
+
+	return SMB_VFS_NEXT_SYS_ACL_SET_FD(handle, fsp, type, theacl);
+}
+
+static int vfs_worm_sys_acl_delete_def_fd(vfs_handle_struct *handle,
+					  struct files_struct *fsp)
+{
+	if (fsp_is_readonly(handle, fsp)) {
+		errno = EACCES;
+		return -1;
+	}
+
+	return SMB_VFS_NEXT_SYS_ACL_DELETE_DEF_FD(handle, fsp);
+}
+
 static struct vfs_fn_pointers vfs_worm_fns = {
 	.connect_fn = vfs_worm_connect,
 	.create_file_fn = vfs_worm_create_file,
+	.openat_fn = vfs_worm_openat,
+	.fntimes_fn = vfs_worm_fntimes,
+	.fchmod_fn = vfs_worm_fchmod,
+	.fchown_fn = vfs_worm_fchown,
+	.renameat_fn = vfs_worm_renameat,
+	.fsetxattr_fn = vfs_worm_fsetxattr,
+	.fremovexattr_fn = vfs_worm_fremotexattr,
+	.unlinkat_fn = vfs_worm_unlinkat,
+	.fset_dos_attributes_fn = vfs_worm_fset_dos_attributes,
+	.fset_nt_acl_fn = vfs_worm_fset_nt_acl,
+	.sys_acl_set_fd_fn = vfs_worm_sys_acl_set_fd,
+	.sys_acl_delete_def_fd_fn = vfs_worm_sys_acl_delete_def_fd,
 };
 
 static_decl_vfs;
-- 
2.43.0


