summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJeremy Harris <jgh146exb@wizmail.org>2014-12-23 20:16:36 +0000
committerJeremy Harris <jgh146exb@wizmail.org>2015-01-12 18:58:36 +0000
commitb6fbf22d63de88b77d79cd0b1d2337e589cac6d7 (patch)
tree5e5e02a8b30c77b4a228ff6f8ed5e449097bd48a /src
parent168dec3b8f4d729ccb7e56181b8ab4c4956726d1 (diff)
Add support for avast malware scanner. Bug 1033
Originally by Dominic Benson <dominic@lenny.cus.org> Rebased for current malware.c by JGH. Testing by Heiko Schlittermann <hs@schlittermann.de>
Diffstat (limited to 'src')
-rw-r--r--src/src/malware.c131
-rw-r--r--src/src/readconf.c4
-rw-r--r--src/src/spam.c2
3 files changed, 130 insertions, 7 deletions
diff --git a/src/src/malware.c b/src/src/malware.c
index 93bcf8667..167f47f2c 100644
--- a/src/src/malware.c
+++ b/src/src/malware.c
@@ -11,7 +11,7 @@
#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
{
@@ -31,6 +31,7 @@ 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 */
};
@@ -1527,7 +1528,128 @@ malware_internal(uschar **listptr, uschar *eml_filename, BOOL faking)
}
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);
@@ -1535,10 +1657,11 @@ malware_internal(uschar **listptr, uschar *eml_filename, BOOL faking)
}
/* 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;
}
diff --git a/src/src/readconf.c b/src/src/readconf.c
index 0b3778054..687b35223 100644
--- a/src/src/readconf.c
+++ b/src/src/readconf.c
@@ -3009,9 +3009,9 @@ if (config_file != NULL)
uschar *p;
config_filename = config_main_filename = string_copy(filename);
- p = strrchr(filename, '/');
+ p = Ustrrchr(filename, '/');
config_main_directory = p ? string_copyn(filename, p - filename)
- : string_copy(".");
+ : string_copy(US".");
}
else
{
diff --git a/src/src/spam.c b/src/src/spam.c
index 45a06931c..c0c3fb373 100644
--- a/src/src/spam.c
+++ b/src/src/spam.c
@@ -48,7 +48,7 @@ spam(uschar **listptr)
fd_set select_fd;
#endif
uschar *spamd_address_work;
- static const char * loglabel = US"spam acl condition:";
+ static const uschar * loglabel = US"spam acl condition:";
/* stop compiler warning */
result = 0;