/* Copyright (c) Tom Kistner <tom@duncanthrax.net> 2003 - 2015
* License: GPL
- * Copyright (c) The Exim Maintainers 2017
+ * Copyright (c) The Exim Maintainers 2017 - 2018
*/
/* Code for calling virus (malware) scanners. Called from acl.c. */
#include "exim.h"
-#ifdef WITH_CONTENT_SCAN
+#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;
contype_t conn;
} m_scans[] =
{
+#ifndef DISABLE_MAL_FFROTD
{ M_FPROTD, US"f-protd", US"localhost 10200-10204", MC_TCP },
+#endif
+#ifndef DISABLE_MAL_FFROT6D
+ { M_FPROT6D, US"f-prot6d", US"localhost 10200", MC_TCP },
+#endif
+#ifndef DISABLE_MAL_DRWEB
{ M_DRWEB, US"drweb", US"/usr/local/drweb/run/drwebd.sock", MC_STRM },
+#endif
+#ifndef DISABLE_MAL_AVE
{ M_AVES, US"aveserver", US"/var/run/aveserver", MC_UNIX },
+#endif
+#ifndef DISABLE_MAL_FSECURE
{ M_FSEC, US"fsecure", US"/var/run/.fsav", MC_UNIX },
+#endif
+#ifndef DISABLE_MAL_KAV
{ M_KAVD, US"kavdaemon", US"/var/run/AvpCtl", MC_UNIX },
- { M_CMDL, US"cmdline", NULL, MC_NONE },
+#endif
+#ifndef DISABLE_MAL_SOPHIE
{ M_SOPHIE, US"sophie", US"/var/run/sophie", MC_UNIX },
+#endif
+#ifndef DISABLE_MAL_CLAM
{ M_CLAMD, US"clamd", US"/tmp/clamd", MC_NONE },
- { M_SOCK, US"sock", US"/tmp/malware.sock", MC_STRM },
+#endif
+#ifndef DISABLE_MAL_MKS
{ M_MKSD, US"mksd", NULL, MC_NONE },
+#endif
+#ifndef DISABLE_MAL_AVAST
{ M_AVAST, US"avast", US"/var/run/avast/scan.sock", MC_STRM },
- { M_FPROT6D, US"f-prot6d", US"localhost 10200", MC_TCP },
+#endif
+#ifndef DISABLE_MAL_SOCK
+ { M_SOCK, US"sock", US"/tmp/malware.sock", MC_STRM },
+#endif
+#ifndef DISABLE_MAL_CMDLINE
+ { M_CMDL, US"cmdline", NULL, MC_NONE },
+#endif
{ -1, NULL, NULL, MC_NONE } /* end-marker */
};
+/******************************************************************************/
+# ifdef MACRO_PREDEF /* build solely to predefine macros */
+
+# include "macro_predef.h"
+
+void
+features_malware(void)
+{
+struct scan * sc;
+uschar * s, * t;
+uschar buf[64];
+
+spf(buf, sizeof(buf), US"_HAVE_MALWARE_");
+
+for (sc = m_scans; sc->scancode != -1; sc++)
+ {
+ for(s = sc->name, t = buf+14; *s; s++) if (*s != '-') *t++ = toupper(*s);
+ *t = '\0';
+ builtin_macro_create(buf);
+ }
+}
+
+/******************************************************************************/
+# else /*!MACRO_PREDEF, main build*/
+
+
+#define MALWARE_TIMEOUT 120 /* default timeout, seconds */
+
+static const uschar * malware_regex_default = US ".+";
+static const pcre * malware_default_re = NULL;
+
+
+
+#ifndef DISABLE_MAL_CLAM
/* The maximum number of clamd servers that are supported in the configuration */
-#define MAX_CLAMD_SERVERS 32
-#define MAX_CLAMD_SERVERS_S "32"
+# define MAX_CLAMD_SERVERS 32
+# define MAX_CLAMD_SERVERS_S "32"
typedef struct clamd_address {
uschar * hostspec;
unsigned tcp_port;
unsigned retry;
} clamd_address;
-
-#ifndef nelements
-# define nelements(arr) (sizeof(arr) / sizeof(arr[0]))
#endif
-#define MALWARE_TIMEOUT 120 /* default timeout, seconds */
-
-
-#define DRWEBD_SCAN_CMD (1) /* scan file, buffer or diskfile */
-#define DRWEBD_RETURN_VIRUSES (1<<0) /* ask daemon return to us viruses names from report */
-#define DRWEBD_IS_MAIL (1<<19) /* say to daemon that format is "archive MAIL" */
+#ifndef DISABLE_MAL_DRWEB
+# define DRWEBD_SCAN_CMD (1) /* scan file, buffer or diskfile */
+# define DRWEBD_RETURN_VIRUSES (1<<0) /* ask daemon return to us viruses names from report */
+# define DRWEBD_IS_MAIL (1<<19) /* say to daemon that format is "archive MAIL" */
-#define DERR_READ_ERR (1<<0) /* read error */
-#define DERR_NOMEMORY (1<<2) /* no memory */
-#define DERR_TIMEOUT (1<<9) /* scan timeout has run out */
-#define DERR_BAD_CALL (1<<15) /* wrong command */
-
-
-static const uschar * malware_regex_default = US ".+";
-static const pcre * malware_default_re = NULL;
+# define DERR_READ_ERR (1<<0) /* read error */
+# define DERR_NOMEMORY (1<<2) /* no memory */
+# define DERR_TIMEOUT (1<<9) /* scan timeout has run out */
+# define DERR_BAD_CALL (1<<15) /* wrong command */
static const uschar * drweb_re_str = US "infected\\swith\\s*(.+?)$";
static const pcre * drweb_re = NULL;
+#endif
+#ifndef DISABLE_MAL_FSECURE
static const uschar * fsec_re_str = US "\\S{0,5}INFECTED\\t[^\\t]*\\t([^\\t]+)\\t\\S*$";
static const pcre * fsec_re = NULL;
+#endif
+#ifndef DISABLE_MAL_KAV
static const uschar * kav_re_sus_str = US "suspicion:\\s*(.+?)\\s*$";
static const uschar * kav_re_inf_str = US "infected:\\s*(.+?)\\s*$";
static const pcre * kav_re_sus = NULL;
static const pcre * kav_re_inf = NULL;
+#endif
+#ifndef DISABLE_MAL_AVAST
static const uschar * ava_re_clean_str = US "(?!\\\\)\\t\\[\\+\\]";
static const uschar * ava_re_virus_str = US "(?!\\\\)\\t\\[L\\]\\d\\.\\d\\t\\d\\s(.*)";
static const pcre * ava_re_clean = NULL;
static const pcre * ava_re_virus = NULL;
+#endif
+#ifndef DISABLE_MAL_FFROT6D
static const uschar * fprot6d_re_error_str = US "^\\d+\\s<(.+?)>$";
static const uschar * fprot6d_re_virus_str = US "^\\d+\\s<infected:\\s+(.+?)>\\s+.+$";
static const pcre * fprot6d_re_error = NULL;
static const pcre * fprot6d_re_virus = NULL;
+#endif
/*************************************************/
+#ifndef DISABLE_MAL_CLAM
/* Only used by the Clamav code, which is working from a list of servers and
uses the returned in_addr to get a second connection to the same system.
*/
return ip_connectedsocket(SOCK_STREAM, hostname, port, port, 5,
host, errstr, fastopen_blob);
}
+#endif
static int
m_sock_send(int sock, uschar * buf, int cnt, uschar ** errstr)
{
int ovector[10*3];
int i = pcre_exec(cre, NULL, CS text, Ustrlen(text), 0, 0,
- ovector, nelements(ovector));
+ ovector, nelem(ovector));
uschar * substr = NULL;
if (i >= 2) /* Got it */
pcre_get_substring(CS text, ovector, i, 1, (const char **) &substr);
+#ifndef DISABLE_MAL_MKS
/* ============= private routines for the "mksd" scanner type ============== */
-#include <sys/uio.h>
+# include <sys/uio.h>
static inline int
mksd_writev (int sock, struct iovec * iov, int iovcnt)
return mksd_parse_line (scanent, CS av_buffer);
}
+#endif /* MKSD */
+#ifndef DISABLE_MAL_CLAM
static int
clamd_option(clamd_address * cd, const uschar * optstr, int * subsep)
{
return FAIL;
return OK;
}
+#endif
+
+
/*************************************************
* Scan content for malware *
switch (scanent->scancode)
{
+#ifndef DISABLE_MAL_FFROTD
case M_FPROTD: /* "f-protd" scanner type -------------------------------- */
{
uschar *fp_scan_option;
}
break;
} /* f-protd */
+#endif
+
+#ifndef DISABLE_MAL_FFROT6D
+ case M_FPROT6D: /* "f-prot6d" scanner type ----------------------------------- */
+ {
+ int bread;
+ uschar * e;
+ uschar * linebuffer;
+ uschar * scanrequest;
+ uschar av_buffer[1024];
+
+ if ((!fprot6d_re_virus && !(fprot6d_re_virus = m_pcre_compile(fprot6d_re_virus_str, &errstr)))
+ || (!fprot6d_re_error && !(fprot6d_re_error = m_pcre_compile(fprot6d_re_error_str, &errstr))))
+ return malware_errlog_defer(errstr);
+
+ scanrequest = string_sprintf("SCAN FILE %s\n", eml_filename);
+ DEBUG(D_acl) debug_printf_indent("Malware scan: issuing %s: %s\n",
+ scanner_name, scanrequest);
+
+ if (m_sock_send(sock, scanrequest, Ustrlen(scanrequest), &errstr) < 0)
+ return m_errlog_defer(scanent, CUS callout_address, errstr);
+
+ bread = ip_recv(sock, av_buffer, sizeof(av_buffer), tmo-time(NULL));
+
+ if (bread <= 0)
+ return m_errlog_defer_3(scanent, CUS callout_address,
+ string_sprintf("unable to read from socket (%s)", strerror(errno)),
+ sock);
+
+ if (bread == sizeof(av_buffer))
+ return m_errlog_defer_3(scanent, CUS callout_address,
+ US"buffer too small", sock);
+
+ av_buffer[bread] = '\0';
+ linebuffer = string_copy(av_buffer);
+
+ m_sock_send(sock, US"QUIT\n", 5, 0);
+
+ if ((e = m_pcre_exec(fprot6d_re_error, linebuffer)))
+ return m_errlog_defer_3(scanent, CUS callout_address,
+ string_sprintf("scanner reported error (%s)", e), sock);
+ if (!(malware_name = m_pcre_exec(fprot6d_re_virus, linebuffer)))
+ malware_name = NULL;
+
+ break;
+ } /* f-prot6d */
+#endif
+
+#ifndef DISABLE_MAL_DRWEB
case M_DRWEB: /* "drweb" scanner type ----------------------------------- */
/* v0.1 - added support for tcp sockets */
/* v0.0 - initial release -- support for unix sockets */
/* try matcher on the line, grab substring */
result = pcre_exec(drweb_re, NULL, CS tmpbuf, Ustrlen(tmpbuf), 0, 0,
- ovector, nelements(ovector));
+ ovector, nelem(ovector));
if (result >= 2)
{
const char * pre_malware_nb;
}
break;
} /* drweb */
+#endif
+#ifndef DISABLE_MAL_AVE
case M_AVES: /* "aveserver" scanner type -------------------------------- */
{
uschar buf[32768];
}
break;
} /* aveserver */
+#endif
+#ifndef DISABLE_MAL_FSECURE
case M_FSEC: /* "fsecure" scanner type ---------------------------------- */
{
int i, j, bread = 0;
scanner_name, scanner_options);
/* pass options */
memset(av_buffer, 0, sizeof(av_buffer));
- for (i = 0; i != nelements(cmdopt); i++)
+ for (i = 0; i != nelem(cmdopt); i++)
{
if (m_sock_send(sock, cmdopt[i], Ustrlen(cmdopt[i]), &errstr) < 0)
fsec_found:
break;
} /* fsecure */
+#endif
+#ifndef DISABLE_MAL_KAV
case M_KAVD: /* "kavdaemon" scanner type -------------------------------- */
{
time_t t;
break;
}
+#endif
+#ifndef DISABLE_MAL_CMDLINE
case M_CMDL: /* "cmdline" scanner type ---------------------------------- */
{
const uschar *cmdline_scanner = scanner_options;
malware_name = NULL;
break;
} /* cmdline */
+#endif
+#ifndef DISABLE_MAL_SOPHIE
case M_SOPHIE: /* "sophie" scanner type --------------------------------- */
{
int bread = 0;
break;
}
+#endif
+#ifndef DISABLE_MAL_CLAM
case M_CLAMD: /* "clamd" scanner type ----------------------------------- */
{
/* This code was originally contributed by David Saez */
break;
} /* clamd */
+#endif
+#ifndef DISABLE_MAL_SOCK
case M_SOCK: /* "sock" scanner type ------------------------------------- */
/* This code was derived by Martin Poole from the clamd code contributed
by David Saez and the cmdline code
malware_name = NULL;
break;
}
+#endif
+#ifndef DISABLE_MAL_MKS
case M_MKSD: /* "mksd" scanner type ------------------------------------- */
{
char *mksd_options_end;
}
break;
}
+#endif
+#ifndef DISABLE_MAL_AVAST
case M_AVAST: /* "avast" scanner type ----------------------------------- */
{
int ovector[1*3];
break; /* ignore the "210 SCAN DATA" message */
if (pcre_exec(ava_re_clean, NULL, CS buf, slen,
- 0, 0, ovector, nelements(ovector)) > 0)
+ 0, 0, ovector, nelem(ovector)) > 0)
break;
if ((malware_name = m_pcre_exec(ava_re_virus, buf)))
}
break;
}
-
- case M_FPROT6D: /* "f-prot6d" scanner type ----------------------------------- */
- {
- int bread;
- uschar * e;
- uschar * linebuffer;
- uschar * scanrequest;
- uschar av_buffer[1024];
-
- if ((!fprot6d_re_virus && !(fprot6d_re_virus = m_pcre_compile(fprot6d_re_virus_str, &errstr)))
- || (!fprot6d_re_error && !(fprot6d_re_error = m_pcre_compile(fprot6d_re_error_str, &errstr))))
- return malware_errlog_defer(errstr);
-
- scanrequest = string_sprintf("SCAN FILE %s\n", eml_filename);
- DEBUG(D_acl) debug_printf_indent("Malware scan: issuing %s: %s\n",
- scanner_name, scanrequest);
-
- if (m_sock_send(sock, scanrequest, Ustrlen(scanrequest), &errstr) < 0)
- return m_errlog_defer(scanent, CUS callout_address, errstr);
-
- bread = ip_recv(sock, av_buffer, sizeof(av_buffer), tmo-time(NULL));
-
- if (bread <= 0)
- return m_errlog_defer_3(scanent, CUS callout_address,
- string_sprintf("unable to read from socket (%s)", strerror(errno)),
- sock);
-
- if (bread == sizeof(av_buffer))
- return m_errlog_defer_3(scanent, CUS callout_address,
- US"buffer too small", sock);
-
- av_buffer[bread] = '\0';
- linebuffer = string_copy(av_buffer);
-
- m_sock_send(sock, US"QUIT\n", 5, 0);
-
- if ((e = m_pcre_exec(fprot6d_re_error, linebuffer)))
- return m_errlog_defer_3(scanent, CUS callout_address,
- string_sprintf("scanner reported error (%s)", e), sock);
-
- if (!(malware_name = m_pcre_exec(fprot6d_re_virus, linebuffer)))
- malware_name = NULL;
-
- break;
- } /* f-prot6d */
+#endif
} /* scanner type switch */
if (sock >= 0)
{
if (!malware_default_re)
malware_default_re = regex_must_compile(malware_regex_default, FALSE, TRUE);
+
+#ifndef DISABLE_MAL_DRWEB
if (!drweb_re)
drweb_re = regex_must_compile(drweb_re_str, FALSE, TRUE);
+#endif
+#ifndef DISABLE_MAL_FSECURE
if (!fsec_re)
fsec_re = regex_must_compile(fsec_re_str, FALSE, TRUE);
+#endif
+#ifndef DISABLE_MAL_KAV
if (!kav_re_sus)
kav_re_sus = regex_must_compile(kav_re_sus_str, FALSE, TRUE);
if (!kav_re_inf)
kav_re_inf = regex_must_compile(kav_re_inf_str, FALSE, TRUE);
+#endif
+#ifndef DISABLE_MAL_AVA
if (!ava_re_clean)
ava_re_clean = regex_must_compile(ava_re_clean_str, FALSE, TRUE);
if (!ava_re_virus)
ava_re_virus = regex_must_compile(ava_re_virus_str, FALSE, TRUE);
+#endif
+#ifndef DISABLE_MAL_FPROT6D
if (!fprot6d_re_error)
fprot6d_re_error = regex_must_compile(fprot6d_re_error_str, FALSE, TRUE);
if (!fprot6d_re_virus)
fprot6d_re_virus = regex_must_compile(fprot6d_re_virus_str, FALSE, TRUE);
+#endif
+}
+
+
+void
+malware_show_supported(FILE * f)
+{
+struct scan * sc;
+fprintf(f, "Malware:");
+for (sc = m_scans; sc->scancode != -1; sc++) fprintf(f, " %s", sc->name);
+fprintf(f, "\n");
}
+
+# endif /*!MACRO_PREDEF*/
#endif /*WITH_CONTENT_SCAN*/
/*
* vi: aw ai sw=2