1
0
mirror of https://github.com/samba-team/samba.git synced 2025-01-22 22:04:08 +03:00

Samba-VirusFilter: Sophos VFS backend.

Signed-off-by: Trever L. Adams <trever.adams@gmail.com>
Signed-off-by: SATOH Fumiyasu <fumiyas@osstech.co.jp>
Reviewed-by: Jeremy Allison <jra@samba.org>
Reviewed-by: Ralph Boehme <slow@samba.org>
This commit is contained in:
Trever L. Adams 2016-10-18 13:38:14 -06:00 committed by Ralph Boehme
parent b1e69edd05
commit 0b25089edd
5 changed files with 414 additions and 5 deletions

View File

@ -41,6 +41,10 @@
<term>virusfilter:scanner</term>
<listitem>
<para>The antivirus scan-engine.</para>
<itemizedlist>
<listitem><para><emphasis>sophos</emphasis>, the Sophos AV
scanner</para></listitem>
</itemizedlist>
</listitem>
</varlistentry>
@ -52,6 +56,8 @@
<para>If this option is not set, the default path depends on the
configured AV scanning engine.
</para>
<para>For the <emphasis>sophos</emphasis>backend the default is
<emphasis>/var/run/savdi/sssp.sock</emphasis>.</para>
</listitem>
</varlistentry>

View File

@ -441,10 +441,17 @@ static int virusfilter_vfs_connect(
return -1;
}
/* This goes away as soon as the next commit adds an actual backend... */
if (config->backend == NULL) {
DBG_INFO("Not implemented\n");
return SMB_VFS_NEXT_CONNECT(handle, svc, user);
switch (backend) {
case VIRUSFILTER_SCANNER_SOPHOS:
ret = virusfilter_sophos_init(config);
break;
default:
DBG_ERR("Unhandled scanner %d\n", backend);
return -1;
}
if (ret != 0) {
DBG_ERR("Scanner backend init failed\n");
return -1;
}
if (config->backend->fns->connect != NULL) {

View File

@ -146,4 +146,6 @@ struct virusfilter_backend {
void *backend_private;
};
int virusfilter_sophos_init(struct virusfilter_config *config);
#endif /* _VIRUSFILTER_COMMON_H */

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#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_fname->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, *reply_saveptr;
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;
}

View File

@ -512,7 +512,10 @@ bld.SAMBA3_MODULE('vfs_snapper',
bld.SAMBA3_MODULE('vfs_virusfilter',
subsystem='vfs',
source='vfs_virusfilter.c',
source='''
vfs_virusfilter.c
vfs_virusfilter_sophos.c
''',
deps='samba-util VFS_VIRUSFILTER_UTILS',
init_function='',
internal_module=bld.SAMBA3_IS_STATIC_MODULE('vfs_virusfilter'),