LCOV - code coverage report
Current view: top level - source3/modules - vfs_worm.c (source / functions) Hit Total Coverage
Test: coverage report for master 2f515e9b Lines: 66 116 56.9 %
Date: 2024-04-21 15:09:00 Functions: 12 18 66.7 %

          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             : }

Generated by: LCOV version 1.14