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