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