Update to protocol used by avast 2.2.0 (Multiline responses) (Bug 2112)
authorHeiko Schlittermann (HS12-RIPE) <hs@schlittermann.de>
Sat, 10 Mar 2018 03:30:56 +0000 (04:30 +0100)
committerHeiko Schlittermann (HS12-RIPE) <hs@schlittermann.de>
Mon, 12 Mar 2018 19:39:33 +0000 (20:39 +0100)
Based on a patch by Victor Ustugov.

doc/doc-docbook/spec.xfpt
doc/doc-txt/ChangeLog
src/src/malware.c
test/confs/4017 [new symlink]
test/log/4017 [new file with mode: 0644]
test/paniclog/4017 [new file with mode: 0644]
test/rejectlog/4017 [new file with mode: 0644]
test/scripts/4017_scan_avast_multiline/4017 [new file with mode: 0644]
test/scripts/4017_scan_avast_multiline/REQUIRES [new file with mode: 0644]
test/stderr/4017 [new file with mode: 0644]
test/stdout/4017 [new file with mode: 0644]

index f950a4d..cf80e92 100644 (file)
@@ -31806,9 +31806,9 @@ though individual ones can be included or not at build time:
 .vitem &%avast%&
 .cindex "virus scanners" "avast"
 This is the scanner daemon of Avast. It has been tested with Avast Core
