From 8daa83a594a2e98f39d764422bfbdbc62c9efd44 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Fri, 19 Apr 2024 19:20:00 +0200 Subject: Adding upstream version 2:4.20.0+dfsg. Signed-off-by: Daniel Baumann --- source3/modules/vfs_virusfilter_fsav.c | 451 +++++++++++++++++++++++++++++++++ 1 file changed, 451 insertions(+) create mode 100644 source3/modules/vfs_virusfilter_fsav.c (limited to 'source3/modules/vfs_virusfilter_fsav.c') diff --git a/source3/modules/vfs_virusfilter_fsav.c b/source3/modules/vfs_virusfilter_fsav.c new file mode 100644 index 0000000..25a2906 --- /dev/null +++ b/source3/modules/vfs_virusfilter_fsav.c @@ -0,0 +1,451 @@ +/* + Samba-VirusFilter VFS modules + F-Secure Anti-Virus fsavd support + Copyright (C) 2010-2016 SATOH Fumiyasu @ OSS Technology Corp., Japan + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "vfs_virusfilter_common.h" +#include "vfs_virusfilter_utils.h" + +#ifdef FSAV_DEFAULT_SOCKET_PATH +# define VIRUSFILTER_DEFAULT_SOCKET_PATH FSAV_DEFAULT_SOCKET_PATH +#else +# define VIRUSFILTER_DEFAULT_SOCKET_PATH "/tmp/.fsav-0" +#endif + +/* Default values for module-specific configuration variables */ +/* 5 = F-Secure Linux 7 or later? */ + +#define VIRUSFILTER_DEFAULT_FSAV_PROTOCOL 5 +#define VIRUSFILTER_DEFAULT_SCAN_RISKWARE false +#define VIRUSFILTER_DEFAULT_STOP_SCAN_ON_FIRST true +#define VIRUSFILTER_DEFAULT_FILTER_FILENAME false + +struct virusfilter_fsav_config { + /* Backpointer */ + struct virusfilter_config *config; + + int fsav_protocol; + bool scan_riskware; + bool stop_scan_on_first; + bool filter_filename; +}; + +static void virusfilter_fsav_scan_end(struct virusfilter_config *config); + +static int virusfilter_fsav_destruct_config( + struct virusfilter_fsav_config *fsav_config) +{ + virusfilter_fsav_scan_end(fsav_config->config); + return 0; +} + +static int virusfilter_fsav_connect( + struct vfs_handle_struct *handle, + struct virusfilter_config *config, + const char *svc, + const char *user) +{ + int snum = SNUM(handle->conn); + struct virusfilter_fsav_config *fsav_config = NULL; + + fsav_config = talloc_zero(config->backend, + struct virusfilter_fsav_config); + if (fsav_config == NULL) { + return -1; + } + + fsav_config->config = config; + + fsav_config->fsav_protocol = lp_parm_int( + snum, "virusfilter", "fsav protocol", + VIRUSFILTER_DEFAULT_FSAV_PROTOCOL); + + fsav_config->scan_riskware = lp_parm_bool( + snum, "virusfilter", "scan riskware", + VIRUSFILTER_DEFAULT_SCAN_RISKWARE); + + fsav_config->stop_scan_on_first = lp_parm_bool( + snum, "virusfilter", "stop scan on first", + VIRUSFILTER_DEFAULT_STOP_SCAN_ON_FIRST); + + fsav_config->filter_filename = lp_parm_bool( + snum, "virusfilter", "filter filename", + VIRUSFILTER_DEFAULT_FILTER_FILENAME); + + talloc_set_destructor(fsav_config, virusfilter_fsav_destruct_config); + + config->backend->backend_private = fsav_config; + + config->block_suspected_file = lp_parm_bool( + snum, "virusfilter", "block suspected file", false); + + return 0; +} + +static virusfilter_result virusfilter_fsav_scan_init( + struct virusfilter_config *config) +{ + struct virusfilter_fsav_config *fsav_config = NULL; + struct virusfilter_io_handle *io_h = config->io_h; + char *reply = NULL; + bool ok; + int ret; + + fsav_config = talloc_get_type_abort(config->backend->backend_private, + struct virusfilter_fsav_config); + + if (io_h->stream != NULL) { + DBG_DEBUG("fsavd: Checking if connection is alive\n"); + + /* FIXME: I don't know the correct PING command format... */ + ok = virusfilter_io_writefl_readl(io_h, &reply, "PING"); + if (ok) { + ret = strncmp(reply, "ERROR\t", 6); + if (ret == 0) { + DBG_DEBUG("fsavd: Re-using existent " + "connection\n"); + goto virusfilter_fsav_init_succeed; + } + } + + DBG_DEBUG("fsavd: Closing dead connection\n"); + virusfilter_fsav_scan_end(config); + } + + DBG_INFO("fsavd: Connecting to socket: %s\n", + config->socket_path); + + become_root(); + ok = virusfilter_io_connect_path(io_h, config->socket_path); + unbecome_root(); + + if (!ok) { + DBG_ERR("fsavd: Connecting to socket failed: %s: %s\n", + config->socket_path, strerror(errno)); + goto virusfilter_fsav_init_failed; + } + + TALLOC_FREE(reply); + + ok = virusfilter_io_readl(talloc_tos(), io_h, &reply); + if (!ok) { + DBG_ERR("fsavd: Reading greeting message failed: %s\n", + strerror(errno)); + goto virusfilter_fsav_init_failed; + } + ret = strncmp(reply, "DBVERSION\t", 10); + if (ret != 0) { + DBG_ERR("fsavd: Invalid greeting message: %s\n", + reply); + goto virusfilter_fsav_init_failed; + } + + DBG_DEBUG("fsavd: Connected\n"); + + DBG_INFO("fsavd: Configuring\n"); + + TALLOC_FREE(reply); + + ok = virusfilter_io_writefl_readl(io_h, &reply, "PROTOCOL\t%d", + fsav_config->fsav_protocol); + if (!ok) { + DBG_ERR("fsavd: PROTOCOL: I/O error: %s\n", strerror(errno)); + goto virusfilter_fsav_init_failed; + } + ret = strncmp(reply, "OK\t", 3); + if (ret != 0) { + DBG_ERR("fsavd: PROTOCOL: Not accepted: %s\n", + reply); + goto virusfilter_fsav_init_failed; + } + + TALLOC_FREE(reply); + + ok = virusfilter_io_writefl_readl(io_h, &reply, + "CONFIGURE\tSTOPONFIRST\t%d", + fsav_config->stop_scan_on_first ? + 1 : 0); + if (!ok) { + DBG_ERR("fsavd: CONFIGURE STOPONFIRST: I/O error: %s\n", + strerror(errno)); + goto virusfilter_fsav_init_failed; + } + ret = strncmp(reply, "OK\t", 3); + if (ret != 0) { + DBG_ERR("fsavd: CONFIGURE STOPONFIRST: Not accepted: %s\n", + reply); + goto virusfilter_fsav_init_failed; + } + + TALLOC_FREE(reply); + + ok = virusfilter_io_writefl_readl(io_h, &reply, "CONFIGURE\tFILTER\t%d", + fsav_config->filter_filename ? 1 : 0); + if (!ok) { + DBG_ERR("fsavd: CONFIGURE FILTER: I/O error: %s\n", + strerror(errno)); + goto virusfilter_fsav_init_failed; + } + ret = strncmp(reply, "OK\t", 3); + if (ret != 0) { + DBG_ERR("fsavd: CONFIGURE FILTER: Not accepted: %s\n", + reply); + goto virusfilter_fsav_init_failed; + } + + TALLOC_FREE(reply); + + ok = virusfilter_io_writefl_readl(io_h, &reply, + "CONFIGURE\tARCHIVE\t%d", + config->scan_archive ? 1 : 0); + if (!ok) { + DBG_ERR("fsavd: CONFIGURE ARCHIVE: I/O error: %s\n", + strerror(errno)); + goto virusfilter_fsav_init_failed; + } + ret = strncmp(reply, "OK\t", 3); + if (ret != 0) { + DBG_ERR("fsavd: CONFIGURE ARCHIVE: Not accepted: %s\n", + reply); + goto virusfilter_fsav_init_failed; + } + + TALLOC_FREE(reply); + + ok = virusfilter_io_writefl_readl(io_h, &reply, + "CONFIGURE\tMAXARCH\t%d", + config->max_nested_scan_archive); + if (!ok) { + DBG_ERR("fsavd: CONFIGURE MAXARCH: I/O error: %s\n", + strerror(errno)); + goto virusfilter_fsav_init_failed; + } + ret = strncmp(reply, "OK\t", 3); + if (ret != 0) { + DBG_ERR("fsavd: CONFIGURE MAXARCH: Not accepted: %s\n", + reply); + goto virusfilter_fsav_init_failed; + } + + TALLOC_FREE(reply); + + ok = virusfilter_io_writefl_readl(io_h, &reply, + "CONFIGURE\tMIME\t%d", + config->scan_mime ? 1 : 0); + if (!ok) { + DBG_ERR("fsavd: CONFIGURE MIME: I/O error: %s\n", + strerror(errno)); + goto virusfilter_fsav_init_failed; + } + ret = strncmp(reply, "OK\t", 3); + if (ret != 0) { + DBG_ERR("fsavd: CONFIGURE MIME: Not accepted: %s\n", + reply); + goto virusfilter_fsav_init_failed; + } + + TALLOC_FREE(reply); + + ok = virusfilter_io_writefl_readl(io_h, &reply, "CONFIGURE\tRISKWARE\t%d", + fsav_config->scan_riskware ? 1 : 0); + if (!ok) { + DBG_ERR("fsavd: CONFIGURE RISKWARE: I/O error: %s\n", + strerror(errno)); + goto virusfilter_fsav_init_failed; + } + ret = strncmp(reply, "OK\t", 3); + if (ret != 0) { + DBG_ERR("fsavd: CONFIGURE RISKWARE: Not accepted: %s\n", + reply); + goto virusfilter_fsav_init_failed; + } + + DBG_DEBUG("fsavd: Configured\n"); + +virusfilter_fsav_init_succeed: + TALLOC_FREE(reply); + return VIRUSFILTER_RESULT_OK; + +virusfilter_fsav_init_failed: + TALLOC_FREE(reply); + virusfilter_fsav_scan_end(config); + + return VIRUSFILTER_RESULT_ERROR; +} + +static void virusfilter_fsav_scan_end(struct virusfilter_config *config) +{ + struct virusfilter_io_handle *io_h = config->io_h; + + DBG_INFO("fsavd: Disconnecting\n"); + virusfilter_io_disconnect(io_h); +} + +static virusfilter_result virusfilter_fsav_scan( + struct vfs_handle_struct *handle, + struct virusfilter_config *config, + const struct files_struct *fsp, + char **reportp) +{ + char *cwd_fname = fsp->conn->cwd_fsp->fsp_name->base_name; + const char *fname = fsp->fsp_name->base_name; + struct virusfilter_io_handle *io_h = config->io_h; + virusfilter_result result = VIRUSFILTER_RESULT_CLEAN; + char *report = NULL; + char *reply = NULL; + char *reply_token = NULL, *reply_saveptr = NULL; + bool ok; + + DBG_INFO("Scanning file: %s/%s\n", cwd_fname, fname); + + ok = virusfilter_io_writevl(io_h, "SCAN\t", 5, cwd_fname, + (int)strlen(cwd_fname), "/", 1, fname, + (int)strlen(fname), NULL); + if (!ok) { + DBG_ERR("fsavd: SCAN: Write error: %s\n", strerror(errno)); + result = VIRUSFILTER_RESULT_ERROR; + report = talloc_asprintf(talloc_tos(), + "Scanner I/O error: %s\n", + strerror(errno)); + goto virusfilter_fsav_scan_return; + } + + TALLOC_FREE(reply); + + for (;;) { + if (virusfilter_io_readl(talloc_tos(), io_h, &reply) != true) { + DBG_ERR("fsavd: SCANFILE: Read error: %s\n", + strerror(errno)); + result = VIRUSFILTER_RESULT_ERROR; + report = talloc_asprintf(talloc_tos(), + "Scanner I/O error: %s\n", + strerror(errno)); + break; + } + + reply_token = strtok_r(reply, "\t", &reply_saveptr); + + if (strcmp(reply_token, "OK") == 0) { + break; + } else if (strcmp(reply_token, "CLEAN") == 0) { + + /* CLEAN\t */ + result = VIRUSFILTER_RESULT_CLEAN; + report = talloc_asprintf(talloc_tos(), "Clean"); + } else if (strcmp(reply_token, "INFECTED") == 0 || + strcmp(reply_token, "ARCHIVE_INFECTED") == 0 || + strcmp(reply_token, "MIME_INFECTED") == 0 || + strcmp(reply_token, "RISKWARE") == 0 || + strcmp(reply_token, "ARCHIVE_RISKWARE") == 0 || + strcmp(reply_token, "MIME_RISKWARE") == 0) + { + + /* INFECTED\t\t\t */ + result = VIRUSFILTER_RESULT_INFECTED; + reply_token = strtok_r(NULL, "\t", &reply_saveptr); + reply_token = strtok_r(NULL, "\t", &reply_saveptr); + if (reply_token != NULL) { + report = talloc_strdup(talloc_tos(), + reply_token); + } else { + report = talloc_asprintf(talloc_tos(), + "UNKNOWN INFECTION"); + } + } else if (strcmp(reply_token, "OPEN_ARCHIVE") == 0) { + + /* Ignore */ + } else if (strcmp(reply_token, "CLOSE_ARCHIVE") == 0) { + + /* Ignore */ + } else if ((strcmp(reply_token, "SUSPECTED") == 0 || + strcmp(reply_token, "ARCHIVE_SUSPECTED") == 0 || + strcmp(reply_token, "MIME_SUSPECTED") == 0) && + config->block_suspected_file) + { + result = VIRUSFILTER_RESULT_SUSPECTED; + reply_token = strtok_r(NULL, "\t", &reply_saveptr); + reply_token = strtok_r(NULL, "\t", &reply_saveptr); + if (reply_token != NULL) { + report = talloc_strdup(talloc_tos(), + reply_token); + } else { + report = talloc_asprintf(talloc_tos(), + "UNKNOWN REASON SUSPECTED"); + } + } else if (strcmp(reply_token, "SCAN_FAILURE") == 0) { + + /* SCAN_FAILURE\t\t0x\t [] */ + result = VIRUSFILTER_RESULT_ERROR; + reply_token = strtok_r(NULL, "\t", &reply_saveptr); + reply_token = strtok_r(NULL, "\t", &reply_saveptr); + DBG_ERR("fsavd: SCANFILE: Scaner error: %s\n", + reply_token ? reply_token : "UNKNOWN ERROR"); + report = talloc_asprintf(talloc_tos(), + "Scanner error: %s", + reply_token ? reply_token : + "UNKNOWN ERROR"); + } else { + result = VIRUSFILTER_RESULT_ERROR; + DBG_ERR("fsavd: SCANFILE: Invalid reply: %s\n", + reply_token); + report = talloc_asprintf(talloc_tos(), + "Scanner communication error"); + } + + TALLOC_FREE(reply); + } + +virusfilter_fsav_scan_return: + TALLOC_FREE(reply); + + if (report == NULL) { + *reportp = talloc_asprintf(talloc_tos(), "Scanner report memory " + "error"); + } else { + *reportp = report; + } + + return result; +} + +static struct virusfilter_backend_fns virusfilter_backend_fsav ={ + .connect = virusfilter_fsav_connect, + .disconnect = NULL, + .scan_init = virusfilter_fsav_scan_init, + .scan = virusfilter_fsav_scan, + .scan_end = virusfilter_fsav_scan_end, +}; + +int virusfilter_fsav_init(struct virusfilter_config *config) +{ + struct virusfilter_backend *backend = NULL; + + if (config->socket_path == NULL) { + config->socket_path = VIRUSFILTER_DEFAULT_SOCKET_PATH; + } + + backend = talloc_zero(config, struct virusfilter_backend); + if (backend == NULL) { + return -1; + } + + backend->fns = &virusfilter_backend_fsav; + backend->name = "fsav"; + + config->backend = backend; + return 0; +} -- cgit v1.2.3