2013-11-20 15:09:47 +04:00
/*
* VFS module to disallow writes for older files
*
* Copyright ( C ) 2013 , Volker Lendecke
2024-01-08 16:25:45 +03:00
* Copyright ( C ) 2023 - 2024 , Björn Jacke
2013-11-20 15:09:47 +04:00
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 3 of the License , or
* ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , see < http : //www.gnu.org/licenses/>.
*/
# include "includes.h"
# include "smbd/smbd.h"
# include "system/filesys.h"
# include "libcli/security/security.h"
2023-12-30 21:53:36 +03:00
struct worm_config_data {
double grace_period ;
} ;
2023-12-30 23:01:04 +03:00
static const uint32_t write_access_flags = FILE_WRITE_DATA | FILE_APPEND_DATA |
FILE_WRITE_ATTRIBUTES |
DELETE_ACCESS | WRITE_DAC_ACCESS |
2024-01-07 16:42:20 +03:00
WRITE_OWNER_ACCESS | FILE_WRITE_EA ;
2023-12-30 21:53:36 +03:00
static int vfs_worm_connect ( struct vfs_handle_struct * handle ,
const char * service , const char * user )
{
struct worm_config_data * config = NULL ;
int ret ;
ret = SMB_VFS_NEXT_CONNECT ( handle , service , user ) ;
if ( ret < 0 ) {
return ret ;
}
if ( IS_IPC ( handle - > conn ) | | IS_PRINT ( handle - > conn ) ) {
return 0 ;
}
config = talloc_zero ( handle - > conn , struct worm_config_data ) ;
if ( config = = NULL ) {
DBG_ERR ( " talloc_zero() failed \n " ) ;
errno = ENOMEM ;
return - 1 ;
}
config - > grace_period = lp_parm_int ( SNUM ( handle - > conn ) , " worm " ,
" grace_period " , 3600 ) ;
SMB_VFS_HANDLE_SET_DATA ( handle , config ,
NULL , struct worm_config_data ,
return - 1 ) ;
return 0 ;
}
2023-12-30 22:45:31 +03:00
static bool is_readonly ( vfs_handle_struct * handle ,
const struct smb_filename * smb_fname )
{
double age ;
struct worm_config_data * config = NULL ;
SMB_VFS_HANDLE_GET_DATA ( handle ,
config ,
struct worm_config_data ,
return true ) ;
if ( ! VALID_STAT ( smb_fname - > st ) ) {
goto out ;
}
age = timespec_elapsed ( & smb_fname - > st . st_ex_ctime ) ;
if ( age > config - > grace_period ) {
return true ;
}
2024-01-04 14:48:59 +03:00
out :
return false ;
}
static bool fsp_is_readonly ( vfs_handle_struct * handle , files_struct * fsp )
{
double age ;
struct worm_config_data * config = NULL ;
SMB_VFS_HANDLE_GET_DATA ( handle ,
config ,
struct worm_config_data ,
return true ) ;
if ( ! VALID_STAT ( fsp - > fsp_name - > st ) ) {
goto out ;
}
age = timespec_elapsed ( & fsp - > fsp_name - > st . st_ex_ctime ) ;
if ( age > config - > grace_period ) {
return true ;
}
2023-12-30 22:45:31 +03:00
out :
return false ;
}
2013-11-20 15:09:47 +04:00
static NTSTATUS vfs_worm_create_file ( vfs_handle_struct * handle ,
struct smb_request * req ,
2021-11-23 14:29:17 +03:00
struct files_struct * dirfsp ,
2013-11-20 15:09:47 +04:00
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 ,
2019-08-07 23:00:11 +03:00
const struct smb2_lease * lease ,
2013-11-20 15:09:47 +04:00
uint64_t allocation_size ,
uint32_t private_flags ,
struct security_descriptor * sd ,
struct ea_list * ea_list ,
files_struct * * result ,
2014-11-26 16:12:51 +03:00
int * pinfo ,
const struct smb2_create_blobs * in_context_blobs ,
struct smb2_create_blobs * out_context_blobs )
2013-11-20 15:09:47 +04:00
{
NTSTATUS status ;
2023-12-30 22:45:31 +03:00
bool readonly ;
2023-12-30 21:53:36 +03:00
2023-12-30 22:45:31 +03:00
readonly = is_readonly ( handle , smb_fname ) ;
2013-11-20 15:09:47 +04:00
if ( readonly & & ( access_mask & write_access_flags ) ) {
return NT_STATUS_ACCESS_DENIED ;
}
status = SMB_VFS_NEXT_CREATE_FILE (
2021-11-23 14:29:17 +03:00
handle , req , dirfsp , smb_fname , access_mask ,
2013-11-20 15:09:47 +04:00
share_access , create_disposition , create_options ,
2013-08-21 17:56:14 +04:00
file_attributes , oplock_request , lease , allocation_size ,
2014-11-26 16:12:51 +03:00
private_flags , sd , ea_list , result , pinfo ,
in_context_blobs , out_context_blobs ) ;
2013-11-20 15:09:47 +04:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
return status ;
}
/*
* Access via MAXIMUM_ALLOWED_ACCESS ?
*/
if ( readonly & & ( ( * result ) - > access_mask & write_access_flags ) ) {
2022-02-01 19:47:29 +03:00
close_file_free ( req , result , NORMAL_CLOSE ) ;
2013-11-20 15:09:47 +04:00
return NT_STATUS_ACCESS_DENIED ;
}
return NT_STATUS_OK ;
}
2024-01-04 14:48:59 +03:00
static int vfs_worm_openat ( vfs_handle_struct * handle ,
const struct files_struct * dirfsp ,
const struct smb_filename * smb_fname ,
files_struct * fsp ,
const struct vfs_open_how * how )
{
if ( is_readonly ( handle , smb_fname ) & &
( fsp - > access_mask & write_access_flags ) ) {
errno = EACCES ;
return - 1 ;
}
return SMB_VFS_NEXT_OPENAT ( handle , dirfsp , smb_fname , fsp , how ) ;
}
static int vfs_worm_fntimes ( vfs_handle_struct * handle ,
files_struct * fsp ,
struct smb_file_time * ft )
{
if ( fsp_is_readonly ( handle , fsp ) ) {
errno = EACCES ;
return - 1 ;
}
return SMB_VFS_NEXT_FNTIMES ( handle , fsp , ft ) ;
}
static int vfs_worm_fchmod ( vfs_handle_struct * handle ,
files_struct * fsp ,
mode_t mode )
{
if ( fsp_is_readonly ( handle , fsp ) ) {
errno = EACCES ;
return - 1 ;
}
return SMB_VFS_NEXT_FCHMOD ( handle , fsp , mode ) ;
}
static int vfs_worm_fchown ( vfs_handle_struct * handle ,
files_struct * fsp ,
uid_t uid ,
gid_t gid )
{
if ( fsp_is_readonly ( handle , fsp ) ) {
errno = EACCES ;
return - 1 ;
}
return SMB_VFS_NEXT_FCHOWN ( handle , fsp , uid , gid ) ;
}
static int vfs_worm_renameat ( vfs_handle_struct * handle ,
files_struct * srcfsp ,
const struct smb_filename * smb_fname_src ,
files_struct * dstfsp ,
const struct smb_filename * smb_fname_dst )
{
if ( is_readonly ( handle , smb_fname_src ) ) {
errno = EACCES ;
return - 1 ;
}
return SMB_VFS_NEXT_RENAMEAT (
handle , srcfsp , smb_fname_src , dstfsp , smb_fname_dst ) ;
}
static int vfs_worm_fsetxattr ( struct vfs_handle_struct * handle ,
struct files_struct * fsp ,
const char * name ,
const void * value ,
size_t size ,
int flags )
{
if ( fsp_is_readonly ( handle , fsp ) ) {
errno = EACCES ;
return - 1 ;
}
return SMB_VFS_NEXT_FSETXATTR ( handle , fsp , name , value , size , flags ) ;
}
static int vfs_worm_fremotexattr ( struct vfs_handle_struct * handle ,
struct files_struct * fsp ,
const char * name )
{
if ( fsp_is_readonly ( handle , fsp ) ) {
errno = EACCES ;
return - 1 ;
}
return SMB_VFS_NEXT_FREMOVEXATTR ( handle , fsp , name ) ;
}
static int vfs_worm_unlinkat ( vfs_handle_struct * handle ,
struct files_struct * dirfsp ,
const struct smb_filename * smb_fname ,
int flags )
{
struct smb_filename * full_fname = NULL ;
bool readonly ;
full_fname = full_path_from_dirfsp_atname ( talloc_tos ( ) ,
dirfsp ,
smb_fname ) ;
if ( full_fname = = NULL ) {
return - 1 ;
}
readonly = is_readonly ( handle , full_fname ) ;
TALLOC_FREE ( full_fname ) ;
if ( readonly ) {
errno = EACCES ;
return - 1 ;
}
return SMB_VFS_NEXT_UNLINKAT ( handle , dirfsp , smb_fname , flags ) ;
}
static NTSTATUS vfs_worm_fset_dos_attributes ( struct vfs_handle_struct * handle ,
struct files_struct * fsp ,
uint32_t dosmode )
{
if ( fsp_is_readonly ( handle , fsp ) ) {
return NT_STATUS_ACCESS_DENIED ;
}
return SMB_VFS_NEXT_FSET_DOS_ATTRIBUTES ( handle , fsp , dosmode ) ;
}
static NTSTATUS vfs_worm_fset_nt_acl ( vfs_handle_struct * handle ,
files_struct * fsp ,
uint32_t security_info_sent ,
const struct security_descriptor * psd )
{
if ( fsp_is_readonly ( handle , fsp ) ) {
return NT_STATUS_ACCESS_DENIED ;
}
return SMB_VFS_NEXT_FSET_NT_ACL ( handle , fsp , security_info_sent , psd ) ;
}
static int vfs_worm_sys_acl_set_fd ( vfs_handle_struct * handle ,
struct files_struct * fsp ,
SMB_ACL_TYPE_T type ,
SMB_ACL_T theacl )
{
if ( fsp_is_readonly ( handle , fsp ) ) {
errno = EACCES ;
return - 1 ;
}
return SMB_VFS_NEXT_SYS_ACL_SET_FD ( handle , fsp , type , theacl ) ;
}
static int vfs_worm_sys_acl_delete_def_fd ( vfs_handle_struct * handle ,
struct files_struct * fsp )
{
if ( fsp_is_readonly ( handle , fsp ) ) {
errno = EACCES ;
return - 1 ;
}
return SMB_VFS_NEXT_SYS_ACL_DELETE_DEF_FD ( handle , fsp ) ;
}
2013-11-20 15:09:47 +04:00
static struct vfs_fn_pointers vfs_worm_fns = {
2023-12-30 21:53:36 +03:00
. connect_fn = vfs_worm_connect ,
2013-11-20 15:09:47 +04:00
. create_file_fn = vfs_worm_create_file ,
2024-01-04 14:48:59 +03:00
. openat_fn = vfs_worm_openat ,
. fntimes_fn = vfs_worm_fntimes ,
. fchmod_fn = vfs_worm_fchmod ,
. fchown_fn = vfs_worm_fchown ,
. renameat_fn = vfs_worm_renameat ,
. fsetxattr_fn = vfs_worm_fsetxattr ,
. fremovexattr_fn = vfs_worm_fremotexattr ,
. unlinkat_fn = vfs_worm_unlinkat ,
. fset_dos_attributes_fn = vfs_worm_fset_dos_attributes ,
. fset_nt_acl_fn = vfs_worm_fset_nt_acl ,
. sys_acl_set_fd_fn = vfs_worm_sys_acl_set_fd ,
. sys_acl_delete_def_fd_fn = vfs_worm_sys_acl_delete_def_fd ,
2013-11-20 15:09:47 +04:00
} ;
2017-12-16 01:32:12 +03:00
static_decl_vfs ;
2017-04-20 22:24:43 +03:00
NTSTATUS vfs_worm_init ( TALLOC_CTX * ctx )
2013-11-20 15:09:47 +04:00
{
NTSTATUS ret ;
ret = smb_register_vfs ( SMB_VFS_INTERFACE_VERSION , " worm " ,
& vfs_worm_fns ) ;
if ( ! NT_STATUS_IS_OK ( ret ) ) {
return ret ;
}
return ret ;
}