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