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_sophos.c | 391 +++++++++++++++++++++++++++++++ 1 file changed, 391 insertions(+) create mode 100644 source3/modules/vfs_virusfilter_sophos.c (limited to 'source3/modules/vfs_virusfilter_sophos.c') diff --git a/source3/modules/vfs_virusfilter_sophos.c b/source3/modules/vfs_virusfilter_sophos.c new file mode 100644 index 0000000..c8cdec5 --- /dev/null +++ b/source3/modules/vfs_virusfilter_sophos.c @@ -0,0 +1,391 @@ +/* + Samba-VirusFilter VFS modules + Sophos Anti-Virus savdid (SSSP/1.0) 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" + +/* Default values for standard "extra" configuration variables */ +#ifdef SOPHOS_DEFAULT_SOCKET_PATH +# define VIRUSFILTER_DEFAULT_SOCKET_PATH SOPHOS_DEFAULT_SOCKET_PATH +#else +# define VIRUSFILTER_DEFAULT_SOCKET_PATH "/var/run/savdi/sssp.sock" +#endif + +static void virusfilter_sophos_scan_end(struct virusfilter_config *config); + +/* Python's urllib.quote(string[, safe]) clone */ +static int virusfilter_url_quote(const char *src, char *dst, int dst_size) +{ + char *dst_c = dst; + static char hex[] = "0123456789ABCDEF"; + + for (; *src != '\0'; src++) { + if ((*src < '0' && *src != '-' && *src != '.' && *src != '/') || + (*src > '9' && *src < 'A') || + (*src > 'Z' && *src < 'a' && *src != '_') || + (*src > 'z')) + { + if (dst_size < 4) { + return -1; + } + *dst_c++ = '%'; + *dst_c++ = hex[(*src >> 4) & 0x0F]; + *dst_c++ = hex[*src & 0x0F]; + dst_size -= 3; + } else { + if (dst_size < 2) { + return -1; + } + *dst_c++ = *src; + dst_size--; + } + } + + *dst_c = '\0'; + + return (dst_c - dst); +} + +static int virusfilter_sophos_connect( + struct vfs_handle_struct *handle, + struct virusfilter_config *config, + const char *svc, + const char *user) +{ + virusfilter_io_set_readl_eol(config->io_h, "\x0D\x0A", 2); + + return 0; +} + +static virusfilter_result virusfilter_sophos_scan_ping( + struct virusfilter_config *config) +{ + struct virusfilter_io_handle *io_h = config->io_h; + char *reply = NULL; + bool ok; + int ret; + + /* SSSP/1.0 has no "PING" command */ + ok = virusfilter_io_writel(io_h, "SSSP/1.0 OPTIONS\n", 17); + if (!ok) { + return VIRUSFILTER_RESULT_ERROR; + } + + for (;;) { + ok = virusfilter_io_readl(talloc_tos(), io_h, &reply); + if (!ok) { + return VIRUSFILTER_RESULT_ERROR; + } + ret = strcmp(reply, ""); + if (ret == 0) { + break; + } + TALLOC_FREE(reply); + } + + TALLOC_FREE(reply); + return VIRUSFILTER_RESULT_OK; +} + +static virusfilter_result virusfilter_sophos_scan_init( + struct virusfilter_config *config) +{ + struct virusfilter_io_handle *io_h = config->io_h; + char *reply = NULL; + int ret; + bool ok; + + if (io_h->stream != NULL) { + DBG_DEBUG("SSSP: Checking if connection is alive\n"); + + ret = virusfilter_sophos_scan_ping(config); + if (ret == VIRUSFILTER_RESULT_OK) + { + DBG_DEBUG("SSSP: Re-using existent connection\n"); + return VIRUSFILTER_RESULT_OK; + } + + DBG_INFO("SSSP: Closing dead connection\n"); + virusfilter_sophos_scan_end(config); + } + + + DBG_INFO("SSSP: 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("SSSP: Connecting to socket failed: %s: %s\n", + config->socket_path, strerror(errno)); + return VIRUSFILTER_RESULT_ERROR; + } + + ok = virusfilter_io_readl(talloc_tos(), io_h, &reply); + if (!ok) { + DBG_ERR("SSSP: Reading greeting message failed: %s\n", + strerror(errno)); + goto virusfilter_sophos_scan_init_failed; + } + ret = strncmp(reply, "OK SSSP/1.0", 11); + if (ret != 0) { + DBG_ERR("SSSP: Invalid greeting message: %s\n", + reply); + goto virusfilter_sophos_scan_init_failed; + } + + DBG_DEBUG("SSSP: Connected\n"); + + DBG_INFO("SSSP: Configuring\n"); + + TALLOC_FREE(reply); + + ok = virusfilter_io_writefl_readl(io_h, &reply, + "SSSP/1.0 OPTIONS\noutput:brief\nsavigrp:GrpArchiveUnpack %d\n", + config->scan_archive ? 1 : 0); + if (!ok) { + DBG_ERR("SSSP: OPTIONS: I/O error: %s\n", strerror(errno)); + goto virusfilter_sophos_scan_init_failed; + } + ret = strncmp(reply, "ACC ", 4); + if (ret != 0) { + DBG_ERR("SSSP: OPTIONS: Not accepted: %s\n", reply); + goto virusfilter_sophos_scan_init_failed; + } + + TALLOC_FREE(reply); + + ok = virusfilter_io_readl(talloc_tos(), io_h, &reply); + if (!ok) { + DBG_ERR("SSSP: OPTIONS: Read error: %s\n", strerror(errno)); + goto virusfilter_sophos_scan_init_failed; + } + ret = strncmp(reply, "DONE OK ", 8); + if (ret != 0) { + DBG_ERR("SSSP: OPTIONS failed: %s\n", reply); + goto virusfilter_sophos_scan_init_failed; + } + + TALLOC_FREE(reply); + + ok = virusfilter_io_readl(talloc_tos(), io_h, &reply); + if (!ok) { + DBG_ERR("SSSP: OPTIONS: Read error: %s\n", strerror(errno)); + goto virusfilter_sophos_scan_init_failed; + } + ret = strcmp(reply, ""); + if (ret != 0) { + DBG_ERR("SSSP: OPTIONS: Invalid reply: %s\n", reply); + goto virusfilter_sophos_scan_init_failed; + } + + DBG_DEBUG("SSSP: Configured\n"); + + return VIRUSFILTER_RESULT_OK; + +virusfilter_sophos_scan_init_failed: + + TALLOC_FREE(reply); + + virusfilter_sophos_scan_end(config); + + return VIRUSFILTER_RESULT_ERROR; +} + +static void virusfilter_sophos_scan_end( + struct virusfilter_config *config) +{ + struct virusfilter_io_handle *io_h = config->io_h; + + DBG_INFO("SSSP: Disconnecting\n"); + + virusfilter_io_disconnect(io_h); +} + +static virusfilter_result virusfilter_sophos_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; + char fileurl[VIRUSFILTER_IO_URL_MAX+1]; + int fileurl_len, fileurl_len2; + struct virusfilter_io_handle *io_h = config->io_h; + virusfilter_result result = VIRUSFILTER_RESULT_ERROR; + char *report = NULL; + char *reply = NULL; + char *reply_token = NULL, *reply_saveptr = NULL; + int ret; + bool ok; + + DBG_INFO("Scanning file: %s/%s\n", cwd_fname, fname); + + fileurl_len = virusfilter_url_quote(cwd_fname, fileurl, + VIRUSFILTER_IO_URL_MAX); + if (fileurl_len < 0) { + DBG_ERR("virusfilter_url_quote failed: File path too long: " + "%s/%s\n", cwd_fname, fname); + result = VIRUSFILTER_RESULT_ERROR; + report = talloc_asprintf(talloc_tos(), "File path too long"); + goto virusfilter_sophos_scan_return; + } + fileurl[fileurl_len] = '/'; + fileurl_len++; + + fileurl_len += fileurl_len2 = virusfilter_url_quote(fname, + fileurl + fileurl_len, VIRUSFILTER_IO_URL_MAX - fileurl_len); + if (fileurl_len2 < 0) { + DBG_ERR("virusfilter_url_quote failed: File path too long: " + "%s/%s\n", cwd_fname, fname); + result = VIRUSFILTER_RESULT_ERROR; + report = talloc_asprintf(talloc_tos(), "File path too long"); + goto virusfilter_sophos_scan_return; + } + fileurl_len += fileurl_len2; + + ok = virusfilter_io_writevl(io_h, "SSSP/1.0 SCANFILE ", 18, fileurl, + fileurl_len, NULL); + if (!ok) { + DBG_ERR("SSSP: SCANFILE: Write error: %s\n", + strerror(errno)); + goto virusfilter_sophos_scan_io_error; + } + + ok = virusfilter_io_readl(talloc_tos(), io_h, &reply); + if (!ok) { + DBG_ERR("SSSP: SCANFILE: Read error: %s\n", strerror(errno)); + goto virusfilter_sophos_scan_io_error; + } + ret = strncmp(reply, "ACC ", 4); + if (ret != 0) { + DBG_ERR("SSSP: SCANFILE: Not accepted: %s\n", + reply); + result = VIRUSFILTER_RESULT_ERROR; + goto virusfilter_sophos_scan_return; + } + + TALLOC_FREE(reply); + + result = VIRUSFILTER_RESULT_CLEAN; + for (;;) { + ok = virusfilter_io_readl(talloc_tos(), io_h, &reply); + if (!ok) { + DBG_ERR("SSSP: SCANFILE: Read error: %s\n", + strerror(errno)); + goto virusfilter_sophos_scan_io_error; + } + + ret = strcmp(reply, ""); + if (ret == 0) { + break; + } + + reply_token = strtok_r(reply, " ", &reply_saveptr); + + if (strcmp(reply_token, "VIRUS") == 0) { + result = VIRUSFILTER_RESULT_INFECTED; + reply_token = strtok_r(NULL, " ", &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, "OK") == 0) { + + /* Ignore */ + } else if (strcmp(reply_token, "DONE") == 0) { + reply_token = strtok_r(NULL, "", &reply_saveptr); + if (reply_token != NULL && + + /* Succeed */ + strncmp(reply_token, "OK 0000 ", 8) != 0 && + + /* Infected */ + strncmp(reply_token, "OK 0203 ", 8) != 0) + { + DBG_ERR("SSSP: SCANFILE: Error: %s\n", + reply_token); + result = VIRUSFILTER_RESULT_ERROR; + report = talloc_asprintf(talloc_tos(), + "Scanner error: %s\n", + reply_token); + } + } else { + DBG_ERR("SSSP: SCANFILE: Invalid reply: %s\n", + reply_token); + result = VIRUSFILTER_RESULT_ERROR; + report = talloc_asprintf(talloc_tos(), "Scanner " + "communication error"); + } + + TALLOC_FREE(reply); + } + +virusfilter_sophos_scan_return: + TALLOC_FREE(reply); + + if (report == NULL) { + *reportp = talloc_asprintf(talloc_tos(), + "Scanner report memory error"); + } else { + *reportp = report; + } + + return result; + +virusfilter_sophos_scan_io_error: + *reportp = talloc_asprintf(talloc_tos(), + "Scanner I/O error: %s\n", strerror(errno)); + + return result; +} + +static struct virusfilter_backend_fns virusfilter_backend_sophos ={ + .connect = virusfilter_sophos_connect, + .disconnect = NULL, + .scan_init = virusfilter_sophos_scan_init, + .scan = virusfilter_sophos_scan, + .scan_end = virusfilter_sophos_scan_end, +}; + +int virusfilter_sophos_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_sophos; + backend->name = "sophos"; + + config->backend = backend; + return 0; +} -- cgit v1.2.3