1 /*************************************************
2 * Exim - an Internet mail transport agent *
3 *************************************************/
5 /* Copyright (c) Tom Kistner <tom@duncanthrax.net> 2003 - 2015
7 * Copyright (c) The Exim Maintainers 2015 - 2020
10 /* Code for calling virus (malware) scanners. Called from acl.c. */
13 #ifdef WITH_CONTENT_SCAN /* entire file */
16 #ifndef DISABLE_MAL_FFROTD
19 #ifndef DISABLE_MAL_FFROT6D
22 #ifndef DISABLE_MAL_DRWEB
25 #ifndef DISABLE_MAL_AVE
28 #ifndef DISABLE_MAL_FSECURE
31 #ifndef DISABLE_MAL_KAV
34 #ifndef DISABLE_MAL_SOPHIE
37 #ifndef DISABLE_MAL_CLAM
40 #ifndef DISABLE_MAL_MKS
43 #ifndef DISABLE_MAL_AVAST
46 #ifndef DISABLE_MAL_SOCK
49 #ifndef DISABLE_MAL_CMDLINE
54 typedef enum {MC_NONE, MC_TCP, MC_UNIX, MC_STRM} contype_t;
59 const uschar * options_default;
63 #ifndef DISABLE_MAL_FFROTD
64 { M_FPROTD, US"f-protd", US"localhost 10200-10204", MC_TCP },
66 #ifndef DISABLE_MAL_FFROT6D
67 { M_FPROT6D, US"f-prot6d", US"localhost 10200", MC_TCP },
69 #ifndef DISABLE_MAL_DRWEB
70 { M_DRWEB, US"drweb", US"/usr/local/drweb/run/drwebd.sock", MC_STRM },
72 #ifndef DISABLE_MAL_AVE
73 { M_AVES, US"aveserver", US"/var/run/aveserver", MC_UNIX },
75 #ifndef DISABLE_MAL_FSECURE
76 { M_FSEC, US"fsecure", US"/var/run/.fsav", MC_UNIX },
78 #ifndef DISABLE_MAL_KAV
79 { M_KAVD, US"kavdaemon", US"/var/run/AvpCtl", MC_UNIX },
81 #ifndef DISABLE_MAL_SOPHIE
82 { M_SOPHIE, US"sophie", US"/var/run/sophie", MC_UNIX },
84 #ifndef DISABLE_MAL_CLAM
85 { M_CLAMD, US"clamd", US"/tmp/clamd", MC_NONE },
87 #ifndef DISABLE_MAL_MKS
88 { M_MKSD, US"mksd", NULL, MC_NONE },
90 #ifndef DISABLE_MAL_AVAST
91 { M_AVAST, US"avast", US"/var/run/avast/scan.sock", MC_STRM },
93 #ifndef DISABLE_MAL_SOCK
94 { M_SOCK, US"sock", US"/tmp/malware.sock", MC_STRM },
96 #ifndef DISABLE_MAL_CMDLINE
97 { M_CMDL, US"cmdline", NULL, MC_NONE },
99 { -1, NULL, NULL, MC_NONE } /* end-marker */
102 /******************************************************************************/
103 # ifdef MACRO_PREDEF /* build solely to predefine macros */
105 # include "macro_predef.h"
108 features_malware(void)
114 spf(buf, sizeof(buf), US"_HAVE_MALWARE_");
116 for (const struct scan * sc = m_scans; sc->scancode != -1; sc++)
118 for (s = sc->name, t = buf+14; *s; s++) if (*s != '-')
121 builtin_macro_create(buf);
125 /******************************************************************************/
126 # else /*!MACRO_PREDEF, main build*/
129 #define MALWARE_TIMEOUT 120 /* default timeout, seconds */
131 static const uschar * malware_regex_default = US ".+";
132 static const pcre * malware_default_re = NULL;
136 #ifndef DISABLE_MAL_CLAM
137 /* The maximum number of clamd servers that are supported in the configuration */
138 # define MAX_CLAMD_SERVERS 32
139 # define MAX_CLAMD_SERVERS_S "32"
141 typedef struct clamd_address {
149 #ifndef DISABLE_MAL_DRWEB
150 # define DRWEBD_SCAN_CMD (1) /* scan file, buffer or diskfile */
151 # define DRWEBD_RETURN_VIRUSES (1<<0) /* ask daemon return to us viruses names from report */
152 # define DRWEBD_IS_MAIL (1<<19) /* say to daemon that format is "archive MAIL" */
154 # define DERR_READ_ERR (1<<0) /* read error */
155 # define DERR_NOMEMORY (1<<2) /* no memory */
156 # define DERR_TIMEOUT (1<<9) /* scan timeout has run out */
157 # define DERR_BAD_CALL (1<<15) /* wrong command */
159 static const uschar * drweb_re_str = US "infected\\swith\\s*(.+?)$";
160 static const pcre * drweb_re = NULL;
163 #ifndef DISABLE_MAL_FSECURE
164 static const uschar * fsec_re_str = US "\\S{0,5}INFECTED\\t[^\\t]*\\t([^\\t]+)\\t\\S*$";
165 static const pcre * fsec_re = NULL;
168 #ifndef DISABLE_MAL_KAV
169 static const uschar * kav_re_sus_str = US "suspicion:\\s*(.+?)\\s*$";
170 static const uschar * kav_re_inf_str = US "infected:\\s*(.+?)\\s*$";
171 static const pcre * kav_re_sus = NULL;
172 static const pcre * kav_re_inf = NULL;
175 #ifndef DISABLE_MAL_AVAST
176 static const uschar * ava_re_clean_str = US "(?!\\\\)\\t\\[\\+\\]";
177 static const uschar * ava_re_virus_str = US "(?!\\\\)\\t\\[L\\]\\d+\\.0\\t0\\s(.*)";
178 static const uschar * ava_re_error_str = US "(?!\\\\)\\t\\[E\\]\\d+\\.0\\tError\\s\\d+\\s(.*)";
179 static const pcre * ava_re_clean = NULL;
180 static const pcre * ava_re_virus = NULL;
181 static const pcre * ava_re_error = NULL;
184 #ifndef DISABLE_MAL_FFROT6D
185 static const uschar * fprot6d_re_error_str = US "^\\d+\\s<(.+?)>$";
186 static const uschar * fprot6d_re_virus_str = US "^\\d+\\s<infected:\\s+(.+?)>\\s+.+$";
187 static const pcre * fprot6d_re_error = NULL;
188 static const pcre * fprot6d_re_virus = NULL;
193 /******************************************************************************/
195 #ifndef DISABLE_MAL_KAV
196 /* Routine to check whether a system is big- or little-endian.
197 Ripped from http://www.faqs.org/faqs/graphics/fileformats-faq/part4/section-7.html
198 Needed for proper kavdaemon implementation. Sigh. */
199 # define BIG_MY_ENDIAN 0
200 # define LITTLE_MY_ENDIAN 1
201 static int test_byte_order(void);
205 short int word = 0x0001;
206 char *byte = CS &word;
207 return(byte[0] ? LITTLE_MY_ENDIAN : BIG_MY_ENDIAN);
211 BOOL malware_ok = FALSE;
213 /* Gross hacks for the -bmalware option; perhaps we should just create
214 the scan directory normally for that case, but look into rigging up the
215 needed header variables if not already set on the command-line? */
216 extern int spool_mbox_ok;
217 extern uschar spooled_message_id[MESSAGE_ID_LENGTH+1];
220 /* Some (currently avast only) use backslash escaped whitespace,
221 this function undoes these escapes */
223 #ifndef DISABLE_MAL_AVAST
229 if (*p == '\\' && (isspace(p[1]) || p[1] == '\\'))
230 for (p0 = p; *p0; ++p0) *p0 = p0[1];
234 /* --- malware_*_defer --- */
236 malware_panic_defer(const uschar * str)
238 log_write(0, LOG_MAIN|LOG_PANIC, "malware acl condition: %s", str);
242 malware_log_defer(const uschar * str)
244 log_write(0, LOG_MAIN, "malware acl condition: %s", str);
247 /* --- m_*_defer --- */
249 m_panic_defer(struct scan * scanent, const uschar * hostport,
252 return malware_panic_defer(string_sprintf("%s %s : %s",
253 scanent->name, hostport ? hostport : CUS"", str));
255 /* --- m_*_defer_3 */
257 m_panic_defer_3(struct scan * scanent, const uschar * hostport,
258 const uschar * str, int fd_to_close)
260 DEBUG(D_acl) debug_print_socket(fd_to_close);
261 (void) close(fd_to_close);
262 return m_panic_defer(scanent, hostport, str);
265 /*************************************************/
267 #ifndef DISABLE_MAL_CLAM
268 /* Only used by the Clamav code, which is working from a list of servers and
269 uses the returned in_addr to get a second connection to the same system.
272 m_tcpsocket(const uschar * hostname, unsigned int port,
273 host_item * host, uschar ** errstr, const blob * fastopen_blob)
275 int fd = ip_connectedsocket(SOCK_STREAM, hostname, port, port, 5,
276 host, errstr, fastopen_blob);
277 #ifdef EXIM_TFO_FREEBSD
278 /* Under some fault conditions, FreeBSD 12.2 seen to send a (non-TFO) SYN
279 and, getting no response, wait for a long time. Impose a 5s max. */
282 struct timeval tv = {.tv_sec = 5};
284 FD_ZERO(fds); FD_SET(fd, &fds); (void) select(fd+1, NULL, &fds, NULL, &tv);
292 m_sock_send(int sock, uschar * buf, int cnt, uschar ** errstr)
294 if (send(sock, buf, cnt, 0) < 0)
298 *errstr = string_sprintf("unable to send to socket (%s): %s",
306 m_pcre_compile(const uschar * re, uschar ** errstr)
308 const uschar * rerror;
312 if (!(cre = pcre_compile(CS re, PCRE_COPT, CCSS &rerror, &roffset, NULL)))
313 *errstr= string_sprintf("regular expression error in '%s': %s at offset %d",
314 re, rerror, roffset);
319 m_pcre_exec(const pcre * cre, uschar * text)
322 int i = pcre_exec(cre, NULL, CS text, Ustrlen(text), 0, 0,
323 ovector, nelem(ovector));
324 uschar * substr = NULL;
325 if (i >= 2) /* Got it */
326 pcre_get_substring(CS text, ovector, i, 1, CCSS &substr);
331 m_pcre_nextinlist(const uschar ** list, int * sep,
332 char * listerr, uschar ** errstr)
334 const uschar * list_ele;
335 const pcre * cre = NULL;
337 if (!(list_ele = string_nextinlist(list, sep, NULL, 0)))
338 *errstr = US listerr;
341 DEBUG(D_acl) debug_printf_indent("%15s%10s'%s'\n", "", "RE: ",
342 string_printing(list_ele));
343 cre = m_pcre_compile(CUS list_ele, errstr);
350 Simple though inefficient wrapper for reading a line. Drop CRs and the
351 trailing newline. Can return early on buffer full. Null-terminate.
352 Apply initial timeout if no data ready.
354 Return: number of chars - zero for an empty line
356 -2 on timeout or error
359 recv_line(int fd, uschar * buffer, int bsize, time_t tmo)
365 if (!fd_ready(fd, tmo))
368 /*XXX tmo handling assumes we always get a whole line */
371 while ((rcv = read(fd, p, 1)) > 0)
374 if (p-buffer > bsize-2) break;
375 if (*p == '\n') break;
382 debug_printf_indent("Malware scan: read %s (%s)\n",
383 rcv==0 ? "EOF" : "error", strerror(errno));
384 debug_print_socket(fd);
386 return rcv==0 ? -1 : -2;
390 DEBUG(D_acl) debug_printf_indent("Malware scan: read '%s'\n", buffer);
394 /* return TRUE iff size as requested */
395 #ifndef DISABLE_MAL_DRWEB
397 recv_len(int sock, void * buf, int size, time_t tmo)
399 return fd_ready(sock, tmo)
400 ? recv(sock, buf, size, 0) == size
407 #ifndef DISABLE_MAL_MKS
408 /* ============= private routines for the "mksd" scanner type ============== */
410 # include <sys/uio.h>
413 mksd_writev (int sock, struct iovec * iov, int iovcnt)
420 i = writev (sock, iov, iovcnt);
421 while (i < 0 && errno == EINTR);
424 (void) malware_panic_defer(
425 US"unable to write to mksd UNIX socket (/var/run/mksd/socket)");
428 for (;;) /* check for short write */
429 if (i >= iov->iov_len)
439 iov->iov_base = CS iov->iov_base + i;
446 mksd_read_lines (int sock, uschar *av_buffer, int av_buffer_size, time_t tmo)
448 client_conn_ctx cctx = {.sock = sock};
454 i = ip_recv(&cctx, av_buffer+offset, av_buffer_size-offset, tmo);
457 (void) malware_panic_defer(US"unable to read from mksd UNIX socket (/var/run/mksd/socket)");
462 /* offset == av_buffer_size -> buffer full */
463 if (offset == av_buffer_size)
465 (void) malware_panic_defer(US"malformed reply received from mksd");
468 } while (av_buffer[offset-1] != '\n');
470 av_buffer[offset] = '\0';
475 mksd_parse_line(struct scan * scanent, char * line)
486 if ((p = strchr (line, '\n')) != NULL)
488 return m_panic_defer(scanent, NULL,
489 string_sprintf("scanner failed: %s", line));
492 if ((p = strchr (line, '\n')) != NULL)
497 && (p = strchr(line+4, ' ')) != NULL
502 malware_name = string_copy(US line+4);
506 return m_panic_defer(scanent, NULL,
507 string_sprintf("malformed reply received: %s", line));
512 mksd_scan_packed(struct scan * scanent, int sock, const uschar * scan_filename,
516 const char *cmd = "MSQ\n";
517 uschar av_buffer[1024];
519 iov[0].iov_base = (void *) cmd;
521 iov[1].iov_base = (void *) scan_filename;
522 iov[1].iov_len = Ustrlen(scan_filename);
523 iov[2].iov_base = (void *) (cmd + 3);
526 if (mksd_writev (sock, iov, 3) < 0)
529 if (mksd_read_lines (sock, av_buffer, sizeof (av_buffer), tmo) < 0)
532 return mksd_parse_line (scanent, CS av_buffer);
537 #ifndef DISABLE_MAL_CLAM
539 clamd_option(clamd_address * cd, const uschar * optstr, int * subsep)
544 while ((s = string_nextinlist(&optstr, subsep, NULL, 0)))
545 if (Ustrncmp(s, "retry=", 6) == 0)
547 int sec = readconf_readtime((s += 6), '\0', FALSE);
560 /*************************************************
561 * Scan content for malware *
562 *************************************************/
564 /* This is an internal interface for scanning an email; the normal interface
565 is via malware(), or there's malware_in_file() used for testing/debugging.
568 malware_re match condition for "malware="
569 scan_filename the file holding the email to be scanned, if we're faking
570 this up for the -bmalware test, else NULL
571 timeout if nonzero, non-default timeoutl
573 Returns: Exim message processing code (OK, FAIL, DEFER, ...)
574 where true means malware was found (condition applies)
577 malware_internal(const uschar * malware_re, const uschar * scan_filename,
581 const uschar *av_scanner_work = av_scanner;
582 uschar *scanner_name;
583 unsigned long mbox_size;
587 struct scan * scanent;
588 const uschar * scanner_options;
589 client_conn_ctx malware_daemon_ctx = {.sock = -1};
591 uschar * eml_filename, * eml_dir;
594 return FAIL; /* empty means "don't match anything" */
596 /* Ensure the eml mbox file is spooled up */
598 if (!(mbox_file = spool_mbox(&mbox_size, scan_filename, &eml_filename)))
599 return malware_panic_defer(US"error while creating mbox spool file");
601 /* None of our current scanners need the mbox file as a stream (they use
602 the name), so we can close it right away. Get the directory too. */
604 (void) fclose(mbox_file);
605 eml_dir = string_copyn(eml_filename, Ustrrchr(eml_filename, '/') - eml_filename);
607 /* parse 1st option */
608 if (strcmpic(malware_re, US"false") == 0 || Ustrcmp(malware_re,"0") == 0)
609 return FAIL; /* explicitly no matching */
611 /* special cases (match anything except empty) */
612 if ( strcmpic(malware_re,US"true") == 0
613 || Ustrcmp(malware_re,"*") == 0
614 || Ustrcmp(malware_re,"1") == 0
617 if ( !malware_default_re
618 && !(malware_default_re = m_pcre_compile(malware_regex_default, &errstr)))
619 return malware_panic_defer(errstr);
620 malware_re = malware_regex_default;
621 re = malware_default_re;
624 /* compile the regex, see if it works */
625 else if (!(re = m_pcre_compile(malware_re, &errstr)))
626 return malware_panic_defer(errstr);
628 /* if av_scanner starts with a dollar, expand it first */
629 if (*av_scanner == '$')
631 if (!(av_scanner_work = expand_string(av_scanner)))
632 return malware_panic_defer(
633 string_sprintf("av_scanner starts with $, but expansion failed: %s",
634 expand_string_message));
637 debug_printf_indent("Expanded av_scanner global: %s\n", av_scanner_work);
638 /* disable result caching in this case */
643 /* Do not scan twice (unless av_scanner is dynamic). */
646 /* find the scanner type from the av_scanner option */
647 if (!(scanner_name = string_nextinlist(&av_scanner_work, &sep, NULL, 0)))
648 return malware_panic_defer(US"av_scanner configuration variable is empty");
649 if (!timeout) timeout = MALWARE_TIMEOUT;
650 tmo = time(NULL) + timeout;
652 for (scanent = m_scans; ; scanent++)
655 return malware_panic_defer(string_sprintf("unknown scanner type '%s'",
657 if (strcmpic(scanner_name, US scanent->name) != 0)
659 DEBUG(D_acl) debug_printf_indent("Malware scan: %s tmo=%s\n",
660 scanner_name, readconf_printtime(timeout));
662 if (!(scanner_options = string_nextinlist(&av_scanner_work, &sep, NULL, 0)))
663 scanner_options = scanent->options_default;
664 if (scanent->conn == MC_NONE)
667 DEBUG(D_acl) debug_printf_indent("%15s%10s%s\n", "", "socket: ", scanner_options);
668 switch(scanent->conn)
671 malware_daemon_ctx.sock = ip_tcpsocket(scanner_options, &errstr, 5, NULL); break;
673 malware_daemon_ctx.sock = ip_unixsocket(scanner_options, &errstr); break;
675 malware_daemon_ctx.sock = ip_streamsocket(scanner_options, &errstr, 5, NULL); break;
677 /* compiler quietening */ break;
679 if (malware_daemon_ctx.sock < 0)
680 return m_panic_defer(scanent, CUS callout_address, errstr);
684 switch (scanent->scancode)
686 #ifndef DISABLE_MAL_FFROTD
687 case M_FPROTD: /* "f-protd" scanner type -------------------------------- */
689 uschar *fp_scan_option;
690 unsigned int detected=0, par_count=0;
691 uschar * scanrequest;
692 uschar buf[32768], *strhelper, *strhelper2;
693 uschar * malware_name_internal = NULL;
696 scanrequest = string_sprintf("GET %s", eml_filename);
698 while ((fp_scan_option = string_nextinlist(&av_scanner_work, &sep,
701 scanrequest = string_sprintf("%s%s%s", scanrequest,
702 par_count ? "%20" : "?", fp_scan_option);
705 scanrequest = string_sprintf("%s HTTP/1.0\r\n\r\n", scanrequest);
706 DEBUG(D_acl) debug_printf_indent("Malware scan: issuing %s: %s\n",
707 scanner_name, scanrequest);
709 /* send scan request */
710 if (m_sock_send(malware_daemon_ctx.sock, scanrequest, Ustrlen(scanrequest)+1, &errstr) < 0)
711 return m_panic_defer(scanent, CUS callout_address, errstr);
713 while ((len = recv_line(malware_daemon_ctx.sock, buf, sizeof(buf), tmo)) >= 0)
716 if (Ustrstr(buf, US"<detected type=\"") != NULL)
718 else if (detected && (strhelper = Ustrstr(buf, US"<name>")))
720 if ((strhelper2 = Ustrstr(buf, US"</name>")) != NULL)
723 malware_name_internal = string_copy(strhelper+6);
726 else if (Ustrstr(buf, US"<summary code=\""))
728 malware_name = Ustrstr(buf, US"<summary code=\"11\">")
729 ? malware_name_internal : NULL;
735 (void)close(malware_daemon_ctx.sock);
742 #ifndef DISABLE_MAL_FFROT6D
743 case M_FPROT6D: /* "f-prot6d" scanner type ----------------------------------- */
748 uschar * scanrequest;
749 uschar av_buffer[1024];
751 if ((!fprot6d_re_virus && !(fprot6d_re_virus = m_pcre_compile(fprot6d_re_virus_str, &errstr)))
752 || (!fprot6d_re_error && !(fprot6d_re_error = m_pcre_compile(fprot6d_re_error_str, &errstr))))
753 return malware_panic_defer(errstr);
755 scanrequest = string_sprintf("SCAN FILE %s\n", eml_filename);
756 DEBUG(D_acl) debug_printf_indent("Malware scan: issuing %s: %s\n",
757 scanner_name, scanrequest);
759 if (m_sock_send(malware_daemon_ctx.sock, scanrequest, Ustrlen(scanrequest), &errstr) < 0)
760 return m_panic_defer(scanent, CUS callout_address, errstr);
762 bread = ip_recv(&malware_daemon_ctx, av_buffer, sizeof(av_buffer), tmo);
765 return m_panic_defer_3(scanent, CUS callout_address,
766 string_sprintf("unable to read from socket (%s)", strerror(errno)),
767 malware_daemon_ctx.sock);
769 if (bread == sizeof(av_buffer))
770 return m_panic_defer_3(scanent, CUS callout_address,
771 US"buffer too small", malware_daemon_ctx.sock);
773 av_buffer[bread] = '\0';
774 linebuffer = string_copy(av_buffer);
776 m_sock_send(malware_daemon_ctx.sock, US"QUIT\n", 5, 0);
778 if ((e = m_pcre_exec(fprot6d_re_error, linebuffer)))
779 return m_panic_defer_3(scanent, CUS callout_address,
780 string_sprintf("scanner reported error (%s)", e), malware_daemon_ctx.sock);
782 if (!(malware_name = m_pcre_exec(fprot6d_re_virus, linebuffer)))
789 #ifndef DISABLE_MAL_DRWEB
790 case M_DRWEB: /* "drweb" scanner type ----------------------------------- */
791 /* v0.1 - added support for tcp sockets */
792 /* v0.0 - initial release -- support for unix sockets */
796 unsigned int fsize_uint;
797 uschar * tmpbuf, *drweb_fbuf;
798 int drweb_rc, drweb_cmd, drweb_flags = 0x0000, drweb_fd,
799 drweb_vnum, drweb_slen, drweb_fin = 0x0000;
801 /* prepare variables */
802 drweb_cmd = htonl(DRWEBD_SCAN_CMD);
803 drweb_flags = htonl(DRWEBD_RETURN_VIRUSES | DRWEBD_IS_MAIL);
805 if (*scanner_options != '/')
808 if ((drweb_fd = exim_open2(CCS eml_filename, O_RDONLY)) == -1)
809 return m_panic_defer_3(scanent, NULL,
810 string_sprintf("can't open spool file %s: %s",
811 eml_filename, strerror(errno)),
812 malware_daemon_ctx.sock);
814 if ((fsize = lseek(drweb_fd, 0, SEEK_END)) == -1)
817 badseek: err = errno;
818 (void)close(drweb_fd);
819 return m_panic_defer_3(scanent, NULL,
820 string_sprintf("can't seek spool file %s: %s",
821 eml_filename, strerror(err)),
822 malware_daemon_ctx.sock);
824 fsize_uint = (unsigned int) fsize;
825 if ((off_t)fsize_uint != fsize)
827 (void)close(drweb_fd);
828 return m_panic_defer_3(scanent, NULL,
829 string_sprintf("seeking spool file %s, size overflow",
831 malware_daemon_ctx.sock);
833 drweb_slen = htonl(fsize);
834 if (lseek(drweb_fd, 0, SEEK_SET) < 0)
837 DEBUG(D_acl) debug_printf_indent("Malware scan: issuing %s remote scan [%s]\n",
838 scanner_name, scanner_options);
840 /* send scan request */
841 if ((send(malware_daemon_ctx.sock, &drweb_cmd, sizeof(drweb_cmd), 0) < 0) ||
842 (send(malware_daemon_ctx.sock, &drweb_flags, sizeof(drweb_flags), 0) < 0) ||
843 (send(malware_daemon_ctx.sock, &drweb_fin, sizeof(drweb_fin), 0) < 0) ||
844 (send(malware_daemon_ctx.sock, &drweb_slen, sizeof(drweb_slen), 0) < 0))
846 (void)close(drweb_fd);
847 return m_panic_defer_3(scanent, CUS callout_address, string_sprintf(
848 "unable to send commands to socket (%s)", scanner_options),
849 malware_daemon_ctx.sock);
852 if (!(drweb_fbuf = store_malloc(fsize_uint)))
854 (void)close(drweb_fd);
855 return m_panic_defer_3(scanent, NULL,
856 string_sprintf("unable to allocate memory %u for file (%s)",
857 fsize_uint, eml_filename),
858 malware_daemon_ctx.sock);
861 if ((result = read (drweb_fd, drweb_fbuf, fsize)) == -1)
864 (void)close(drweb_fd);
865 store_free(drweb_fbuf);
866 return m_panic_defer_3(scanent, NULL,
867 string_sprintf("can't read spool file %s: %s",
868 eml_filename, strerror(err)),
869 malware_daemon_ctx.sock);
871 (void)close(drweb_fd);
873 /* send file body to socket */
874 if (send(malware_daemon_ctx.sock, drweb_fbuf, fsize, 0) < 0)
876 store_free(drweb_fbuf);
877 return m_panic_defer_3(scanent, CUS callout_address, string_sprintf(
878 "unable to send file body to socket (%s)", scanner_options),
879 malware_daemon_ctx.sock);
881 store_free(drweb_fbuf);
885 drweb_slen = htonl(Ustrlen(eml_filename));
887 DEBUG(D_acl) debug_printf_indent("Malware scan: issuing %s local scan [%s]\n",
888 scanner_name, scanner_options);
890 /* send scan request */
891 if ((send(malware_daemon_ctx.sock, &drweb_cmd, sizeof(drweb_cmd), 0) < 0) ||
892 (send(malware_daemon_ctx.sock, &drweb_flags, sizeof(drweb_flags), 0) < 0) ||
893 (send(malware_daemon_ctx.sock, &drweb_slen, sizeof(drweb_slen), 0) < 0) ||
894 (send(malware_daemon_ctx.sock, eml_filename, Ustrlen(eml_filename), 0) < 0) ||
895 (send(malware_daemon_ctx.sock, &drweb_fin, sizeof(drweb_fin), 0) < 0))
896 return m_panic_defer_3(scanent, CUS callout_address, string_sprintf(
897 "unable to send commands to socket (%s)", scanner_options),
898 malware_daemon_ctx.sock);
901 /* wait for result */
902 if (!recv_len(malware_daemon_ctx.sock, &drweb_rc, sizeof(drweb_rc), tmo))
903 return m_panic_defer_3(scanent, CUS callout_address,
904 US"unable to read return code", malware_daemon_ctx.sock);
905 drweb_rc = ntohl(drweb_rc);
907 if (!recv_len(malware_daemon_ctx.sock, &drweb_vnum, sizeof(drweb_vnum), tmo))
908 return m_panic_defer_3(scanent, CUS callout_address,
909 US"unable to read the number of viruses", malware_daemon_ctx.sock);
910 drweb_vnum = ntohl(drweb_vnum);
912 /* "virus(es) found" if virus number is > 0 */
917 /* setup default virus name */
918 malware_name = US"unknown";
920 /* set up match regex */
922 drweb_re = m_pcre_compile(drweb_re_str, &errstr);
924 /* read and concatenate virus names into one string */
925 for (int i = 0; i < drweb_vnum; i++)
929 /* read the size of report */
930 if (!recv_len(malware_daemon_ctx.sock, &drweb_slen, sizeof(drweb_slen), tmo))
931 return m_panic_defer_3(scanent, CUS callout_address,
932 US"cannot read report size", malware_daemon_ctx.sock);
933 drweb_slen = ntohl(drweb_slen);
935 /* assume tainted, since it is external input */
936 tmpbuf = store_get(drweb_slen, TRUE);
938 /* read report body */
939 if (!recv_len(malware_daemon_ctx.sock, tmpbuf, drweb_slen, tmo))
940 return m_panic_defer_3(scanent, CUS callout_address,
941 US"cannot read report string", malware_daemon_ctx.sock);
942 tmpbuf[drweb_slen] = '\0';
944 /* try matcher on the line, grab substring */
945 result = pcre_exec(drweb_re, NULL, CS tmpbuf, Ustrlen(tmpbuf), 0, 0,
946 ovector, nelem(ovector));
949 const char * pre_malware_nb;
951 pcre_get_substring(CS tmpbuf, ovector, result, 1, &pre_malware_nb);
953 if (i==0) /* the first name we just copy to malware_name */
954 g = string_cat(NULL, US pre_malware_nb);
956 /*XXX could be string_append_listele? */
957 else /* concatenate each new virus name to previous */
958 g = string_append(g, 2, "/", pre_malware_nb);
960 pcre_free_substring(pre_malware_nb);
963 malware_name = string_from_gstring(g);
967 const char *drweb_s = NULL;
969 if (drweb_rc & DERR_READ_ERR) drweb_s = "read error";
970 if (drweb_rc & DERR_NOMEMORY) drweb_s = "no memory";
971 if (drweb_rc & DERR_TIMEOUT) drweb_s = "timeout";
972 if (drweb_rc & DERR_BAD_CALL) drweb_s = "wrong command";
973 /* retcodes DERR_SYMLINK, DERR_NO_REGFILE, DERR_SKIPPED.
974 * DERR_TOO_BIG, DERR_TOO_COMPRESSED, DERR_SPAM,
975 * DERR_CRC_ERROR, DERR_READSOCKET, DERR_WRITE_ERR
976 * and others are ignored */
978 return m_panic_defer_3(scanent, CUS callout_address,
979 string_sprintf("drweb daemon retcode 0x%x (%s)", drweb_rc, drweb_s),
980 malware_daemon_ctx.sock);
989 #ifndef DISABLE_MAL_AVE
990 case M_AVES: /* "aveserver" scanner type -------------------------------- */
995 /* read aveserver's greeting and see if it is ready (2xx greeting) */
997 recv_line(malware_daemon_ctx.sock, buf, sizeof(buf), tmo);
999 if (buf[0] != '2') /* aveserver is having problems */
1000 return m_panic_defer_3(scanent, CUS callout_address,
1001 string_sprintf("unavailable (Responded: %s).",
1002 ((buf[0] != 0) ? buf : US "nothing") ),
1003 malware_daemon_ctx.sock);
1005 /* prepare our command */
1006 (void)string_format(buf, sizeof(buf), "SCAN bPQRSTUW %s\r\n",
1010 DEBUG(D_acl) debug_printf_indent("Malware scan: issuing %s %s\n",
1012 if (m_sock_send(malware_daemon_ctx.sock, buf, Ustrlen(buf), &errstr) < 0)
1013 return m_panic_defer(scanent, CUS callout_address, errstr);
1015 malware_name = NULL;
1017 /* read response lines, find malware name and final response */
1018 while (recv_line(malware_daemon_ctx.sock, buf, sizeof(buf), tmo) > 0)
1022 if (buf[0] == '5') /* aveserver is having problems */
1024 result = m_panic_defer(scanent, CUS callout_address,
1025 string_sprintf("unable to scan file %s (Responded: %s).",
1026 eml_filename, buf));
1029 if (Ustrncmp(buf,"322",3) == 0)
1031 uschar *p = Ustrchr(&buf[4], ' ');
1033 malware_name = string_copy(&buf[4]);
1037 if (m_sock_send(malware_daemon_ctx.sock, US"quit\r\n", 6, &errstr) < 0)
1038 return m_panic_defer(scanent, CUS callout_address, errstr);
1040 /* read aveserver's greeting and see if it is ready (2xx greeting) */
1042 recv_line(malware_daemon_ctx.sock, buf, sizeof(buf), tmo);
1044 if (buf[0] != '2') /* aveserver is having problems */
1045 return m_panic_defer_3(scanent, CUS callout_address,
1046 string_sprintf("unable to quit dialogue (Responded: %s).",
1047 ((buf[0] != 0) ? buf : US "nothing") ),
1048 malware_daemon_ctx.sock);
1050 if (result == DEFER)
1052 (void)close(malware_daemon_ctx.sock);
1059 #ifndef DISABLE_MAL_FSECURE
1060 case M_FSEC: /* "fsecure" scanner type ---------------------------------- */
1064 uschar av_buffer[1024];
1065 static uschar *cmdopt[] = { US"CONFIGURE\tARCHIVE\t1\n",
1066 US"CONFIGURE\tTIMEOUT\t0\n",
1067 US"CONFIGURE\tMAXARCH\t5\n",
1068 US"CONFIGURE\tMIME\t1\n" };
1070 malware_name = NULL;
1072 DEBUG(D_acl) debug_printf_indent("Malware scan: issuing %s scan [%s]\n",
1073 scanner_name, scanner_options);
1075 memset(av_buffer, 0, sizeof(av_buffer));
1076 for (i = 0; i != nelem(cmdopt); i++)
1079 if (m_sock_send(malware_daemon_ctx.sock, cmdopt[i], Ustrlen(cmdopt[i]), &errstr) < 0)
1080 return m_panic_defer(scanent, CUS callout_address, errstr);
1082 bread = ip_recv(&malware_daemon_ctx, av_buffer, sizeof(av_buffer), tmo);
1083 if (bread > 0) av_buffer[bread]='\0';
1085 return m_panic_defer_3(scanent, CUS callout_address,
1086 string_sprintf("unable to read answer %d (%s)", i, strerror(errno)),
1087 malware_daemon_ctx.sock);
1088 for (int j = 0; j < bread; j++)
1089 if (av_buffer[j] == '\r' || av_buffer[j] == '\n')
1093 /* pass the mailfile to fsecure */
1094 file_name = string_sprintf("SCAN\t%s\n", eml_filename);
1096 if (m_sock_send(malware_daemon_ctx.sock, file_name, Ustrlen(file_name), &errstr) < 0)
1097 return m_panic_defer(scanent, CUS callout_address, errstr);
1100 /* todo also SUSPICION\t */
1102 fsec_re = m_pcre_compile(fsec_re_str, &errstr);
1104 /* read report, linewise. Apply a timeout as the Fsecure daemon
1105 sometimes wants an answer to "PING" but they won't tell us what */
1107 uschar * p = av_buffer;
1113 i = av_buffer+sizeof(av_buffer)-p;
1114 if ((bread= ip_recv(&malware_daemon_ctx, p, i-1, tmo)) < 0)
1115 return m_panic_defer_3(scanent, CUS callout_address,
1116 string_sprintf("unable to read result (%s)", strerror(errno)),
1117 malware_daemon_ctx.sock);
1119 for (p[bread] = '\0'; (q = Ustrchr(p, '\n')); p = q+1)
1123 /* Really search for virus again? */
1125 /* try matcher on the line, grab substring */
1126 malware_name = m_pcre_exec(fsec_re, p);
1128 if (Ustrstr(p, "OK\tScan ok."))
1132 /* copy down the trailing partial line then read another chunk */
1133 i = av_buffer+sizeof(av_buffer)-p;
1134 memmove(av_buffer, p, i);
1144 #ifndef DISABLE_MAL_KAV
1145 case M_KAVD: /* "kavdaemon" scanner type -------------------------------- */
1148 uschar tmpbuf[1024];
1149 uschar * scanrequest;
1151 unsigned long kav_reportlen;
1156 /* get current date and time, build scan request */
1158 /* pdp note: before the eml_filename parameter, this scanned the
1159 directory; not finding documentation, so we'll strip off the directory.
1160 The side-effect is that the test framework scanning may end up in
1161 scanning more than was requested, but for the normal interface, this is
1164 strftime(CS tmpbuf, sizeof(tmpbuf), "%d %b %H:%M:%S", localtime(&t));
1165 scanrequest = string_sprintf("<0>%s:%s", CS tmpbuf, eml_filename);
1166 p = Ustrrchr(scanrequest, '/');
1170 DEBUG(D_acl) debug_printf_indent("Malware scan: issuing %s scan [%s]\n",
1171 scanner_name, scanner_options);
1173 /* send scan request */
1174 if (m_sock_send(malware_daemon_ctx.sock, scanrequest, Ustrlen(scanrequest)+1, &errstr) < 0)
1175 return m_panic_defer(scanent, CUS callout_address, errstr);
1177 /* wait for result */
1178 if (!recv_len(malware_daemon_ctx.sock, tmpbuf, 2, tmo))
1179 return m_panic_defer_3(scanent, CUS callout_address,
1180 US"unable to read 2 bytes from socket.", malware_daemon_ctx.sock);
1182 /* get errorcode from one nibble */
1183 kav_rc = tmpbuf[ test_byte_order()==LITTLE_MY_ENDIAN ? 0 : 1 ] & 0x0F;
1186 case 5: case 6: /* improper kavdaemon configuration */
1187 return m_panic_defer_3(scanent, CUS callout_address,
1188 US"please reconfigure kavdaemon to NOT disinfect or remove infected files.",
1189 malware_daemon_ctx.sock);
1191 return m_panic_defer_3(scanent, CUS callout_address,
1192 US"reported 'scanning not completed' (code 1).", malware_daemon_ctx.sock);
1194 return m_panic_defer_3(scanent, CUS callout_address,
1195 US"reported 'kavdaemon damaged' (code 7).", malware_daemon_ctx.sock);
1198 /* code 8 is not handled, since it is ambiguous. It appears mostly on
1199 bounces where part of a file has been cut off */
1201 /* "virus found" return codes (2-4) */
1202 if (kav_rc > 1 && kav_rc < 5)
1204 int report_flag = 0;
1206 /* setup default virus name */
1207 malware_name = US"unknown";
1209 report_flag = tmpbuf[ test_byte_order() == LITTLE_MY_ENDIAN ? 1 : 0 ];
1211 /* read the report, if available */
1212 if (report_flag == 1)
1214 /* read report size */
1215 if (!recv_len(malware_daemon_ctx.sock, &kav_reportlen, 4, tmo))
1216 return m_panic_defer_3(scanent, CUS callout_address,
1217 US"cannot read report size", malware_daemon_ctx.sock);
1219 /* it's possible that avp returns av_buffer[1] == 1 but the
1220 reportsize is 0 (!?) */
1221 if (kav_reportlen > 0)
1223 /* set up match regex, depends on retcode */
1226 if (!kav_re_sus) kav_re_sus = m_pcre_compile(kav_re_sus_str, &errstr);
1227 kav_re = kav_re_sus;
1231 if (!kav_re_inf) kav_re_inf = m_pcre_compile(kav_re_inf_str, &errstr);
1232 kav_re = kav_re_inf;
1235 /* read report, linewise. Using size from stream to read amount of data
1236 from same stream is safe enough. */
1237 /* coverity[tainted_data] */
1238 while (kav_reportlen > 0)
1240 if ((bread = recv_line(malware_daemon_ctx.sock, tmpbuf, sizeof(tmpbuf), tmo)) < 0)
1242 kav_reportlen -= bread+1;
1244 /* try matcher on the line, grab substring */
1245 if ((malware_name = m_pcre_exec(kav_re, tmpbuf)))
1251 else /* no virus found */
1252 malware_name = NULL;
1258 #ifndef DISABLE_MAL_CMDLINE
1259 case M_CMDL: /* "cmdline" scanner type ---------------------------------- */
1261 const uschar *cmdline_scanner = scanner_options;
1262 const pcre *cmdline_trigger_re;
1263 const pcre *cmdline_regex_re;
1265 uschar * commandline;
1266 void (*eximsigchld)(int);
1267 void (*eximsigpipe)(int);
1268 FILE *scanner_out = NULL;
1270 FILE *scanner_record = NULL;
1271 uschar linebuffer[32767];
1276 if (!cmdline_scanner)
1277 return m_panic_defer(scanent, NULL, errstr);
1279 /* find scanner output trigger */
1280 cmdline_trigger_re = m_pcre_nextinlist(&av_scanner_work, &sep,
1281 "missing trigger specification", &errstr);
1282 if (!cmdline_trigger_re)
1283 return m_panic_defer(scanent, NULL, errstr);
1285 /* find scanner name regex */
1286 cmdline_regex_re = m_pcre_nextinlist(&av_scanner_work, &sep,
1287 "missing virus name regex specification", &errstr);
1288 if (!cmdline_regex_re)
1289 return m_panic_defer(scanent, NULL, errstr);
1291 /* prepare scanner call; despite the naming, file_name holds a directory
1292 name which is documented as the value given to %s. */
1294 file_name = string_copy(eml_filename);
1295 p = Ustrrchr(file_name, '/');
1298 commandline = string_sprintf(CS cmdline_scanner, file_name);
1300 /* redirect STDERR too */
1301 commandline = string_sprintf("%s 2>&1", commandline);
1303 DEBUG(D_acl) debug_printf_indent("Malware scan: issuing %s scan [%s]\n",
1304 scanner_name, commandline);
1306 /* store exims signal handlers */
1307 eximsigchld = signal(SIGCHLD,SIG_DFL);
1308 eximsigpipe = signal(SIGPIPE,SIG_DFL);
1310 if (!(scanner_out = popen(CS commandline,"r")))
1313 signal(SIGCHLD,eximsigchld); signal(SIGPIPE,eximsigpipe);
1314 return m_panic_defer(scanent, NULL,
1315 string_sprintf("call (%s) failed: %s.", commandline, strerror(err)));
1317 scanner_fd = fileno(scanner_out);
1319 file_name = string_sprintf("%s/%s_scanner_output", eml_dir, message_id);
1321 if (!(scanner_record = modefopen(file_name, "wb", SPOOL_MODE)))
1324 (void) pclose(scanner_out);
1325 signal(SIGCHLD,eximsigchld); signal(SIGPIPE,eximsigpipe);
1326 return m_panic_defer(scanent, NULL, string_sprintf(
1327 "opening scanner output file (%s) failed: %s.",
1328 file_name, strerror(err)));
1331 /* look for trigger while recording output */
1332 while ((rcnt = recv_line(scanner_fd, linebuffer,
1333 sizeof(linebuffer), tmo)))
1340 (void) pclose(scanner_out);
1341 signal(SIGCHLD,eximsigchld); signal(SIGPIPE,eximsigpipe);
1342 return m_panic_defer(scanent, NULL, string_sprintf(
1343 "unable to read from scanner (%s): %s",
1344 commandline, strerror(err)));
1347 if (Ustrlen(linebuffer) > fwrite(linebuffer, 1, Ustrlen(linebuffer), scanner_record))
1350 (void) pclose(scanner_out);
1351 signal(SIGCHLD,eximsigchld); signal(SIGPIPE,eximsigpipe);
1352 return m_panic_defer(scanent, NULL, string_sprintf(
1353 "short write on scanner output file (%s).", file_name));
1355 putc('\n', scanner_record);
1356 /* try trigger match */
1358 && regex_match_and_setup(cmdline_trigger_re, linebuffer, 0, -1)
1363 (void)fclose(scanner_record);
1364 sep = pclose(scanner_out);
1365 signal(SIGCHLD,eximsigchld); signal(SIGPIPE,eximsigpipe);
1367 return m_panic_defer(scanent, NULL,
1369 ? string_sprintf("running scanner failed: %s", strerror(sep))
1370 : string_sprintf("scanner returned error code: %d", sep));
1375 /* setup default virus name */
1376 malware_name = US"unknown";
1378 /* re-open the scanner output file, look for name match */
1379 scanner_record = Ufopen(file_name, "rb");
1380 while (Ufgets(linebuffer, sizeof(linebuffer), scanner_record))
1381 if ((s = m_pcre_exec(cmdline_regex_re, linebuffer))) /* try match */
1383 (void)fclose(scanner_record);
1385 else /* no virus found */
1386 malware_name = NULL;
1391 #ifndef DISABLE_MAL_SOPHIE
1392 case M_SOPHIE: /* "sophie" scanner type --------------------------------- */
1397 uschar av_buffer[1024];
1399 /* pass the scan directory to sophie */
1400 file_name = string_copy(eml_filename);
1401 if ((p = Ustrrchr(file_name, '/')))
1404 DEBUG(D_acl) debug_printf_indent("Malware scan: issuing %s scan [%s]\n",
1405 scanner_name, scanner_options);
1407 if ( write(malware_daemon_ctx.sock, file_name, Ustrlen(file_name)) < 0
1408 || write(malware_daemon_ctx.sock, "\n", 1) != 1
1410 return m_panic_defer_3(scanent, CUS callout_address,
1411 string_sprintf("unable to write to UNIX socket (%s)", scanner_options),
1412 malware_daemon_ctx.sock);
1414 /* wait for result */
1415 memset(av_buffer, 0, sizeof(av_buffer));
1416 if ((bread = ip_recv(&malware_daemon_ctx, av_buffer, sizeof(av_buffer), tmo)) <= 0)
1417 return m_panic_defer_3(scanent, CUS callout_address,
1418 string_sprintf("unable to read from UNIX socket (%s)", scanner_options),
1419 malware_daemon_ctx.sock);
1422 if (av_buffer[0] == '1') {
1423 uschar * s = Ustrchr(av_buffer, '\n');
1426 malware_name = string_copy(&av_buffer[2]);
1428 else if (!strncmp(CS av_buffer, "-1", 2))
1429 return m_panic_defer_3(scanent, CUS callout_address,
1430 US"scanner reported error", malware_daemon_ctx.sock);
1431 else /* all ok, no virus */
1432 malware_name = NULL;
1438 #ifndef DISABLE_MAL_CLAM
1439 case M_CLAMD: /* "clamd" scanner type ----------------------------------- */
1441 /* This code was originally contributed by David Saez */
1442 /* There are three scanning methods available to us:
1443 * (1) Use the SCAN command, pointing to a file in the filesystem
1444 * (2) Use the STREAM command, send the data on a separate port
1445 * (3) Use the zINSTREAM command, send the data inline
1446 * The zINSTREAM command was introduced with ClamAV 0.95, which marked
1447 * STREAM deprecated; see: http://wiki.clamav.net/bin/view/Main/UpgradeNotes095
1448 * In Exim, we use SCAN if using a Unix-domain socket or explicitly told that
1449 * the TCP-connected daemon is actually local; otherwise we use zINSTREAM
1450 * See Exim bug 926 for details. */
1452 uschar *p, *vname, *result_tag;
1454 uschar av_buffer[1024];
1455 uschar *hostname = US"";
1457 uschar *clamav_fbuf;
1458 int clam_fd, result;
1459 unsigned int fsize_uint;
1460 BOOL use_scan_command = FALSE;
1461 clamd_address * cv[MAX_CLAMD_SERVERS];
1462 int num_servers = 0;
1463 uint32_t send_size, send_final_zeroblock;
1466 /*XXX if unixdomain socket, only one server supported. Needs fixing;
1467 there's no reason we should not mix local and remote servers */
1469 if (*scanner_options == '/')
1472 const uschar * sublist;
1475 /* Local file; so we def want to use_scan_command and don't want to try
1476 * passing IP/port combinations */
1477 use_scan_command = TRUE;
1478 cd = (clamd_address *) store_get(sizeof(clamd_address), FALSE);
1480 /* extract socket-path part */
1481 sublist = scanner_options;
1482 cd->hostspec = string_nextinlist(&sublist, &subsep, NULL, 0);
1485 if (clamd_option(cd, sublist, &subsep) != OK)
1486 return m_panic_defer(scanent, NULL,
1487 string_sprintf("bad option '%s'", scanner_options));
1492 /* Go through the rest of the list of host/port and construct an array
1493 * of servers to try. The first one is the bit we just passed from
1494 * scanner_options so process that first and then scan the remainder of
1495 * the address buffer */
1499 const uschar * sublist;
1503 /* The 'local' option means use the SCAN command over the network
1504 * socket (ie common file storage in use) */
1505 /*XXX we could accept this also as a local option? */
1506 if (strcmpic(scanner_options, US"local") == 0)
1508 use_scan_command = TRUE;
1512 cd = (clamd_address *) store_get(sizeof(clamd_address), FALSE);
1514 /* extract host and port part */
1515 sublist = scanner_options;
1516 if (!(cd->hostspec = string_nextinlist(&sublist, &subsep, NULL, 0)))
1518 (void) m_panic_defer(scanent, NULL,
1519 string_sprintf("missing address: '%s'", scanner_options));
1522 if (!(s = string_nextinlist(&sublist, &subsep, NULL, 0)))
1524 (void) m_panic_defer(scanent, NULL,
1525 string_sprintf("missing port: '%s'", scanner_options));
1528 cd->tcp_port = atoi(CS s);
1531 /*XXX should these options be common over scanner types? */
1532 if (clamd_option(cd, sublist, &subsep) != OK)
1533 return m_panic_defer(scanent, NULL,
1534 string_sprintf("bad option '%s'", scanner_options));
1536 cv[num_servers++] = cd;
1537 if (num_servers >= MAX_CLAMD_SERVERS)
1539 (void) m_panic_defer(scanent, NULL,
1540 US"More than " MAX_CLAMD_SERVERS_S " clamd servers "
1541 "specified; only using the first " MAX_CLAMD_SERVERS_S );
1544 } while ((scanner_options = string_nextinlist(&av_scanner_work, &sep,
1547 /* check if we have at least one server */
1549 return m_panic_defer(scanent, NULL,
1550 US"no useable server addresses in malware configuration option.");
1553 /* See the discussion of response formats below to see why we really
1554 don't like colons in filenames when passing filenames to ClamAV. */
1555 if (use_scan_command && Ustrchr(eml_filename, ':'))
1556 return m_panic_defer(scanent, NULL,
1557 string_sprintf("local/SCAN mode incompatible with" \
1558 " : in path to email filename [%s]", eml_filename));
1560 /* Set up the very first data we will be sending */
1561 if (!use_scan_command)
1562 { cmd_str.data = US"zINSTREAM"; cmd_str.len = 10; }
1565 cmd_str.data = string_sprintf("SCAN %s\n", eml_filename);
1566 cmd_str.len = Ustrlen(cmd_str.data);
1569 /* We have some network servers specified */
1572 /* Confirmed in ClamAV source (0.95.3) that the TCPAddr option of clamd
1573 * only supports AF_INET, but we should probably be looking to the
1574 * future and rewriting this to be protocol-independent anyway. */
1576 while (num_servers > 0)
1578 int i = random_number(num_servers);
1579 clamd_address * cd = cv[i];
1581 DEBUG(D_acl) debug_printf_indent("trying server name %s, port %u\n",
1582 cd->hostspec, cd->tcp_port);
1584 /* Lookup the host. This is to ensure that we connect to the same IP
1585 * on both connections (as one host could resolve to multiple ips) */
1588 /*XXX we trust that the cmd_str is ideempotent */
1589 if ((malware_daemon_ctx.sock = m_tcpsocket(cd->hostspec, cd->tcp_port,
1590 &connhost, &errstr, &cmd_str)) >= 0)
1592 /* Connection successfully established with a server */
1593 hostname = cd->hostspec;
1597 if (cd->retry <= 0) break;
1598 while (cd->retry > 0) cd->retry = sleep(cd->retry);
1600 if (malware_daemon_ctx.sock >= 0)
1603 (void) m_panic_defer(scanent, CUS callout_address, errstr);
1605 /* Remove the server from the list. XXX We should free the memory */
1607 for (; i < num_servers; i++)
1611 if (num_servers == 0)
1612 return m_panic_defer(scanent, NULL, US"all servers failed");
1617 if ((malware_daemon_ctx.sock = ip_unixsocket(cv[0]->hostspec, &errstr)) >= 0)
1619 hostname = cv[0]->hostspec;
1622 if (cv[0]->retry <= 0)
1623 return m_panic_defer(scanent, CUS callout_address, errstr);
1624 while (cv[0]->retry > 0) cv[0]->retry = sleep(cv[0]->retry);
1627 /* have socket in variable "sock"; command to use is semi-independent of
1628 * the socket protocol. We use SCAN if is local (either Unix/local
1629 * domain socket, or explicitly told local) else we stream the data.
1630 * How we stream the data depends upon how we were built. */
1632 if (!use_scan_command)
1635 /* New protocol: "zINSTREAM\n" followed by a sequence of <length><data>
1636 chunks, <n> a 4-byte number (network order), terminated by a zero-length
1639 DEBUG(D_acl) debug_printf_indent(
1640 "Malware scan: issuing %s new-style remote scan (zINSTREAM)\n",
1643 /* Pass the string to ClamAV (10 = "zINSTREAM\0"), if not already sent */
1645 if (send(malware_daemon_ctx.sock, cmd_str.data, cmd_str.len, 0) < 0)
1646 return m_panic_defer_3(scanent, CUS hostname,
1647 string_sprintf("unable to send zINSTREAM to socket (%s)",
1649 malware_daemon_ctx.sock);
1651 if ((clam_fd = exim_open2(CS eml_filename, O_RDONLY)) < 0)
1654 return m_panic_defer_3(scanent, NULL,
1655 string_sprintf("can't open spool file %s: %s",
1656 eml_filename, strerror(err)),
1657 malware_daemon_ctx.sock);
1659 if (fstat(clam_fd, &st) < 0)
1662 (void)close(clam_fd);
1663 return m_panic_defer_3(scanent, NULL,
1664 string_sprintf("can't stat spool file %s: %s",
1665 eml_filename, strerror(err)),
1666 malware_daemon_ctx.sock);
1668 fsize_uint = (unsigned int) st.st_size;
1669 if ((off_t)fsize_uint != st.st_size)
1671 (void)close(clam_fd);
1672 return m_panic_defer_3(scanent, NULL,
1673 string_sprintf("stat spool file %s, size overflow", eml_filename),
1674 malware_daemon_ctx.sock);
1677 /* send file body to socket */
1678 send_size = htonl(fsize_uint);
1679 if (send(malware_daemon_ctx.sock, &send_size, sizeof(send_size), 0) < 0)
1680 return m_panic_defer_3(scanent, NULL,
1681 string_sprintf("unable to send file size to socket (%s)", hostname),
1682 malware_daemon_ctx.sock);
1686 unsigned n = MIN(fsize_uint, big_buffer_size);
1687 if ((n = read(clam_fd, big_buffer, n)) < 0)
1688 return m_panic_defer_3(scanent, NULL,
1689 string_sprintf("can't read spool file %s: %s",
1690 eml_filename, strerror(errno)),
1691 malware_daemon_ctx.sock);
1692 if ((n = send(malware_daemon_ctx.sock, clamav_fbuf, n, 0)) < 0)
1693 return m_panic_defer_3(scanent, NULL,
1694 string_sprintf("unable to send file body to socket (%s)", hostname),
1695 malware_daemon_ctx.sock);
1699 send_final_zeroblock = 0;
1700 if (send(malware_daemon_ctx.sock, &send_final_zeroblock, sizeof(send_final_zeroblock), 0) < 0)
1701 return m_panic_defer_3(scanent, NULL,
1702 string_sprintf("unable to send file terminator to socket (%s)", hostname),
1703 malware_daemon_ctx.sock);
1706 { /* use scan command */
1707 /* Send a SCAN command pointing to a filename; then in the then in the
1708 * scan-method-neutral part, read the response back */
1710 /* ================================================================= */
1712 /* Prior to the reworking post-Exim-4.72, this scanned a directory,
1713 which dates to when ClamAV needed us to break apart the email into the
1714 MIME parts (eg, with the now deprecated demime condition coming first).
1715 Some time back, ClamAV gained the ability to deconstruct the emails, so
1716 doing this would actually have resulted in the mail attachments being
1717 scanned twice, in the broken out files and from the original .eml.
1718 Since ClamAV now handles emails (and has for quite some time) we can
1719 just use the email file itself. */
1720 /* Pass the string to ClamAV (7 = "SCAN \n" + \0), if not already sent */
1722 DEBUG(D_acl) debug_printf_indent(
1723 "Malware scan: issuing %s local-path scan [%s]\n",
1724 scanner_name, scanner_options);
1727 if (send(malware_daemon_ctx.sock, cmd_str.data, cmd_str.len, 0) < 0)
1728 return m_panic_defer_3(scanent, CUS callout_address,
1729 string_sprintf("unable to write to socket (%s)", strerror(errno)),
1730 malware_daemon_ctx.sock);
1732 /* Do not shut down the socket for writing; a user report noted that
1733 * clamd 0.70 does not react well to this. */
1735 /* Commands have been sent, no matter which scan method or connection
1736 * type we're using; now just read the result, independent of method. */
1738 /* Read the result */
1739 memset(av_buffer, 0, sizeof(av_buffer));
1740 bread = ip_recv(&malware_daemon_ctx, av_buffer, sizeof(av_buffer), tmo);
1741 (void)close(malware_daemon_ctx.sock);
1742 malware_daemon_ctx.sock = -1;
1743 malware_daemon_ctx.tls_ctx = NULL;
1746 return m_panic_defer(scanent, CUS callout_address,
1747 string_sprintf("unable to read from socket (%s)",
1748 errno == 0 ? "EOF" : strerror(errno)));
1750 if (bread == sizeof(av_buffer))
1751 return m_panic_defer(scanent, CUS callout_address,
1752 US"buffer too small");
1753 /* We're now assured of a NULL at the end of av_buffer */
1755 /* Check the result. ClamAV returns one of two result formats.
1756 In the basic mode, the response is of the form:
1757 infected: -> "<filename>: <virusname> FOUND"
1758 not-infected: -> "<filename>: OK"
1759 error: -> "<filename>: <errcode> ERROR
1760 If the ExtendedDetectionInfo option has been turned on, then we get:
1761 "<filename>: <virusname>(<virushash>:<virussize>) FOUND"
1762 for the infected case. Compare:
1763 /tmp/eicar.com: Eicar-Test-Signature FOUND
1764 /tmp/eicar.com: Eicar-Test-Signature(44d88612fea8a8f36de82e1278abb02f:68) FOUND
1766 In the streaming case, clamd uses the filename "stream" which you should
1767 be able to verify with { ktrace clamdscan --stream /tmp/eicar.com }. (The
1768 client app will replace "stream" with the original filename before returning
1769 results to stdout, but the trace shows the data).
1771 We will assume that the pathname passed to clamd from Exim does not contain
1772 a colon. We will have whined loudly above if the eml_filename does (and we're
1773 passing a filename to clamd). */
1776 return m_panic_defer(scanent, CUS callout_address,
1777 US"ClamAV returned null");
1779 /* strip newline at the end (won't be present for zINSTREAM)
1780 (also any trailing whitespace, which shouldn't exist, but we depend upon
1781 this below, so double-check) */
1782 p = av_buffer + Ustrlen(av_buffer) - 1;
1783 if (*p == '\n') *p = '\0';
1785 DEBUG(D_acl) debug_printf_indent("Malware response: %s\n", av_buffer);
1787 while (isspace(*--p) && (p > av_buffer))
1791 /* colon in returned output? */
1792 if(!(p = Ustrchr(av_buffer,':')))
1793 return m_panic_defer(scanent, CUS callout_address, string_sprintf(
1794 "ClamAV returned malformed result (missing colon): %s",
1797 /* strip filename */
1798 while (*p && isspace(*++p)) /**/;
1801 /* It would be bad to encounter a virus with "FOUND" in part of the name,
1802 but we should at least be resistant to it. */
1803 p = Ustrrchr(vname, ' ');
1804 result_tag = p ? p+1 : vname;
1806 if (Ustrcmp(result_tag, "FOUND") == 0)
1808 /* p should still be the whitespace before the result_tag */
1809 while (isspace(*p)) --p;
1811 /* Strip off the extended information too, which will be in parens
1812 after the virus name, with no intervening whitespace. */
1815 /* "(hash:size)", so previous '(' will do; if not found, we have
1816 a curious virus name, but not an error. */
1817 p = Ustrrchr(vname, '(');
1821 malware_name = string_copy(vname);
1822 DEBUG(D_acl) debug_printf_indent("Malware found, name \"%s\"\n", malware_name);
1825 else if (Ustrcmp(result_tag, "ERROR") == 0)
1826 return m_panic_defer(scanent, CUS callout_address,
1827 string_sprintf("ClamAV returned: %s", av_buffer));
1829 else if (Ustrcmp(result_tag, "OK") == 0)
1831 /* Everything should be OK */
1832 malware_name = NULL;
1833 DEBUG(D_acl) debug_printf_indent("Malware not found\n");
1837 return m_panic_defer(scanent, CUS callout_address,
1838 string_sprintf("unparseable response from ClamAV: {%s}", av_buffer));
1844 #ifndef DISABLE_MAL_SOCK
1845 case M_SOCK: /* "sock" scanner type ------------------------------------- */
1846 /* This code was derived by Martin Poole from the clamd code contributed
1847 by David Saez and the cmdline code
1851 uschar * commandline;
1852 uschar av_buffer[1024];
1853 uschar * linebuffer;
1854 uschar * sockline_scanner;
1855 uschar sockline_scanner_default[] = "%s\n";
1856 const pcre *sockline_trig_re;
1857 const pcre *sockline_name_re;
1859 /* find scanner command line */
1860 if ( (sockline_scanner = string_nextinlist(&av_scanner_work, &sep,
1862 && *sockline_scanner
1864 { /* check for no expansions apart from one %s */
1865 uschar * s = Ustrchr(sockline_scanner, '%');
1867 if ((*s != 's' && *s != '%') || Ustrchr(s+1, '%'))
1868 return m_panic_defer_3(scanent, NULL,
1869 US"unsafe sock scanner call spec", malware_daemon_ctx.sock);
1872 sockline_scanner = sockline_scanner_default;
1873 DEBUG(D_acl) debug_printf_indent("%15s%10s'%s'\n", "", "cmdline: ",
1874 string_printing(sockline_scanner));
1876 /* find scanner output trigger */
1877 sockline_trig_re = m_pcre_nextinlist(&av_scanner_work, &sep,
1878 "missing trigger specification", &errstr);
1879 if (!sockline_trig_re)
1880 return m_panic_defer_3(scanent, NULL, errstr, malware_daemon_ctx.sock);
1882 /* find virus name regex */
1883 sockline_name_re = m_pcre_nextinlist(&av_scanner_work, &sep,
1884 "missing virus name regex specification", &errstr);
1885 if (!sockline_name_re)
1886 return m_panic_defer_3(scanent, NULL, errstr, malware_daemon_ctx.sock);
1888 /* prepare scanner call - security depends on expansions check above */
1889 commandline = string_sprintf( CS sockline_scanner, CS eml_filename);
1890 DEBUG(D_acl) debug_printf_indent("%15s%10s'%s'\n", "", "expanded: ",
1891 string_printing(commandline));
1893 /* Pass the command string to the socket */
1894 if (m_sock_send(malware_daemon_ctx.sock, commandline, Ustrlen(commandline), &errstr) < 0)
1895 return m_panic_defer(scanent, CUS callout_address, errstr);
1897 /* Read the result */
1898 bread = ip_recv(&malware_daemon_ctx, av_buffer, sizeof(av_buffer), tmo);
1901 return m_panic_defer_3(scanent, CUS callout_address,
1902 string_sprintf("unable to read from socket (%s)", strerror(errno)),
1903 malware_daemon_ctx.sock);
1905 if (bread == sizeof(av_buffer))
1906 return m_panic_defer_3(scanent, CUS callout_address,
1907 US"buffer too small", malware_daemon_ctx.sock);
1908 av_buffer[bread] = '\0';
1909 linebuffer = string_copy(av_buffer);
1910 DEBUG(D_acl) debug_printf_indent("%15s%10s'%s'\n", "", "answer: ",
1911 string_printing(linebuffer));
1913 /* try trigger match */
1914 if (regex_match_and_setup(sockline_trig_re, linebuffer, 0, -1))
1916 if (!(malware_name = m_pcre_exec(sockline_name_re, av_buffer)))
1917 malware_name = US "unknown";
1918 DEBUG(D_acl) debug_printf_indent("%15s%10s'%s'\n", "", "name: ",
1919 string_printing(malware_name));
1921 else /* no virus found */
1922 malware_name = NULL;
1927 #ifndef DISABLE_MAL_MKS
1928 case M_MKSD: /* "mksd" scanner type ------------------------------------- */
1930 char *mksd_options_end;
1931 int mksd_maxproc = 1; /* default, if no option supplied */
1934 if (scanner_options)
1936 mksd_maxproc = (int)strtol(CS scanner_options, &mksd_options_end, 10);
1937 if ( *scanner_options == '\0'
1938 || *mksd_options_end != '\0'
1940 || mksd_maxproc > 32
1942 return m_panic_defer(scanent, CUS callout_address,
1943 string_sprintf("invalid option '%s'", scanner_options));
1946 if((malware_daemon_ctx.sock = ip_unixsocket(US "/var/run/mksd/socket", &errstr)) < 0)
1947 return m_panic_defer(scanent, CUS callout_address, errstr);
1949 malware_name = NULL;
1951 DEBUG(D_acl) debug_printf_indent("Malware scan: issuing %s scan\n", scanner_name);
1953 if ((retval = mksd_scan_packed(scanent, malware_daemon_ctx.sock, eml_filename, tmo)) != OK)
1955 close (malware_daemon_ctx.sock);
1962 #ifndef DISABLE_MAL_AVAST
1963 case M_AVAST: /* "avast" scanner type ----------------------------------- */
1966 uschar * scanrequest;
1967 enum {AVA_HELO, AVA_OPT, AVA_RSP, AVA_DONE} avast_stage;
1969 uschar * error_message = NULL;
1970 BOOL more_data = FALSE;
1973 /* According to Martin Tuma @avast the protocol uses "escaped
1974 whitespace", that is, every embedded whitespace is backslash
1975 escaped, as well as backslash is protected by backslash.
1976 The returned lines contain the name of the scanned file, a tab
1980 [E] - some error occurred
1981 Such marker follows the first non-escaped TAB. For more information
1982 see avast-protocol(5)
1984 We observed two cases:
1986 <- /file [E]0.0 Error 13 Permission denied
1987 <- 451 SCAN Engine error 13 permission denied
1990 <- /file… [E]3.0 Error 41120 The file is a decompression bomb
1992 <- /file… [+]2.0 0 Eicar Test Virus!!!
1995 If the scanner returns 4xx, DEFER is a good decision, combined
1996 with a panic log entry, to get the admin's attention.
1998 If the scanner returns 200, we reject it as malware, if found any,
1999 or, in case of an error, we set the malware message to the error
2002 Some of the >= 42000 errors are message related - usually some
2003 broken archives etc, but some of them are e.g. license related.
2004 Once the license expires the engine starts returning errors for
2005 every scanning attempt. I¹ have the full list of the error codes
2006 but it is not a public API and is subject to change. It is hard
2007 for me to say what you should do in case of an engine error. You
2008 can have a “Treat * unscanned file as infection” policy or “Treat
2009 unscanned file as clean” policy. ¹) Jakub Bednar
2013 if ( ( !ava_re_clean
2014 && !(ava_re_clean = m_pcre_compile(ava_re_clean_str, &errstr)))
2016 && !(ava_re_virus = m_pcre_compile(ava_re_virus_str, &errstr)))
2018 && !(ava_re_error = m_pcre_compile(ava_re_error_str, &errstr)))
2020 return malware_panic_defer(errstr);
2022 /* wait for result */
2023 for (avast_stage = AVA_HELO;
2024 (nread = recv_line(malware_daemon_ctx.sock, buf, sizeof(buf), tmo)) > 0;
2027 int slen = Ustrlen(buf);
2031 /* Multi line responses are bracketed between 210 … and nnn … */
2032 if (Ustrncmp(buf, "210", 3) == 0)
2037 else if (more_data && isdigit(buf[0])) more_data = 0;
2039 switch (avast_stage)
2042 if (more_data) continue;
2043 if (Ustrncmp(buf, "220", 3) != 0)
2044 goto endloop; /* require a 220 */
2048 if (more_data) continue;
2049 if (Ustrncmp(buf, "200", 3) != 0)
2050 goto endloop; /* require a 200 */
2055 /* Check for another option to send. Newline-terminate it. */
2056 if ((scanrequest = string_nextinlist(&av_scanner_work, &sep,
2059 if (Ustrcmp(scanrequest, "pass_unscanned") == 0)
2061 DEBUG(D_acl) debug_printf_indent("pass unscanned files as clean\n");
2065 scanrequest = string_sprintf("%s\n", scanrequest);
2066 avast_stage = AVA_OPT; /* just sent option */
2067 DEBUG(D_acl) debug_printf_indent("send to avast OPTION: %s", scanrequest);
2071 scanrequest = string_sprintf("SCAN %s\n", eml_dir);
2072 avast_stage = AVA_RSP; /* just sent command */
2073 DEBUG(D_acl) debug_printf_indent("send to avast REQUEST: SCAN %s\n", eml_dir);
2076 /* send config-cmd or scan-request to socket */
2077 len = Ustrlen(scanrequest);
2078 if (send(malware_daemon_ctx.sock, scanrequest, len, 0) == -1)
2080 scanrequest[len-1] = '\0';
2081 return m_panic_defer_3(scanent, CUS callout_address, string_sprintf(
2082 "unable to send request '%s' to socket (%s): %s",
2083 scanrequest, scanner_options, strerror(errno)), malware_daemon_ctx.sock);
2090 if (isdigit(buf[0])) /* We're done */
2093 if (malware_name) /* Nothing else matters, just read on */
2096 if (pcre_exec(ava_re_clean, NULL, CS buf, slen, 0, 0, NULL, 0) == 0)
2099 if ((malware_name = m_pcre_exec(ava_re_virus, buf)))
2101 unescape(malware_name);
2103 debug_printf_indent("unescaped malware name: '%s'\n", malware_name);
2107 if (strict) /* treat scanner errors as malware */
2109 if ((malware_name = m_pcre_exec(ava_re_error, buf)))
2111 unescape(malware_name);
2113 debug_printf_indent("unescaped error message: '%s'\n", malware_name);
2117 else if (pcre_exec(ava_re_error, NULL, CS buf, slen, 0, 0, NULL, 0) == 0)
2119 log_write(0, LOG_MAIN, "internal scanner error (ignored): %s", buf);
2123 /* here also for any unexpected response from the scanner */
2124 DEBUG(D_acl) debug_printf("avast response not handled: '%s'\n", buf);
2128 default: log_write(0, LOG_PANIC, "%s:%d:%s: should not happen",
2129 __FILE__, __LINE__, __FUNCTION__);
2136 if (nread == -1) error_message = US"EOF from scanner";
2137 else if (nread < 0) error_message = US"timeout from scanner";
2138 else if (nread == 0) error_message = US"got nothing from scanner";
2139 else if (buf[0] != '2') error_message = buf;
2141 DEBUG(D_acl) debug_printf_indent("sent to avast QUIT\n");
2142 if (send(malware_daemon_ctx.sock, "QUIT\n", 5, 0) == -1)
2143 return m_panic_defer_3(scanent, CUS callout_address,
2144 string_sprintf("unable to send quit request to socket (%s): %s",
2145 scanner_options, strerror(errno)), malware_daemon_ctx.sock);
2148 return m_panic_defer_3(scanent, CUS callout_address, error_message, malware_daemon_ctx.sock);
2152 } /* scanner type switch */
2154 if (malware_daemon_ctx.sock >= 0)
2155 (void) close (malware_daemon_ctx.sock);
2156 malware_ok = TRUE; /* set "been here, done that" marker */
2159 /* match virus name against pattern (caseless ------->----------v) */
2160 if (malware_name && regex_match_and_setup(re, malware_name, 0, -1))
2162 DEBUG(D_acl) debug_printf_indent(
2163 "Matched regex to malware [%s] [%s]\n", malware_re, malware_name);
2171 /*************************************************
2172 * Scan an email for malware *
2173 *************************************************/
2175 /* This is the normal interface for scanning an email, which doesn't need a
2176 filename; it's a wrapper around the malware_file function.
2179 malware_re match condition for "malware="
2180 timeout if nonzero, timeout in seconds
2182 Returns: Exim message processing code (OK, FAIL, DEFER, ...)
2183 where true means malware was found (condition applies)
2186 malware(const uschar * malware_re, int timeout)
2188 int ret = malware_internal(malware_re, NULL, timeout);
2190 if (ret == DEFER) av_failed = TRUE;
2195 /*************************************************
2196 * Scan a file for malware *
2197 *************************************************/
2199 /* This is a test wrapper for scanning an email, which is not used in
2200 normal processing. Scan any file, using the Exim scanning interface.
2201 This function tampers with various global variables so is unsafe to use
2202 in any other context.
2205 eml_filename a file holding the message to be scanned
2207 Returns: Exim message processing code (OK, FAIL, DEFER, ...)
2208 where true means malware was found (condition applies)
2211 malware_in_file(uschar *eml_filename)
2213 uschar message_id_buf[64];
2216 /* spool_mbox() assumes various parameters exist, when creating
2217 the relevant directory and the email within */
2219 (void) string_format(message_id_buf, sizeof(message_id_buf),
2220 "dummy-%d", vaguely_random_number(INT_MAX));
2221 message_id = message_id_buf;
2222 sender_address = US"malware-sender@example.net";
2224 recipients_list = NULL;
2225 receive_add_recipient(US"malware-victim@example.net", -1);
2226 f.enable_dollar_recipients = TRUE;
2228 ret = malware_internal(US"*", eml_filename, 0);
2230 Ustrncpy(spooled_message_id, message_id, sizeof(spooled_message_id));
2233 /* don't set no_mbox_unspool; at present, there's no way for it to become
2234 set, but if that changes, then it should apply to these tests too */
2238 /* silence static analysis tools */
2248 if (!malware_default_re)
2249 malware_default_re = regex_must_compile(malware_regex_default, FALSE, TRUE);
2251 #ifndef DISABLE_MAL_DRWEB
2253 drweb_re = regex_must_compile(drweb_re_str, FALSE, TRUE);
2255 #ifndef DISABLE_MAL_FSECURE
2257 fsec_re = regex_must_compile(fsec_re_str, FALSE, TRUE);
2259 #ifndef DISABLE_MAL_KAV
2261 kav_re_sus = regex_must_compile(kav_re_sus_str, FALSE, TRUE);
2263 kav_re_inf = regex_must_compile(kav_re_inf_str, FALSE, TRUE);
2265 #ifndef DISABLE_MAL_AVAST
2267 ava_re_clean = regex_must_compile(ava_re_clean_str, FALSE, TRUE);
2269 ava_re_virus = regex_must_compile(ava_re_virus_str, FALSE, TRUE);
2271 ava_re_error = regex_must_compile(ava_re_error_str, FALSE, TRUE);
2273 #ifndef DISABLE_MAL_FFROT6D
2274 if (!fprot6d_re_error)
2275 fprot6d_re_error = regex_must_compile(fprot6d_re_error_str, FALSE, TRUE);
2276 if (!fprot6d_re_virus)
2277 fprot6d_re_virus = regex_must_compile(fprot6d_re_virus_str, FALSE, TRUE);
2283 malware_show_supported(gstring * g)
2285 g = string_cat(g, US"Malware:");
2286 for (struct scan * sc = m_scans; sc->scancode != (scanner_t)-1; sc++)
2287 g = string_fmt_append(g, " %s", sc->name);
2288 return string_cat(g, US"\n");
2292 # endif /*!MACRO_PREDEF*/
2293 #endif /*WITH_CONTENT_SCAN*/