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;
135 #ifndef DISABLE_MAL_CLAM
136 /* The maximum number of clamd servers that are supported in the configuration */
137 # define MAX_CLAMD_SERVERS 32
138 # define MAX_CLAMD_SERVERS_S "32"
140 typedef struct clamd_address {
148 #ifndef DISABLE_MAL_DRWEB
149 # define DRWEBD_SCAN_CMD (1) /* scan file, buffer or diskfile */
150 # define DRWEBD_RETURN_VIRUSES (1<<0) /* ask daemon return to us viruses names from report */
151 # define DRWEBD_IS_MAIL (1<<19) /* say to daemon that format is "archive MAIL" */
153 # define DERR_READ_ERR (1<<0) /* read error */
154 # define DERR_NOMEMORY (1<<2) /* no memory */
155 # define DERR_TIMEOUT (1<<9) /* scan timeout has run out */
156 # define DERR_BAD_CALL (1<<15) /* wrong command */
158 static const uschar * drweb_re_str = US "infected\\swith\\s*(.+?)$";
159 static const pcre * drweb_re = NULL;
162 #ifndef DISABLE_MAL_FSECURE
163 static const uschar * fsec_re_str = US "\\S{0,5}INFECTED\\t[^\\t]*\\t([^\\t]+)\\t\\S*$";
164 static const pcre * fsec_re = NULL;
167 #ifndef DISABLE_MAL_KAV
168 static const uschar * kav_re_sus_str = US "suspicion:\\s*(.+?)\\s*$";
169 static const uschar * kav_re_inf_str = US "infected:\\s*(.+?)\\s*$";
170 static const pcre * kav_re_sus = NULL;
171 static const pcre * kav_re_inf = NULL;
174 #ifndef DISABLE_MAL_AVAST
175 static const uschar * ava_re_clean_str = US "(?!\\\\)\\t\\[\\+\\]";
176 static const uschar * ava_re_virus_str = US "(?!\\\\)\\t\\[L\\]\\d+\\.0\\t0\\s(.*)";
177 static const uschar * ava_re_error_str = US "(?!\\\\)\\t\\[E\\]\\d+\\.0\\tError\\s\\d+\\s(.*)";
178 static const pcre * ava_re_clean = NULL;
179 static const pcre * ava_re_virus = NULL;
180 static const pcre * ava_re_error = NULL;
183 #ifndef DISABLE_MAL_FFROT6D
184 static const uschar * fprot6d_re_error_str = US "^\\d+\\s<(.+?)>$";
185 static const uschar * fprot6d_re_virus_str = US "^\\d+\\s<infected:\\s+(.+?)>\\s+.+$";
186 static const pcre * fprot6d_re_error = NULL;
187 static const pcre * fprot6d_re_virus = NULL;
192 /******************************************************************************/
194 #ifndef DISABLE_MAL_KAV
195 /* Routine to check whether a system is big- or little-endian.
196 Ripped from http://www.faqs.org/faqs/graphics/fileformats-faq/part4/section-7.html
197 Needed for proper kavdaemon implementation. Sigh. */
198 # define BIG_MY_ENDIAN 0
199 # define LITTLE_MY_ENDIAN 1
200 static int test_byte_order(void);
204 short int word = 0x0001;
205 char *byte = CS &word;
206 return(byte[0] ? LITTLE_MY_ENDIAN : BIG_MY_ENDIAN);
210 BOOL malware_ok = FALSE;
212 /* Gross hacks for the -bmalware option; perhaps we should just create
213 the scan directory normally for that case, but look into rigging up the
214 needed header variables if not already set on the command-line? */
215 extern int spool_mbox_ok;
216 extern uschar spooled_message_id[MESSAGE_ID_LENGTH+1];
219 /* Some (currently avast only) use backslash escaped whitespace,
220 this function undoes these escapes */
222 #ifndef DISABLE_MAL_AVAST
228 if (*p == '\\' && (isspace(p[1]) || p[1] == '\\'))
229 for (p0 = p; *p0; ++p0) *p0 = p0[1];
233 /* --- malware_*_defer --- */
235 malware_panic_defer(const uschar * str)
237 log_write(0, LOG_MAIN|LOG_PANIC, "malware acl condition: %s", str);
241 malware_log_defer(const uschar * str)
243 log_write(0, LOG_MAIN, "malware acl condition: %s", str);
246 /* --- m_*_defer --- */
248 m_panic_defer(struct scan * scanent, const uschar * hostport,
251 return malware_panic_defer(string_sprintf("%s %s : %s",
252 scanent->name, hostport ? hostport : CUS"", str));
254 /* --- m_*_defer_3 */
256 m_panic_defer_3(struct scan * scanent, const uschar * hostport,
257 const uschar * str, int fd_to_close)
259 DEBUG(D_acl) debug_print_socket(fd_to_close);
260 (void) close(fd_to_close);
261 return m_panic_defer(scanent, hostport, str);
264 /*************************************************/
266 #ifndef DISABLE_MAL_CLAM
267 /* Only used by the Clamav code, which is working from a list of servers and
268 uses the returned in_addr to get a second connection to the same system.
271 m_tcpsocket(const uschar * hostname, unsigned int port,
272 host_item * host, uschar ** errstr, const blob * fastopen_blob)
274 int fd = ip_connectedsocket(SOCK_STREAM, hostname, port, port, 5,
275 host, errstr, fastopen_blob);
276 #ifdef EXIM_TFO_FREEBSD
277 /* Under some fault conditions, FreeBSD 12.2 seen to send a (non-TFO) SYN
278 and, getting no response, wait for a long time. Impose a 5s max. */
281 struct timeval tv = {.tv_sec = 5};
283 FD_ZERO(fds); FD_SET(fd, &fds); (void) select(fd+1, NULL, &fds, NULL, &tv);
291 m_sock_send(int sock, uschar * buf, int cnt, uschar ** errstr)
293 if (send(sock, buf, cnt, 0) < 0)
297 *errstr = string_sprintf("unable to send to socket (%s): %s",
305 m_pcre_compile(const uschar * re, uschar ** errstr)
307 const uschar * rerror;
311 if (!(cre = pcre_compile(CS re, PCRE_COPT, CCSS &rerror, &roffset, NULL)))
312 *errstr= string_sprintf("regular expression error in '%s': %s at offset %d",
313 re, rerror, roffset);
318 m_pcre_exec(const pcre * cre, uschar * text)
321 int i = pcre_exec(cre, NULL, CS text, Ustrlen(text), 0, 0,
322 ovector, nelem(ovector));
323 uschar * substr = NULL;
324 if (i >= 2) /* Got it */
325 pcre_get_substring(CS text, ovector, i, 1, CCSS &substr);
330 m_pcre_nextinlist(const uschar ** list, int * sep,
331 char * listerr, uschar ** errstr)
333 const uschar * list_ele;
334 const pcre * cre = NULL;
336 if (!(list_ele = string_nextinlist(list, sep, NULL, 0)))
337 *errstr = US listerr;
340 DEBUG(D_acl) debug_printf_indent("%15s%10s'%s'\n", "", "RE: ",
341 string_printing(list_ele));
342 cre = m_pcre_compile(CUS list_ele, errstr);
349 Simple though inefficient wrapper for reading a line. Drop CRs and the
350 trailing newline. Can return early on buffer full. Null-terminate.
351 Apply initial timeout if no data ready.
353 Return: number of chars - zero for an empty line
355 -2 on timeout or error
358 recv_line(int fd, uschar * buffer, int bsize, time_t tmo)
364 if (!fd_ready(fd, tmo))
367 /*XXX tmo handling assumes we always get a whole line */
370 while ((rcv = read(fd, p, 1)) > 0)
373 if (p-buffer > bsize-2) break;
374 if (*p == '\n') break;
381 debug_printf_indent("Malware scan: read %s (%s)\n",
382 rcv==0 ? "EOF" : "error", strerror(errno));
383 debug_print_socket(fd);
385 return rcv==0 ? -1 : -2;
389 DEBUG(D_acl) debug_printf_indent("Malware scan: read '%s'\n", buffer);
393 /* return TRUE iff size as requested */
394 #ifndef DISABLE_MAL_DRWEB
396 recv_len(int sock, void * buf, int size, time_t tmo)
398 return fd_ready(sock, tmo)
399 ? recv(sock, buf, size, 0) == size
406 #ifndef DISABLE_MAL_MKS
407 /* ============= private routines for the "mksd" scanner type ============== */
409 # include <sys/uio.h>
412 mksd_writev (int sock, struct iovec * iov, int iovcnt)
419 i = writev (sock, iov, iovcnt);
420 while (i < 0 && errno == EINTR);
423 (void) malware_panic_defer(
424 US"unable to write to mksd UNIX socket (/var/run/mksd/socket)");
427 for (;;) /* check for short write */
428 if (i >= iov->iov_len)
438 iov->iov_base = CS iov->iov_base + i;
445 mksd_read_lines (int sock, uschar *av_buffer, int av_buffer_size, time_t tmo)
447 client_conn_ctx cctx = {.sock = sock};
453 i = ip_recv(&cctx, av_buffer+offset, av_buffer_size-offset, tmo);
456 (void) malware_panic_defer(US"unable to read from mksd UNIX socket (/var/run/mksd/socket)");
461 /* offset == av_buffer_size -> buffer full */
462 if (offset == av_buffer_size)
464 (void) malware_panic_defer(US"malformed reply received from mksd");
467 } while (av_buffer[offset-1] != '\n');
469 av_buffer[offset] = '\0';
474 mksd_parse_line(struct scan * scanent, char * line)
485 if ((p = strchr (line, '\n')) != NULL)
487 return m_panic_defer(scanent, NULL,
488 string_sprintf("scanner failed: %s", line));
491 if ((p = strchr (line, '\n')) != NULL)
496 && (p = strchr(line+4, ' ')) != NULL
501 malware_name = string_copy(US line+4);
505 return m_panic_defer(scanent, NULL,
506 string_sprintf("malformed reply received: %s", line));
511 mksd_scan_packed(struct scan * scanent, int sock, const uschar * scan_filename,
515 const char *cmd = "MSQ\n";
516 uschar av_buffer[1024];
518 iov[0].iov_base = (void *) cmd;
520 iov[1].iov_base = (void *) scan_filename;
521 iov[1].iov_len = Ustrlen(scan_filename);
522 iov[2].iov_base = (void *) (cmd + 3);
525 if (mksd_writev (sock, iov, 3) < 0)
528 if (mksd_read_lines (sock, av_buffer, sizeof (av_buffer), tmo) < 0)
531 return mksd_parse_line (scanent, CS av_buffer);
536 #ifndef DISABLE_MAL_CLAM
538 clamd_option(clamd_address * cd, const uschar * optstr, int * subsep)
543 while ((s = string_nextinlist(&optstr, subsep, NULL, 0)))
544 if (Ustrncmp(s, "retry=", 6) == 0)
546 int sec = readconf_readtime((s += 6), '\0', FALSE);
559 /*************************************************
560 * Scan content for malware *
561 *************************************************/
563 /* This is an internal interface for scanning an email; the normal interface
564 is via malware(), or there's malware_in_file() used for testing/debugging.
567 malware_re match condition for "malware="
568 scan_filename the file holding the email to be scanned, if we're faking
569 this up for the -bmalware test, else NULL
570 timeout if nonzero, non-default timeoutl
572 Returns: Exim message processing code (OK, FAIL, DEFER, ...)
573 where true means malware was found (condition applies)
576 malware_internal(const uschar * malware_re, const uschar * scan_filename,
580 const uschar *av_scanner_work = av_scanner;
581 uschar *scanner_name;
582 unsigned long mbox_size;
586 struct scan * scanent;
587 const uschar * scanner_options;
588 client_conn_ctx malware_daemon_ctx = {.sock = -1};
590 uschar * eml_filename, * eml_dir;
593 return FAIL; /* empty means "don't match anything" */
595 /* Ensure the eml mbox file is spooled up */
597 if (!(mbox_file = spool_mbox(&mbox_size, scan_filename, &eml_filename)))
598 return malware_panic_defer(US"error while creating mbox spool file");
600 /* None of our current scanners need the mbox file as a stream (they use
601 the name), so we can close it right away. Get the directory too. */
603 (void) fclose(mbox_file);
604 eml_dir = string_copyn(eml_filename, Ustrrchr(eml_filename, '/') - eml_filename);
606 /* parse 1st option */
607 if (strcmpic(malware_re, US"false") == 0 || Ustrcmp(malware_re,"0") == 0)
608 return FAIL; /* explicitly no matching */
610 /* special cases (match anything except empty) */
611 if ( strcmpic(malware_re,US"true") == 0
612 || Ustrcmp(malware_re,"*") == 0
613 || Ustrcmp(malware_re,"1") == 0
616 if ( !malware_default_re
617 && !(malware_default_re = m_pcre_compile(malware_regex_default, &errstr)))
618 return malware_panic_defer(errstr);
619 malware_re = malware_regex_default;
620 re = malware_default_re;
623 /* compile the regex, see if it works */
624 else if (!(re = m_pcre_compile(malware_re, &errstr)))
625 return malware_panic_defer(errstr);
627 /* if av_scanner starts with a dollar, expand it first */
628 if (*av_scanner == '$')
630 if (!(av_scanner_work = expand_string(av_scanner)))
631 return malware_panic_defer(
632 string_sprintf("av_scanner starts with $, but expansion failed: %s",
633 expand_string_message));
636 debug_printf_indent("Expanded av_scanner global: %s\n", av_scanner_work);
637 /* disable result caching in this case */
642 /* Do not scan twice (unless av_scanner is dynamic). */
645 /* find the scanner type from the av_scanner option */
646 if (!(scanner_name = string_nextinlist(&av_scanner_work, &sep, NULL, 0)))
647 return malware_panic_defer(US"av_scanner configuration variable is empty");
648 if (!timeout) timeout = MALWARE_TIMEOUT;
649 tmo = time(NULL) + timeout;
651 for (scanent = m_scans; ; scanent++)
654 return malware_panic_defer(string_sprintf("unknown scanner type '%s'",
656 if (strcmpic(scanner_name, US scanent->name) != 0)
658 DEBUG(D_acl) debug_printf_indent("Malware scan: %s tmo=%s\n",
659 scanner_name, readconf_printtime(timeout));
661 if (!(scanner_options = string_nextinlist(&av_scanner_work, &sep, NULL, 0)))
662 scanner_options = scanent->options_default;
663 if (scanent->conn == MC_NONE)
666 DEBUG(D_acl) debug_printf_indent("%15s%10s%s\n", "", "socket: ", scanner_options);
667 switch(scanent->conn)
670 malware_daemon_ctx.sock = ip_tcpsocket(scanner_options, &errstr, 5, NULL); break;
672 malware_daemon_ctx.sock = ip_unixsocket(scanner_options, &errstr); break;
674 malware_daemon_ctx.sock = ip_streamsocket(scanner_options, &errstr, 5, NULL); break;
676 /* compiler quietening */ break;
678 if (malware_daemon_ctx.sock < 0)
679 return m_panic_defer(scanent, CUS callout_address, errstr);
683 switch (scanent->scancode)
685 #ifndef DISABLE_MAL_FFROTD
686 case M_FPROTD: /* "f-protd" scanner type -------------------------------- */
688 uschar *fp_scan_option;
689 unsigned int detected=0, par_count=0;
690 uschar * scanrequest;
691 uschar buf[32768], *strhelper, *strhelper2;
692 uschar * malware_name_internal = NULL;
695 scanrequest = string_sprintf("GET %s", eml_filename);
697 while ((fp_scan_option = string_nextinlist(&av_scanner_work, &sep,
700 scanrequest = string_sprintf("%s%s%s", scanrequest,
701 par_count ? "%20" : "?", fp_scan_option);
704 scanrequest = string_sprintf("%s HTTP/1.0\r\n\r\n", scanrequest);
705 DEBUG(D_acl) debug_printf_indent("Malware scan: issuing %s: %s\n",
706 scanner_name, scanrequest);
708 /* send scan request */
709 if (m_sock_send(malware_daemon_ctx.sock, scanrequest, Ustrlen(scanrequest)+1, &errstr) < 0)
710 return m_panic_defer(scanent, CUS callout_address, errstr);
712 while ((len = recv_line(malware_daemon_ctx.sock, buf, sizeof(buf), tmo)) >= 0)
715 if (Ustrstr(buf, US"<detected type=\"") != NULL)
717 else if (detected && (strhelper = Ustrstr(buf, US"<name>")))
719 if ((strhelper2 = Ustrstr(buf, US"</name>")) != NULL)
722 malware_name_internal = string_copy(strhelper+6);
725 else if (Ustrstr(buf, US"<summary code=\""))
727 malware_name = Ustrstr(buf, US"<summary code=\"11\">")
728 ? malware_name_internal : NULL;
734 (void)close(malware_daemon_ctx.sock);
741 #ifndef DISABLE_MAL_FFROT6D
742 case M_FPROT6D: /* "f-prot6d" scanner type ----------------------------------- */
747 uschar * scanrequest;
748 uschar av_buffer[1024];
750 if ((!fprot6d_re_virus && !(fprot6d_re_virus = m_pcre_compile(fprot6d_re_virus_str, &errstr)))
751 || (!fprot6d_re_error && !(fprot6d_re_error = m_pcre_compile(fprot6d_re_error_str, &errstr))))
752 return malware_panic_defer(errstr);
754 scanrequest = string_sprintf("SCAN FILE %s\n", eml_filename);
755 DEBUG(D_acl) debug_printf_indent("Malware scan: issuing %s: %s\n",
756 scanner_name, scanrequest);
758 if (m_sock_send(malware_daemon_ctx.sock, scanrequest, Ustrlen(scanrequest), &errstr) < 0)
759 return m_panic_defer(scanent, CUS callout_address, errstr);
761 bread = ip_recv(&malware_daemon_ctx, av_buffer, sizeof(av_buffer), tmo);
764 return m_panic_defer_3(scanent, CUS callout_address,
765 string_sprintf("unable to read from socket (%s)", strerror(errno)),
766 malware_daemon_ctx.sock);
768 if (bread == sizeof(av_buffer))
769 return m_panic_defer_3(scanent, CUS callout_address,
770 US"buffer too small", malware_daemon_ctx.sock);
772 av_buffer[bread] = '\0';
773 linebuffer = string_copy(av_buffer);
775 m_sock_send(malware_daemon_ctx.sock, US"QUIT\n", 5, 0);
777 if ((e = m_pcre_exec(fprot6d_re_error, linebuffer)))
778 return m_panic_defer_3(scanent, CUS callout_address,
779 string_sprintf("scanner reported error (%s)", e), malware_daemon_ctx.sock);
781 if (!(malware_name = m_pcre_exec(fprot6d_re_virus, linebuffer)))
788 #ifndef DISABLE_MAL_DRWEB
789 case M_DRWEB: /* "drweb" scanner type ----------------------------------- */
790 /* v0.1 - added support for tcp sockets */
791 /* v0.0 - initial release -- support for unix sockets */
795 unsigned int fsize_uint;
796 uschar * tmpbuf, *drweb_fbuf;
797 int drweb_rc, drweb_cmd, drweb_flags = 0x0000, drweb_fd,
798 drweb_vnum, drweb_slen, drweb_fin = 0x0000;
800 /* prepare variables */
801 drweb_cmd = htonl(DRWEBD_SCAN_CMD);
802 drweb_flags = htonl(DRWEBD_RETURN_VIRUSES | DRWEBD_IS_MAIL);
804 if (*scanner_options != '/')
807 if ((drweb_fd = exim_open2(CCS eml_filename, O_RDONLY)) == -1)
808 return m_panic_defer_3(scanent, NULL,
809 string_sprintf("can't open spool file %s: %s",
810 eml_filename, strerror(errno)),
811 malware_daemon_ctx.sock);
813 if ((fsize = lseek(drweb_fd, 0, SEEK_END)) == -1)
816 badseek: err = errno;
817 (void)close(drweb_fd);
818 return m_panic_defer_3(scanent, NULL,
819 string_sprintf("can't seek spool file %s: %s",
820 eml_filename, strerror(err)),
821 malware_daemon_ctx.sock);
823 fsize_uint = (unsigned int) fsize;
824 if ((off_t)fsize_uint != fsize)
826 (void)close(drweb_fd);
827 return m_panic_defer_3(scanent, NULL,
828 string_sprintf("seeking spool file %s, size overflow",
830 malware_daemon_ctx.sock);
832 drweb_slen = htonl(fsize);
833 if (lseek(drweb_fd, 0, SEEK_SET) < 0)
836 DEBUG(D_acl) debug_printf_indent("Malware scan: issuing %s remote scan [%s]\n",
837 scanner_name, scanner_options);
839 /* send scan request */
840 if ((send(malware_daemon_ctx.sock, &drweb_cmd, sizeof(drweb_cmd), 0) < 0) ||
841 (send(malware_daemon_ctx.sock, &drweb_flags, sizeof(drweb_flags), 0) < 0) ||
842 (send(malware_daemon_ctx.sock, &drweb_fin, sizeof(drweb_fin), 0) < 0) ||
843 (send(malware_daemon_ctx.sock, &drweb_slen, sizeof(drweb_slen), 0) < 0))
845 (void)close(drweb_fd);
846 return m_panic_defer_3(scanent, CUS callout_address, string_sprintf(
847 "unable to send commands to socket (%s)", scanner_options),
848 malware_daemon_ctx.sock);
851 if (!(drweb_fbuf = store_malloc(fsize_uint)))
853 (void)close(drweb_fd);
854 return m_panic_defer_3(scanent, NULL,
855 string_sprintf("unable to allocate memory %u for file (%s)",
856 fsize_uint, eml_filename),
857 malware_daemon_ctx.sock);
860 if ((result = read (drweb_fd, drweb_fbuf, fsize)) == -1)
863 (void)close(drweb_fd);
864 store_free(drweb_fbuf);
865 return m_panic_defer_3(scanent, NULL,
866 string_sprintf("can't read spool file %s: %s",
867 eml_filename, strerror(err)),
868 malware_daemon_ctx.sock);
870 (void)close(drweb_fd);
872 /* send file body to socket */
873 if (send(malware_daemon_ctx.sock, drweb_fbuf, fsize, 0) < 0)
875 store_free(drweb_fbuf);
876 return m_panic_defer_3(scanent, CUS callout_address, string_sprintf(
877 "unable to send file body to socket (%s)", scanner_options),
878 malware_daemon_ctx.sock);
880 store_free(drweb_fbuf);
884 drweb_slen = htonl(Ustrlen(eml_filename));
886 DEBUG(D_acl) debug_printf_indent("Malware scan: issuing %s local scan [%s]\n",
887 scanner_name, scanner_options);
889 /* send scan request */
890 if ((send(malware_daemon_ctx.sock, &drweb_cmd, sizeof(drweb_cmd), 0) < 0) ||
891 (send(malware_daemon_ctx.sock, &drweb_flags, sizeof(drweb_flags), 0) < 0) ||
892 (send(malware_daemon_ctx.sock, &drweb_slen, sizeof(drweb_slen), 0) < 0) ||
893 (send(malware_daemon_ctx.sock, eml_filename, Ustrlen(eml_filename), 0) < 0) ||
894 (send(malware_daemon_ctx.sock, &drweb_fin, sizeof(drweb_fin), 0) < 0))
895 return m_panic_defer_3(scanent, CUS callout_address, string_sprintf(
896 "unable to send commands to socket (%s)", scanner_options),
897 malware_daemon_ctx.sock);
900 /* wait for result */
901 if (!recv_len(malware_daemon_ctx.sock, &drweb_rc, sizeof(drweb_rc), tmo))
902 return m_panic_defer_3(scanent, CUS callout_address,
903 US"unable to read return code", malware_daemon_ctx.sock);
904 drweb_rc = ntohl(drweb_rc);
906 if (!recv_len(malware_daemon_ctx.sock, &drweb_vnum, sizeof(drweb_vnum), tmo))
907 return m_panic_defer_3(scanent, CUS callout_address,
908 US"unable to read the number of viruses", malware_daemon_ctx.sock);
909 drweb_vnum = ntohl(drweb_vnum);
911 /* "virus(es) found" if virus number is > 0 */
916 /* setup default virus name */
917 malware_name = US"unknown";
919 /* set up match regex */
921 drweb_re = m_pcre_compile(drweb_re_str, &errstr);
923 /* read and concatenate virus names into one string */
924 for (int i = 0; i < drweb_vnum; i++)
928 /* read the size of report */
929 if (!recv_len(malware_daemon_ctx.sock, &drweb_slen, sizeof(drweb_slen), tmo))
930 return m_panic_defer_3(scanent, CUS callout_address,
931 US"cannot read report size", malware_daemon_ctx.sock);
932 drweb_slen = ntohl(drweb_slen);
934 /* assume tainted, since it is external input */
935 tmpbuf = store_get(drweb_slen, TRUE);
937 /* read report body */
938 if (!recv_len(malware_daemon_ctx.sock, tmpbuf, drweb_slen, tmo))
939 return m_panic_defer_3(scanent, CUS callout_address,
940 US"cannot read report string", malware_daemon_ctx.sock);
941 tmpbuf[drweb_slen] = '\0';
943 /* try matcher on the line, grab substring */
944 result = pcre_exec(drweb_re, NULL, CS tmpbuf, Ustrlen(tmpbuf), 0, 0,
945 ovector, nelem(ovector));
948 const char * pre_malware_nb;
950 pcre_get_substring(CS tmpbuf, ovector, result, 1, &pre_malware_nb);
952 if (i==0) /* the first name we just copy to malware_name */
953 g = string_cat(NULL, US pre_malware_nb);
955 /*XXX could be string_append_listele? */
956 else /* concatenate each new virus name to previous */
957 g = string_append(g, 2, "/", pre_malware_nb);
959 pcre_free_substring(pre_malware_nb);
962 malware_name = string_from_gstring(g);
966 const char *drweb_s = NULL;
968 if (drweb_rc & DERR_READ_ERR) drweb_s = "read error";
969 if (drweb_rc & DERR_NOMEMORY) drweb_s = "no memory";
970 if (drweb_rc & DERR_TIMEOUT) drweb_s = "timeout";
971 if (drweb_rc & DERR_BAD_CALL) drweb_s = "wrong command";
972 /* retcodes DERR_SYMLINK, DERR_NO_REGFILE, DERR_SKIPPED.
973 * DERR_TOO_BIG, DERR_TOO_COMPRESSED, DERR_SPAM,
974 * DERR_CRC_ERROR, DERR_READSOCKET, DERR_WRITE_ERR
975 * and others are ignored */
977 return m_panic_defer_3(scanent, CUS callout_address,
978 string_sprintf("drweb daemon retcode 0x%x (%s)", drweb_rc, drweb_s),
979 malware_daemon_ctx.sock);
988 #ifndef DISABLE_MAL_AVE
989 case M_AVES: /* "aveserver" scanner type -------------------------------- */
994 /* read aveserver's greeting and see if it is ready (2xx greeting) */
996 recv_line(malware_daemon_ctx.sock, buf, sizeof(buf), tmo);
998 if (buf[0] != '2') /* aveserver is having problems */
999 return m_panic_defer_3(scanent, CUS callout_address,
1000 string_sprintf("unavailable (Responded: %s).",
1001 ((buf[0] != 0) ? buf : US "nothing") ),
1002 malware_daemon_ctx.sock);
1004 /* prepare our command */
1005 (void)string_format(buf, sizeof(buf), "SCAN bPQRSTUW %s\r\n",
1009 DEBUG(D_acl) debug_printf_indent("Malware scan: issuing %s %s\n",
1011 if (m_sock_send(malware_daemon_ctx.sock, buf, Ustrlen(buf), &errstr) < 0)
1012 return m_panic_defer(scanent, CUS callout_address, errstr);
1014 malware_name = NULL;
1016 /* read response lines, find malware name and final response */
1017 while (recv_line(malware_daemon_ctx.sock, buf, sizeof(buf), tmo) > 0)
1021 if (buf[0] == '5') /* aveserver is having problems */
1023 result = m_panic_defer(scanent, CUS callout_address,
1024 string_sprintf("unable to scan file %s (Responded: %s).",
1025 eml_filename, buf));
1028 if (Ustrncmp(buf,"322",3) == 0)
1030 uschar *p = Ustrchr(&buf[4], ' ');
1032 malware_name = string_copy(&buf[4]);
1036 if (m_sock_send(malware_daemon_ctx.sock, US"quit\r\n", 6, &errstr) < 0)
1037 return m_panic_defer(scanent, CUS callout_address, errstr);
1039 /* read aveserver's greeting and see if it is ready (2xx greeting) */
1041 recv_line(malware_daemon_ctx.sock, buf, sizeof(buf), tmo);
1043 if (buf[0] != '2') /* aveserver is having problems */
1044 return m_panic_defer_3(scanent, CUS callout_address,
1045 string_sprintf("unable to quit dialogue (Responded: %s).",
1046 ((buf[0] != 0) ? buf : US "nothing") ),
1047 malware_daemon_ctx.sock);
1049 if (result == DEFER)
1051 (void)close(malware_daemon_ctx.sock);
1058 #ifndef DISABLE_MAL_FSECURE
1059 case M_FSEC: /* "fsecure" scanner type ---------------------------------- */
1063 uschar av_buffer[1024];
1064 static uschar *cmdopt[] = { US"CONFIGURE\tARCHIVE\t1\n",
1065 US"CONFIGURE\tTIMEOUT\t0\n",
1066 US"CONFIGURE\tMAXARCH\t5\n",
1067 US"CONFIGURE\tMIME\t1\n" };
1069 malware_name = NULL;
1071 DEBUG(D_acl) debug_printf_indent("Malware scan: issuing %s scan [%s]\n",
1072 scanner_name, scanner_options);
1074 memset(av_buffer, 0, sizeof(av_buffer));
1075 for (i = 0; i != nelem(cmdopt); i++)
1078 if (m_sock_send(malware_daemon_ctx.sock, cmdopt[i], Ustrlen(cmdopt[i]), &errstr) < 0)
1079 return m_panic_defer(scanent, CUS callout_address, errstr);
1081 bread = ip_recv(&malware_daemon_ctx, av_buffer, sizeof(av_buffer), tmo);
1082 if (bread > 0) av_buffer[bread]='\0';
1084 return m_panic_defer_3(scanent, CUS callout_address,
1085 string_sprintf("unable to read answer %d (%s)", i, strerror(errno)),
1086 malware_daemon_ctx.sock);
1087 for (int j = 0; j < bread; j++)
1088 if (av_buffer[j] == '\r' || av_buffer[j] == '\n')
1092 /* pass the mailfile to fsecure */
1093 file_name = string_sprintf("SCAN\t%s\n", eml_filename);
1095 if (m_sock_send(malware_daemon_ctx.sock, file_name, Ustrlen(file_name), &errstr) < 0)
1096 return m_panic_defer(scanent, CUS callout_address, errstr);
1099 /* todo also SUSPICION\t */
1101 fsec_re = m_pcre_compile(fsec_re_str, &errstr);
1103 /* read report, linewise. Apply a timeout as the Fsecure daemon
1104 sometimes wants an answer to "PING" but they won't tell us what */
1106 uschar * p = av_buffer;
1112 i = av_buffer+sizeof(av_buffer)-p;
1113 if ((bread= ip_recv(&malware_daemon_ctx, p, i-1, tmo)) < 0)
1114 return m_panic_defer_3(scanent, CUS callout_address,
1115 string_sprintf("unable to read result (%s)", strerror(errno)),
1116 malware_daemon_ctx.sock);
1118 for (p[bread] = '\0'; (q = Ustrchr(p, '\n')); p = q+1)
1122 /* Really search for virus again? */
1124 /* try matcher on the line, grab substring */
1125 malware_name = m_pcre_exec(fsec_re, p);
1127 if (Ustrstr(p, "OK\tScan ok."))
1131 /* copy down the trailing partial line then read another chunk */
1132 i = av_buffer+sizeof(av_buffer)-p;
1133 memmove(av_buffer, p, i);
1143 #ifndef DISABLE_MAL_KAV
1144 case M_KAVD: /* "kavdaemon" scanner type -------------------------------- */
1147 uschar tmpbuf[1024];
1148 uschar * scanrequest;
1150 unsigned long kav_reportlen;
1155 /* get current date and time, build scan request */
1157 /* pdp note: before the eml_filename parameter, this scanned the
1158 directory; not finding documentation, so we'll strip off the directory.
1159 The side-effect is that the test framework scanning may end up in
1160 scanning more than was requested, but for the normal interface, this is
1163 strftime(CS tmpbuf, sizeof(tmpbuf), "%d %b %H:%M:%S", localtime(&t));
1164 scanrequest = string_sprintf("<0>%s:%s", CS tmpbuf, eml_filename);
1165 p = Ustrrchr(scanrequest, '/');
1169 DEBUG(D_acl) debug_printf_indent("Malware scan: issuing %s scan [%s]\n",
1170 scanner_name, scanner_options);
1172 /* send scan request */
1173 if (m_sock_send(malware_daemon_ctx.sock, scanrequest, Ustrlen(scanrequest)+1, &errstr) < 0)
1174 return m_panic_defer(scanent, CUS callout_address, errstr);
1176 /* wait for result */
1177 if (!recv_len(malware_daemon_ctx.sock, tmpbuf, 2, tmo))
1178 return m_panic_defer_3(scanent, CUS callout_address,
1179 US"unable to read 2 bytes from socket.", malware_daemon_ctx.sock);
1181 /* get errorcode from one nibble */
1182 kav_rc = tmpbuf[ test_byte_order()==LITTLE_MY_ENDIAN ? 0 : 1 ] & 0x0F;
1185 case 5: case 6: /* improper kavdaemon configuration */
1186 return m_panic_defer_3(scanent, CUS callout_address,
1187 US"please reconfigure kavdaemon to NOT disinfect or remove infected files.",
1188 malware_daemon_ctx.sock);
1190 return m_panic_defer_3(scanent, CUS callout_address,
1191 US"reported 'scanning not completed' (code 1).", malware_daemon_ctx.sock);
1193 return m_panic_defer_3(scanent, CUS callout_address,
1194 US"reported 'kavdaemon damaged' (code 7).", malware_daemon_ctx.sock);
1197 /* code 8 is not handled, since it is ambiguous. It appears mostly on
1198 bounces where part of a file has been cut off */
1200 /* "virus found" return codes (2-4) */
1201 if (kav_rc > 1 && kav_rc < 5)
1203 int report_flag = 0;
1205 /* setup default virus name */
1206 malware_name = US"unknown";
1208 report_flag = tmpbuf[ test_byte_order() == LITTLE_MY_ENDIAN ? 1 : 0 ];
1210 /* read the report, if available */
1211 if (report_flag == 1)
1213 /* read report size */
1214 if (!recv_len(malware_daemon_ctx.sock, &kav_reportlen, 4, tmo))
1215 return m_panic_defer_3(scanent, CUS callout_address,
1216 US"cannot read report size", malware_daemon_ctx.sock);
1218 /* it's possible that avp returns av_buffer[1] == 1 but the
1219 reportsize is 0 (!?) */
1220 if (kav_reportlen > 0)
1222 /* set up match regex, depends on retcode */
1225 if (!kav_re_sus) kav_re_sus = m_pcre_compile(kav_re_sus_str, &errstr);
1226 kav_re = kav_re_sus;
1230 if (!kav_re_inf) kav_re_inf = m_pcre_compile(kav_re_inf_str, &errstr);
1231 kav_re = kav_re_inf;
1234 /* read report, linewise. Using size from stream to read amount of data
1235 from same stream is safe enough. */
1236 /* coverity[tainted_data] */
1237 while (kav_reportlen > 0)
1239 if ((bread = recv_line(malware_daemon_ctx.sock, tmpbuf, sizeof(tmpbuf), tmo)) < 0)
1241 kav_reportlen -= bread+1;
1243 /* try matcher on the line, grab substring */
1244 if ((malware_name = m_pcre_exec(kav_re, tmpbuf)))
1250 else /* no virus found */
1251 malware_name = NULL;
1257 #ifndef DISABLE_MAL_CMDLINE
1258 case M_CMDL: /* "cmdline" scanner type ---------------------------------- */
1260 const uschar *cmdline_scanner = scanner_options;
1261 const pcre *cmdline_trigger_re;
1262 const pcre *cmdline_regex_re;
1264 uschar * commandline;
1265 void (*eximsigchld)(int);
1266 void (*eximsigpipe)(int);
1267 FILE *scanner_out = NULL;
1269 FILE *scanner_record = NULL;
1270 uschar linebuffer[32767];
1275 if (!cmdline_scanner)
1276 return m_panic_defer(scanent, NULL, errstr);
1278 /* find scanner output trigger */
1279 cmdline_trigger_re = m_pcre_nextinlist(&av_scanner_work, &sep,
1280 "missing trigger specification", &errstr);
1281 if (!cmdline_trigger_re)
1282 return m_panic_defer(scanent, NULL, errstr);
1284 /* find scanner name regex */
1285 cmdline_regex_re = m_pcre_nextinlist(&av_scanner_work, &sep,
1286 "missing virus name regex specification", &errstr);
1287 if (!cmdline_regex_re)
1288 return m_panic_defer(scanent, NULL, errstr);
1290 /* prepare scanner call; despite the naming, file_name holds a directory
1291 name which is documented as the value given to %s. */
1293 file_name = string_copy(eml_filename);
1294 p = Ustrrchr(file_name, '/');
1297 commandline = string_sprintf(CS cmdline_scanner, file_name);
1299 /* redirect STDERR too */
1300 commandline = string_sprintf("%s 2>&1", commandline);
1302 DEBUG(D_acl) debug_printf_indent("Malware scan: issuing %s scan [%s]\n",
1303 scanner_name, commandline);
1305 /* store exims signal handlers */
1306 eximsigchld = signal(SIGCHLD,SIG_DFL);
1307 eximsigpipe = signal(SIGPIPE,SIG_DFL);
1309 if (!(scanner_out = popen(CS commandline,"r")))
1312 signal(SIGCHLD,eximsigchld); signal(SIGPIPE,eximsigpipe);
1313 return m_panic_defer(scanent, NULL,
1314 string_sprintf("call (%s) failed: %s.", commandline, strerror(err)));
1316 scanner_fd = fileno(scanner_out);
1318 file_name = string_sprintf("%s/%s_scanner_output", eml_dir, message_id);
1320 if (!(scanner_record = modefopen(file_name, "wb", SPOOL_MODE)))
1323 (void) pclose(scanner_out);
1324 signal(SIGCHLD,eximsigchld); signal(SIGPIPE,eximsigpipe);
1325 return m_panic_defer(scanent, NULL, string_sprintf(
1326 "opening scanner output file (%s) failed: %s.",
1327 file_name, strerror(err)));
1330 /* look for trigger while recording output */
1331 while ((rcnt = recv_line(scanner_fd, linebuffer,
1332 sizeof(linebuffer), tmo)))
1339 (void) pclose(scanner_out);
1340 signal(SIGCHLD,eximsigchld); signal(SIGPIPE,eximsigpipe);
1341 return m_panic_defer(scanent, NULL, string_sprintf(
1342 "unable to read from scanner (%s): %s",
1343 commandline, strerror(err)));
1346 if (Ustrlen(linebuffer) > fwrite(linebuffer, 1, Ustrlen(linebuffer), scanner_record))
1349 (void) pclose(scanner_out);
1350 signal(SIGCHLD,eximsigchld); signal(SIGPIPE,eximsigpipe);
1351 return m_panic_defer(scanent, NULL, string_sprintf(
1352 "short write on scanner output file (%s).", file_name));
1354 putc('\n', scanner_record);
1355 /* try trigger match */
1357 && regex_match_and_setup(cmdline_trigger_re, linebuffer, 0, -1)
1362 (void)fclose(scanner_record);
1363 sep = pclose(scanner_out);
1364 signal(SIGCHLD,eximsigchld); signal(SIGPIPE,eximsigpipe);
1366 return m_panic_defer(scanent, NULL,
1368 ? string_sprintf("running scanner failed: %s", strerror(sep))
1369 : string_sprintf("scanner returned error code: %d", sep));
1374 /* setup default virus name */
1375 malware_name = US"unknown";
1377 /* re-open the scanner output file, look for name match */
1378 scanner_record = Ufopen(file_name, "rb");
1379 while (Ufgets(linebuffer, sizeof(linebuffer), scanner_record))
1380 if ((s = m_pcre_exec(cmdline_regex_re, linebuffer))) /* try match */
1382 (void)fclose(scanner_record);
1384 else /* no virus found */
1385 malware_name = NULL;
1390 #ifndef DISABLE_MAL_SOPHIE
1391 case M_SOPHIE: /* "sophie" scanner type --------------------------------- */
1396 uschar av_buffer[1024];
1398 /* pass the scan directory to sophie */
1399 file_name = string_copy(eml_filename);
1400 if ((p = Ustrrchr(file_name, '/')))
1403 DEBUG(D_acl) debug_printf_indent("Malware scan: issuing %s scan [%s]\n",
1404 scanner_name, scanner_options);
1406 if ( write(malware_daemon_ctx.sock, file_name, Ustrlen(file_name)) < 0
1407 || write(malware_daemon_ctx.sock, "\n", 1) != 1
1409 return m_panic_defer_3(scanent, CUS callout_address,
1410 string_sprintf("unable to write to UNIX socket (%s)", scanner_options),
1411 malware_daemon_ctx.sock);
1413 /* wait for result */
1414 memset(av_buffer, 0, sizeof(av_buffer));
1415 if ((bread = ip_recv(&malware_daemon_ctx, av_buffer, sizeof(av_buffer), tmo)) <= 0)
1416 return m_panic_defer_3(scanent, CUS callout_address,
1417 string_sprintf("unable to read from UNIX socket (%s)", scanner_options),
1418 malware_daemon_ctx.sock);
1421 if (av_buffer[0] == '1') {
1422 uschar * s = Ustrchr(av_buffer, '\n');
1425 malware_name = string_copy(&av_buffer[2]);
1427 else if (!strncmp(CS av_buffer, "-1", 2))
1428 return m_panic_defer_3(scanent, CUS callout_address,
1429 US"scanner reported error", malware_daemon_ctx.sock);
1430 else /* all ok, no virus */
1431 malware_name = NULL;
1437 #ifndef DISABLE_MAL_CLAM
1438 case M_CLAMD: /* "clamd" scanner type ----------------------------------- */
1440 /* This code was originally contributed by David Saez */
1441 /* There are three scanning methods available to us:
1442 * (1) Use the SCAN command, pointing to a file in the filesystem
1443 * (2) Use the STREAM command, send the data on a separate port
1444 * (3) Use the zINSTREAM command, send the data inline
1445 * The zINSTREAM command was introduced with ClamAV 0.95, which marked
1446 * STREAM deprecated; see: http://wiki.clamav.net/bin/view/Main/UpgradeNotes095
1447 * In Exim, we use SCAN if using a Unix-domain socket or explicitly told that
1448 * the TCP-connected daemon is actually local; otherwise we use zINSTREAM
1449 * See Exim bug 926 for details. */
1451 uschar *p, *vname, *result_tag;
1453 uschar av_buffer[1024];
1454 uschar *hostname = US"";
1456 int clam_fd, result;
1457 unsigned int fsize_uint;
1458 BOOL use_scan_command = FALSE;
1459 clamd_address * cv[MAX_CLAMD_SERVERS];
1460 int num_servers = 0;
1461 uint32_t send_size, send_final_zeroblock;
1464 /*XXX if unixdomain socket, only one server supported. Needs fixing;
1465 there's no reason we should not mix local and remote servers */
1467 if (*scanner_options == '/')
1470 const uschar * sublist;
1473 /* Local file; so we def want to use_scan_command and don't want to try
1474 * passing IP/port combinations */
1475 use_scan_command = TRUE;
1476 cd = (clamd_address *) store_get(sizeof(clamd_address), FALSE);
1478 /* extract socket-path part */
1479 sublist = scanner_options;
1480 cd->hostspec = string_nextinlist(&sublist, &subsep, NULL, 0);
1483 if (clamd_option(cd, sublist, &subsep) != OK)
1484 return m_panic_defer(scanent, NULL,
1485 string_sprintf("bad option '%s'", scanner_options));
1490 /* Go through the rest of the list of host/port and construct an array
1491 * of servers to try. The first one is the bit we just passed from
1492 * scanner_options so process that first and then scan the remainder of
1493 * the address buffer */
1497 const uschar * sublist;
1501 /* The 'local' option means use the SCAN command over the network
1502 * socket (ie common file storage in use) */
1503 /*XXX we could accept this also as a local option? */
1504 if (strcmpic(scanner_options, US"local") == 0)
1506 use_scan_command = TRUE;
1510 cd = (clamd_address *) store_get(sizeof(clamd_address), FALSE);
1512 /* extract host and port part */
1513 sublist = scanner_options;
1514 if (!(cd->hostspec = string_nextinlist(&sublist, &subsep, NULL, 0)))
1516 (void) m_panic_defer(scanent, NULL,
1517 string_sprintf("missing address: '%s'", scanner_options));
1520 if (!(s = string_nextinlist(&sublist, &subsep, NULL, 0)))
1522 (void) m_panic_defer(scanent, NULL,
1523 string_sprintf("missing port: '%s'", scanner_options));
1526 cd->tcp_port = atoi(CS s);
1529 /*XXX should these options be common over scanner types? */
1530 if (clamd_option(cd, sublist, &subsep) != OK)
1531 return m_panic_defer(scanent, NULL,
1532 string_sprintf("bad option '%s'", scanner_options));
1534 cv[num_servers++] = cd;
1535 if (num_servers >= MAX_CLAMD_SERVERS)
1537 (void) m_panic_defer(scanent, NULL,
1538 US"More than " MAX_CLAMD_SERVERS_S " clamd servers "
1539 "specified; only using the first " MAX_CLAMD_SERVERS_S );
1542 } while ((scanner_options = string_nextinlist(&av_scanner_work, &sep,
1545 /* check if we have at least one server */
1547 return m_panic_defer(scanent, NULL,
1548 US"no useable server addresses in malware configuration option.");
1551 /* See the discussion of response formats below to see why we really
1552 don't like colons in filenames when passing filenames to ClamAV. */
1553 if (use_scan_command && Ustrchr(eml_filename, ':'))
1554 return m_panic_defer(scanent, NULL,
1555 string_sprintf("local/SCAN mode incompatible with" \
1556 " : in path to email filename [%s]", eml_filename));
1558 /* Set up the very first data we will be sending */
1559 if (!use_scan_command)
1560 { cmd_str.data = US"zINSTREAM"; cmd_str.len = 10; }
1563 cmd_str.data = string_sprintf("SCAN %s\n", eml_filename);
1564 cmd_str.len = Ustrlen(cmd_str.data);
1567 /* We have some network servers specified */
1570 /* Confirmed in ClamAV source (0.95.3) that the TCPAddr option of clamd
1571 only supports AF_INET, but we should probably be looking to the
1572 future and rewriting this to be protocol-independent anyway. */
1574 while (num_servers > 0)
1576 int i = random_number(num_servers);
1577 clamd_address * cd = cv[i];
1579 DEBUG(D_acl) debug_printf_indent("trying server name %s, port %u\n",
1580 cd->hostspec, cd->tcp_port);
1582 /* Lookup the host. This is to ensure that we connect to the same IP
1583 on both connections (as one host could resolve to multiple ips) */
1586 /*XXX we trust that the cmd_str is ideempotent */
1587 if ((malware_daemon_ctx.sock = m_tcpsocket(cd->hostspec, cd->tcp_port,
1588 &connhost, &errstr, &cmd_str)) >= 0)
1590 /* Connection successfully established with a server */
1591 hostname = cd->hostspec;
1595 if (cd->retry <= 0) break;
1596 while (cd->retry > 0) cd->retry = sleep(cd->retry);
1598 if (malware_daemon_ctx.sock >= 0)
1601 (void) m_panic_defer(scanent, CUS callout_address, errstr);
1603 /* Remove the server from the list. XXX We should free the memory */
1605 for (; i < num_servers; i++)
1609 if (num_servers == 0)
1610 return m_panic_defer(scanent, NULL, US"all servers failed");
1615 if ((malware_daemon_ctx.sock = ip_unixsocket(cv[0]->hostspec, &errstr)) >= 0)
1617 hostname = cv[0]->hostspec;
1620 if (cv[0]->retry <= 0)
1621 return m_panic_defer(scanent, CUS callout_address, errstr);
1622 while (cv[0]->retry > 0) cv[0]->retry = sleep(cv[0]->retry);
1625 /* have socket in variable "sock"; command to use is semi-independent of
1626 the socket protocol. We use SCAN if is local (either Unix/local
1627 domain socket, or explicitly told local) else we stream the data.
1628 How we stream the data depends upon how we were built. */
1630 if (!use_scan_command)
1633 #ifdef EXIM_TCP_CORK
1636 /* New protocol: "zINSTREAM\n" followed by a sequence of <length><data>
1637 chunks, <n> a 4-byte number (network order), terminated by a zero-length
1640 DEBUG(D_acl) debug_printf_indent(
1641 "Malware scan: issuing %s new-style remote scan (zINSTREAM)\n",
1644 /* Pass the string to ClamAV (10 = "zINSTREAM\0"), if not already sent */
1646 if (send(malware_daemon_ctx.sock, cmd_str.data, cmd_str.len, 0) < 0)
1647 return m_panic_defer_3(scanent, CUS hostname,
1648 string_sprintf("unable to send zINSTREAM to socket (%s)",
1650 malware_daemon_ctx.sock);
1652 if ((clam_fd = exim_open2(CS eml_filename, O_RDONLY)) < 0)
1655 return m_panic_defer_3(scanent, NULL,
1656 string_sprintf("can't open spool file %s: %s",
1657 eml_filename, strerror(err)),
1658 malware_daemon_ctx.sock);
1660 if (fstat(clam_fd, &st) < 0)
1663 (void)close(clam_fd);
1664 return m_panic_defer_3(scanent, NULL,
1665 string_sprintf("can't stat spool file %s: %s",
1666 eml_filename, strerror(err)),
1667 malware_daemon_ctx.sock);
1669 fsize_uint = (unsigned int) st.st_size;
1670 if ((off_t)fsize_uint != st.st_size)
1672 (void)close(clam_fd);
1673 return m_panic_defer_3(scanent, NULL,
1674 string_sprintf("stat spool file %s, size overflow", eml_filename),
1675 malware_daemon_ctx.sock);
1678 /* send file size */
1679 #ifdef EXIM_TCP_CORK
1680 (void) setsockopt(malware_daemon_ctx.sock, IPPROTO_TCP, EXIM_TCP_CORK,
1681 US &on, sizeof(on));
1683 send_size = htonl(fsize_uint);
1684 if (send(malware_daemon_ctx.sock, &send_size, sizeof(send_size), 0) < 0)
1685 return m_panic_defer_3(scanent, NULL,
1686 string_sprintf("unable to send file size to socket (%s)", hostname),
1687 malware_daemon_ctx.sock);
1689 /* send file body */
1693 unsigned n = MIN(fsize_uint, big_buffer_size);
1694 if ((n = read(clam_fd, big_buffer, n)) < 0)
1695 return m_panic_defer_3(scanent, NULL,
1696 string_sprintf("can't read spool file %s: %s",
1697 eml_filename, strerror(errno)),
1698 malware_daemon_ctx.sock);
1699 if (send(malware_daemon_ctx.sock, big_buffer, (size_t)n, 0) < 0)
1700 return m_panic_defer_3(scanent, NULL,
1701 string_sprintf("unable to send file body to socket (%s): %s", hostname, strerror(errno)),
1702 malware_daemon_ctx.sock);
1704 #ifdef EXIM_TCP_CORK
1708 (void) setsockopt(malware_daemon_ctx.sock, IPPROTO_TCP, EXIM_TCP_CORK,
1709 US &off, sizeof(off));
1714 send_final_zeroblock = 0;
1715 if (send(malware_daemon_ctx.sock, &send_final_zeroblock, sizeof(send_final_zeroblock), 0) < 0)
1716 return m_panic_defer_3(scanent, NULL,
1717 string_sprintf("unable to send file terminator to socket (%s)", hostname),
1718 malware_daemon_ctx.sock);
1721 { /* use scan command */
1722 /* Send a SCAN command pointing to a filename; then in the then in the
1723 * scan-method-neutral part, read the response back */
1725 /* ================================================================= */
1727 /* Prior to the reworking post-Exim-4.72, this scanned a directory,
1728 which dates to when ClamAV needed us to break apart the email into the
1729 MIME parts (eg, with the now deprecated demime condition coming first).
1730 Some time back, ClamAV gained the ability to deconstruct the emails, so
1731 doing this would actually have resulted in the mail attachments being
1732 scanned twice, in the broken out files and from the original .eml.
1733 Since ClamAV now handles emails (and has for quite some time) we can
1734 just use the email file itself. */
1735 /* Pass the string to ClamAV (7 = "SCAN \n" + \0), if not already sent */
1737 DEBUG(D_acl) debug_printf_indent(
1738 "Malware scan: issuing %s local-path scan [%s]\n",
1739 scanner_name, scanner_options);
1742 if (send(malware_daemon_ctx.sock, cmd_str.data, cmd_str.len, 0) < 0)
1743 return m_panic_defer_3(scanent, CUS callout_address,
1744 string_sprintf("unable to write to socket (%s)", strerror(errno)),
1745 malware_daemon_ctx.sock);
1747 /* Do not shut down the socket for writing; a user report noted that
1748 clamd 0.70 does not react well to this. */
1750 /* Commands have been sent, no matter which scan method or connection
1751 type we're using; now just read the result, independent of method. */
1753 /* Read the result */
1754 memset(av_buffer, 0, sizeof(av_buffer));
1755 bread = ip_recv(&malware_daemon_ctx, av_buffer, sizeof(av_buffer), tmo);
1756 (void)close(malware_daemon_ctx.sock);
1757 malware_daemon_ctx.sock = -1;
1758 malware_daemon_ctx.tls_ctx = NULL;
1761 return m_panic_defer(scanent, CUS callout_address,
1762 string_sprintf("unable to read from socket (%s)",
1763 errno == 0 ? "EOF" : strerror(errno)));
1765 if (bread == sizeof(av_buffer))
1766 return m_panic_defer(scanent, CUS callout_address,
1767 US"buffer too small");
1768 /* We're now assured of a NULL at the end of av_buffer */
1770 /* Check the result. ClamAV returns one of two result formats.
1771 In the basic mode, the response is of the form:
1772 infected: -> "<filename>: <virusname> FOUND"
1773 not-infected: -> "<filename>: OK"
1774 error: -> "<filename>: <errcode> ERROR
1775 If the ExtendedDetectionInfo option has been turned on, then we get:
1776 "<filename>: <virusname>(<virushash>:<virussize>) FOUND"
1777 for the infected case. Compare:
1778 /tmp/eicar.com: Eicar-Test-Signature FOUND
1779 /tmp/eicar.com: Eicar-Test-Signature(44d88612fea8a8f36de82e1278abb02f:68) FOUND
1781 In the streaming case, clamd uses the filename "stream" which you should
1782 be able to verify with { ktrace clamdscan --stream /tmp/eicar.com }. (The
1783 client app will replace "stream" with the original filename before returning
1784 results to stdout, but the trace shows the data).
1786 We will assume that the pathname passed to clamd from Exim does not contain
1787 a colon. We will have whined loudly above if the eml_filename does (and we're
1788 passing a filename to clamd). */
1791 return m_panic_defer(scanent, CUS callout_address,
1792 US"ClamAV returned null");
1794 /* strip newline at the end (won't be present for zINSTREAM)
1795 (also any trailing whitespace, which shouldn't exist, but we depend upon
1796 this below, so double-check) */
1798 p = av_buffer + Ustrlen(av_buffer) - 1;
1799 if (*p == '\n') *p = '\0';
1801 DEBUG(D_acl) debug_printf_indent("Malware response: %s\n", av_buffer);
1803 while (isspace(*--p) && (p > av_buffer))
1807 /* colon in returned output? */
1808 if(!(p = Ustrchr(av_buffer,':')))
1809 return m_panic_defer(scanent, CUS callout_address, string_sprintf(
1810 "ClamAV returned malformed result (missing colon): %s",
1813 /* strip filename */
1814 while (*p && isspace(*++p)) /**/;
1817 /* It would be bad to encounter a virus with "FOUND" in part of the name,
1818 but we should at least be resistant to it. */
1819 p = Ustrrchr(vname, ' ');
1820 result_tag = p ? p+1 : vname;
1822 if (Ustrcmp(result_tag, "FOUND") == 0)
1824 /* p should still be the whitespace before the result_tag */
1825 while (isspace(*p)) --p;
1827 /* Strip off the extended information too, which will be in parens
1828 after the virus name, with no intervening whitespace. */
1831 /* "(hash:size)", so previous '(' will do; if not found, we have
1832 a curious virus name, but not an error. */
1833 p = Ustrrchr(vname, '(');
1837 malware_name = string_copy(vname);
1838 DEBUG(D_acl) debug_printf_indent("Malware found, name \"%s\"\n", malware_name);
1841 else if (Ustrcmp(result_tag, "ERROR") == 0)
1842 return m_panic_defer(scanent, CUS callout_address,
1843 string_sprintf("ClamAV returned: %s", av_buffer));
1845 else if (Ustrcmp(result_tag, "OK") == 0)
1847 /* Everything should be OK */
1848 malware_name = NULL;
1849 DEBUG(D_acl) debug_printf_indent("Malware not found\n");
1853 return m_panic_defer(scanent, CUS callout_address,
1854 string_sprintf("unparseable response from ClamAV: {%s}", av_buffer));
1860 #ifndef DISABLE_MAL_SOCK
1861 case M_SOCK: /* "sock" scanner type ------------------------------------- */
1862 /* This code was derived by Martin Poole from the clamd code contributed
1863 by David Saez and the cmdline code
1867 uschar * commandline;
1868 uschar av_buffer[1024];
1869 uschar * linebuffer;
1870 uschar * sockline_scanner;
1871 uschar sockline_scanner_default[] = "%s\n";
1872 const pcre *sockline_trig_re;
1873 const pcre *sockline_name_re;
1875 /* find scanner command line */
1876 if ( (sockline_scanner = string_nextinlist(&av_scanner_work, &sep,
1878 && *sockline_scanner
1880 { /* check for no expansions apart from one %s */
1881 uschar * s = Ustrchr(sockline_scanner, '%');
1883 if ((*s != 's' && *s != '%') || Ustrchr(s+1, '%'))
1884 return m_panic_defer_3(scanent, NULL,
1885 US"unsafe sock scanner call spec", malware_daemon_ctx.sock);
1888 sockline_scanner = sockline_scanner_default;
1889 DEBUG(D_acl) debug_printf_indent("%15s%10s'%s'\n", "", "cmdline: ",
1890 string_printing(sockline_scanner));
1892 /* find scanner output trigger */
1893 sockline_trig_re = m_pcre_nextinlist(&av_scanner_work, &sep,
1894 "missing trigger specification", &errstr);
1895 if (!sockline_trig_re)
1896 return m_panic_defer_3(scanent, NULL, errstr, malware_daemon_ctx.sock);
1898 /* find virus name regex */
1899 sockline_name_re = m_pcre_nextinlist(&av_scanner_work, &sep,
1900 "missing virus name regex specification", &errstr);
1901 if (!sockline_name_re)
1902 return m_panic_defer_3(scanent, NULL, errstr, malware_daemon_ctx.sock);
1904 /* prepare scanner call - security depends on expansions check above */
1905 commandline = string_sprintf( CS sockline_scanner, CS eml_filename);
1906 DEBUG(D_acl) debug_printf_indent("%15s%10s'%s'\n", "", "expanded: ",
1907 string_printing(commandline));
1909 /* Pass the command string to the socket */
1910 if (m_sock_send(malware_daemon_ctx.sock, commandline, Ustrlen(commandline), &errstr) < 0)
1911 return m_panic_defer(scanent, CUS callout_address, errstr);
1913 /* Read the result */
1914 bread = ip_recv(&malware_daemon_ctx, av_buffer, sizeof(av_buffer), tmo);
1917 return m_panic_defer_3(scanent, CUS callout_address,
1918 string_sprintf("unable to read from socket (%s)", strerror(errno)),
1919 malware_daemon_ctx.sock);
1921 if (bread == sizeof(av_buffer))
1922 return m_panic_defer_3(scanent, CUS callout_address,
1923 US"buffer too small", malware_daemon_ctx.sock);
1924 av_buffer[bread] = '\0';
1925 linebuffer = string_copy(av_buffer);
1926 DEBUG(D_acl) debug_printf_indent("%15s%10s'%s'\n", "", "answer: ",
1927 string_printing(linebuffer));
1929 /* try trigger match */
1930 if (regex_match_and_setup(sockline_trig_re, linebuffer, 0, -1))
1932 if (!(malware_name = m_pcre_exec(sockline_name_re, av_buffer)))
1933 malware_name = US "unknown";
1934 DEBUG(D_acl) debug_printf_indent("%15s%10s'%s'\n", "", "name: ",
1935 string_printing(malware_name));
1937 else /* no virus found */
1938 malware_name = NULL;
1943 #ifndef DISABLE_MAL_MKS
1944 case M_MKSD: /* "mksd" scanner type ------------------------------------- */
1946 char *mksd_options_end;
1947 int mksd_maxproc = 1; /* default, if no option supplied */
1950 if (scanner_options)
1952 mksd_maxproc = (int)strtol(CS scanner_options, &mksd_options_end, 10);
1953 if ( *scanner_options == '\0'
1954 || *mksd_options_end != '\0'
1956 || mksd_maxproc > 32
1958 return m_panic_defer(scanent, CUS callout_address,
1959 string_sprintf("invalid option '%s'", scanner_options));
1962 if((malware_daemon_ctx.sock = ip_unixsocket(US "/var/run/mksd/socket", &errstr)) < 0)
1963 return m_panic_defer(scanent, CUS callout_address, errstr);
1965 malware_name = NULL;
1967 DEBUG(D_acl) debug_printf_indent("Malware scan: issuing %s scan\n", scanner_name);
1969 if ((retval = mksd_scan_packed(scanent, malware_daemon_ctx.sock, eml_filename, tmo)) != OK)
1971 close (malware_daemon_ctx.sock);
1978 #ifndef DISABLE_MAL_AVAST
1979 case M_AVAST: /* "avast" scanner type ----------------------------------- */
1982 uschar * scanrequest;
1983 enum {AVA_HELO, AVA_OPT, AVA_RSP, AVA_DONE} avast_stage;
1985 uschar * error_message = NULL;
1986 BOOL more_data = FALSE;
1989 /* According to Martin Tuma @avast the protocol uses "escaped
1990 whitespace", that is, every embedded whitespace is backslash
1991 escaped, as well as backslash is protected by backslash.
1992 The returned lines contain the name of the scanned file, a tab
1996 [E] - some error occurred
1997 Such marker follows the first non-escaped TAB. For more information
1998 see avast-protocol(5)
2000 We observed two cases:
2002 <- /file [E]0.0 Error 13 Permission denied
2003 <- 451 SCAN Engine error 13 permission denied
2006 <- /file… [E]3.0 Error 41120 The file is a decompression bomb
2008 <- /file… [+]2.0 0 Eicar Test Virus!!!
2011 If the scanner returns 4xx, DEFER is a good decision, combined
2012 with a panic log entry, to get the admin's attention.
2014 If the scanner returns 200, we reject it as malware, if found any,
2015 or, in case of an error, we set the malware message to the error
2018 Some of the >= 42000 errors are message related - usually some
2019 broken archives etc, but some of them are e.g. license related.
2020 Once the license expires the engine starts returning errors for
2021 every scanning attempt. I¹ have the full list of the error codes
2022 but it is not a public API and is subject to change. It is hard
2023 for me to say what you should do in case of an engine error. You
2024 can have a “Treat * unscanned file as infection” policy or “Treat
2025 unscanned file as clean” policy. ¹) Jakub Bednar
2029 if ( ( !ava_re_clean
2030 && !(ava_re_clean = m_pcre_compile(ava_re_clean_str, &errstr)))
2032 && !(ava_re_virus = m_pcre_compile(ava_re_virus_str, &errstr)))
2034 && !(ava_re_error = m_pcre_compile(ava_re_error_str, &errstr)))
2036 return malware_panic_defer(errstr);
2038 /* wait for result */
2039 for (avast_stage = AVA_HELO;
2040 (nread = recv_line(malware_daemon_ctx.sock, buf, sizeof(buf), tmo)) > 0;
2043 int slen = Ustrlen(buf);
2047 /* Multi line responses are bracketed between 210 … and nnn … */
2048 if (Ustrncmp(buf, "210", 3) == 0)
2053 else if (more_data && isdigit(buf[0])) more_data = 0;
2055 switch (avast_stage)
2058 if (more_data) continue;
2059 if (Ustrncmp(buf, "220", 3) != 0)
2060 goto endloop; /* require a 220 */
2064 if (more_data) continue;
2065 if (Ustrncmp(buf, "200", 3) != 0)
2066 goto endloop; /* require a 200 */
2071 /* Check for another option to send. Newline-terminate it. */
2072 if ((scanrequest = string_nextinlist(&av_scanner_work, &sep,
2075 if (Ustrcmp(scanrequest, "pass_unscanned") == 0)
2077 DEBUG(D_acl) debug_printf_indent("pass unscanned files as clean\n");
2081 scanrequest = string_sprintf("%s\n", scanrequest);
2082 avast_stage = AVA_OPT; /* just sent option */
2083 DEBUG(D_acl) debug_printf_indent("send to avast OPTION: %s", scanrequest);
2087 scanrequest = string_sprintf("SCAN %s\n", eml_dir);
2088 avast_stage = AVA_RSP; /* just sent command */
2089 DEBUG(D_acl) debug_printf_indent("send to avast REQUEST: SCAN %s\n", eml_dir);
2092 /* send config-cmd or scan-request to socket */
2093 len = Ustrlen(scanrequest);
2094 if (send(malware_daemon_ctx.sock, scanrequest, len, 0) == -1)
2096 scanrequest[len-1] = '\0';
2097 return m_panic_defer_3(scanent, CUS callout_address, string_sprintf(
2098 "unable to send request '%s' to socket (%s): %s",
2099 scanrequest, scanner_options, strerror(errno)), malware_daemon_ctx.sock);
2106 if (isdigit(buf[0])) /* We're done */
2109 if (malware_name) /* Nothing else matters, just read on */
2112 if (pcre_exec(ava_re_clean, NULL, CS buf, slen, 0, 0, NULL, 0) == 0)
2115 if ((malware_name = m_pcre_exec(ava_re_virus, buf)))
2117 unescape(malware_name);
2119 debug_printf_indent("unescaped malware name: '%s'\n", malware_name);
2123 if (strict) /* treat scanner errors as malware */
2125 if ((malware_name = m_pcre_exec(ava_re_error, buf)))
2127 unescape(malware_name);
2129 debug_printf_indent("unescaped error message: '%s'\n", malware_name);
2133 else if (pcre_exec(ava_re_error, NULL, CS buf, slen, 0, 0, NULL, 0) == 0)
2135 log_write(0, LOG_MAIN, "internal scanner error (ignored): %s", buf);
2139 /* here also for any unexpected response from the scanner */
2140 DEBUG(D_acl) debug_printf("avast response not handled: '%s'\n", buf);
2144 default: log_write(0, LOG_PANIC, "%s:%d:%s: should not happen",
2145 __FILE__, __LINE__, __FUNCTION__);
2152 if (nread == -1) error_message = US"EOF from scanner";
2153 else if (nread < 0) error_message = US"timeout from scanner";
2154 else if (nread == 0) error_message = US"got nothing from scanner";
2155 else if (buf[0] != '2') error_message = buf;
2157 DEBUG(D_acl) debug_printf_indent("sent to avast QUIT\n");
2158 if (send(malware_daemon_ctx.sock, "QUIT\n", 5, 0) == -1)
2159 return m_panic_defer_3(scanent, CUS callout_address,
2160 string_sprintf("unable to send quit request to socket (%s): %s",
2161 scanner_options, strerror(errno)), malware_daemon_ctx.sock);
2164 return m_panic_defer_3(scanent, CUS callout_address, error_message, malware_daemon_ctx.sock);
2168 } /* scanner type switch */
2170 if (malware_daemon_ctx.sock >= 0)
2171 (void) close (malware_daemon_ctx.sock);
2172 malware_ok = TRUE; /* set "been here, done that" marker */
2175 /* match virus name against pattern (caseless ------->----------v) */
2176 if (malware_name && regex_match_and_setup(re, malware_name, 0, -1))
2178 DEBUG(D_acl) debug_printf_indent(
2179 "Matched regex to malware [%s] [%s]\n", malware_re, malware_name);
2187 /*************************************************
2188 * Scan an email for malware *
2189 *************************************************/
2191 /* This is the normal interface for scanning an email, which doesn't need a
2192 filename; it's a wrapper around the malware_file function.
2195 malware_re match condition for "malware="
2196 timeout if nonzero, timeout in seconds
2198 Returns: Exim message processing code (OK, FAIL, DEFER, ...)
2199 where true means malware was found (condition applies)
2202 malware(const uschar * malware_re, int timeout)
2204 int ret = malware_internal(malware_re, NULL, timeout);
2206 if (ret == DEFER) av_failed = TRUE;
2211 /*************************************************
2212 * Scan a file for malware *
2213 *************************************************/
2215 /* This is a test wrapper for scanning an email, which is not used in
2216 normal processing. Scan any file, using the Exim scanning interface.
2217 This function tampers with various global variables so is unsafe to use
2218 in any other context.
2221 eml_filename a file holding the message to be scanned
2223 Returns: Exim message processing code (OK, FAIL, DEFER, ...)
2224 where true means malware was found (condition applies)
2227 malware_in_file(uschar *eml_filename)
2229 uschar message_id_buf[64];
2232 /* spool_mbox() assumes various parameters exist, when creating
2233 the relevant directory and the email within */
2235 (void) string_format(message_id_buf, sizeof(message_id_buf),
2236 "dummy-%d", vaguely_random_number(INT_MAX));
2237 message_id = message_id_buf;
2238 sender_address = US"malware-sender@example.net";
2240 recipients_list = NULL;
2241 receive_add_recipient(US"malware-victim@example.net", -1);
2242 f.enable_dollar_recipients = TRUE;
2244 ret = malware_internal(US"*", eml_filename, 0);
2246 Ustrncpy(spooled_message_id, message_id, sizeof(spooled_message_id));
2249 /* don't set no_mbox_unspool; at present, there's no way for it to become
2250 set, but if that changes, then it should apply to these tests too */
2254 /* silence static analysis tools */
2264 if (!malware_default_re)
2265 malware_default_re = regex_must_compile(malware_regex_default, FALSE, TRUE);
2267 #ifndef DISABLE_MAL_DRWEB
2269 drweb_re = regex_must_compile(drweb_re_str, FALSE, TRUE);
2271 #ifndef DISABLE_MAL_FSECURE
2273 fsec_re = regex_must_compile(fsec_re_str, FALSE, TRUE);
2275 #ifndef DISABLE_MAL_KAV
2277 kav_re_sus = regex_must_compile(kav_re_sus_str, FALSE, TRUE);
2279 kav_re_inf = regex_must_compile(kav_re_inf_str, FALSE, TRUE);
2281 #ifndef DISABLE_MAL_AVAST
2283 ava_re_clean = regex_must_compile(ava_re_clean_str, FALSE, TRUE);
2285 ava_re_virus = regex_must_compile(ava_re_virus_str, FALSE, TRUE);
2287 ava_re_error = regex_must_compile(ava_re_error_str, FALSE, TRUE);
2289 #ifndef DISABLE_MAL_FFROT6D
2290 if (!fprot6d_re_error)
2291 fprot6d_re_error = regex_must_compile(fprot6d_re_error_str, FALSE, TRUE);
2292 if (!fprot6d_re_virus)
2293 fprot6d_re_virus = regex_must_compile(fprot6d_re_virus_str, FALSE, TRUE);
2299 malware_show_supported(gstring * g)
2301 g = string_cat(g, US"Malware:");
2302 for (struct scan * sc = m_scans; sc->scancode != (scanner_t)-1; sc++)
2303 g = string_fmt_append(g, " %s", sc->name);
2304 return string_cat(g, US"\n");
2308 # endif /*!MACRO_PREDEF*/
2309 #endif /*WITH_CONTENT_SCAN*/