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