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
,
219 const struct vfs_rename_how
*how
)
221 if (is_readonly(handle
, smb_fname_src
)) {
226 return SMB_VFS_NEXT_RENAMEAT(
227 handle
, srcfsp
, smb_fname_src
, dstfsp
, smb_fname_dst
, how
);
230 static int vfs_worm_fsetxattr(struct vfs_handle_struct
*handle
,
231 struct files_struct
*fsp
,
237 if (fsp_is_readonly(handle
, fsp
)) {
242 return SMB_VFS_NEXT_FSETXATTR(handle
, fsp
, name
, value
, size
, flags
);
245 static int vfs_worm_fremotexattr(struct vfs_handle_struct
*handle
,
246 struct files_struct
*fsp
,
249 if (fsp_is_readonly(handle
, fsp
)) {
254 return SMB_VFS_NEXT_FREMOVEXATTR(handle
, fsp
, name
);
257 static int vfs_worm_unlinkat(vfs_handle_struct
*handle
,
258 struct files_struct
*dirfsp
,
259 const struct smb_filename
*smb_fname
,
262 struct smb_filename
*full_fname
= NULL
;
265 full_fname
= full_path_from_dirfsp_atname(talloc_tos(),
268 if (full_fname
== NULL
) {
272 readonly
= is_readonly(handle
, full_fname
);
274 TALLOC_FREE(full_fname
);
281 return SMB_VFS_NEXT_UNLINKAT(handle
, dirfsp
, smb_fname
, flags
);
284 static NTSTATUS
vfs_worm_fset_dos_attributes(struct vfs_handle_struct
*handle
,
285 struct files_struct
*fsp
,
288 if (fsp_is_readonly(handle
, fsp
)) {
289 return NT_STATUS_ACCESS_DENIED
;
292 return SMB_VFS_NEXT_FSET_DOS_ATTRIBUTES(handle
, fsp
, dosmode
);
295 static NTSTATUS
vfs_worm_fset_nt_acl(vfs_handle_struct
*handle
,
297 uint32_t security_info_sent
,
298 const struct security_descriptor
*psd
)
300 if (fsp_is_readonly(handle
, fsp
)) {
301 return NT_STATUS_ACCESS_DENIED
;
304 return SMB_VFS_NEXT_FSET_NT_ACL(handle
, fsp
, security_info_sent
, psd
);
307 static int vfs_worm_sys_acl_set_fd(vfs_handle_struct
*handle
,
308 struct files_struct
*fsp
,
312 if (fsp_is_readonly(handle
, fsp
)) {
317 return SMB_VFS_NEXT_SYS_ACL_SET_FD(handle
, fsp
, type
, theacl
);
320 static int vfs_worm_sys_acl_delete_def_fd(vfs_handle_struct
*handle
,
321 struct files_struct
*fsp
)
323 if (fsp_is_readonly(handle
, fsp
)) {
328 return SMB_VFS_NEXT_SYS_ACL_DELETE_DEF_FD(handle
, fsp
);
331 static struct vfs_fn_pointers vfs_worm_fns
= {
332 .connect_fn
= vfs_worm_connect
,
333 .create_file_fn
= vfs_worm_create_file
,
334 .openat_fn
= vfs_worm_openat
,
335 .fntimes_fn
= vfs_worm_fntimes
,
336 .fchmod_fn
= vfs_worm_fchmod
,
337 .fchown_fn
= vfs_worm_fchown
,
338 .renameat_fn
= vfs_worm_renameat
,
339 .fsetxattr_fn
= vfs_worm_fsetxattr
,
340 .fremovexattr_fn
= vfs_worm_fremotexattr
,
341 .unlinkat_fn
= vfs_worm_unlinkat
,
342 .fset_dos_attributes_fn
= vfs_worm_fset_dos_attributes
,
343 .fset_nt_acl_fn
= vfs_worm_fset_nt_acl
,
344 .sys_acl_set_fd_fn
= vfs_worm_sys_acl_set_fd
,
345 .sys_acl_delete_def_fd_fn
= vfs_worm_sys_acl_delete_def_fd
,
349 NTSTATUS
vfs_worm_init(TALLOC_CTX
*ctx
)
353 ret
= smb_register_vfs(SMB_VFS_INTERFACE_VERSION
, "worm",
355 if (!NT_STATUS_IS_OK(ret
)) {