s3:libsmb: allow store_cldap_reply() to work with a ipv6 response
[samba.git] / source3 / modules / vfs_worm.c
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 static int vfs_worm_connect(struct vfs_handle_struct *handle,
35                             const char *service, const char *user)
36 {
37         struct worm_config_data *config = NULL;
38         int ret;
39
40         ret = SMB_VFS_NEXT_CONNECT(handle, service, user);
41         if (ret < 0) {
42                 return ret;
43         }
44
45         if (IS_IPC(handle->conn) || IS_PRINT(handle->conn)) {
46                 return 0;
47         }
48
49         config = talloc_zero(handle->conn, struct worm_config_data);
50         if (config == NULL) {
51                 DBG_ERR("talloc_zero() failed\n");
52                 errno = ENOMEM;
53                 return -1;
54         }
55         config->grace_period = lp_parm_int(SNUM(handle->conn), "worm",
56                                                 "grace_period", 3600);
57
58         SMB_VFS_HANDLE_SET_DATA(handle, config,
59                                 NULL, struct worm_config_data,
60                                 return -1);
61         return 0;
62
63 }
64
65 static bool is_readonly(vfs_handle_struct *handle,
66                         const struct smb_filename *smb_fname)
67 {
68         double age;
69         struct worm_config_data *config = NULL;
70
71         SMB_VFS_HANDLE_GET_DATA(handle,
72                                 config,
73                                 struct worm_config_data,
74                                 return true);
75
76         if (!VALID_STAT(smb_fname->st)) {
77                 goto out;
78         }
79
80         age = timespec_elapsed(&smb_fname->st.st_ex_ctime);
81
82         if (age > config->grace_period) {
83                 return true;
84         }
85
86 out:
87         return false;
88 }
89 static bool fsp_is_readonly(vfs_handle_struct *handle, files_struct *fsp)
90 {
91         double age;
92         struct worm_config_data *config = NULL;
93
94         SMB_VFS_HANDLE_GET_DATA(handle,
95                                 config,
96                                 struct worm_config_data,
97                                 return true);
98
99         if (!VALID_STAT(fsp->fsp_name->st)) {
100                 goto out;
101         }
102
103         age = timespec_elapsed(&fsp->fsp_name->st.st_ex_ctime);
104
105         if (age > config->grace_period) {
106                 return true;
107         }
108
109 out:
110         return false;
111 }
112
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,
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         readonly = is_readonly(handle, smb_fname);
137
138         if (readonly && (access_mask & write_access_flags)) {
139                 return NT_STATUS_ACCESS_DENIED;
140         }
141
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)) {
149                 return status;
150         }
151
152         /*
153          * Access via MAXIMUM_ALLOWED_ACCESS?
154          */
155         if (readonly && ((*result)->access_mask & write_access_flags)) {
156                 close_file_free(req, result, NORMAL_CLOSE);
157                 return NT_STATUS_ACCESS_DENIED;
158         }
159         return NT_STATUS_OK;
160 }
161
162 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         if (is_readonly(handle, smb_fname) &&
169             (fsp->access_mask & write_access_flags)) {
170                 errno = EACCES;
171                 return -1;
172         }
173
174         return SMB_VFS_NEXT_OPENAT(handle, dirfsp, smb_fname, fsp, how);
175 }
176
177 static int vfs_worm_fntimes(vfs_handle_struct *handle,
178                             files_struct *fsp,
179                             struct smb_file_time *ft)
180 {
181         if (fsp_is_readonly(handle, fsp)) {
182                 errno = EACCES;
183                 return -1;
184         }
185
186         return SMB_VFS_NEXT_FNTIMES(handle, fsp, ft);
187 }
188
189 static int vfs_worm_fchmod(vfs_handle_struct *handle,
190                            files_struct *fsp,
191                            mode_t mode)
192 {
193         if (fsp_is_readonly(handle, fsp)) {
194                 errno = EACCES;
195                 return -1;
196         }
197
198         return SMB_VFS_NEXT_FCHMOD(handle, fsp, mode);
199 }
200
201 static int vfs_worm_fchown(vfs_handle_struct *handle,
202                            files_struct *fsp,
203                            uid_t uid,
204                            gid_t gid)
205 {
206         if (fsp_is_readonly(handle, fsp)) {
207                 errno = EACCES;
208                 return -1;
209         }
210
211         return SMB_VFS_NEXT_FCHOWN(handle, fsp, uid, gid);
212 }
213
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 {
220         if (is_readonly(handle, smb_fname_src)) {
221                 errno = EACCES;
222                 return -1;
223         }
224
225         return SMB_VFS_NEXT_RENAMEAT(
226                 handle, srcfsp, smb_fname_src, dstfsp, smb_fname_dst);
227 }
228
229 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         if (fsp_is_readonly(handle, fsp)) {
237                 errno = EACCES;
238                 return -1;
239         }
240
241         return SMB_VFS_NEXT_FSETXATTR(handle, fsp, name, value, size, flags);
242 }
243
244 static int vfs_worm_fremotexattr(struct vfs_handle_struct *handle,
245                                  struct files_struct *fsp,
246                                  const char *name)
247 {
248         if (fsp_is_readonly(handle, fsp)) {
249                 errno = EACCES;
250                 return -1;
251         }
252
253         return SMB_VFS_NEXT_FREMOVEXATTR(handle, fsp, name);
254 }
255
256 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         struct smb_filename *full_fname = NULL;
262         bool readonly;
263
264         full_fname = full_path_from_dirfsp_atname(talloc_tos(),
265                                                   dirfsp,
266                                                   smb_fname);
267         if (full_fname == NULL) {
268                 return -1;
269         }
270
271         readonly = is_readonly(handle, full_fname);
272
273         TALLOC_FREE(full_fname);
274
275         if (readonly) {
276                 errno = EACCES;
277                 return -1;
278         }
279
280         return SMB_VFS_NEXT_UNLINKAT(handle, dirfsp, smb_fname, flags);
281 }
282
283 static NTSTATUS vfs_worm_fset_dos_attributes(struct vfs_handle_struct *handle,
284                                              struct files_struct *fsp,
285                                              uint32_t dosmode)
286 {
287         if (fsp_is_readonly(handle, fsp)) {
288                 return NT_STATUS_ACCESS_DENIED;
289         }
290
291         return SMB_VFS_NEXT_FSET_DOS_ATTRIBUTES(handle, fsp, dosmode);
292 }
293
294 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         if (fsp_is_readonly(handle, fsp)) {
300                 return NT_STATUS_ACCESS_DENIED;
301         }
302
303         return SMB_VFS_NEXT_FSET_NT_ACL(handle, fsp, security_info_sent, psd);
304 }
305
306 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         if (fsp_is_readonly(handle, fsp)) {
312                 errno = EACCES;
313                 return -1;
314         }
315
316         return SMB_VFS_NEXT_SYS_ACL_SET_FD(handle, fsp, type, theacl);
317 }
318
319 static int vfs_worm_sys_acl_delete_def_fd(vfs_handle_struct *handle,
320                                           struct files_struct *fsp)
321 {
322         if (fsp_is_readonly(handle, fsp)) {
323                 errno = EACCES;
324                 return -1;
325         }
326
327         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 NTSTATUS vfs_worm_init(TALLOC_CTX *ctx)
349 {
350         NTSTATUS ret;
351
352         ret = smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "worm",
353                                &vfs_worm_fns);
354         if (!NT_STATUS_IS_OK(ret)) {
355                 return ret;
356         }
357
358         return ret;
359 }