2 * Copyright (C) 2010-2016 SATOH Fumiyasu @ OSS Technology Corp., Japan
3 * Copyright (C) 2016-2017 Trever L. Adams
4 * Copyright (C) 2017 Ralph Boehme <slow@samba.org>
5 * Copyright (C) 2017 Jeremy Allison <jra@samba.org>
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.
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.
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/>.
21 #include "vfs_virusfilter_common.h"
22 #include "vfs_virusfilter_utils.h"
25 * Default configuration values
26 * ======================================================================
29 #define VIRUSFILTER_DEFAULT_QUARANTINE_PREFIX "virusfilter."
30 #define VIRUSFILTER_DEFAULT_QUARANTINE_SUFFIX ".infected"
31 #define VIRUSFILTER_DEFAULT_RENAME_PREFIX "virusfilter."
32 #define VIRUSFILTER_DEFAULT_RENAME_SUFFIX ".infected"
34 /* ====================================================================== */
36 enum virusfilter_scanner_enum {
37 VIRUSFILTER_SCANNER_CLAMAV,
38 VIRUSFILTER_SCANNER_FSAV,
39 VIRUSFILTER_SCANNER_SOPHOS
42 static const struct enum_list scanner_list[] = {
43 { VIRUSFILTER_SCANNER_CLAMAV, "clamav" },
44 { VIRUSFILTER_SCANNER_FSAV, "fsav" },
45 { VIRUSFILTER_SCANNER_SOPHOS, "sophos" },
49 static const struct enum_list virusfilter_actions[] = {
50 { VIRUSFILTER_ACTION_QUARANTINE, "quarantine" },
51 { VIRUSFILTER_ACTION_RENAME, "rename" },
52 { VIRUSFILTER_ACTION_DELETE, "delete" },
54 /* alias for "delete" */
55 { VIRUSFILTER_ACTION_DELETE, "remove" },
57 /* alias for "delete" */
58 { VIRUSFILTER_ACTION_DELETE, "unlink" },
59 { VIRUSFILTER_ACTION_DO_NOTHING, "nothing" },
63 static int virusfilter_config_destructor(struct virusfilter_config *config)
65 TALLOC_FREE(config->backend);
70 * This is adapted from vfs_recycle module.
71 * Caller must have become_root();
73 static bool quarantine_directory_exist(
74 struct vfs_handle_struct *handle,
78 struct smb_filename smb_fname = {
79 .base_name = discard_const_p(char, dname)
82 ret = SMB_VFS_STAT(handle->conn, &smb_fname);
84 return S_ISDIR(smb_fname.st.st_ex_mode);
91 * Create directory tree
92 * @param conn connection
93 * @param dname Directory tree to be created
94 * @return Returns true for success
95 * This is adapted from vfs_recycle module.
96 * Caller must have become_root();
98 static bool quarantine_create_dir(
99 struct vfs_handle_struct *handle,
100 struct virusfilter_config *config,
105 char *new_dir = NULL;
106 char *tmp_str = NULL;
108 char *tok_str = NULL;
112 char *saveptr = NULL;
114 tmp_str = talloc_strdup(talloc_tos(), dname);
115 if (tmp_str == NULL) {
116 DBG_ERR("virusfilter-vfs: out of memory!\n");
122 len = strlen(dname)+1;
123 new_dir = (char *)talloc_size(talloc_tos(), len + 1);
124 if (new_dir == NULL) {
125 DBG_ERR("virusfilter-vfs: out of memory!\n");
130 if (dname[0] == '/') {
132 cat_len = strlcat(new_dir, "/", len + 1);
133 if (cat_len >= len+1) {
138 /* Create directory tree if necessary */
139 for (token = strtok_r(tok_str, "/", &saveptr);
141 token = strtok_r(NULL, "/", &saveptr))
143 cat_len = strlcat(new_dir, token, len + 1);
144 if (cat_len >= len+1) {
147 ok = quarantine_directory_exist(handle, new_dir);
149 DBG_DEBUG("quarantine: dir %s already exists\n",
152 struct smb_filename *smb_fname = NULL;
154 DBG_INFO("quarantine: creating new dir %s\n", new_dir);
156 smb_fname = synthetic_smb_fname(talloc_tos(),
162 if (smb_fname == NULL) {
166 ret = SMB_VFS_NEXT_MKDIRAT(handle,
167 handle->conn->cwd_fsp,
169 config->quarantine_dir_mode);
171 TALLOC_FREE(smb_fname);
173 DBG_WARNING("quarantine: mkdirat failed for %s "
174 "with error: %s\n", new_dir,
179 TALLOC_FREE(smb_fname);
181 cat_len = strlcat(new_dir, "/", len + 1);
182 if (cat_len >= len + 1) {
189 TALLOC_FREE(tmp_str);
190 TALLOC_FREE(new_dir);
194 static int virusfilter_vfs_connect(
195 struct vfs_handle_struct *handle,
199 int snum = SNUM(handle->conn);
200 struct virusfilter_config *config = NULL;
201 const char *exclude_files = NULL;
202 const char *temp_quarantine_dir_mode = NULL;
205 enum virusfilter_scanner_enum backend;
206 int connect_timeout = 0;
210 config = talloc_zero(handle, struct virusfilter_config);
211 if (config == NULL) {
212 DBG_ERR("talloc_zero failed\n");
215 talloc_set_destructor(config, virusfilter_config_destructor);
217 SMB_VFS_HANDLE_SET_DATA(handle, config, NULL,
218 struct virusfilter_config, return -1);
220 config->scan_request_limit = lp_parm_int(
221 snum, "virusfilter", "scan request limit", 0);
223 config->scan_on_open = lp_parm_bool(
224 snum, "virusfilter", "scan on open", true);
226 config->scan_on_close = lp_parm_bool(
227 snum, "virusfilter", "scan on close", false);
229 config->max_nested_scan_archive = lp_parm_int(
230 snum, "virusfilter", "max nested scan archive", 1);
232 config->scan_archive = lp_parm_bool(
233 snum, "virusfilter", "scan archive", false);
235 config->scan_mime = lp_parm_bool(
236 snum, "virusfilter", "scan mime", false);
238 config->max_file_size = (ssize_t)lp_parm_ulong(
239 snum, "virusfilter", "max file size", 100000000L);
241 config->min_file_size = (ssize_t)lp_parm_ulong(
242 snum, "virusfilter", "min file size", 10);
244 exclude_files = lp_parm_const_string(
245 snum, "virusfilter", "exclude files", NULL);
246 if (exclude_files != NULL) {
247 set_namearray(&config->exclude_files, exclude_files);
250 config->cache_entry_limit = lp_parm_int(
251 snum, "virusfilter", "cache entry limit", 100);
253 config->cache_time_limit = lp_parm_int(
254 snum, "virusfilter", "cache time limit", 10);
256 config->infected_file_action = lp_parm_enum(
257 snum, "virusfilter", "infected file action",
258 virusfilter_actions, VIRUSFILTER_ACTION_DO_NOTHING);
260 config->infected_file_command = lp_parm_const_string(
261 snum, "virusfilter", "infected file command", NULL);
263 config->scan_error_command = lp_parm_const_string(
264 snum, "virusfilter", "scan error command", NULL);
266 config->block_access_on_error = lp_parm_bool(
267 snum, "virusfilter", "block access on error", false);
269 tmp = talloc_asprintf(config, "%s/.quarantine",
270 handle->conn->connectpath);
272 config->quarantine_dir = lp_parm_const_string(
273 snum, "virusfilter", "quarantine directory",
274 tmp ? tmp : "/tmp/.quarantine");
276 if (tmp != config->quarantine_dir) {
280 temp_quarantine_dir_mode = lp_parm_const_string(
281 snum, "virusfilter", "quarantine directory mode", "0755");
282 if (temp_quarantine_dir_mode != NULL) {
283 unsigned int mode = 0;
284 sscanf(temp_quarantine_dir_mode, "%o", &mode);
285 config->quarantine_dir_mode = mode;
288 config->quarantine_prefix = lp_parm_const_string(
289 snum, "virusfilter", "quarantine prefix",
290 VIRUSFILTER_DEFAULT_QUARANTINE_PREFIX);
292 config->quarantine_suffix = lp_parm_const_string(
293 snum, "virusfilter", "quarantine suffix",
294 VIRUSFILTER_DEFAULT_QUARANTINE_SUFFIX);
297 * Make sure prefixes and suffixes do not contain directory
300 sret = strstr(config->quarantine_prefix, "/");
302 DBG_ERR("quarantine prefix must not contain directory "
303 "delimiter(s) such as '/' (%s replaced with %s)\n",
304 config->quarantine_prefix,
305 VIRUSFILTER_DEFAULT_QUARANTINE_PREFIX);
306 config->quarantine_prefix =
307 VIRUSFILTER_DEFAULT_QUARANTINE_PREFIX;
309 sret = strstr(config->quarantine_suffix, "/");
311 DBG_ERR("quarantine suffix must not contain directory "
312 "delimiter(s) such as '/' (%s replaced with %s)\n",
313 config->quarantine_suffix,
314 VIRUSFILTER_DEFAULT_QUARANTINE_SUFFIX);
315 config->quarantine_suffix =
316 VIRUSFILTER_DEFAULT_QUARANTINE_SUFFIX;
319 config->quarantine_keep_tree = lp_parm_bool(
320 snum, "virusfilter", "quarantine keep tree", true);
322 config->quarantine_keep_name = lp_parm_bool(
323 snum, "virusfilter", "quarantine keep name", true);
325 config->rename_prefix = lp_parm_const_string(
326 snum, "virusfilter", "rename prefix",
327 VIRUSFILTER_DEFAULT_RENAME_PREFIX);
329 config->rename_suffix = lp_parm_const_string(
330 snum, "virusfilter", "rename suffix",
331 VIRUSFILTER_DEFAULT_RENAME_SUFFIX);
334 * Make sure prefixes and suffixes do not contain directory
337 sret = strstr(config->rename_prefix, "/");
339 DBG_ERR("rename prefix must not contain directory "
340 "delimiter(s) such as '/' (%s replaced with %s)\n",
341 config->rename_prefix,
342 VIRUSFILTER_DEFAULT_RENAME_PREFIX);
343 config->rename_prefix =
344 VIRUSFILTER_DEFAULT_RENAME_PREFIX;
346 sret = strstr(config->rename_suffix, "/");
348 DBG_ERR("rename suffix must not contain directory "
349 "delimiter(s) such as '/' (%s replaced with %s)\n",
350 config->rename_suffix,
351 VIRUSFILTER_DEFAULT_RENAME_SUFFIX);
352 config->rename_suffix =
353 VIRUSFILTER_DEFAULT_RENAME_SUFFIX;
356 config->infected_open_errno = lp_parm_int(
357 snum, "virusfilter", "infected file errno on open", EACCES);
359 config->infected_close_errno = lp_parm_int(
360 snum, "virusfilter", "infected file errno on close", 0);
362 config->scan_error_open_errno = lp_parm_int(
363 snum, "virusfilter", "scan error errno on open", EACCES);
365 config->scan_error_close_errno = lp_parm_int(
366 snum, "virusfilter", "scan error errno on close", 0);
368 config->socket_path = lp_parm_const_string(
369 snum, "virusfilter", "socket path", NULL);
371 /* canonicalize socket_path */
372 if (config->socket_path != NULL && config->socket_path[0] != '/') {
373 DBG_ERR("socket path must be an absolute path. "
374 "Using backend default\n");
375 config->socket_path = NULL;
377 if (config->socket_path != NULL) {
378 config->socket_path = canonicalize_absolute_path(
379 handle, config->socket_path);
380 if (config->socket_path == NULL) {
386 connect_timeout = lp_parm_int(snum, "virusfilter",
387 "connect timeout", 30000);
389 io_timeout = lp_parm_int(snum, "virusfilter", "io timeout", 60000);
391 config->io_h = virusfilter_io_new(config, connect_timeout, io_timeout);
392 if (config->io_h == NULL) {
393 DBG_ERR("virusfilter_io_new failed");
397 if (config->cache_entry_limit > 0) {
398 config->cache = virusfilter_cache_new(handle,
399 config->cache_entry_limit,
400 config->cache_time_limit);
401 if (config->cache == NULL) {
402 DBG_ERR("Initializing cache failed: Cache disabled\n");
408 * Check quarantine directory now to save processing
409 * and becoming root over and over.
411 if (config->infected_file_action == VIRUSFILTER_ACTION_QUARANTINE) {
416 * Do SMB_VFS_NEXT_MKDIR(config->quarantine_dir)
420 dir_exists = quarantine_directory_exist(handle,
421 config->quarantine_dir);
423 DBG_DEBUG("Creating quarantine directory: %s\n",
424 config->quarantine_dir);
425 ok = quarantine_create_dir(handle, config,
426 config->quarantine_dir);
430 DBG_ERR("Creating quarantine directory %s "
432 config->quarantine_dir,
439 * Now that the frontend options are initialized, load the configured
443 backend = (enum virusfilter_scanner_enum)lp_parm_enum(snum,
448 if (backend == (enum virusfilter_scanner_enum)-1) {
449 DBG_ERR("No AV-Scanner configured, "
450 "please set \"virusfilter:scanner\"\n");
455 case VIRUSFILTER_SCANNER_SOPHOS:
456 ret = virusfilter_sophos_init(config);
458 case VIRUSFILTER_SCANNER_FSAV:
459 ret = virusfilter_fsav_init(config);
461 case VIRUSFILTER_SCANNER_CLAMAV:
462 ret = virusfilter_clamav_init(config);
465 DBG_ERR("Unhandled scanner %d\n", backend);
469 DBG_ERR("Scanner backend init failed\n");
473 if (config->backend->fns->connect != NULL) {
474 ret = config->backend->fns->connect(handle, config, svc, user);
480 return SMB_VFS_NEXT_CONNECT(handle, svc, user);
483 static void virusfilter_vfs_disconnect(struct vfs_handle_struct *handle)
485 struct virusfilter_config *config = NULL;
487 SMB_VFS_HANDLE_GET_DATA(handle, config,
488 struct virusfilter_config, return);
490 if (config->backend->fns->disconnect != NULL) {
491 config->backend->fns->disconnect(handle);
494 free_namearray(config->exclude_files);
495 virusfilter_io_disconnect(config->io_h);
497 SMB_VFS_NEXT_DISCONNECT(handle);
500 static int virusfilter_set_module_env(TALLOC_CTX *mem_ctx,
501 struct virusfilter_config *config,
506 ret = virusfilter_env_set(mem_ctx, env_list, "VIRUSFILTER_VERSION",
507 VIRUSFILTER_VERSION);
511 ret = virusfilter_env_set(mem_ctx, env_list, "VIRUSFILTER_MODULE_NAME",
512 config->backend->name);
517 if (config->backend->version != 0) {
518 char *version = NULL;
520 version = talloc_asprintf(talloc_tos(), "%u",
521 config->backend->version);
522 if (version == NULL) {
525 ret = virusfilter_env_set(mem_ctx, env_list,
526 "VIRUSFILTER_MODULE_VERSION",
528 TALLOC_FREE(version);
537 static char *quarantine_check_tree(TALLOC_CTX *mem_ctx,
538 struct vfs_handle_struct *handle,
539 struct virusfilter_config *config,
540 const struct smb_filename *smb_fname,
544 char *temp_path = NULL;
545 char *q_dir_out = NULL;
548 temp_path = talloc_asprintf(talloc_tos(), "%s/%s", q_dir_in, cwd_fname);
549 if (temp_path == NULL) {
550 DBG_ERR("talloc_asprintf failed\n");
555 ok = quarantine_directory_exist(handle, temp_path);
558 DBG_DEBUG("quarantine: directory [%s] exists\n", temp_path);
559 q_dir_out = talloc_move(mem_ctx, &temp_path);
563 DBG_DEBUG("quarantine: Creating directory %s\n", temp_path);
566 ok = quarantine_create_dir(handle, config, temp_path);
569 DBG_NOTICE("Could not create quarantine directory [%s], "
570 "ignoring for [%s]\n",
571 temp_path, smb_fname_str_dbg(smb_fname));
575 q_dir_out = talloc_move(mem_ctx, &temp_path);
578 TALLOC_FREE(temp_path);
582 static virusfilter_action infected_file_action_quarantine(
583 struct vfs_handle_struct *handle,
584 struct virusfilter_config *config,
586 const struct files_struct *fsp,
587 const char **filepath_newp)
589 TALLOC_CTX *frame = talloc_stackframe();
590 connection_struct *conn = handle->conn;
591 char *cwd_fname = fsp->conn->cwd_fsp->fsp_name->base_name;
592 char *fname = fsp->fsp_name->base_name;
593 const struct smb_filename *smb_fname = fsp->fsp_name;
594 struct smb_filename *q_smb_fname = NULL;
596 char *q_prefix = NULL;
597 char *q_suffix = NULL;
598 char *q_filepath = NULL;
599 char *dir_name = NULL;
600 const char *base_name = NULL;
601 char *rand_filename_component = NULL;
602 virusfilter_action action = VIRUSFILTER_ACTION_QUARANTINE;
607 q_dir = virusfilter_string_sub(frame, conn,
608 config->quarantine_dir);
609 q_prefix = virusfilter_string_sub(frame, conn,
610 config->quarantine_prefix);
611 q_suffix = virusfilter_string_sub(frame, conn,
612 config->quarantine_suffix);
613 if (q_dir == NULL || q_prefix == NULL || q_suffix == NULL) {
614 DBG_ERR("Quarantine failed: %s/%s: Cannot allocate "
615 "memory\n", cwd_fname, fname);
616 action = VIRUSFILTER_ACTION_DO_NOTHING;
620 if (config->quarantine_keep_name || config->quarantine_keep_tree) {
621 ok = parent_dirname(frame, smb_fname->base_name,
622 &dir_name, &base_name);
624 DBG_ERR("parent_dirname failed\n");
625 action = VIRUSFILTER_ACTION_DO_NOTHING;
629 if (config->quarantine_keep_tree) {
632 tree = quarantine_check_tree(frame, handle, config,
637 * If we can't create the tree, just move it
638 * into the toplevel quarantine dir.
646 /* Get a 16 byte + \0 random filename component. */
647 rand_filename_component = generate_random_str(frame, 16);
648 if (rand_filename_component == NULL) {
649 DBG_ERR("generate_random_str failed\n");
650 action = VIRUSFILTER_ACTION_DO_NOTHING;
654 if (config->quarantine_keep_name) {
655 q_filepath = talloc_asprintf(frame, "%s/%s%s%s-%s",
658 rand_filename_component);
660 q_filepath = talloc_asprintf(frame, "%s/%s%s",
662 rand_filename_component);
664 if (q_filepath == NULL) {
665 DBG_ERR("talloc_asprintf failed\n");
666 action = VIRUSFILTER_ACTION_DO_NOTHING;
670 q_smb_fname = synthetic_smb_fname(frame,
672 smb_fname->stream_name,
676 if (q_smb_fname == NULL) {
677 action = VIRUSFILTER_ACTION_DO_NOTHING;
682 ret = virusfilter_vfs_next_move(handle, smb_fname, q_smb_fname);
688 DBG_ERR("Quarantine [%s/%s] rename to %s failed: %s\n",
689 cwd_fname, fname, q_filepath, strerror(saved_errno));
691 action = VIRUSFILTER_ACTION_DO_NOTHING;
695 *filepath_newp = talloc_move(mem_ctx, &q_filepath);
702 static virusfilter_action infected_file_action_rename(
703 struct vfs_handle_struct *handle,
704 struct virusfilter_config *config,
706 const struct files_struct *fsp,
707 const char **filepath_newp)
709 TALLOC_CTX *frame = talloc_stackframe();
710 connection_struct *conn = handle->conn;
711 char *cwd_fname = fsp->conn->cwd_fsp->fsp_name->base_name;
712 char *fname = fsp->fsp_name->base_name;
713 const struct smb_filename *smb_fname = fsp->fsp_name;
714 struct smb_filename *q_smb_fname = NULL;
716 char *q_prefix = NULL;
717 char *q_suffix = NULL;
718 char *q_filepath = NULL;
719 const char *base_name = NULL;
720 virusfilter_action action = VIRUSFILTER_ACTION_RENAME;
725 q_prefix = virusfilter_string_sub(frame, conn,
726 config->rename_prefix);
727 q_suffix = virusfilter_string_sub(frame, conn,
728 config->rename_suffix);
729 if (q_prefix == NULL || q_suffix == NULL) {
730 DBG_ERR("Rename failed: %s/%s: Cannot allocate "
731 "memory\n", cwd_fname, fname);
732 action = VIRUSFILTER_ACTION_DO_NOTHING;
736 ok = parent_dirname(frame, fname, &q_dir, &base_name);
738 DBG_ERR("Rename failed: %s/%s: Cannot allocate "
739 "memory\n", cwd_fname, fname);
740 action = VIRUSFILTER_ACTION_DO_NOTHING;
745 DBG_ERR("Rename failed: %s/%s: Cannot allocate "
746 "memory\n", cwd_fname, fname);
747 action = VIRUSFILTER_ACTION_DO_NOTHING;
751 q_filepath = talloc_asprintf(frame, "%s/%s%s%s", q_dir,
752 q_prefix, base_name, q_suffix);
754 q_smb_fname = synthetic_smb_fname(frame, q_filepath,
755 smb_fname->stream_name, NULL,
758 if (q_smb_fname == NULL) {
759 action = VIRUSFILTER_ACTION_DO_NOTHING;
764 ret = virusfilter_vfs_next_move(handle, smb_fname, q_smb_fname);
771 DBG_ERR("Rename failed: %s/%s: Rename failed: %s\n",
772 cwd_fname, fname, strerror(saved_errno));
774 action = VIRUSFILTER_ACTION_DO_NOTHING;
778 *filepath_newp = talloc_move(mem_ctx, &q_filepath);
785 static virusfilter_action infected_file_action_delete(
786 struct vfs_handle_struct *handle,
787 const struct files_struct *fsp)
793 ret = SMB_VFS_NEXT_UNLINKAT(handle,
794 handle->conn->cwd_fsp,
802 DBG_ERR("Delete [%s/%s] failed: %s\n",
803 fsp->conn->cwd_fsp->fsp_name->base_name,
804 fsp->fsp_name->base_name,
805 strerror(saved_errno));
807 return VIRUSFILTER_ACTION_DO_NOTHING;
810 return VIRUSFILTER_ACTION_DELETE;
813 static virusfilter_action virusfilter_do_infected_file_action(
814 struct vfs_handle_struct *handle,
815 struct virusfilter_config *config,
817 const struct files_struct *fsp,
818 const char **filepath_newp)
820 virusfilter_action action;
822 *filepath_newp = NULL;
824 switch (config->infected_file_action) {
825 case VIRUSFILTER_ACTION_RENAME:
826 action = infected_file_action_rename(handle, config, mem_ctx,
830 case VIRUSFILTER_ACTION_QUARANTINE:
831 action = infected_file_action_quarantine(handle, config, mem_ctx,
835 case VIRUSFILTER_ACTION_DELETE:
836 action = infected_file_action_delete(handle, fsp);
839 case VIRUSFILTER_ACTION_DO_NOTHING:
841 action = VIRUSFILTER_ACTION_DO_NOTHING;
848 static virusfilter_action virusfilter_treat_infected_file(
849 struct vfs_handle_struct *handle,
850 struct virusfilter_config *config,
851 const struct files_struct *fsp,
855 connection_struct *conn = handle->conn;
856 char *cwd_fname = fsp->conn->cwd_fsp->fsp_name->base_name;
857 char *fname = fsp->fsp_name->base_name;
858 TALLOC_CTX *mem_ctx = talloc_tos();
860 virusfilter_action action;
861 const char *action_name = "UNKNOWN";
862 const char *filepath_q = NULL;
863 char *env_list = NULL;
864 char *command = NULL;
868 action = virusfilter_do_infected_file_action(handle, config, mem_ctx,
870 for (i=0; virusfilter_actions[i].name; i++) {
871 if (virusfilter_actions[i].value == action) {
872 action_name = virusfilter_actions[i].name;
876 DBG_WARNING("Infected file action: %s/%s: %s\n", cwd_fname,
879 if (!config->infected_file_command) {
883 ret = virusfilter_set_module_env(mem_ctx, config, &env_list);
887 ret = virusfilter_env_set(mem_ctx, &env_list,
888 "VIRUSFILTER_INFECTED_SERVICE_FILE_PATH",
893 if (report != NULL) {
894 ret = virusfilter_env_set(mem_ctx, &env_list,
895 "VIRUSFILTER_INFECTED_FILE_REPORT",
901 ret = virusfilter_env_set(mem_ctx, &env_list,
902 "VIRUSFILTER_INFECTED_FILE_ACTION",
907 if (filepath_q != NULL) {
908 ret = virusfilter_env_set(mem_ctx, &env_list,
909 "VIRUSFILTER_QUARANTINED_FILE_PATH",
916 ret = virusfilter_env_set(mem_ctx, &env_list,
917 "VIRUSFILTER_RESULT_IS_CACHE", "yes");
923 command = virusfilter_string_sub(mem_ctx, conn,
924 config->infected_file_command);
925 if (command == NULL) {
926 DBG_ERR("virusfilter_string_sub failed\n");
930 DBG_NOTICE("Infected file command line: %s/%s: %s\n", cwd_fname,
933 command_result = virusfilter_shell_run(mem_ctx, command, &env_list,
935 if (command_result != 0) {
936 DBG_ERR("Infected file command failed: %d\n", command_result);
939 DBG_DEBUG("Infected file command finished: %d\n", command_result);
942 TALLOC_FREE(env_list);
943 TALLOC_FREE(command);
948 static void virusfilter_treat_scan_error(
949 struct vfs_handle_struct *handle,
950 struct virusfilter_config *config,
951 const struct files_struct *fsp,
955 connection_struct *conn = handle->conn;
956 const char *cwd_fname = fsp->conn->cwd_fsp->fsp_name->base_name;
957 const char *fname = fsp->fsp_name->base_name;
958 TALLOC_CTX *mem_ctx = talloc_tos();
959 char *env_list = NULL;
960 char *command = NULL;
964 if (!config->scan_error_command) {
967 ret = virusfilter_set_module_env(mem_ctx, config, &env_list);
971 ret = virusfilter_env_set(mem_ctx, &env_list,
972 "VIRUSFILTER_SCAN_ERROR_SERVICE_FILE_PATH",
977 if (report != NULL) {
978 ret = virusfilter_env_set(mem_ctx, &env_list,
979 "VIRUSFILTER_SCAN_ERROR_REPORT",
986 ret = virusfilter_env_set(mem_ctx, &env_list,
987 "VIRUSFILTER_RESULT_IS_CACHE", "1");
993 command = virusfilter_string_sub(mem_ctx, conn,
994 config->scan_error_command);
995 if (command == NULL) {
996 DBG_ERR("virusfilter_string_sub failed\n");
1000 DBG_NOTICE("Scan error command line: %s/%s: %s\n", cwd_fname,
1003 command_result = virusfilter_shell_run(mem_ctx, command, &env_list,
1005 if (command_result != 0) {
1006 DBG_ERR("Scan error command failed: %d\n", command_result);
1010 TALLOC_FREE(env_list);
1011 TALLOC_FREE(command);
1014 static virusfilter_result virusfilter_scan(
1015 struct vfs_handle_struct *handle,
1016 struct virusfilter_config *config,
1017 const struct files_struct *fsp)
1019 virusfilter_result scan_result;
1020 char *scan_report = NULL;
1021 const char *fname = fsp->fsp_name->base_name;
1022 const char *cwd_fname = fsp->conn->cwd_fsp->fsp_name->base_name;
1023 struct virusfilter_cache_entry *scan_cache_e = NULL;
1024 bool is_cache = false;
1025 virusfilter_action file_action = VIRUSFILTER_ACTION_DO_NOTHING;
1026 bool add_scan_cache = true;
1029 if (config->cache) {
1030 DBG_DEBUG("Searching cache entry: fname: %s\n", fname);
1031 scan_cache_e = virusfilter_cache_get(config->cache,
1033 if (scan_cache_e != NULL) {
1034 DBG_DEBUG("Cache entry found: cached result: %d\n",
1035 scan_cache_e->result);
1037 scan_result = scan_cache_e->result;
1038 scan_report = scan_cache_e->report;
1039 goto virusfilter_scan_result_eval;
1041 DBG_DEBUG("Cache entry not found\n");
1044 if (config->backend->fns->scan_init != NULL) {
1045 scan_result = config->backend->fns->scan_init(config);
1046 if (scan_result != VIRUSFILTER_RESULT_OK) {
1047 scan_result = VIRUSFILTER_RESULT_ERROR;
1048 scan_report = talloc_asprintf(
1050 "Initializing scanner failed");
1051 goto virusfilter_scan_result_eval;
1055 scan_result = config->backend->fns->scan(handle, config, fsp,
1058 if (config->backend->fns->scan_end != NULL) {
1059 bool scan_end = true;
1061 if (config->scan_request_limit > 0) {
1063 config->scan_request_count++;
1064 if (config->scan_request_count >=
1065 config->scan_request_limit)
1068 config->scan_request_count = 0;
1072 config->backend->fns->scan_end(config);
1076 virusfilter_scan_result_eval:
1078 switch (scan_result) {
1079 case VIRUSFILTER_RESULT_CLEAN:
1080 DBG_INFO("Scan result: Clean: %s/%s\n", cwd_fname, fname);
1083 case VIRUSFILTER_RESULT_INFECTED:
1084 DBG_ERR("Scan result: Infected: %s/%s: %s\n",
1085 cwd_fname, fname, scan_report ? scan_report :
1086 "infected (memory error on report)");
1087 file_action = virusfilter_treat_infected_file(handle,
1088 config, fsp, scan_report, is_cache);
1089 if (file_action != VIRUSFILTER_ACTION_DO_NOTHING) {
1090 add_scan_cache = false;
1094 case VIRUSFILTER_RESULT_SUSPECTED:
1095 if (!config->block_suspected_file) {
1098 DBG_ERR("Scan result: Suspected: %s/%s: %s\n",
1099 cwd_fname, fname, scan_report ? scan_report :
1100 "suspected infection (memory error on report)");
1101 file_action = virusfilter_treat_infected_file(handle,
1102 config, fsp, scan_report, is_cache);
1103 if (file_action != VIRUSFILTER_ACTION_DO_NOTHING) {
1104 add_scan_cache = false;
1108 case VIRUSFILTER_RESULT_ERROR:
1109 DBG_ERR("Scan result: Error: %s/%s: %s\n",
1110 cwd_fname, fname, scan_report ? scan_report :
1111 "error (memory error on report)");
1112 virusfilter_treat_scan_error(handle, config, fsp,
1113 scan_report, is_cache);
1114 add_scan_cache = false;
1118 DBG_ERR("Scan result: Unknown result code %d: %s/%s: %s\n",
1119 scan_result, cwd_fname, fname, scan_report ?
1120 scan_report : "Unknown (memory error on report)");
1121 virusfilter_treat_scan_error(handle, config, fsp,
1122 scan_report, is_cache);
1123 add_scan_cache = false;
1127 if (config->cache) {
1128 if (!is_cache && add_scan_cache) {
1129 DBG_DEBUG("Adding new cache entry: %s, %d\n", fname,
1131 ok = virusfilter_cache_entry_add(
1132 config->cache, cwd_fname, fname,
1133 scan_result, scan_report);
1135 DBG_ERR("Cannot create cache entry: "
1136 "virusfilter_cache_entry_new failed");
1137 goto virusfilter_scan_return;
1139 } else if (is_cache) {
1140 virusfilter_cache_entry_free(scan_cache_e);
1144 virusfilter_scan_return:
1148 static int virusfilter_vfs_open(
1149 struct vfs_handle_struct *handle,
1150 struct smb_filename *smb_fname,
1155 TALLOC_CTX *mem_ctx = talloc_tos();
1156 struct virusfilter_config *config;
1157 const char *cwd_fname = fsp->conn->cwd_fsp->fsp_name->base_name;
1158 virusfilter_result scan_result;
1159 const char *fname = fsp->fsp_name->base_name;
1160 char *dir_name = NULL;
1161 const char *base_name = NULL;
1165 int rename_trap_count = 0;
1170 SMB_VFS_HANDLE_GET_DATA(handle, config,
1171 struct virusfilter_config, return -1);
1173 if (fsp->fsp_flags.is_directory) {
1174 DBG_INFO("Not scanned: Directory: %s/\n", cwd_fname);
1175 goto virusfilter_vfs_open_next;
1178 test_prefix = strlen(config->rename_prefix);
1179 test_suffix = strlen(config->rename_suffix);
1180 if (test_prefix > 0) {
1181 rename_trap_count++;
1183 if (test_suffix > 0) {
1184 rename_trap_count++;
1187 if (is_named_stream(smb_fname)) {
1188 DBG_INFO("Not scanned: only file backed streams can be scanned:"
1189 " %s/%s\n", cwd_fname, fname);
1190 goto virusfilter_vfs_open_next;
1193 if (!config->scan_on_open) {
1194 DBG_INFO("Not scanned: scan on open is disabled: %s/%s\n",
1196 goto virusfilter_vfs_open_next;
1199 if (flags & O_TRUNC) {
1200 DBG_INFO("Not scanned: Open flags have O_TRUNC: %s/%s\n",
1202 goto virusfilter_vfs_open_next;
1205 ret = SMB_VFS_NEXT_STAT(handle, smb_fname);
1209 * Do not return immediately if !(flags & O_CREAT) &&
1211 * Do not do this here or anywhere else. The module is
1212 * stackable and there may be modules below, such as audit
1213 * modules, which should be handled.
1215 goto virusfilter_vfs_open_next;
1217 ret = S_ISREG(smb_fname->st.st_ex_mode);
1219 DBG_INFO("Not scanned: Directory or special file: %s/%s\n",
1221 goto virusfilter_vfs_open_next;
1223 if (config->max_file_size > 0 &&
1224 smb_fname->st.st_ex_size > config->max_file_size)
1226 DBG_INFO("Not scanned: file size > max file size: %s/%s\n",
1228 goto virusfilter_vfs_open_next;
1230 if (config->min_file_size > 0 &&
1231 smb_fname->st.st_ex_size < config->min_file_size)
1233 DBG_INFO("Not scanned: file size < min file size: %s/%s\n",
1235 goto virusfilter_vfs_open_next;
1238 ok1 = is_in_path(fname, config->exclude_files, false);
1239 if (config->exclude_files && ok1)
1241 DBG_INFO("Not scanned: exclude files: %s/%s\n",
1243 goto virusfilter_vfs_open_next;
1246 if (config->infected_file_action == VIRUSFILTER_ACTION_QUARANTINE) {
1247 sret = strstr_m(fname, config->quarantine_dir);
1249 scan_errno = config->infected_open_errno;
1250 goto virusfilter_vfs_open_fail;
1254 if (test_prefix > 0 || test_suffix > 0) {
1255 ok1 = parent_dirname(mem_ctx, fname, &dir_name, &base_name);
1258 if (test_prefix > 0) {
1259 ret = strncmp(base_name,
1260 config->rename_prefix, test_prefix);
1265 if (test_suffix > 0) {
1266 ret = strcmp(base_name + (strlen(base_name)
1268 config->rename_suffix);
1274 TALLOC_FREE(dir_name);
1276 if ((rename_trap_count == 2 && test_prefix &&
1277 test_suffix) || (rename_trap_count == 1 &&
1278 (test_prefix || test_suffix)))
1281 config->infected_open_errno;
1282 goto virusfilter_vfs_open_fail;
1287 scan_result = virusfilter_scan(handle, config, fsp);
1289 switch (scan_result) {
1290 case VIRUSFILTER_RESULT_CLEAN:
1292 case VIRUSFILTER_RESULT_INFECTED:
1293 scan_errno = config->infected_open_errno;
1294 goto virusfilter_vfs_open_fail;
1295 case VIRUSFILTER_RESULT_ERROR:
1296 if (config->block_access_on_error) {
1297 DBG_INFO("Block access\n");
1298 scan_errno = config->scan_error_open_errno;
1299 goto virusfilter_vfs_open_fail;
1303 scan_errno = config->scan_error_open_errno;
1304 goto virusfilter_vfs_open_fail;
1307 virusfilter_vfs_open_next:
1308 return SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
1310 virusfilter_vfs_open_fail:
1311 errno = (scan_errno != 0) ? scan_errno : EACCES;
1315 static int virusfilter_vfs_openat(struct vfs_handle_struct *handle,
1316 const struct files_struct *dirfsp,
1317 const struct smb_filename *smb_fname_in,
1318 struct files_struct *fsp,
1322 TALLOC_CTX *mem_ctx = talloc_tos();
1323 struct virusfilter_config *config = NULL;
1324 const char *cwd_fname = dirfsp->fsp_name->base_name;
1325 virusfilter_result scan_result;
1326 const char *fname = fsp->fsp_name->base_name;
1327 char *dir_name = NULL;
1328 const char *base_name = NULL;
1332 int rename_trap_count = 0;
1336 struct smb_filename *smb_fname = NULL;
1339 * For now assert this, so SMB_VFS_NEXT_STAT() below works.
1341 SMB_ASSERT(dirfsp->fh->fd == AT_FDCWD);
1343 SMB_VFS_HANDLE_GET_DATA(handle, config,
1344 struct virusfilter_config, return -1);
1346 if (fsp->fsp_flags.is_directory) {
1347 DBG_INFO("Not scanned: Directory: %s/\n", cwd_fname);
1348 goto virusfilter_vfs_open_next;
1351 test_prefix = strlen(config->rename_prefix);
1352 test_suffix = strlen(config->rename_suffix);
1353 if (test_prefix > 0) {
1354 rename_trap_count++;
1356 if (test_suffix > 0) {
1357 rename_trap_count++;
1360 smb_fname = cp_smb_filename(mem_ctx, smb_fname_in);
1361 if (smb_fname == NULL) {
1362 goto virusfilter_vfs_open_fail;
1365 if (is_named_stream(smb_fname)) {
1366 DBG_INFO("Not scanned: only file backed streams can be scanned:"
1367 " %s/%s\n", cwd_fname, fname);
1368 goto virusfilter_vfs_open_next;
1371 if (!config->scan_on_open) {
1372 DBG_INFO("Not scanned: scan on open is disabled: %s/%s\n",
1374 goto virusfilter_vfs_open_next;
1377 if (flags & O_TRUNC) {
1378 DBG_INFO("Not scanned: Open flags have O_TRUNC: %s/%s\n",
1380 goto virusfilter_vfs_open_next;
1383 ret = SMB_VFS_NEXT_STAT(handle, smb_fname);
1387 * Do not return immediately if !(flags & O_CREAT) &&
1389 * Do not do this here or anywhere else. The module is
1390 * stackable and there may be modules below, such as audit
1391 * modules, which should be handled.
1393 goto virusfilter_vfs_open_next;
1395 ret = S_ISREG(smb_fname->st.st_ex_mode);
1397 DBG_INFO("Not scanned: Directory or special file: %s/%s\n",
1399 goto virusfilter_vfs_open_next;
1401 if (config->max_file_size > 0 &&
1402 smb_fname->st.st_ex_size > config->max_file_size)
1404 DBG_INFO("Not scanned: file size > max file size: %s/%s\n",
1406 goto virusfilter_vfs_open_next;
1408 if (config->min_file_size > 0 &&
1409 smb_fname->st.st_ex_size < config->min_file_size)
1411 DBG_INFO("Not scanned: file size < min file size: %s/%s\n",
1413 goto virusfilter_vfs_open_next;
1416 ok1 = is_in_path(fname, config->exclude_files, false);
1417 if (config->exclude_files && ok1)
1419 DBG_INFO("Not scanned: exclude files: %s/%s\n",
1421 goto virusfilter_vfs_open_next;
1424 if (config->infected_file_action == VIRUSFILTER_ACTION_QUARANTINE) {
1425 sret = strstr_m(fname, config->quarantine_dir);
1427 scan_errno = config->infected_open_errno;
1428 goto virusfilter_vfs_open_fail;
1432 if (test_prefix > 0 || test_suffix > 0) {
1433 ok1 = parent_dirname(mem_ctx, fname, &dir_name, &base_name);
1436 if (test_prefix > 0) {
1437 ret = strncmp(base_name,
1438 config->rename_prefix, test_prefix);
1443 if (test_suffix > 0) {
1444 ret = strcmp(base_name + (strlen(base_name)
1446 config->rename_suffix);
1452 TALLOC_FREE(dir_name);
1454 if ((rename_trap_count == 2 && test_prefix &&
1455 test_suffix) || (rename_trap_count == 1 &&
1456 (test_prefix || test_suffix)))
1459 config->infected_open_errno;
1460 goto virusfilter_vfs_open_fail;
1465 scan_result = virusfilter_scan(handle, config, fsp);
1467 switch (scan_result) {
1468 case VIRUSFILTER_RESULT_CLEAN:
1470 case VIRUSFILTER_RESULT_INFECTED:
1471 scan_errno = config->infected_open_errno;
1472 goto virusfilter_vfs_open_fail;
1473 case VIRUSFILTER_RESULT_ERROR:
1474 if (config->block_access_on_error) {
1475 DBG_INFO("Block access\n");
1476 scan_errno = config->scan_error_open_errno;
1477 goto virusfilter_vfs_open_fail;
1481 scan_errno = config->scan_error_open_errno;
1482 goto virusfilter_vfs_open_fail;
1485 TALLOC_FREE(smb_fname);
1487 virusfilter_vfs_open_next:
1488 return SMB_VFS_NEXT_OPENAT(handle, dirfsp, smb_fname_in, fsp, flags, mode);
1490 virusfilter_vfs_open_fail:
1491 TALLOC_FREE(smb_fname);
1492 errno = (scan_errno != 0) ? scan_errno : EACCES;
1496 static int virusfilter_vfs_close(
1497 struct vfs_handle_struct *handle,
1501 * The name of this variable is for consistency. If API changes to
1502 * match _open change to cwd_fname as in virusfilter_vfs_open.
1504 const char *cwd_fname = handle->conn->connectpath;
1506 struct virusfilter_config *config = NULL;
1507 char *fname = fsp->fsp_name->base_name;
1508 int close_result = -1;
1509 int close_errno = 0;
1510 virusfilter_result scan_result;
1513 SMB_VFS_HANDLE_GET_DATA(handle, config,
1514 struct virusfilter_config, return -1);
1517 * Must close after scan? It appears not as the scanners are not
1518 * internal and other modules such as greyhole seem to do
1519 * SMB_VFS_NEXT_* functions before processing.
1521 close_result = SMB_VFS_NEXT_CLOSE(handle, fsp);
1522 if (close_result == -1) {
1523 close_errno = errno;
1527 * Return immediately if close_result == -1, and close_errno == EBADF.
1528 * If close failed, file likely doesn't exist, do not try to scan.
1530 if (close_result == -1 && close_errno == EBADF) {
1531 if (fsp->fsp_flags.modified) {
1532 DBG_DEBUG("Removing cache entry (if existent): "
1533 "fname: %s\n", fname);
1534 virusfilter_cache_remove(config->cache,
1537 goto virusfilter_vfs_close_fail;
1540 if (fsp->fsp_flags.is_directory) {
1541 DBG_INFO("Not scanned: Directory: %s/\n", cwd_fname);
1542 return close_result;
1545 if (is_named_stream(fsp->fsp_name)) {
1546 if (config->scan_on_open && fsp->fsp_flags.modified) {
1547 if (config->cache) {
1548 DBG_DEBUG("Removing cache entry (if existent)"
1549 ": fname: %s\n", fname);
1550 virusfilter_cache_remove(
1555 DBG_INFO("Not scanned: only file backed streams can be scanned:"
1556 " %s/%s\n", cwd_fname, fname);
1557 return close_result;
1560 if (!config->scan_on_close) {
1561 if (config->scan_on_open && fsp->fsp_flags.modified) {
1562 if (config->cache) {
1563 DBG_DEBUG("Removing cache entry (if existent)"
1564 ": fname: %s\n", fname);
1565 virusfilter_cache_remove(
1570 DBG_INFO("Not scanned: scan on close is disabled: %s/%s\n",
1572 return close_result;
1575 if (!fsp->fsp_flags.modified) {
1576 DBG_NOTICE("Not scanned: File not modified: %s/%s\n",
1579 return close_result;
1582 if (config->exclude_files && is_in_path(fname,
1583 config->exclude_files, false))
1585 DBG_INFO("Not scanned: exclude files: %s/%s\n",
1587 return close_result;
1590 scan_result = virusfilter_scan(handle, config, fsp);
1592 switch (scan_result) {
1593 case VIRUSFILTER_RESULT_CLEAN:
1595 case VIRUSFILTER_RESULT_INFECTED:
1596 scan_errno = config->infected_close_errno;
1597 goto virusfilter_vfs_close_fail;
1598 case VIRUSFILTER_RESULT_ERROR:
1599 if (config->block_access_on_error) {
1600 DBG_INFO("Block access\n");
1601 scan_errno = config->scan_error_close_errno;
1602 goto virusfilter_vfs_close_fail;
1606 scan_errno = config->scan_error_close_errno;
1607 goto virusfilter_vfs_close_fail;
1610 if (close_errno != 0) {
1611 errno = close_errno;
1614 return close_result;
1616 virusfilter_vfs_close_fail:
1618 errno = (scan_errno != 0) ? scan_errno : close_errno;
1620 return close_result;
1623 static int virusfilter_vfs_unlinkat(struct vfs_handle_struct *handle,
1624 struct files_struct *dirfsp,
1625 const struct smb_filename *smb_fname,
1628 int ret = SMB_VFS_NEXT_UNLINKAT(handle,
1632 struct virusfilter_config *config = NULL;
1634 char *cwd_fname = handle->conn->cwd_fsp->fsp_name->base_name;
1636 if (ret != 0 && errno != ENOENT) {
1640 SMB_VFS_HANDLE_GET_DATA(handle, config,
1641 struct virusfilter_config, return -1);
1643 if (config->cache == NULL) {
1647 fname = smb_fname->base_name;
1649 DBG_DEBUG("Removing cache entry (if existent): fname: %s\n", fname);
1650 virusfilter_cache_remove(config->cache, cwd_fname, fname);
1655 static int virusfilter_vfs_renameat(
1656 struct vfs_handle_struct *handle,
1657 files_struct *srcfsp,
1658 const struct smb_filename *smb_fname_src,
1659 files_struct *dstfsp,
1660 const struct smb_filename *smb_fname_dst)
1662 int ret = SMB_VFS_NEXT_RENAMEAT(handle,
1667 struct virusfilter_config *config = NULL;
1669 char *dst_fname = NULL;
1670 char *cwd_fname = handle->conn->cwd_fsp->fsp_name->base_name;
1676 SMB_VFS_HANDLE_GET_DATA(handle, config,
1677 struct virusfilter_config, return -1);
1679 if (config->cache == NULL) {
1683 fname = smb_fname_src->base_name;
1684 dst_fname = smb_fname_dst->base_name;
1686 DBG_DEBUG("Renaming cache entry: fname: %s to: %s\n",
1688 virusfilter_cache_entry_rename(config->cache,
1696 /* VFS operations */
1697 static struct vfs_fn_pointers vfs_virusfilter_fns = {
1698 .connect_fn = virusfilter_vfs_connect,
1699 .disconnect_fn = virusfilter_vfs_disconnect,
1700 .open_fn = virusfilter_vfs_open,
1701 .openat_fn = virusfilter_vfs_openat,
1702 .close_fn = virusfilter_vfs_close,
1703 .unlinkat_fn = virusfilter_vfs_unlinkat,
1704 .renameat_fn = virusfilter_vfs_renameat,
1707 NTSTATUS vfs_virusfilter_init(TALLOC_CTX *);
1708 NTSTATUS vfs_virusfilter_init(TALLOC_CTX *ctx)
1712 status = smb_register_vfs(SMB_VFS_INTERFACE_VERSION,
1714 &vfs_virusfilter_fns);
1715 if (!NT_STATUS_IS_OK(status)) {
1719 virusfilter_debug_class = debug_add_class("virusfilter");
1720 if (virusfilter_debug_class == -1) {
1721 virusfilter_debug_class = DBGC_VFS;
1722 DBG_ERR("Couldn't register custom debugging class!\n");
1724 DBG_DEBUG("Debug class number: %d\n", virusfilter_debug_class);
1727 DBG_INFO("registered\n");