-Security (currently at version 1.1.7).
-You can get a trial version at &url(http://www.avast.com) or for Linux
-at &url(http://www.avast.com/linux-server-antivirus).
+Security (currently at version 2.2.0).
+You can get a trial version at &url(https://www.avast.com) or for Linux
+at &url(https://www.avast.com/linux-server-antivirus).
 This scanner type takes one option,
 which can be either a full path to a UNIX socket,
 or host and port specifiers separated by white space.
@@ -31835,6 +31835,8 @@ $ socat UNIX:/var/run/avast/scan.sock STDIO:
     PACK
 .endd
 
+Only the first virus detected will be reported.
+
 
 .vitem &%aveserver%&
 .cindex "virus scanners" "Kaspersky"
index e4ad9f0..786df05 100644 (file)
@@ -138,6 +138,9 @@ JH/26 Bug 2253: For logging delivery lines under PRDR, append the overall
 JH/27 Bug 2251: Fix ldap lookups that return a single attribute having zero-
       length value.  Previously this would segfault.
 
+HS/02 Support Avast multiline protoocol, this allows passing flags to
+      newer versions of the scanner.
+
 
 Exim version 4.90
 -----------------
index 5b9d5ad..948aede 100644 (file)
@@ -1893,6 +1893,7 @@ b_seek:   err = errno;
       uschar * scanrequest;
       enum {AVA_HELO, AVA_OPT, AVA_RSP, AVA_DONE} avast_stage;
       int nread;
+      int more_data;
 
       /* According to Martin Tuma @avast the protocol uses "escaped
       whitespace", that is, every embedded whitespace is backslash
@@ -1902,7 +1903,9 @@ b_seek:   err = errno;
       [+] - not infected
       [L] - infected
       [E] - some error occured
-      Such marker follows the first non-escaped TAB.  */
+      Such marker follows the first non-escaped TAB.  For more information
+      see avast-protocol(5)
+      */
       if (  (  !ava_re_clean
             && !(ava_re_clean = m_pcre_compile(ava_re_clean_str, &errstr)))
         || (  !ava_re_virus
@@ -1918,17 +1921,25 @@ b_seek:   err = errno;
        int slen = Ustrlen(buf);
        if (slen >= 1)
          {
-         DEBUG(D_acl) debug_printf_indent("got from avast: %s\n", buf);
+
+          /* Multi line responses are bracketed between 210 … and nnn … */
+          if (Ustrncmp(buf, "210", 3) == 0)
+            {
+            more_data = 1;
+            continue;
+            }
+          else if (more_data && isdigit(buf[0])) more_data = 0;
+
          switch (avast_stage)
            {
            case AVA_HELO:
+              if (more_data) continue;
              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 (more_data) continue;
              if (Ustrncmp(buf, "200", 3) != 0)
                goto endloop;                   /* require a 200 */
 
@@ -1941,11 +1952,13 @@ b_seek:   err = errno;
                {
                scanrequest = string_sprintf("%s\n", scanrequest);
                avast_stage = AVA_OPT;          /* just sent option */
+               DEBUG(D_acl) debug_printf_indent("send to avast OPTION: %s", scanrequest);
                }
              else
                {
                scanrequest = string_sprintf("SCAN %s\n", eml_dir);
                avast_stage = AVA_RSP;          /* just sent command */
+               DEBUG(D_acl) debug_printf_indent("send to avast REQUEST: SCAN %s\n", eml_dir);
                }
 
              /* send config-cmd or scan-request to socket */
@@ -1961,15 +1974,27 @@ b_seek:   err = errno;
              }
 
            case AVA_RSP:
-             if (Ustrncmp(buf, "210", 3) == 0)
-               break;  /* ignore the "210 SCAN DATA" message */
+
+             if (Ustrncmp(buf, "200", 3) == 0)
+               { /* we're done finally */
+               if (send(sock, "QUIT\n", 5, 0) < 0) /* courtesy */
+                 return m_errlog_defer_3(scanent, CUS callout_address,
+                         string_sprintf(
+                             "unable to send quit request to socket (%s): %s",
+                             scanner_options, strerror(errno)),
+                             sock);
+
+               avast_stage = AVA_DONE;
+                goto endloop;
+               }
+
+              if (malware_name) break;  /* found malware already, nothing to do anymore */
 
              if (pcre_exec(ava_re_clean, NULL, CS buf, slen,
                    0, 0, ovector, nelem(ovector)) > 0)
                break;
 
-             if (  !malware_name
-                && (malware_name = m_pcre_exec(ava_re_virus, buf)))
+             if (malware_name = m_pcre_exec(ava_re_virus, buf))
                { /* remove backslash in front of [whitespace|backslash] */
                uschar * p, * p0;
                for (p = malware_name; *p; ++p)
@@ -1977,22 +2002,10 @@ b_seek:   err = errno;
                    for (p0 = p; *p0; ++p0) *p0 = p0[1];
 
                DEBUG(D_acl)
-                 debug_printf_indent("unescaped m-name: '%s'\n", malware_name);
+                 debug_printf_indent("unescaped malware name: '%s'\n", malware_name);
                break;
                }
 
-             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, CUS callout_address,
-                         string_sprintf(
-                             "unable to send quit request to socket (%s): %s",
-                             scanner_options, strerror(errno)),
-                             sock);
-
-               avast_stage = AVA_DONE;
-               }
-
              /* here also for any unexpected response from the scanner */
              goto endloop;
 
diff --git a/test/confs/4017 b/test/confs/4017
new file mode 120000 (symlink)
index 0000000..cf23719
--- /dev/null
@@ -0,0 +1 @@
+4007
\ No newline at end of file
diff --git a/test/log/4017 b/test/log/4017
new file mode 100644 (file)
index 0000000..cd53a5c
--- /dev/null
@@ -0,0 +1,7 @@
+1999-03-02 09:44:33 10HmaZ-0005vi-00 <= CALLER@myhost.test.ex U=CALLER P=local-esmtp S=sss T="message should be accepted"
+1999-03-02 09:44:33 10HmaZ-0005vi-00 => :blackhole: <userx@test.ex> R=r
+1999-03-02 09:44:33 10HmaZ-0005vi-00 Completed
+1999-03-02 09:44:33 10HmaX-0005vi-00 malware acl condition: avast TESTSUITE/eximdir/avast_sock : invalid response from scanner: '/bin/error    [E]0.0'
+1999-03-02 09:44:33 10HmaX-0005vi-00 U=CALLER F=<CALLER@myhost.test.ex> temporarily rejected after DATA
+1999-03-02 09:44:33 10HmaY-0005vi-00 malware_name This ist not even an EICAR test virus.
+1999-03-02 09:44:33 10HmaY-0005vi-00 U=CALLER F=<CALLER@myhost.test.ex> rejected after DATA
diff --git a/test/paniclog/4017 b/test/paniclog/4017
new file mode 100644 (file)
index 0000000..b6fcc05
--- /dev/null
@@ -0,0 +1 @@
+1999-03-02 09:44:33 10HmaX-0005vi-00 malware acl condition: avast TESTSUITE/eximdir/avast_sock : invalid response from scanner: '/bin/error    [E]0.0'
diff --git a/test/rejectlog/4017 b/test/rejectlog/4017
new file mode 100644 (file)
index 0000000..f14a316
--- /dev/null
@@ -0,0 +1,24 @@
+1999-03-02 09:44:33 10HmaX-0005vi-00 U=CALLER F=<CALLER@myhost.test.ex> temporarily rejected after DATA
+Envelope-from: <CALLER@myhost.test.ex>
+Envelope-to: <userx@test.ex>
+P Received: from CALLER (helo=test.ex)
+       by myhost.test.ex with local-esmtp (Exim x.yz)
+       (envelope-from <CALLER@myhost.test.ex>)
+       id 10HmaX-0005vi-00
+       for userx@test.ex; Tue, 2 Mar 1999 09:44:33 +0000
+  Date: Tue, 2 Mar 1999 09:44:33 +0000
+  Subject: defer this one, the scanner had an error
+I Message-Id: <E10HmaX-0005vi-00@myhost.test.ex>
+F From: CALLER_NAME <CALLER@myhost.test.ex>
+1999-03-02 09:44:33 10HmaY-0005vi-00 U=CALLER F=<CALLER@myhost.test.ex> rejected after DATA
+Envelope-from: <CALLER@myhost.test.ex>
+Envelope-to: <userx@test.ex>
+P Received: from CALLER (helo=test.ex)
+       by myhost.test.ex with local-esmtp (Exim x.yz)
+       (envelope-from <CALLER@myhost.test.ex>)
+       id 10HmaY-0005vi-00
+       for userx@test.ex; Tue, 2 Mar 1999 09:44:33 +0000
+  Date: Tue, 2 Mar 1999 09:44:33 +0000
+  Subject: message should be rejected
+I Message-Id: <E10HmaY-0005vi-00@myhost.test.ex>
+F From: CALLER_NAME <CALLER@myhost.test.ex>
diff --git a/test/scripts/4017_scan_avast_multiline/4017 b/test/scripts/4017_scan_avast_multiline/4017
new file mode 100644 (file)
index 0000000..d075825
--- /dev/null
@@ -0,0 +1,86 @@
+# content scan interface: avast
+### clean |  multiline response
+server DIR/eximdir/avast_sock
+>LF>220 ready
+<FLAGS -fullfiles
+>LF>210 FLAGS DATA
+>LF>FLAGS -fullfiles
+>LF>FLAGS +extra
+>LF>200 FLAGS OK
+<SCAN
+>LF>210 SCAN DATA
+>LF>/bin/clean1        [+]
+>LF>/bin/clean2        [+]
+>LF>200 SCAN OK
+<QUIT
+*eof
+****
+#
+#
+#
+exim -odi -bs -DOPTION="FLAGS -fullfiles" -DINSERT=
+ehlo test.ex
+mail from:<>
+rcpt to:<userx@test.ex>
+data
+Date: Fri, 17 Dec 2004 14:35:01 +0100
+Subject: message should be accepted
+
+.
+quit
+****
+#
+#
+### clean and error | multiline response
+server DIR/eximdir/avast_sock
+>LF>220 ready
+<SCAN
+>LF>210 SCAN DATA
+>LF>/bin/ok    [+]
+>LF>/bin/error [E]0.0
+>LF>/bin/infected      [L]0.0  0 This is not even EICAR!
+>LF>200 SCAN OK
+<QUIT
+*eof
+****
+#
+#
+#
+exim -odi -bs -DOPTION= -DINSERT=
+ehlo test.ex
+mail from:<>
+rcpt to:<userx@test.ex>
+data
+Date: Fri, 17 Dec 2004 14:35:01 +0100
+Subject: defer this one, the scanner had an error
+
+.
+quit
+****
+#
+#
+# clean and infected | multiline response
+server DIR/eximdir/avast_sock
+>LF>220 ready
+<SCAN
+>LF>210 SCAN DATA
+>LF>/bin/clean [+]
+>LF>v\\ i\\ a\\ r\\ u\\ s      [L]9.9  9 This ist not even an EICAR test virus.
+>LF>200 SCAN OK
+<QUIT
+*eof
+****
+#
+#
+#
+exim -odi -bs -DOPTION= -DINSERT="/defer_ok"
+ehlo test.ex
+mail from:<>
+rcpt to:<userx@test.ex>
+data
+Date: Fri, 17 Dec 2004 14:35:01 +0100
+Subject: message should be rejected
+
+.
+quit
+****
diff --git a/test/scripts/4017_scan_avast_multiline/REQUIRES b/test/scripts/4017_scan_avast_multiline/REQUIRES
new file mode 100644 (file)
index 0000000..d5a6979
--- /dev/null
@@ -0,0 +1,2 @@
+support Content_Scanning
+malware avast
diff --git a/test/stderr/4017 b/test/stderr/4017
new file mode 100644 (file)
index 0000000..22157c3
--- /dev/null
@@ -0,0 +1,7 @@
+### clean |  multiline response
+### clean and error | multiline response
+1999-03-02 09:44:33 10HmaX-0005vi-00 malware acl condition: avast TESTSUITE/eximdir/avast_sock : invalid response from scanner: '/bin/error    [E]0.0'
+
+******** SERVER ********
+### clean |  multiline response
+### clean and error | multiline response
diff --git a/test/stdout/4017 b/test/stdout/4017
new file mode 100644 (file)
index 0000000..e46e8f3
--- /dev/null
@@ -0,0 +1,76 @@
+### clean |  multiline response
+220 myhost.test.ex ESMTP Exim x.yz Tue, 2 Mar 1999 09:44:33 +0000\r
+250-myhost.test.ex Hello CALLER at test.ex\r
+250-SIZE 52428800\r
+250-8BITMIME\r
+250-PIPELINING\r
+250 HELP\r
+250 OK\r
+250 Accepted\r
+354 Enter message, ending with "." on a line by itself\r
+250 OK id=10HmaZ-0005vi-00\r
+221 myhost.test.ex closing connection\r
+### clean and error | multiline response
+220 myhost.test.ex ESMTP Exim x.yz Tue, 2 Mar 1999 09:44:33 +0000\r
+250-myhost.test.ex Hello CALLER at test.ex\r
+250-SIZE 52428800\r
+250-8BITMIME\r
+250-PIPELINING\r
+250 HELP\r
+250 OK\r
+250 Accepted\r
+354 Enter message, ending with "." on a line by itself\r
+451 Temporary local problem - please try later\r
+221 myhost.test.ex closing connection\r
+220 myhost.test.ex ESMTP Exim x.yz Tue, 2 Mar 1999 09:44:33 +0000\r
+250-myhost.test.ex Hello CALLER at test.ex\r
+250-SIZE 52428800\r
+250-8BITMIME\r
+250-PIPELINING\r
+250 HELP\r
+250 OK\r
+250 Accepted\r
+354 Enter message, ending with "." on a line by itself\r
+550 Administrative prohibition\r
+221 myhost.test.ex closing connection\r
+
+******** SERVER ********
+### clean |  multiline response
+Listening on TESTSUITE/eximdir/avast_sock ... 
+Connection request
+>LF>220 ready
+<FLAGS -fullfiles
+>LF>210 FLAGS DATA
+>LF>FLAGS -fullfiles
+>LF>FLAGS +extra
+>LF>200 FLAGS OK
+<SCAN TESTSUITE/spool/scan/10HmaZ-0005vi-00
+>LF>210 SCAN DATA
+>LF>/bin/clean1\x09[+]
+>LF>/bin/clean2\x09[+]
+>LF>200 SCAN OK
+<QUIT
+Expected EOF read from client
+End of script
+### clean and error | multiline response
+Listening on TESTSUITE/eximdir/avast_sock ... 
+Connection request
+>LF>220 ready
+<SCAN TESTSUITE/spool/scan/10HmaX-0005vi-00
+>LF>210 SCAN DATA
+>LF>/bin/ok\x09[+]
+>LF>/bin/error\x09[E]0.0
+>LF>/bin/infected\x09[L]0.0\x090 This is not even EICAR!
+>LF>200 SCAN OK
+Unexpected EOF read from client
+Listening on TESTSUITE/eximdir/avast_sock ... 
+Connection request
+>LF>220 ready
+<SCAN TESTSUITE/spool/scan/10HmaY-0005vi-00
+>LF>210 SCAN DATA
+>LF>/bin/clean\x09[+]
+>LF>v\\ i\\ a\\ r\\ u\\ s\x09[L]9.9\x099 This ist not even an EICAR test virus.
+>LF>200 SCAN OK
+<QUIT
+Expected EOF read from client
+End of script