diff --git a/source3/modules/vfs_worm.c b/source3/modules/vfs_worm.c index 3097419..eff81ce 100644 --- a/source3/modules/vfs_worm.c +++ b/source3/modules/vfs_worm.c @@ -22,77 +22,173 @@ #include "system/filesys.h" #include "libcli/security/security.h" +const uint32_t write_access_flags = + FILE_WRITE_DATA | FILE_APPEND_DATA | + FILE_WRITE_ATTRIBUTES | DELETE_ACCESS | + WRITE_DAC_ACCESS | WRITE_OWNER_ACCESS; + +/* + * Helpers + */ + +static bool is_readonly(vfs_handle_struct *handle, const struct smb_filename *smb_fname) +{ + bool readonly = false; + + if (VALID_STAT(smb_fname->st)) { + double age; + age = timespec_elapsed(&smb_fname->st.st_ex_ctime); + int grace_period = lp_parm_int(SNUM(handle->conn), "conn", + "grace_period", 3600); + if (age > grace_period) { + readonly = true; + } + } + + return readonly; +} + +static NTSTATUS filename_to_smb_filename_convert(connection_struct *conn, + const char *filename, + struct smb_filename **smb_fname) +{ + NTSTATUS status; + TALLOC_CTX *ctx = talloc_tos(); + char *name = talloc_strdup(ctx, filename); + + if (!name) { + return NT_STATUS_NO_MEMORY; + } + unix_format(name); + name = unix_clean_name(ctx, name); + if (!name) { + return NT_STATUS_NO_MEMORY; + } + trim_string(name, "/", "/"); + + status = unix_convert(ctx, conn, name, smb_fname, 0); + if (!NT_STATUS_IS_OK(status)) { + return NT_STATUS_NO_MEMORY; + } + + return NT_STATUS_OK; +} + +/* + * Wrappers + */ + static NTSTATUS vfs_worm_create_file(vfs_handle_struct *handle, - struct smb_request *req, - uint16_t root_dir_fid, - struct smb_filename *smb_fname, - uint32_t access_mask, - uint32_t share_access, - uint32_t create_disposition, - uint32_t create_options, - uint32_t file_attributes, - uint32_t oplock_request, - struct smb2_lease *lease, - uint64_t allocation_size, - uint32_t private_flags, - struct security_descriptor *sd, - struct ea_list *ea_list, - files_struct **result, - int *pinfo) + struct smb_request *req, + uint16_t root_dir_fid, + struct smb_filename *smb_fname, + uint32_t access_mask, + uint32_t share_access, + uint32_t create_disposition, + uint32_t create_options, + uint32_t file_attributes, + uint32_t oplock_request, + struct smb2_lease *lease, + uint64_t allocation_size, + uint32_t private_flags, + struct security_descriptor *sd, + struct ea_list *ea_list, + files_struct **result, + int *pinfo) { - bool readonly = false; - const uint32_t write_access_flags = - FILE_WRITE_DATA | FILE_APPEND_DATA | - FILE_WRITE_ATTRIBUTES | DELETE_ACCESS | - WRITE_DAC_ACCESS | WRITE_OWNER_ACCESS; - NTSTATUS status; - - if (VALID_STAT(smb_fname->st)) { - double age; - age = timespec_elapsed(&smb_fname->st.st_ex_ctime); - if (age > lp_parm_int(SNUM(handle->conn), "worm", - "grace_period", 3600)) { - readonly = true; - } - } - - if (readonly && (access_mask & write_access_flags)) { - return NT_STATUS_ACCESS_DENIED; - } - - status = SMB_VFS_NEXT_CREATE_FILE( - handle, req, root_dir_fid, smb_fname, access_mask, - share_access, create_disposition, create_options, - file_attributes, oplock_request, lease, allocation_size, - private_flags, sd, ea_list, result, pinfo); - if (!NT_STATUS_IS_OK(status)) { - return status; - } - - /* - * Access via MAXIMUM_ALLOWED_ACCESS? - */ - if (readonly && ((*result)->access_mask & write_access_flags)) { - close_file(req, *result, NORMAL_CLOSE); - return NT_STATUS_ACCESS_DENIED; - } - return NT_STATUS_OK; + bool readonly = false; + + if (readonly && (access_mask & write_access_flags)) { + return NT_STATUS_ACCESS_DENIED; + } + + NTSTATUS status = SMB_VFS_NEXT_CREATE_FILE( + handle, req, root_dir_fid, smb_fname, access_mask, + share_access, create_disposition, create_options, + file_attributes, oplock_request, lease, allocation_size, + private_flags, sd, ea_list, result, pinfo); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + /* + * Access via MAXIMUM_ALLOWED_ACCESS? + */ + if (readonly && ((*result)->access_mask & write_access_flags)) { + close_file(req, *result, NORMAL_CLOSE); + return NT_STATUS_ACCESS_DENIED; + } + return NT_STATUS_OK; } +static int vfs_worm_open(vfs_handle_struct *handle, + struct smb_filename *smb_fname, + files_struct *fsp, int flags, mode_t mode) +{ + bool readonly = is_readonly(handle, smb_fname); + + if (readonly && (fsp->access_mask & write_access_flags)) { + errno = EACCES; + return -1; + } + + return SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode); +} + +static int vfs_worm_ntimes(vfs_handle_struct *handle, + const struct smb_filename *smb_fname, + struct smb_file_time *ft) +{ + bool readonly = is_readonly(handle, smb_fname); + + if (readonly) { + errno = EACCES; + return -1; + } + + return SMB_VFS_NEXT_NTIMES(handle, smb_fname, ft); +} + +static int vfs_worm_chmod(vfs_handle_struct *handle, const char *file_name, mode_t mode) +{ + struct smb_filename *smb_fname; + bool readonly = 1; + + NTSTATUS status = filename_to_smb_filename_convert(handle->conn, file_name, &smb_fname); + + if (NT_STATUS_IS_OK(status)) { + readonly = is_readonly(handle, smb_fname); + } + + if (readonly) { + errno = EACCES; + return -1; + } + + return SMB_VFS_NEXT_CHMOD(handle, file_name, mode); +} + +/* + * Init && pointers table + */ + static struct vfs_fn_pointers vfs_worm_fns = { - .create_file_fn = vfs_worm_create_file, + .create_file_fn = vfs_worm_create_file, + .open_fn = vfs_worm_open, + .ntimes_fn = vfs_worm_ntimes, + .chmod_fn = vfs_worm_chmod }; NTSTATUS vfs_worm_init(void); NTSTATUS vfs_worm_init(void) { - NTSTATUS ret; + NTSTATUS ret; - ret = smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "worm", - &vfs_worm_fns); - if (!NT_STATUS_IS_OK(ret)) { - return ret; - } + ret = smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "worm", + &vfs_worm_fns); + if (!NT_STATUS_IS_OK(ret)) { + return ret; + } - return ret; + return ret; }