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(), new_dir
,
158 if (smb_fname
== NULL
) {
162 ret
= SMB_VFS_NEXT_MKDIRAT(handle
,
163 handle
->conn
->cwd_fsp
,
165 config
->quarantine_dir_mode
);
167 TALLOC_FREE(smb_fname
);
169 DBG_WARNING("quarantine: mkdirat failed for %s "
170 "with error: %s\n", new_dir
,
175 TALLOC_FREE(smb_fname
);
177 cat_len
= strlcat(new_dir
, "/", len
+ 1);
178 if (cat_len
>= len
+ 1) {
185 TALLOC_FREE(tmp_str
);
186 TALLOC_FREE(new_dir
);
190 static int virusfilter_vfs_connect(
191 struct vfs_handle_struct
*handle
,
195 int snum
= SNUM(handle
->conn
);
196 struct virusfilter_config
*config
= NULL
;
197 const char *exclude_files
= NULL
;
198 const char *temp_quarantine_dir_mode
= NULL
;
201 enum virusfilter_scanner_enum backend
;
202 int connect_timeout
= 0;
206 config
= talloc_zero(handle
, struct virusfilter_config
);
207 if (config
== NULL
) {
208 DBG_ERR("talloc_zero failed\n");
211 talloc_set_destructor(config
, virusfilter_config_destructor
);
213 SMB_VFS_HANDLE_SET_DATA(handle
, config
, NULL
,
214 struct virusfilter_config
, return -1);
216 config
->scan_request_limit
= lp_parm_int(
217 snum
, "virusfilter", "scan request limit", 0);
219 config
->scan_on_open
= lp_parm_bool(
220 snum
, "virusfilter", "scan on open", true);
222 config
->scan_on_close
= lp_parm_bool(
223 snum
, "virusfilter", "scan on close", false);
225 config
->max_nested_scan_archive
= lp_parm_int(
226 snum
, "virusfilter", "max nested scan archive", 1);
228 config
->scan_archive
= lp_parm_bool(
229 snum
, "virusfilter", "scan archive", false);
231 config
->scan_mime
= lp_parm_bool(
232 snum
, "virusfilter", "scan mime", false);
234 config
->max_file_size
= (ssize_t
)lp_parm_ulong(
235 snum
, "virusfilter", "max file size", 100000000L);
237 config
->min_file_size
= (ssize_t
)lp_parm_ulong(
238 snum
, "virusfilter", "min file size", 10);
240 exclude_files
= lp_parm_const_string(
241 snum
, "virusfilter", "exclude files", NULL
);
242 if (exclude_files
!= NULL
) {
243 set_namearray(&config
->exclude_files
, exclude_files
);
246 config
->cache_entry_limit
= lp_parm_int(
247 snum
, "virusfilter", "cache entry limit", 100);
249 config
->cache_time_limit
= lp_parm_int(
250 snum
, "virusfilter", "cache time limit", 10);
252 config
->infected_file_action
= lp_parm_enum(
253 snum
, "virusfilter", "infected file action",
254 virusfilter_actions
, VIRUSFILTER_ACTION_DO_NOTHING
);
256 config
->infected_file_command
= lp_parm_const_string(
257 snum
, "virusfilter", "infected file command", NULL
);
259 config
->scan_error_command
= lp_parm_const_string(
260 snum
, "virusfilter", "scan error command", NULL
);
262 config
->block_access_on_error
= lp_parm_bool(
263 snum
, "virusfilter", "block access on error", false);
265 tmp
= talloc_asprintf(config
, "%s/.quarantine",
266 handle
->conn
->connectpath
);
268 config
->quarantine_dir
= lp_parm_const_string(
269 snum
, "virusfilter", "quarantine directory",
270 tmp
? tmp
: "/tmp/.quarantine");
272 if (tmp
!= config
->quarantine_dir
) {
276 temp_quarantine_dir_mode
= lp_parm_const_string(
277 snum
, "virusfilter", "quarantine directory mode", "0755");
278 if (temp_quarantine_dir_mode
!= NULL
) {
279 unsigned int mode
= 0;
280 sscanf(temp_quarantine_dir_mode
, "%o", &mode
);
281 config
->quarantine_dir_mode
= mode
;
284 config
->quarantine_prefix
= lp_parm_const_string(
285 snum
, "virusfilter", "quarantine prefix",
286 VIRUSFILTER_DEFAULT_QUARANTINE_PREFIX
);
288 config
->quarantine_suffix
= lp_parm_const_string(
289 snum
, "virusfilter", "quarantine suffix",
290 VIRUSFILTER_DEFAULT_QUARANTINE_SUFFIX
);
293 * Make sure prefixes and suffixes do not contain directory
296 sret
= strstr(config
->quarantine_prefix
, "/");
298 DBG_ERR("quarantine prefix must not contain directory "
299 "delimiter(s) such as '/' (%s replaced with %s)\n",
300 config
->quarantine_prefix
,
301 VIRUSFILTER_DEFAULT_QUARANTINE_PREFIX
);
302 config
->quarantine_prefix
=
303 VIRUSFILTER_DEFAULT_QUARANTINE_PREFIX
;
305 sret
= strstr(config
->quarantine_suffix
, "/");
307 DBG_ERR("quarantine suffix must not contain directory "
308 "delimiter(s) such as '/' (%s replaced with %s)\n",
309 config
->quarantine_suffix
,
310 VIRUSFILTER_DEFAULT_QUARANTINE_SUFFIX
);
311 config
->quarantine_suffix
=
312 VIRUSFILTER_DEFAULT_QUARANTINE_SUFFIX
;
315 config
->quarantine_keep_tree
= lp_parm_bool(
316 snum
, "virusfilter", "quarantine keep tree", true);
318 config
->quarantine_keep_name
= lp_parm_bool(
319 snum
, "virusfilter", "quarantine keep name", true);
321 config
->rename_prefix
= lp_parm_const_string(
322 snum
, "virusfilter", "rename prefix",
323 VIRUSFILTER_DEFAULT_RENAME_PREFIX
);
325 config
->rename_suffix
= lp_parm_const_string(
326 snum
, "virusfilter", "rename suffix",
327 VIRUSFILTER_DEFAULT_RENAME_SUFFIX
);
330 * Make sure prefixes and suffixes do not contain directory
333 sret
= strstr(config
->rename_prefix
, "/");
335 DBG_ERR("rename prefix must not contain directory "
336 "delimiter(s) such as '/' (%s replaced with %s)\n",
337 config
->rename_prefix
,
338 VIRUSFILTER_DEFAULT_RENAME_PREFIX
);
339 config
->rename_prefix
=
340 VIRUSFILTER_DEFAULT_RENAME_PREFIX
;
342 sret
= strstr(config
->rename_suffix
, "/");
344 DBG_ERR("rename suffix must not contain directory "
345 "delimiter(s) such as '/' (%s replaced with %s)\n",
346 config
->rename_suffix
,
347 VIRUSFILTER_DEFAULT_RENAME_SUFFIX
);
348 config
->rename_suffix
=
349 VIRUSFILTER_DEFAULT_RENAME_SUFFIX
;
352 config
->infected_open_errno
= lp_parm_int(
353 snum
, "virusfilter", "infected file errno on open", EACCES
);
355 config
->infected_close_errno
= lp_parm_int(
356 snum
, "virusfilter", "infected file errno on close", 0);
358 config
->scan_error_open_errno
= lp_parm_int(
359 snum
, "virusfilter", "scan error errno on open", EACCES
);
361 config
->scan_error_close_errno
= lp_parm_int(
362 snum
, "virusfilter", "scan error errno on close", 0);
364 config
->socket_path
= lp_parm_const_string(
365 snum
, "virusfilter", "socket path", NULL
);
367 /* canonicalize socket_path */
368 if (config
->socket_path
!= NULL
&& config
->socket_path
[0] != '/') {
369 DBG_ERR("socket path must be an absolute path. "
370 "Using backend default\n");
371 config
->socket_path
= NULL
;
373 if (config
->socket_path
!= NULL
) {
374 canonicalize_absolute_path(handle
,
375 config
->socket_path
);
378 connect_timeout
= lp_parm_int(snum
, "virusfilter",
379 "connect timeout", 30000);
381 io_timeout
= lp_parm_int(snum
, "virusfilter", "io timeout", 60000);
383 config
->io_h
= virusfilter_io_new(config
, connect_timeout
, io_timeout
);
384 if (config
->io_h
== NULL
) {
385 DBG_ERR("virusfilter_io_new failed");
389 if (config
->cache_entry_limit
> 0) {
390 config
->cache
= virusfilter_cache_new(handle
,
391 config
->cache_entry_limit
,
392 config
->cache_time_limit
);
393 if (config
->cache
== NULL
) {
394 DBG_ERR("Initializing cache failed: Cache disabled\n");
400 * Check quarantine directory now to save processing
401 * and becoming root over and over.
403 if (config
->infected_file_action
== VIRUSFILTER_ACTION_QUARANTINE
) {
408 * Do SMB_VFS_NEXT_MKDIR(config->quarantine_dir)
412 dir_exists
= quarantine_directory_exist(handle
,
413 config
->quarantine_dir
);
415 DBG_DEBUG("Creating quarantine directory: %s\n",
416 config
->quarantine_dir
);
417 ok
= quarantine_create_dir(handle
, config
,
418 config
->quarantine_dir
);
422 DBG_ERR("Creating quarantine directory %s "
424 config
->quarantine_dir
,
431 * Now that the frontend options are initialized, load the configured
435 backend
= (enum virusfilter_scanner_enum
)lp_parm_enum(snum
,
440 if (backend
== (enum virusfilter_scanner_enum
)-1) {
441 DBG_ERR("No AV-Scanner configured, "
442 "please set \"virusfilter:scanner\"\n");
447 case VIRUSFILTER_SCANNER_SOPHOS
:
448 ret
= virusfilter_sophos_init(config
);
450 case VIRUSFILTER_SCANNER_FSAV
:
451 ret
= virusfilter_fsav_init(config
);
453 case VIRUSFILTER_SCANNER_CLAMAV
:
454 ret
= virusfilter_clamav_init(config
);
457 DBG_ERR("Unhandled scanner %d\n", backend
);
461 DBG_ERR("Scanner backend init failed\n");
465 if (config
->backend
->fns
->connect
!= NULL
) {
466 ret
= config
->backend
->fns
->connect(handle
, config
, svc
, user
);
472 return SMB_VFS_NEXT_CONNECT(handle
, svc
, user
);
475 static void virusfilter_vfs_disconnect(struct vfs_handle_struct
*handle
)
477 struct virusfilter_config
*config
= NULL
;
479 SMB_VFS_HANDLE_GET_DATA(handle
, config
,
480 struct virusfilter_config
, return);
482 if (config
->backend
->fns
->disconnect
!= NULL
) {
483 config
->backend
->fns
->disconnect(handle
);
486 free_namearray(config
->exclude_files
);
487 virusfilter_io_disconnect(config
->io_h
);
489 SMB_VFS_NEXT_DISCONNECT(handle
);
492 static int virusfilter_set_module_env(TALLOC_CTX
*mem_ctx
,
493 struct virusfilter_config
*config
,
498 ret
= virusfilter_env_set(mem_ctx
, env_list
, "VIRUSFILTER_VERSION",
499 VIRUSFILTER_VERSION
);
503 ret
= virusfilter_env_set(mem_ctx
, env_list
, "VIRUSFILTER_MODULE_NAME",
504 config
->backend
->name
);
509 if (config
->backend
->version
!= 0) {
510 char *version
= NULL
;
512 version
= talloc_asprintf(talloc_tos(), "%u",
513 config
->backend
->version
);
514 if (version
== NULL
) {
517 ret
= virusfilter_env_set(mem_ctx
, env_list
,
518 "VIRUSFILTER_MODULE_VERSION",
520 TALLOC_FREE(version
);
529 static char *quarantine_check_tree(TALLOC_CTX
*mem_ctx
,
530 struct vfs_handle_struct
*handle
,
531 struct virusfilter_config
*config
,
532 const struct smb_filename
*smb_fname
,
536 char *temp_path
= NULL
;
537 char *q_dir_out
= NULL
;
540 temp_path
= talloc_asprintf(talloc_tos(), "%s/%s", q_dir_in
, cwd_fname
);
541 if (temp_path
== NULL
) {
542 DBG_ERR("talloc_asprintf failed\n");
547 ok
= quarantine_directory_exist(handle
, temp_path
);
550 DBG_DEBUG("quarantine: directory [%s] exists\n", temp_path
);
551 q_dir_out
= talloc_move(mem_ctx
, &temp_path
);
555 DBG_DEBUG("quarantine: Creating directory %s\n", temp_path
);
558 ok
= quarantine_create_dir(handle
, config
, temp_path
);
561 DBG_NOTICE("Could not create quarantine directory [%s], "
562 "ignoring for [%s]\n",
563 temp_path
, smb_fname_str_dbg(smb_fname
));
567 q_dir_out
= talloc_move(mem_ctx
, &temp_path
);
570 TALLOC_FREE(temp_path
);
574 static virusfilter_action
infected_file_action_quarantine(
575 struct vfs_handle_struct
*handle
,
576 struct virusfilter_config
*config
,
578 const struct files_struct
*fsp
,
579 const char **filepath_newp
)
581 TALLOC_CTX
*frame
= talloc_stackframe();
582 connection_struct
*conn
= handle
->conn
;
583 char *cwd_fname
= fsp
->conn
->cwd_fsp
->fsp_name
->base_name
;
584 char *fname
= fsp
->fsp_name
->base_name
;
585 const struct smb_filename
*smb_fname
= fsp
->fsp_name
;
586 struct smb_filename
*q_smb_fname
= NULL
;
588 char *q_prefix
= NULL
;
589 char *q_suffix
= NULL
;
590 char *q_filepath
= NULL
;
591 char *dir_name
= NULL
;
592 const char *base_name
= NULL
;
593 char *rand_filename_component
= NULL
;
594 virusfilter_action action
= VIRUSFILTER_ACTION_QUARANTINE
;
599 q_dir
= virusfilter_string_sub(frame
, conn
,
600 config
->quarantine_dir
);
601 q_prefix
= virusfilter_string_sub(frame
, conn
,
602 config
->quarantine_prefix
);
603 q_suffix
= virusfilter_string_sub(frame
, conn
,
604 config
->quarantine_suffix
);
605 if (q_dir
== NULL
|| q_prefix
== NULL
|| q_suffix
== NULL
) {
606 DBG_ERR("Quarantine failed: %s/%s: Cannot allocate "
607 "memory\n", cwd_fname
, fname
);
608 action
= VIRUSFILTER_ACTION_DO_NOTHING
;
612 if (config
->quarantine_keep_name
|| config
->quarantine_keep_tree
) {
613 ok
= parent_dirname(frame
, smb_fname
->base_name
,
614 &dir_name
, &base_name
);
616 DBG_ERR("parent_dirname failed\n");
617 action
= VIRUSFILTER_ACTION_DO_NOTHING
;
621 if (config
->quarantine_keep_tree
) {
624 tree
= quarantine_check_tree(frame
, handle
, config
,
629 * If we can't create the tree, just move it
630 * into the toplevel quarantine dir.
638 /* Get a 16 byte + \0 random filename component. */
639 rand_filename_component
= generate_random_str(frame
, 16);
640 if (rand_filename_component
== NULL
) {
641 DBG_ERR("generate_random_str failed\n");
642 action
= VIRUSFILTER_ACTION_DO_NOTHING
;
646 if (config
->quarantine_keep_name
) {
647 q_filepath
= talloc_asprintf(frame
, "%s/%s%s%s-%s",
650 rand_filename_component
);
652 q_filepath
= talloc_asprintf(frame
, "%s/%s%s",
654 rand_filename_component
);
656 if (q_filepath
== NULL
) {
657 DBG_ERR("talloc_asprintf failed\n");
658 action
= VIRUSFILTER_ACTION_DO_NOTHING
;
662 q_smb_fname
= synthetic_smb_fname(frame
, q_filepath
,
663 smb_fname
->stream_name
,
664 NULL
, smb_fname
->flags
);
665 if (q_smb_fname
== NULL
) {
666 action
= VIRUSFILTER_ACTION_DO_NOTHING
;
671 ret
= virusfilter_vfs_next_move(handle
, smb_fname
, q_smb_fname
);
677 DBG_ERR("Quarantine [%s/%s] rename to %s failed: %s\n",
678 cwd_fname
, fname
, q_filepath
, strerror(saved_errno
));
680 action
= VIRUSFILTER_ACTION_DO_NOTHING
;
684 *filepath_newp
= talloc_move(mem_ctx
, &q_filepath
);
691 static virusfilter_action
infected_file_action_rename(
692 struct vfs_handle_struct
*handle
,
693 struct virusfilter_config
*config
,
695 const struct files_struct
*fsp
,
696 const char **filepath_newp
)
698 TALLOC_CTX
*frame
= talloc_stackframe();
699 connection_struct
*conn
= handle
->conn
;
700 char *cwd_fname
= fsp
->conn
->cwd_fsp
->fsp_name
->base_name
;
701 char *fname
= fsp
->fsp_name
->base_name
;
702 const struct smb_filename
*smb_fname
= fsp
->fsp_name
;
703 struct smb_filename
*q_smb_fname
= NULL
;
705 char *q_prefix
= NULL
;
706 char *q_suffix
= NULL
;
707 char *q_filepath
= NULL
;
708 const char *base_name
= NULL
;
709 virusfilter_action action
= VIRUSFILTER_ACTION_RENAME
;
714 q_prefix
= virusfilter_string_sub(frame
, conn
,
715 config
->rename_prefix
);
716 q_suffix
= virusfilter_string_sub(frame
, conn
,
717 config
->rename_suffix
);
718 if (q_prefix
== NULL
|| q_suffix
== NULL
) {
719 DBG_ERR("Rename failed: %s/%s: Cannot allocate "
720 "memory\n", cwd_fname
, fname
);
721 action
= VIRUSFILTER_ACTION_DO_NOTHING
;
725 ok
= parent_dirname(frame
, fname
, &q_dir
, &base_name
);
727 DBG_ERR("Rename failed: %s/%s: Cannot allocate "
728 "memory\n", cwd_fname
, fname
);
729 action
= VIRUSFILTER_ACTION_DO_NOTHING
;
734 DBG_ERR("Rename failed: %s/%s: Cannot allocate "
735 "memory\n", cwd_fname
, fname
);
736 action
= VIRUSFILTER_ACTION_DO_NOTHING
;
740 q_filepath
= talloc_asprintf(frame
, "%s/%s%s%s", q_dir
,
741 q_prefix
, base_name
, q_suffix
);
743 q_smb_fname
= synthetic_smb_fname(frame
, q_filepath
,
744 smb_fname
->stream_name
, NULL
,
746 if (q_smb_fname
== NULL
) {
747 action
= VIRUSFILTER_ACTION_DO_NOTHING
;
752 ret
= virusfilter_vfs_next_move(handle
, smb_fname
, q_smb_fname
);
759 DBG_ERR("Rename failed: %s/%s: Rename failed: %s\n",
760 cwd_fname
, fname
, strerror(saved_errno
));
762 action
= VIRUSFILTER_ACTION_DO_NOTHING
;
766 *filepath_newp
= talloc_move(mem_ctx
, &q_filepath
);
773 static virusfilter_action
infected_file_action_delete(
774 struct vfs_handle_struct
*handle
,
775 const struct files_struct
*fsp
)
781 ret
= SMB_VFS_NEXT_UNLINKAT(handle
,
782 handle
->conn
->cwd_fsp
,
790 DBG_ERR("Delete [%s/%s] failed: %s\n",
791 fsp
->conn
->cwd_fsp
->fsp_name
->base_name
,
792 fsp
->fsp_name
->base_name
,
793 strerror(saved_errno
));
795 return VIRUSFILTER_ACTION_DO_NOTHING
;
798 return VIRUSFILTER_ACTION_DELETE
;
801 static virusfilter_action
virusfilter_do_infected_file_action(
802 struct vfs_handle_struct
*handle
,
803 struct virusfilter_config
*config
,
805 const struct files_struct
*fsp
,
806 const char **filepath_newp
)
808 virusfilter_action action
;
810 *filepath_newp
= NULL
;
812 switch (config
->infected_file_action
) {
813 case VIRUSFILTER_ACTION_RENAME
:
814 action
= infected_file_action_rename(handle
, config
, mem_ctx
,
818 case VIRUSFILTER_ACTION_QUARANTINE
:
819 action
= infected_file_action_quarantine(handle
, config
, mem_ctx
,
823 case VIRUSFILTER_ACTION_DELETE
:
824 action
= infected_file_action_delete(handle
, fsp
);
827 case VIRUSFILTER_ACTION_DO_NOTHING
:
829 action
= VIRUSFILTER_ACTION_DO_NOTHING
;
836 static virusfilter_action
virusfilter_treat_infected_file(
837 struct vfs_handle_struct
*handle
,
838 struct virusfilter_config
*config
,
839 const struct files_struct
*fsp
,
843 connection_struct
*conn
= handle
->conn
;
844 char *cwd_fname
= fsp
->conn
->cwd_fsp
->fsp_name
->base_name
;
845 char *fname
= fsp
->fsp_name
->base_name
;
846 TALLOC_CTX
*mem_ctx
= talloc_tos();
848 virusfilter_action action
;
849 const char *action_name
= "UNKNOWN";
850 const char *filepath_q
= NULL
;
851 char *env_list
= NULL
;
852 char *command
= NULL
;
856 action
= virusfilter_do_infected_file_action(handle
, config
, mem_ctx
,
858 for (i
=0; virusfilter_actions
[i
].name
; i
++) {
859 if (virusfilter_actions
[i
].value
== action
) {
860 action_name
= virusfilter_actions
[i
].name
;
864 DBG_WARNING("Infected file action: %s/%s: %s\n", cwd_fname
,
867 if (!config
->infected_file_command
) {
871 ret
= virusfilter_set_module_env(mem_ctx
, config
, &env_list
);
875 ret
= virusfilter_env_set(mem_ctx
, &env_list
,
876 "VIRUSFILTER_INFECTED_SERVICE_FILE_PATH",
881 if (report
!= NULL
) {
882 ret
= virusfilter_env_set(mem_ctx
, &env_list
,
883 "VIRUSFILTER_INFECTED_FILE_REPORT",
889 ret
= virusfilter_env_set(mem_ctx
, &env_list
,
890 "VIRUSFILTER_INFECTED_FILE_ACTION",
895 if (filepath_q
!= NULL
) {
896 ret
= virusfilter_env_set(mem_ctx
, &env_list
,
897 "VIRUSFILTER_QUARANTINED_FILE_PATH",
904 ret
= virusfilter_env_set(mem_ctx
, &env_list
,
905 "VIRUSFILTER_RESULT_IS_CACHE", "yes");
911 command
= virusfilter_string_sub(mem_ctx
, conn
,
912 config
->infected_file_command
);
913 if (command
== NULL
) {
914 DBG_ERR("virusfilter_string_sub failed\n");
918 DBG_NOTICE("Infected file command line: %s/%s: %s\n", cwd_fname
,
921 command_result
= virusfilter_shell_run(mem_ctx
, command
, &env_list
,
923 if (command_result
!= 0) {
924 DBG_ERR("Infected file command failed: %d\n", command_result
);
927 DBG_DEBUG("Infected file command finished: %d\n", command_result
);
930 TALLOC_FREE(env_list
);
931 TALLOC_FREE(command
);
936 static void virusfilter_treat_scan_error(
937 struct vfs_handle_struct
*handle
,
938 struct virusfilter_config
*config
,
939 const struct files_struct
*fsp
,
943 connection_struct
*conn
= handle
->conn
;
944 const char *cwd_fname
= fsp
->conn
->cwd_fsp
->fsp_name
->base_name
;
945 const char *fname
= fsp
->fsp_name
->base_name
;
946 TALLOC_CTX
*mem_ctx
= talloc_tos();
947 char *env_list
= NULL
;
948 char *command
= NULL
;
952 if (!config
->scan_error_command
) {
955 ret
= virusfilter_set_module_env(mem_ctx
, config
, &env_list
);
959 ret
= virusfilter_env_set(mem_ctx
, &env_list
,
960 "VIRUSFILTER_SCAN_ERROR_SERVICE_FILE_PATH",
965 if (report
!= NULL
) {
966 ret
= virusfilter_env_set(mem_ctx
, &env_list
,
967 "VIRUSFILTER_SCAN_ERROR_REPORT",
974 ret
= virusfilter_env_set(mem_ctx
, &env_list
,
975 "VIRUSFILTER_RESULT_IS_CACHE", "1");
981 command
= virusfilter_string_sub(mem_ctx
, conn
,
982 config
->scan_error_command
);
983 if (command
== NULL
) {
984 DBG_ERR("virusfilter_string_sub failed\n");
988 DBG_NOTICE("Scan error command line: %s/%s: %s\n", cwd_fname
,
991 command_result
= virusfilter_shell_run(mem_ctx
, command
, &env_list
,
993 if (command_result
!= 0) {
994 DBG_ERR("Scan error command failed: %d\n", command_result
);
998 TALLOC_FREE(env_list
);
999 TALLOC_FREE(command
);
1002 static virusfilter_result
virusfilter_scan(
1003 struct vfs_handle_struct
*handle
,
1004 struct virusfilter_config
*config
,
1005 const struct files_struct
*fsp
)
1007 virusfilter_result scan_result
;
1008 char *scan_report
= NULL
;
1009 const char *fname
= fsp
->fsp_name
->base_name
;
1010 const char *cwd_fname
= fsp
->conn
->cwd_fsp
->fsp_name
->base_name
;
1011 struct virusfilter_cache_entry
*scan_cache_e
= NULL
;
1012 bool is_cache
= false;
1013 virusfilter_action file_action
= VIRUSFILTER_ACTION_DO_NOTHING
;
1014 bool add_scan_cache
= true;
1017 if (config
->cache
) {
1018 DBG_DEBUG("Searching cache entry: fname: %s\n", fname
);
1019 scan_cache_e
= virusfilter_cache_get(config
->cache
,
1021 if (scan_cache_e
!= NULL
) {
1022 DBG_DEBUG("Cache entry found: cached result: %d\n",
1023 scan_cache_e
->result
);
1025 scan_result
= scan_cache_e
->result
;
1026 scan_report
= scan_cache_e
->report
;
1027 goto virusfilter_scan_result_eval
;
1029 DBG_DEBUG("Cache entry not found\n");
1032 if (config
->backend
->fns
->scan_init
!= NULL
) {
1033 scan_result
= config
->backend
->fns
->scan_init(config
);
1034 if (scan_result
!= VIRUSFILTER_RESULT_OK
) {
1035 scan_result
= VIRUSFILTER_RESULT_ERROR
;
1036 scan_report
= talloc_asprintf(
1038 "Initializing scanner failed");
1039 goto virusfilter_scan_result_eval
;
1043 scan_result
= config
->backend
->fns
->scan(handle
, config
, fsp
,
1046 if (config
->backend
->fns
->scan_end
!= NULL
) {
1047 bool scan_end
= true;
1049 if (config
->scan_request_limit
> 0) {
1051 config
->scan_request_count
++;
1052 if (config
->scan_request_count
>=
1053 config
->scan_request_limit
)
1056 config
->scan_request_count
= 0;
1060 config
->backend
->fns
->scan_end(config
);
1064 virusfilter_scan_result_eval
:
1066 switch (scan_result
) {
1067 case VIRUSFILTER_RESULT_CLEAN
:
1068 DBG_INFO("Scan result: Clean: %s/%s\n", cwd_fname
, fname
);
1071 case VIRUSFILTER_RESULT_INFECTED
:
1072 DBG_ERR("Scan result: Infected: %s/%s: %s\n",
1073 cwd_fname
, fname
, scan_report
? scan_report
:
1074 "infected (memory error on report)");
1075 file_action
= virusfilter_treat_infected_file(handle
,
1076 config
, fsp
, scan_report
, is_cache
);
1077 if (file_action
!= VIRUSFILTER_ACTION_DO_NOTHING
) {
1078 add_scan_cache
= false;
1082 case VIRUSFILTER_RESULT_SUSPECTED
:
1083 if (!config
->block_suspected_file
) {
1086 DBG_ERR("Scan result: Suspected: %s/%s: %s\n",
1087 cwd_fname
, fname
, scan_report
? scan_report
:
1088 "suspected infection (memory error on report)");
1089 file_action
= virusfilter_treat_infected_file(handle
,
1090 config
, fsp
, scan_report
, is_cache
);
1091 if (file_action
!= VIRUSFILTER_ACTION_DO_NOTHING
) {
1092 add_scan_cache
= false;
1096 case VIRUSFILTER_RESULT_ERROR
:
1097 DBG_ERR("Scan result: Error: %s/%s: %s\n",
1098 cwd_fname
, fname
, scan_report
? scan_report
:
1099 "error (memory error on report)");
1100 virusfilter_treat_scan_error(handle
, config
, fsp
,
1101 scan_report
, is_cache
);
1102 add_scan_cache
= false;
1106 DBG_ERR("Scan result: Unknown result code %d: %s/%s: %s\n",
1107 scan_result
, cwd_fname
, fname
, scan_report
?
1108 scan_report
: "Unknown (memory error on report)");
1109 virusfilter_treat_scan_error(handle
, config
, fsp
,
1110 scan_report
, is_cache
);
1111 add_scan_cache
= false;
1115 if (config
->cache
) {
1116 if (!is_cache
&& add_scan_cache
) {
1117 DBG_DEBUG("Adding new cache entry: %s, %d\n", fname
,
1119 ok
= virusfilter_cache_entry_add(
1120 config
->cache
, cwd_fname
, fname
,
1121 scan_result
, scan_report
);
1123 DBG_ERR("Cannot create cache entry: "
1124 "virusfilter_cache_entry_new failed");
1125 goto virusfilter_scan_return
;
1127 } else if (is_cache
) {
1128 virusfilter_cache_entry_free(scan_cache_e
);
1132 virusfilter_scan_return
:
1136 static int virusfilter_vfs_open(
1137 struct vfs_handle_struct
*handle
,
1138 struct smb_filename
*smb_fname
,
1143 TALLOC_CTX
*mem_ctx
= talloc_tos();
1144 struct virusfilter_config
*config
;
1145 const char *cwd_fname
= fsp
->conn
->cwd_fsp
->fsp_name
->base_name
;
1146 virusfilter_result scan_result
;
1147 const char *fname
= fsp
->fsp_name
->base_name
;
1148 char *dir_name
= NULL
;
1149 const char *base_name
= NULL
;
1153 int rename_trap_count
= 0;
1158 SMB_VFS_HANDLE_GET_DATA(handle
, config
,
1159 struct virusfilter_config
, return -1);
1161 if (fsp
->is_directory
) {
1162 DBG_INFO("Not scanned: Directory: %s/\n", cwd_fname
);
1163 goto virusfilter_vfs_open_next
;
1166 test_prefix
= strlen(config
->rename_prefix
);
1167 test_suffix
= strlen(config
->rename_suffix
);
1168 if (test_prefix
> 0) {
1169 rename_trap_count
++;
1171 if (test_suffix
> 0) {
1172 rename_trap_count
++;
1175 if (is_named_stream(smb_fname
)) {
1176 DBG_INFO("Not scanned: only file backed streams can be scanned:"
1177 " %s/%s\n", cwd_fname
, fname
);
1178 goto virusfilter_vfs_open_next
;
1181 if (!config
->scan_on_open
) {
1182 DBG_INFO("Not scanned: scan on open is disabled: %s/%s\n",
1184 goto virusfilter_vfs_open_next
;
1187 if (flags
& O_TRUNC
) {
1188 DBG_INFO("Not scanned: Open flags have O_TRUNC: %s/%s\n",
1190 goto virusfilter_vfs_open_next
;
1193 ret
= SMB_VFS_NEXT_STAT(handle
, smb_fname
);
1197 * Do not return immediately if !(flags & O_CREAT) &&
1199 * Do not do this here or anywhere else. The module is
1200 * stackable and there may be modules below, such as audit
1201 * modules, which should be handled.
1203 goto virusfilter_vfs_open_next
;
1205 ret
= S_ISREG(smb_fname
->st
.st_ex_mode
);
1207 DBG_INFO("Not scanned: Directory or special file: %s/%s\n",
1209 goto virusfilter_vfs_open_next
;
1211 if (config
->max_file_size
> 0 &&
1212 smb_fname
->st
.st_ex_size
> config
->max_file_size
)
1214 DBG_INFO("Not scanned: file size > max file size: %s/%s\n",
1216 goto virusfilter_vfs_open_next
;
1218 if (config
->min_file_size
> 0 &&
1219 smb_fname
->st
.st_ex_size
< config
->min_file_size
)
1221 DBG_INFO("Not scanned: file size < min file size: %s/%s\n",
1223 goto virusfilter_vfs_open_next
;
1226 ok1
= is_in_path(fname
, config
->exclude_files
, false);
1227 if (config
->exclude_files
&& ok1
)
1229 DBG_INFO("Not scanned: exclude files: %s/%s\n",
1231 goto virusfilter_vfs_open_next
;
1234 if (config
->infected_file_action
== VIRUSFILTER_ACTION_QUARANTINE
) {
1235 sret
= strstr_m(fname
, config
->quarantine_dir
);
1237 scan_errno
= config
->infected_open_errno
;
1238 goto virusfilter_vfs_open_fail
;
1242 if (test_prefix
> 0 || test_suffix
> 0) {
1243 ok1
= parent_dirname(mem_ctx
, fname
, &dir_name
, &base_name
);
1246 if (test_prefix
> 0) {
1247 ret
= strncmp(base_name
,
1248 config
->rename_prefix
, test_prefix
);
1253 if (test_suffix
> 0) {
1254 ret
= strcmp(base_name
+ (strlen(base_name
)
1256 config
->rename_suffix
);
1262 TALLOC_FREE(dir_name
);
1264 if ((rename_trap_count
== 2 && test_prefix
&&
1265 test_suffix
) || (rename_trap_count
== 1 &&
1266 (test_prefix
|| test_suffix
)))
1269 config
->infected_open_errno
;
1270 goto virusfilter_vfs_open_fail
;
1275 scan_result
= virusfilter_scan(handle
, config
, fsp
);
1277 switch (scan_result
) {
1278 case VIRUSFILTER_RESULT_CLEAN
:
1280 case VIRUSFILTER_RESULT_INFECTED
:
1281 scan_errno
= config
->infected_open_errno
;
1282 goto virusfilter_vfs_open_fail
;
1283 case VIRUSFILTER_RESULT_ERROR
:
1284 if (config
->block_access_on_error
) {
1285 DBG_INFO("Block access\n");
1286 scan_errno
= config
->scan_error_open_errno
;
1287 goto virusfilter_vfs_open_fail
;
1291 scan_errno
= config
->scan_error_open_errno
;
1292 goto virusfilter_vfs_open_fail
;
1295 virusfilter_vfs_open_next
:
1296 return SMB_VFS_NEXT_OPEN(handle
, smb_fname
, fsp
, flags
, mode
);
1298 virusfilter_vfs_open_fail
:
1299 errno
= (scan_errno
!= 0) ? scan_errno
: EACCES
;
1303 static int virusfilter_vfs_close(
1304 struct vfs_handle_struct
*handle
,
1308 * The name of this variable is for consistency. If API changes to
1309 * match _open change to cwd_fname as in virusfilter_vfs_open.
1311 const char *cwd_fname
= handle
->conn
->connectpath
;
1313 struct virusfilter_config
*config
= NULL
;
1314 char *fname
= fsp
->fsp_name
->base_name
;
1315 int close_result
= -1;
1316 int close_errno
= 0;
1317 virusfilter_result scan_result
;
1320 SMB_VFS_HANDLE_GET_DATA(handle
, config
,
1321 struct virusfilter_config
, return -1);
1324 * Must close after scan? It appears not as the scanners are not
1325 * internal and other modules such as greyhole seem to do
1326 * SMB_VFS_NEXT_* functions before processing.
1328 close_result
= SMB_VFS_NEXT_CLOSE(handle
, fsp
);
1329 if (close_result
== -1) {
1330 close_errno
= errno
;
1334 * Return immediately if close_result == -1, and close_errno == EBADF.
1335 * If close failed, file likely doesn't exist, do not try to scan.
1337 if (close_result
== -1 && close_errno
== EBADF
) {
1338 if (fsp
->modified
) {
1339 DBG_DEBUG("Removing cache entry (if existent): "
1340 "fname: %s\n", fname
);
1341 virusfilter_cache_remove(config
->cache
,
1344 goto virusfilter_vfs_close_fail
;
1347 if (fsp
->is_directory
) {
1348 DBG_INFO("Not scanned: Directory: %s/\n", cwd_fname
);
1349 return close_result
;
1352 if (is_named_stream(fsp
->fsp_name
)) {
1353 if (config
->scan_on_open
&& fsp
->modified
) {
1354 if (config
->cache
) {
1355 DBG_DEBUG("Removing cache entry (if existent)"
1356 ": fname: %s\n", fname
);
1357 virusfilter_cache_remove(
1362 DBG_INFO("Not scanned: only file backed streams can be scanned:"
1363 " %s/%s\n", cwd_fname
, fname
);
1364 return close_result
;
1367 if (!config
->scan_on_close
) {
1368 if (config
->scan_on_open
&& fsp
->modified
) {
1369 if (config
->cache
) {
1370 DBG_DEBUG("Removing cache entry (if existent)"
1371 ": fname: %s\n", fname
);
1372 virusfilter_cache_remove(
1377 DBG_INFO("Not scanned: scan on close is disabled: %s/%s\n",
1379 return close_result
;
1382 if (!fsp
->modified
) {
1383 DBG_NOTICE("Not scanned: File not modified: %s/%s\n",
1386 return close_result
;
1389 if (config
->exclude_files
&& is_in_path(fname
,
1390 config
->exclude_files
, false))
1392 DBG_INFO("Not scanned: exclude files: %s/%s\n",
1394 return close_result
;
1397 scan_result
= virusfilter_scan(handle
, config
, fsp
);
1399 switch (scan_result
) {
1400 case VIRUSFILTER_RESULT_CLEAN
:
1402 case VIRUSFILTER_RESULT_INFECTED
:
1403 scan_errno
= config
->infected_close_errno
;
1404 goto virusfilter_vfs_close_fail
;
1405 case VIRUSFILTER_RESULT_ERROR
:
1406 if (config
->block_access_on_error
) {
1407 DBG_INFO("Block access\n");
1408 scan_errno
= config
->scan_error_close_errno
;
1409 goto virusfilter_vfs_close_fail
;
1413 scan_errno
= config
->scan_error_close_errno
;
1414 goto virusfilter_vfs_close_fail
;
1417 if (close_errno
!= 0) {
1418 errno
= close_errno
;
1421 return close_result
;
1423 virusfilter_vfs_close_fail
:
1425 errno
= (scan_errno
!= 0) ? scan_errno
: close_errno
;
1427 return close_result
;
1430 static int virusfilter_vfs_unlinkat(struct vfs_handle_struct
*handle
,
1431 struct files_struct
*dirfsp
,
1432 const struct smb_filename
*smb_fname
,
1435 int ret
= SMB_VFS_NEXT_UNLINKAT(handle
,
1439 struct virusfilter_config
*config
= NULL
;
1441 char *cwd_fname
= handle
->conn
->cwd_fsp
->fsp_name
->base_name
;
1443 if (ret
!= 0 && errno
!= ENOENT
) {
1447 SMB_VFS_HANDLE_GET_DATA(handle
, config
,
1448 struct virusfilter_config
, return -1);
1450 if (config
->cache
== NULL
) {
1454 fname
= smb_fname
->base_name
;
1456 DBG_DEBUG("Removing cache entry (if existent): fname: %s\n", fname
);
1457 virusfilter_cache_remove(config
->cache
, cwd_fname
, fname
);
1462 static int virusfilter_vfs_renameat(
1463 struct vfs_handle_struct
*handle
,
1464 files_struct
*srcfsp
,
1465 const struct smb_filename
*smb_fname_src
,
1466 files_struct
*dstfsp
,
1467 const struct smb_filename
*smb_fname_dst
)
1469 int ret
= SMB_VFS_NEXT_RENAMEAT(handle
,
1474 struct virusfilter_config
*config
= NULL
;
1476 char *dst_fname
= NULL
;
1477 char *cwd_fname
= handle
->conn
->cwd_fsp
->fsp_name
->base_name
;
1483 SMB_VFS_HANDLE_GET_DATA(handle
, config
,
1484 struct virusfilter_config
, return -1);
1486 if (config
->cache
== NULL
) {
1490 fname
= smb_fname_src
->base_name
;
1491 dst_fname
= smb_fname_dst
->base_name
;
1493 DBG_DEBUG("Renaming cache entry: fname: %s to: %s\n",
1495 virusfilter_cache_entry_rename(config
->cache
,
1503 /* VFS operations */
1504 static struct vfs_fn_pointers vfs_virusfilter_fns
= {
1505 .connect_fn
= virusfilter_vfs_connect
,
1506 .disconnect_fn
= virusfilter_vfs_disconnect
,
1507 .open_fn
= virusfilter_vfs_open
,
1508 .close_fn
= virusfilter_vfs_close
,
1509 .unlinkat_fn
= virusfilter_vfs_unlinkat
,
1510 .renameat_fn
= virusfilter_vfs_renameat
,
1513 NTSTATUS
vfs_virusfilter_init(TALLOC_CTX
*);
1514 NTSTATUS
vfs_virusfilter_init(TALLOC_CTX
*ctx
)
1518 status
= smb_register_vfs(SMB_VFS_INTERFACE_VERSION
,
1520 &vfs_virusfilter_fns
);
1521 if (!NT_STATUS_IS_OK(status
)) {
1525 virusfilter_debug_class
= debug_add_class("virusfilter");
1526 if (virusfilter_debug_class
== -1) {
1527 virusfilter_debug_class
= DBGC_VFS
;
1528 DBG_ERR("Couldn't register custom debugging class!\n");
1530 DBG_DEBUG("Debug class number: %d\n", virusfilter_debug_class
);
1533 DBG_INFO("registered\n");