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; }
1562 cmd_str.data = string_sprintf("SCAN %s\n%n", eml_filename, &cmd_str.len);
1564 /* We have some network servers specified */
1567 /* Confirmed in ClamAV source (0.95.3) that the TCPAddr option of clamd
1568 only supports AF_INET, but we should probably be looking to the
1569 future and rewriting this to be protocol-independent anyway. */
1571 while (num_servers > 0)
1573 int i = random_number(num_servers);
1574 clamd_address * cd = cv[i];
1576 DEBUG(D_acl) debug_printf_indent("trying server name %s, port %u\n",
1577 cd->hostspec, cd->tcp_port);
1579 /* Lookup the host. This is to ensure that we connect to the same IP
1580 on both connections (as one host could resolve to multiple ips) */
1583 /*XXX we trust that the cmd_str is idempotent */
1584 if ((malware_daemon_ctx.sock = m_tcpsocket(cd->hostspec, cd->tcp_port,
1586 use_scan_command ? &cmd_str : NULL)) >= 0)
1588 /* Connection successfully established with a server */
1589 hostname = cd->hostspec;
1590 if (use_scan_command) cmd_str.len = 0;
1593 if (cd->retry <= 0) break;
1594 while (cd->retry > 0) cd->retry = sleep(cd->retry);
1596 if (malware_daemon_ctx.sock >= 0)
1599 (void) m_panic_defer(scanent, CUS callout_address, errstr);
1601 /* Remove the server from the list. XXX We should free the memory */
1603 for (; i < num_servers; i++)
1607 if (num_servers == 0)
1608 return m_panic_defer(scanent, NULL, US"all servers failed");
1613 if ((malware_daemon_ctx.sock = ip_unixsocket(cv[0]->hostspec, &errstr)) >= 0)
1615 hostname = cv[0]->hostspec;
1618 if (cv[0]->retry <= 0)
1619 return m_panic_defer(scanent, CUS callout_address, errstr);
1620 while (cv[0]->retry > 0) cv[0]->retry = sleep(cv[0]->retry);
1623 /* have socket in variable "sock"; command to use is semi-independent of
1624 the socket protocol. We use SCAN if is local (either Unix/local
1625 domain socket, or explicitly told local) else we stream the data.
1626 How we stream the data depends upon how we were built. */
1628 if (!use_scan_command)
1631 #if defined(EXIM_TCP_CORK) && !defined(OS_SENDFILE)
1634 /* New protocol: "zINSTREAM\n" followed by a sequence of <length><data>
1635 chunks, <n> a 4-byte number (network order), terminated by a zero-length
1636 chunk. We only send one chunk. */
1638 DEBUG(D_acl) debug_printf_indent(
1639 "Malware scan: issuing %s new-style remote scan (zINSTREAM)\n",
1642 #if defined(EXIM_TCP_CORK)
1643 (void) setsockopt(malware_daemon_ctx.sock, IPPROTO_TCP, EXIM_TCP_CORK,
1644 US &on, sizeof(on));
1646 /* Pass the string to ClamAV (10 = "zINSTREAM\0"), if not already sent */
1648 if (send(malware_daemon_ctx.sock, cmd_str.data, cmd_str.len, 0) < 0)
1649 return m_panic_defer_3(scanent, CUS hostname,
1650 string_sprintf("unable to send zINSTREAM to socket (%s)",
1652 malware_daemon_ctx.sock);
1654 if ((clam_fd = exim_open2(CS eml_filename, O_RDONLY)) < 0)
1657 return m_panic_defer_3(scanent, NULL,
1658 string_sprintf("can't open spool file %s: %s",
1659 eml_filename, strerror(err)),
1660 malware_daemon_ctx.sock);
1662 if (fstat(clam_fd, &st) < 0)
1665 (void)close(clam_fd);
1666 return m_panic_defer_3(scanent, NULL,
1667 string_sprintf("can't stat spool file %s: %s",
1668 eml_filename, strerror(err)),
1669 malware_daemon_ctx.sock);
1671 fsize_uint = (unsigned int) st.st_size;
1672 if ((off_t)fsize_uint != st.st_size)
1674 (void)close(clam_fd);
1675 return m_panic_defer_3(scanent, NULL,
1676 string_sprintf("stat spool file %s, size overflow", eml_filename),
1677 malware_daemon_ctx.sock);
1680 /* send file size */
1681 send_size = htonl(fsize_uint);
1682 if (send(malware_daemon_ctx.sock, &send_size, sizeof(send_size), 0) < 0)
1683 return m_panic_defer_3(scanent, NULL,
1684 string_sprintf("unable to send file size to socket (%s)", hostname),
1685 malware_daemon_ctx.sock);
1687 /* send file body */
1691 int n = os_sendfile(malware_daemon_ctx.sock, clam_fd, NULL, (size_t)fsize_uint);
1693 return m_panic_defer_3(scanent, NULL,
1694 string_sprintf("unable to send file body to socket (%s): %s", hostname, strerror(errno)),
1695 malware_daemon_ctx.sock);
1698 int n = MIN(fsize_uint, big_buffer_size);
1699 if ((n = read(clam_fd, big_buffer, n)) < 0)
1700 return m_panic_defer_3(scanent, NULL,
1701 string_sprintf("can't read spool file %s: %s",
1702 eml_filename, strerror(errno)),
1703 malware_daemon_ctx.sock);
1704 if (send(malware_daemon_ctx.sock, big_buffer, (size_t)n, 0) < 0)
1705 return m_panic_defer_3(scanent, NULL,
1706 string_sprintf("unable to send file body to socket (%s): %s", hostname, strerror(errno)),
1707 malware_daemon_ctx.sock);
1709 # ifdef EXIM_TCP_CORK
1713 (void) setsockopt(malware_daemon_ctx.sock, IPPROTO_TCP, EXIM_TCP_CORK,
1714 US &off, sizeof(off));
1717 #endif /*!OS_SENDFILE*/
1721 send_final_zeroblock = 0;
1722 if (send(malware_daemon_ctx.sock, &send_final_zeroblock, sizeof(send_final_zeroblock), 0) < 0)
1723 return m_panic_defer_3(scanent, NULL,
1724 string_sprintf("unable to send file terminator to socket (%s)", hostname),
1725 malware_daemon_ctx.sock);
1727 (void) setsockopt(malware_daemon_ctx.sock, IPPROTO_TCP, EXIM_TCP_CORK,
1728 US &off, sizeof(off));
1732 { /* use scan command */
1733 /* Send a SCAN command pointing to a filename; then in the then in the
1734 scan-method-neutral part, read the response back */
1736 /* ================================================================= */
1738 /* Prior to the reworking post-Exim-4.72, this scanned a directory,
1739 which dates to when ClamAV needed us to break apart the email into the
1740 MIME parts (eg, with the now deprecated demime condition coming first).
1741 Some time back, ClamAV gained the ability to deconstruct the emails, so
1742 doing this would actually have resulted in the mail attachments being
1743 scanned twice, in the broken out files and from the original .eml.
1744 Since ClamAV now handles emails (and has for quite some time) we can
1745 just use the email file itself. */
1746 /* Pass the string to ClamAV (7 = "SCAN \n" + \0), if not already sent */
1748 DEBUG(D_acl) debug_printf_indent(
1749 "Malware scan: issuing %s local-path scan [%s]\n",
1750 scanner_name, scanner_options);
1753 if (send(malware_daemon_ctx.sock, cmd_str.data, cmd_str.len, 0) < 0)
1754 return m_panic_defer_3(scanent, CUS callout_address,
1755 string_sprintf("unable to write to socket (%s)", strerror(errno)),
1756 malware_daemon_ctx.sock);
1758 /* Do not shut down the socket for writing; a user report noted that
1759 clamd 0.70 does not react well to this. */
1761 /* Commands have been sent, no matter which scan method or connection
1762 type we're using; now just read the result, independent of method. */
1764 /* Read the result */
1765 memset(av_buffer, 0, sizeof(av_buffer));
1766 bread = ip_recv(&malware_daemon_ctx, av_buffer, sizeof(av_buffer), tmo);
1767 (void)close(malware_daemon_ctx.sock);
1768 malware_daemon_ctx.sock = -1;
1769 malware_daemon_ctx.tls_ctx = NULL;
1772 return m_panic_defer(scanent, CUS callout_address,
1773 string_sprintf("unable to read from socket (%s)",
1774 errno == 0 ? "EOF" : strerror(errno)));
1776 if (bread == sizeof(av_buffer))
1777 return m_panic_defer(scanent, CUS callout_address,
1778 US"buffer too small");
1779 /* We're now assured of a NULL at the end of av_buffer */
1781 /* Check the result. ClamAV returns one of two result formats.
1782 In the basic mode, the response is of the form:
1783 infected: -> "<filename>: <virusname> FOUND"
1784 not-infected: -> "<filename>: OK"
1785 error: -> "<filename>: <errcode> ERROR
1786 If the ExtendedDetectionInfo option has been turned on, then we get:
1787 "<filename>: <virusname>(<virushash>:<virussize>) FOUND"
1788 for the infected case. Compare:
1789 /tmp/eicar.com: Eicar-Test-Signature FOUND
1790 /tmp/eicar.com: Eicar-Test-Signature(44d88612fea8a8f36de82e1278abb02f:68) FOUND
1792 In the streaming case, clamd uses the filename "stream" which you should
1793 be able to verify with { ktrace clamdscan --stream /tmp/eicar.com }. (The
1794 client app will replace "stream" with the original filename before returning
1795 results to stdout, but the trace shows the data).
1797 We will assume that the pathname passed to clamd from Exim does not contain
1798 a colon. We will have whined loudly above if the eml_filename does (and we're
1799 passing a filename to clamd). */
1802 return m_panic_defer(scanent, CUS callout_address,
1803 US"ClamAV returned null");
1805 /* strip newline at the end (won't be present for zINSTREAM)
1806 (also any trailing whitespace, which shouldn't exist, but we depend upon
1807 this below, so double-check) */
1809 p = av_buffer + Ustrlen(av_buffer) - 1;
1810 if (*p == '\n') *p = '\0';
1812 DEBUG(D_acl) debug_printf_indent("Malware response: %s\n", av_buffer);
1814 while (isspace(*--p) && (p > av_buffer))
1818 /* colon in returned output? */
1819 if(!(p = Ustrchr(av_buffer,':')))
1820 return m_panic_defer(scanent, CUS callout_address, string_sprintf(
1821 "ClamAV returned malformed result (missing colon): %s",
1824 /* strip filename */
1825 while (*p && isspace(*++p)) /**/;
1828 /* It would be bad to encounter a virus with "FOUND" in part of the name,
1829 but we should at least be resistant to it. */
1830 p = Ustrrchr(vname, ' ');
1831 result_tag = p ? p+1 : vname;
1833 if (Ustrcmp(result_tag, "FOUND") == 0)
1835 /* p should still be the whitespace before the result_tag */
1836 while (isspace(*p)) --p;
1838 /* Strip off the extended information too, which will be in parens
1839 after the virus name, with no intervening whitespace. */
1842 /* "(hash:size)", so previous '(' will do; if not found, we have
1843 a curious virus name, but not an error. */
1844 p = Ustrrchr(vname, '(');
1848 malware_name = string_copy(vname);
1849 DEBUG(D_acl) debug_printf_indent("Malware found, name \"%s\"\n", malware_name);
1852 else if (Ustrcmp(result_tag, "ERROR") == 0)
1853 return m_panic_defer(scanent, CUS callout_address,
1854 string_sprintf("ClamAV returned: %s", av_buffer));
1856 else if (Ustrcmp(result_tag, "OK") == 0)
1858 /* Everything should be OK */
1859 malware_name = NULL;
1860 DEBUG(D_acl) debug_printf_indent("Malware not found\n");
1864 return m_panic_defer(scanent, CUS callout_address,
1865 string_sprintf("unparseable response from ClamAV: {%s}", av_buffer));
1871 #ifndef DISABLE_MAL_SOCK
1872 case M_SOCK: /* "sock" scanner type ------------------------------------- */
1873 /* This code was derived by Martin Poole from the clamd code contributed
1874 by David Saez and the cmdline code
1878 uschar * commandline;
1879 uschar av_buffer[1024];
1880 uschar * linebuffer;
1881 uschar * sockline_scanner;
1882 uschar sockline_scanner_default[] = "%s\n";
1883 const pcre *sockline_trig_re;
1884 const pcre *sockline_name_re;
1886 /* find scanner command line */
1887 if ( (sockline_scanner = string_nextinlist(&av_scanner_work, &sep,
1889 && *sockline_scanner
1891 { /* check for no expansions apart from one %s */
1892 uschar * s = Ustrchr(sockline_scanner, '%');
1894 if ((*s != 's' && *s != '%') || Ustrchr(s+1, '%'))
1895 return m_panic_defer_3(scanent, NULL,
1896 US"unsafe sock scanner call spec", malware_daemon_ctx.sock);
1899 sockline_scanner = sockline_scanner_default;
1900 DEBUG(D_acl) debug_printf_indent("%15s%10s'%s'\n", "", "cmdline: ",
1901 string_printing(sockline_scanner));
1903 /* find scanner output trigger */
1904 sockline_trig_re = m_pcre_nextinlist(&av_scanner_work, &sep,
1905 "missing trigger specification", &errstr);
1906 if (!sockline_trig_re)
1907 return m_panic_defer_3(scanent, NULL, errstr, malware_daemon_ctx.sock);
1909 /* find virus name regex */
1910 sockline_name_re = m_pcre_nextinlist(&av_scanner_work, &sep,
1911 "missing virus name regex specification", &errstr);
1912 if (!sockline_name_re)
1913 return m_panic_defer_3(scanent, NULL, errstr, malware_daemon_ctx.sock);
1915 /* prepare scanner call - security depends on expansions check above */
1916 commandline = string_sprintf( CS sockline_scanner, CS eml_filename);
1917 DEBUG(D_acl) debug_printf_indent("%15s%10s'%s'\n", "", "expanded: ",
1918 string_printing(commandline));
1920 /* Pass the command string to the socket */
1921 if (m_sock_send(malware_daemon_ctx.sock, commandline, Ustrlen(commandline), &errstr) < 0)
1922 return m_panic_defer(scanent, CUS callout_address, errstr);
1924 /* Read the result */
1925 bread = ip_recv(&malware_daemon_ctx, av_buffer, sizeof(av_buffer), tmo);
1928 return m_panic_defer_3(scanent, CUS callout_address,
1929 string_sprintf("unable to read from socket (%s)", strerror(errno)),
1930 malware_daemon_ctx.sock);
1932 if (bread == sizeof(av_buffer))
1933 return m_panic_defer_3(scanent, CUS callout_address,
1934 US"buffer too small", malware_daemon_ctx.sock);
1935 av_buffer[bread] = '\0';
1936 linebuffer = string_copy(av_buffer);
1937 DEBUG(D_acl) debug_printf_indent("%15s%10s'%s'\n", "", "answer: ",
1938 string_printing(linebuffer));
1940 /* try trigger match */
1941 if (regex_match_and_setup(sockline_trig_re, linebuffer, 0, -1))
1943 if (!(malware_name = m_pcre_exec(sockline_name_re, av_buffer)))
1944 malware_name = US "unknown";
1945 DEBUG(D_acl) debug_printf_indent("%15s%10s'%s'\n", "", "name: ",
1946 string_printing(malware_name));
1948 else /* no virus found */
1949 malware_name = NULL;
1954 #ifndef DISABLE_MAL_MKS
1955 case M_MKSD: /* "mksd" scanner type ------------------------------------- */
1957 char *mksd_options_end;
1958 int mksd_maxproc = 1; /* default, if no option supplied */
1961 if (scanner_options)
1963 mksd_maxproc = (int)strtol(CS scanner_options, &mksd_options_end, 10);
1964 if ( *scanner_options == '\0'
1965 || *mksd_options_end != '\0'
1967 || mksd_maxproc > 32
1969 return m_panic_defer(scanent, CUS callout_address,
1970 string_sprintf("invalid option '%s'", scanner_options));
1973 if((malware_daemon_ctx.sock = ip_unixsocket(US "/var/run/mksd/socket", &errstr)) < 0)
1974 return m_panic_defer(scanent, CUS callout_address, errstr);
1976 malware_name = NULL;
1978 DEBUG(D_acl) debug_printf_indent("Malware scan: issuing %s scan\n", scanner_name);
1980 if ((retval = mksd_scan_packed(scanent, malware_daemon_ctx.sock, eml_filename, tmo)) != OK)
1982 close (malware_daemon_ctx.sock);
1989 #ifndef DISABLE_MAL_AVAST
1990 case M_AVAST: /* "avast" scanner type ----------------------------------- */
1993 uschar * scanrequest;
1994 enum {AVA_HELO, AVA_OPT, AVA_RSP, AVA_DONE} avast_stage;
1996 uschar * error_message = NULL;
1997 BOOL more_data = FALSE;
2000 /* According to Martin Tuma @avast the protocol uses "escaped
2001 whitespace", that is, every embedded whitespace is backslash
2002 escaped, as well as backslash is protected by backslash.
2003 The returned lines contain the name of the scanned file, a tab
2007 [E] - some error occurred
2008 Such marker follows the first non-escaped TAB. For more information
2009 see avast-protocol(5)
2011 We observed two cases:
2013 <- /file [E]0.0 Error 13 Permission denied
2014 <- 451 SCAN Engine error 13 permission denied
2017 <- /file… [E]3.0 Error 41120 The file is a decompression bomb
2019 <- /file… [+]2.0 0 Eicar Test Virus!!!
2022 If the scanner returns 4xx, DEFER is a good decision, combined
2023 with a panic log entry, to get the admin's attention.
2025 If the scanner returns 200, we reject it as malware, if found any,
2026 or, in case of an error, we set the malware message to the error
2029 Some of the >= 42000 errors are message related - usually some
2030 broken archives etc, but some of them are e.g. license related.
2031 Once the license expires the engine starts returning errors for
2032 every scanning attempt. I¹ have the full list of the error codes
2033 but it is not a public API and is subject to change. It is hard
2034 for me to say what you should do in case of an engine error. You
2035 can have a “Treat * unscanned file as infection” policy or “Treat
2036 unscanned file as clean” policy. ¹) Jakub Bednar
2040 if ( ( !ava_re_clean
2041 && !(ava_re_clean = m_pcre_compile(ava_re_clean_str, &errstr)))
2043 && !(ava_re_virus = m_pcre_compile(ava_re_virus_str, &errstr)))
2045 && !(ava_re_error = m_pcre_compile(ava_re_error_str, &errstr)))
2047 return malware_panic_defer(errstr);
2049 /* wait for result */
2050 for (avast_stage = AVA_HELO;
2051 (nread = recv_line(malware_daemon_ctx.sock, buf, sizeof(buf), tmo)) > 0;
2054 int slen = Ustrlen(buf);
2058 /* Multi line responses are bracketed between 210 … and nnn … */
2059 if (Ustrncmp(buf, "210", 3) == 0)
2064 else if (more_data && isdigit(buf[0])) more_data = 0;
2066 switch (avast_stage)
2069 if (more_data) continue;
2070 if (Ustrncmp(buf, "220", 3) != 0)
2071 goto endloop; /* require a 220 */
2075 if (more_data) continue;
2076 if (Ustrncmp(buf, "200", 3) != 0)
2077 goto endloop; /* require a 200 */
2082 /* Check for another option to send. Newline-terminate it. */
2083 if ((scanrequest = string_nextinlist(&av_scanner_work, &sep,
2086 if (Ustrcmp(scanrequest, "pass_unscanned") == 0)
2088 DEBUG(D_acl) debug_printf_indent("pass unscanned files as clean\n");
2092 scanrequest = string_sprintf("%s\n", scanrequest);
2093 avast_stage = AVA_OPT; /* just sent option */
2094 DEBUG(D_acl) debug_printf_indent("send to avast OPTION: %s", scanrequest);
2098 scanrequest = string_sprintf("SCAN %s\n", eml_dir);
2099 avast_stage = AVA_RSP; /* just sent command */
2100 DEBUG(D_acl) debug_printf_indent("send to avast REQUEST: SCAN %s\n", eml_dir);
2103 /* send config-cmd or scan-request to socket */
2104 len = Ustrlen(scanrequest);
2105 if (send(malware_daemon_ctx.sock, scanrequest, len, 0) == -1)
2107 scanrequest[len-1] = '\0';
2108 return m_panic_defer_3(scanent, CUS callout_address, string_sprintf(
2109 "unable to send request '%s' to socket (%s): %s",
2110 scanrequest, scanner_options, strerror(errno)), malware_daemon_ctx.sock);
2117 if (isdigit(buf[0])) /* We're done */
2120 if (malware_name) /* Nothing else matters, just read on */
2123 if (pcre_exec(ava_re_clean, NULL, CS buf, slen, 0, 0, NULL, 0) == 0)
2126 if ((malware_name = m_pcre_exec(ava_re_virus, buf)))
2128 unescape(malware_name);
2130 debug_printf_indent("unescaped malware name: '%s'\n", malware_name);
2134 if (strict) /* treat scanner errors as malware */
2136 if ((malware_name = m_pcre_exec(ava_re_error, buf)))
2138 unescape(malware_name);
2140 debug_printf_indent("unescaped error message: '%s'\n", malware_name);
2144 else if (pcre_exec(ava_re_error, NULL, CS buf, slen, 0, 0, NULL, 0) == 0)
2146 log_write(0, LOG_MAIN, "internal scanner error (ignored): %s", buf);
2150 /* here also for any unexpected response from the scanner */
2151 DEBUG(D_acl) debug_printf("avast response not handled: '%s'\n", buf);
2155 default: log_write(0, LOG_PANIC, "%s:%d:%s: should not happen",
2156 __FILE__, __LINE__, __FUNCTION__);
2163 if (nread == -1) error_message = US"EOF from scanner";
2164 else if (nread < 0) error_message = US"timeout from scanner";
2165 else if (nread == 0) error_message = US"got nothing from scanner";
2166 else if (buf[0] != '2') error_message = buf;
2168 DEBUG(D_acl) debug_printf_indent("sent to avast QUIT\n");
2169 if (send(malware_daemon_ctx.sock, "QUIT\n", 5, 0) == -1)
2170 return m_panic_defer_3(scanent, CUS callout_address,
2171 string_sprintf("unable to send quit request to socket (%s): %s",
2172 scanner_options, strerror(errno)), malware_daemon_ctx.sock);
2175 return m_panic_defer_3(scanent, CUS callout_address, error_message, malware_daemon_ctx.sock);
2179 } /* scanner type switch */
2181 if (malware_daemon_ctx.sock >= 0)
2182 (void) close (malware_daemon_ctx.sock);
2183 malware_ok = TRUE; /* set "been here, done that" marker */
2186 /* match virus name against pattern (caseless ------->----------v) */
2187 if (malware_name && regex_match_and_setup(re, malware_name, 0, -1))
2189 DEBUG(D_acl) debug_printf_indent(
2190 "Matched regex to malware [%s] [%s]\n", malware_re, malware_name);
2198 /*************************************************
2199 * Scan an email for malware *
2200 *************************************************/
2202 /* This is the normal interface for scanning an email, which doesn't need a
2203 filename; it's a wrapper around the malware_file function.
2206 malware_re match condition for "malware="
2207 timeout if nonzero, timeout in seconds
2209 Returns: Exim message processing code (OK, FAIL, DEFER, ...)
2210 where true means malware was found (condition applies)
2213 malware(const uschar * malware_re, int timeout)
2215 int ret = malware_internal(malware_re, NULL, timeout);
2217 if (ret == DEFER) av_failed = TRUE;
2222 /*************************************************
2223 * Scan a file for malware *
2224 *************************************************/
2226 /* This is a test wrapper for scanning an email, which is not used in
2227 normal processing. Scan any file, using the Exim scanning interface.
2228 This function tampers with various global variables so is unsafe to use
2229 in any other context.
2232 eml_filename a file holding the message to be scanned
2234 Returns: Exim message processing code (OK, FAIL, DEFER, ...)
2235 where true means malware was found (condition applies)
2238 malware_in_file(uschar *eml_filename)
2240 uschar message_id_buf[64];
2243 /* spool_mbox() assumes various parameters exist, when creating
2244 the relevant directory and the email within */
2246 (void) string_format(message_id_buf, sizeof(message_id_buf),
2247 "dummy-%d", vaguely_random_number(INT_MAX));
2248 message_id = message_id_buf;
2249 sender_address = US"malware-sender@example.net";
2251 recipients_list = NULL;
2252 receive_add_recipient(US"malware-victim@example.net", -1);
2253 f.enable_dollar_recipients = TRUE;
2255 ret = malware_internal(US"*", eml_filename, 0);
2257 Ustrncpy(spooled_message_id, message_id, sizeof(spooled_message_id));
2260 /* don't set no_mbox_unspool; at present, there's no way for it to become
2261 set, but if that changes, then it should apply to these tests too */
2265 /* silence static analysis tools */
2275 if (!malware_default_re)
2276 malware_default_re = regex_must_compile(malware_regex_default, FALSE, TRUE);
2278 #ifndef DISABLE_MAL_DRWEB
2280 drweb_re = regex_must_compile(drweb_re_str, FALSE, TRUE);
2282 #ifndef DISABLE_MAL_FSECURE
2284 fsec_re = regex_must_compile(fsec_re_str, FALSE, TRUE);
2286 #ifndef DISABLE_MAL_KAV
2288 kav_re_sus = regex_must_compile(kav_re_sus_str, FALSE, TRUE);
2290 kav_re_inf = regex_must_compile(kav_re_inf_str, FALSE, TRUE);
2292 #ifndef DISABLE_MAL_AVAST
2294 ava_re_clean = regex_must_compile(ava_re_clean_str, FALSE, TRUE);
2296 ava_re_virus = regex_must_compile(ava_re_virus_str, FALSE, TRUE);
2298 ava_re_error = regex_must_compile(ava_re_error_str, FALSE, TRUE);
2300 #ifndef DISABLE_MAL_FFROT6D
2301 if (!fprot6d_re_error)
2302 fprot6d_re_error = regex_must_compile(fprot6d_re_error_str, FALSE, TRUE);
2303 if (!fprot6d_re_virus)
2304 fprot6d_re_virus = regex_must_compile(fprot6d_re_virus_str, FALSE, TRUE);
2310 malware_show_supported(gstring * g)
2312 g = string_cat(g, US"Malware:");
2313 for (struct scan * sc = m_scans; sc->scancode != (scanner_t)-1; sc++)
2314 g = string_fmt_append(g, " %s", sc->name);
2315 return string_cat(g, US"\n");
2319 # endif /*!MACRO_PREDEF*/
2320 #endif /*WITH_CONTENT_SCAN*/