smbd: avoid a panic in close_directory()
[Samba.git] / source3 / modules / vfs_virusfilter.c
blobc0cf9ff78db598d5dcf04e4281e98f291c2aacd4
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;
220 bool ok;
222 config = talloc_zero(handle, struct virusfilter_config);
223 if (config == NULL) {
224 DBG_ERR("talloc_zero failed\n");
225 return -1;
227 talloc_set_destructor(config, virusfilter_config_destructor);
229 SMB_VFS_HANDLE_SET_DATA(handle, config, NULL,
230 struct virusfilter_config, return -1);
232 config->scan_request_limit = lp_parm_int(
233 snum, "virusfilter", "scan request limit", 0);
235 config->scan_on_open = lp_parm_bool(
236 snum, "virusfilter", "scan on open", true);
238 config->scan_on_close = lp_parm_bool(
239 snum, "virusfilter", "scan on close", false);
241 config->max_nested_scan_archive = lp_parm_int(
242 snum, "virusfilter", "max nested scan archive", 1);
244 config->scan_archive = lp_parm_bool(
245 snum, "virusfilter", "scan archive", false);
247 config->scan_mime = lp_parm_bool(
248 snum, "virusfilter", "scan mime", false);
250 config->max_file_size = (ssize_t)lp_parm_ulong(
251 snum, "virusfilter", "max file size", 100000000L);
253 config->min_file_size = (ssize_t)lp_parm_ulong(
254 snum, "virusfilter", "min file size", 10);
256 exclude_files = lp_parm_const_string(
257 snum, "virusfilter", "exclude files", NULL);
258 if (exclude_files != NULL) {
259 ok = set_namearray(config,
260 exclude_files,
261 &config->exclude_files);
262 if (!ok) {
263 DBG_ERR("set_namearray failed\n");
264 return -1;
268 infected_files = lp_parm_const_string(
269 snum, "virusfilter", "infected files", NULL);
270 if (infected_files != NULL) {
271 ok = set_namearray(config,
272 infected_files,
273 &config->infected_files);
274 if (!ok) {
275 DBG_ERR("set_namearray failed\n");
276 return -1;
280 config->cache_entry_limit = lp_parm_int(
281 snum, "virusfilter", "cache entry limit", 100);
283 config->cache_time_limit = lp_parm_int(
284 snum, "virusfilter", "cache time limit", 10);
286 config->infected_file_action = lp_parm_enum(
287 snum, "virusfilter", "infected file action",
288 virusfilter_actions, VIRUSFILTER_ACTION_DO_NOTHING);
290 infected_file_command = lp_parm_const_string(
291 snum, "virusfilter", "infected file command", NULL);
292 if (infected_file_command != NULL) {
293 config->infected_file_command = talloc_strdup(
294 config,
295 infected_file_command);
296 if (config->infected_file_command == NULL) {
297 DBG_ERR("virusfilter-vfs: out of memory!\n");
298 return -1;
301 scan_error_command = lp_parm_const_string(
302 snum, "virusfilter", "scan error command", NULL);
303 if (scan_error_command != NULL) {
304 config->scan_error_command = talloc_strdup(config,
305 scan_error_command);
306 if (config->scan_error_command == NULL) {
307 DBG_ERR("virusfilter-vfs: out of memory!\n");
308 return -1;
312 config->block_access_on_error = lp_parm_bool(
313 snum, "virusfilter", "block access on error", false);
315 tmp = talloc_asprintf(config, "%s/.quarantine",
316 handle->conn->connectpath);
318 quarantine_dir = lp_parm_const_string(
319 snum, "virusfilter", "quarantine directory",
320 tmp ? tmp : "/tmp/.quarantine");
321 if (quarantine_dir != NULL) {
322 config->quarantine_dir = talloc_strdup(config, quarantine_dir);
323 if (config->quarantine_dir == NULL) {
324 DBG_ERR("virusfilter-vfs: out of memory!\n");
325 return -1;
329 if (tmp != config->quarantine_dir) {
330 TALLOC_FREE(tmp);
333 temp_quarantine_dir_mode = lp_parm_const_string(
334 snum, "virusfilter", "quarantine directory mode", "0755");
335 if (temp_quarantine_dir_mode != NULL) {
336 unsigned int mode = 0;
337 sscanf(temp_quarantine_dir_mode, "%o", &mode);
338 config->quarantine_dir_mode = mode;
341 quarantine_prefix = lp_parm_const_string(
342 snum, "virusfilter", "quarantine prefix",
343 VIRUSFILTER_DEFAULT_QUARANTINE_PREFIX);
344 if (quarantine_prefix != NULL) {
345 config->quarantine_prefix = talloc_strdup(config,
346 quarantine_prefix);
347 if (config->quarantine_prefix == NULL) {
348 DBG_ERR("virusfilter-vfs: out of memory!\n");
349 return -1;
353 quarantine_suffix = lp_parm_const_string(
354 snum, "virusfilter", "quarantine suffix",
355 VIRUSFILTER_DEFAULT_QUARANTINE_SUFFIX);
356 if (quarantine_suffix != NULL) {
357 config->quarantine_suffix = talloc_strdup(config,
358 quarantine_suffix);
359 if (config->quarantine_suffix == NULL) {
360 DBG_ERR("virusfilter-vfs: out of memory!\n");
361 return -1;
366 * Make sure prefixes and suffixes do not contain directory
367 * delimiters
369 if (config->quarantine_prefix != NULL) {
370 sret = strstr(config->quarantine_prefix, "/");
371 if (sret != NULL) {
372 DBG_ERR("quarantine prefix must not contain directory "
373 "delimiter(s) such as '/' (%s replaced with %s)\n",
374 config->quarantine_prefix,
375 VIRUSFILTER_DEFAULT_QUARANTINE_PREFIX);
376 config->quarantine_prefix =
377 VIRUSFILTER_DEFAULT_QUARANTINE_PREFIX;
380 if (config->quarantine_suffix != NULL) {
381 sret = strstr(config->quarantine_suffix, "/");
382 if (sret != NULL) {
383 DBG_ERR("quarantine suffix must not contain directory "
384 "delimiter(s) such as '/' (%s replaced with %s)\n",
385 config->quarantine_suffix,
386 VIRUSFILTER_DEFAULT_QUARANTINE_SUFFIX);
387 config->quarantine_suffix =
388 VIRUSFILTER_DEFAULT_QUARANTINE_SUFFIX;
392 config->quarantine_keep_tree = lp_parm_bool(
393 snum, "virusfilter", "quarantine keep tree", true);
395 config->quarantine_keep_name = lp_parm_bool(
396 snum, "virusfilter", "quarantine keep name", true);
398 rename_prefix = lp_parm_const_string(
399 snum, "virusfilter", "rename prefix",
400 VIRUSFILTER_DEFAULT_RENAME_PREFIX);
401 if (rename_prefix != NULL) {
402 config->rename_prefix = talloc_strdup(config, rename_prefix);
403 if (config->rename_prefix == NULL) {
404 DBG_ERR("virusfilter-vfs: out of memory!\n");
405 return -1;
409 rename_suffix = lp_parm_const_string(
410 snum, "virusfilter", "rename suffix",
411 VIRUSFILTER_DEFAULT_RENAME_SUFFIX);
412 if (rename_suffix != NULL) {
413 config->rename_suffix = talloc_strdup(config, rename_suffix);
414 if (config->rename_suffix == NULL) {
415 DBG_ERR("virusfilter-vfs: out of memory!\n");
416 return -1;
421 * Make sure prefixes and suffixes do not contain directory
422 * delimiters
424 if (config->rename_prefix != NULL) {
425 sret = strstr(config->rename_prefix, "/");
426 if (sret != NULL) {
427 DBG_ERR("rename prefix must not contain directory "
428 "delimiter(s) such as '/' (%s replaced with %s)\n",
429 config->rename_prefix,
430 VIRUSFILTER_DEFAULT_RENAME_PREFIX);
431 config->rename_prefix =
432 VIRUSFILTER_DEFAULT_RENAME_PREFIX;
435 if (config->rename_suffix != NULL) {
436 sret = strstr(config->rename_suffix, "/");
437 if (sret != NULL) {
438 DBG_ERR("rename suffix must not contain directory "
439 "delimiter(s) such as '/' (%s replaced with %s)\n",
440 config->rename_suffix,
441 VIRUSFILTER_DEFAULT_RENAME_SUFFIX);
442 config->rename_suffix =
443 VIRUSFILTER_DEFAULT_RENAME_SUFFIX;
447 config->infected_open_errno = lp_parm_int(
448 snum, "virusfilter", "infected file errno on open", EACCES);
450 config->infected_close_errno = lp_parm_int(
451 snum, "virusfilter", "infected file errno on close", 0);
453 config->scan_error_open_errno = lp_parm_int(
454 snum, "virusfilter", "scan error errno on open", EACCES);
456 config->scan_error_close_errno = lp_parm_int(
457 snum, "virusfilter", "scan error errno on close", 0);
459 socket_path = lp_parm_const_string(
460 snum, "virusfilter", "socket path", NULL);
461 if (socket_path != NULL) {
462 config->socket_path = talloc_strdup(config, socket_path);
463 if (config->socket_path == NULL) {
464 DBG_ERR("virusfilter-vfs: out of memory!\n");
465 return -1;
469 /* canonicalize socket_path */
470 if (config->socket_path != NULL && config->socket_path[0] != '/') {
471 DBG_ERR("socket path must be an absolute path. "
472 "Using backend default\n");
473 config->socket_path = NULL;
475 if (config->socket_path != NULL) {
476 config->socket_path = canonicalize_absolute_path(
477 handle, config->socket_path);
478 if (config->socket_path == NULL) {
479 errno = ENOMEM;
480 return -1;
484 connect_timeout = lp_parm_int(snum, "virusfilter",
485 "connect timeout", 30000);
487 io_timeout = lp_parm_int(snum, "virusfilter", "io timeout", 60000);
489 config->io_h = virusfilter_io_new(config, connect_timeout, io_timeout);
490 if (config->io_h == NULL) {
491 DBG_ERR("virusfilter_io_new failed\n");
492 return -1;
495 if (config->cache_entry_limit > 0) {
496 config->cache = virusfilter_cache_new(handle,
497 config->cache_entry_limit,
498 config->cache_time_limit);
499 if (config->cache == NULL) {
500 DBG_ERR("Initializing cache failed: Cache disabled\n");
501 return -1;
506 * Check quarantine directory now to save processing
507 * and becoming root over and over.
509 if (config->infected_file_action == VIRUSFILTER_ACTION_QUARANTINE) {
510 bool dir_exists;
511 ok = true;
514 * Do SMB_VFS_NEXT_MKDIR(config->quarantine_dir)
515 * hierarchy
517 become_root();
518 dir_exists = quarantine_directory_exist(handle,
519 config->quarantine_dir);
520 if (!dir_exists) {
521 DBG_DEBUG("Creating quarantine directory: %s\n",
522 config->quarantine_dir);
523 ok = quarantine_create_dir(handle, config,
524 config->quarantine_dir);
526 unbecome_root();
527 if (!ok) {
528 DBG_ERR("Creating quarantine directory %s "
529 "failed with %s\n",
530 config->quarantine_dir,
531 strerror(errno));
532 return -1;
537 * Now that the frontend options are initialized, load the configured
538 * backend.
541 backend = (enum virusfilter_scanner_enum)lp_parm_enum(snum,
542 "virusfilter",
543 "scanner",
544 scanner_list,
545 -1);
546 if (backend == (enum virusfilter_scanner_enum)-1) {
547 DBG_ERR("No AV-Scanner configured, "
548 "please set \"virusfilter:scanner\"\n");
549 return -1;
552 switch (backend) {
553 case VIRUSFILTER_SCANNER_SOPHOS:
554 ret = virusfilter_sophos_init(config);
555 break;
556 case VIRUSFILTER_SCANNER_FSAV:
557 ret = virusfilter_fsav_init(config);
558 break;
559 case VIRUSFILTER_SCANNER_CLAMAV:
560 ret = virusfilter_clamav_init(config);
561 break;
562 case VIRUSFILTER_SCANNER_DUMMY:
563 ret = virusfilter_dummy_init(config);
564 break;
565 default:
566 DBG_ERR("Unhandled scanner %d\n", backend);
567 return -1;
569 if (ret != 0) {
570 DBG_ERR("Scanner backend init failed\n");
571 return -1;
574 if (config->backend->fns->connect != NULL) {
575 ret = config->backend->fns->connect(handle, config, svc, user);
576 if (ret == -1) {
577 return -1;
581 return SMB_VFS_NEXT_CONNECT(handle, svc, user);
584 static void virusfilter_vfs_disconnect(struct vfs_handle_struct *handle)
586 struct virusfilter_config *config = NULL;
588 SMB_VFS_HANDLE_GET_DATA(handle, config,
589 struct virusfilter_config, return);
591 if (config->backend->fns->disconnect != NULL) {
592 config->backend->fns->disconnect(handle);
595 virusfilter_io_disconnect(config->io_h);
597 SMB_VFS_NEXT_DISCONNECT(handle);
600 static int virusfilter_set_module_env(TALLOC_CTX *mem_ctx,
601 struct virusfilter_config *config,
602 char **env_list)
604 int ret;
606 ret = virusfilter_env_set(mem_ctx, env_list, "VIRUSFILTER_VERSION",
607 VIRUSFILTER_VERSION);
608 if (ret == -1) {
609 return -1;
611 ret = virusfilter_env_set(mem_ctx, env_list, "VIRUSFILTER_MODULE_NAME",
612 config->backend->name);
613 if (ret == -1) {
614 return -1;
617 if (config->backend->version != 0) {
618 char *version = NULL;
620 version = talloc_asprintf(talloc_tos(), "%u",
621 config->backend->version);
622 if (version == NULL) {
623 return -1;
625 ret = virusfilter_env_set(mem_ctx, env_list,
626 "VIRUSFILTER_MODULE_VERSION",
627 version);
628 TALLOC_FREE(version);
629 if (ret == -1) {
630 return -1;
634 return 0;
637 static char *quarantine_check_tree(TALLOC_CTX *mem_ctx,
638 struct vfs_handle_struct *handle,
639 struct virusfilter_config *config,
640 const struct smb_filename *smb_fname,
641 char *q_dir_in,
642 char *cwd_fname)
644 char *temp_path = NULL;
645 char *q_dir_out = NULL;
646 bool ok;
648 temp_path = talloc_asprintf(talloc_tos(), "%s/%s", q_dir_in, cwd_fname);
649 if (temp_path == NULL) {
650 DBG_ERR("talloc_asprintf failed\n");
651 goto out;
654 become_root();
655 ok = quarantine_directory_exist(handle, temp_path);
656 unbecome_root();
657 if (ok) {
658 DBG_DEBUG("quarantine: directory [%s] exists\n", temp_path);
659 q_dir_out = talloc_move(mem_ctx, &temp_path);
660 goto out;
663 DBG_DEBUG("quarantine: Creating directory %s\n", temp_path);
665 become_root();
666 ok = quarantine_create_dir(handle, config, temp_path);
667 unbecome_root();
668 if (!ok) {
669 DBG_NOTICE("Could not create quarantine directory [%s], "
670 "ignoring for [%s]\n",
671 temp_path, smb_fname_str_dbg(smb_fname));
672 goto out;
675 q_dir_out = talloc_move(mem_ctx, &temp_path);
677 out:
678 TALLOC_FREE(temp_path);
679 return q_dir_out;
682 static virusfilter_action infected_file_action_quarantine(
683 struct vfs_handle_struct *handle,
684 struct virusfilter_config *config,
685 TALLOC_CTX *mem_ctx,
686 const struct files_struct *fsp,
687 const char **filepath_newp)
689 TALLOC_CTX *frame = talloc_stackframe();
690 connection_struct *conn = handle->conn;
691 char *cwd_fname = fsp->conn->cwd_fsp->fsp_name->base_name;
692 char *fname = fsp->fsp_name->base_name;
693 const struct smb_filename *smb_fname = fsp->fsp_name;
694 struct smb_filename *q_smb_fname = NULL;
695 char *q_dir = NULL;
696 char *q_prefix = NULL;
697 char *q_suffix = NULL;
698 char *q_filepath = NULL;
699 char *dir_name = NULL;
700 const char *base_name = NULL;
701 char *rand_filename_component = NULL;
702 virusfilter_action action = VIRUSFILTER_ACTION_QUARANTINE;
703 bool ok = false;
704 int ret = -1;
705 int saved_errno = 0;
707 q_dir = virusfilter_string_sub(frame, conn,
708 config->quarantine_dir);
709 q_prefix = virusfilter_string_sub(frame, conn,
710 config->quarantine_prefix);
711 q_suffix = virusfilter_string_sub(frame, conn,
712 config->quarantine_suffix);
713 if (q_dir == NULL || q_prefix == NULL || q_suffix == NULL) {
714 DBG_ERR("Quarantine failed: %s/%s: Cannot allocate "
715 "memory\n", cwd_fname, fname);
716 action = VIRUSFILTER_ACTION_DO_NOTHING;
717 goto out;
720 if (config->quarantine_keep_name || config->quarantine_keep_tree) {
721 ok = parent_dirname(frame, smb_fname->base_name,
722 &dir_name, &base_name);
723 if (!ok) {
724 DBG_ERR("parent_dirname failed\n");
725 action = VIRUSFILTER_ACTION_DO_NOTHING;
726 goto out;
729 if (config->quarantine_keep_tree) {
730 char *tree = NULL;
732 tree = quarantine_check_tree(frame, handle, config,
733 smb_fname, q_dir,
734 cwd_fname);
735 if (tree == NULL) {
737 * If we can't create the tree, just move it
738 * into the toplevel quarantine dir.
740 tree = q_dir;
742 q_dir = tree;
746 /* Get a 16 byte + \0 random filename component. */
747 rand_filename_component = generate_random_str(frame, 16);
748 if (rand_filename_component == NULL) {
749 DBG_ERR("generate_random_str failed\n");
750 action = VIRUSFILTER_ACTION_DO_NOTHING;
751 goto out;
754 if (config->quarantine_keep_name) {
755 q_filepath = talloc_asprintf(frame, "%s/%s%s%s-%s",
756 q_dir, q_prefix,
757 base_name, q_suffix,
758 rand_filename_component);
759 } else {
760 q_filepath = talloc_asprintf(frame, "%s/%s%s",
761 q_dir, q_prefix,
762 rand_filename_component);
764 if (q_filepath == NULL) {
765 DBG_ERR("talloc_asprintf failed\n");
766 action = VIRUSFILTER_ACTION_DO_NOTHING;
767 goto out;
770 q_smb_fname = synthetic_smb_fname(frame,
771 q_filepath,
772 smb_fname->stream_name,
773 NULL,
775 smb_fname->flags);
776 if (q_smb_fname == NULL) {
777 action = VIRUSFILTER_ACTION_DO_NOTHING;
778 goto out;
781 become_root();
782 ret = virusfilter_vfs_next_move(handle, smb_fname, q_smb_fname);
783 if (ret == -1) {
784 saved_errno = errno;
786 unbecome_root();
787 if (ret == -1) {
788 DBG_ERR("Quarantine [%s/%s] rename to %s failed: %s\n",
789 cwd_fname, fname, q_filepath, strerror(saved_errno));
790 errno = saved_errno;
791 action = VIRUSFILTER_ACTION_DO_NOTHING;
792 goto out;
795 *filepath_newp = talloc_move(mem_ctx, &q_filepath);
797 out:
798 TALLOC_FREE(frame);
799 return action;
802 static virusfilter_action infected_file_action_rename(
803 struct vfs_handle_struct *handle,
804 struct virusfilter_config *config,
805 TALLOC_CTX *mem_ctx,
806 const struct files_struct *fsp,
807 const char **filepath_newp)
809 TALLOC_CTX *frame = talloc_stackframe();
810 connection_struct *conn = handle->conn;
811 char *cwd_fname = fsp->conn->cwd_fsp->fsp_name->base_name;
812 char *fname = fsp->fsp_name->base_name;
813 const struct smb_filename *smb_fname = fsp->fsp_name;
814 struct smb_filename *q_smb_fname = NULL;
815 char *q_dir = NULL;
816 char *q_prefix = NULL;
817 char *q_suffix = NULL;
818 char *q_filepath = NULL;
819 const char *base_name = NULL;
820 virusfilter_action action = VIRUSFILTER_ACTION_RENAME;
821 bool ok = false;
822 int ret = -1;
823 int saved_errno = 0;
825 q_prefix = virusfilter_string_sub(frame, conn,
826 config->rename_prefix);
827 q_suffix = virusfilter_string_sub(frame, conn,
828 config->rename_suffix);
829 if (q_prefix == NULL || q_suffix == NULL) {
830 DBG_ERR("Rename failed: %s/%s: Cannot allocate "
831 "memory\n", cwd_fname, fname);
832 action = VIRUSFILTER_ACTION_DO_NOTHING;
833 goto out;
836 ok = parent_dirname(frame, fname, &q_dir, &base_name);
837 if (!ok) {
838 DBG_ERR("Rename failed: %s/%s: Cannot allocate "
839 "memory\n", cwd_fname, fname);
840 action = VIRUSFILTER_ACTION_DO_NOTHING;
841 goto out;
844 if (q_dir == NULL) {
845 DBG_ERR("Rename failed: %s/%s: Cannot allocate "
846 "memory\n", cwd_fname, fname);
847 action = VIRUSFILTER_ACTION_DO_NOTHING;
848 goto out;
851 q_filepath = talloc_asprintf(frame, "%s/%s%s%s", q_dir,
852 q_prefix, base_name, q_suffix);
854 q_smb_fname = synthetic_smb_fname(frame, q_filepath,
855 smb_fname->stream_name, NULL,
857 smb_fname->flags);
858 if (q_smb_fname == NULL) {
859 action = VIRUSFILTER_ACTION_DO_NOTHING;
860 goto out;
863 become_root();
864 ret = virusfilter_vfs_next_move(handle, smb_fname, q_smb_fname);
865 if (ret == -1) {
866 saved_errno = errno;
868 unbecome_root();
870 if (ret == -1) {
871 DBG_ERR("Rename failed: %s/%s: Rename failed: %s\n",
872 cwd_fname, fname, strerror(saved_errno));
873 errno = saved_errno;
874 action = VIRUSFILTER_ACTION_DO_NOTHING;
875 goto out;
878 *filepath_newp = talloc_move(mem_ctx, &q_filepath);
880 out:
881 TALLOC_FREE(frame);
882 return action;
885 static virusfilter_action infected_file_action_delete(
886 struct vfs_handle_struct *handle,
887 const struct files_struct *fsp)
889 int ret;
890 int saved_errno = 0;
892 become_root();
893 ret = SMB_VFS_NEXT_UNLINKAT(handle,
894 handle->conn->cwd_fsp,
895 fsp->fsp_name,
897 if (ret == -1) {
898 saved_errno = errno;
900 unbecome_root();
901 if (ret == -1) {
902 DBG_ERR("Delete [%s/%s] failed: %s\n",
903 fsp->conn->cwd_fsp->fsp_name->base_name,
904 fsp->fsp_name->base_name,
905 strerror(saved_errno));
906 errno = saved_errno;
907 return VIRUSFILTER_ACTION_DO_NOTHING;
910 return VIRUSFILTER_ACTION_DELETE;
913 static virusfilter_action virusfilter_do_infected_file_action(
914 struct vfs_handle_struct *handle,
915 struct virusfilter_config *config,
916 TALLOC_CTX *mem_ctx,
917 const struct files_struct *fsp,
918 const char **filepath_newp)
920 virusfilter_action action;
922 *filepath_newp = NULL;
924 switch (config->infected_file_action) {
925 case VIRUSFILTER_ACTION_RENAME:
926 action = infected_file_action_rename(handle, config, mem_ctx,
927 fsp, filepath_newp);
928 break;
930 case VIRUSFILTER_ACTION_QUARANTINE:
931 action = infected_file_action_quarantine(handle, config, mem_ctx,
932 fsp, filepath_newp);
933 break;
935 case VIRUSFILTER_ACTION_DELETE:
936 action = infected_file_action_delete(handle, fsp);
937 break;
939 case VIRUSFILTER_ACTION_DO_NOTHING:
940 default:
941 action = VIRUSFILTER_ACTION_DO_NOTHING;
942 break;
945 return action;
948 static virusfilter_action virusfilter_treat_infected_file(
949 struct vfs_handle_struct *handle,
950 struct virusfilter_config *config,
951 const struct files_struct *fsp,
952 const char *report,
953 bool is_cache)
955 connection_struct *conn = handle->conn;
956 char *cwd_fname = fsp->conn->cwd_fsp->fsp_name->base_name;
957 char *fname = fsp->fsp_name->base_name;
958 TALLOC_CTX *mem_ctx = talloc_tos();
959 int i;
960 virusfilter_action action;
961 const char *action_name = "UNKNOWN";
962 const char *filepath_q = NULL;
963 char *env_list = NULL;
964 char *command = NULL;
965 int command_result;
966 int ret;
968 action = virusfilter_do_infected_file_action(handle, config, mem_ctx,
969 fsp, &filepath_q);
970 for (i=0; virusfilter_actions[i].name; i++) {
971 if (virusfilter_actions[i].value == action) {
972 action_name = virusfilter_actions[i].name;
973 break;
976 DBG_WARNING("Infected file action: %s/%s: %s\n", cwd_fname,
977 fname, action_name);
979 if (!config->infected_file_command) {
980 return action;
983 ret = virusfilter_set_module_env(mem_ctx, config, &env_list);
984 if (ret == -1) {
985 goto done;
987 ret = virusfilter_env_set(mem_ctx, &env_list,
988 "VIRUSFILTER_INFECTED_SERVICE_FILE_PATH",
989 fname);
990 if (ret == -1) {
991 goto done;
993 if (report != NULL) {
994 ret = virusfilter_env_set(mem_ctx, &env_list,
995 "VIRUSFILTER_INFECTED_FILE_REPORT",
996 report);
997 if (ret == -1) {
998 goto done;
1001 ret = virusfilter_env_set(mem_ctx, &env_list,
1002 "VIRUSFILTER_INFECTED_FILE_ACTION",
1003 action_name);
1004 if (ret == -1) {
1005 goto done;
1007 if (filepath_q != NULL) {
1008 ret = virusfilter_env_set(mem_ctx, &env_list,
1009 "VIRUSFILTER_QUARANTINED_FILE_PATH",
1010 filepath_q);
1011 if (ret == -1) {
1012 goto done;
1015 if (is_cache) {
1016 ret = virusfilter_env_set(mem_ctx, &env_list,
1017 "VIRUSFILTER_RESULT_IS_CACHE", "yes");
1018 if (ret == -1) {
1019 goto done;
1023 command = virusfilter_string_sub(mem_ctx, conn,
1024 config->infected_file_command);
1025 if (command == NULL) {
1026 DBG_ERR("virusfilter_string_sub failed\n");
1027 goto done;
1030 DBG_NOTICE("Infected file command line: %s/%s: %s\n", cwd_fname,
1031 fname, command);
1033 command_result = virusfilter_shell_run(mem_ctx, command, &env_list,
1034 conn, true);
1035 if (command_result != 0) {
1036 DBG_ERR("Infected file command failed: %d\n", command_result);
1039 DBG_DEBUG("Infected file command finished: %d\n", command_result);
1041 done:
1042 TALLOC_FREE(env_list);
1043 TALLOC_FREE(command);
1045 return action;
1048 static void virusfilter_treat_scan_error(
1049 struct vfs_handle_struct *handle,
1050 struct virusfilter_config *config,
1051 const struct files_struct *fsp,
1052 const char *report,
1053 bool is_cache)
1055 connection_struct *conn = handle->conn;
1056 const char *cwd_fname = fsp->conn->cwd_fsp->fsp_name->base_name;
1057 const char *fname = fsp->fsp_name->base_name;
1058 TALLOC_CTX *mem_ctx = talloc_tos();
1059 char *env_list = NULL;
1060 char *command = NULL;
1061 int command_result;
1062 int ret;
1064 if (!config->scan_error_command) {
1065 return;
1067 ret = virusfilter_set_module_env(mem_ctx, config, &env_list);
1068 if (ret == -1) {
1069 goto done;
1071 ret = virusfilter_env_set(mem_ctx, &env_list,
1072 "VIRUSFILTER_SCAN_ERROR_SERVICE_FILE_PATH",
1073 fname);
1074 if (ret == -1) {
1075 goto done;
1077 if (report != NULL) {
1078 ret = virusfilter_env_set(mem_ctx, &env_list,
1079 "VIRUSFILTER_SCAN_ERROR_REPORT",
1080 report);
1081 if (ret == -1) {
1082 goto done;
1085 if (is_cache) {
1086 ret = virusfilter_env_set(mem_ctx, &env_list,
1087 "VIRUSFILTER_RESULT_IS_CACHE", "1");
1088 if (ret == -1) {
1089 goto done;
1093 command = virusfilter_string_sub(mem_ctx, conn,
1094 config->scan_error_command);
1095 if (command == NULL) {
1096 DBG_ERR("virusfilter_string_sub failed\n");
1097 goto done;
1100 DBG_NOTICE("Scan error command line: %s/%s: %s\n", cwd_fname,
1101 fname, command);
1103 command_result = virusfilter_shell_run(mem_ctx, command, &env_list,
1104 conn, true);
1105 if (command_result != 0) {
1106 DBG_ERR("Scan error command failed: %d\n", command_result);
1109 done:
1110 TALLOC_FREE(env_list);
1111 TALLOC_FREE(command);
1114 static virusfilter_result virusfilter_scan(
1115 struct vfs_handle_struct *handle,
1116 struct virusfilter_config *config,
1117 const struct files_struct *fsp)
1119 virusfilter_result scan_result;
1120 char *scan_report = NULL;
1121 const char *fname = fsp->fsp_name->base_name;
1122 const char *cwd_fname = fsp->conn->cwd_fsp->fsp_name->base_name;
1123 struct virusfilter_cache_entry *scan_cache_e = NULL;
1124 bool is_cache = false;
1125 virusfilter_action file_action = VIRUSFILTER_ACTION_DO_NOTHING;
1126 bool add_scan_cache = true;
1127 bool ok = false;
1129 if (config->cache) {
1130 DBG_DEBUG("Searching cache entry: fname: %s\n", fname);
1131 scan_cache_e = virusfilter_cache_get(config->cache,
1132 cwd_fname, fname);
1133 if (scan_cache_e != NULL) {
1134 DBG_DEBUG("Cache entry found: cached result: %d\n",
1135 scan_cache_e->result);
1136 is_cache = true;
1137 scan_result = scan_cache_e->result;
1138 scan_report = scan_cache_e->report;
1139 goto virusfilter_scan_result_eval;
1141 DBG_DEBUG("Cache entry not found\n");
1144 if (config->backend->fns->scan_init != NULL) {
1145 scan_result = config->backend->fns->scan_init(config);
1146 if (scan_result != VIRUSFILTER_RESULT_OK) {
1147 scan_result = VIRUSFILTER_RESULT_ERROR;
1148 scan_report = talloc_asprintf(
1149 talloc_tos(),
1150 "Initializing scanner failed");
1151 goto virusfilter_scan_result_eval;
1155 scan_result = config->backend->fns->scan(handle, config, fsp,
1156 &scan_report);
1158 if (config->backend->fns->scan_end != NULL) {
1159 bool scan_end = true;
1161 if (config->scan_request_limit > 0) {
1162 scan_end = false;
1163 config->scan_request_count++;
1164 if (config->scan_request_count >=
1165 config->scan_request_limit)
1167 scan_end = true;
1168 config->scan_request_count = 0;
1171 if (scan_end) {
1172 config->backend->fns->scan_end(config);
1176 virusfilter_scan_result_eval:
1178 switch (scan_result) {
1179 case VIRUSFILTER_RESULT_CLEAN:
1180 DBG_INFO("Scan result: Clean: %s/%s\n", cwd_fname, fname);
1181 break;
1183 case VIRUSFILTER_RESULT_INFECTED:
1184 DBG_ERR("Scan result: Infected: %s/%s: %s\n",
1185 cwd_fname, fname, scan_report ? scan_report :
1186 "infected (memory error on report)");
1187 file_action = virusfilter_treat_infected_file(handle,
1188 config, fsp, scan_report, is_cache);
1189 if (file_action != VIRUSFILTER_ACTION_DO_NOTHING) {
1190 add_scan_cache = false;
1192 break;
1194 case VIRUSFILTER_RESULT_SUSPECTED:
1195 if (!config->block_suspected_file) {
1196 break;
1198 DBG_ERR("Scan result: Suspected: %s/%s: %s\n",
1199 cwd_fname, fname, scan_report ? scan_report :
1200 "suspected infection (memory error on report)");
1201 file_action = virusfilter_treat_infected_file(handle,
1202 config, fsp, scan_report, is_cache);
1203 if (file_action != VIRUSFILTER_ACTION_DO_NOTHING) {
1204 add_scan_cache = false;
1206 break;
1208 case VIRUSFILTER_RESULT_ERROR:
1209 DBG_ERR("Scan result: Error: %s/%s: %s\n",
1210 cwd_fname, fname, scan_report ? scan_report :
1211 "error (memory error on report)");
1212 virusfilter_treat_scan_error(handle, config, fsp,
1213 scan_report, is_cache);
1214 add_scan_cache = false;
1215 break;
1217 default:
1218 DBG_ERR("Scan result: Unknown result code %d: %s/%s: %s\n",
1219 scan_result, cwd_fname, fname, scan_report ?
1220 scan_report : "Unknown (memory error on report)");
1221 virusfilter_treat_scan_error(handle, config, fsp,
1222 scan_report, is_cache);
1223 add_scan_cache = false;
1224 break;
1227 if (config->cache) {
1228 if (!is_cache && add_scan_cache) {
1229 DBG_DEBUG("Adding new cache entry: %s, %d\n", fname,
1230 scan_result);
1231 ok = virusfilter_cache_entry_add(
1232 config->cache, cwd_fname, fname,
1233 scan_result, scan_report);
1234 if (!ok) {
1235 DBG_ERR("Cannot create cache entry: "
1236 "virusfilter_cache_entry_new failed\n");
1237 goto virusfilter_scan_return;
1239 } else if (is_cache) {
1240 virusfilter_cache_entry_free(scan_cache_e);
1244 virusfilter_scan_return:
1245 return scan_result;
1248 static int virusfilter_vfs_openat(struct vfs_handle_struct *handle,
1249 const struct files_struct *dirfsp,
1250 const struct smb_filename *smb_fname_in,
1251 struct files_struct *fsp,
1252 const struct vfs_open_how *how)
1254 TALLOC_CTX *mem_ctx = talloc_tos();
1255 struct virusfilter_config *config = NULL;
1256 const char *cwd_fname = dirfsp->fsp_name->base_name;
1257 virusfilter_result scan_result;
1258 const char *fname = fsp->fsp_name->base_name;
1259 char *dir_name = NULL;
1260 const char *base_name = NULL;
1261 int scan_errno = 0;
1262 size_t test_prefix;
1263 size_t test_suffix;
1264 int rename_trap_count = 0;
1265 int ret;
1266 bool ok1;
1267 char *sret = NULL;
1268 struct smb_filename *smb_fname = NULL;
1269 SMB_STRUCT_STAT sbuf = smb_fname_in->st;
1271 SMB_VFS_HANDLE_GET_DATA(handle, config,
1272 struct virusfilter_config, return -1);
1274 if (fsp->fsp_flags.is_directory) {
1275 DBG_INFO("Not scanned: Directory: %s/\n", cwd_fname);
1276 goto virusfilter_vfs_open_next;
1279 test_prefix = strlen(config->rename_prefix);
1280 test_suffix = strlen(config->rename_suffix);
1281 if (test_prefix > 0) {
1282 rename_trap_count++;
1284 if (test_suffix > 0) {
1285 rename_trap_count++;
1288 smb_fname = cp_smb_filename(mem_ctx, smb_fname_in);
1289 if (smb_fname == NULL) {
1290 goto virusfilter_vfs_open_fail;
1293 if (is_named_stream(smb_fname)) {
1294 DBG_INFO("Not scanned: only file backed streams can be scanned:"
1295 " %s/%s\n", cwd_fname, fname);
1296 goto virusfilter_vfs_open_next;
1299 if (!config->scan_on_open) {
1300 DBG_INFO("Not scanned: scan on open is disabled: %s/%s\n",
1301 cwd_fname, fname);
1302 goto virusfilter_vfs_open_next;
1305 if (how->flags & O_TRUNC) {
1306 DBG_INFO("Not scanned: Open flags have O_TRUNC: %s/%s\n",
1307 cwd_fname, fname);
1308 goto virusfilter_vfs_open_next;
1311 ret = SMB_VFS_NEXT_FSTAT(handle, fsp, &sbuf);
1312 if (ret != 0) {
1315 * Do not return immediately if !(flags & O_CREAT) &&
1316 * errno != ENOENT.
1317 * Do not do this here or anywhere else. The module is
1318 * stackable and there may be modules below, such as audit
1319 * modules, which should be handled.
1321 goto virusfilter_vfs_open_next;
1323 ret = S_ISREG(sbuf.st_ex_mode);
1324 if (ret == 0) {
1325 DBG_INFO("Not scanned: Directory or special file: %s/%s\n",
1326 cwd_fname, fname);
1327 goto virusfilter_vfs_open_next;
1329 if (config->max_file_size > 0 &&
1330 sbuf.st_ex_size > config->max_file_size)
1332 DBG_INFO("Not scanned: file size > max file size: %s/%s\n",
1333 cwd_fname, fname);
1334 goto virusfilter_vfs_open_next;
1336 if (config->min_file_size > 0 &&
1337 sbuf.st_ex_size < config->min_file_size)
1339 DBG_INFO("Not scanned: file size < min file size: %s/%s\n",
1340 cwd_fname, fname);
1341 goto virusfilter_vfs_open_next;
1344 ok1 = is_in_path(fname, config->exclude_files, false);
1345 if (config->exclude_files && ok1)
1347 DBG_INFO("Not scanned: exclude files: %s/%s\n",
1348 cwd_fname, fname);
1349 goto virusfilter_vfs_open_next;
1352 if (config->infected_file_action == VIRUSFILTER_ACTION_QUARANTINE) {
1353 sret = strstr_m(fname, config->quarantine_dir);
1354 if (sret != NULL) {
1355 scan_errno = config->infected_open_errno;
1356 goto virusfilter_vfs_open_fail;
1360 if (test_prefix > 0 || test_suffix > 0) {
1361 ok1 = parent_dirname(mem_ctx, fname, &dir_name, &base_name);
1362 if (ok1)
1364 if (test_prefix > 0) {
1365 ret = strncmp(base_name,
1366 config->rename_prefix, test_prefix);
1367 if (ret != 0) {
1368 test_prefix = 0;
1371 if (test_suffix > 0) {
1372 ret = strcmp(base_name + (strlen(base_name)
1373 - test_suffix),
1374 config->rename_suffix);
1375 if (ret != 0) {
1376 test_suffix = 0;
1380 TALLOC_FREE(dir_name);
1382 if ((rename_trap_count == 2 && test_prefix &&
1383 test_suffix) || (rename_trap_count == 1 &&
1384 (test_prefix || test_suffix)))
1386 scan_errno =
1387 config->infected_open_errno;
1388 goto virusfilter_vfs_open_fail;
1393 scan_result = virusfilter_scan(handle, config, fsp);
1395 switch (scan_result) {
1396 case VIRUSFILTER_RESULT_CLEAN:
1397 break;
1398 case VIRUSFILTER_RESULT_INFECTED:
1399 scan_errno = config->infected_open_errno;
1400 goto virusfilter_vfs_open_fail;
1401 case VIRUSFILTER_RESULT_ERROR:
1402 if (config->block_access_on_error) {
1403 DBG_INFO("Block access\n");
1404 scan_errno = config->scan_error_open_errno;
1405 goto virusfilter_vfs_open_fail;
1407 break;
1408 default:
1409 scan_errno = config->scan_error_open_errno;
1410 goto virusfilter_vfs_open_fail;
1413 TALLOC_FREE(smb_fname);
1415 virusfilter_vfs_open_next:
1416 return SMB_VFS_NEXT_OPENAT(handle, dirfsp, smb_fname_in, fsp, how);
1418 virusfilter_vfs_open_fail:
1419 TALLOC_FREE(smb_fname);
1420 errno = (scan_errno != 0) ? scan_errno : EACCES;
1421 return -1;
1424 static int virusfilter_vfs_close(
1425 struct vfs_handle_struct *handle,
1426 files_struct *fsp)
1429 * The name of this variable is for consistency. If API changes to
1430 * match _open change to cwd_fname as in virusfilter_vfs_open.
1432 const char *cwd_fname = handle->conn->connectpath;
1434 struct virusfilter_config *config = NULL;
1435 char *fname = fsp->fsp_name->base_name;
1436 int close_result = -1;
1437 int close_errno = 0;
1438 virusfilter_result scan_result;
1439 int scan_errno = 0;
1441 SMB_VFS_HANDLE_GET_DATA(handle, config,
1442 struct virusfilter_config, return -1);
1445 * Must close after scan? It appears not as the scanners are not
1446 * internal and other modules such as greyhole seem to do
1447 * SMB_VFS_NEXT_* functions before processing.
1449 close_result = SMB_VFS_NEXT_CLOSE(handle, fsp);
1450 if (close_result == -1) {
1451 close_errno = errno;
1455 * Return immediately if close_result == -1, and close_errno == EBADF.
1456 * If close failed, file likely doesn't exist, do not try to scan.
1458 if (close_result == -1 && close_errno == EBADF) {
1459 if (fsp->fsp_flags.modified) {
1460 DBG_DEBUG("Removing cache entry (if existent): "
1461 "fname: %s\n", fname);
1462 virusfilter_cache_remove(config->cache,
1463 cwd_fname, fname);
1465 goto virusfilter_vfs_close_fail;
1468 if (fsp->fsp_flags.is_directory) {
1469 DBG_INFO("Not scanned: Directory: %s/\n", cwd_fname);
1470 return close_result;
1473 if (fsp_is_alternate_stream(fsp)) {
1474 if (config->scan_on_open && fsp->fsp_flags.modified) {
1475 if (config->cache) {
1476 DBG_DEBUG("Removing cache entry (if existent)"
1477 ": fname: %s\n", fname);
1478 virusfilter_cache_remove(
1479 config->cache,
1480 cwd_fname, fname);
1483 DBG_INFO("Not scanned: only file backed streams can be scanned:"
1484 " %s/%s\n", cwd_fname, fname);
1485 return close_result;
1488 if (!config->scan_on_close) {
1489 if (config->scan_on_open && fsp->fsp_flags.modified) {
1490 if (config->cache) {
1491 DBG_DEBUG("Removing cache entry (if existent)"
1492 ": fname: %s\n", fname);
1493 virusfilter_cache_remove(
1494 config->cache,
1495 cwd_fname, fname);
1498 DBG_INFO("Not scanned: scan on close is disabled: %s/%s\n",
1499 cwd_fname, fname);
1500 return close_result;
1503 if (!fsp->fsp_flags.modified) {
1504 DBG_NOTICE("Not scanned: File not modified: %s/%s\n",
1505 cwd_fname, fname);
1507 return close_result;
1510 if (is_in_path(fname, config->exclude_files, false)) {
1511 DBG_INFO("Not scanned: exclude files: %s/%s\n",
1512 cwd_fname, fname);
1513 return close_result;
1516 scan_result = virusfilter_scan(handle, config, fsp);
1518 switch (scan_result) {
1519 case VIRUSFILTER_RESULT_CLEAN:
1520 break;
1521 case VIRUSFILTER_RESULT_INFECTED:
1522 scan_errno = config->infected_close_errno;
1523 goto virusfilter_vfs_close_fail;
1524 case VIRUSFILTER_RESULT_ERROR:
1525 if (config->block_access_on_error) {
1526 DBG_INFO("Block access\n");
1527 scan_errno = config->scan_error_close_errno;
1528 goto virusfilter_vfs_close_fail;
1530 break;
1531 default:
1532 scan_errno = config->scan_error_close_errno;
1533 goto virusfilter_vfs_close_fail;
1536 if (close_errno != 0) {
1537 errno = close_errno;
1540 return close_result;
1542 virusfilter_vfs_close_fail:
1544 errno = (scan_errno != 0) ? scan_errno : close_errno;
1546 return close_result;
1549 static int virusfilter_vfs_unlinkat(struct vfs_handle_struct *handle,
1550 struct files_struct *dirfsp,
1551 const struct smb_filename *smb_fname,
1552 int flags)
1554 int ret = SMB_VFS_NEXT_UNLINKAT(handle,
1555 dirfsp,
1556 smb_fname,
1557 flags);
1558 struct virusfilter_config *config = NULL;
1559 struct smb_filename *full_fname = NULL;
1560 char *fname = NULL;
1561 char *cwd_fname = dirfsp->fsp_name->base_name;
1563 if (ret != 0 && errno != ENOENT) {
1564 return ret;
1567 SMB_VFS_HANDLE_GET_DATA(handle, config,
1568 struct virusfilter_config, return -1);
1570 if (config->cache == NULL) {
1571 return 0;
1574 full_fname = full_path_from_dirfsp_atname(talloc_tos(),
1575 dirfsp,
1576 smb_fname);
1577 if (full_fname == NULL) {
1578 return -1;
1581 fname = full_fname->base_name;
1583 DBG_DEBUG("Removing cache entry (if existent): fname: %s\n", fname);
1584 virusfilter_cache_remove(config->cache, cwd_fname, fname);
1586 TALLOC_FREE(full_fname);
1587 return 0;
1590 static int virusfilter_vfs_renameat(
1591 struct vfs_handle_struct *handle,
1592 files_struct *srcfsp,
1593 const struct smb_filename *smb_fname_src,
1594 files_struct *dstfsp,
1595 const struct smb_filename *smb_fname_dst,
1596 const struct vfs_rename_how *how)
1598 int ret = SMB_VFS_NEXT_RENAMEAT(handle,
1599 srcfsp,
1600 smb_fname_src,
1601 dstfsp,
1602 smb_fname_dst,
1603 how);
1604 struct virusfilter_config *config = NULL;
1605 char *fname = NULL;
1606 char *dst_fname = NULL;
1607 char *cwd_fname = handle->conn->cwd_fsp->fsp_name->base_name;
1608 struct smb_filename *full_src = NULL;
1609 struct smb_filename *full_dst = NULL;
1611 if (ret != 0) {
1612 return ret;
1615 SMB_VFS_HANDLE_GET_DATA(handle, config,
1616 struct virusfilter_config, return -1);
1618 if (config->cache == NULL) {
1619 return 0;
1622 full_src = full_path_from_dirfsp_atname(talloc_tos(),
1623 srcfsp,
1624 smb_fname_src);
1625 if (full_src == NULL) {
1626 errno = ENOMEM;
1627 ret = -1;
1628 goto out;
1631 full_dst = full_path_from_dirfsp_atname(talloc_tos(),
1632 dstfsp,
1633 smb_fname_dst);
1634 if (full_dst == NULL) {
1635 errno = ENOMEM;
1636 ret = -1;
1637 goto out;
1640 fname = full_src->base_name;
1641 dst_fname = full_dst->base_name;
1643 DBG_DEBUG("Renaming cache entry: fname: %s to: %s\n",
1644 fname, dst_fname);
1645 virusfilter_cache_entry_rename(config->cache,
1646 cwd_fname,
1647 fname,
1648 dst_fname);
1650 ret = 0;
1651 out:
1652 TALLOC_FREE(full_src);
1653 TALLOC_FREE(full_dst);
1654 return ret;
1658 /* VFS operations */
1659 static struct vfs_fn_pointers vfs_virusfilter_fns = {
1660 .connect_fn = virusfilter_vfs_connect,
1661 .disconnect_fn = virusfilter_vfs_disconnect,
1662 .openat_fn = virusfilter_vfs_openat,
1663 .close_fn = virusfilter_vfs_close,
1664 .unlinkat_fn = virusfilter_vfs_unlinkat,
1665 .renameat_fn = virusfilter_vfs_renameat,
1668 NTSTATUS vfs_virusfilter_init(TALLOC_CTX *);
1669 NTSTATUS vfs_virusfilter_init(TALLOC_CTX *ctx)
1671 NTSTATUS status;
1673 status = smb_register_vfs(SMB_VFS_INTERFACE_VERSION,
1674 "virusfilter",
1675 &vfs_virusfilter_fns);
1676 if (!NT_STATUS_IS_OK(status)) {
1677 return status;
1680 virusfilter_debug_class = debug_add_class("virusfilter");
1681 if (virusfilter_debug_class == -1) {
1682 virusfilter_debug_class = DBGC_VFS;
1683 DBG_ERR("Couldn't register custom debugging class!\n");
1684 } else {
1685 DBG_DEBUG("Debug class number: %d\n", virusfilter_debug_class);
1688 DBG_INFO("registered\n");
1690 return status;