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