2 * VFS module to disallow writes for older files
4 * Copyright (C) 2013, Volker Lendecke
5 * Copyright (C) 2023-2024, Björn Jacke
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 3 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, see <http://www.gnu.org/licenses/>.
22 #include "smbd/smbd.h"
23 #include "system/filesys.h"
24 #include "libcli/security/security.h"
26 struct worm_config_data
{
29 static const uint32_t write_access_flags
= FILE_WRITE_DATA
| FILE_APPEND_DATA
|
30 FILE_WRITE_ATTRIBUTES
|
31 DELETE_ACCESS
| WRITE_DAC_ACCESS
|
32 WRITE_OWNER_ACCESS
| FILE_WRITE_EA
;
34 static int vfs_worm_connect(struct vfs_handle_struct
*handle
,
35 const char *service
, const char *user
)
37 struct worm_config_data
*config
= NULL
;
40 ret
= SMB_VFS_NEXT_CONNECT(handle
, service
, user
);
45 if (IS_IPC(handle
->conn
) || IS_PRINT(handle
->conn
)) {
49 config
= talloc_zero(handle
->conn
, struct worm_config_data
);
51 DBG_ERR("talloc_zero() failed\n");
55 config
->grace_period
= lp_parm_int(SNUM(handle
->conn
), "worm",
56 "grace_period", 3600);
58 SMB_VFS_HANDLE_SET_DATA(handle
, config
,
59 NULL
, struct worm_config_data
,
65 static bool is_readonly(vfs_handle_struct
*handle
,
66 const struct smb_filename
*smb_fname
)
69 struct worm_config_data
*config
= NULL
;
71 SMB_VFS_HANDLE_GET_DATA(handle
,
73 struct worm_config_data
,
76 if (!VALID_STAT(smb_fname
->st
)) {
80 age
= timespec_elapsed(&smb_fname
->st
.st_ex_ctime
);
82 if (age
> config
->grace_period
) {
89 static bool fsp_is_readonly(vfs_handle_struct
*handle
, files_struct
*fsp
)
92 struct worm_config_data
*config
= NULL
;
94 SMB_VFS_HANDLE_GET_DATA(handle
,
96 struct worm_config_data
,
99 if (!VALID_STAT(fsp
->fsp_name
->st
)) {
103 age
= timespec_elapsed(&fsp
->fsp_name
->st
.st_ex_ctime
);
105 if (age
> config
->grace_period
) {
113 static NTSTATUS
vfs_worm_create_file(vfs_handle_struct
*handle
,
114 struct smb_request
*req
,
115 struct files_struct
*dirfsp
,
116 struct smb_filename
*smb_fname
,
117 uint32_t access_mask
,
118 uint32_t share_access
,
119 uint32_t create_disposition
,
120 uint32_t create_options
,
121 uint32_t file_attributes
,
122 uint32_t oplock_request
,
123 const struct smb2_lease
*lease
,
124 uint64_t allocation_size
,
125 uint32_t private_flags
,
126 struct security_descriptor
*sd
,
127 struct ea_list
*ea_list
,
128 files_struct
**result
,
130 const struct smb2_create_blobs
*in_context_blobs
,
131 struct smb2_create_blobs
*out_context_blobs
)
136 readonly
= is_readonly(handle
, smb_fname
);
138 if (readonly
&& (access_mask
& write_access_flags
)) {
139 return NT_STATUS_ACCESS_DENIED
;
142 status
= SMB_VFS_NEXT_CREATE_FILE(
143 handle
, req
, dirfsp
, smb_fname
, access_mask
,
144 share_access
, create_disposition
, create_options
,
145 file_attributes
, oplock_request
, lease
, allocation_size
,
146 private_flags
, sd
, ea_list
, result
, pinfo
,
147 in_context_blobs
, out_context_blobs
);
148 if (!NT_STATUS_IS_OK(status
)) {
153 * Access via MAXIMUM_ALLOWED_ACCESS?
155 if (readonly
&& ((*result
)->access_mask
& write_access_flags
)) {
156 close_file_free(req
, result
, NORMAL_CLOSE
);
157 return NT_STATUS_ACCESS_DENIED
;
162 static int vfs_worm_openat(vfs_handle_struct
*handle
,
163 const struct files_struct
*dirfsp
,
164 const struct smb_filename
*smb_fname
,
166 const struct vfs_open_how
*how
)
168 if (is_readonly(handle
, smb_fname
) &&
169 (fsp
->access_mask
& write_access_flags
)) {
174 return SMB_VFS_NEXT_OPENAT(handle
, dirfsp
, smb_fname
, fsp
, how
);
177 static int vfs_worm_fntimes(vfs_handle_struct
*handle
,
179 struct smb_file_time
*ft
)
181 if (fsp_is_readonly(handle
, fsp
)) {
186 return SMB_VFS_NEXT_FNTIMES(handle
, fsp
, ft
);
189 static int vfs_worm_fchmod(vfs_handle_struct
*handle
,
193 if (fsp_is_readonly(handle
, fsp
)) {
198 return SMB_VFS_NEXT_FCHMOD(handle
, fsp
, mode
);
201 static int vfs_worm_fchown(vfs_handle_struct
*handle
,
206 if (fsp_is_readonly(handle
, fsp
)) {
211 return SMB_VFS_NEXT_FCHOWN(handle
, fsp
, uid
, gid
);
214 static int vfs_worm_renameat(vfs_handle_struct
*handle
,
215 files_struct
*srcfsp
,
216 const struct smb_filename
*smb_fname_src
,
217 files_struct
*dstfsp
,
218 const struct smb_filename
*smb_fname_dst
)
220 if (is_readonly(handle
, smb_fname_src
)) {
225 return SMB_VFS_NEXT_RENAMEAT(
226 handle
, srcfsp
, smb_fname_src
, dstfsp
, smb_fname_dst
);
229 static int vfs_worm_fsetxattr(struct vfs_handle_struct
*handle
,
230 struct files_struct
*fsp
,
236 if (fsp_is_readonly(handle
, fsp
)) {
241 return SMB_VFS_NEXT_FSETXATTR(handle
, fsp
, name
, value
, size
, flags
);
244 static int vfs_worm_fremotexattr(struct vfs_handle_struct
*handle
,
245 struct files_struct
*fsp
,
248 if (fsp_is_readonly(handle
, fsp
)) {
253 return SMB_VFS_NEXT_FREMOVEXATTR(handle
, fsp
, name
);
256 static int vfs_worm_unlinkat(vfs_handle_struct
*handle
,
257 struct files_struct
*dirfsp
,
258 const struct smb_filename
*smb_fname
,
261 struct smb_filename
*full_fname
= NULL
;
264 full_fname
= full_path_from_dirfsp_atname(talloc_tos(),
267 if (full_fname
== NULL
) {
271 readonly
= is_readonly(handle
, full_fname
);
273 TALLOC_FREE(full_fname
);
280 return SMB_VFS_NEXT_UNLINKAT(handle
, dirfsp
, smb_fname
, flags
);
283 static NTSTATUS
vfs_worm_fset_dos_attributes(struct vfs_handle_struct
*handle
,
284 struct files_struct
*fsp
,
287 if (fsp_is_readonly(handle
, fsp
)) {
288 return NT_STATUS_ACCESS_DENIED
;
291 return SMB_VFS_NEXT_FSET_DOS_ATTRIBUTES(handle
, fsp
, dosmode
);
294 static NTSTATUS
vfs_worm_fset_nt_acl(vfs_handle_struct
*handle
,
296 uint32_t security_info_sent
,
297 const struct security_descriptor
*psd
)
299 if (fsp_is_readonly(handle
, fsp
)) {
300 return NT_STATUS_ACCESS_DENIED
;
303 return SMB_VFS_NEXT_FSET_NT_ACL(handle
, fsp
, security_info_sent
, psd
);
306 static int vfs_worm_sys_acl_set_fd(vfs_handle_struct
*handle
,
307 struct files_struct
*fsp
,
311 if (fsp_is_readonly(handle
, fsp
)) {
316 return SMB_VFS_NEXT_SYS_ACL_SET_FD(handle
, fsp
, type
, theacl
);
319 static int vfs_worm_sys_acl_delete_def_fd(vfs_handle_struct
*handle
,
320 struct files_struct
*fsp
)
322 if (fsp_is_readonly(handle
, fsp
)) {
327 return SMB_VFS_NEXT_SYS_ACL_DELETE_DEF_FD(handle
, fsp
);
330 static struct vfs_fn_pointers vfs_worm_fns
= {
331 .connect_fn
= vfs_worm_connect
,
332 .create_file_fn
= vfs_worm_create_file
,
333 .openat_fn
= vfs_worm_openat
,
334 .fntimes_fn
= vfs_worm_fntimes
,
335 .fchmod_fn
= vfs_worm_fchmod
,
336 .fchown_fn
= vfs_worm_fchown
,
337 .renameat_fn
= vfs_worm_renameat
,
338 .fsetxattr_fn
= vfs_worm_fsetxattr
,
339 .fremovexattr_fn
= vfs_worm_fremotexattr
,
340 .unlinkat_fn
= vfs_worm_unlinkat
,
341 .fset_dos_attributes_fn
= vfs_worm_fset_dos_attributes
,
342 .fset_nt_acl_fn
= vfs_worm_fset_nt_acl
,
343 .sys_acl_set_fd_fn
= vfs_worm_sys_acl_set_fd
,
344 .sys_acl_delete_def_fd_fn
= vfs_worm_sys_acl_delete_def_fd
,
348 NTSTATUS
vfs_worm_init(TALLOC_CTX
*ctx
)
352 ret
= smb_register_vfs(SMB_VFS_INTERFACE_VERSION
, "worm",
354 if (!NT_STATUS_IS_OK(ret
)) {