#ifdef WITH_CONTENT_SCAN
typedef enum {M_FPROTD, M_DRWEB, M_AVES, M_FSEC, M_KAVD, M_CMDL,
- M_SOPHIE, M_CLAMD, M_SOCK, M_MKSD} scanner_t;
+ M_SOPHIE, M_CLAMD, M_SOCK, M_MKSD, M_AVAST} scanner_t;
typedef enum {MC_NONE, MC_TCP, MC_UNIX, MC_STRM} contype_t;
static struct scan
{
{ M_CLAMD, US"clamd", US"/tmp/clamd", MC_NONE },
{ M_SOCK, US"sock", US"/tmp/malware.sock", MC_STRM },
{ M_MKSD, US"mksd", NULL, MC_NONE },
+ { M_AVAST, US"avast", US"/var/run/avast/scan.sock", MC_STRM },
{ -1, NULL, NULL, MC_NONE } /* end-marker */
};
#define MAX_CLAMD_ADDRESS_LENGTH_S "64"
typedef struct clamd_address_container {
- uschar tcp_addr[MAX_CLAMD_ADDRESS_LENGTH];
+ uschar tcp_addr[MAX_CLAMD_ADDRESS_LENGTH+1];
unsigned int tcp_port;
} clamd_address_container;
case MC_TCP: sock = m_tcpsocket_fromdef(scanner_options, &errstr); break;
case MC_UNIX: sock = m_unixsocket(scanner_options, &errstr); break;
case MC_STRM: sock = m_streamsocket(scanner_options, &errstr); break;
+ default: /* compiler quietening */ break;
}
if (sock < 0)
return m_errlog_defer(scanent, errstr);
/* v0.0 - initial release -- support for unix sockets */
{
int result;
- unsigned int fsize;
+ off_t fsize;
+ unsigned int fsize_uint;
uschar * tmpbuf, *drweb_fbuf;
int drweb_rc, drweb_cmd, drweb_flags = 0x0000, drweb_fd,
drweb_vnum, drweb_slen, drweb_fin = 0x0000;
eml_filename, strerror(err)),
sock);
}
+ fsize_uint = (unsigned int) fsize;
+ if ((off_t)fsize_uint != fsize) {
+ (void)close(drweb_fd);
+ return m_errlog_defer_3(scanent,
+ string_sprintf("seeking spool file %s, size overflow",
+ eml_filename),
+ sock);
+ }
drweb_slen = htonl(fsize);
lseek(drweb_fd, 0, SEEK_SET);
sock);
}
- if (!(drweb_fbuf = (uschar *) malloc (fsize))) {
+ if (!(drweb_fbuf = (uschar *) malloc (fsize_uint))) {
(void)close(drweb_fd);
return m_errlog_defer_3(scanent,
string_sprintf("unable to allocate memory %u for file (%s)",
- fsize, eml_filename),
+ fsize_uint, eml_filename),
sock);
}
sep = pclose(scanner_out);
signal(SIGCHLD,eximsigchld); signal(SIGPIPE,eximsigpipe);
if (sep != 0)
- if (sep == -1)
- return m_errlog_defer(scanent,
- string_sprintf("running scanner failed: %s", strerror(sep)));
- else
return m_errlog_defer(scanent,
- string_sprintf("scanner returned error code: %d", sep));
+ sep == -1
+ ? string_sprintf("running scanner failed: %s", strerror(sep))
+ : string_sprintf("scanner returned error code: %d", sep));
if (trigger) {
uschar * s;
uschar *p, *vname, *result_tag, *response_end;
int bread=0;
- unsigned int port;
uschar * file_name;
uschar av_buffer[1024];
uschar *hostname = US"";
host_item connhost;
uschar *clamav_fbuf;
int clam_fd, result;
- unsigned int fsize;
+ off_t fsize;
+ unsigned int fsize_uint;
BOOL use_scan_command = FALSE;
clamd_address_container * clamd_address_vector[MAX_CLAMD_SERVERS];
int current_server;
int num_servers = 0;
#ifdef WITH_OLD_CLAMAV_STREAM
+ unsigned int port;
uschar av_buffer2[1024];
int sockData;
#else
eml_filename, strerror(err)),
sock);
}
+ fsize_uint = (unsigned int) fsize;
+ if ((off_t)fsize_uint != fsize) {
+ CLOSE_SOCKDATA; (void)close(clam_fd);
+ return m_errlog_defer_3(scanent,
+ string_sprintf("seeking spool file %s, size overflow",
+ eml_filename),
+ sock);
+ }
lseek(clam_fd, 0, SEEK_SET);
- if (!(clamav_fbuf = (uschar *) malloc (fsize))) {
+ if (!(clamav_fbuf = (uschar *) malloc (fsize_uint))) {
CLOSE_SOCKDATA; (void)close(clam_fd);
return m_errlog_defer_3(scanent,
string_sprintf("unable to allocate memory %u for file (%s)",
- fsize, eml_filename),
+ fsize_uint, eml_filename),
sock);
}
- if ((result = read(clam_fd, clamav_fbuf, fsize)) < 0) {
+ if ((result = read(clam_fd, clamav_fbuf, fsize_uint)) < 0) {
int err = errno;
free(clamav_fbuf); CLOSE_SOCKDATA; (void)close(clam_fd);
return m_errlog_defer_3(scanent,
/* send file body to socket */
#ifdef WITH_OLD_CLAMAV_STREAM
- if (send(sockData, clamav_fbuf, fsize, 0) < 0) {
+ if (send(sockData, clamav_fbuf, fsize_uint, 0) < 0) {
free(clamav_fbuf); CLOSE_SOCKDATA;
return m_errlog_defer_3(scanent,
string_sprintf("unable to send file body to socket (%s:%u)",
sock);
}
#else
- send_size = htonl(fsize);
+ send_size = htonl(fsize_uint);
send_final_zeroblock = 0;
if ((send(sock, &send_size, sizeof(send_size), 0) < 0) ||
- (send(sock, clamav_fbuf, fsize, 0) < 0) ||
+ (send(sock, clamav_fbuf, fsize_uint, 0) < 0) ||
(send(sock, &send_final_zeroblock, sizeof(send_final_zeroblock), 0) < 0))
{
free(clamav_fbuf);
return m_errlog_defer_3(scanent,
- string_sprintf("unable to send file body to socket (%s:%u)",
- hostname, port),
+ string_sprintf("unable to send file body to socket (%s)", hostname),
sock);
}
#endif
if ((sockline_scanner = string_nextinlist(&av_scanner_work, &sep,
NULL, 0)))
{ /* check for no expansions apart from one %s */
- char * s = index(CS sockline_scanner, '%');
+ uschar * s = Ustrchr(sockline_scanner, '%');
if (s++)
- if ((*s != 's' && *s != '%') || index(s+1, '%'))
+ if ((*s != 's' && *s != '%') || Ustrchr(s+1, '%'))
return m_errlog_defer_3(scanent,
US"unsafe sock scanner call spec", sock);
}
}
break;
}
- }
+ case M_AVAST: /* "avast" scanner type ----------------------------------- */
+ {
+ int ovector[1*3];
+ uschar buf[1024];
+ uschar * scanrequest;
+ const pcre * avast_clean_re, * avast_virus_re;
+ enum {AVA_HELO, AVA_OPT, AVA_RSP, AVA_DONE} avast_stage;
+
+ /* According to Martin Tuma @avast the protocol uses "escaped
+ whitespace", that is, every embedded whitespace is backslash
+ escaped, as well as backslash is protected by backslash.
+ The returned lines contain the name of the scanned file, a tab
+ and the [ ] marker.
+ [+] - not infected
+ [L] - infected
+ [E] - some error occured
+ Such marker follows the first non-escaped TAB. */
+ if ( !(avast_clean_re =
+ m_pcre_compile(US"(?!\\\\)\\t\\[\\+\\]", &errstr))
+ || !(avast_virus_re =
+ m_pcre_compile(US"(?!\\\\)\\t\\[L\\]\\d\\.\\d\\t\\d\\s(.*)",
+ &errstr))
+ )
+ return malware_errlog_defer(errstr);
+
+ /* wait for result */
+ for (avast_stage = AVA_HELO; recv_line(sock, buf, sizeof(buf)) > 0; )
+ {
+ int slen = Ustrlen(buf);
+ if (slen >= 1)
+ {
+ DEBUG(D_acl) debug_printf("got from avast: %s\n", buf);
+ switch (avast_stage)
+ {
+ case AVA_HELO:
+ if (Ustrncmp(buf, "220", 3) != 0)
+ goto endloop; /* require a 220 */
+ goto sendreq;
+
+ case AVA_OPT:
+ if (Ustrncmp(buf, "210", 3) == 0)
+ break; /* ignore 210 responses */
+ if (Ustrncmp(buf, "200", 3) != 0)
+ goto endloop; /* require a 200 */
+
+ sendreq:
+ {
+ int len;
+ /* Check for another option to send. Newline-terminate it. */
+ if ((scanrequest = string_nextinlist(&av_scanner_work, &sep,
+ NULL, 0)))
+ {
+ scanrequest = string_sprintf("%s\n", scanrequest);
+ avast_stage = AVA_OPT; /* just sent option */
+ }
+ else
+ {
+ scanrequest = string_sprintf("SCAN %s/scan/%s\n",
+ spool_directory, message_id);
+ avast_stage = AVA_RSP; /* just sent command */
+ }
+
+ /* send config-cmd or scan-request to socket */
+ len = Ustrlen(scanrequest);
+ if (send(sock, scanrequest, len, 0) < 0)
+ {
+ scanrequest[len-1] = '\0';
+ return m_errlog_defer_3(scanent, string_sprintf(
+ "unable to send request '%s' to socket (%s): %s",
+ scanrequest, scanner_options, strerror(errno)), sock);
+ }
+ break;
+ }
+
+ case AVA_RSP:
+ if (Ustrncmp(buf, "210", 3) == 0)
+ break; /* ignore the "210 SCAN DATA" message */
+
+ if (pcre_exec(avast_clean_re, NULL, CS buf, slen,
+ 0, 0, ovector, nelements(ovector)) > 0)
+ break;
+
+ if ((malware_name = m_pcre_exec(avast_virus_re, buf)))
+ { /* remove backslash in front of [whitespace|backslash] */
+ uschar * p, * p0;
+ for (p = malware_name; *p; ++p)
+ if (*p == '\\' && (isspace(p[1]) || p[1] == '\\'))
+ for (p0 = p; *p0; ++p0) *p0 = p0[1];
+
+ avast_stage = AVA_DONE;
+ goto endloop;
+ }
+
+ if (Ustrncmp(buf, "200 SCAN OK", 11) == 0)
+ { /* we're done finally */
+ if (send(sock, "QUIT\n", 5, 0) < 0) /* courtesy */
+ return m_errlog_defer_3(scanent, string_sprintf(
+ "unable to send quit request to socket (%s): %s",
+ scanner_options, strerror(errno)),
+ sock);
+ malware_name = NULL;
+ avast_stage = AVA_DONE;
+ goto endloop;
+ }
+
+ /* here for any unexpected response from the scanner */
+ goto endloop;
+ }
+ }
+ }
+ endloop:
+
+ switch(avast_stage)
+ {
+ case AVA_HELO:
+ case AVA_OPT:
+ case AVA_RSP: return m_errlog_defer_3(scanent, string_sprintf(
+ "invalid response from scanner: %s\n", buf), sock);
+ default: break;
+ }
+ }
+ } /* scanner type switch */
if (sock >= 0)
(void) close (sock);
}
/* match virus name against pattern (caseless ------->----------v) */
- if ( malware_name && (regex_match_and_setup(re, malware_name, 0, -1)) ) {
+ if (malware_name && regex_match_and_setup(re, malware_name, 0, -1))
+ {
DEBUG(D_acl) debug_printf("Matched regex to malware [%s] [%s]\n", malware_regex, malware_name);
return OK;
- }
+ }
else
return FAIL;
}