s3:libads: add ads_connect_machine() helper
[Samba.git] / source3 / modules / vfs_virusfilter.c
blobea1886d85c839eb49107926a34b2cf0b4219a547
1 /*
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_DUMMY,
39 VIRUSFILTER_SCANNER_FSAV,
40 VIRUSFILTER_SCANNER_SOPHOS
43 static const struct enum_list scanner_list[] = {
44 { VIRUSFILTER_SCANNER_CLAMAV, "clamav" },
45 { VIRUSFILTER_SCANNER_DUMMY, "dummy" },
46 { VIRUSFILTER_SCANNER_FSAV, "fsav" },
47 { VIRUSFILTER_SCANNER_SOPHOS, "sophos" },
48 { -1, NULL }
51 static const struct enum_list virusfilter_actions[] = {
52 { VIRUSFILTER_ACTION_QUARANTINE, "quarantine" },
53 { VIRUSFILTER_ACTION_RENAME, "rename" },
54 { VIRUSFILTER_ACTION_DELETE, "delete" },
56 /* alias for "delete" */
57 { VIRUSFILTER_ACTION_DELETE, "remove" },
59 /* alias for "delete" */
60 { VIRUSFILTER_ACTION_DELETE, "unlink" },
61 { VIRUSFILTER_ACTION_DO_NOTHING, "nothing" },
62 { -1, NULL}
65 static int virusfilter_config_destructor(struct virusfilter_config *config)
67 TALLOC_FREE(config->backend);
68 return 0;
72 * This is adapted from vfs_recycle module.
73 * Caller must have become_root();
75 static bool quarantine_directory_exist(
76 struct vfs_handle_struct *handle,
77 const char *dname)
79 int ret = -1;
80 struct smb_filename smb_fname = {
81 .base_name = discard_const_p(char, dname)
84 ret = SMB_VFS_STAT(handle->conn, &smb_fname);
85 if (ret == 0) {
86 return S_ISDIR(smb_fname.st.st_ex_mode);
89 return false;
92 /**
93 * Create directory tree
94 * @param conn connection
95 * @param dname Directory tree to be created
96 * @return Returns true for success
97 * This is adapted from vfs_recycle module.
98 * Caller must have become_root();
100 static bool quarantine_create_dir(
101 struct vfs_handle_struct *handle,
102 struct virusfilter_config *config,
103 const char *dname)
105 size_t len = 0;
106 size_t cat_len = 0;
107 char *new_dir = NULL;
108 char *tmp_str = NULL;
109 char *token = NULL;
110 char *tok_str = NULL;
111 bool status = false;
112 bool ok = false;
113 int ret = -1;
114 char *saveptr = NULL;
116 tmp_str = talloc_strdup(talloc_tos(), dname);
117 if (tmp_str == NULL) {
118 DBG_ERR("virusfilter-vfs: out of memory!\n");
119 errno = ENOMEM;
120 goto done;
122 tok_str = tmp_str;
124 len = strlen(dname)+1;
125 new_dir = (char *)talloc_size(talloc_tos(), len + 1);
126 if (new_dir == NULL) {
127 DBG_ERR("virusfilter-vfs: out of memory!\n");
128 errno = ENOMEM;
129 goto done;
131 *new_dir = '\0';
132 if (dname[0] == '/') {
133 /* Absolute path. */
134 cat_len = strlcat(new_dir, "/", len + 1);
135 if (cat_len >= len+1) {
136 goto done;
140 /* Create directory tree if necessary */
141 for (token = strtok_r(tok_str, "/", &saveptr);
142 token != NULL;
143 token = strtok_r(NULL, "/", &saveptr))
145 cat_len = strlcat(new_dir, token, len + 1);
146 if (cat_len >= len+1) {
147 goto done;
149 ok = quarantine_directory_exist(handle, new_dir);
150 if (ok == true) {
151 DBG_DEBUG("quarantine: dir %s already exists\n",
152 new_dir);
153 } else {
154 struct smb_filename *smb_fname = NULL;
156 DBG_INFO("quarantine: creating new dir %s\n", new_dir);
158 smb_fname = synthetic_smb_fname(talloc_tos(),
159 new_dir,
160 NULL,
161 NULL,
164 if (smb_fname == NULL) {
165 goto done;
168 ret = SMB_VFS_NEXT_MKDIRAT(handle,
169 handle->conn->cwd_fsp,
170 smb_fname,
171 config->quarantine_dir_mode);
172 if (ret != 0) {
173 TALLOC_FREE(smb_fname);
175 DBG_WARNING("quarantine: mkdirat failed for %s "
176 "with error: %s\n", new_dir,
177 strerror(errno));
178 status = false;
179 goto done;
181 TALLOC_FREE(smb_fname);
183 cat_len = strlcat(new_dir, "/", len + 1);
184 if (cat_len >= len + 1) {
185 goto done;
189 status = true;
190 done:
191 TALLOC_FREE(tmp_str);
192 TALLOC_FREE(new_dir);
193 return status;
196 static int virusfilter_vfs_connect(
197 struct vfs_handle_struct *handle,
198 const char *svc,
199 const char *user)
201 int snum = SNUM(handle->conn);
202 struct virusfilter_config *config = NULL;
203 const char *exclude_files = NULL;
204 const char *infected_files = NULL;
205 const char *temp_quarantine_dir_mode = NULL;
206 const char *infected_file_command = NULL;
207 const char *scan_error_command = NULL;
208 const char *quarantine_dir = NULL;
209 const char *quarantine_prefix = NULL;
210 const char *quarantine_suffix = NULL;
211 const char *rename_prefix = NULL;
212 const char *rename_suffix = NULL;
213 const char *socket_path = NULL;
214 char *sret = NULL;
215 char *tmp = NULL;
216 enum virusfilter_scanner_enum backend;
217 int connect_timeout = 0;
218 int io_timeout = 0;
219 int ret = -1;
221 config = talloc_zero(handle, struct virusfilter_config);
222 if (config == NULL) {
223 DBG_ERR("talloc_zero failed\n");
224 return -1;
226 talloc_set_destructor(config, virusfilter_config_destructor);
228 SMB_VFS_HANDLE_SET_DATA(handle, config, NULL,
229 struct virusfilter_config, return -1);
231 config->scan_request_limit = lp_parm_int(
232 snum, "virusfilter", "scan request limit", 0);
234 config->scan_on_open = lp_parm_bool(
235 snum, "virusfilter", "scan on open", true);
237 config->scan_on_close = lp_parm_bool(
238 snum, "virusfilter", "scan on close", false);
240 config->max_nested_scan_archive = lp_parm_int(
241 snum, "virusfilter", "max nested scan archive", 1);
243 config->scan_archive = lp_parm_bool(
244 snum, "virusfilter", "scan archive", false);
246 config->scan_mime = lp_parm_bool(
247 snum, "virusfilter", "scan mime", false);
249 config->max_file_size = (ssize_t)lp_parm_ulong(
250 snum, "virusfilter", "max file size", 100000000L);
252 config->min_file_size = (ssize_t)lp_parm_ulong(
253 snum, "virusfilter", "min file size", 10);
255 exclude_files = lp_parm_const_string(
256 snum, "virusfilter", "exclude files", NULL);
257 if (exclude_files != NULL) {
258 set_namearray(&config->exclude_files, exclude_files);
261 infected_files = lp_parm_const_string(
262 snum, "virusfilter", "infected files", NULL);
263 if (infected_files != NULL) {
264 set_namearray(&config->infected_files, infected_files);
267 config->cache_entry_limit = lp_parm_int(
268 snum, "virusfilter", "cache entry limit", 100);
270 config->cache_time_limit = lp_parm_int(
271 snum, "virusfilter", "cache time limit", 10);
273 config->infected_file_action = lp_parm_enum(
274 snum, "virusfilter", "infected file action",
275 virusfilter_actions, VIRUSFILTER_ACTION_DO_NOTHING);
277 infected_file_command = lp_parm_const_string(
278 snum, "virusfilter", "infected file command", NULL);
279 if (infected_file_command != NULL) {
280 config->infected_file_command = talloc_strdup(
281 config,
282 infected_file_command);
283 if (config->infected_file_command == NULL) {
284 DBG_ERR("virusfilter-vfs: out of memory!\n");
285 return -1;
288 scan_error_command = lp_parm_const_string(
289 snum, "virusfilter", "scan error command", NULL);
290 if (scan_error_command != NULL) {
291 config->scan_error_command = talloc_strdup(config,
292 scan_error_command);
293 if (config->scan_error_command == NULL) {
294 DBG_ERR("virusfilter-vfs: out of memory!\n");
295 return -1;
299 config->block_access_on_error = lp_parm_bool(
300 snum, "virusfilter", "block access on error", false);
302 tmp = talloc_asprintf(config, "%s/.quarantine",
303 handle->conn->connectpath);
305 quarantine_dir = lp_parm_const_string(
306 snum, "virusfilter", "quarantine directory",
307 tmp ? tmp : "/tmp/.quarantine");
308 if (quarantine_dir != NULL) {
309 config->quarantine_dir = talloc_strdup(config, quarantine_dir);
310 if (config->quarantine_dir == NULL) {
311 DBG_ERR("virusfilter-vfs: out of memory!\n");
312 return -1;
316 if (tmp != config->quarantine_dir) {
317 TALLOC_FREE(tmp);
320 temp_quarantine_dir_mode = lp_parm_const_string(
321 snum, "virusfilter", "quarantine directory mode", "0755");
322 if (temp_quarantine_dir_mode != NULL) {
323 unsigned int mode = 0;
324 sscanf(temp_quarantine_dir_mode, "%o", &mode);
325 config->quarantine_dir_mode = mode;
328 quarantine_prefix = lp_parm_const_string(
329 snum, "virusfilter", "quarantine prefix",
330 VIRUSFILTER_DEFAULT_QUARANTINE_PREFIX);
331 if (quarantine_prefix != NULL) {
332 config->quarantine_prefix = talloc_strdup(config,
333 quarantine_prefix);
334 if (config->quarantine_prefix == NULL) {
335 DBG_ERR("virusfilter-vfs: out of memory!\n");
336 return -1;
340 quarantine_suffix = lp_parm_const_string(
341 snum, "virusfilter", "quarantine suffix",
342 VIRUSFILTER_DEFAULT_QUARANTINE_SUFFIX);
343 if (quarantine_suffix != NULL) {
344 config->quarantine_suffix = talloc_strdup(config,
345 quarantine_suffix);
346 if (config->quarantine_suffix == NULL) {
347 DBG_ERR("virusfilter-vfs: out of memory!\n");
348 return -1;
353 * Make sure prefixes and suffixes do not contain directory
354 * delimiters
356 if (config->quarantine_prefix != NULL) {
357 sret = strstr(config->quarantine_prefix, "/");
358 if (sret != NULL) {
359 DBG_ERR("quarantine prefix must not contain directory "
360 "delimiter(s) such as '/' (%s replaced with %s)\n",
361 config->quarantine_prefix,
362 VIRUSFILTER_DEFAULT_QUARANTINE_PREFIX);
363 config->quarantine_prefix =
364 VIRUSFILTER_DEFAULT_QUARANTINE_PREFIX;
367 if (config->quarantine_suffix != NULL) {
368 sret = strstr(config->quarantine_suffix, "/");
369 if (sret != NULL) {
370 DBG_ERR("quarantine suffix must not contain directory "
371 "delimiter(s) such as '/' (%s replaced with %s)\n",
372 config->quarantine_suffix,
373 VIRUSFILTER_DEFAULT_QUARANTINE_SUFFIX);
374 config->quarantine_suffix =
375 VIRUSFILTER_DEFAULT_QUARANTINE_SUFFIX;
379 config->quarantine_keep_tree = lp_parm_bool(
380 snum, "virusfilter", "quarantine keep tree", true);
382 config->quarantine_keep_name = lp_parm_bool(
383 snum, "virusfilter", "quarantine keep name", true);
385 rename_prefix = lp_parm_const_string(
386 snum, "virusfilter", "rename prefix",
387 VIRUSFILTER_DEFAULT_RENAME_PREFIX);
388 if (rename_prefix != NULL) {
389 config->rename_prefix = talloc_strdup(config, rename_prefix);
390 if (config->rename_prefix == NULL) {
391 DBG_ERR("virusfilter-vfs: out of memory!\n");
392 return -1;
396 rename_suffix = lp_parm_const_string(
397 snum, "virusfilter", "rename suffix",
398 VIRUSFILTER_DEFAULT_RENAME_SUFFIX);
399 if (rename_suffix != NULL) {
400 config->rename_suffix = talloc_strdup(config, rename_suffix);
401 if (config->rename_suffix == NULL) {
402 DBG_ERR("virusfilter-vfs: out of memory!\n");
403 return -1;
408 * Make sure prefixes and suffixes do not contain directory
409 * delimiters
411 if (config->rename_prefix != NULL) {
412 sret = strstr(config->rename_prefix, "/");
413 if (sret != NULL) {
414 DBG_ERR("rename prefix must not contain directory "
415 "delimiter(s) such as '/' (%s replaced with %s)\n",
416 config->rename_prefix,
417 VIRUSFILTER_DEFAULT_RENAME_PREFIX);
418 config->rename_prefix =
419 VIRUSFILTER_DEFAULT_RENAME_PREFIX;
422 if (config->rename_suffix != NULL) {
423 sret = strstr(config->rename_suffix, "/");
424 if (sret != NULL) {
425 DBG_ERR("rename suffix must not contain directory "
426 "delimiter(s) such as '/' (%s replaced with %s)\n",
427 config->rename_suffix,
428 VIRUSFILTER_DEFAULT_RENAME_SUFFIX);
429 config->rename_suffix =
430 VIRUSFILTER_DEFAULT_RENAME_SUFFIX;
434 config->infected_open_errno = lp_parm_int(
435 snum, "virusfilter", "infected file errno on open", EACCES);
437 config->infected_close_errno = lp_parm_int(
438 snum, "virusfilter", "infected file errno on close", 0);
440 config->scan_error_open_errno = lp_parm_int(
441 snum, "virusfilter", "scan error errno on open", EACCES);
443 config->scan_error_close_errno = lp_parm_int(
444 snum, "virusfilter", "scan error errno on close", 0);
446 socket_path = lp_parm_const_string(
447 snum, "virusfilter", "socket path", NULL);
448 if (socket_path != NULL) {
449 config->socket_path = talloc_strdup(config, socket_path);
450 if (config->socket_path == NULL) {
451 DBG_ERR("virusfilter-vfs: out of memory!\n");
452 return -1;
456 /* canonicalize socket_path */
457 if (config->socket_path != NULL && config->socket_path[0] != '/') {
458 DBG_ERR("socket path must be an absolute path. "
459 "Using backend default\n");
460 config->socket_path = NULL;
462 if (config->socket_path != NULL) {
463 config->socket_path = canonicalize_absolute_path(
464 handle, config->socket_path);
465 if (config->socket_path == NULL) {
466 errno = ENOMEM;
467 return -1;
471 connect_timeout = lp_parm_int(snum, "virusfilter",
472 "connect timeout", 30000);
474 io_timeout = lp_parm_int(snum, "virusfilter", "io timeout", 60000);
476 config->io_h = virusfilter_io_new(config, connect_timeout, io_timeout);
477 if (config->io_h == NULL) {
478 DBG_ERR("virusfilter_io_new failed\n");
479 return -1;
482 if (config->cache_entry_limit > 0) {
483 config->cache = virusfilter_cache_new(handle,
484 config->cache_entry_limit,
485 config->cache_time_limit);
486 if (config->cache == NULL) {
487 DBG_ERR("Initializing cache failed: Cache disabled\n");
488 return -1;
493 * Check quarantine directory now to save processing
494 * and becoming root over and over.
496 if (config->infected_file_action == VIRUSFILTER_ACTION_QUARANTINE) {
497 bool ok = true;
498 bool dir_exists;
501 * Do SMB_VFS_NEXT_MKDIR(config->quarantine_dir)
502 * hierarchy
504 become_root();
505 dir_exists = quarantine_directory_exist(handle,
506 config->quarantine_dir);
507 if (!dir_exists) {
508 DBG_DEBUG("Creating quarantine directory: %s\n",
509 config->quarantine_dir);
510 ok = quarantine_create_dir(handle, config,
511 config->quarantine_dir);
513 unbecome_root();
514 if (!ok) {
515 DBG_ERR("Creating quarantine directory %s "
516 "failed with %s\n",
517 config->quarantine_dir,
518 strerror(errno));
519 return -1;
524 * Now that the frontend options are initialized, load the configured
525 * backend.
528 backend = (enum virusfilter_scanner_enum)lp_parm_enum(snum,
529 "virusfilter",
530 "scanner",
531 scanner_list,
532 -1);
533 if (backend == (enum virusfilter_scanner_enum)-1) {
534 DBG_ERR("No AV-Scanner configured, "
535 "please set \"virusfilter:scanner\"\n");
536 return -1;
539 switch (backend) {
540 case VIRUSFILTER_SCANNER_SOPHOS:
541 ret = virusfilter_sophos_init(config);
542 break;
543 case VIRUSFILTER_SCANNER_FSAV:
544 ret = virusfilter_fsav_init(config);
545 break;
546 case VIRUSFILTER_SCANNER_CLAMAV:
547 ret = virusfilter_clamav_init(config);
548 break;
549 case VIRUSFILTER_SCANNER_DUMMY:
550 ret = virusfilter_dummy_init(config);
551 break;
552 default:
553 DBG_ERR("Unhandled scanner %d\n", backend);
554 return -1;
556 if (ret != 0) {
557 DBG_ERR("Scanner backend init failed\n");
558 return -1;
561 if (config->backend->fns->connect != NULL) {
562 ret = config->backend->fns->connect(handle, config, svc, user);
563 if (ret == -1) {
564 return -1;
568 return SMB_VFS_NEXT_CONNECT(handle, svc, user);
571 static void virusfilter_vfs_disconnect(struct vfs_handle_struct *handle)
573 struct virusfilter_config *config = NULL;
575 SMB_VFS_HANDLE_GET_DATA(handle, config,
576 struct virusfilter_config, return);
578 if (config->backend->fns->disconnect != NULL) {
579 config->backend->fns->disconnect(handle);
582 free_namearray(config->exclude_files);
583 virusfilter_io_disconnect(config->io_h);
585 SMB_VFS_NEXT_DISCONNECT(handle);
588 static int virusfilter_set_module_env(TALLOC_CTX *mem_ctx,
589 struct virusfilter_config *config,
590 char **env_list)
592 int ret;
594 ret = virusfilter_env_set(mem_ctx, env_list, "VIRUSFILTER_VERSION",
595 VIRUSFILTER_VERSION);
596 if (ret == -1) {
597 return -1;
599 ret = virusfilter_env_set(mem_ctx, env_list, "VIRUSFILTER_MODULE_NAME",
600 config->backend->name);
601 if (ret == -1) {
602 return -1;
605 if (config->backend->version != 0) {
606 char *version = NULL;
608 version = talloc_asprintf(talloc_tos(), "%u",
609 config->backend->version);
610 if (version == NULL) {
611 return -1;
613 ret = virusfilter_env_set(mem_ctx, env_list,
614 "VIRUSFILTER_MODULE_VERSION",
615 version);
616 TALLOC_FREE(version);
617 if (ret == -1) {
618 return -1;
622 return 0;
625 static char *quarantine_check_tree(TALLOC_CTX *mem_ctx,
626 struct vfs_handle_struct *handle,
627 struct virusfilter_config *config,
628 const struct smb_filename *smb_fname,
629 char *q_dir_in,
630 char *cwd_fname)
632 char *temp_path = NULL;
633 char *q_dir_out = NULL;
634 bool ok;
636 temp_path = talloc_asprintf(talloc_tos(), "%s/%s", q_dir_in, cwd_fname);
637 if (temp_path == NULL) {
638 DBG_ERR("talloc_asprintf failed\n");
639 goto out;
642 become_root();
643 ok = quarantine_directory_exist(handle, temp_path);
644 unbecome_root();
645 if (ok) {
646 DBG_DEBUG("quarantine: directory [%s] exists\n", temp_path);
647 q_dir_out = talloc_move(mem_ctx, &temp_path);
648 goto out;
651 DBG_DEBUG("quarantine: Creating directory %s\n", temp_path);
653 become_root();
654 ok = quarantine_create_dir(handle, config, temp_path);
655 unbecome_root();
656 if (!ok) {
657 DBG_NOTICE("Could not create quarantine directory [%s], "
658 "ignoring for [%s]\n",
659 temp_path, smb_fname_str_dbg(smb_fname));
660 goto out;
663 q_dir_out = talloc_move(mem_ctx, &temp_path);
665 out:
666 TALLOC_FREE(temp_path);
667 return q_dir_out;
670 static virusfilter_action infected_file_action_quarantine(
671 struct vfs_handle_struct *handle,
672 struct virusfilter_config *config,
673 TALLOC_CTX *mem_ctx,
674 const struct files_struct *fsp,
675 const char **filepath_newp)
677 TALLOC_CTX *frame = talloc_stackframe();
678 connection_struct *conn = handle->conn;
679 char *cwd_fname = fsp->conn->cwd_fsp->fsp_name->base_name;
680 char *fname = fsp->fsp_name->base_name;
681 const struct smb_filename *smb_fname = fsp->fsp_name;
682 struct smb_filename *q_smb_fname = NULL;
683 char *q_dir = NULL;
684 char *q_prefix = NULL;
685 char *q_suffix = NULL;
686 char *q_filepath = NULL;
687 char *dir_name = NULL;
688 const char *base_name = NULL;
689 char *rand_filename_component = NULL;
690 virusfilter_action action = VIRUSFILTER_ACTION_QUARANTINE;
691 bool ok = false;
692 int ret = -1;
693 int saved_errno = 0;
695 q_dir = virusfilter_string_sub(frame, conn,
696 config->quarantine_dir);
697 q_prefix = virusfilter_string_sub(frame, conn,
698 config->quarantine_prefix);
699 q_suffix = virusfilter_string_sub(frame, conn,
700 config->quarantine_suffix);
701 if (q_dir == NULL || q_prefix == NULL || q_suffix == NULL) {
702 DBG_ERR("Quarantine failed: %s/%s: Cannot allocate "
703 "memory\n", cwd_fname, fname);
704 action = VIRUSFILTER_ACTION_DO_NOTHING;
705 goto out;
708 if (config->quarantine_keep_name || config->quarantine_keep_tree) {
709 ok = parent_dirname(frame, smb_fname->base_name,
710 &dir_name, &base_name);
711 if (!ok) {
712 DBG_ERR("parent_dirname failed\n");
713 action = VIRUSFILTER_ACTION_DO_NOTHING;
714 goto out;
717 if (config->quarantine_keep_tree) {
718 char *tree = NULL;
720 tree = quarantine_check_tree(frame, handle, config,
721 smb_fname, q_dir,
722 cwd_fname);
723 if (tree == NULL) {
725 * If we can't create the tree, just move it
726 * into the toplevel quarantine dir.
728 tree = q_dir;
730 q_dir = tree;
734 /* Get a 16 byte + \0 random filename component. */
735 rand_filename_component = generate_random_str(frame, 16);
736 if (rand_filename_component == NULL) {
737 DBG_ERR("generate_random_str failed\n");
738 action = VIRUSFILTER_ACTION_DO_NOTHING;
739 goto out;
742 if (config->quarantine_keep_name) {
743 q_filepath = talloc_asprintf(frame, "%s/%s%s%s-%s",
744 q_dir, q_prefix,
745 base_name, q_suffix,
746 rand_filename_component);
747 } else {
748 q_filepath = talloc_asprintf(frame, "%s/%s%s",
749 q_dir, q_prefix,
750 rand_filename_component);
752 if (q_filepath == NULL) {
753 DBG_ERR("talloc_asprintf failed\n");
754 action = VIRUSFILTER_ACTION_DO_NOTHING;
755 goto out;
758 q_smb_fname = synthetic_smb_fname(frame,
759 q_filepath,
760 smb_fname->stream_name,
761 NULL,
763 smb_fname->flags);
764 if (q_smb_fname == NULL) {
765 action = VIRUSFILTER_ACTION_DO_NOTHING;
766 goto out;
769 become_root();
770 ret = virusfilter_vfs_next_move(handle, smb_fname, q_smb_fname);
771 if (ret == -1) {
772 saved_errno = errno;
774 unbecome_root();
775 if (ret == -1) {
776 DBG_ERR("Quarantine [%s/%s] rename to %s failed: %s\n",
777 cwd_fname, fname, q_filepath, strerror(saved_errno));
778 errno = saved_errno;
779 action = VIRUSFILTER_ACTION_DO_NOTHING;
780 goto out;
783 *filepath_newp = talloc_move(mem_ctx, &q_filepath);
785 out:
786 TALLOC_FREE(frame);
787 return action;
790 static virusfilter_action infected_file_action_rename(
791 struct vfs_handle_struct *handle,
792 struct virusfilter_config *config,
793 TALLOC_CTX *mem_ctx,
794 const struct files_struct *fsp,
795 const char **filepath_newp)
797 TALLOC_CTX *frame = talloc_stackframe();
798 connection_struct *conn = handle->conn;
799 char *cwd_fname = fsp->conn->cwd_fsp->fsp_name->base_name;
800 char *fname = fsp->fsp_name->base_name;
801 const struct smb_filename *smb_fname = fsp->fsp_name;
802 struct smb_filename *q_smb_fname = NULL;
803 char *q_dir = NULL;
804 char *q_prefix = NULL;
805 char *q_suffix = NULL;
806 char *q_filepath = NULL;
807 const char *base_name = NULL;
808 virusfilter_action action = VIRUSFILTER_ACTION_RENAME;
809 bool ok = false;
810 int ret = -1;
811 int saved_errno = 0;
813 q_prefix = virusfilter_string_sub(frame, conn,
814 config->rename_prefix);
815 q_suffix = virusfilter_string_sub(frame, conn,
816 config->rename_suffix);
817 if (q_prefix == NULL || q_suffix == NULL) {
818 DBG_ERR("Rename failed: %s/%s: Cannot allocate "
819 "memory\n", cwd_fname, fname);
820 action = VIRUSFILTER_ACTION_DO_NOTHING;
821 goto out;
824 ok = parent_dirname(frame, fname, &q_dir, &base_name);
825 if (!ok) {
826 DBG_ERR("Rename failed: %s/%s: Cannot allocate "
827 "memory\n", cwd_fname, fname);
828 action = VIRUSFILTER_ACTION_DO_NOTHING;
829 goto out;
832 if (q_dir == NULL) {
833 DBG_ERR("Rename failed: %s/%s: Cannot allocate "
834 "memory\n", cwd_fname, fname);
835 action = VIRUSFILTER_ACTION_DO_NOTHING;
836 goto out;
839 q_filepath = talloc_asprintf(frame, "%s/%s%s%s", q_dir,
840 q_prefix, base_name, q_suffix);
842 q_smb_fname = synthetic_smb_fname(frame, q_filepath,
843 smb_fname->stream_name, NULL,
845 smb_fname->flags);
846 if (q_smb_fname == NULL) {
847 action = VIRUSFILTER_ACTION_DO_NOTHING;
848 goto out;
851 become_root();
852 ret = virusfilter_vfs_next_move(handle, smb_fname, q_smb_fname);
853 if (ret == -1) {
854 saved_errno = errno;
856 unbecome_root();
858 if (ret == -1) {
859 DBG_ERR("Rename failed: %s/%s: Rename failed: %s\n",
860 cwd_fname, fname, strerror(saved_errno));
861 errno = saved_errno;
862 action = VIRUSFILTER_ACTION_DO_NOTHING;
863 goto out;
866 *filepath_newp = talloc_move(mem_ctx, &q_filepath);
868 out:
869 TALLOC_FREE(frame);
870 return action;
873 static virusfilter_action infected_file_action_delete(
874 struct vfs_handle_struct *handle,
875 const struct files_struct *fsp)
877 int ret;
878 int saved_errno = 0;
880 become_root();
881 ret = SMB_VFS_NEXT_UNLINKAT(handle,
882 handle->conn->cwd_fsp,
883 fsp->fsp_name,
885 if (ret == -1) {
886 saved_errno = errno;
888 unbecome_root();
889 if (ret == -1) {
890 DBG_ERR("Delete [%s/%s] failed: %s\n",
891 fsp->conn->cwd_fsp->fsp_name->base_name,
892 fsp->fsp_name->base_name,
893 strerror(saved_errno));
894 errno = saved_errno;
895 return VIRUSFILTER_ACTION_DO_NOTHING;
898 return VIRUSFILTER_ACTION_DELETE;
901 static virusfilter_action virusfilter_do_infected_file_action(
902 struct vfs_handle_struct *handle,
903 struct virusfilter_config *config,
904 TALLOC_CTX *mem_ctx,
905 const struct files_struct *fsp,
906 const char **filepath_newp)
908 virusfilter_action action;
910 *filepath_newp = NULL;
912 switch (config->infected_file_action) {
913 case VIRUSFILTER_ACTION_RENAME:
914 action = infected_file_action_rename(handle, config, mem_ctx,
915 fsp, filepath_newp);
916 break;
918 case VIRUSFILTER_ACTION_QUARANTINE:
919 action = infected_file_action_quarantine(handle, config, mem_ctx,
920 fsp, filepath_newp);
921 break;
923 case VIRUSFILTER_ACTION_DELETE:
924 action = infected_file_action_delete(handle, fsp);
925 break;
927 case VIRUSFILTER_ACTION_DO_NOTHING:
928 default:
929 action = VIRUSFILTER_ACTION_DO_NOTHING;
930 break;
933 return action;
936 static virusfilter_action virusfilter_treat_infected_file(
937 struct vfs_handle_struct *handle,
938 struct virusfilter_config *config,
939 const struct files_struct *fsp,
940 const char *report,
941 bool is_cache)
943 connection_struct *conn = handle->conn;
944 char *cwd_fname = fsp->conn->cwd_fsp->fsp_name->base_name;
945 char *fname = fsp->fsp_name->base_name;
946 TALLOC_CTX *mem_ctx = talloc_tos();
947 int i;
948 virusfilter_action action;
949 const char *action_name = "UNKNOWN";
950 const char *filepath_q = NULL;
951 char *env_list = NULL;
952 char *command = NULL;
953 int command_result;
954 int ret;
956 action = virusfilter_do_infected_file_action(handle, config, mem_ctx,
957 fsp, &filepath_q);
958 for (i=0; virusfilter_actions[i].name; i++) {
959 if (virusfilter_actions[i].value == action) {
960 action_name = virusfilter_actions[i].name;
961 break;
964 DBG_WARNING("Infected file action: %s/%s: %s\n", cwd_fname,
965 fname, action_name);
967 if (!config->infected_file_command) {
968 return action;
971 ret = virusfilter_set_module_env(mem_ctx, config, &env_list);
972 if (ret == -1) {
973 goto done;
975 ret = virusfilter_env_set(mem_ctx, &env_list,
976 "VIRUSFILTER_INFECTED_SERVICE_FILE_PATH",
977 fname);
978 if (ret == -1) {
979 goto done;
981 if (report != NULL) {
982 ret = virusfilter_env_set(mem_ctx, &env_list,
983 "VIRUSFILTER_INFECTED_FILE_REPORT",
984 report);
985 if (ret == -1) {
986 goto done;
989 ret = virusfilter_env_set(mem_ctx, &env_list,
990 "VIRUSFILTER_INFECTED_FILE_ACTION",
991 action_name);
992 if (ret == -1) {
993 goto done;
995 if (filepath_q != NULL) {
996 ret = virusfilter_env_set(mem_ctx, &env_list,
997 "VIRUSFILTER_QUARANTINED_FILE_PATH",
998 filepath_q);
999 if (ret == -1) {
1000 goto done;
1003 if (is_cache) {
1004 ret = virusfilter_env_set(mem_ctx, &env_list,
1005 "VIRUSFILTER_RESULT_IS_CACHE", "yes");
1006 if (ret == -1) {
1007 goto done;
1011 command = virusfilter_string_sub(mem_ctx, conn,
1012 config->infected_file_command);
1013 if (command == NULL) {
1014 DBG_ERR("virusfilter_string_sub failed\n");
1015 goto done;
1018 DBG_NOTICE("Infected file command line: %s/%s: %s\n", cwd_fname,
1019 fname, command);
1021 command_result = virusfilter_shell_run(mem_ctx, command, &env_list,
1022 conn, true);
1023 if (command_result != 0) {
1024 DBG_ERR("Infected file command failed: %d\n", command_result);
1027 DBG_DEBUG("Infected file command finished: %d\n", command_result);
1029 done:
1030 TALLOC_FREE(env_list);
1031 TALLOC_FREE(command);
1033 return action;
1036 static void virusfilter_treat_scan_error(
1037 struct vfs_handle_struct *handle,
1038 struct virusfilter_config *config,
1039 const struct files_struct *fsp,
1040 const char *report,
1041 bool is_cache)
1043 connection_struct *conn = handle->conn;
1044 const char *cwd_fname = fsp->conn->cwd_fsp->fsp_name->base_name;
1045 const char *fname = fsp->fsp_name->base_name;
1046 TALLOC_CTX *mem_ctx = talloc_tos();
1047 char *env_list = NULL;
1048 char *command = NULL;
1049 int command_result;
1050 int ret;
1052 if (!config->scan_error_command) {
1053 return;
1055 ret = virusfilter_set_module_env(mem_ctx, config, &env_list);
1056 if (ret == -1) {
1057 goto done;
1059 ret = virusfilter_env_set(mem_ctx, &env_list,
1060 "VIRUSFILTER_SCAN_ERROR_SERVICE_FILE_PATH",
1061 fname);
1062 if (ret == -1) {
1063 goto done;
1065 if (report != NULL) {
1066 ret = virusfilter_env_set(mem_ctx, &env_list,
1067 "VIRUSFILTER_SCAN_ERROR_REPORT",
1068 report);
1069 if (ret == -1) {
1070 goto done;
1073 if (is_cache) {
1074 ret = virusfilter_env_set(mem_ctx, &env_list,
1075 "VIRUSFILTER_RESULT_IS_CACHE", "1");
1076 if (ret == -1) {
1077 goto done;
1081 command = virusfilter_string_sub(mem_ctx, conn,
1082 config->scan_error_command);
1083 if (command == NULL) {
1084 DBG_ERR("virusfilter_string_sub failed\n");
1085 goto done;
1088 DBG_NOTICE("Scan error command line: %s/%s: %s\n", cwd_fname,
1089 fname, command);
1091 command_result = virusfilter_shell_run(mem_ctx, command, &env_list,
1092 conn, true);
1093 if (command_result != 0) {
1094 DBG_ERR("Scan error command failed: %d\n", command_result);
1097 done:
1098 TALLOC_FREE(env_list);
1099 TALLOC_FREE(command);
1102 static virusfilter_result virusfilter_scan(
1103 struct vfs_handle_struct *handle,
1104 struct virusfilter_config *config,
1105 const struct files_struct *fsp)
1107 virusfilter_result scan_result;
1108 char *scan_report = NULL;
1109 const char *fname = fsp->fsp_name->base_name;
1110 const char *cwd_fname = fsp->conn->cwd_fsp->fsp_name->base_name;
1111 struct virusfilter_cache_entry *scan_cache_e = NULL;
1112 bool is_cache = false;
1113 virusfilter_action file_action = VIRUSFILTER_ACTION_DO_NOTHING;
1114 bool add_scan_cache = true;
1115 bool ok = false;
1117 if (config->cache) {
1118 DBG_DEBUG("Searching cache entry: fname: %s\n", fname);
1119 scan_cache_e = virusfilter_cache_get(config->cache,
1120 cwd_fname, fname);
1121 if (scan_cache_e != NULL) {
1122 DBG_DEBUG("Cache entry found: cached result: %d\n",
1123 scan_cache_e->result);
1124 is_cache = true;
1125 scan_result = scan_cache_e->result;
1126 scan_report = scan_cache_e->report;
1127 goto virusfilter_scan_result_eval;
1129 DBG_DEBUG("Cache entry not found\n");
1132 if (config->backend->fns->scan_init != NULL) {
1133 scan_result = config->backend->fns->scan_init(config);
1134 if (scan_result != VIRUSFILTER_RESULT_OK) {
1135 scan_result = VIRUSFILTER_RESULT_ERROR;
1136 scan_report = talloc_asprintf(
1137 talloc_tos(),
1138 "Initializing scanner failed");
1139 goto virusfilter_scan_result_eval;
1143 scan_result = config->backend->fns->scan(handle, config, fsp,
1144 &scan_report);
1146 if (config->backend->fns->scan_end != NULL) {
1147 bool scan_end = true;
1149 if (config->scan_request_limit > 0) {
1150 scan_end = false;
1151 config->scan_request_count++;
1152 if (config->scan_request_count >=
1153 config->scan_request_limit)
1155 scan_end = true;
1156 config->scan_request_count = 0;
1159 if (scan_end) {
1160 config->backend->fns->scan_end(config);
1164 virusfilter_scan_result_eval:
1166 switch (scan_result) {
1167 case VIRUSFILTER_RESULT_CLEAN:
1168 DBG_INFO("Scan result: Clean: %s/%s\n", cwd_fname, fname);
1169 break;
1171 case VIRUSFILTER_RESULT_INFECTED:
1172 DBG_ERR("Scan result: Infected: %s/%s: %s\n",
1173 cwd_fname, fname, scan_report ? scan_report :
1174 "infected (memory error on report)");
1175 file_action = virusfilter_treat_infected_file(handle,
1176 config, fsp, scan_report, is_cache);
1177 if (file_action != VIRUSFILTER_ACTION_DO_NOTHING) {
1178 add_scan_cache = false;
1180 break;
1182 case VIRUSFILTER_RESULT_SUSPECTED:
1183 if (!config->block_suspected_file) {
1184 break;
1186 DBG_ERR("Scan result: Suspected: %s/%s: %s\n",
1187 cwd_fname, fname, scan_report ? scan_report :
1188 "suspected infection (memory error on report)");
1189 file_action = virusfilter_treat_infected_file(handle,
1190 config, fsp, scan_report, is_cache);
1191 if (file_action != VIRUSFILTER_ACTION_DO_NOTHING) {
1192 add_scan_cache = false;
1194 break;
1196 case VIRUSFILTER_RESULT_ERROR:
1197 DBG_ERR("Scan result: Error: %s/%s: %s\n",
1198 cwd_fname, fname, scan_report ? scan_report :
1199 "error (memory error on report)");
1200 virusfilter_treat_scan_error(handle, config, fsp,
1201 scan_report, is_cache);
1202 add_scan_cache = false;
1203 break;
1205 default:
1206 DBG_ERR("Scan result: Unknown result code %d: %s/%s: %s\n",
1207 scan_result, cwd_fname, fname, scan_report ?
1208 scan_report : "Unknown (memory error on report)");
1209 virusfilter_treat_scan_error(handle, config, fsp,
1210 scan_report, is_cache);
1211 add_scan_cache = false;
1212 break;
1215 if (config->cache) {
1216 if (!is_cache && add_scan_cache) {
1217 DBG_DEBUG("Adding new cache entry: %s, %d\n", fname,
1218 scan_result);
1219 ok = virusfilter_cache_entry_add(
1220 config->cache, cwd_fname, fname,
1221 scan_result, scan_report);
1222 if (!ok) {
1223 DBG_ERR("Cannot create cache entry: "
1224 "virusfilter_cache_entry_new failed\n");
1225 goto virusfilter_scan_return;
1227 } else if (is_cache) {
1228 virusfilter_cache_entry_free(scan_cache_e);
1232 virusfilter_scan_return:
1233 return scan_result;
1236 static int virusfilter_vfs_openat(struct vfs_handle_struct *handle,
1237 const struct files_struct *dirfsp,
1238 const struct smb_filename *smb_fname_in,
1239 struct files_struct *fsp,
1240 const struct vfs_open_how *how)
1242 TALLOC_CTX *mem_ctx = talloc_tos();
1243 struct virusfilter_config *config = NULL;
1244 const char *cwd_fname = dirfsp->fsp_name->base_name;
1245 virusfilter_result scan_result;
1246 const char *fname = fsp->fsp_name->base_name;
1247 char *dir_name = NULL;
1248 const char *base_name = NULL;
1249 int scan_errno = 0;
1250 size_t test_prefix;
1251 size_t test_suffix;
1252 int rename_trap_count = 0;
1253 int ret;
1254 bool ok1;
1255 char *sret = NULL;
1256 struct smb_filename *smb_fname = NULL;
1257 SMB_STRUCT_STAT sbuf = smb_fname_in->st;
1259 SMB_VFS_HANDLE_GET_DATA(handle, config,
1260 struct virusfilter_config, return -1);
1262 if (fsp->fsp_flags.is_directory) {
1263 DBG_INFO("Not scanned: Directory: %s/\n", cwd_fname);
1264 goto virusfilter_vfs_open_next;
1267 test_prefix = strlen(config->rename_prefix);
1268 test_suffix = strlen(config->rename_suffix);
1269 if (test_prefix > 0) {
1270 rename_trap_count++;
1272 if (test_suffix > 0) {
1273 rename_trap_count++;
1276 smb_fname = cp_smb_filename(mem_ctx, smb_fname_in);
1277 if (smb_fname == NULL) {
1278 goto virusfilter_vfs_open_fail;
1281 if (is_named_stream(smb_fname)) {
1282 DBG_INFO("Not scanned: only file backed streams can be scanned:"
1283 " %s/%s\n", cwd_fname, fname);
1284 goto virusfilter_vfs_open_next;
1287 if (!config->scan_on_open) {
1288 DBG_INFO("Not scanned: scan on open is disabled: %s/%s\n",
1289 cwd_fname, fname);
1290 goto virusfilter_vfs_open_next;
1293 if (how->flags & O_TRUNC) {
1294 DBG_INFO("Not scanned: Open flags have O_TRUNC: %s/%s\n",
1295 cwd_fname, fname);
1296 goto virusfilter_vfs_open_next;
1299 ret = SMB_VFS_NEXT_FSTAT(handle, fsp, &sbuf);
1300 if (ret != 0) {
1303 * Do not return immediately if !(flags & O_CREAT) &&
1304 * errno != ENOENT.
1305 * Do not do this here or anywhere else. The module is
1306 * stackable and there may be modules below, such as audit
1307 * modules, which should be handled.
1309 goto virusfilter_vfs_open_next;
1311 ret = S_ISREG(sbuf.st_ex_mode);
1312 if (ret == 0) {
1313 DBG_INFO("Not scanned: Directory or special file: %s/%s\n",
1314 cwd_fname, fname);
1315 goto virusfilter_vfs_open_next;
1317 if (config->max_file_size > 0 &&
1318 sbuf.st_ex_size > config->max_file_size)
1320 DBG_INFO("Not scanned: file size > max file size: %s/%s\n",
1321 cwd_fname, fname);
1322 goto virusfilter_vfs_open_next;
1324 if (config->min_file_size > 0 &&
1325 sbuf.st_ex_size < config->min_file_size)
1327 DBG_INFO("Not scanned: file size < min file size: %s/%s\n",
1328 cwd_fname, fname);
1329 goto virusfilter_vfs_open_next;
1332 ok1 = is_in_path(fname, config->exclude_files, false);
1333 if (config->exclude_files && ok1)
1335 DBG_INFO("Not scanned: exclude files: %s/%s\n",
1336 cwd_fname, fname);
1337 goto virusfilter_vfs_open_next;
1340 if (config->infected_file_action == VIRUSFILTER_ACTION_QUARANTINE) {
1341 sret = strstr_m(fname, config->quarantine_dir);
1342 if (sret != NULL) {
1343 scan_errno = config->infected_open_errno;
1344 goto virusfilter_vfs_open_fail;
1348 if (test_prefix > 0 || test_suffix > 0) {
1349 ok1 = parent_dirname(mem_ctx, fname, &dir_name, &base_name);
1350 if (ok1)
1352 if (test_prefix > 0) {
1353 ret = strncmp(base_name,
1354 config->rename_prefix, test_prefix);
1355 if (ret != 0) {
1356 test_prefix = 0;
1359 if (test_suffix > 0) {
1360 ret = strcmp(base_name + (strlen(base_name)
1361 - test_suffix),
1362 config->rename_suffix);
1363 if (ret != 0) {
1364 test_suffix = 0;
1368 TALLOC_FREE(dir_name);
1370 if ((rename_trap_count == 2 && test_prefix &&
1371 test_suffix) || (rename_trap_count == 1 &&
1372 (test_prefix || test_suffix)))
1374 scan_errno =
1375 config->infected_open_errno;
1376 goto virusfilter_vfs_open_fail;
1381 scan_result = virusfilter_scan(handle, config, fsp);
1383 switch (scan_result) {
1384 case VIRUSFILTER_RESULT_CLEAN:
1385 break;
1386 case VIRUSFILTER_RESULT_INFECTED:
1387 scan_errno = config->infected_open_errno;
1388 goto virusfilter_vfs_open_fail;
1389 case VIRUSFILTER_RESULT_ERROR:
1390 if (config->block_access_on_error) {
1391 DBG_INFO("Block access\n");
1392 scan_errno = config->scan_error_open_errno;
1393 goto virusfilter_vfs_open_fail;
1395 break;
1396 default:
1397 scan_errno = config->scan_error_open_errno;
1398 goto virusfilter_vfs_open_fail;
1401 TALLOC_FREE(smb_fname);
1403 virusfilter_vfs_open_next:
1404 return SMB_VFS_NEXT_OPENAT(handle, dirfsp, smb_fname_in, fsp, how);
1406 virusfilter_vfs_open_fail:
1407 TALLOC_FREE(smb_fname);
1408 errno = (scan_errno != 0) ? scan_errno : EACCES;
1409 return -1;
1412 static int virusfilter_vfs_close(
1413 struct vfs_handle_struct *handle,
1414 files_struct *fsp)
1417 * The name of this variable is for consistency. If API changes to
1418 * match _open change to cwd_fname as in virusfilter_vfs_open.
1420 const char *cwd_fname = handle->conn->connectpath;
1422 struct virusfilter_config *config = NULL;
1423 char *fname = fsp->fsp_name->base_name;
1424 int close_result = -1;
1425 int close_errno = 0;
1426 virusfilter_result scan_result;
1427 int scan_errno = 0;
1429 SMB_VFS_HANDLE_GET_DATA(handle, config,
1430 struct virusfilter_config, return -1);
1433 * Must close after scan? It appears not as the scanners are not
1434 * internal and other modules such as greyhole seem to do
1435 * SMB_VFS_NEXT_* functions before processing.
1437 close_result = SMB_VFS_NEXT_CLOSE(handle, fsp);
1438 if (close_result == -1) {
1439 close_errno = errno;
1443 * Return immediately if close_result == -1, and close_errno == EBADF.
1444 * If close failed, file likely doesn't exist, do not try to scan.
1446 if (close_result == -1 && close_errno == EBADF) {
1447 if (fsp->fsp_flags.modified) {
1448 DBG_DEBUG("Removing cache entry (if existent): "
1449 "fname: %s\n", fname);
1450 virusfilter_cache_remove(config->cache,
1451 cwd_fname, fname);
1453 goto virusfilter_vfs_close_fail;
1456 if (fsp->fsp_flags.is_directory) {
1457 DBG_INFO("Not scanned: Directory: %s/\n", cwd_fname);
1458 return close_result;
1461 if (fsp_is_alternate_stream(fsp)) {
1462 if (config->scan_on_open && fsp->fsp_flags.modified) {
1463 if (config->cache) {
1464 DBG_DEBUG("Removing cache entry (if existent)"
1465 ": fname: %s\n", fname);
1466 virusfilter_cache_remove(
1467 config->cache,
1468 cwd_fname, fname);
1471 DBG_INFO("Not scanned: only file backed streams can be scanned:"
1472 " %s/%s\n", cwd_fname, fname);
1473 return close_result;
1476 if (!config->scan_on_close) {
1477 if (config->scan_on_open && fsp->fsp_flags.modified) {
1478 if (config->cache) {
1479 DBG_DEBUG("Removing cache entry (if existent)"
1480 ": fname: %s\n", fname);
1481 virusfilter_cache_remove(
1482 config->cache,
1483 cwd_fname, fname);
1486 DBG_INFO("Not scanned: scan on close is disabled: %s/%s\n",
1487 cwd_fname, fname);
1488 return close_result;
1491 if (!fsp->fsp_flags.modified) {
1492 DBG_NOTICE("Not scanned: File not modified: %s/%s\n",
1493 cwd_fname, fname);
1495 return close_result;
1498 if (is_in_path(fname, config->exclude_files, false)) {
1499 DBG_INFO("Not scanned: exclude files: %s/%s\n",
1500 cwd_fname, fname);
1501 return close_result;
1504 scan_result = virusfilter_scan(handle, config, fsp);
1506 switch (scan_result) {
1507 case VIRUSFILTER_RESULT_CLEAN:
1508 break;
1509 case VIRUSFILTER_RESULT_INFECTED:
1510 scan_errno = config->infected_close_errno;
1511 goto virusfilter_vfs_close_fail;
1512 case VIRUSFILTER_RESULT_ERROR:
1513 if (config->block_access_on_error) {
1514 DBG_INFO("Block access\n");
1515 scan_errno = config->scan_error_close_errno;
1516 goto virusfilter_vfs_close_fail;
1518 break;
1519 default:
1520 scan_errno = config->scan_error_close_errno;
1521 goto virusfilter_vfs_close_fail;
1524 if (close_errno != 0) {
1525 errno = close_errno;
1528 return close_result;
1530 virusfilter_vfs_close_fail:
1532 errno = (scan_errno != 0) ? scan_errno : close_errno;
1534 return close_result;
1537 static int virusfilter_vfs_unlinkat(struct vfs_handle_struct *handle,
1538 struct files_struct *dirfsp,
1539 const struct smb_filename *smb_fname,
1540 int flags)
1542 int ret = SMB_VFS_NEXT_UNLINKAT(handle,
1543 dirfsp,
1544 smb_fname,
1545 flags);
1546 struct virusfilter_config *config = NULL;
1547 struct smb_filename *full_fname = NULL;
1548 char *fname = NULL;
1549 char *cwd_fname = dirfsp->fsp_name->base_name;
1551 if (ret != 0 && errno != ENOENT) {
1552 return ret;
1555 SMB_VFS_HANDLE_GET_DATA(handle, config,
1556 struct virusfilter_config, return -1);
1558 if (config->cache == NULL) {
1559 return 0;
1562 full_fname = full_path_from_dirfsp_atname(talloc_tos(),
1563 dirfsp,
1564 smb_fname);
1565 if (full_fname == NULL) {
1566 return -1;
1569 fname = full_fname->base_name;
1571 DBG_DEBUG("Removing cache entry (if existent): fname: %s\n", fname);
1572 virusfilter_cache_remove(config->cache, cwd_fname, fname);
1574 TALLOC_FREE(full_fname);
1575 return 0;
1578 static int virusfilter_vfs_renameat(
1579 struct vfs_handle_struct *handle,
1580 files_struct *srcfsp,
1581 const struct smb_filename *smb_fname_src,
1582 files_struct *dstfsp,
1583 const struct smb_filename *smb_fname_dst)
1585 int ret = SMB_VFS_NEXT_RENAMEAT(handle,
1586 srcfsp,
1587 smb_fname_src,
1588 dstfsp,
1589 smb_fname_dst);
1590 struct virusfilter_config *config = NULL;
1591 char *fname = NULL;
1592 char *dst_fname = NULL;
1593 char *cwd_fname = handle->conn->cwd_fsp->fsp_name->base_name;
1594 struct smb_filename *full_src = NULL;
1595 struct smb_filename *full_dst = NULL;
1597 if (ret != 0) {
1598 return ret;
1601 SMB_VFS_HANDLE_GET_DATA(handle, config,
1602 struct virusfilter_config, return -1);
1604 if (config->cache == NULL) {
1605 return 0;
1608 full_src = full_path_from_dirfsp_atname(talloc_tos(),
1609 srcfsp,
1610 smb_fname_src);
1611 if (full_src == NULL) {
1612 errno = ENOMEM;
1613 ret = -1;
1614 goto out;
1617 full_dst = full_path_from_dirfsp_atname(talloc_tos(),
1618 dstfsp,
1619 smb_fname_dst);
1620 if (full_dst == NULL) {
1621 errno = ENOMEM;
1622 ret = -1;
1623 goto out;
1626 fname = full_src->base_name;
1627 dst_fname = full_dst->base_name;
1629 DBG_DEBUG("Renaming cache entry: fname: %s to: %s\n",
1630 fname, dst_fname);
1631 virusfilter_cache_entry_rename(config->cache,
1632 cwd_fname,
1633 fname,
1634 dst_fname);
1636 ret = 0;
1637 out:
1638 TALLOC_FREE(full_src);
1639 TALLOC_FREE(full_dst);
1640 return ret;
1644 /* VFS operations */
1645 static struct vfs_fn_pointers vfs_virusfilter_fns = {
1646 .connect_fn = virusfilter_vfs_connect,
1647 .disconnect_fn = virusfilter_vfs_disconnect,
1648 .openat_fn = virusfilter_vfs_openat,
1649 .close_fn = virusfilter_vfs_close,
1650 .unlinkat_fn = virusfilter_vfs_unlinkat,
1651 .renameat_fn = virusfilter_vfs_renameat,
1654 NTSTATUS vfs_virusfilter_init(TALLOC_CTX *);
1655 NTSTATUS vfs_virusfilter_init(TALLOC_CTX *ctx)
1657 NTSTATUS status;
1659 status = smb_register_vfs(SMB_VFS_INTERFACE_VERSION,
1660 "virusfilter",
1661 &vfs_virusfilter_fns);
1662 if (!NT_STATUS_IS_OK(status)) {
1663 return status;
1666 virusfilter_debug_class = debug_add_class("virusfilter");
1667 if (virusfilter_debug_class == -1) {
1668 virusfilter_debug_class = DBGC_VFS;
1669 DBG_ERR("Couldn't register custom debugging class!\n");
1670 } else {
1671 DBG_DEBUG("Debug class number: %d\n", virusfilter_debug_class);
1674 DBG_INFO("registered\n");
1676 return status;