Line data Source code
1 : /*
2 : * VFS module to disallow writes for older files
3 : *
4 : * Copyright (C) 2013, Volker Lendecke
5 : * Copyright (C) 2023-2024, Björn Jacke
6 : *
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.
11 : *
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.
16 : *
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/>.
19 : */
20 :
21 : #include "includes.h"
22 : #include "smbd/smbd.h"
23 : #include "system/filesys.h"
24 : #include "libcli/security/security.h"
25 :
26 : struct worm_config_data {
27 : double grace_period;
28 : };
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;
33 :
34 14 : static int vfs_worm_connect(struct vfs_handle_struct *handle,
35 : const char *service, const char *user)
36 : {
37 14 : struct worm_config_data *config = NULL;
38 : int ret;
39 :
40 14 : ret = SMB_VFS_NEXT_CONNECT(handle, service, user);
41 14 : if (ret < 0) {
42 0 : return ret;
43 : }
44 :
45 14 : if (IS_IPC(handle->conn) || IS_PRINT(handle->conn)) {
46 0 : return 0;
47 : }
48 :
49 14 : config = talloc_zero(handle->conn, struct worm_config_data);
50 14 : if (config == NULL) {
51 0 : DBG_ERR("talloc_zero() failed\n");
52 0 : errno = ENOMEM;
53 0 : return -1;
54 : }
55 14 : config->grace_period = lp_parm_int(SNUM(handle->conn), "worm",
56 : "grace_period", 3600);
57 :
58 14 : SMB_VFS_HANDLE_SET_DATA(handle, config,
59 : NULL, struct worm_config_data,
60 : return -1);
61 14 : return 0;
62 :
63 : }
64 :
65 194 : static bool is_readonly(vfs_handle_struct *handle,
66 : const struct smb_filename *smb_fname)
67 : {
68 : double age;
69 194 : struct worm_config_data *config = NULL;
70 :
71 194 : SMB_VFS_HANDLE_GET_DATA(handle,
72 : config,
73 : struct worm_config_data,
74 : return true);
75 :
76 194 : if (!VALID_STAT(smb_fname->st)) {
77 150 : goto out;
78 : }
79 :
80 44 : age = timespec_elapsed(&smb_fname->st.st_ex_ctime);
81 :
82 44 : if (age > config->grace_period) {
83 10 : return true;
84 : }
85 :
86 34 : out:
87 184 : return false;
88 : }
89 50 : static bool fsp_is_readonly(vfs_handle_struct *handle, files_struct *fsp)
90 : {
91 : double age;
92 50 : struct worm_config_data *config = NULL;
93 :
94 50 : SMB_VFS_HANDLE_GET_DATA(handle,
95 : config,
96 : struct worm_config_data,
97 : return true);
98 :
99 50 : if (!VALID_STAT(fsp->fsp_name->st)) {
100 0 : goto out;
101 : }
102 :
103 50 : age = timespec_elapsed(&fsp->fsp_name->st.st_ex_ctime);
104 :
105 50 : if (age > config->grace_period) {
106 2 : return true;
107 : }
108 :
109 48 : out:
110 48 : return false;
111 : }
112 :
113 42 : 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,
129 : int *pinfo,
130 : const struct smb2_create_blobs *in_context_blobs,
131 : struct smb2_create_blobs *out_context_blobs)
132 : {
133 : NTSTATUS status;
134 : bool readonly;
135 :
136 42 : readonly = is_readonly(handle, smb_fname);
137 :
138 42 : if (readonly && (access_mask & write_access_flags)) {
139 4 : return NT_STATUS_ACCESS_DENIED;
140 : }
141 :
142 38 : 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 38 : if (!NT_STATUS_IS_OK(status)) {
149 0 : return status;
150 : }
151 :
152 : /*
153 : * Access via MAXIMUM_ALLOWED_ACCESS?
154 : */
155 38 : if (readonly && ((*result)->access_mask & write_access_flags)) {
156 0 : close_file_free(req, result, NORMAL_CLOSE);
157 0 : return NT_STATUS_ACCESS_DENIED;
158 : }
159 38 : return NT_STATUS_OK;
160 : }
161 :
162 144 : static int vfs_worm_openat(vfs_handle_struct *handle,
163 : const struct files_struct *dirfsp,
164 : const struct smb_filename *smb_fname,
165 : files_struct *fsp,
166 : const struct vfs_open_how *how)
167 : {
168 144 : if (is_readonly(handle, smb_fname) &&
169 0 : (fsp->access_mask & write_access_flags)) {
170 0 : errno = EACCES;
171 0 : return -1;
172 : }
173 :
174 144 : return SMB_VFS_NEXT_OPENAT(handle, dirfsp, smb_fname, fsp, how);
175 : }
176 :
177 12 : static int vfs_worm_fntimes(vfs_handle_struct *handle,
178 : files_struct *fsp,
179 : struct smb_file_time *ft)
180 : {
181 12 : if (fsp_is_readonly(handle, fsp)) {
182 0 : errno = EACCES;
183 0 : return -1;
184 : }
185 :
186 12 : return SMB_VFS_NEXT_FNTIMES(handle, fsp, ft);
187 : }
188 :
189 14 : static int vfs_worm_fchmod(vfs_handle_struct *handle,
190 : files_struct *fsp,
191 : mode_t mode)
192 : {
193 14 : if (fsp_is_readonly(handle, fsp)) {
194 2 : errno = EACCES;
195 2 : return -1;
196 : }
197 :
198 12 : return SMB_VFS_NEXT_FCHMOD(handle, fsp, mode);
199 : }
200 :
201 0 : static int vfs_worm_fchown(vfs_handle_struct *handle,
202 : files_struct *fsp,
203 : uid_t uid,
204 : gid_t gid)
205 : {
206 0 : if (fsp_is_readonly(handle, fsp)) {
207 0 : errno = EACCES;
208 0 : return -1;
209 : }
210 :
211 0 : return SMB_VFS_NEXT_FCHOWN(handle, fsp, uid, gid);
212 : }
213 :
214 0 : 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 : {
220 0 : if (is_readonly(handle, smb_fname_src)) {
221 0 : errno = EACCES;
222 0 : return -1;
223 : }
224 :
225 0 : return SMB_VFS_NEXT_RENAMEAT(
226 : handle, srcfsp, smb_fname_src, dstfsp, smb_fname_dst);
227 : }
228 :
229 12 : static int vfs_worm_fsetxattr(struct vfs_handle_struct *handle,
230 : struct files_struct *fsp,
231 : const char *name,
232 : const void *value,
233 : size_t size,
234 : int flags)
235 : {
236 12 : if (fsp_is_readonly(handle, fsp)) {
237 0 : errno = EACCES;
238 0 : return -1;
239 : }
240 :
241 12 : return SMB_VFS_NEXT_FSETXATTR(handle, fsp, name, value, size, flags);
242 : }
243 :
244 0 : static int vfs_worm_fremotexattr(struct vfs_handle_struct *handle,
245 : struct files_struct *fsp,
246 : const char *name)
247 : {
248 0 : if (fsp_is_readonly(handle, fsp)) {
249 0 : errno = EACCES;
250 0 : return -1;
251 : }
252 :
253 0 : return SMB_VFS_NEXT_FREMOVEXATTR(handle, fsp, name);
254 : }
255 :
256 8 : static int vfs_worm_unlinkat(vfs_handle_struct *handle,
257 : struct files_struct *dirfsp,
258 : const struct smb_filename *smb_fname,
259 : int flags)
260 : {
261 8 : struct smb_filename *full_fname = NULL;
262 : bool readonly;
263 :
264 8 : full_fname = full_path_from_dirfsp_atname(talloc_tos(),
265 : dirfsp,
266 : smb_fname);
267 8 : if (full_fname == NULL) {
268 0 : return -1;
269 : }
270 :
271 8 : readonly = is_readonly(handle, full_fname);
272 :
273 8 : TALLOC_FREE(full_fname);
274 :
275 8 : if (readonly) {
276 0 : errno = EACCES;
277 0 : return -1;
278 : }
279 :
280 8 : return SMB_VFS_NEXT_UNLINKAT(handle, dirfsp, smb_fname, flags);
281 : }
282 :
283 12 : static NTSTATUS vfs_worm_fset_dos_attributes(struct vfs_handle_struct *handle,
284 : struct files_struct *fsp,
285 : uint32_t dosmode)
286 : {
287 12 : if (fsp_is_readonly(handle, fsp)) {
288 0 : return NT_STATUS_ACCESS_DENIED;
289 : }
290 :
291 12 : return SMB_VFS_NEXT_FSET_DOS_ATTRIBUTES(handle, fsp, dosmode);
292 : }
293 :
294 0 : static NTSTATUS vfs_worm_fset_nt_acl(vfs_handle_struct *handle,
295 : files_struct *fsp,
296 : uint32_t security_info_sent,
297 : const struct security_descriptor *psd)
298 : {
299 0 : if (fsp_is_readonly(handle, fsp)) {
300 0 : return NT_STATUS_ACCESS_DENIED;
301 : }
302 :
303 0 : return SMB_VFS_NEXT_FSET_NT_ACL(handle, fsp, security_info_sent, psd);
304 : }
305 :
306 0 : static int vfs_worm_sys_acl_set_fd(vfs_handle_struct *handle,
307 : struct files_struct *fsp,
308 : SMB_ACL_TYPE_T type,
309 : SMB_ACL_T theacl)
310 : {
311 0 : if (fsp_is_readonly(handle, fsp)) {
312 0 : errno = EACCES;
313 0 : return -1;
314 : }
315 :
316 0 : return SMB_VFS_NEXT_SYS_ACL_SET_FD(handle, fsp, type, theacl);
317 : }
318 :
319 0 : static int vfs_worm_sys_acl_delete_def_fd(vfs_handle_struct *handle,
320 : struct files_struct *fsp)
321 : {
322 0 : if (fsp_is_readonly(handle, fsp)) {
323 0 : errno = EACCES;
324 0 : return -1;
325 : }
326 :
327 0 : return SMB_VFS_NEXT_SYS_ACL_DELETE_DEF_FD(handle, fsp);
328 : }
329 :
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,
345 : };
346 :
347 : static_decl_vfs;
348 41 : NTSTATUS vfs_worm_init(TALLOC_CTX *ctx)
349 : {
350 : NTSTATUS ret;
351 :
352 41 : ret = smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "worm",
353 : &vfs_worm_fns);
354 41 : if (!NT_STATUS_IS_OK(ret)) {
355 0 : return ret;
356 : }
357 :
358 41 : return ret;
359 : }
|