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