/*
Samba-VirusFilter VFS modules
ClamAV clamd 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 .
*/
/* Default values for standard "extra" configuration variables */
#ifdef CLAMAV_DEFAULT_SOCKET_PATH
# define VIRUSFILTER_DEFAULT_SOCKET_PATH CLAMAV_DEFAULT_SOCKET_PATH
#else
# define VIRUSFILTER_DEFAULT_SOCKET_PATH "/var/run/clamav/clamd.ctl"
#endif
#include "modules/vfs_virusfilter_common.h"
#include "modules/vfs_virusfilter_utils.h"
static int virusfilter_clamav_connect(struct vfs_handle_struct *handle,
struct virusfilter_config *config,
const char *svc,
const char *user)
{
/* To use clamd "zXXXX" commands */
virusfilter_io_set_writel_eol(config->io_h, "\0", 1);
virusfilter_io_set_readl_eol(config->io_h, "\0", 1);
return 0;
}
static virusfilter_result virusfilter_clamav_scan_init(
struct virusfilter_config *config)
{
struct virusfilter_io_handle *io_h = config->io_h;
bool ok;
DBG_INFO("clamd: 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("clamd: Connecting to socket failed: %s: %s\n",
config->socket_path, strerror(errno));
return VIRUSFILTER_RESULT_ERROR;
}
DBG_INFO("clamd: Connected\n");
return VIRUSFILTER_RESULT_OK;
}
static void virusfilter_clamav_scan_end(
struct virusfilter_config *config)
{
struct virusfilter_io_handle *io_h = config->io_h;
DBG_INFO("clamd: Disconnecting\n");
virusfilter_io_disconnect(io_h);
}
static virusfilter_result virusfilter_clamav_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;
size_t filepath_len = strlen(cwd_fname) + 1 /* slash */ + strlen(fname);
struct virusfilter_io_handle *io_h = config->io_h;
virusfilter_result result = VIRUSFILTER_RESULT_CLEAN;
char *report = NULL;
char *reply = NULL;
char *reply_msg = NULL;
char *reply_token;
bool ok;
DBG_INFO("Scanning file: %s/%s\n", cwd_fname, fname);
ok = virusfilter_io_writefl_readl(io_h, &reply, "zSCAN %s/%s",
cwd_fname, fname);
if (!ok) {
DBG_ERR("clamd: zSCAN: I/O error: %s\n", strerror(errno));
result = VIRUSFILTER_RESULT_ERROR;
report = talloc_asprintf(talloc_tos(),
"Scanner I/O error: %s\n",
strerror(errno));
goto virusfilter_clamav_scan_return;
}
if (reply[filepath_len] != ':' ||
reply[filepath_len+1] != ' ')
{
DBG_ERR("clamd: zSCAN: Invalid reply: %s\n",
reply);
result = VIRUSFILTER_RESULT_ERROR;
report = talloc_asprintf(talloc_tos(),
"Scanner communication error");
goto virusfilter_clamav_scan_return;
}
reply_msg = reply + filepath_len + 2;
reply_token = strrchr(reply, ' ');
if (reply_token == NULL) {
DBG_ERR("clamd: zSCAN: Invalid reply: %s\n",
reply);
result = VIRUSFILTER_RESULT_ERROR;
report = talloc_asprintf(talloc_tos(),
"Scanner communication error");
goto virusfilter_clamav_scan_return;
}
*reply_token = '\0';
reply_token++;
if (strcmp(reply_token, "OK") == 0) {
/* : OK */
result = VIRUSFILTER_RESULT_CLEAN;
report = talloc_asprintf(talloc_tos(), "Clean");
} else if (strcmp(reply_token, "FOUND") == 0) {
/* : FOUND */
result = VIRUSFILTER_RESULT_INFECTED;
report = talloc_strdup(talloc_tos(), reply_msg);
} else if (strcmp(reply_token, "ERROR") == 0) {
/* : ERROR */
DBG_ERR("clamd: zSCAN: Error: %s\n", reply_msg);
result = VIRUSFILTER_RESULT_ERROR;
report = talloc_asprintf(talloc_tos(),
"Scanner error: %s\t", reply_msg);
} else {
DBG_ERR("clamd: zSCAN: Invalid reply: %s\n", reply_token);
result = VIRUSFILTER_RESULT_ERROR;
report = talloc_asprintf(talloc_tos(),
"Scanner communication error");
}
virusfilter_clamav_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_clamav = {
.connect = virusfilter_clamav_connect,
.disconnect = NULL,
.scan_init = virusfilter_clamav_scan_init,
.scan = virusfilter_clamav_scan,
.scan_end = virusfilter_clamav_scan_end,
};
int virusfilter_clamav_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_clamav;
backend->name = "clamav";
config->backend = backend;
return 0;
}