From: Ralph Boehme Date: Wed, 20 May 2020 20:31:04 +0000 (+0200) Subject: vfs_virusfilter: implement SMB_VFS_OPENAT() X-Git-Tag: ldb-2.2.0~376 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=7d0616f5f10a8fbf15c1293d92b29bd31b633b67;p=thirdparty%2Fsamba.git vfs_virusfilter: implement SMB_VFS_OPENAT() Signed-off-by: Ralph Boehme Reviewed-by: Jeremy Allison --- diff --git a/source3/modules/vfs_virusfilter.c b/source3/modules/vfs_virusfilter.c index a9a3ac86409..bf7f25d181f 100644 --- a/source3/modules/vfs_virusfilter.c +++ b/source3/modules/vfs_virusfilter.c @@ -1312,6 +1312,187 @@ virusfilter_vfs_open_fail: return -1; } +static int virusfilter_vfs_openat(struct vfs_handle_struct *handle, + const struct files_struct *dirfsp, + const struct smb_filename *smb_fname_in, + struct files_struct *fsp, + int flags, + mode_t mode) +{ + TALLOC_CTX *mem_ctx = talloc_tos(); + struct virusfilter_config *config = NULL; + const char *cwd_fname = dirfsp->fsp_name->base_name; + virusfilter_result scan_result; + const char *fname = fsp->fsp_name->base_name; + char *dir_name = NULL; + const char *base_name = NULL; + int scan_errno = 0; + size_t test_prefix; + size_t test_suffix; + int rename_trap_count = 0; + int ret; + bool ok1; + char *sret = NULL; + struct smb_filename *smb_fname = NULL; + + /* + * For now assert this, so SMB_VFS_NEXT_STAT() below works. + */ + SMB_ASSERT(dirfsp->fh->fd == AT_FDCWD); + + SMB_VFS_HANDLE_GET_DATA(handle, config, + struct virusfilter_config, return -1); + + if (fsp->fsp_flags.is_directory) { + DBG_INFO("Not scanned: Directory: %s/\n", cwd_fname); + goto virusfilter_vfs_open_next; + } + + test_prefix = strlen(config->rename_prefix); + test_suffix = strlen(config->rename_suffix); + if (test_prefix > 0) { + rename_trap_count++; + } + if (test_suffix > 0) { + rename_trap_count++; + } + + smb_fname = cp_smb_filename(mem_ctx, smb_fname_in); + if (smb_fname == NULL) { + goto virusfilter_vfs_open_fail; + } + + if (is_named_stream(smb_fname)) { + DBG_INFO("Not scanned: only file backed streams can be scanned:" + " %s/%s\n", cwd_fname, fname); + goto virusfilter_vfs_open_next; + } + + if (!config->scan_on_open) { + DBG_INFO("Not scanned: scan on open is disabled: %s/%s\n", + cwd_fname, fname); + goto virusfilter_vfs_open_next; + } + + if (flags & O_TRUNC) { + DBG_INFO("Not scanned: Open flags have O_TRUNC: %s/%s\n", + cwd_fname, fname); + goto virusfilter_vfs_open_next; + } + + ret = SMB_VFS_NEXT_STAT(handle, smb_fname); + if (ret != 0) { + + /* + * Do not return immediately if !(flags & O_CREAT) && + * errno != ENOENT. + * Do not do this here or anywhere else. The module is + * stackable and there may be modules below, such as audit + * modules, which should be handled. + */ + goto virusfilter_vfs_open_next; + } + ret = S_ISREG(smb_fname->st.st_ex_mode); + if (ret == 0) { + DBG_INFO("Not scanned: Directory or special file: %s/%s\n", + cwd_fname, fname); + goto virusfilter_vfs_open_next; + } + if (config->max_file_size > 0 && + smb_fname->st.st_ex_size > config->max_file_size) + { + DBG_INFO("Not scanned: file size > max file size: %s/%s\n", + cwd_fname, fname); + goto virusfilter_vfs_open_next; + } + if (config->min_file_size > 0 && + smb_fname->st.st_ex_size < config->min_file_size) + { + DBG_INFO("Not scanned: file size < min file size: %s/%s\n", + cwd_fname, fname); + goto virusfilter_vfs_open_next; + } + + ok1 = is_in_path(fname, config->exclude_files, false); + if (config->exclude_files && ok1) + { + DBG_INFO("Not scanned: exclude files: %s/%s\n", + cwd_fname, fname); + goto virusfilter_vfs_open_next; + } + + if (config->infected_file_action == VIRUSFILTER_ACTION_QUARANTINE) { + sret = strstr_m(fname, config->quarantine_dir); + if (sret != NULL) { + scan_errno = config->infected_open_errno; + goto virusfilter_vfs_open_fail; + } + } + + if (test_prefix > 0 || test_suffix > 0) { + ok1 = parent_dirname(mem_ctx, fname, &dir_name, &base_name); + if (ok1) + { + if (test_prefix > 0) { + ret = strncmp(base_name, + config->rename_prefix, test_prefix); + if (ret != 0) { + test_prefix = 0; + } + } + if (test_suffix > 0) { + ret = strcmp(base_name + (strlen(base_name) + - test_suffix), + config->rename_suffix); + if (ret != 0) { + test_suffix = 0; + } + } + + TALLOC_FREE(dir_name); + + if ((rename_trap_count == 2 && test_prefix && + test_suffix) || (rename_trap_count == 1 && + (test_prefix || test_suffix))) + { + scan_errno = + config->infected_open_errno; + goto virusfilter_vfs_open_fail; + } + } + } + + scan_result = virusfilter_scan(handle, config, fsp); + + switch (scan_result) { + case VIRUSFILTER_RESULT_CLEAN: + break; + case VIRUSFILTER_RESULT_INFECTED: + scan_errno = config->infected_open_errno; + goto virusfilter_vfs_open_fail; + case VIRUSFILTER_RESULT_ERROR: + if (config->block_access_on_error) { + DBG_INFO("Block access\n"); + scan_errno = config->scan_error_open_errno; + goto virusfilter_vfs_open_fail; + } + break; + default: + scan_errno = config->scan_error_open_errno; + goto virusfilter_vfs_open_fail; + } + + TALLOC_FREE(smb_fname); + +virusfilter_vfs_open_next: + return SMB_VFS_NEXT_OPENAT(handle, dirfsp, smb_fname_in, fsp, flags, mode); + +virusfilter_vfs_open_fail: + TALLOC_FREE(smb_fname); + errno = (scan_errno != 0) ? scan_errno : EACCES; + return -1; +} + static int virusfilter_vfs_close( struct vfs_handle_struct *handle, files_struct *fsp) @@ -1517,6 +1698,7 @@ static struct vfs_fn_pointers vfs_virusfilter_fns = { .connect_fn = virusfilter_vfs_connect, .disconnect_fn = virusfilter_vfs_disconnect, .open_fn = virusfilter_vfs_open, + .openat_fn = virusfilter_vfs_openat, .close_fn = virusfilter_vfs_close, .unlinkat_fn = virusfilter_vfs_unlinkat, .renameat_fn = virusfilter_vfs_renameat,