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)
112 uschar buf[EXIM_DRIVERNAME_MAX];
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; }
1564 cmd_str.data = string_sprintf("SCAN %s\n%n", eml_filename, &n);
1565 cmd_str.len = n; /* .len is a size_t */
1568 /* We have some network servers specified */
1571 /* Confirmed in ClamAV source (0.95.3) that the TCPAddr option of clamd
1572 only supports AF_INET, but we should probably be looking to the
1573 future and rewriting this to be protocol-independent anyway. */
1575 while (num_servers > 0)
1577 int i = random_number(num_servers);
1578 clamd_address * cd = cv[i];
1580 DEBUG(D_acl) debug_printf_indent("trying server name %s, port %u\n",
1581 cd->hostspec, cd->tcp_port);
1583 /* Lookup the host. This is to ensure that we connect to the same IP
1584 on both connections (as one host could resolve to multiple ips) */
1587 /*XXX we trust that the cmd_str is idempotent */
1588 if ((malware_daemon_ctx.sock = m_tcpsocket(cd->hostspec, cd->tcp_port,
1590 use_scan_command ? &cmd_str : NULL)) >= 0)
1592 /* Connection successfully established with a server */
1593 hostname = cd->hostspec;
1594 if (use_scan_command) cmd_str.len = 0;
1597 if (cd->retry <= 0) break;
1598 while (cd->retry > 0) cd->retry = sleep(cd->retry);
1600 if (malware_daemon_ctx.sock >= 0)
1603 (void) m_panic_defer(scanent, CUS callout_address, errstr);
1605 /* Remove the server from the list. XXX We should free the memory */
1607 for (; i < num_servers; i++)
1611 if (num_servers == 0)
1612 return m_panic_defer(scanent, NULL, US"all servers failed");
1617 if ((malware_daemon_ctx.sock = ip_unixsocket(cv[0]->hostspec, &errstr)) >= 0)
1619 hostname = cv[0]->hostspec;
1622 if (cv[0]->retry <= 0)
1623 return m_panic_defer(scanent, CUS callout_address, errstr);
1624 while (cv[0]->retry > 0) cv[0]->retry = sleep(cv[0]->retry);
1627 /* have socket in variable "sock"; command to use is semi-independent of
1628 the socket protocol. We use SCAN if is local (either Unix/local
1629 domain socket, or explicitly told local) else we stream the data.
1630 How we stream the data depends upon how we were built. */
1632 if (!use_scan_command)
1635 #if defined(EXIM_TCP_CORK) && !defined(OS_SENDFILE)
1638 /* New protocol: "zINSTREAM\n" followed by a sequence of <length><data>
1639 chunks, <n> a 4-byte number (network order), terminated by a zero-length
1640 chunk. We only send one chunk. */
1642 DEBUG(D_acl) debug_printf_indent(
1643 "Malware scan: issuing %s new-style remote scan (zINSTREAM)\n",
1646 #if defined(EXIM_TCP_CORK)
1647 (void) setsockopt(malware_daemon_ctx.sock, IPPROTO_TCP, EXIM_TCP_CORK,
1648 US &on, sizeof(on));
1650 /* Pass the string to ClamAV (10 = "zINSTREAM\0"), if not already sent */
1652 if (send(malware_daemon_ctx.sock, cmd_str.data, cmd_str.len, 0) < 0)
1653 return m_panic_defer_3(scanent, CUS hostname,
1654 string_sprintf("unable to send zINSTREAM to socket (%s)",
1656 malware_daemon_ctx.sock);
1658 if ((clam_fd = exim_open2(CS eml_filename, O_RDONLY)) < 0)
1661 return m_panic_defer_3(scanent, NULL,
1662 string_sprintf("can't open spool file %s: %s",
1663 eml_filename, strerror(err)),
1664 malware_daemon_ctx.sock);
1666 if (fstat(clam_fd, &st) < 0)
1669 (void)close(clam_fd);
1670 return m_panic_defer_3(scanent, NULL,
1671 string_sprintf("can't stat spool file %s: %s",
1672 eml_filename, strerror(err)),
1673 malware_daemon_ctx.sock);
1675 fsize_uint = (unsigned int) st.st_size;
1676 if ((off_t)fsize_uint != st.st_size)
1678 (void)close(clam_fd);
1679 return m_panic_defer_3(scanent, NULL,
1680 string_sprintf("stat spool file %s, size overflow", eml_filename),
1681 malware_daemon_ctx.sock);
1684 /* send file size */
1685 send_size = htonl(fsize_uint);
1686 if (send(malware_daemon_ctx.sock, &send_size, sizeof(send_size), 0) < 0)
1687 return m_panic_defer_3(scanent, NULL,
1688 string_sprintf("unable to send file size to socket (%s)", hostname),
1689 malware_daemon_ctx.sock);
1691 /* send file body */
1695 int n = os_sendfile(malware_daemon_ctx.sock, clam_fd, NULL, (size_t)fsize_uint);
1697 return m_panic_defer_3(scanent, NULL,
1698 string_sprintf("unable to send file body to socket (%s): %s", hostname, strerror(errno)),
1699 malware_daemon_ctx.sock);
1702 int n = MIN(fsize_uint, big_buffer_size);
1703 if ((n = read(clam_fd, big_buffer, n)) < 0)
1704 return m_panic_defer_3(scanent, NULL,
1705 string_sprintf("can't read spool file %s: %s",
1706 eml_filename, strerror(errno)),
1707 malware_daemon_ctx.sock);
1708 if (send(malware_daemon_ctx.sock, big_buffer, (size_t)n, 0) < 0)
1709 return m_panic_defer_3(scanent, NULL,
1710 string_sprintf("unable to send file body to socket (%s): %s", hostname, strerror(errno)),
1711 malware_daemon_ctx.sock);
1713 # ifdef EXIM_TCP_CORK
1717 (void) setsockopt(malware_daemon_ctx.sock, IPPROTO_TCP, EXIM_TCP_CORK,
1718 US &off, sizeof(off));
1721 #endif /*!OS_SENDFILE*/
1725 send_final_zeroblock = 0;
1726 if (send(malware_daemon_ctx.sock, &send_final_zeroblock, sizeof(send_final_zeroblock), 0) < 0)
1727 return m_panic_defer_3(scanent, NULL,
1728 string_sprintf("unable to send file terminator to socket (%s)", hostname),
1729 malware_daemon_ctx.sock);
1731 (void) setsockopt(malware_daemon_ctx.sock, IPPROTO_TCP, EXIM_TCP_CORK,
1732 US &off, sizeof(off));
1736 { /* use scan command */
1737 /* Send a SCAN command pointing to a filename; then in the then in the
1738 scan-method-neutral part, read the response back */
1740 /* ================================================================= */
1742 /* Prior to the reworking post-Exim-4.72, this scanned a directory,
1743 which dates to when ClamAV needed us to break apart the email into the
1744 MIME parts (eg, with the now deprecated demime condition coming first).
1745 Some time back, ClamAV gained the ability to deconstruct the emails, so
1746 doing this would actually have resulted in the mail attachments being
1747 scanned twice, in the broken out files and from the original .eml.
1748 Since ClamAV now handles emails (and has for quite some time) we can
1749 just use the email file itself. */
1750 /* Pass the string to ClamAV (7 = "SCAN \n" + \0), if not already sent */
1752 DEBUG(D_acl) debug_printf_indent(
1753 "Malware scan: issuing %s local-path scan [%s]\n",
1754 scanner_name, scanner_options);
1757 if (send(malware_daemon_ctx.sock, cmd_str.data, cmd_str.len, 0) < 0)
1758 return m_panic_defer_3(scanent, CUS callout_address,
1759 string_sprintf("unable to write to socket (%s)", strerror(errno)),
1760 malware_daemon_ctx.sock);
1762 /* Do not shut down the socket for writing; a user report noted that
1763 clamd 0.70 does not react well to this. */
1765 /* Commands have been sent, no matter which scan method or connection
1766 type we're using; now just read the result, independent of method. */
1768 /* Read the result */
1769 memset(av_buffer, 0, sizeof(av_buffer));
1770 bread = ip_recv(&malware_daemon_ctx, av_buffer, sizeof(av_buffer), tmo);
1771 (void)close(malware_daemon_ctx.sock);
1772 malware_daemon_ctx.sock = -1;
1773 malware_daemon_ctx.tls_ctx = NULL;
1776 return m_panic_defer(scanent, CUS callout_address,
1777 string_sprintf("unable to read from socket (%s)",
1778 errno == 0 ? "EOF" : strerror(errno)));
1780 if (bread == sizeof(av_buffer))
1781 return m_panic_defer(scanent, CUS callout_address,
1782 US"buffer too small");
1783 /* We're now assured of a NULL at the end of av_buffer */
1785 /* Check the result. ClamAV returns one of two result formats.
1786 In the basic mode, the response is of the form:
1787 infected: -> "<filename>: <virusname> FOUND"
1788 not-infected: -> "<filename>: OK"
1789 error: -> "<filename>: <errcode> ERROR
1790 If the ExtendedDetectionInfo option has been turned on, then we get:
1791 "<filename>: <virusname>(<virushash>:<virussize>) FOUND"
1792 for the infected case. Compare:
1793 /tmp/eicar.com: Eicar-Test-Signature FOUND
1794 /tmp/eicar.com: Eicar-Test-Signature(44d88612fea8a8f36de82e1278abb02f:68) FOUND
1796 In the streaming case, clamd uses the filename "stream" which you should
1797 be able to verify with { ktrace clamdscan --stream /tmp/eicar.com }. (The
1798 client app will replace "stream" with the original filename before returning
1799 results to stdout, but the trace shows the data).
1801 We will assume that the pathname passed to clamd from Exim does not contain
1802 a colon. We will have whined loudly above if the eml_filename does (and we're
1803 passing a filename to clamd). */
1806 return m_panic_defer(scanent, CUS callout_address,
1807 US"ClamAV returned null");
1809 /* strip newline at the end (won't be present for zINSTREAM)
1810 (also any trailing whitespace, which shouldn't exist, but we depend upon
1811 this below, so double-check) */
1813 p = av_buffer + Ustrlen(av_buffer) - 1;
1814 if (*p == '\n') *p = '\0';
1816 DEBUG(D_acl) debug_printf_indent("Malware response: %s\n", av_buffer);
1818 while (isspace(*--p) && (p > av_buffer))
1822 /* colon in returned output? */
1823 if (!(p = Ustrchr(av_buffer,':')))
1824 return m_panic_defer(scanent, CUS callout_address, string_sprintf(
1825 "ClamAV returned malformed result (missing colon): %s",
1828 /* strip filename */
1829 while (*p && isspace(*++p)) /**/;
1832 /* It would be bad to encounter a virus with "FOUND" in part of the name,
1833 but we should at least be resistant to it. */
1834 p = Ustrrchr(vname, ' ');
1835 result_tag = p ? p+1 : vname;
1837 if (Ustrcmp(result_tag, "FOUND") == 0)
1839 /* p should still be the whitespace before the result_tag */
1840 while (isspace(*p)) --p;
1842 /* Strip off the extended information too, which will be in parens
1843 after the virus name, with no intervening whitespace. */
1846 /* "(hash:size)", so previous '(' will do; if not found, we have
1847 a curious virus name, but not an error. */
1848 p = Ustrrchr(vname, '(');
1852 malware_name = string_copy(vname);
1853 DEBUG(D_acl) debug_printf_indent("Malware found, name \"%s\"\n", malware_name);
1856 else if (Ustrcmp(result_tag, "ERROR") == 0)
1857 return m_panic_defer(scanent, CUS callout_address,
1858 string_sprintf("ClamAV returned: %s", av_buffer));
1860 else if (Ustrcmp(result_tag, "OK") == 0)
1862 /* Everything should be OK */
1863 malware_name = NULL;
1864 DEBUG(D_acl) debug_printf_indent("Malware not found\n");
1868 return m_panic_defer(scanent, CUS callout_address,
1869 string_sprintf("unparseable response from ClamAV: {%s}", av_buffer));
1875 #ifndef DISABLE_MAL_SOCK
1876 case M_SOCK: /* "sock" scanner type ------------------------------------- */
1877 /* This code was derived by Martin Poole from the clamd code contributed
1878 by David Saez and the cmdline code
1882 uschar * commandline;
1883 uschar av_buffer[1024];
1884 uschar * linebuffer;
1885 uschar * sockline_scanner;
1886 uschar sockline_scanner_default[] = "%s\n";
1887 const pcre *sockline_trig_re;
1888 const pcre *sockline_name_re;
1890 /* find scanner command line */
1891 if ( (sockline_scanner = string_nextinlist(&av_scanner_work, &sep,
1893 && *sockline_scanner
1895 { /* check for no expansions apart from one %s */
1896 uschar * s = Ustrchr(sockline_scanner, '%');
1898 if ((*s != 's' && *s != '%') || Ustrchr(s+1, '%'))
1899 return m_panic_defer_3(scanent, NULL,
1900 US"unsafe sock scanner call spec", malware_daemon_ctx.sock);
1903 sockline_scanner = sockline_scanner_default;
1904 DEBUG(D_acl) debug_printf_indent("%15s%10s'%s'\n", "", "cmdline: ",
1905 string_printing(sockline_scanner));
1907 /* find scanner output trigger */
1908 sockline_trig_re = m_pcre_nextinlist(&av_scanner_work, &sep,
1909 "missing trigger specification", &errstr);
1910 if (!sockline_trig_re)
1911 return m_panic_defer_3(scanent, NULL, errstr, malware_daemon_ctx.sock);
1913 /* find virus name regex */
1914 sockline_name_re = m_pcre_nextinlist(&av_scanner_work, &sep,
1915 "missing virus name regex specification", &errstr);
1916 if (!sockline_name_re)
1917 return m_panic_defer_3(scanent, NULL, errstr, malware_daemon_ctx.sock);
1919 /* prepare scanner call - security depends on expansions check above */
1920 commandline = string_sprintf( CS sockline_scanner, CS eml_filename);
1921 DEBUG(D_acl) debug_printf_indent("%15s%10s'%s'\n", "", "expanded: ",
1922 string_printing(commandline));
1924 /* Pass the command string to the socket */
1925 if (m_sock_send(malware_daemon_ctx.sock, commandline, Ustrlen(commandline), &errstr) < 0)
1926 return m_panic_defer(scanent, CUS callout_address, errstr);
1928 /* Read the result */
1929 bread = ip_recv(&malware_daemon_ctx, av_buffer, sizeof(av_buffer), tmo);
1932 return m_panic_defer_3(scanent, CUS callout_address,
1933 string_sprintf("unable to read from socket (%s)", strerror(errno)),
1934 malware_daemon_ctx.sock);
1936 if (bread == sizeof(av_buffer))
1937 return m_panic_defer_3(scanent, CUS callout_address,
1938 US"buffer too small", malware_daemon_ctx.sock);
1939 av_buffer[bread] = '\0';
1940 linebuffer = string_copy(av_buffer);
1941 DEBUG(D_acl) debug_printf_indent("%15s%10s'%s'\n", "", "answer: ",
1942 string_printing(linebuffer));
1944 /* try trigger match */
1945 if (regex_match_and_setup(sockline_trig_re, linebuffer, 0, -1))
1947 if (!(malware_name = m_pcre_exec(sockline_name_re, av_buffer)))
1948 malware_name = US "unknown";
1949 DEBUG(D_acl) debug_printf_indent("%15s%10s'%s'\n", "", "name: ",
1950 string_printing(malware_name));
1952 else /* no virus found */
1953 malware_name = NULL;
1958 #ifndef DISABLE_MAL_MKS
1959 case M_MKSD: /* "mksd" scanner type ------------------------------------- */
1961 char *mksd_options_end;
1962 int mksd_maxproc = 1; /* default, if no option supplied */
1965 if (scanner_options)
1967 mksd_maxproc = (int)strtol(CS scanner_options, &mksd_options_end, 10);
1968 if ( *scanner_options == '\0'
1969 || *mksd_options_end != '\0'
1971 || mksd_maxproc > 32
1973 return m_panic_defer(scanent, CUS callout_address,
1974 string_sprintf("invalid option '%s'", scanner_options));
1977 if((malware_daemon_ctx.sock = ip_unixsocket(US "/var/run/mksd/socket", &errstr)) < 0)
1978 return m_panic_defer(scanent, CUS callout_address, errstr);
1980 malware_name = NULL;
1982 DEBUG(D_acl) debug_printf_indent("Malware scan: issuing %s scan\n", scanner_name);
1984 if ((retval = mksd_scan_packed(scanent, malware_daemon_ctx.sock, eml_filename, tmo)) != OK)
1986 close (malware_daemon_ctx.sock);
1993 #ifndef DISABLE_MAL_AVAST
1994 case M_AVAST: /* "avast" scanner type ----------------------------------- */
1997 uschar * scanrequest;
1998 enum {AVA_HELO, AVA_OPT, AVA_RSP, AVA_DONE} avast_stage;
2000 uschar * error_message = NULL;
2001 BOOL more_data = FALSE;
2004 /* According to Martin Tuma @avast the protocol uses "escaped
2005 whitespace", that is, every embedded whitespace is backslash
2006 escaped, as well as backslash is protected by backslash.
2007 The returned lines contain the name of the scanned file, a tab
2011 [E] - some error occurred
2012 Such marker follows the first non-escaped TAB. For more information
2013 see avast-protocol(5)
2015 We observed two cases:
2017 <- /file [E]0.0 Error 13 Permission denied
2018 <- 451 SCAN Engine error 13 permission denied
2021 <- /file… [E]3.0 Error 41120 The file is a decompression bomb
2023 <- /file… [+]2.0 0 Eicar Test Virus!!!
2026 If the scanner returns 4xx, DEFER is a good decision, combined
2027 with a panic log entry, to get the admin's attention.
2029 If the scanner returns 200, we reject it as malware, if found any,
2030 or, in case of an error, we set the malware message to the error
2033 Some of the >= 42000 errors are message related - usually some
2034 broken archives etc, but some of them are e.g. license related.
2035 Once the license expires the engine starts returning errors for
2036 every scanning attempt. I¹ have the full list of the error codes
2037 but it is not a public API and is subject to change. It is hard
2038 for me to say what you should do in case of an engine error. You
2039 can have a “Treat * unscanned file as infection” policy or “Treat
2040 unscanned file as clean” policy. ¹) Jakub Bednar
2044 if ( ( !ava_re_clean
2045 && !(ava_re_clean = m_pcre_compile(ava_re_clean_str, &errstr)))
2047 && !(ava_re_virus = m_pcre_compile(ava_re_virus_str, &errstr)))
2049 && !(ava_re_error = m_pcre_compile(ava_re_error_str, &errstr)))
2051 return malware_panic_defer(errstr);
2053 /* wait for result */
2054 for (avast_stage = AVA_HELO;
2055 (nread = recv_line(malware_daemon_ctx.sock, buf, sizeof(buf), tmo)) > 0;
2058 int slen = Ustrlen(buf);
2062 /* Multi line responses are bracketed between 210 … and nnn … */
2063 if (Ustrncmp(buf, "210", 3) == 0)
2068 else if (more_data && isdigit(buf[0])) more_data = 0;
2070 switch (avast_stage)
2073 if (more_data) continue;
2074 if (Ustrncmp(buf, "220", 3) != 0)
2075 goto endloop; /* require a 220 */
2079 if (more_data) continue;
2080 if (Ustrncmp(buf, "200", 3) != 0)
2081 goto endloop; /* require a 200 */
2086 /* Check for another option to send. Newline-terminate it. */
2087 if ((scanrequest = string_nextinlist(&av_scanner_work, &sep,
2090 if (Ustrcmp(scanrequest, "pass_unscanned") == 0)
2092 DEBUG(D_acl) debug_printf_indent("pass unscanned files as clean\n");
2096 scanrequest = string_sprintf("%s\n", scanrequest);
2097 avast_stage = AVA_OPT; /* just sent option */
2098 DEBUG(D_acl) debug_printf_indent("send to avast OPTION: %s", scanrequest);
2102 scanrequest = string_sprintf("SCAN %s\n", eml_dir);
2103 avast_stage = AVA_RSP; /* just sent command */
2104 DEBUG(D_acl) debug_printf_indent("send to avast REQUEST: SCAN %s\n", eml_dir);
2107 /* send config-cmd or scan-request to socket */
2108 len = Ustrlen(scanrequest);
2109 if (send(malware_daemon_ctx.sock, scanrequest, len, 0) == -1)
2111 scanrequest[len-1] = '\0';
2112 return m_panic_defer_3(scanent, CUS callout_address, string_sprintf(
2113 "unable to send request '%s' to socket (%s): %s",
2114 scanrequest, scanner_options, strerror(errno)), malware_daemon_ctx.sock);
2121 if (isdigit(buf[0])) /* We're done */
2124 if (malware_name) /* Nothing else matters, just read on */
2127 if (pcre_exec(ava_re_clean, NULL, CS buf, slen, 0, 0, NULL, 0) == 0)
2130 if ((malware_name = m_pcre_exec(ava_re_virus, buf)))
2132 unescape(malware_name);
2134 debug_printf_indent("unescaped malware name: '%s'\n", malware_name);
2138 if (strict) /* treat scanner errors as malware */
2140 if ((malware_name = m_pcre_exec(ava_re_error, buf)))
2142 unescape(malware_name);
2144 debug_printf_indent("unescaped error message: '%s'\n", malware_name);
2148 else if (pcre_exec(ava_re_error, NULL, CS buf, slen, 0, 0, NULL, 0) == 0)
2150 log_write(0, LOG_MAIN, "internal scanner error (ignored): %s", buf);
2154 /* here also for any unexpected response from the scanner */
2155 DEBUG(D_acl) debug_printf("avast response not handled: '%s'\n", buf);
2159 default: log_write(0, LOG_PANIC, "%s:%d:%s: should not happen",
2160 __FILE__, __LINE__, __FUNCTION__);
2167 if (nread == -1) error_message = US"EOF from scanner";
2168 else if (nread < 0) error_message = US"timeout from scanner";
2169 else if (nread == 0) error_message = US"got nothing from scanner";
2170 else if (buf[0] != '2') error_message = buf;
2172 DEBUG(D_acl) debug_printf_indent("sent to avast QUIT\n");
2173 if (send(malware_daemon_ctx.sock, "QUIT\n", 5, 0) == -1)
2174 return m_panic_defer_3(scanent, CUS callout_address,
2175 string_sprintf("unable to send quit request to socket (%s): %s",
2176 scanner_options, strerror(errno)), malware_daemon_ctx.sock);
2179 return m_panic_defer_3(scanent, CUS callout_address, error_message, malware_daemon_ctx.sock);
2183 } /* scanner type switch */
2185 if (malware_daemon_ctx.sock >= 0)
2186 (void) close (malware_daemon_ctx.sock);
2187 malware_ok = TRUE; /* set "been here, done that" marker */
2190 /* match virus name against pattern (caseless ------->----------v) */
2191 if (malware_name && regex_match_and_setup(re, malware_name, 0, -1))
2193 DEBUG(D_acl) debug_printf_indent(
2194 "Matched regex to malware [%s] [%s]\n", malware_re, malware_name);
2202 /*************************************************
2203 * Scan an email for malware *
2204 *************************************************/
2206 /* This is the normal interface for scanning an email, which doesn't need a
2207 filename; it's a wrapper around the malware_file function.
2210 malware_re match condition for "malware="
2211 timeout if nonzero, timeout in seconds
2213 Returns: Exim message processing code (OK, FAIL, DEFER, ...)
2214 where true means malware was found (condition applies)
2217 malware(const uschar * malware_re, int timeout)
2219 int ret = malware_internal(malware_re, NULL, timeout);
2221 if (ret == DEFER) av_failed = TRUE;
2226 /*************************************************
2227 * Scan a file for malware *
2228 *************************************************/
2230 /* This is a test wrapper for scanning an email, which is not used in
2231 normal processing. Scan any file, using the Exim scanning interface.
2232 This function tampers with various global variables so is unsafe to use
2233 in any other context.
2236 eml_filename a file holding the message to be scanned
2238 Returns: Exim message processing code (OK, FAIL, DEFER, ...)
2239 where true means malware was found (condition applies)
2242 malware_in_file(uschar *eml_filename)
2244 uschar message_id_buf[64];
2247 /* spool_mbox() assumes various parameters exist, when creating
2248 the relevant directory and the email within */
2250 (void) string_format(message_id_buf, sizeof(message_id_buf),
2251 "dummy-%d", vaguely_random_number(INT_MAX));
2252 message_id = message_id_buf;
2253 sender_address = US"malware-sender@example.net";
2255 recipients_list = NULL;
2256 receive_add_recipient(US"malware-victim@example.net", -1);
2257 f.enable_dollar_recipients = TRUE;
2259 ret = malware_internal(US"*", eml_filename, 0);
2261 Ustrncpy(spooled_message_id, message_id, sizeof(spooled_message_id));
2264 /* don't set no_mbox_unspool; at present, there's no way for it to become
2265 set, but if that changes, then it should apply to these tests too */
2269 /* silence static analysis tools */
2279 if (!malware_default_re)
2280 malware_default_re = regex_must_compile(malware_regex_default, FALSE, TRUE);
2282 #ifndef DISABLE_MAL_DRWEB
2284 drweb_re = regex_must_compile(drweb_re_str, FALSE, TRUE);
2286 #ifndef DISABLE_MAL_FSECURE
2288 fsec_re = regex_must_compile(fsec_re_str, FALSE, TRUE);
2290 #ifndef DISABLE_MAL_KAV
2292 kav_re_sus = regex_must_compile(kav_re_sus_str, FALSE, TRUE);
2294 kav_re_inf = regex_must_compile(kav_re_inf_str, FALSE, TRUE);
2296 #ifndef DISABLE_MAL_AVAST
2298 ava_re_clean = regex_must_compile(ava_re_clean_str, FALSE, TRUE);
2300 ava_re_virus = regex_must_compile(ava_re_virus_str, FALSE, TRUE);
2302 ava_re_error = regex_must_compile(ava_re_error_str, FALSE, TRUE);
2304 #ifndef DISABLE_MAL_FFROT6D
2305 if (!fprot6d_re_error)
2306 fprot6d_re_error = regex_must_compile(fprot6d_re_error_str, FALSE, TRUE);
2307 if (!fprot6d_re_virus)
2308 fprot6d_re_virus = regex_must_compile(fprot6d_re_virus_str, FALSE, TRUE);
2314 malware_show_supported(gstring * g)
2316 g = string_cat(g, US"Malware:");
2317 for (struct scan * sc = m_scans; sc->scancode != (scanner_t)-1; sc++)
2318 g = string_fmt_append(g, " %s", sc->name);
2319 return string_cat(g, US"\n");
2323 # endif /*!MACRO_PREDEF*/
2324 #endif /*WITH_CONTENT_SCAN*/