#include "exim.h"
#ifdef WITH_CONTENT_SCAN /* entire file */
-typedef enum {M_FPROTD, M_DRWEB, M_AVES, M_FSEC, M_KAVD, M_CMDL,
- M_SOPHIE, M_CLAMD, M_SOCK, M_MKSD, M_AVAST, M_FPROT6D} scanner_t;
+typedef enum {
+#ifndef DISABLE_MAL_FFROTD
+ M_FPROTD,
+#endif
+#ifndef DISABLE_MAL_FFROT6D
+ M_FPROT6D,
+#endif
+#ifndef DISABLE_MAL_DRWEB
+ M_DRWEB,
+#endif
+#ifndef DISABLE_MAL_AVE
+ M_AVES,
+#endif
+#ifndef DISABLE_MAL_FSECURE
+ M_FSEC,
+#endif
+#ifndef DISABLE_MAL_KAV
+ M_KAVD,
+#endif
+#ifndef DISABLE_MAL_SOPHIE
+ M_SOPHIE,
+#endif
+#ifndef DISABLE_MAL_CLAM
+ M_CLAMD,
+#endif
+#ifndef DISABLE_MAL_MKS
+ M_MKSD,
+#endif
+#ifndef DISABLE_MAL_AVAST
+ M_AVAST,
+#endif
+#ifndef DISABLE_MAL_SOCK
+ M_SOCK,
+#endif
+#ifndef DISABLE_MAL_CMDLINE
+ M_CMDL,
+#endif
+ } scanner_t;
typedef enum {MC_NONE, MC_TCP, MC_UNIX, MC_STRM} contype_t;
static struct scan
{
/* Some (currently avast only) use backslash escaped whitespace,
this function undoes these escapes */
+
static inline void
-unescape(char *p) {
- uschar *p0;
- for (; *p; ++p)
- if (*p == '\\' && (isspace(p[1]) || p[1] == '\\'))
- for (p0 = p; *p0; ++p0) *p0 = p0[1];
+unescape(uschar *p)
+{
+uschar *p0;
+for (; *p; ++p)
+ if (*p == '\\' && (isspace(p[1]) || p[1] == '\\'))
+ for (p0 = p; *p0; ++p0) *p0 = p0[1];
}
/* --- malware_*_defer --- */
#ifndef DISABLE_MAL_AVAST
case M_AVAST: /* "avast" scanner type ----------------------------------- */
{
- int ovector[10*3];
uschar buf[1024];
uschar * scanrequest;
enum {AVA_HELO, AVA_OPT, AVA_RSP, AVA_DONE} avast_stage;
int nread;
- int more_data;
uschar * error_message = NULL;
+ BOOL more_data = FALSE;
+ BOOL strict = TRUE;
/* According to Martin Tuma @avast the protocol uses "escaped
whitespace", that is, every embedded whitespace is backslash
if ((scanrequest = string_nextinlist(&av_scanner_work, &sep,
NULL, 0)))
{
+ if (Ustrcmp(scanrequest, "pass_unscanned") == 0)
+ {
+ DEBUG(D_acl) debug_printf_indent("pass unscanned files as clean\n");
+ strict = FALSE;
+ goto sendreq;
+ }
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);
case AVA_RSP:
- if (isdigit(buf[0]))
- { /* we're done, this is the last response line from the scanner */
- DEBUG(D_acl) debug_printf_indent("sent to avast QUIT\n");
- if (send(sock, "QUIT\n", 5, 0) == -1) /* courtesy */
- return m_panic_defer_3(scanent, CUS callout_address,
- string_sprintf(
- "unable to send quit request to socket (%s): %s",
- scanner_options, strerror(errno)),
- sock);
-
- if (buf[0] != '2') error_message = buf;
- avast_stage = AVA_DONE;
+ if (isdigit(buf[0])) /* We're done */
goto endloop;
- }
- #if 0
- /* found malware or another error already, nothing to do anymore, just read on */
- if (malware_name || error_message)
+ if (malware_name) /* Nothing else matters, just read on */
break;
- #endif
- if (pcre_exec(ava_re_clean, NULL, CS buf, slen,
- 0, 0, ovector, nelem(ovector)) > 0)
+ if (pcre_exec(ava_re_clean, NULL, CS buf, slen, 0, 0, NULL, 0) == 0)
break;
- if (malware_name)
- break;
-
- if (malware_name = m_pcre_exec(ava_re_virus, buf))
+ if ((malware_name = m_pcre_exec(ava_re_virus, buf)))
{
unescape(malware_name);
DEBUG(D_acl)
break;
}
- /*if (pcre_exec(ava_re_error, NULL, CS buf, nread, 0, 0, ovector, nelem(ovector)) == 2)
- unescape(malware_name = buf + ovector[1*2]);
- */
- if (malware_name = m_pcre_exec(ava_re_error, buf))
+ if (strict) /* treat scanner errors as malware */
{
- unescape(malware_name);
- DEBUG(D_acl)
- debug_printf_indent("unescaped error message: '%s'\n", malware_name);
+ if ((malware_name = m_pcre_exec(ava_re_error, buf)))
+ {
+ unescape(malware_name);
+ DEBUG(D_acl)
+ debug_printf_indent("unescaped error message: '%s'\n", malware_name);
+ break;
+ }
+ }
+ else if (pcre_exec(ava_re_error, NULL, CS buf, slen, 0, 0, NULL, 0) == 0)
+ {
+ log_write(0, LOG_MAIN, "internal scanner error (ignored): %s", buf);
break;
}
/* here also for any unexpected response from the scanner */
+ DEBUG(D_acl) debug_printf("avast response not handled: '%s'\n", buf);
+
goto endloop;
default: log_write(0, LOG_PANIC, "%s:%d:%s: should not happen",
}
}
}
+
endloop:
- switch(avast_stage)
- {
- case AVA_HELO:
- case AVA_OPT:
- case AVA_RSP:
- if (nread == -1) error_message = "EOF from scanner";
- else if (nread < 0) error_message = "timeout from scanner";
+ if (nread == -1) error_message = US"EOF from scanner";
+ else if (nread < 0) error_message = US"timeout from scanner";
+ else if (nread == 0) error_message = US"got nothing from scanner";
+ else if (buf[0] != '2') error_message = buf;
- case AVA_DONE:
- if (error_message)
- return m_panic_defer_3(scanent, CUS callout_address, error_message, sock);
+ DEBUG(D_acl) debug_printf_indent("sent to avast QUIT\n");
+ if (send(sock, "QUIT\n", 5, 0) == -1)
+ return m_panic_defer_3(scanent, CUS callout_address,
+ string_sprintf("unable to send quit request to socket (%s): %s",
+ scanner_options, strerror(errno)), sock);
+
+ if (error_message)
+ return m_panic_defer_3(scanent, CUS callout_address, error_message, sock);
- default: break;
- }
- break;
}
#endif
} /* scanner type switch */