audit_logging: Remove debug log header and JSON Authentication: prefix
[amitay/samba.git] / source3 / modules / vfs_virusfilter.c
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>
6  *
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.
11  *
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.
16  *
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/>.
19  */
20
21 #include "vfs_virusfilter_common.h"
22 #include "vfs_virusfilter_utils.h"
23
24 /*
25  * Default configuration values
26  * ======================================================================
27  */
28
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"
33
34 /* ====================================================================== */
35
36 enum virusfilter_scanner_enum {
37         VIRUSFILTER_SCANNER_CLAMAV,
38         VIRUSFILTER_SCANNER_FSAV,
39         VIRUSFILTER_SCANNER_SOPHOS
40 };
41
42 static const struct enum_list scanner_list[] = {
43         { VIRUSFILTER_SCANNER_CLAMAV,   "clamav" },
44         { VIRUSFILTER_SCANNER_FSAV,     "fsav" },
45         { VIRUSFILTER_SCANNER_SOPHOS,   "sophos" },
46         { -1,                           NULL }
47 };
48
49 static const struct enum_list virusfilter_actions[] = {
50         { VIRUSFILTER_ACTION_QUARANTINE,        "quarantine" },
51         { VIRUSFILTER_ACTION_RENAME,            "rename" },
52         { VIRUSFILTER_ACTION_DELETE,            "delete" },
53
54         /* alias for "delete" */
55         { VIRUSFILTER_ACTION_DELETE,            "remove" },
56
57         /* alias for "delete" */
58         { VIRUSFILTER_ACTION_DELETE,            "unlink" },
59         { VIRUSFILTER_ACTION_DO_NOTHING,        "nothing" },
60         { -1,                                   NULL}
61 };
62
63 static int virusfilter_config_destructor(struct virusfilter_config *config)
64 {
65         TALLOC_FREE(config->backend);
66         return 0;
67 }
68
69 /*
70  * This is adapted from vfs_recycle module.
71  * Caller must have become_root();
72  */
73 static bool quarantine_directory_exist(
74         struct vfs_handle_struct *handle,
75         const char *dname)
76 {
77         int ret = -1;
78         struct smb_filename smb_fname = {
79                 .base_name = discard_const_p(char, dname)
80         };
81
82         ret = SMB_VFS_STAT(handle->conn, &smb_fname);
83         if (ret == 0) {
84                 return S_ISDIR(smb_fname.st.st_ex_mode);
85         }
86
87         return false;
88 }
89
90 /**
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();
97  */
98 static bool quarantine_create_dir(
99         struct vfs_handle_struct *handle,
100         struct virusfilter_config *config,
101         const char *dname)
102 {
103         size_t len = 0;
104         size_t cat_len = 0;
105         char *new_dir = NULL;
106         char *tmp_str = NULL;
107         char *token = NULL;
108         char *tok_str = NULL;
109         bool status = false;
110         bool ok = false;
111         int ret = -1;
112         char *saveptr = NULL;
113
114         tmp_str = talloc_strdup(talloc_tos(), dname);
115         if (tmp_str == NULL) {
116                 DBG_ERR("virusfilter-vfs: out of memory!\n");
117                 errno = ENOMEM;
118                 goto done;
119         }
120         tok_str = tmp_str;
121
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");
126                 errno = ENOMEM;
127                 goto done;
128         }
129         *new_dir = '\0';
130         if (dname[0] == '/') {
131                 /* Absolute path. */
132                 cat_len = strlcat(new_dir, "/", len + 1);
133                 if (cat_len >= len+1) {
134                         goto done;
135                 }
136         }
137
138         /* Create directory tree if neccessary */
139         for (token = strtok_r(tok_str, "/", &saveptr);
140              token != NULL;
141              token = strtok_r(NULL, "/", &saveptr))
142         {
143                 cat_len = strlcat(new_dir, token, len + 1);
144                 if (cat_len >= len+1) {
145                         goto done;
146                 }
147                 ok = quarantine_directory_exist(handle, new_dir);
148                 if (ok == true) {
149                         DBG_DEBUG("quarantine: dir %s already exists\n",
150                                   new_dir);
151                 } else {
152                         struct smb_filename *smb_fname = NULL;
153
154                         DBG_INFO("quarantine: creating new dir %s\n", new_dir);
155
156                         smb_fname = synthetic_smb_fname(talloc_tos(), new_dir,
157                                                         NULL, NULL, 0);
158                         if (smb_fname == NULL) {
159                                 goto done;
160                         }
161
162                         ret = SMB_VFS_NEXT_MKDIR(handle,
163                                         smb_fname,
164                                         config->quarantine_dir_mode);
165                         if (ret != 0) {
166                                 TALLOC_FREE(smb_fname);
167
168                                 DBG_WARNING("quarantine: mkdir failed for %s "
169                                             "with error: %s\n", new_dir,
170                                             strerror(errno));
171                                 status = false;
172                                 goto done;
173                         }
174                         TALLOC_FREE(smb_fname);
175                 }
176                 cat_len = strlcat(new_dir, "/", len + 1);
177                 if (cat_len >= len + 1) {
178                         goto done;
179                 }
180         }
181
182         status = true;
183 done:
184         TALLOC_FREE(tmp_str);
185         TALLOC_FREE(new_dir);
186         return status;
187 }
188
189 static int virusfilter_vfs_connect(
190         struct vfs_handle_struct *handle,
191         const char *svc,
192         const char *user)
193 {
194         int snum = SNUM(handle->conn);
195         struct virusfilter_config *config = NULL;
196         const char *exclude_files = NULL;
197         const char *temp_quarantine_dir_mode = NULL;
198         char *sret = NULL;
199         char *tmp = NULL;
200         enum virusfilter_scanner_enum backend;
201         int connect_timeout = 0;
202         int io_timeout = 0;
203         int ret = -1;
204
205         config = talloc_zero(handle, struct virusfilter_config);
206         if (config == NULL) {
207                 DBG_ERR("talloc_zero failed\n");
208                 return -1;
209         }
210         talloc_set_destructor(config, virusfilter_config_destructor);
211
212         SMB_VFS_HANDLE_SET_DATA(handle, config, NULL,
213                                 struct virusfilter_config, return -1);
214
215         config->scan_request_limit = lp_parm_int(
216                 snum, "virusfilter", "scan request limit", 0);
217
218         config->scan_on_open = lp_parm_bool(
219                 snum, "virusfilter", "scan on open", true);
220
221         config->scan_on_close = lp_parm_bool(
222                 snum, "virusfilter", "scan on close", false);
223
224         config->max_nested_scan_archive = lp_parm_int(
225                 snum, "virusfilter", "max nested scan archive", 1);
226
227         config->scan_archive = lp_parm_bool(
228                 snum, "virusfilter", "scan archive", false);
229
230         config->scan_mime = lp_parm_bool(
231                 snum, "virusfilter", "scan mime", false);
232
233         config->max_file_size = (ssize_t)lp_parm_ulong(
234                 snum, "virusfilter", "max file size", 100000000L);
235
236         config->min_file_size = (ssize_t)lp_parm_ulong(
237                 snum, "virusfilter", "min file size", 10);
238
239         exclude_files = lp_parm_const_string(
240                 snum, "virusfilter", "exclude files", NULL);
241         if (exclude_files != NULL) {
242                 set_namearray(&config->exclude_files, exclude_files);
243         }
244
245         config->cache_entry_limit = lp_parm_int(
246                 snum, "virusfilter", "cache entry limit", 100);
247
248         config->cache_time_limit = lp_parm_int(
249                 snum, "virusfilter", "cache time limit", 10);
250
251         config->infected_file_action = lp_parm_enum(
252                 snum, "virusfilter", "infected file action",
253                 virusfilter_actions, VIRUSFILTER_ACTION_DO_NOTHING);
254
255         config->infected_file_command = lp_parm_const_string(
256                 snum, "virusfilter", "infected file command", NULL);
257
258         config->scan_error_command = lp_parm_const_string(
259                 snum, "virusfilter", "scan error command", NULL);
260
261         config->block_access_on_error = lp_parm_bool(
262                 snum, "virusfilter", "block access on error", false);
263
264         tmp = talloc_asprintf(config, "%s/.quarantine",
265                 handle->conn->connectpath);
266
267         config->quarantine_dir = lp_parm_const_string(
268                 snum, "virusfilter", "quarantine directory",
269                 tmp ? tmp : "/tmp/.quarantine");
270
271         if (tmp != config->quarantine_dir) {
272                 TALLOC_FREE(tmp);
273         }
274
275         temp_quarantine_dir_mode = lp_parm_const_string(
276                 snum, "virusfilter", "quarantine directory mode", "0755");
277         if (temp_quarantine_dir_mode != NULL) {
278                 unsigned int mode = 0;
279                 sscanf(temp_quarantine_dir_mode, "%o", &mode);
280                 config->quarantine_dir_mode = mode;
281         }
282
283         config->quarantine_prefix = lp_parm_const_string(
284                 snum, "virusfilter", "quarantine prefix",
285                 VIRUSFILTER_DEFAULT_QUARANTINE_PREFIX);
286
287         config->quarantine_suffix = lp_parm_const_string(
288                 snum, "virusfilter", "quarantine suffix",
289                 VIRUSFILTER_DEFAULT_QUARANTINE_SUFFIX);
290
291         /*
292          * Make sure prefixes and suffixes do not contain directory
293          * delimiters
294          */
295         sret = strstr(config->quarantine_prefix, "/");
296         if (sret != NULL) {
297                 DBG_ERR("quarantine prefix must not contain directory "
298                         "delimiter(s) such as '/' (%s replaced with %s)\n",
299                         config->quarantine_prefix,
300                         VIRUSFILTER_DEFAULT_QUARANTINE_PREFIX);
301                 config->quarantine_prefix =
302                         VIRUSFILTER_DEFAULT_QUARANTINE_PREFIX;
303         }
304         sret = strstr(config->quarantine_suffix, "/");
305         if (sret != NULL) {
306                 DBG_ERR("quarantine suffix must not contain directory "
307                         "delimiter(s) such as '/' (%s replaced with %s)\n",
308                         config->quarantine_suffix,
309                         VIRUSFILTER_DEFAULT_QUARANTINE_SUFFIX);
310                 config->quarantine_suffix =
311                         VIRUSFILTER_DEFAULT_QUARANTINE_SUFFIX;
312         }
313
314         config->quarantine_keep_tree = lp_parm_bool(
315                 snum, "virusfilter", "quarantine keep tree", true);
316
317         config->quarantine_keep_name = lp_parm_bool(
318                 snum, "virusfilter", "quarantine keep name", true);
319
320         config->rename_prefix = lp_parm_const_string(
321                 snum, "virusfilter", "rename prefix",
322                 VIRUSFILTER_DEFAULT_RENAME_PREFIX);
323
324         config->rename_suffix = lp_parm_const_string(
325                 snum, "virusfilter", "rename suffix",
326                 VIRUSFILTER_DEFAULT_RENAME_SUFFIX);
327
328         /*
329          * Make sure prefixes and suffixes do not contain directory
330          * delimiters
331          */
332         sret = strstr(config->rename_prefix, "/");
333         if (sret != NULL) {
334                 DBG_ERR("rename prefix must not contain directory "
335                         "delimiter(s) such as '/' (%s replaced with %s)\n",
336                         config->rename_prefix,
337                         VIRUSFILTER_DEFAULT_RENAME_PREFIX);
338                 config->rename_prefix =
339                         VIRUSFILTER_DEFAULT_RENAME_PREFIX;
340         }
341         sret = strstr(config->rename_suffix, "/");
342         if (sret != NULL) {
343                 DBG_ERR("rename suffix must not contain directory "
344                         "delimiter(s) such as '/' (%s replaced with %s)\n",
345                         config->rename_suffix,
346                         VIRUSFILTER_DEFAULT_RENAME_SUFFIX);
347                 config->rename_suffix =
348                         VIRUSFILTER_DEFAULT_RENAME_SUFFIX;
349         }
350
351         config->infected_open_errno = lp_parm_int(
352                 snum, "virusfilter", "infected file errno on open", EACCES);
353
354         config->infected_close_errno = lp_parm_int(
355                 snum, "virusfilter", "infected file errno on close", 0);
356
357         config->scan_error_open_errno = lp_parm_int(
358                 snum, "virusfilter", "scan error errno on open", EACCES);
359
360         config->scan_error_close_errno = lp_parm_int(
361                 snum, "virusfilter", "scan error errno on close", 0);
362
363         config->socket_path = lp_parm_const_string(
364                 snum, "virusfilter", "socket path", NULL);
365
366         /* canonicalize socket_path */
367         if (config->socket_path != NULL && config->socket_path[0] != '/') {
368                 DBG_ERR("socket path must be an absolute path. "
369                         "Using backend default\n");
370                 config->socket_path = NULL;
371         }
372         if (config->socket_path != NULL) {
373                 canonicalize_absolute_path(handle,
374                                            config->socket_path);
375         }
376
377         connect_timeout = lp_parm_int(snum, "virusfilter",
378                                       "connect timeout", 30000);
379
380         io_timeout = lp_parm_int(snum, "virusfilter", "io timeout", 60000);
381
382         config->io_h = virusfilter_io_new(config, connect_timeout, io_timeout);
383         if (config->io_h == NULL) {
384                 DBG_ERR("virusfilter_io_new failed");
385                 return -1;
386         }
387
388         if (config->cache_entry_limit > 0) {
389                 config->cache = virusfilter_cache_new(handle,
390                                         config->cache_entry_limit,
391                                         config->cache_time_limit);
392                 if (config->cache == NULL) {
393                         DBG_ERR("Initializing cache failed: Cache disabled\n");
394                         return -1;
395                 }
396         }
397
398         /*
399          * Check quarantine directory now to save processing
400          * and becoming root over and over.
401          */
402         if (config->infected_file_action == VIRUSFILTER_ACTION_QUARANTINE) {
403                 bool ok = true;
404                 bool dir_exists;
405
406                 /*
407                  * Do SMB_VFS_NEXT_MKDIR(config->quarantine_dir)
408                  * hierarchy
409                  */
410                 become_root();
411                 dir_exists = quarantine_directory_exist(handle,
412                                                 config->quarantine_dir);
413                 if (!dir_exists) {
414                         DBG_DEBUG("Creating quarantine directory: %s\n",
415                                   config->quarantine_dir);
416                         ok = quarantine_create_dir(handle, config,
417                                               config->quarantine_dir);
418                 }
419                 unbecome_root();
420                 if (!ok) {
421                         DBG_ERR("Creating quarantine directory %s "
422                                 "failed with %s\n",
423                                 config->quarantine_dir,
424                                 strerror(errno));
425                         return -1;
426                 }
427         }
428
429         /*
430          * Now that the frontend options are initialized, load the configured
431          * backend.
432          */
433
434         backend = (enum virusfilter_scanner_enum)lp_parm_enum(snum,
435                                 "virusfilter",
436                                 "scanner",
437                                 scanner_list,
438                                -1);
439         if (backend == (enum virusfilter_scanner_enum)-1) {
440                 DBG_ERR("No AV-Scanner configured, "
441                         "please set \"virusfilter:scanner\"\n");
442                 return -1;
443         }
444
445         switch (backend) {
446         case VIRUSFILTER_SCANNER_SOPHOS:
447                 ret = virusfilter_sophos_init(config);
448                 break;
449         case VIRUSFILTER_SCANNER_FSAV:
450                 ret = virusfilter_fsav_init(config);
451                 break;
452         case VIRUSFILTER_SCANNER_CLAMAV:
453                 ret = virusfilter_clamav_init(config);
454                 break;
455         default:
456                 DBG_ERR("Unhandled scanner %d\n", backend);
457                 return -1;
458         }
459         if (ret != 0) {
460                 DBG_ERR("Scanner backend init failed\n");
461                 return -1;
462         }
463
464         if (config->backend->fns->connect != NULL) {
465                 ret = config->backend->fns->connect(handle, config, svc, user);
466                 if (ret == -1) {
467                         return -1;
468                 }
469         }
470
471         return SMB_VFS_NEXT_CONNECT(handle, svc, user);
472 }
473
474 static void virusfilter_vfs_disconnect(struct vfs_handle_struct *handle)
475 {
476         struct virusfilter_config *config = NULL;
477
478         SMB_VFS_HANDLE_GET_DATA(handle, config,
479                                 struct virusfilter_config, return);
480
481         if (config->backend->fns->disconnect != NULL) {
482                 config->backend->fns->disconnect(handle);
483         }
484
485         free_namearray(config->exclude_files);
486         virusfilter_io_disconnect(config->io_h);
487
488         SMB_VFS_NEXT_DISCONNECT(handle);
489 }
490
491 static int virusfilter_set_module_env(TALLOC_CTX *mem_ctx,
492                                       struct virusfilter_config *config,
493                                       char **env_list)
494 {
495         int ret;
496
497         ret = virusfilter_env_set(mem_ctx, env_list, "VIRUSFILTER_VERSION",
498                                   VIRUSFILTER_VERSION);
499         if (ret == -1) {
500                 return -1;
501         }
502         ret = virusfilter_env_set(mem_ctx, env_list, "VIRUSFILTER_MODULE_NAME",
503                                   config->backend->name);
504         if (ret == -1) {
505                 return -1;
506         }
507
508         if (config->backend->version != 0) {
509                 char *version = NULL;
510
511                 version = talloc_asprintf(talloc_tos(), "%u",
512                                           config->backend->version);
513                 if (version == NULL) {
514                         return -1;
515                 }
516                 ret = virusfilter_env_set(mem_ctx, env_list,
517                                           "VIRUSFILTER_MODULE_VERSION",
518                                           version);
519                 TALLOC_FREE(version);
520                 if (ret == -1) {
521                         return -1;
522                 }
523         }
524
525         return 0;
526 }
527
528 static char *quarantine_check_tree(TALLOC_CTX *mem_ctx,
529                                    struct vfs_handle_struct *handle,
530                                    struct virusfilter_config *config,
531                                    const struct smb_filename *smb_fname,
532                                    char *q_dir_in,
533                                    char *cwd_fname)
534 {
535         char *temp_path = NULL;
536         char *q_dir_out = NULL;
537         bool ok;
538
539         temp_path = talloc_asprintf(talloc_tos(), "%s/%s", q_dir_in, cwd_fname);
540         if (temp_path == NULL) {
541                 DBG_ERR("talloc_asprintf failed\n");
542                 goto out;
543         }
544
545         become_root();
546         ok = quarantine_directory_exist(handle, temp_path);
547         unbecome_root();
548         if (ok) {
549                 DBG_DEBUG("quarantine: directory [%s] exists\n", temp_path);
550                 q_dir_out = talloc_move(mem_ctx, &temp_path);
551                 goto out;
552         }
553
554         DBG_DEBUG("quarantine: Creating directory %s\n", temp_path);
555
556         become_root();
557         ok = quarantine_create_dir(handle, config, temp_path);
558         unbecome_root();
559         if (!ok) {
560                 DBG_NOTICE("Could not create quarantine directory [%s], "
561                            "ignoring for [%s]\n",
562                            temp_path, smb_fname_str_dbg(smb_fname));
563                 goto out;
564         }
565
566         q_dir_out = talloc_move(mem_ctx, &temp_path);
567
568 out:
569         TALLOC_FREE(temp_path);
570         return q_dir_out;
571 }
572
573 static virusfilter_action infected_file_action_quarantine(
574         struct vfs_handle_struct *handle,
575         struct virusfilter_config *config,
576         TALLOC_CTX *mem_ctx,
577         const struct files_struct *fsp,
578         const char **filepath_newp)
579 {
580         TALLOC_CTX *frame = talloc_stackframe();
581         connection_struct *conn = handle->conn;
582         char *cwd_fname = fsp->conn->cwd_fname->base_name;
583         char *fname = fsp->fsp_name->base_name;
584         const struct smb_filename *smb_fname = fsp->fsp_name;
585         struct smb_filename *q_smb_fname = NULL;
586         char *q_dir = NULL;
587         char *q_prefix = NULL;
588         char *q_suffix = NULL;
589         char *q_filepath = NULL;
590         char *dir_name = NULL;
591         const char *base_name = NULL;
592         char *rand_filename_component = NULL;
593         virusfilter_action action = VIRUSFILTER_ACTION_QUARANTINE;
594         bool ok = false;
595         int ret = -1;
596         int saved_errno = 0;
597
598         q_dir = virusfilter_string_sub(frame, conn,
599                                        config->quarantine_dir);
600         q_prefix = virusfilter_string_sub(frame, conn,
601                                           config->quarantine_prefix);
602         q_suffix = virusfilter_string_sub(frame, conn,
603                                           config->quarantine_suffix);
604         if (q_dir == NULL || q_prefix == NULL || q_suffix == NULL) {
605                 DBG_ERR("Quarantine failed: %s/%s: Cannot allocate "
606                         "memory\n", cwd_fname, fname);
607                 action = VIRUSFILTER_ACTION_DO_NOTHING;
608                 goto out;
609         }
610
611         if (config->quarantine_keep_name || config->quarantine_keep_tree) {
612                 ok = parent_dirname(frame, smb_fname->base_name,
613                                     &dir_name, &base_name);
614                 if (!ok) {
615                         DBG_ERR("parent_dirname failed\n");
616                         action = VIRUSFILTER_ACTION_DO_NOTHING;
617                         goto out;
618                 }
619
620                 if (config->quarantine_keep_tree) {
621                         char *tree = NULL;
622
623                         tree = quarantine_check_tree(frame, handle, config,
624                                                      smb_fname, q_dir,
625                                                      cwd_fname);
626                         if (tree == NULL) {
627                                 /*
628                                  * If we can't create the tree, just move it
629                                  * into the toplevel quarantine dir.
630                                  */
631                                 tree = q_dir;
632                         }
633                         q_dir = tree;
634                 }
635         }
636
637         /* Get a 16 byte + \0 random filename component. */
638         rand_filename_component = generate_random_str(frame, 16);
639         if (rand_filename_component == NULL) {
640                 DBG_ERR("generate_random_str failed\n");
641                 action = VIRUSFILTER_ACTION_DO_NOTHING;
642                 goto out;
643         }
644
645         if (config->quarantine_keep_name) {
646                 q_filepath = talloc_asprintf(frame, "%s/%s%s%s-%s",
647                                              q_dir, q_prefix,
648                                              base_name, q_suffix,
649                                              rand_filename_component);
650         } else {
651                 q_filepath = talloc_asprintf(frame, "%s/%s%s",
652                                              q_dir, q_prefix,
653                                              rand_filename_component);
654         }
655         if (q_filepath == NULL) {
656                 DBG_ERR("talloc_asprintf failed\n");
657                 action = VIRUSFILTER_ACTION_DO_NOTHING;
658                 goto out;
659         }
660
661         q_smb_fname = synthetic_smb_fname(frame, q_filepath,
662                                           smb_fname->stream_name,
663                                           NULL, smb_fname->flags);
664         if (q_smb_fname == NULL) {
665                 action = VIRUSFILTER_ACTION_DO_NOTHING;
666                 goto out;
667         }
668
669         become_root();
670         ret = virusfilter_vfs_next_move(handle, smb_fname, q_smb_fname);
671         if (ret == -1) {
672                 saved_errno = errno;
673         }
674         unbecome_root();
675         if (ret == -1) {
676                 DBG_ERR("Quarantine [%s/%s] rename to %s failed: %s\n",
677                         cwd_fname, fname, q_filepath, strerror(saved_errno));
678                 errno = saved_errno;
679                 action = VIRUSFILTER_ACTION_DO_NOTHING;
680                 goto out;
681         }
682
683         *filepath_newp = talloc_move(mem_ctx, &q_filepath);
684
685 out:
686         TALLOC_FREE(frame);
687         return action;
688 }
689
690 static virusfilter_action infected_file_action_rename(
691         struct vfs_handle_struct *handle,
692         struct virusfilter_config *config,
693         TALLOC_CTX *mem_ctx,
694         const struct files_struct *fsp,
695         const char **filepath_newp)
696 {
697         TALLOC_CTX *frame = talloc_stackframe();
698         connection_struct *conn = handle->conn;
699         char *cwd_fname = fsp->conn->cwd_fname->base_name;
700         char *fname = fsp->fsp_name->base_name;
701         const struct smb_filename *smb_fname = fsp->fsp_name;
702         struct smb_filename *q_smb_fname = NULL;
703         char *q_dir = NULL;
704         char *q_prefix = NULL;
705         char *q_suffix = NULL;
706         char *q_filepath = NULL;
707         const char *base_name = NULL;
708         virusfilter_action action = VIRUSFILTER_ACTION_RENAME;
709         bool ok = false;
710         int ret = -1;
711         int saved_errno = 0;
712
713         q_prefix = virusfilter_string_sub(frame, conn,
714                                           config->rename_prefix);
715         q_suffix = virusfilter_string_sub(frame, conn,
716                                           config->rename_suffix);
717         if (q_prefix == NULL || q_suffix == NULL) {
718                 DBG_ERR("Rename failed: %s/%s: Cannot allocate "
719                         "memory\n", cwd_fname, fname);
720                 action = VIRUSFILTER_ACTION_DO_NOTHING;
721                 goto out;
722         }
723
724         ok = parent_dirname(frame, fname, &q_dir, &base_name);
725         if (!ok) {
726                 DBG_ERR("Rename failed: %s/%s: Cannot allocate "
727                         "memory\n", cwd_fname, fname);
728                 action = VIRUSFILTER_ACTION_DO_NOTHING;
729                 goto out;
730         }
731
732         if (q_dir == NULL) {
733                 DBG_ERR("Rename failed: %s/%s: Cannot allocate "
734                         "memory\n", cwd_fname, fname);
735                 action = VIRUSFILTER_ACTION_DO_NOTHING;
736                 goto out;
737         }
738
739         q_filepath = talloc_asprintf(frame, "%s/%s%s%s", q_dir,
740                                      q_prefix, base_name, q_suffix);
741
742         q_smb_fname = synthetic_smb_fname(frame, q_filepath,
743                                           smb_fname->stream_name, NULL,
744                                           smb_fname->flags);
745         if (q_smb_fname == NULL) {
746                 action = VIRUSFILTER_ACTION_DO_NOTHING;
747                 goto out;
748         }
749
750         become_root();
751         ret = virusfilter_vfs_next_move(handle, smb_fname, q_smb_fname);
752         if (ret == -1) {
753                 saved_errno = errno;
754         }
755         unbecome_root();
756
757         if (ret == -1) {
758                 DBG_ERR("Rename failed: %s/%s: Rename failed: %s\n",
759                         cwd_fname, fname, strerror(saved_errno));
760                 errno = saved_errno;
761                 action = VIRUSFILTER_ACTION_DO_NOTHING;
762                 goto out;
763         }
764
765         *filepath_newp = talloc_move(mem_ctx, &q_filepath);
766
767 out:
768         TALLOC_FREE(frame);
769         return action;
770 }
771
772 static virusfilter_action infected_file_action_delete(
773         struct vfs_handle_struct *handle,
774         const struct files_struct *fsp)
775 {
776         int ret;
777         int saved_errno = 0;
778
779         become_root();
780         ret = SMB_VFS_NEXT_UNLINK(handle, fsp->fsp_name);
781         if (ret == -1) {
782                 saved_errno = errno;
783         }
784         unbecome_root();
785         if (ret == -1) {
786                 DBG_ERR("Delete [%s/%s] failed: %s\n",
787                         fsp->conn->cwd_fname->base_name,
788                         fsp->fsp_name->base_name,
789                         strerror(saved_errno));
790                 errno = saved_errno;
791                 return VIRUSFILTER_ACTION_DO_NOTHING;
792         }
793
794         return VIRUSFILTER_ACTION_DELETE;
795 }
796
797 static virusfilter_action virusfilter_do_infected_file_action(
798         struct vfs_handle_struct *handle,
799         struct virusfilter_config *config,
800         TALLOC_CTX *mem_ctx,
801         const struct files_struct *fsp,
802         const char **filepath_newp)
803 {
804         virusfilter_action action;
805
806         *filepath_newp = NULL;
807
808         switch (config->infected_file_action) {
809         case VIRUSFILTER_ACTION_RENAME:
810                 action = infected_file_action_rename(handle, config, mem_ctx,
811                                                      fsp, filepath_newp);
812                 break;
813
814         case VIRUSFILTER_ACTION_QUARANTINE:
815                 action = infected_file_action_quarantine(handle, config, mem_ctx,
816                                                          fsp, filepath_newp);
817                 break;
818
819         case VIRUSFILTER_ACTION_DELETE:
820                 action = infected_file_action_delete(handle, fsp);
821                 break;
822
823         case VIRUSFILTER_ACTION_DO_NOTHING:
824         default:
825                 action = VIRUSFILTER_ACTION_DO_NOTHING;
826                 break;
827         }
828
829         return action;
830 }
831
832 static virusfilter_action virusfilter_treat_infected_file(
833         struct vfs_handle_struct *handle,
834         struct virusfilter_config *config,
835         const struct files_struct *fsp,
836         const char *report,
837         bool is_cache)
838 {
839         connection_struct *conn = handle->conn;
840         char *cwd_fname = fsp->conn->cwd_fname->base_name;
841         char *fname = fsp->fsp_name->base_name;
842         TALLOC_CTX *mem_ctx = talloc_tos();
843         int i;
844         virusfilter_action action;
845         const char *action_name = "UNKNOWN";
846         const char *filepath_q = NULL;
847         char *env_list = NULL;
848         char *command = NULL;
849         int command_result;
850         int ret;
851
852         action = virusfilter_do_infected_file_action(handle, config, mem_ctx,
853                                                      fsp, &filepath_q);
854         for (i=0; virusfilter_actions[i].name; i++) {
855                 if (virusfilter_actions[i].value == action) {
856                         action_name = virusfilter_actions[i].name;
857                         break;
858                 }
859         }
860         DBG_WARNING("Infected file action: %s/%s: %s\n", cwd_fname,
861                     fname, action_name);
862
863         if (!config->infected_file_command) {
864                 return action;
865         }
866
867         ret = virusfilter_set_module_env(mem_ctx, config, &env_list);
868         if (ret == -1) {
869                 goto done;
870         }
871         ret = virusfilter_env_set(mem_ctx, &env_list,
872                                   "VIRUSFILTER_INFECTED_SERVICE_FILE_PATH",
873                                   fname);
874         if (ret == -1) {
875                 goto done;
876         }
877         if (report != NULL) {
878                 ret = virusfilter_env_set(mem_ctx, &env_list,
879                                           "VIRUSFILTER_INFECTED_FILE_REPORT",
880                                           report);
881                 if (ret == -1) {
882                         goto done;
883                 }
884         }
885         ret = virusfilter_env_set(mem_ctx, &env_list,
886                                   "VIRUSFILTER_INFECTED_FILE_ACTION",
887                                   action_name);
888         if (ret == -1) {
889                 goto done;
890         }
891         if (filepath_q != NULL) {
892                 ret = virusfilter_env_set(mem_ctx, &env_list,
893                                           "VIRUSFILTER_QUARANTINED_FILE_PATH",
894                                           filepath_q);
895                 if (ret == -1) {
896                         goto done;
897                 }
898         }
899         if (is_cache) {
900                 ret = virusfilter_env_set(mem_ctx, &env_list,
901                                           "VIRUSFILTER_RESULT_IS_CACHE", "yes");
902                 if (ret == -1) {
903                         goto done;
904                 }
905         }
906
907         command = virusfilter_string_sub(mem_ctx, conn,
908                                          config->infected_file_command);
909         if (command == NULL) {
910                 DBG_ERR("virusfilter_string_sub failed\n");
911                 goto done;
912         }
913
914         DBG_NOTICE("Infected file command line: %s/%s: %s\n", cwd_fname,
915                    fname, command);
916
917         command_result = virusfilter_shell_run(mem_ctx, command, &env_list,
918                                                conn, true);
919         if (command_result != 0) {
920                 DBG_ERR("Infected file command failed: %d\n", command_result);
921         }
922
923         DBG_DEBUG("Infected file command finished: %d\n", command_result);
924
925 done:
926         TALLOC_FREE(env_list);
927         TALLOC_FREE(command);
928
929         return action;
930 }
931
932 static void virusfilter_treat_scan_error(
933         struct vfs_handle_struct *handle,
934         struct virusfilter_config *config,
935         const struct files_struct *fsp,
936         const char *report,
937         bool is_cache)
938 {
939         connection_struct *conn = handle->conn;
940         const char *cwd_fname = fsp->conn->cwd_fname->base_name;
941         const char *fname = fsp->fsp_name->base_name;
942         TALLOC_CTX *mem_ctx = talloc_tos();
943         char *env_list = NULL;
944         char *command = NULL;
945         int command_result;
946         int ret;
947
948         if (!config->scan_error_command) {
949                 return;
950         }
951         ret = virusfilter_set_module_env(mem_ctx, config, &env_list);
952         if (ret == -1) {
953                 goto done;
954         }
955         ret = virusfilter_env_set(mem_ctx, &env_list,
956                                   "VIRUSFILTER_SCAN_ERROR_SERVICE_FILE_PATH",
957                                   fname);
958         if (ret == -1) {
959                 goto done;
960         }
961         if (report != NULL) {
962                 ret = virusfilter_env_set(mem_ctx, &env_list,
963                                           "VIRUSFILTER_SCAN_ERROR_REPORT",
964                                           report);
965                 if (ret == -1) {
966                         goto done;
967                 }
968         }
969         if (is_cache) {
970                 ret = virusfilter_env_set(mem_ctx, &env_list,
971                                           "VIRUSFILTER_RESULT_IS_CACHE", "1");
972                 if (ret == -1) {
973                         goto done;
974                 }
975         }
976
977         command = virusfilter_string_sub(mem_ctx, conn,
978                                          config->scan_error_command);
979         if (command == NULL) {
980                 DBG_ERR("virusfilter_string_sub failed\n");
981                 goto done;
982         }
983
984         DBG_NOTICE("Scan error command line: %s/%s: %s\n", cwd_fname,
985                    fname, command);
986
987         command_result = virusfilter_shell_run(mem_ctx, command, &env_list,
988                                                conn, true);
989         if (command_result != 0) {
990                 DBG_ERR("Scan error command failed: %d\n", command_result);
991         }
992
993 done:
994         TALLOC_FREE(env_list);
995         TALLOC_FREE(command);
996 }
997
998 static virusfilter_result virusfilter_scan(
999         struct vfs_handle_struct *handle,
1000         struct virusfilter_config *config,
1001         const struct files_struct *fsp)
1002 {
1003         virusfilter_result scan_result;
1004         char *scan_report = NULL;
1005         const char *fname = fsp->fsp_name->base_name;
1006         const char *cwd_fname = fsp->conn->cwd_fname->base_name;
1007         struct virusfilter_cache_entry *scan_cache_e = NULL;
1008         bool is_cache = false;
1009         virusfilter_action file_action = VIRUSFILTER_ACTION_DO_NOTHING;
1010         bool add_scan_cache = true;
1011         bool ok = false;
1012
1013         if (config->cache) {
1014                 DBG_DEBUG("Searching cache entry: fname: %s\n", fname);
1015                 scan_cache_e = virusfilter_cache_get(config->cache,
1016                                                      cwd_fname, fname);
1017                 if (scan_cache_e != NULL) {
1018                         DBG_DEBUG("Cache entry found: cached result: %d\n",
1019                               scan_cache_e->result);
1020                         is_cache = true;
1021                         scan_result = scan_cache_e->result;
1022                         scan_report = scan_cache_e->report;
1023                         goto virusfilter_scan_result_eval;
1024                 }
1025                 DBG_DEBUG("Cache entry not found\n");
1026         }
1027
1028         if (config->backend->fns->scan_init != NULL) {
1029                 scan_result = config->backend->fns->scan_init(config);
1030                 if (scan_result != VIRUSFILTER_RESULT_OK) {
1031                         scan_result = VIRUSFILTER_RESULT_ERROR;
1032                         scan_report = talloc_asprintf(
1033                                 talloc_tos(),
1034                                 "Initializing scanner failed");
1035                         goto virusfilter_scan_result_eval;
1036                 }
1037         }
1038
1039         scan_result = config->backend->fns->scan(handle, config, fsp,
1040                                                  &scan_report);
1041
1042         if (config->backend->fns->scan_end != NULL) {
1043                 bool scan_end = true;
1044
1045                 if (config->scan_request_limit > 0) {
1046                         scan_end = false;
1047                         config->scan_request_count++;
1048                         if (config->scan_request_count >=
1049                             config->scan_request_limit)
1050                         {
1051                                 scan_end = true;
1052                                 config->scan_request_count = 0;
1053                         }
1054                 }
1055                 if (scan_end) {
1056                         config->backend->fns->scan_end(config);
1057                 }
1058         }
1059
1060 virusfilter_scan_result_eval:
1061
1062         switch (scan_result) {
1063         case VIRUSFILTER_RESULT_CLEAN:
1064                 DBG_INFO("Scan result: Clean: %s/%s\n", cwd_fname, fname);
1065                 break;
1066
1067         case VIRUSFILTER_RESULT_INFECTED:
1068                 DBG_ERR("Scan result: Infected: %s/%s: %s\n",
1069                         cwd_fname, fname, scan_report ? scan_report :
1070                         "infected (memory error on report)");
1071                 file_action = virusfilter_treat_infected_file(handle,
1072                                         config, fsp, scan_report, is_cache);
1073                 if (file_action != VIRUSFILTER_ACTION_DO_NOTHING) {
1074                         add_scan_cache = false;
1075                 }
1076                 break;
1077
1078         case VIRUSFILTER_RESULT_SUSPECTED:
1079                 if (!config->block_suspected_file) {
1080                         break;
1081                 }
1082                 DBG_ERR("Scan result: Suspected: %s/%s: %s\n",
1083                         cwd_fname, fname, scan_report ? scan_report :
1084                         "suspected infection (memory error on report)");
1085                 file_action = virusfilter_treat_infected_file(handle,
1086                                         config, fsp, scan_report, is_cache);
1087                 if (file_action != VIRUSFILTER_ACTION_DO_NOTHING) {
1088                         add_scan_cache = false;
1089                 }
1090                 break;
1091
1092         case VIRUSFILTER_RESULT_ERROR:
1093                 DBG_ERR("Scan result: Error: %s/%s: %s\n",
1094                         cwd_fname, fname, scan_report ? scan_report :
1095                         "error (memory error on report)");
1096                 virusfilter_treat_scan_error(handle, config, fsp,
1097                                              scan_report, is_cache);
1098                 add_scan_cache = false;
1099                 break;
1100
1101         default:
1102                 DBG_ERR("Scan result: Unknown result code %d: %s/%s: %s\n",
1103                         scan_result, cwd_fname, fname, scan_report ?
1104                         scan_report : "Unknown (memory error on report)");
1105                 virusfilter_treat_scan_error(handle, config, fsp,
1106                                              scan_report, is_cache);
1107                 add_scan_cache = false;
1108                 break;
1109         }
1110
1111         if (config->cache) {
1112                 if (!is_cache && add_scan_cache) {
1113                         DBG_DEBUG("Adding new cache entry: %s, %d\n", fname,
1114                                   scan_result);
1115                         ok = virusfilter_cache_entry_add(
1116                                         config->cache, cwd_fname, fname,
1117                                         scan_result, scan_report);
1118                         if (!ok) {
1119                                 DBG_ERR("Cannot create cache entry: "
1120                                         "virusfilter_cache_entry_new failed");
1121                                 goto virusfilter_scan_return;
1122                         }
1123                 } else if (is_cache) {
1124                         virusfilter_cache_entry_free(scan_cache_e);
1125                 }
1126         }
1127
1128 virusfilter_scan_return:
1129         return scan_result;
1130 }
1131
1132 static int virusfilter_vfs_open(
1133         struct vfs_handle_struct *handle,
1134         struct smb_filename *smb_fname,
1135         files_struct *fsp,
1136         int flags,
1137         mode_t mode)
1138 {
1139         TALLOC_CTX *mem_ctx = talloc_tos();
1140         struct virusfilter_config *config;
1141         const char *cwd_fname = fsp->conn->cwd_fname->base_name;
1142         virusfilter_result scan_result;
1143         const char *fname = fsp->fsp_name->base_name;
1144         char *dir_name = NULL;
1145         const char *base_name = NULL;
1146         int scan_errno = 0;
1147         size_t test_prefix;
1148         size_t test_suffix;
1149         int rename_trap_count = 0;
1150         int ret;
1151         bool ok1, ok2;
1152         char *sret = NULL;
1153
1154         SMB_VFS_HANDLE_GET_DATA(handle, config,
1155                                 struct virusfilter_config, return -1);
1156
1157         if (fsp->is_directory) {
1158                 DBG_INFO("Not scanned: Directory: %s/\n", cwd_fname);
1159                 goto virusfilter_vfs_open_next;
1160         }
1161
1162         test_prefix = strlen(config->rename_prefix);
1163         test_suffix = strlen(config->rename_suffix);
1164         if (test_prefix > 0) {
1165                 rename_trap_count++;
1166         }
1167         if (test_suffix > 0) {
1168                 rename_trap_count++;
1169         }
1170
1171         ok1 = is_ntfs_stream_smb_fname(smb_fname);
1172         ok2 = is_ntfs_default_stream_smb_fname(smb_fname);
1173         if (ok1 && !ok2) {
1174                 DBG_INFO("Not scanned: only file backed streams can be scanned:"
1175                          " %s/%s\n", cwd_fname, fname);
1176                 goto virusfilter_vfs_open_next;
1177         }
1178
1179         if (!config->scan_on_open) {
1180                 DBG_INFO("Not scanned: scan on open is disabled: %s/%s\n",
1181                          cwd_fname, fname);
1182                 goto virusfilter_vfs_open_next;
1183         }
1184
1185         if (flags & O_TRUNC) {
1186                 DBG_INFO("Not scanned: Open flags have O_TRUNC: %s/%s\n",
1187                          cwd_fname, fname);
1188                 goto virusfilter_vfs_open_next;
1189         }
1190
1191         ret = SMB_VFS_NEXT_STAT(handle, smb_fname);
1192         if (ret != 0) {
1193
1194                 /*
1195                  * Do not return immediately if !(flags & O_CREAT) &&
1196                  * errno != ENOENT.
1197                  * Do not do this here or anywhere else. The module is
1198                  * stackable and there may be modules below, such as audit
1199                  * modules, which should be handled.
1200                  */
1201                 goto virusfilter_vfs_open_next;
1202         }
1203         ret = S_ISREG(smb_fname->st.st_ex_mode);
1204         if (ret == 0) {
1205                 DBG_INFO("Not scanned: Directory or special file: %s/%s\n",
1206                          cwd_fname, fname);
1207                 goto virusfilter_vfs_open_next;
1208         }
1209         if (config->max_file_size > 0 &&
1210             smb_fname->st.st_ex_size > config->max_file_size)
1211         {
1212                 DBG_INFO("Not scanned: file size > max file size: %s/%s\n",
1213                          cwd_fname, fname);
1214                 goto virusfilter_vfs_open_next;
1215         }
1216         if (config->min_file_size > 0 &&
1217             smb_fname->st.st_ex_size < config->min_file_size)
1218         {
1219                 DBG_INFO("Not scanned: file size < min file size: %s/%s\n",
1220                       cwd_fname, fname);
1221                 goto virusfilter_vfs_open_next;
1222         }
1223
1224         ok1 = is_in_path(fname, config->exclude_files, false);
1225         if (config->exclude_files && ok1)
1226         {
1227                 DBG_INFO("Not scanned: exclude files: %s/%s\n",
1228                          cwd_fname, fname);
1229                 goto virusfilter_vfs_open_next;
1230         }
1231
1232         if (config->infected_file_action == VIRUSFILTER_ACTION_QUARANTINE) {
1233                 sret = strstr_m(fname, config->quarantine_dir);
1234                 if (sret != NULL) {
1235                         scan_errno = config->infected_open_errno;
1236                         goto virusfilter_vfs_open_fail;
1237                 }
1238         }
1239
1240         if (test_prefix > 0 || test_suffix > 0) {
1241                 ok1 = parent_dirname(mem_ctx, fname, &dir_name, &base_name);
1242                 if (ok1)
1243                 {
1244                         if (test_prefix > 0) {
1245                                 ret = strncmp(base_name,
1246                                     config->rename_prefix, test_prefix);
1247                                 if (ret != 0) {
1248                                         test_prefix = 0;
1249                                 }
1250                         }
1251                         if (test_suffix > 0) {
1252                                 ret = strcmp(base_name + (strlen(base_name)
1253                                                  - test_suffix),
1254                                                  config->rename_suffix);
1255                                 if (ret != 0) {
1256                                         test_suffix = 0;
1257                                 }
1258                         }
1259
1260                         TALLOC_FREE(dir_name);
1261
1262                         if ((rename_trap_count == 2 && test_prefix &&
1263                             test_suffix) || (rename_trap_count == 1 &&
1264                             (test_prefix || test_suffix)))
1265                         {
1266                                 scan_errno =
1267                                         config->infected_open_errno;
1268                                 goto virusfilter_vfs_open_fail;
1269                         }
1270                 }
1271         }
1272
1273         scan_result = virusfilter_scan(handle, config, fsp);
1274
1275         switch (scan_result) {
1276         case VIRUSFILTER_RESULT_CLEAN:
1277                 break;
1278         case VIRUSFILTER_RESULT_INFECTED:
1279                 scan_errno = config->infected_open_errno;
1280                 goto virusfilter_vfs_open_fail;
1281         case VIRUSFILTER_RESULT_ERROR:
1282                 if (config->block_access_on_error) {
1283                         DBG_INFO("Block access\n");
1284                         scan_errno = config->scan_error_open_errno;
1285                         goto virusfilter_vfs_open_fail;
1286                 }
1287                 break;
1288         default:
1289                 scan_errno = config->scan_error_open_errno;
1290                 goto virusfilter_vfs_open_fail;
1291         }
1292
1293 virusfilter_vfs_open_next:
1294         return SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
1295
1296 virusfilter_vfs_open_fail:
1297         errno = (scan_errno != 0) ? scan_errno : EACCES;
1298         return -1;
1299 }
1300
1301 static int virusfilter_vfs_close(
1302         struct vfs_handle_struct *handle,
1303         files_struct *fsp)
1304 {
1305         /*
1306          * The name of this variable is for consistency. If API changes to
1307          * match _open change to cwd_fname as in virusfilter_vfs_open.
1308          */
1309         const char *cwd_fname = handle->conn->connectpath;
1310
1311         struct virusfilter_config *config = NULL;
1312         char *fname = fsp->fsp_name->base_name;
1313         int close_result = -1;
1314         int close_errno = 0;
1315         virusfilter_result scan_result;
1316         int scan_errno = 0;
1317         bool ok1, ok2;
1318
1319         SMB_VFS_HANDLE_GET_DATA(handle, config,
1320                                 struct virusfilter_config, return -1);
1321
1322         /*
1323          * Must close after scan? It appears not as the scanners are not
1324          * internal and other modules such as greyhole seem to do
1325          * SMB_VFS_NEXT_* functions before processing.
1326          */
1327         close_result = SMB_VFS_NEXT_CLOSE(handle, fsp);
1328         if (close_result == -1) {
1329                 close_errno = errno;
1330         }
1331
1332         /*
1333          * Return immediately if close_result == -1, and close_errno == EBADF.
1334          * If close failed, file likely doesn't exist, do not try to scan.
1335          */
1336         if (close_result == -1 && close_errno == EBADF) {
1337                 if (fsp->modified) {
1338                         DBG_DEBUG("Removing cache entry (if existent): "
1339                                   "fname: %s\n", fname);
1340                         virusfilter_cache_remove(config->cache,
1341                                                  cwd_fname, fname);
1342                 }
1343                 goto virusfilter_vfs_close_fail;
1344         }
1345
1346         if (fsp->is_directory) {
1347                 DBG_INFO("Not scanned: Directory: %s/\n", cwd_fname);
1348                 return close_result;
1349         }
1350
1351         ok1 = is_ntfs_stream_smb_fname(fsp->fsp_name);
1352         ok2 = is_ntfs_default_stream_smb_fname(fsp->fsp_name);
1353         if (ok1 && !ok2) {
1354                 if (config->scan_on_open && fsp->modified) {
1355                         if (config->cache) {
1356                                 DBG_DEBUG("Removing cache entry (if existent)"
1357                                           ": fname: %s\n", fname);
1358                                 virusfilter_cache_remove(
1359                                                 config->cache,
1360                                                 cwd_fname, fname);
1361                         }
1362                 }
1363                 DBG_INFO("Not scanned: only file backed streams can be scanned:"
1364                          " %s/%s\n", cwd_fname, fname);
1365                 return close_result;
1366         }
1367
1368         if (!config->scan_on_close) {
1369                 if (config->scan_on_open && fsp->modified) {
1370                         if (config->cache) {
1371                                 DBG_DEBUG("Removing cache entry (if existent)"
1372                                           ": fname: %s\n", fname);
1373                                 virusfilter_cache_remove(
1374                                                 config->cache,
1375                                                 cwd_fname, fname);
1376                         }
1377                 }
1378                 DBG_INFO("Not scanned: scan on close is disabled: %s/%s\n",
1379                          cwd_fname, fname);
1380                 return close_result;
1381         }
1382
1383         if (!fsp->modified) {
1384                 DBG_NOTICE("Not scanned: File not modified: %s/%s\n",
1385                            cwd_fname, fname);
1386
1387                 return close_result;
1388         }
1389
1390         if (config->exclude_files && is_in_path(fname,
1391             config->exclude_files, false))
1392         {
1393                 DBG_INFO("Not scanned: exclude files: %s/%s\n",
1394                          cwd_fname, fname);
1395                 return close_result;
1396         }
1397
1398         scan_result = virusfilter_scan(handle, config, fsp);
1399
1400         switch (scan_result) {
1401         case VIRUSFILTER_RESULT_CLEAN:
1402                 break;
1403         case VIRUSFILTER_RESULT_INFECTED:
1404                 scan_errno = config->infected_close_errno;
1405                 goto virusfilter_vfs_close_fail;
1406         case VIRUSFILTER_RESULT_ERROR:
1407                 if (config->block_access_on_error) {
1408                         DBG_INFO("Block access\n");
1409                         scan_errno = config->scan_error_close_errno;
1410                         goto virusfilter_vfs_close_fail;
1411                 }
1412                 break;
1413         default:
1414                 scan_errno = config->scan_error_close_errno;
1415                 goto virusfilter_vfs_close_fail;
1416         }
1417
1418         if (close_errno != 0) {
1419                 errno = close_errno;
1420         }
1421
1422         return close_result;
1423
1424 virusfilter_vfs_close_fail:
1425
1426         errno = (scan_errno != 0) ? scan_errno : close_errno;
1427
1428         return close_result;
1429 }
1430
1431 static int virusfilter_vfs_unlink(
1432         struct vfs_handle_struct *handle,
1433         const struct smb_filename *smb_fname)
1434 {
1435         int ret = SMB_VFS_NEXT_UNLINK(handle, smb_fname);
1436         struct virusfilter_config *config = NULL;
1437         char *fname = NULL;
1438         char *cwd_fname = handle->conn->cwd_fname->base_name;
1439
1440         if (ret != 0 && errno != ENOENT) {
1441                 return ret;
1442         }
1443
1444         SMB_VFS_HANDLE_GET_DATA(handle, config,
1445                                 struct virusfilter_config, return -1);
1446
1447         if (config->cache == NULL) {
1448                 return 0;
1449         }
1450
1451         fname = smb_fname->base_name;
1452
1453         DBG_DEBUG("Removing cache entry (if existent): fname: %s\n", fname);
1454         virusfilter_cache_remove(config->cache, cwd_fname, fname);
1455
1456         return 0;
1457 }
1458
1459 static int virusfilter_vfs_rename(
1460         struct vfs_handle_struct *handle,
1461         const struct smb_filename *smb_fname_src,
1462         const struct smb_filename *smb_fname_dst)
1463 {
1464         int ret = SMB_VFS_NEXT_RENAME(handle, smb_fname_src, smb_fname_dst);
1465         struct virusfilter_config *config = NULL;
1466         char *fname = NULL;
1467         char *dst_fname = NULL;
1468         char *cwd_fname = handle->conn->cwd_fname->base_name;
1469
1470         if (ret != 0) {
1471                 return ret;
1472         }
1473
1474         SMB_VFS_HANDLE_GET_DATA(handle, config,
1475                                 struct virusfilter_config, return -1);
1476
1477         if (config->cache == NULL) {
1478                 return 0;
1479         }
1480
1481         fname = smb_fname_src->base_name;
1482         dst_fname = smb_fname_dst->base_name;
1483
1484         DBG_DEBUG("Renaming cache entry: fname: %s to: %s\n",
1485                   fname, dst_fname);
1486         virusfilter_cache_entry_rename(config->cache,
1487                                        cwd_fname, fname,
1488                                        dst_fname);
1489
1490         return 0;
1491 }
1492
1493 /* VFS operations */
1494 static struct vfs_fn_pointers vfs_virusfilter_fns = {
1495         .connect_fn     = virusfilter_vfs_connect,
1496         .disconnect_fn  = virusfilter_vfs_disconnect,
1497         .open_fn        = virusfilter_vfs_open,
1498         .close_fn       = virusfilter_vfs_close,
1499         .unlink_fn      = virusfilter_vfs_unlink,
1500         .rename_fn      = virusfilter_vfs_rename,
1501 };
1502
1503 NTSTATUS vfs_virusfilter_init(TALLOC_CTX *);
1504 NTSTATUS vfs_virusfilter_init(TALLOC_CTX *ctx)
1505 {
1506         NTSTATUS status;
1507
1508         status = smb_register_vfs(SMB_VFS_INTERFACE_VERSION,
1509                                   "virusfilter",
1510                                   &vfs_virusfilter_fns);
1511         if (!NT_STATUS_IS_OK(status)) {
1512                 return status;
1513         }
1514
1515         virusfilter_debug_class = debug_add_class("virusfilter");
1516         if (virusfilter_debug_class == -1) {
1517                 virusfilter_debug_class = DBGC_VFS;
1518                 DBG_ERR("Couldn't register custom debugging class!\n");
1519         } else {
1520                 DBG_DEBUG("Debug class number: %d\n", virusfilter_debug_class);
1521         }
1522
1523         DBG_INFO("registered\n");
1524
1525         return status;
1526 }