s3: VFS: vfs_virusfilter: Remove unlink_fn. No longer used.
[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_UNLINKAT(handle,
782                                 handle->conn->cwd_fsp,
783                                 fsp->fsp_name,
784                                 0);
785         if (ret == -1) {
786                 saved_errno = errno;
787         }
788         unbecome_root();
789         if (ret == -1) {
790                 DBG_ERR("Delete [%s/%s] failed: %s\n",
791                         fsp->conn->cwd_fsp->fsp_name->base_name,
792                         fsp->fsp_name->base_name,
793                         strerror(saved_errno));
794                 errno = saved_errno;
795                 return VIRUSFILTER_ACTION_DO_NOTHING;
796         }
797
798         return VIRUSFILTER_ACTION_DELETE;
799 }
800
801 static virusfilter_action virusfilter_do_infected_file_action(
802         struct vfs_handle_struct *handle,
803         struct virusfilter_config *config,
804         TALLOC_CTX *mem_ctx,
805         const struct files_struct *fsp,
806         const char **filepath_newp)
807 {
808         virusfilter_action action;
809
810         *filepath_newp = NULL;
811
812         switch (config->infected_file_action) {
813         case VIRUSFILTER_ACTION_RENAME:
814                 action = infected_file_action_rename(handle, config, mem_ctx,
815                                                      fsp, filepath_newp);
816                 break;
817
818         case VIRUSFILTER_ACTION_QUARANTINE:
819                 action = infected_file_action_quarantine(handle, config, mem_ctx,
820                                                          fsp, filepath_newp);
821                 break;
822
823         case VIRUSFILTER_ACTION_DELETE:
824                 action = infected_file_action_delete(handle, fsp);
825                 break;
826
827         case VIRUSFILTER_ACTION_DO_NOTHING:
828         default:
829                 action = VIRUSFILTER_ACTION_DO_NOTHING;
830                 break;
831         }
832
833         return action;
834 }
835
836 static virusfilter_action virusfilter_treat_infected_file(
837         struct vfs_handle_struct *handle,
838         struct virusfilter_config *config,
839         const struct files_struct *fsp,
840         const char *report,
841         bool is_cache)
842 {
843         connection_struct *conn = handle->conn;
844         char *cwd_fname = fsp->conn->cwd_fsp->fsp_name->base_name;
845         char *fname = fsp->fsp_name->base_name;
846         TALLOC_CTX *mem_ctx = talloc_tos();
847         int i;
848         virusfilter_action action;
849         const char *action_name = "UNKNOWN";
850         const char *filepath_q = NULL;
851         char *env_list = NULL;
852         char *command = NULL;
853         int command_result;
854         int ret;
855
856         action = virusfilter_do_infected_file_action(handle, config, mem_ctx,
857                                                      fsp, &filepath_q);
858         for (i=0; virusfilter_actions[i].name; i++) {
859                 if (virusfilter_actions[i].value == action) {
860                         action_name = virusfilter_actions[i].name;
861                         break;
862                 }
863         }
864         DBG_WARNING("Infected file action: %s/%s: %s\n", cwd_fname,
865                     fname, action_name);
866
867         if (!config->infected_file_command) {
868                 return action;
869         }
870
871         ret = virusfilter_set_module_env(mem_ctx, config, &env_list);
872         if (ret == -1) {
873                 goto done;
874         }
875         ret = virusfilter_env_set(mem_ctx, &env_list,
876                                   "VIRUSFILTER_INFECTED_SERVICE_FILE_PATH",
877                                   fname);
878         if (ret == -1) {
879                 goto done;
880         }
881         if (report != NULL) {
882                 ret = virusfilter_env_set(mem_ctx, &env_list,
883                                           "VIRUSFILTER_INFECTED_FILE_REPORT",
884                                           report);
885                 if (ret == -1) {
886                         goto done;
887                 }
888         }
889         ret = virusfilter_env_set(mem_ctx, &env_list,
890                                   "VIRUSFILTER_INFECTED_FILE_ACTION",
891                                   action_name);
892         if (ret == -1) {
893                 goto done;
894         }
895         if (filepath_q != NULL) {
896                 ret = virusfilter_env_set(mem_ctx, &env_list,
897                                           "VIRUSFILTER_QUARANTINED_FILE_PATH",
898                                           filepath_q);
899                 if (ret == -1) {
900                         goto done;
901                 }
902         }
903         if (is_cache) {
904                 ret = virusfilter_env_set(mem_ctx, &env_list,
905                                           "VIRUSFILTER_RESULT_IS_CACHE", "yes");
906                 if (ret == -1) {
907                         goto done;
908                 }
909         }
910
911         command = virusfilter_string_sub(mem_ctx, conn,
912                                          config->infected_file_command);
913         if (command == NULL) {
914                 DBG_ERR("virusfilter_string_sub failed\n");
915                 goto done;
916         }
917
918         DBG_NOTICE("Infected file command line: %s/%s: %s\n", cwd_fname,
919                    fname, command);
920
921         command_result = virusfilter_shell_run(mem_ctx, command, &env_list,
922                                                conn, true);
923         if (command_result != 0) {
924                 DBG_ERR("Infected file command failed: %d\n", command_result);
925         }
926
927         DBG_DEBUG("Infected file command finished: %d\n", command_result);
928
929 done:
930         TALLOC_FREE(env_list);
931         TALLOC_FREE(command);
932
933         return action;
934 }
935
936 static void virusfilter_treat_scan_error(
937         struct vfs_handle_struct *handle,
938         struct virusfilter_config *config,
939         const struct files_struct *fsp,
940         const char *report,
941         bool is_cache)
942 {
943         connection_struct *conn = handle->conn;
944         const char *cwd_fname = fsp->conn->cwd_fsp->fsp_name->base_name;
945         const char *fname = fsp->fsp_name->base_name;
946         TALLOC_CTX *mem_ctx = talloc_tos();
947         char *env_list = NULL;
948         char *command = NULL;
949         int command_result;
950         int ret;
951
952         if (!config->scan_error_command) {
953                 return;
954         }
955         ret = virusfilter_set_module_env(mem_ctx, config, &env_list);
956         if (ret == -1) {
957                 goto done;
958         }
959         ret = virusfilter_env_set(mem_ctx, &env_list,
960                                   "VIRUSFILTER_SCAN_ERROR_SERVICE_FILE_PATH",
961                                   fname);
962         if (ret == -1) {
963                 goto done;
964         }
965         if (report != NULL) {
966                 ret = virusfilter_env_set(mem_ctx, &env_list,
967                                           "VIRUSFILTER_SCAN_ERROR_REPORT",
968                                           report);
969                 if (ret == -1) {
970                         goto done;
971                 }
972         }
973         if (is_cache) {
974                 ret = virusfilter_env_set(mem_ctx, &env_list,
975                                           "VIRUSFILTER_RESULT_IS_CACHE", "1");
976                 if (ret == -1) {
977                         goto done;
978                 }
979         }
980
981         command = virusfilter_string_sub(mem_ctx, conn,
982                                          config->scan_error_command);
983         if (command == NULL) {
984                 DBG_ERR("virusfilter_string_sub failed\n");
985                 goto done;
986         }
987
988         DBG_NOTICE("Scan error command line: %s/%s: %s\n", cwd_fname,
989                    fname, command);
990
991         command_result = virusfilter_shell_run(mem_ctx, command, &env_list,
992                                                conn, true);
993         if (command_result != 0) {
994                 DBG_ERR("Scan error command failed: %d\n", command_result);
995         }
996
997 done:
998         TALLOC_FREE(env_list);
999         TALLOC_FREE(command);
1000 }
1001
1002 static virusfilter_result virusfilter_scan(
1003         struct vfs_handle_struct *handle,
1004         struct virusfilter_config *config,
1005         const struct files_struct *fsp)
1006 {
1007         virusfilter_result scan_result;
1008         char *scan_report = NULL;
1009         const char *fname = fsp->fsp_name->base_name;
1010         const char *cwd_fname = fsp->conn->cwd_fsp->fsp_name->base_name;
1011         struct virusfilter_cache_entry *scan_cache_e = NULL;
1012         bool is_cache = false;
1013         virusfilter_action file_action = VIRUSFILTER_ACTION_DO_NOTHING;
1014         bool add_scan_cache = true;
1015         bool ok = false;
1016
1017         if (config->cache) {
1018                 DBG_DEBUG("Searching cache entry: fname: %s\n", fname);
1019                 scan_cache_e = virusfilter_cache_get(config->cache,
1020                                                      cwd_fname, fname);
1021                 if (scan_cache_e != NULL) {
1022                         DBG_DEBUG("Cache entry found: cached result: %d\n",
1023                               scan_cache_e->result);
1024                         is_cache = true;
1025                         scan_result = scan_cache_e->result;
1026                         scan_report = scan_cache_e->report;
1027                         goto virusfilter_scan_result_eval;
1028                 }
1029                 DBG_DEBUG("Cache entry not found\n");
1030         }
1031
1032         if (config->backend->fns->scan_init != NULL) {
1033                 scan_result = config->backend->fns->scan_init(config);
1034                 if (scan_result != VIRUSFILTER_RESULT_OK) {
1035                         scan_result = VIRUSFILTER_RESULT_ERROR;
1036                         scan_report = talloc_asprintf(
1037                                 talloc_tos(),
1038                                 "Initializing scanner failed");
1039                         goto virusfilter_scan_result_eval;
1040                 }
1041         }
1042
1043         scan_result = config->backend->fns->scan(handle, config, fsp,
1044                                                  &scan_report);
1045
1046         if (config->backend->fns->scan_end != NULL) {
1047                 bool scan_end = true;
1048
1049                 if (config->scan_request_limit > 0) {
1050                         scan_end = false;
1051                         config->scan_request_count++;
1052                         if (config->scan_request_count >=
1053                             config->scan_request_limit)
1054                         {
1055                                 scan_end = true;
1056                                 config->scan_request_count = 0;
1057                         }
1058                 }
1059                 if (scan_end) {
1060                         config->backend->fns->scan_end(config);
1061                 }
1062         }
1063
1064 virusfilter_scan_result_eval:
1065
1066         switch (scan_result) {
1067         case VIRUSFILTER_RESULT_CLEAN:
1068                 DBG_INFO("Scan result: Clean: %s/%s\n", cwd_fname, fname);
1069                 break;
1070
1071         case VIRUSFILTER_RESULT_INFECTED:
1072                 DBG_ERR("Scan result: Infected: %s/%s: %s\n",
1073                         cwd_fname, fname, scan_report ? scan_report :
1074                         "infected (memory error on report)");
1075                 file_action = virusfilter_treat_infected_file(handle,
1076                                         config, fsp, scan_report, is_cache);
1077                 if (file_action != VIRUSFILTER_ACTION_DO_NOTHING) {
1078                         add_scan_cache = false;
1079                 }
1080                 break;
1081
1082         case VIRUSFILTER_RESULT_SUSPECTED:
1083                 if (!config->block_suspected_file) {
1084                         break;
1085                 }
1086                 DBG_ERR("Scan result: Suspected: %s/%s: %s\n",
1087                         cwd_fname, fname, scan_report ? scan_report :
1088                         "suspected infection (memory error on report)");
1089                 file_action = virusfilter_treat_infected_file(handle,
1090                                         config, fsp, scan_report, is_cache);
1091                 if (file_action != VIRUSFILTER_ACTION_DO_NOTHING) {
1092                         add_scan_cache = false;
1093                 }
1094                 break;
1095
1096         case VIRUSFILTER_RESULT_ERROR:
1097                 DBG_ERR("Scan result: Error: %s/%s: %s\n",
1098                         cwd_fname, fname, scan_report ? scan_report :
1099                         "error (memory error on report)");
1100                 virusfilter_treat_scan_error(handle, config, fsp,
1101                                              scan_report, is_cache);
1102                 add_scan_cache = false;
1103                 break;
1104
1105         default:
1106                 DBG_ERR("Scan result: Unknown result code %d: %s/%s: %s\n",
1107                         scan_result, cwd_fname, fname, scan_report ?
1108                         scan_report : "Unknown (memory error on report)");
1109                 virusfilter_treat_scan_error(handle, config, fsp,
1110                                              scan_report, is_cache);
1111                 add_scan_cache = false;
1112                 break;
1113         }
1114
1115         if (config->cache) {
1116                 if (!is_cache && add_scan_cache) {
1117                         DBG_DEBUG("Adding new cache entry: %s, %d\n", fname,
1118                                   scan_result);
1119                         ok = virusfilter_cache_entry_add(
1120                                         config->cache, cwd_fname, fname,
1121                                         scan_result, scan_report);
1122                         if (!ok) {
1123                                 DBG_ERR("Cannot create cache entry: "
1124                                         "virusfilter_cache_entry_new failed");
1125                                 goto virusfilter_scan_return;
1126                         }
1127                 } else if (is_cache) {
1128                         virusfilter_cache_entry_free(scan_cache_e);
1129                 }
1130         }
1131
1132 virusfilter_scan_return:
1133         return scan_result;
1134 }
1135
1136 static int virusfilter_vfs_open(
1137         struct vfs_handle_struct *handle,
1138         struct smb_filename *smb_fname,
1139         files_struct *fsp,
1140         int flags,
1141         mode_t mode)
1142 {
1143         TALLOC_CTX *mem_ctx = talloc_tos();
1144         struct virusfilter_config *config;
1145         const char *cwd_fname = fsp->conn->cwd_fsp->fsp_name->base_name;
1146         virusfilter_result scan_result;
1147         const char *fname = fsp->fsp_name->base_name;
1148         char *dir_name = NULL;
1149         const char *base_name = NULL;
1150         int scan_errno = 0;
1151         size_t test_prefix;
1152         size_t test_suffix;
1153         int rename_trap_count = 0;
1154         int ret;
1155         bool ok1, ok2;
1156         char *sret = NULL;
1157
1158         SMB_VFS_HANDLE_GET_DATA(handle, config,
1159                                 struct virusfilter_config, return -1);
1160
1161         if (fsp->is_directory) {
1162                 DBG_INFO("Not scanned: Directory: %s/\n", cwd_fname);
1163                 goto virusfilter_vfs_open_next;
1164         }
1165
1166         test_prefix = strlen(config->rename_prefix);
1167         test_suffix = strlen(config->rename_suffix);
1168         if (test_prefix > 0) {
1169                 rename_trap_count++;
1170         }
1171         if (test_suffix > 0) {
1172                 rename_trap_count++;
1173         }
1174
1175         ok1 = is_ntfs_stream_smb_fname(smb_fname);
1176         ok2 = is_ntfs_default_stream_smb_fname(smb_fname);
1177         if (ok1 && !ok2) {
1178                 DBG_INFO("Not scanned: only file backed streams can be scanned:"
1179                          " %s/%s\n", cwd_fname, fname);
1180                 goto virusfilter_vfs_open_next;
1181         }
1182
1183         if (!config->scan_on_open) {
1184                 DBG_INFO("Not scanned: scan on open is disabled: %s/%s\n",
1185                          cwd_fname, fname);
1186                 goto virusfilter_vfs_open_next;
1187         }
1188
1189         if (flags & O_TRUNC) {
1190                 DBG_INFO("Not scanned: Open flags have O_TRUNC: %s/%s\n",
1191                          cwd_fname, fname);
1192                 goto virusfilter_vfs_open_next;
1193         }
1194
1195         ret = SMB_VFS_NEXT_STAT(handle, smb_fname);
1196         if (ret != 0) {
1197
1198                 /*
1199                  * Do not return immediately if !(flags & O_CREAT) &&
1200                  * errno != ENOENT.
1201                  * Do not do this here or anywhere else. The module is
1202                  * stackable and there may be modules below, such as audit
1203                  * modules, which should be handled.
1204                  */
1205                 goto virusfilter_vfs_open_next;
1206         }
1207         ret = S_ISREG(smb_fname->st.st_ex_mode);
1208         if (ret == 0) {
1209                 DBG_INFO("Not scanned: Directory or special file: %s/%s\n",
1210                          cwd_fname, fname);
1211                 goto virusfilter_vfs_open_next;
1212         }
1213         if (config->max_file_size > 0 &&
1214             smb_fname->st.st_ex_size > config->max_file_size)
1215         {
1216                 DBG_INFO("Not scanned: file size > max file size: %s/%s\n",
1217                          cwd_fname, fname);
1218                 goto virusfilter_vfs_open_next;
1219         }
1220         if (config->min_file_size > 0 &&
1221             smb_fname->st.st_ex_size < config->min_file_size)
1222         {
1223                 DBG_INFO("Not scanned: file size < min file size: %s/%s\n",
1224                       cwd_fname, fname);
1225                 goto virusfilter_vfs_open_next;
1226         }
1227
1228         ok1 = is_in_path(fname, config->exclude_files, false);
1229         if (config->exclude_files && ok1)
1230         {
1231                 DBG_INFO("Not scanned: exclude files: %s/%s\n",
1232                          cwd_fname, fname);
1233                 goto virusfilter_vfs_open_next;
1234         }
1235
1236         if (config->infected_file_action == VIRUSFILTER_ACTION_QUARANTINE) {
1237                 sret = strstr_m(fname, config->quarantine_dir);
1238                 if (sret != NULL) {
1239                         scan_errno = config->infected_open_errno;
1240                         goto virusfilter_vfs_open_fail;
1241                 }
1242         }
1243
1244         if (test_prefix > 0 || test_suffix > 0) {
1245                 ok1 = parent_dirname(mem_ctx, fname, &dir_name, &base_name);
1246                 if (ok1)
1247                 {
1248                         if (test_prefix > 0) {
1249                                 ret = strncmp(base_name,
1250                                     config->rename_prefix, test_prefix);
1251                                 if (ret != 0) {
1252                                         test_prefix = 0;
1253                                 }
1254                         }
1255                         if (test_suffix > 0) {
1256                                 ret = strcmp(base_name + (strlen(base_name)
1257                                                  - test_suffix),
1258                                                  config->rename_suffix);
1259                                 if (ret != 0) {
1260                                         test_suffix = 0;
1261                                 }
1262                         }
1263
1264                         TALLOC_FREE(dir_name);
1265
1266                         if ((rename_trap_count == 2 && test_prefix &&
1267                             test_suffix) || (rename_trap_count == 1 &&
1268                             (test_prefix || test_suffix)))
1269                         {
1270                                 scan_errno =
1271                                         config->infected_open_errno;
1272                                 goto virusfilter_vfs_open_fail;
1273                         }
1274                 }
1275         }
1276
1277         scan_result = virusfilter_scan(handle, config, fsp);
1278
1279         switch (scan_result) {
1280         case VIRUSFILTER_RESULT_CLEAN:
1281                 break;
1282         case VIRUSFILTER_RESULT_INFECTED:
1283                 scan_errno = config->infected_open_errno;
1284                 goto virusfilter_vfs_open_fail;
1285         case VIRUSFILTER_RESULT_ERROR:
1286                 if (config->block_access_on_error) {
1287                         DBG_INFO("Block access\n");
1288                         scan_errno = config->scan_error_open_errno;
1289                         goto virusfilter_vfs_open_fail;
1290                 }
1291                 break;
1292         default:
1293                 scan_errno = config->scan_error_open_errno;
1294                 goto virusfilter_vfs_open_fail;
1295         }
1296
1297 virusfilter_vfs_open_next:
1298         return SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
1299
1300 virusfilter_vfs_open_fail:
1301         errno = (scan_errno != 0) ? scan_errno : EACCES;
1302         return -1;
1303 }
1304
1305 static int virusfilter_vfs_close(
1306         struct vfs_handle_struct *handle,
1307         files_struct *fsp)
1308 {
1309         /*
1310          * The name of this variable is for consistency. If API changes to
1311          * match _open change to cwd_fname as in virusfilter_vfs_open.
1312          */
1313         const char *cwd_fname = handle->conn->connectpath;
1314
1315         struct virusfilter_config *config = NULL;
1316         char *fname = fsp->fsp_name->base_name;
1317         int close_result = -1;
1318         int close_errno = 0;
1319         virusfilter_result scan_result;
1320         int scan_errno = 0;
1321         bool ok1, ok2;
1322
1323         SMB_VFS_HANDLE_GET_DATA(handle, config,
1324                                 struct virusfilter_config, return -1);
1325
1326         /*
1327          * Must close after scan? It appears not as the scanners are not
1328          * internal and other modules such as greyhole seem to do
1329          * SMB_VFS_NEXT_* functions before processing.
1330          */
1331         close_result = SMB_VFS_NEXT_CLOSE(handle, fsp);
1332         if (close_result == -1) {
1333                 close_errno = errno;
1334         }
1335
1336         /*
1337          * Return immediately if close_result == -1, and close_errno == EBADF.
1338          * If close failed, file likely doesn't exist, do not try to scan.
1339          */
1340         if (close_result == -1 && close_errno == EBADF) {
1341                 if (fsp->modified) {
1342                         DBG_DEBUG("Removing cache entry (if existent): "
1343                                   "fname: %s\n", fname);
1344                         virusfilter_cache_remove(config->cache,
1345                                                  cwd_fname, fname);
1346                 }
1347                 goto virusfilter_vfs_close_fail;
1348         }
1349
1350         if (fsp->is_directory) {
1351                 DBG_INFO("Not scanned: Directory: %s/\n", cwd_fname);
1352                 return close_result;
1353         }
1354
1355         ok1 = is_ntfs_stream_smb_fname(fsp->fsp_name);
1356         ok2 = is_ntfs_default_stream_smb_fname(fsp->fsp_name);
1357         if (ok1 && !ok2) {
1358                 if (config->scan_on_open && fsp->modified) {
1359                         if (config->cache) {
1360                                 DBG_DEBUG("Removing cache entry (if existent)"
1361                                           ": fname: %s\n", fname);
1362                                 virusfilter_cache_remove(
1363                                                 config->cache,
1364                                                 cwd_fname, fname);
1365                         }
1366                 }
1367                 DBG_INFO("Not scanned: only file backed streams can be scanned:"
1368                          " %s/%s\n", cwd_fname, fname);
1369                 return close_result;
1370         }
1371
1372         if (!config->scan_on_close) {
1373                 if (config->scan_on_open && fsp->modified) {
1374                         if (config->cache) {
1375                                 DBG_DEBUG("Removing cache entry (if existent)"
1376                                           ": fname: %s\n", fname);
1377                                 virusfilter_cache_remove(
1378                                                 config->cache,
1379                                                 cwd_fname, fname);
1380                         }
1381                 }
1382                 DBG_INFO("Not scanned: scan on close is disabled: %s/%s\n",
1383                          cwd_fname, fname);
1384                 return close_result;
1385         }
1386
1387         if (!fsp->modified) {
1388                 DBG_NOTICE("Not scanned: File not modified: %s/%s\n",
1389                            cwd_fname, fname);
1390
1391                 return close_result;
1392         }
1393
1394         if (config->exclude_files && is_in_path(fname,
1395             config->exclude_files, false))
1396         {
1397                 DBG_INFO("Not scanned: exclude files: %s/%s\n",
1398                          cwd_fname, fname);
1399                 return close_result;
1400         }
1401
1402         scan_result = virusfilter_scan(handle, config, fsp);
1403
1404         switch (scan_result) {
1405         case VIRUSFILTER_RESULT_CLEAN:
1406                 break;
1407         case VIRUSFILTER_RESULT_INFECTED:
1408                 scan_errno = config->infected_close_errno;
1409                 goto virusfilter_vfs_close_fail;
1410         case VIRUSFILTER_RESULT_ERROR:
1411                 if (config->block_access_on_error) {
1412                         DBG_INFO("Block access\n");
1413                         scan_errno = config->scan_error_close_errno;
1414                         goto virusfilter_vfs_close_fail;
1415                 }
1416                 break;
1417         default:
1418                 scan_errno = config->scan_error_close_errno;
1419                 goto virusfilter_vfs_close_fail;
1420         }
1421
1422         if (close_errno != 0) {
1423                 errno = close_errno;
1424         }
1425
1426         return close_result;
1427
1428 virusfilter_vfs_close_fail:
1429
1430         errno = (scan_errno != 0) ? scan_errno : close_errno;
1431
1432         return close_result;
1433 }
1434
1435 static int virusfilter_vfs_unlinkat(struct vfs_handle_struct *handle,
1436                 struct files_struct *dirfsp,
1437                 const struct smb_filename *smb_fname,
1438                 int flags)
1439 {
1440         int ret = SMB_VFS_NEXT_UNLINKAT(handle,
1441                         dirfsp,
1442                         smb_fname,
1443                         flags);
1444         struct virusfilter_config *config = NULL;
1445         char *fname = NULL;
1446         char *cwd_fname = handle->conn->cwd_fsp->fsp_name->base_name;
1447
1448         if (ret != 0 && errno != ENOENT) {
1449                 return ret;
1450         }
1451
1452         SMB_VFS_HANDLE_GET_DATA(handle, config,
1453                                 struct virusfilter_config, return -1);
1454
1455         if (config->cache == NULL) {
1456                 return 0;
1457         }
1458
1459         fname = smb_fname->base_name;
1460
1461         DBG_DEBUG("Removing cache entry (if existent): fname: %s\n", fname);
1462         virusfilter_cache_remove(config->cache, cwd_fname, fname);
1463
1464         return 0;
1465 }
1466
1467 static int virusfilter_vfs_renameat(
1468         struct vfs_handle_struct *handle,
1469         files_struct *srcfsp,
1470         const struct smb_filename *smb_fname_src,
1471         files_struct *dstfsp,
1472         const struct smb_filename *smb_fname_dst)
1473 {
1474         int ret = SMB_VFS_NEXT_RENAMEAT(handle,
1475                         srcfsp,
1476                         smb_fname_src,
1477                         dstfsp,
1478                         smb_fname_dst);
1479         struct virusfilter_config *config = NULL;
1480         char *fname = NULL;
1481         char *dst_fname = NULL;
1482         char *cwd_fname = handle->conn->cwd_fsp->fsp_name->base_name;
1483
1484         if (ret != 0) {
1485                 return ret;
1486         }
1487
1488         SMB_VFS_HANDLE_GET_DATA(handle, config,
1489                                 struct virusfilter_config, return -1);
1490
1491         if (config->cache == NULL) {
1492                 return 0;
1493         }
1494
1495         fname = smb_fname_src->base_name;
1496         dst_fname = smb_fname_dst->base_name;
1497
1498         DBG_DEBUG("Renaming cache entry: fname: %s to: %s\n",
1499                   fname, dst_fname);
1500         virusfilter_cache_entry_rename(config->cache,
1501                                        cwd_fname, fname,
1502                                        dst_fname);
1503
1504         return 0;
1505 }
1506
1507
1508 /* VFS operations */
1509 static struct vfs_fn_pointers vfs_virusfilter_fns = {
1510         .connect_fn     = virusfilter_vfs_connect,
1511         .disconnect_fn  = virusfilter_vfs_disconnect,
1512         .open_fn        = virusfilter_vfs_open,
1513         .close_fn       = virusfilter_vfs_close,
1514         .unlinkat_fn    = virusfilter_vfs_unlinkat,
1515         .renameat_fn    = virusfilter_vfs_renameat,
1516 };
1517
1518 NTSTATUS vfs_virusfilter_init(TALLOC_CTX *);
1519 NTSTATUS vfs_virusfilter_init(TALLOC_CTX *ctx)
1520 {
1521         NTSTATUS status;
1522
1523         status = smb_register_vfs(SMB_VFS_INTERFACE_VERSION,
1524                                   "virusfilter",
1525                                   &vfs_virusfilter_fns);
1526         if (!NT_STATUS_IS_OK(status)) {
1527                 return status;
1528         }
1529
1530         virusfilter_debug_class = debug_add_class("virusfilter");
1531         if (virusfilter_debug_class == -1) {
1532                 virusfilter_debug_class = DBGC_VFS;
1533                 DBG_ERR("Couldn't register custom debugging class!\n");
1534         } else {
1535                 DBG_DEBUG("Debug class number: %d\n", virusfilter_debug_class);
1536         }
1537
1538         DBG_INFO("registered\n");
1539
1540         return status;
1541 }