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