Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
110 changes: 99 additions & 11 deletions services/virus_scan/clamd_mod.c
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ int CLAMD_SESSION_REUSE = 100;
int CLAMD_SESSION_TIMEOUT = 10; /*seconds*/
int CLAMD_MAX_CONNECTIONS = -1;
char CLAMD_ADDR[CI_MAX_PATH];
char *CLAMD_COMMAND = "zSCAN";

static struct ci_conf_entry clamd_conf_variables[] = {
#ifdef HAVE_FD_PASSING
Expand All @@ -38,6 +39,7 @@ static struct ci_conf_entry clamd_conf_variables[] = {
{"SessionReuse", &CLAMD_SESSION_REUSE, ci_cfg_set_int, NULL},
{"SessionTimeout", &CLAMD_SESSION_TIMEOUT, ci_cfg_set_int, NULL},
{"MaxConnections", &CLAMD_MAX_CONNECTIONS, ci_cfg_set_int, NULL},
{"ClamdCommand", &CLAMD_COMMAND, ci_cfg_set_str, NULL},
{NULL, NULL, NULL, NULL}
};

Expand Down Expand Up @@ -411,34 +413,114 @@ static const char *clamd_response(struct clamd_conn *conn, char *buf, size_t siz
return buf;
}

static int send_filename(struct clamd_conn *conn, const char *filename)
static int send_filename_scan(struct clamd_conn *conn, const char *filename)
{
int len;
char buf[CI_MAX_PATH];

if (!conn || conn->sockd < 0)
len = snprintf(buf, sizeof(buf), "zSCAN %s", filename);
if (len >= sizeof(buf)) {
ci_debug_printf(1, "Too long filename: %s\n", filename);
return 0;
}

ci_debug_printf(5, "send_filename_scan: Send '%s' to clamd (len=%d)\n", buf, len);
if (clamd_command(conn, buf, len + 1) <= 0) {
return 0;
}
return 1;
}

static int send_filename_instream_filesize(const char *filename, unsigned int *len)
{
FILE *fp;

fp = fopen(filename, "r");
if (!fp) {
ci_debug_printf(1, "Failed open file: '%s'\n", filename);
return -1;
}
fseek(fp, 0L, SEEK_END);
*len = htonl(ftell(fp));
fclose(fp);
return 1;
}

if (! filename) {
ci_debug_printf(1, "send_filename: Filename to be sent to clamd cannot be NULL!\n");
/**
* Send: zINSTREAM\0<length><data>\0\0\0\0
*/
static int send_filename_instream(struct clamd_conn *conn, const char *filename)
{
int fd;
int len;
unsigned int len_file;
char buf[CI_MAX_PATH];

if (send_filename_instream_filesize(filename, &len_file) < 0) {
return -1;
}

len = snprintf(buf, sizeof(buf), "zINSTREAM");
ci_debug_printf(5, "send_filename_instream: Send zINSTREAM to clamd\n");
if (clamd_command(conn, buf, len + 1) <= 0) {
ci_debug_printf(1, "Failed send zINSTREAM to clamd\n");
return 0;
}
ci_debug_printf(5, "send_filename: File '%s' should be scanned.\n", filename);

len = snprintf(buf, sizeof(buf), "zSCAN %s", filename);
if (len >= sizeof(buf)) {
ci_debug_printf(1, "Too long filename: %s\n", filename);
memcpy(buf, &len_file, sizeof(len_file));
ci_debug_printf(5, "send_filename_instream: Send file size to clamd\n");
if (clamd_command(conn, buf, sizeof(len_file)) <= 0) {
return 0;
}

ci_debug_printf(5, "send_filename: Send '%s' to clamd (len=%d)\n", buf, len);
if (clamd_command(conn, buf, len + 1) <= 0) {
fd = open(filename, O_RDONLY);
if (fd == -1) {
ci_debug_printf(1, "Failed open file: '%s'\n", filename);
return 0;
}

while ((len = read(fd, buf, CI_MAX_PATH - 1)) > 0) {
ci_debug_printf(5, "send_filename_instream: Send %d/%d byte to clamd\n", len, ntohl(len_file));
if (clamd_command(conn, buf, len) <= 0) {
close(fd);
ci_debug_printf(1, "Failed send %d/%d byte to clamd\n", len, ntohl(len_file));
return 0;
}
}
close(fd);

ci_debug_printf(5, "send_filename_instream: INSTREAM close\n");
memset(buf, 0, 4);
if (clamd_command(conn, buf, 4) <= 0) {
ci_debug_printf(1, "Failed INSTREAM close\n");
return 0;
}
return 1;
}

/**
* return -1 internal error, 0 clamd error, >0 ok
*/
static int send_filename(struct clamd_conn *conn, const char *filename)
{
if (!conn || conn->sockd < 0)
return -1;

if (! filename) {
ci_debug_printf(1, "send_filename: Filename to be sent to clamd cannot be NULL!\n");
return -1;
}
ci_debug_printf(5, "send_filename: File '%s' should be scanned.\n", filename);

if (!strcmp(CLAMD_COMMAND, "zSCAN")) {
return send_filename_scan(conn, filename);
} else if (!strcmp(CLAMD_COMMAND, "zINSTREAM")) {
return send_filename_instream(conn, filename);
}
ci_debug_printf(1, "Command not recongnized: '%s'!\n", CLAMD_COMMAND);
return 0;
}

#ifdef HAVE_FD_PASSING
static int send_fd(struct clamd_conn *conn, int fd)
{
Expand Down Expand Up @@ -645,9 +727,15 @@ static int clamd_scan_simple_file(ci_simple_file_t *body, av_virus_info_t *vinfo
filename = body->filename;
ci_debug_printf(5, "clamd_scan: Scan file '%s'\n", body->filename);
ret = send_filename(&conn, filename);
if (ret < 0) {
ci_debug_printf(1, "clamd_scan: Error internal!\n");
clamd_release_connection(&conn, 1);
sinfo->err = "Undefined";
return 0;
}
}
const char *response = clamd_response(&conn, buffer, sizeof(buffer));
if (ret < 0) {
if (!response) {
ci_debug_printf(1, "clamd_scan: Error reading response from clamd server!\n");
clamd_release_connection(&conn, 1);
sinfo->err = "Clamd response failed";
Expand Down
8 changes: 8 additions & 0 deletions services/virus_scan/clamd_mod.conf
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,14 @@ Module common clamd_mod.so
# Default:
# clamd_mod.ClamdPort None, ClamdSocket is used.

# TAG: clamd_mod.ClamdCommand
# Format: clamd_mod.ClamdCommand command
# Description:
# The command to be used to dialogue to Clamd if a ClamdPort is specified.
# Accept value: zINSTREAM, zSCAN
# Default:
# clamd_mod.ClamdCommand zSCAN

# TAG: clamd_mod.ReportVirusOnFailure
# Format: clamd_mod.ReportVirusOnFailure on|off
# Description:
Expand Down