1 /*************************************************
2 * Exim - an Internet mail transport agent *
3 *************************************************/
6 * Copyright (c) The Exim Maintainers 2015 - 2022
7 * Copyright (c) Tom Kistner <tom@duncanthrax.net> 2003 - 2015
9 * SPDX-License-Identifier: GPL-2.0-only
12 /* Code for calling virus (malware) scanners. Called from acl.c. */
15 #ifdef WITH_CONTENT_SCAN /* entire file */
18 #ifndef DISABLE_MAL_FFROTD
21 #ifndef DISABLE_MAL_FFROT6D
24 #ifndef DISABLE_MAL_DRWEB
27 #ifndef DISABLE_MAL_AVE
30 #ifndef DISABLE_MAL_FSECURE
33 #ifndef DISABLE_MAL_KAV
36 #ifndef DISABLE_MAL_SOPHIE
39 #ifndef DISABLE_MAL_CLAM
42 #ifndef DISABLE_MAL_MKS
45 #ifndef DISABLE_MAL_AVAST
48 #ifndef DISABLE_MAL_SOCK
51 #ifndef DISABLE_MAL_CMDLINE
56 typedef enum {MC_NONE, MC_TCP, MC_UNIX, MC_STRM} contype_t;
61 const uschar * options_default;
65 #ifndef DISABLE_MAL_FFROTD
66 { M_FPROTD, US"f-protd", US"localhost 10200-10204", MC_TCP },
68 #ifndef DISABLE_MAL_FFROT6D
69 { M_FPROT6D, US"f-prot6d", US"localhost 10200", MC_TCP },
71 #ifndef DISABLE_MAL_DRWEB
72 { M_DRWEB, US"drweb", US"/usr/local/drweb/run/drwebd.sock", MC_STRM },
74 #ifndef DISABLE_MAL_AVE
75 { M_AVES, US"aveserver", US"/var/run/aveserver", MC_UNIX },
77 #ifndef DISABLE_MAL_FSECURE
78 { M_FSEC, US"fsecure", US"/var/run/.fsav", MC_UNIX },
80 #ifndef DISABLE_MAL_KAV
81 { M_KAVD, US"kavdaemon", US"/var/run/AvpCtl", MC_UNIX },
83 #ifndef DISABLE_MAL_SOPHIE
84 { M_SOPHIE, US"sophie", US"/var/run/sophie", MC_UNIX },
86 #ifndef DISABLE_MAL_CLAM
87 { M_CLAMD, US"clamd", US"/tmp/clamd", MC_NONE },
89 #ifndef DISABLE_MAL_MKS
90 { M_MKSD, US"mksd", NULL, MC_NONE },
92 #ifndef DISABLE_MAL_AVAST
93 { M_AVAST, US"avast", US"/var/run/avast/scan.sock", MC_STRM },
95 #ifndef DISABLE_MAL_SOCK
96 { M_SOCK, US"sock", US"/tmp/malware.sock", MC_STRM },
98 #ifndef DISABLE_MAL_CMDLINE
99 { M_CMDL, US"cmdline", NULL, MC_NONE },
101 { -1, NULL, NULL, MC_NONE } /* end-marker */
104 /******************************************************************************/
105 # ifdef MACRO_PREDEF /* build solely to predefine macros */
107 # include "macro_predef.h"
110 features_malware(void)
114 uschar buf[EXIM_DRIVERNAME_MAX];
116 spf(buf, sizeof(buf), US"_HAVE_MALWARE_");
118 for (const struct scan * sc = m_scans; sc->scancode != -1; sc++)
120 for (s = sc->name, t = buf+14; *s; s++) if (*s != '-')
123 builtin_macro_create(buf);
127 /******************************************************************************/
128 # else /*!MACRO_PREDEF, main build*/
131 #define MALWARE_TIMEOUT 120 /* default timeout, seconds */
133 static const uschar * malware_regex_default = US ".+";
134 static const pcre2_code * malware_default_re = NULL;
137 #ifndef DISABLE_MAL_CLAM
138 /* The maximum number of clamd servers that are supported in the configuration */
139 # define MAX_CLAMD_SERVERS 32
140 # define MAX_CLAMD_SERVERS_S "32"
142 typedef struct clamd_address {
150 #ifndef DISABLE_MAL_DRWEB
151 # define DRWEBD_SCAN_CMD (1) /* scan file, buffer or diskfile */
152 # define DRWEBD_RETURN_VIRUSES (1<<0) /* ask daemon return to us viruses names from report */
153 # define DRWEBD_IS_MAIL (1<<19) /* say to daemon that format is "archive MAIL" */
155 # define DERR_READ_ERR (1<<0) /* read error */
156 # define DERR_NOMEMORY (1<<2) /* no memory */
157 # define DERR_TIMEOUT (1<<9) /* scan timeout has run out */
158 # define DERR_BAD_CALL (1<<15) /* wrong command */
160 static const uschar * drweb_re_str = US "infected\\swith\\s*(.+?)$";
161 static const pcre2_code * drweb_re = NULL;
164 #ifndef DISABLE_MAL_FSECURE
165 static const uschar * fsec_re_str = US "\\S{0,5}INFECTED\\t[^\\t]*\\t([^\\t]+)\\t\\S*$";
166 static const pcre2_code * fsec_re = NULL;
169 #ifndef DISABLE_MAL_KAV
170 static const uschar * kav_re_sus_str = US "suspicion:\\s*(.+?)\\s*$";
171 static const uschar * kav_re_inf_str = US "infected:\\s*(.+?)\\s*$";
172 static const pcre2_code * kav_re_sus = NULL;
173 static const pcre2_code * kav_re_inf = NULL;
176 #ifndef DISABLE_MAL_AVAST
177 static const uschar * ava_re_clean_str = US "(?!\\\\)\\t\\[\\+\\]";
178 static const uschar * ava_re_virus_str = US "(?!\\\\)\\t\\[L\\]\\d+\\.0\\t0\\s(.*)";
179 static const uschar * ava_re_error_str = US "(?!\\\\)\\t\\[E\\]\\d+\\.0\\tError\\s\\d+\\s(.*)";
180 static const pcre2_code * ava_re_clean = NULL;
181 static const pcre2_code * ava_re_virus = NULL;
182 static const pcre2_code * ava_re_error = NULL;
185 #ifndef DISABLE_MAL_FFROT6D
186 static const uschar * fprot6d_re_error_str = US "^\\d+\\s<(.+?)>$";
187 static const uschar * fprot6d_re_virus_str = US "^\\d+\\s<infected:\\s+(.+?)>\\s+.+$";
188 static const pcre2_code * fprot6d_re_error = NULL;
189 static const pcre2_code * fprot6d_re_virus = NULL;
194 /******************************************************************************/
196 #ifndef DISABLE_MAL_KAV
197 /* Routine to check whether a system is big- or little-endian.
198 Ripped from http://www.faqs.org/faqs/graphics/fileformats-faq/part4/section-7.html
199 Needed for proper kavdaemon implementation. Sigh. */
200 # define BIG_MY_ENDIAN 0
201 # define LITTLE_MY_ENDIAN 1
202 static int test_byte_order(void);
206 short int word = 0x0001;
207 char *byte = CS &word;
208 return(byte[0] ? LITTLE_MY_ENDIAN : BIG_MY_ENDIAN);
212 BOOL malware_ok = FALSE;
214 /* Gross hacks for the -bmalware option; perhaps we should just create
215 the scan directory normally for that case, but look into rigging up the
216 needed header variables if not already set on the command-line? */
217 extern int spool_mbox_ok;
218 extern uschar spooled_message_id[MESSAGE_ID_LENGTH+1];
221 /* Some (currently avast only) use backslash escaped whitespace,
222 this function undoes these escapes */
224 #ifndef DISABLE_MAL_AVAST
230 if (*p == '\\' && (isspace(p[1]) || p[1] == '\\'))
231 for (p0 = p; *p0; ++p0) *p0 = p0[1];
235 /* --- malware_*_defer --- */
237 malware_panic_defer(const uschar * str)
239 log_write(0, LOG_MAIN|LOG_PANIC, "malware acl condition: %s", str);
243 malware_log_defer(const uschar * str)
245 log_write(0, LOG_MAIN, "malware acl condition: %s", str);
248 /* --- m_*_defer --- */
250 m_panic_defer(struct scan * scanent, const uschar * hostport,
253 return malware_panic_defer(string_sprintf("%s %s : %s",
254 scanent->name, hostport ? hostport : CUS"", str));
256 /* --- m_*_defer_3 */
258 m_panic_defer_3(struct scan * scanent, const uschar * hostport,
259 const uschar * str, int fd_to_close)
261 DEBUG(D_acl) debug_print_socket(fd_to_close);
262 (void) close(fd_to_close);
263 return m_panic_defer(scanent, hostport, str);
266 /*************************************************/
268 #ifndef DISABLE_MAL_CLAM
269 /* Only used by the Clamav code, which is working from a list of servers and
270 uses the returned in_addr to get a second connection to the same system.
273 m_tcpsocket(const uschar * hostname, unsigned int port,
274 host_item * host, uschar ** errstr, const blob * fastopen_blob)
276 int fd = ip_connectedsocket(SOCK_STREAM, hostname, port, port, 5,
277 host, errstr, fastopen_blob);
278 #ifdef EXIM_TFO_FREEBSD
279 /* Under some fault conditions, FreeBSD 12.2 seen to send a (non-TFO) SYN
280 and, getting no response, wait for a long time. Impose a 5s max. */
282 (void) poll_one_fd(fd, POLLOUT, 5 * 1000);
289 m_sock_send(int sock, uschar * buf, int cnt, uschar ** errstr)
291 if (send(sock, buf, cnt, 0) < 0)
295 *errstr = string_sprintf("unable to send to socket (%s): %s",
302 static const pcre2_code *
303 m_pcre_compile(const uschar * re, BOOL cacheable, uschar ** errstr)
305 return regex_compile(re, cacheable ? MCS_CACHEABLE : MCS_NOFLAGS, errstr,
310 m_pcre_exec(const pcre2_code * cre, uschar * text)
312 pcre2_match_data * md = pcre2_match_data_create(2, pcre_gen_ctx);
313 int i = pcre2_match(cre, text, PCRE2_ZERO_TERMINATED, 0, 0, md, pcre_gen_mtc_ctx);
314 uschar * substr = NULL;
316 if (i >= 2) /* Got it */
318 PCRE2_SIZE * ovec = pcre2_get_ovector_pointer(md);
319 int len = ovec[3] - ovec[2];
320 substr = string_copyn(text + ovec[2], len);
322 /* pcre2_match_data_free(md); gen ctx needs no free */
326 static const pcre2_code *
327 m_pcre_nextinlist(const uschar ** list, int * sep,
328 BOOL cacheable, char * listerr, uschar ** errstr)
330 const uschar * list_ele;
331 const pcre2_code * cre = NULL;
333 if (!(list_ele = string_nextinlist(list, sep, NULL, 0)))
334 *errstr = US listerr;
337 DEBUG(D_acl) debug_printf_indent("%15s%10s'%s'\n", "", "RE: ",
338 string_printing(list_ele));
339 cre = m_pcre_compile(CUS list_ele, cacheable, errstr);
346 Simple though inefficient wrapper for reading a line. Drop CRs and the
347 trailing newline. Can return early on buffer full. Null-terminate.
348 Apply initial timeout if no data ready.
350 Return: number of chars - zero for an empty line
352 -2 on timeout or error
355 recv_line(int fd, uschar * buffer, int bsize, time_t tmo)
361 if (!fd_ready(fd, tmo))
364 /*XXX tmo handling assumes we always get a whole line */
367 while ((rcv = read(fd, p, 1)) > 0)
370 if (p-buffer > bsize-2) break;
371 if (*p == '\n') break;
378 debug_printf_indent("Malware scan: read %s (%s)\n",
379 rcv==0 ? "EOF" : "error", strerror(errno));
380 debug_print_socket(fd);
382 return rcv==0 ? -1 : -2;
386 DEBUG(D_acl) debug_printf_indent("Malware scan: read '%s'\n", buffer);
390 /* return TRUE iff size as requested */
391 #ifndef DISABLE_MAL_DRWEB
393 recv_len(int sock, void * buf, int size, time_t tmo)
395 return fd_ready(sock, tmo)
396 ? recv(sock, buf, size, 0) == size
403 #ifndef DISABLE_MAL_MKS
404 /* ============= private routines for the "mksd" scanner type ============== */
406 # include <sys/uio.h>
409 mksd_writev (int sock, struct iovec * iov, int iovcnt)
416 i = writev (sock, iov, iovcnt);
417 while (i < 0 && errno == EINTR);
420 (void) malware_panic_defer(
421 US"unable to write to mksd UNIX socket (/var/run/mksd/socket)");
424 for (;;) /* check for short write */
425 if (i >= iov->iov_len)
435 iov->iov_base = CS iov->iov_base + i;
442 mksd_read_lines (int sock, uschar *av_buffer, int av_buffer_size, time_t tmo)
444 client_conn_ctx cctx = {.sock = sock};
450 i = ip_recv(&cctx, av_buffer+offset, av_buffer_size-offset, tmo);
453 (void) malware_panic_defer(US"unable to read from mksd UNIX socket (/var/run/mksd/socket)");
458 /* offset == av_buffer_size -> buffer full */
459 if (offset == av_buffer_size)
461 (void) malware_panic_defer(US"malformed reply received from mksd");
464 } while (av_buffer[offset-1] != '\n');
466 av_buffer[offset] = '\0';
471 mksd_parse_line(struct scan * scanent, char * line)
482 if ((p = strchr (line, '\n')) != NULL)
484 return m_panic_defer(scanent, NULL,
485 string_sprintf("scanner failed: %s", line));
488 if ((p = strchr (line, '\n')) != NULL)
493 && (p = strchr(line+4, ' ')) != NULL
498 malware_name = string_copy(US line+4);
502 return m_panic_defer(scanent, NULL,
503 string_sprintf("malformed reply received: %s", line));
508 mksd_scan_packed(struct scan * scanent, int sock, const uschar * scan_filename,
512 const char *cmd = "MSQ\n";
513 uschar av_buffer[1024];
515 iov[0].iov_base = (void *) cmd;
517 iov[1].iov_base = (void *) scan_filename;
518 iov[1].iov_len = Ustrlen(scan_filename);
519 iov[2].iov_base = (void *) (cmd + 3);
522 if (mksd_writev (sock, iov, 3) < 0)
525 if (mksd_read_lines (sock, av_buffer, sizeof (av_buffer), tmo) < 0)
528 return mksd_parse_line (scanent, CS av_buffer);
533 #ifndef DISABLE_MAL_CLAM
535 clamd_option(clamd_address * cd, const uschar * optstr, int * subsep)
540 while ((s = string_nextinlist(&optstr, subsep, NULL, 0)))
541 if (Ustrncmp(s, "retry=", 6) == 0)
543 int sec = readconf_readtime((s += 6), '\0', FALSE);
556 /*************************************************
557 * Scan content for malware *
558 *************************************************/
560 /* This is an internal interface for scanning an email; the normal interface
561 is via malware(), or there's malware_in_file() used for testing/debugging.
564 malware_re match condition for "malware="
565 cacheable the RE did not use any dynamic elements during expansion
566 scan_filename the file holding the email to be scanned, if we're faking
567 this up for the -bmalware test, else NULL
568 timeout if nonzero, non-default timeoutl
570 Returns: Exim message processing code (OK, FAIL, DEFER, ...)
571 where true means malware was found (condition applies)
574 malware_internal(const uschar * malware_re, BOOL cacheable,
575 const uschar * scan_filename, int timeout)
578 const uschar *av_scanner_work = av_scanner;
579 BOOL av_scanner_textonly;
580 uschar *scanner_name;
581 unsigned long mbox_size;
583 const pcre2_code *re;
585 struct scan * scanent;
586 const uschar * scanner_options;
587 client_conn_ctx malware_daemon_ctx = {.sock = -1};
589 uschar * eml_filename, * eml_dir;
592 return FAIL; /* empty means "don't match anything" */
594 /* Ensure the eml mbox file is spooled up */
596 if (!(mbox_file = spool_mbox(&mbox_size, scan_filename, &eml_filename)))
597 return malware_panic_defer(US"error while creating mbox spool file");
599 /* None of our current scanners need the mbox file as a stream (they use
600 the name), so we can close it right away. Get the directory too. */
602 (void) fclose(mbox_file);
603 eml_dir = string_copyn(eml_filename, Ustrrchr(eml_filename, '/') - eml_filename);
605 /* parse 1st option */
606 if (strcmpic(malware_re, US"false") == 0 || Ustrcmp(malware_re, "0") == 0)
607 return FAIL; /* explicitly no matching */
609 /* special cases (match anything except empty) */
610 if ( strcmpic(malware_re, US"true") == 0
611 || Ustrcmp(malware_re, "*") == 0
612 || Ustrcmp(malware_re, "1") == 0
615 if ( !malware_default_re
616 && !(malware_default_re = m_pcre_compile(malware_regex_default, FALSE, &errstr)))
617 return malware_panic_defer(errstr);
618 malware_re = malware_regex_default;
619 re = malware_default_re;
622 /* compile the regex, see if it works */
623 else if (!(re = m_pcre_compile(malware_re, cacheable, &errstr)))
624 return malware_panic_defer(errstr);
626 /* if av_scanner starts with a dollar, expand it first */
627 if (*av_scanner == '$')
629 if (!(av_scanner_work = expand_string_2(av_scanner, &av_scanner_textonly)))
630 return malware_panic_defer(
631 string_sprintf("av_scanner starts with $, but expansion failed: %s",
632 expand_string_message));
635 debug_printf_indent("Expanded av_scanner global: %s\n", av_scanner_work);
636 /* disable result caching in this case */
641 av_scanner_textonly = TRUE;
643 /* Do not scan twice (unless av_scanner is dynamic). */
646 /* find the scanner type from the av_scanner option */
647 if (!(scanner_name = string_nextinlist(&av_scanner_work, &sep, NULL, 0)))
648 return malware_panic_defer(US"av_scanner configuration variable is empty");
649 if (!timeout) timeout = MALWARE_TIMEOUT;
650 tmo = time(NULL) + timeout;
652 for (scanent = m_scans; ; scanent++)
655 return malware_panic_defer(string_sprintf("unknown scanner type '%s'",
657 if (strcmpic(scanner_name, US scanent->name) != 0)
659 DEBUG(D_acl) debug_printf_indent("Malware scan: %s tmo=%s\n",
660 scanner_name, readconf_printtime(timeout));
662 if (!(scanner_options = string_nextinlist(&av_scanner_work, &sep, NULL, 0)))
663 scanner_options = scanent->options_default;
664 if (scanent->conn == MC_NONE)
667 DEBUG(D_acl) debug_printf_indent("%15s%10s%s\n", "", "socket: ", scanner_options);
668 switch(scanent->conn)
671 malware_daemon_ctx.sock = ip_tcpsocket(scanner_options, &errstr, 5, NULL); break;
673 malware_daemon_ctx.sock = ip_unixsocket(scanner_options, &errstr); break;
675 malware_daemon_ctx.sock = ip_streamsocket(scanner_options, &errstr, 5, NULL); break;
677 /* compiler quietening */ break;
679 if (malware_daemon_ctx.sock < 0)
680 return m_panic_defer(scanent, CUS callout_address, errstr);
684 switch (scanent->scancode)
686 #ifndef DISABLE_MAL_FFROTD
687 case M_FPROTD: /* "f-protd" scanner type -------------------------------- */
689 uschar *fp_scan_option;
690 unsigned int detected=0, par_count=0;
691 uschar * scanrequest;
692 uschar buf[32768], *strhelper, *strhelper2;
693 uschar * malware_name_internal = NULL;
696 scanrequest = string_sprintf("GET %s", eml_filename);
698 while ((fp_scan_option = string_nextinlist(&av_scanner_work, &sep,
701 scanrequest = string_sprintf("%s%s%s", scanrequest,
702 par_count ? "%20" : "?", fp_scan_option);
705 scanrequest = string_sprintf("%s HTTP/1.0\r\n\r\n", scanrequest);
706 DEBUG(D_acl) debug_printf_indent("Malware scan: issuing %s: %s\n",
707 scanner_name, scanrequest);
709 /* send scan request */
710 if (m_sock_send(malware_daemon_ctx.sock, scanrequest, Ustrlen(scanrequest)+1, &errstr) < 0)
711 return m_panic_defer(scanent, CUS callout_address, errstr);
713 while ((len = recv_line(malware_daemon_ctx.sock, buf, sizeof(buf), tmo)) >= 0)
716 if (Ustrstr(buf, US"<detected type=\"") != NULL)
718 else if (detected && (strhelper = Ustrstr(buf, US"<name>")))
720 if ((strhelper2 = Ustrstr(buf, US"</name>")) != NULL)
723 malware_name_internal = string_copy(strhelper+6);
726 else if (Ustrstr(buf, US"<summary code=\""))
728 malware_name = Ustrstr(buf, US"<summary code=\"11\">")
729 ? malware_name_internal : NULL;
735 (void)close(malware_daemon_ctx.sock);
742 #ifndef DISABLE_MAL_FFROT6D
743 case M_FPROT6D: /* "f-prot6d" scanner type ----------------------------------- */
746 uschar * e, * linebuffer, * scanrequest;
747 uschar av_buffer[1024];
749 if ((!fprot6d_re_virus && !(fprot6d_re_virus = m_pcre_compile(fprot6d_re_virus_str, FALSE, &errstr)))
750 || (!fprot6d_re_error && !(fprot6d_re_error = m_pcre_compile(fprot6d_re_error_str, FALSE, &errstr))))
751 return malware_panic_defer(errstr);
753 scanrequest = string_sprintf("SCAN FILE %s\n", eml_filename);
754 DEBUG(D_acl) debug_printf_indent("Malware scan: issuing %s: %s\n",
755 scanner_name, scanrequest);
757 if (m_sock_send(malware_daemon_ctx.sock, scanrequest, Ustrlen(scanrequest), &errstr) < 0)
758 return m_panic_defer(scanent, CUS callout_address, errstr);
760 bread = ip_recv(&malware_daemon_ctx, av_buffer, sizeof(av_buffer), tmo);
763 return m_panic_defer_3(scanent, CUS callout_address,
764 string_sprintf("unable to read from socket (%s)", strerror(errno)),
765 malware_daemon_ctx.sock);
767 if (bread == sizeof(av_buffer))
768 return m_panic_defer_3(scanent, CUS callout_address,
769 US"buffer too small", malware_daemon_ctx.sock);
771 av_buffer[bread] = '\0';
772 linebuffer = string_copy(av_buffer);
774 m_sock_send(malware_daemon_ctx.sock, US"QUIT\n", 5, 0);
776 if ((e = m_pcre_exec(fprot6d_re_error, linebuffer)))
777 return m_panic_defer_3(scanent, CUS callout_address,
778 string_sprintf("scanner reported error (%s)", e), malware_daemon_ctx.sock);
780 if (!(malware_name = m_pcre_exec(fprot6d_re_virus, linebuffer)))
787 #ifndef DISABLE_MAL_DRWEB
788 case M_DRWEB: /* "drweb" scanner type ----------------------------------- */
789 /* v0.1 - added support for tcp sockets */
790 /* v0.0 - initial release -- support for unix sockets */
794 unsigned int fsize_uint;
795 uschar * tmpbuf, *drweb_fbuf;
796 int drweb_rc, drweb_cmd, drweb_flags = 0x0000, drweb_fd,
797 drweb_vnum, drweb_slen, drweb_fin = 0x0000;
799 /* prepare variables */
800 drweb_cmd = htonl(DRWEBD_SCAN_CMD);
801 drweb_flags = htonl(DRWEBD_RETURN_VIRUSES | DRWEBD_IS_MAIL);
803 if (*scanner_options != '/')
806 if ((drweb_fd = exim_open2(CCS eml_filename, O_RDONLY)) == -1)
807 return m_panic_defer_3(scanent, NULL,
808 string_sprintf("can't open spool file %s: %s",
809 eml_filename, strerror(errno)),
810 malware_daemon_ctx.sock);
812 if ((fsize = lseek(drweb_fd, 0, SEEK_END)) == -1)
815 badseek: err = errno;
816 (void)close(drweb_fd);
817 return m_panic_defer_3(scanent, NULL,
818 string_sprintf("can't seek spool file %s: %s",
819 eml_filename, strerror(err)),
820 malware_daemon_ctx.sock);
822 fsize_uint = (unsigned int) fsize;
823 if ((off_t)fsize_uint != fsize)
825 (void)close(drweb_fd);
826 return m_panic_defer_3(scanent, NULL,
827 string_sprintf("seeking spool file %s, size overflow",
829 malware_daemon_ctx.sock);
831 drweb_slen = htonl(fsize);
832 if (lseek(drweb_fd, 0, SEEK_SET) < 0)
835 DEBUG(D_acl) debug_printf_indent("Malware scan: issuing %s remote scan [%s]\n",
836 scanner_name, scanner_options);
838 /* send scan request */
839 if ((send(malware_daemon_ctx.sock, &drweb_cmd, sizeof(drweb_cmd), 0) < 0) ||
840 (send(malware_daemon_ctx.sock, &drweb_flags, sizeof(drweb_flags), 0) < 0) ||
841 (send(malware_daemon_ctx.sock, &drweb_fin, sizeof(drweb_fin), 0) < 0) ||
842 (send(malware_daemon_ctx.sock, &drweb_slen, sizeof(drweb_slen), 0) < 0))
844 (void)close(drweb_fd);
845 return m_panic_defer_3(scanent, CUS callout_address, string_sprintf(
846 "unable to send commands to socket (%s)", scanner_options),
847 malware_daemon_ctx.sock);
850 if (!(drweb_fbuf = store_malloc(fsize_uint)))
852 (void)close(drweb_fd);
853 return m_panic_defer_3(scanent, NULL,
854 string_sprintf("unable to allocate memory %u for file (%s)",
855 fsize_uint, eml_filename),
856 malware_daemon_ctx.sock);
859 if ((result = read (drweb_fd, drweb_fbuf, fsize)) == -1)
862 (void)close(drweb_fd);
863 store_free(drweb_fbuf);
864 return m_panic_defer_3(scanent, NULL,
865 string_sprintf("can't read spool file %s: %s",
866 eml_filename, strerror(err)),
867 malware_daemon_ctx.sock);
869 (void)close(drweb_fd);
871 /* send file body to socket */
872 if (send(malware_daemon_ctx.sock, drweb_fbuf, fsize, 0) < 0)
874 store_free(drweb_fbuf);
875 return m_panic_defer_3(scanent, CUS callout_address, string_sprintf(
876 "unable to send file body to socket (%s)", scanner_options),
877 malware_daemon_ctx.sock);
879 store_free(drweb_fbuf);
883 drweb_slen = htonl(Ustrlen(eml_filename));
885 DEBUG(D_acl) debug_printf_indent("Malware scan: issuing %s local scan [%s]\n",
886 scanner_name, scanner_options);
888 /* send scan request */
889 if ((send(malware_daemon_ctx.sock, &drweb_cmd, sizeof(drweb_cmd), 0) < 0) ||
890 (send(malware_daemon_ctx.sock, &drweb_flags, sizeof(drweb_flags), 0) < 0) ||
891 (send(malware_daemon_ctx.sock, &drweb_slen, sizeof(drweb_slen), 0) < 0) ||
892 (send(malware_daemon_ctx.sock, eml_filename, Ustrlen(eml_filename), 0) < 0) ||
893 (send(malware_daemon_ctx.sock, &drweb_fin, sizeof(drweb_fin), 0) < 0))
894 return m_panic_defer_3(scanent, CUS callout_address, string_sprintf(
895 "unable to send commands to socket (%s)", scanner_options),
896 malware_daemon_ctx.sock);
899 /* wait for result */
900 if (!recv_len(malware_daemon_ctx.sock, &drweb_rc, sizeof(drweb_rc), tmo))
901 return m_panic_defer_3(scanent, CUS callout_address,
902 US"unable to read return code", malware_daemon_ctx.sock);
903 drweb_rc = ntohl(drweb_rc);
905 if (!recv_len(malware_daemon_ctx.sock, &drweb_vnum, sizeof(drweb_vnum), tmo))
906 return m_panic_defer_3(scanent, CUS callout_address,
907 US"unable to read the number of viruses", malware_daemon_ctx.sock);
908 drweb_vnum = ntohl(drweb_vnum);
910 /* "virus(es) found" if virus number is > 0 */
915 /* setup default virus name */
916 malware_name = US"unknown";
918 /* set up match regex */
920 drweb_re = m_pcre_compile(drweb_re_str, FALSE, &errstr);
922 /* read and concatenate virus names into one string */
923 for (int i = 0; i < drweb_vnum; i++)
925 pcre2_match_data * md = pcre2_match_data_create(2, pcre_gen_ctx);
927 /* read the size of report */
928 if (!recv_len(malware_daemon_ctx.sock, &drweb_slen, sizeof(drweb_slen), tmo))
929 return m_panic_defer_3(scanent, CUS callout_address,
930 US"cannot read report size", malware_daemon_ctx.sock);
931 drweb_slen = ntohl(drweb_slen);
933 /* assume tainted, since it is external input */
934 tmpbuf = store_get(drweb_slen, GET_TAINTED);
936 /* read report body */
937 if (!recv_len(malware_daemon_ctx.sock, tmpbuf, drweb_slen, tmo))
938 return m_panic_defer_3(scanent, CUS callout_address,
939 US"cannot read report string", malware_daemon_ctx.sock);
940 tmpbuf[drweb_slen] = '\0';
942 /* try matcher on the line, grab substring */
943 result = pcre2_match(drweb_re, (PCRE2_SPTR)tmpbuf, PCRE2_ZERO_TERMINATED,
944 0, 0, md, pcre_gen_mtc_ctx);
947 PCRE2_SIZE * ovec = pcre2_get_ovector_pointer(md);
949 if (i==0) /* the first name we just copy to malware_name */
950 g = string_catn(NULL, US ovec[2], ovec[3] - ovec[2]);
952 else /* concatenate each new virus name to previous */
954 g = string_catn(g, US"/", 1);
955 g = string_catn(g, US ovec[2], ovec[3] - ovec[2]);
958 /* pcre2_match_data_free(md); gen ctx needs no free */
960 malware_name = string_from_gstring(g);
964 const char *drweb_s = NULL;
966 if (drweb_rc & DERR_READ_ERR) drweb_s = "read error";
967 if (drweb_rc & DERR_NOMEMORY) drweb_s = "no memory";
968 if (drweb_rc & DERR_TIMEOUT) drweb_s = "timeout";
969 if (drweb_rc & DERR_BAD_CALL) drweb_s = "wrong command";
970 /* retcodes DERR_SYMLINK, DERR_NO_REGFILE, DERR_SKIPPED.
971 * DERR_TOO_BIG, DERR_TOO_COMPRESSED, DERR_SPAM,
972 * DERR_CRC_ERROR, DERR_READSOCKET, DERR_WRITE_ERR
973 * and others are ignored */
975 return m_panic_defer_3(scanent, CUS callout_address,
976 string_sprintf("drweb daemon retcode 0x%x (%s)", drweb_rc, drweb_s),
977 malware_daemon_ctx.sock);
986 #ifndef DISABLE_MAL_AVE
987 case M_AVES: /* "aveserver" scanner type -------------------------------- */
992 /* read aveserver's greeting and see if it is ready (2xx greeting) */
994 recv_line(malware_daemon_ctx.sock, buf, sizeof(buf), tmo);
996 if (buf[0] != '2') /* aveserver is having problems */
997 return m_panic_defer_3(scanent, CUS callout_address,
998 string_sprintf("unavailable (Responded: %s).",
999 ((buf[0] != 0) ? buf : US "nothing") ),
1000 malware_daemon_ctx.sock);
1002 /* prepare our command */
1003 (void)string_format(buf, sizeof(buf), "SCAN bPQRSTUW %s\r\n",
1007 DEBUG(D_acl) debug_printf_indent("Malware scan: issuing %s %s\n",
1009 if (m_sock_send(malware_daemon_ctx.sock, buf, Ustrlen(buf), &errstr) < 0)
1010 return m_panic_defer(scanent, CUS callout_address, errstr);
1012 malware_name = NULL;
1014 /* read response lines, find malware name and final response */
1015 while (recv_line(malware_daemon_ctx.sock, buf, sizeof(buf), tmo) > 0)
1019 if (buf[0] == '5') /* aveserver is having problems */
1021 result = m_panic_defer(scanent, CUS callout_address,
1022 string_sprintf("unable to scan file %s (Responded: %s).",
1023 eml_filename, buf));
1026 if (Ustrncmp(buf,"322",3) == 0)
1028 uschar *p = Ustrchr(&buf[4], ' ');
1030 malware_name = string_copy(&buf[4]);
1034 if (m_sock_send(malware_daemon_ctx.sock, US"quit\r\n", 6, &errstr) < 0)
1035 return m_panic_defer(scanent, CUS callout_address, errstr);
1037 /* read aveserver's greeting and see if it is ready (2xx greeting) */
1039 recv_line(malware_daemon_ctx.sock, buf, sizeof(buf), tmo);
1041 if (buf[0] != '2') /* aveserver is having problems */
1042 return m_panic_defer_3(scanent, CUS callout_address,
1043 string_sprintf("unable to quit dialogue (Responded: %s).",
1044 ((buf[0] != 0) ? buf : US "nothing") ),
1045 malware_daemon_ctx.sock);
1047 if (result == DEFER)
1049 (void)close(malware_daemon_ctx.sock);
1056 #ifndef DISABLE_MAL_FSECURE
1057 case M_FSEC: /* "fsecure" scanner type ---------------------------------- */
1061 uschar av_buffer[1024];
1062 static uschar *cmdopt[] = { US"CONFIGURE\tARCHIVE\t1\n",
1063 US"CONFIGURE\tTIMEOUT\t0\n",
1064 US"CONFIGURE\tMAXARCH\t5\n",
1065 US"CONFIGURE\tMIME\t1\n" };
1067 malware_name = NULL;
1069 DEBUG(D_acl) debug_printf_indent("Malware scan: issuing %s scan [%s]\n",
1070 scanner_name, scanner_options);
1072 memset(av_buffer, 0, sizeof(av_buffer));
1073 for (i = 0; i != nelem(cmdopt); i++)
1076 if (m_sock_send(malware_daemon_ctx.sock, cmdopt[i], Ustrlen(cmdopt[i]), &errstr) < 0)
1077 return m_panic_defer(scanent, CUS callout_address, errstr);
1079 bread = ip_recv(&malware_daemon_ctx, av_buffer, sizeof(av_buffer), tmo);
1080 if (bread > 0) av_buffer[bread]='\0';
1082 return m_panic_defer_3(scanent, CUS callout_address,
1083 string_sprintf("unable to read answer %d (%s)", i, strerror(errno)),
1084 malware_daemon_ctx.sock);
1085 for (int j = 0; j < bread; j++)
1086 if (av_buffer[j] == '\r' || av_buffer[j] == '\n')
1090 /* pass the mailfile to fsecure */
1091 file_name = string_sprintf("SCAN\t%s\n", eml_filename);
1093 if (m_sock_send(malware_daemon_ctx.sock, file_name, Ustrlen(file_name), &errstr) < 0)
1094 return m_panic_defer(scanent, CUS callout_address, errstr);
1097 /* todo also SUSPICION\t */
1099 fsec_re = m_pcre_compile(fsec_re_str, FALSE, &errstr);
1101 /* read report, linewise. Apply a timeout as the Fsecure daemon
1102 sometimes wants an answer to "PING" but they won't tell us what */
1104 uschar * p = av_buffer;
1110 i = av_buffer+sizeof(av_buffer)-p;
1111 if ((bread= ip_recv(&malware_daemon_ctx, p, i-1, tmo)) < 0)
1112 return m_panic_defer_3(scanent, CUS callout_address,
1113 string_sprintf("unable to read result (%s)", strerror(errno)),
1114 malware_daemon_ctx.sock);
1116 for (p[bread] = '\0'; (q = Ustrchr(p, '\n')); p = q+1)
1120 /* Really search for virus again? */
1122 /* try matcher on the line, grab substring */
1123 malware_name = m_pcre_exec(fsec_re, p);
1125 if (Ustrstr(p, "OK\tScan ok."))
1129 /* copy down the trailing partial line then read another chunk */
1130 i = av_buffer+sizeof(av_buffer)-p;
1131 memmove(av_buffer, p, i);
1141 #ifndef DISABLE_MAL_KAV
1142 case M_KAVD: /* "kavdaemon" scanner type -------------------------------- */
1145 uschar tmpbuf[1024];
1146 uschar * scanrequest;
1148 unsigned long kav_reportlen;
1150 const pcre2_code *kav_re;
1153 /* get current date and time, build scan request */
1155 /* pdp note: before the eml_filename parameter, this scanned the
1156 directory; not finding documentation, so we'll strip off the directory.
1157 The side-effect is that the test framework scanning may end up in
1158 scanning more than was requested, but for the normal interface, this is
1161 strftime(CS tmpbuf, sizeof(tmpbuf), "%d %b %H:%M:%S", localtime(&t));
1162 scanrequest = string_sprintf("<0>%s:%s", CS tmpbuf, eml_filename);
1163 p = Ustrrchr(scanrequest, '/');
1167 DEBUG(D_acl) debug_printf_indent("Malware scan: issuing %s scan [%s]\n",
1168 scanner_name, scanner_options);
1170 /* send scan request */
1171 if (m_sock_send(malware_daemon_ctx.sock, scanrequest, Ustrlen(scanrequest)+1, &errstr) < 0)
1172 return m_panic_defer(scanent, CUS callout_address, errstr);
1174 /* wait for result */
1175 if (!recv_len(malware_daemon_ctx.sock, tmpbuf, 2, tmo))
1176 return m_panic_defer_3(scanent, CUS callout_address,
1177 US"unable to read 2 bytes from socket.", malware_daemon_ctx.sock);
1179 /* get errorcode from one nibble */
1180 kav_rc = tmpbuf[ test_byte_order()==LITTLE_MY_ENDIAN ? 0 : 1 ] & 0x0F;
1183 case 5: case 6: /* improper kavdaemon configuration */
1184 return m_panic_defer_3(scanent, CUS callout_address,
1185 US"please reconfigure kavdaemon to NOT disinfect or remove infected files.",
1186 malware_daemon_ctx.sock);
1188 return m_panic_defer_3(scanent, CUS callout_address,
1189 US"reported 'scanning not completed' (code 1).", malware_daemon_ctx.sock);
1191 return m_panic_defer_3(scanent, CUS callout_address,
1192 US"reported 'kavdaemon damaged' (code 7).", malware_daemon_ctx.sock);
1195 /* code 8 is not handled, since it is ambiguous. It appears mostly on
1196 bounces where part of a file has been cut off */
1198 /* "virus found" return codes (2-4) */
1199 if (kav_rc > 1 && kav_rc < 5)
1201 int report_flag = 0;
1203 /* setup default virus name */
1204 malware_name = US"unknown";
1206 report_flag = tmpbuf[ test_byte_order() == LITTLE_MY_ENDIAN ? 1 : 0 ];
1208 /* read the report, if available */
1209 if (report_flag == 1)
1211 /* read report size */
1212 if (!recv_len(malware_daemon_ctx.sock, &kav_reportlen, 4, tmo))
1213 return m_panic_defer_3(scanent, CUS callout_address,
1214 US"cannot read report size", malware_daemon_ctx.sock);
1216 /* it's possible that avp returns av_buffer[1] == 1 but the
1217 reportsize is 0 (!?) */
1218 if (kav_reportlen > 0)
1220 /* set up match regex, depends on retcode */
1223 if (!kav_re_sus) kav_re_sus = m_pcre_compile(kav_re_sus_str, FALSE, &errstr);
1224 kav_re = kav_re_sus;
1228 if (!kav_re_inf) kav_re_inf = m_pcre_compile(kav_re_inf_str, FALSE, &errstr);
1229 kav_re = kav_re_inf;
1232 /* read report, linewise. Using size from stream to read amount of data
1233 from same stream is safe enough. */
1234 /* coverity[tainted_data] */
1235 while (kav_reportlen > 0)
1237 if ((bread = recv_line(malware_daemon_ctx.sock, tmpbuf, sizeof(tmpbuf), tmo)) < 0)
1239 kav_reportlen -= bread+1;
1241 /* try matcher on the line, grab substring */
1242 if ((malware_name = m_pcre_exec(kav_re, tmpbuf)))
1248 else /* no virus found */
1249 malware_name = NULL;
1255 #ifndef DISABLE_MAL_CMDLINE
1256 case M_CMDL: /* "cmdline" scanner type ---------------------------------- */
1258 const uschar *cmdline_scanner = scanner_options;
1259 const pcre2_code *cmdline_trigger_re;
1260 const pcre2_code *cmdline_regex_re;
1262 uschar * commandline;
1263 void (*eximsigchld)(int);
1264 void (*eximsigpipe)(int);
1265 FILE *scanner_out = NULL;
1267 FILE *scanner_record = NULL;
1268 uschar linebuffer[32767];
1273 if (!cmdline_scanner)
1274 return m_panic_defer(scanent, NULL, errstr);
1276 /* find scanner output trigger */
1277 cmdline_trigger_re = m_pcre_nextinlist(&av_scanner_work, &sep, av_scanner_textonly,
1278 "missing trigger specification", &errstr);
1279 if (!cmdline_trigger_re)
1280 return m_panic_defer(scanent, NULL, errstr);
1282 /* find scanner name regex */
1283 cmdline_regex_re = m_pcre_nextinlist(&av_scanner_work, &sep, av_scanner_textonly,
1284 "missing virus name regex specification", &errstr);
1285 if (!cmdline_regex_re)
1286 return m_panic_defer(scanent, NULL, errstr);
1288 /* prepare scanner call; despite the naming, file_name holds a directory
1289 name which is documented as the value given to %s. */
1291 file_name = string_copy(eml_filename);
1292 p = Ustrrchr(file_name, '/');
1295 commandline = string_sprintf(CS cmdline_scanner, file_name);
1297 /* redirect STDERR too */
1298 commandline = string_sprintf("%s 2>&1", commandline);
1300 DEBUG(D_acl) debug_printf_indent("Malware scan: issuing %s scan [%s]\n",
1301 scanner_name, commandline);
1303 /* store exims signal handlers */
1304 eximsigchld = signal(SIGCHLD,SIG_DFL);
1305 eximsigpipe = signal(SIGPIPE,SIG_DFL);
1307 if (!(scanner_out = popen(CS commandline,"r")))
1310 signal(SIGCHLD,eximsigchld); signal(SIGPIPE,eximsigpipe);
1311 return m_panic_defer(scanent, NULL,
1312 string_sprintf("call (%s) failed: %s.", commandline, strerror(err)));
1314 scanner_fd = fileno(scanner_out);
1316 file_name = string_sprintf("%s/%s_scanner_output", eml_dir, message_id);
1318 if (!(scanner_record = modefopen(file_name, "wb", SPOOL_MODE)))
1321 (void) pclose(scanner_out);
1322 signal(SIGCHLD,eximsigchld); signal(SIGPIPE,eximsigpipe);
1323 return m_panic_defer(scanent, NULL, string_sprintf(
1324 "opening scanner output file (%s) failed: %s.",
1325 file_name, strerror(err)));
1328 /* look for trigger while recording output */
1329 while ((rcnt = recv_line(scanner_fd, linebuffer,
1330 sizeof(linebuffer), tmo)))
1337 (void) pclose(scanner_out);
1338 signal(SIGCHLD,eximsigchld); signal(SIGPIPE,eximsigpipe);
1339 return m_panic_defer(scanent, NULL, string_sprintf(
1340 "unable to read from scanner (%s): %s",
1341 commandline, strerror(err)));
1344 if (Ustrlen(linebuffer) > fwrite(linebuffer, 1, Ustrlen(linebuffer), scanner_record))
1347 (void) pclose(scanner_out);
1348 signal(SIGCHLD,eximsigchld); signal(SIGPIPE,eximsigpipe);
1349 return m_panic_defer(scanent, NULL, string_sprintf(
1350 "short write on scanner output file (%s).", file_name));
1352 putc('\n', scanner_record);
1353 /* try trigger match */
1355 && regex_match_and_setup(cmdline_trigger_re, linebuffer, 0, -1)
1360 (void)fclose(scanner_record);
1361 sep = pclose(scanner_out);
1362 signal(SIGCHLD,eximsigchld); signal(SIGPIPE,eximsigpipe);
1364 return m_panic_defer(scanent, NULL,
1366 ? string_sprintf("running scanner failed: %s", strerror(sep))
1367 : string_sprintf("scanner returned error code: %d", sep));
1372 /* setup default virus name */
1373 malware_name = US"unknown";
1375 /* re-open the scanner output file, look for name match */
1376 scanner_record = Ufopen(file_name, "rb");
1377 while (Ufgets(linebuffer, sizeof(linebuffer), scanner_record))
1378 if ((s = m_pcre_exec(cmdline_regex_re, linebuffer))) /* try match */
1380 (void)fclose(scanner_record);
1382 else /* no virus found */
1383 malware_name = NULL;
1388 #ifndef DISABLE_MAL_SOPHIE
1389 case M_SOPHIE: /* "sophie" scanner type --------------------------------- */
1394 uschar av_buffer[1024];
1396 /* pass the scan directory to sophie */
1397 file_name = string_copy(eml_filename);
1398 if ((p = Ustrrchr(file_name, '/')))
1401 DEBUG(D_acl) debug_printf_indent("Malware scan: issuing %s scan [%s]\n",
1402 scanner_name, scanner_options);
1404 if ( write(malware_daemon_ctx.sock, file_name, Ustrlen(file_name)) < 0
1405 || write(malware_daemon_ctx.sock, "\n", 1) != 1
1407 return m_panic_defer_3(scanent, CUS callout_address,
1408 string_sprintf("unable to write to UNIX socket (%s)", scanner_options),
1409 malware_daemon_ctx.sock);
1411 /* wait for result */
1412 memset(av_buffer, 0, sizeof(av_buffer));
1413 if ((bread = ip_recv(&malware_daemon_ctx, av_buffer, sizeof(av_buffer), tmo)) <= 0)
1414 return m_panic_defer_3(scanent, CUS callout_address,
1415 string_sprintf("unable to read from UNIX socket (%s)", scanner_options),
1416 malware_daemon_ctx.sock);
1419 if (av_buffer[0] == '1') {
1420 uschar * s = Ustrchr(av_buffer, '\n');
1423 malware_name = string_copy(&av_buffer[2]);
1425 else if (!strncmp(CS av_buffer, "-1", 2))
1426 return m_panic_defer_3(scanent, CUS callout_address,
1427 US"scanner reported error", malware_daemon_ctx.sock);
1428 else /* all ok, no virus */
1429 malware_name = NULL;
1435 #ifndef DISABLE_MAL_CLAM
1436 case M_CLAMD: /* "clamd" scanner type ----------------------------------- */
1438 /* This code was originally contributed by David Saez */
1439 /* There are three scanning methods available to us:
1440 * (1) Use the SCAN command, pointing to a file in the filesystem
1441 * (2) Use the STREAM command, send the data on a separate port
1442 * (3) Use the zINSTREAM command, send the data inline
1443 * The zINSTREAM command was introduced with ClamAV 0.95, which marked
1444 * STREAM deprecated; see: http://wiki.clamav.net/bin/view/Main/UpgradeNotes095
1445 * In Exim, we use SCAN if using a Unix-domain socket or explicitly told that
1446 * the TCP-connected daemon is actually local; otherwise we use zINSTREAM
1447 * See Exim bug 926 for details. */
1449 uschar *p, *vname, *result_tag;
1451 uschar av_buffer[1024];
1452 uschar *hostname = US"";
1455 unsigned int fsize_uint;
1456 BOOL use_scan_command = FALSE;
1457 clamd_address * cv[MAX_CLAMD_SERVERS];
1458 int num_servers = 0;
1459 uint32_t send_size, send_final_zeroblock;
1462 /*XXX if unixdomain socket, only one server supported. Needs fixing;
1463 there's no reason we should not mix local and remote servers */
1465 if (*scanner_options == '/')
1468 const uschar * sublist;
1471 /* Local file; so we def want to use_scan_command and don't want to try
1472 passing IP/port combinations */
1473 use_scan_command = TRUE;
1474 cd = (clamd_address *) store_get(sizeof(clamd_address), GET_UNTAINTED);
1476 /* extract socket-path part */
1477 sublist = scanner_options;
1478 cd->hostspec = string_nextinlist(&sublist, &subsep, NULL, 0);
1481 if (clamd_option(cd, sublist, &subsep) != OK)
1482 return m_panic_defer(scanent, NULL,
1483 string_sprintf("bad option '%s'", scanner_options));
1488 /* Go through the rest of the list of host/port and construct an array
1489 * of servers to try. The first one is the bit we just passed from
1490 * scanner_options so process that first and then scan the remainder of
1491 * the address buffer */
1495 const uschar * sublist;
1499 /* The 'local' option means use the SCAN command over the network
1500 * socket (ie common file storage in use) */
1501 /*XXX we could accept this also as a local option? */
1502 if (strcmpic(scanner_options, US"local") == 0)
1504 use_scan_command = TRUE;
1508 cd = (clamd_address *) store_get(sizeof(clamd_address), GET_UNTAINTED);
1510 /* extract host and port part */
1511 sublist = scanner_options;
1512 if (!(cd->hostspec = string_nextinlist(&sublist, &subsep, NULL, 0)))
1514 (void) m_panic_defer(scanent, NULL,
1515 string_sprintf("missing address: '%s'", scanner_options));
1518 if (!(s = string_nextinlist(&sublist, &subsep, NULL, 0)))
1520 (void) m_panic_defer(scanent, NULL,
1521 string_sprintf("missing port: '%s'", scanner_options));
1524 cd->tcp_port = atoi(CS s);
1527 /*XXX should these options be common over scanner types? */
1528 if (clamd_option(cd, sublist, &subsep) != OK)
1529 return m_panic_defer(scanent, NULL,
1530 string_sprintf("bad option '%s'", scanner_options));
1532 cv[num_servers++] = cd;
1533 if (num_servers >= MAX_CLAMD_SERVERS)
1535 (void) m_panic_defer(scanent, NULL,
1536 US"More than " MAX_CLAMD_SERVERS_S " clamd servers "
1537 "specified; only using the first " MAX_CLAMD_SERVERS_S );
1540 } while ((scanner_options = string_nextinlist(&av_scanner_work, &sep,
1543 /* check if we have at least one server */
1545 return m_panic_defer(scanent, NULL,
1546 US"no useable server addresses in malware configuration option.");
1549 /* See the discussion of response formats below to see why we really
1550 don't like colons in filenames when passing filenames to ClamAV. */
1551 if (use_scan_command && Ustrchr(eml_filename, ':'))
1552 return m_panic_defer(scanent, NULL,
1553 string_sprintf("local/SCAN mode incompatible with" \
1554 " : in path to email filename [%s]", eml_filename));
1556 /* Set up the very first data we will be sending */
1557 if (!use_scan_command)
1558 { cmd_str.data = US"zINSTREAM"; cmd_str.len = 10; }
1562 cmd_str.data = string_sprintf("SCAN %s\n%n", eml_filename, &n);
1563 cmd_str.len = n; /* .len is a size_t */
1566 /* We have some network servers specified */
1569 /* Confirmed in ClamAV source (0.95.3) that the TCPAddr option of clamd
1570 only supports AF_INET, but we should probably be looking to the
1571 future and rewriting this to be protocol-independent anyway. */
1573 while (num_servers > 0)
1575 int i = random_number(num_servers);
1576 clamd_address * cd = cv[i];
1578 DEBUG(D_acl) debug_printf_indent("trying server name %s, port %u\n",
1579 cd->hostspec, cd->tcp_port);
1581 /* Lookup the host. This is to ensure that we connect to the same IP
1582 on both connections (as one host could resolve to multiple ips) */
1585 /*XXX we trust that the cmd_str is idempotent */
1586 if ((malware_daemon_ctx.sock = m_tcpsocket(cd->hostspec, cd->tcp_port,
1588 use_scan_command ? &cmd_str : NULL)) >= 0)
1590 /* Connection successfully established with a server */
1591 hostname = cd->hostspec;
1592 if (use_scan_command) cmd_str.len = 0;
1595 if (cd->retry <= 0) break;
1596 while (cd->retry > 0) cd->retry = sleep(cd->retry);
1598 if (malware_daemon_ctx.sock >= 0)
1601 (void) m_panic_defer(scanent, CUS callout_address, errstr);
1603 /* Remove the server from the list. XXX We should free the memory */
1605 for (; i < num_servers; i++)
1609 if (num_servers == 0)
1610 return m_panic_defer(scanent, NULL, US"all servers failed");
1615 if ((malware_daemon_ctx.sock = ip_unixsocket(cv[0]->hostspec, &errstr)) >= 0)
1617 hostname = cv[0]->hostspec;
1620 if (cv[0]->retry <= 0)
1621 return m_panic_defer(scanent, CUS callout_address, errstr);
1622 while (cv[0]->retry > 0) cv[0]->retry = sleep(cv[0]->retry);
1625 /* have socket in variable "sock"; command to use is semi-independent of
1626 the socket protocol. We use SCAN if is local (either Unix/local
1627 domain socket, or explicitly told local) else we stream the data.
1628 How we stream the data depends upon how we were built. */
1630 if (!use_scan_command)
1633 #if defined(EXIM_TCP_CORK) && !defined(OS_SENDFILE)
1636 /* New protocol: "zINSTREAM\n" followed by a sequence of <length><data>
1637 chunks, <n> a 4-byte number (network order), terminated by a zero-length
1638 chunk. We only send one chunk. */
1640 DEBUG(D_acl) debug_printf_indent(
1641 "Malware scan: issuing %s new-style remote scan (zINSTREAM)\n",
1644 #if defined(EXIM_TCP_CORK)
1645 (void) setsockopt(malware_daemon_ctx.sock, IPPROTO_TCP, EXIM_TCP_CORK,
1646 US &on, sizeof(on));
1648 /* Pass the string to ClamAV (10 = "zINSTREAM\0"), if not already sent */
1650 if (send(malware_daemon_ctx.sock, cmd_str.data, cmd_str.len, 0) < 0)
1651 return m_panic_defer_3(scanent, CUS hostname,
1652 string_sprintf("unable to send zINSTREAM to socket (%s)",
1654 malware_daemon_ctx.sock);
1656 if ((clam_fd = exim_open2(CS eml_filename, O_RDONLY)) < 0)
1659 return m_panic_defer_3(scanent, NULL,
1660 string_sprintf("can't open spool file %s: %s",
1661 eml_filename, strerror(err)),
1662 malware_daemon_ctx.sock);
1664 if (fstat(clam_fd, &st) < 0)
1667 (void)close(clam_fd);
1668 return m_panic_defer_3(scanent, NULL,
1669 string_sprintf("can't stat spool file %s: %s",
1670 eml_filename, strerror(err)),
1671 malware_daemon_ctx.sock);
1673 fsize_uint = (unsigned int) st.st_size;
1674 if ((off_t)fsize_uint != st.st_size)
1676 (void)close(clam_fd);
1677 return m_panic_defer_3(scanent, NULL,
1678 string_sprintf("stat spool file %s, size overflow", eml_filename),
1679 malware_daemon_ctx.sock);
1682 /* send file size */
1683 send_size = htonl(fsize_uint);
1684 if (send(malware_daemon_ctx.sock, &send_size, sizeof(send_size), 0) < 0)
1685 return m_panic_defer_3(scanent, NULL,
1686 string_sprintf("unable to send file size to socket (%s)", hostname),
1687 malware_daemon_ctx.sock);
1689 /* send file body */
1693 int n = os_sendfile(malware_daemon_ctx.sock, clam_fd, NULL, (size_t)fsize_uint);
1695 return m_panic_defer_3(scanent, NULL,
1696 string_sprintf("unable to send file body to socket (%s): %s", hostname, strerror(errno)),
1697 malware_daemon_ctx.sock);
1700 int n = MIN(fsize_uint, big_buffer_size);
1701 if ((n = read(clam_fd, big_buffer, n)) < 0)
1702 return m_panic_defer_3(scanent, NULL,
1703 string_sprintf("can't read spool file %s: %s",
1704 eml_filename, strerror(errno)),
1705 malware_daemon_ctx.sock);
1706 if (send(malware_daemon_ctx.sock, big_buffer, (size_t)n, 0) < 0)
1707 return m_panic_defer_3(scanent, NULL,
1708 string_sprintf("unable to send file body to socket (%s): %s", hostname, strerror(errno)),
1709 malware_daemon_ctx.sock);
1711 # ifdef EXIM_TCP_CORK
1715 (void) setsockopt(malware_daemon_ctx.sock, IPPROTO_TCP, EXIM_TCP_CORK,
1716 US &off, sizeof(off));
1719 #endif /*!OS_SENDFILE*/
1723 send_final_zeroblock = 0;
1724 if (send(malware_daemon_ctx.sock, &send_final_zeroblock, sizeof(send_final_zeroblock), 0) < 0)
1725 return m_panic_defer_3(scanent, NULL,
1726 string_sprintf("unable to send file terminator to socket (%s)", hostname),
1727 malware_daemon_ctx.sock);
1729 (void) setsockopt(malware_daemon_ctx.sock, IPPROTO_TCP, EXIM_TCP_CORK,
1730 US &off, sizeof(off));
1734 { /* use scan command */
1735 /* Send a SCAN command pointing to a filename; then in the then in the
1736 scan-method-neutral part, read the response back */
1738 /* ================================================================= */
1740 /* Prior to the reworking post-Exim-4.72, this scanned a directory,
1741 which dates to when ClamAV needed us to break apart the email into the
1742 MIME parts (eg, with the now deprecated demime condition coming first).
1743 Some time back, ClamAV gained the ability to deconstruct the emails, so
1744 doing this would actually have resulted in the mail attachments being
1745 scanned twice, in the broken out files and from the original .eml.
1746 Since ClamAV now handles emails (and has for quite some time) we can
1747 just use the email file itself. */
1748 /* Pass the string to ClamAV (7 = "SCAN \n" + \0), if not already sent */
1750 DEBUG(D_acl) debug_printf_indent(
1751 "Malware scan: issuing %s local-path scan [%s]\n",
1752 scanner_name, scanner_options);
1755 if (send(malware_daemon_ctx.sock, cmd_str.data, cmd_str.len, 0) < 0)
1756 return m_panic_defer_3(scanent, CUS callout_address,
1757 string_sprintf("unable to write to socket (%s)", strerror(errno)),
1758 malware_daemon_ctx.sock);
1760 /* Do not shut down the socket for writing; a user report noted that
1761 clamd 0.70 does not react well to this. */
1763 /* Commands have been sent, no matter which scan method or connection
1764 type we're using; now just read the result, independent of method. */
1766 /* Read the result */
1767 memset(av_buffer, 0, sizeof(av_buffer));
1768 bread = ip_recv(&malware_daemon_ctx, av_buffer, sizeof(av_buffer), tmo);
1769 (void)close(malware_daemon_ctx.sock);
1770 malware_daemon_ctx.sock = -1;
1771 malware_daemon_ctx.tls_ctx = NULL;
1774 return m_panic_defer(scanent, CUS callout_address,
1775 string_sprintf("unable to read from socket (%s)",
1776 errno == 0 ? "EOF" : strerror(errno)));
1778 if (bread == sizeof(av_buffer))
1779 return m_panic_defer(scanent, CUS callout_address,
1780 US"buffer too small");
1781 /* We're now assured of a NULL at the end of av_buffer */
1783 /* Check the result. ClamAV returns one of two result formats.
1784 In the basic mode, the response is of the form:
1785 infected: -> "<filename>: <virusname> FOUND"
1786 not-infected: -> "<filename>: OK"
1787 error: -> "<filename>: <errcode> ERROR
1788 If the ExtendedDetectionInfo option has been turned on, then we get:
1789 "<filename>: <virusname>(<virushash>:<virussize>) FOUND"
1790 for the infected case. Compare:
1791 /tmp/eicar.com: Eicar-Test-Signature FOUND
1792 /tmp/eicar.com: Eicar-Test-Signature(44d88612fea8a8f36de82e1278abb02f:68) FOUND
1794 In the streaming case, clamd uses the filename "stream" which you should
1795 be able to verify with { ktrace clamdscan --stream /tmp/eicar.com }. (The
1796 client app will replace "stream" with the original filename before returning
1797 results to stdout, but the trace shows the data).
1799 We will assume that the pathname passed to clamd from Exim does not contain
1800 a colon. We will have whined loudly above if the eml_filename does (and we're
1801 passing a filename to clamd). */
1804 return m_panic_defer(scanent, CUS callout_address,
1805 US"ClamAV returned null");
1807 /* strip newline at the end (won't be present for zINSTREAM)
1808 (also any trailing whitespace, which shouldn't exist, but we depend upon
1809 this below, so double-check) */
1811 p = av_buffer + Ustrlen(av_buffer) - 1;
1812 if (*p == '\n') *p = '\0';
1814 DEBUG(D_acl) debug_printf_indent("Malware response: %s\n", av_buffer);
1816 while (isspace(*--p) && (p > av_buffer))
1820 /* colon in returned output? */
1821 if (!(p = Ustrchr(av_buffer,':')))
1822 return m_panic_defer(scanent, CUS callout_address, string_sprintf(
1823 "ClamAV returned malformed result (missing colon): %s",
1826 /* strip filename */
1827 while (*p && isspace(*++p)) /**/;
1830 /* It would be bad to encounter a virus with "FOUND" in part of the name,
1831 but we should at least be resistant to it. */
1832 p = Ustrrchr(vname, ' ');
1833 result_tag = p ? p+1 : vname;
1835 if (Ustrcmp(result_tag, "FOUND") == 0)
1837 /* p should still be the whitespace before the result_tag */
1838 while (isspace(*p)) --p;
1840 /* Strip off the extended information too, which will be in parens
1841 after the virus name, with no intervening whitespace. */
1844 /* "(hash:size)", so previous '(' will do; if not found, we have
1845 a curious virus name, but not an error. */
1846 p = Ustrrchr(vname, '(');
1850 malware_name = string_copy(vname);
1851 DEBUG(D_acl) debug_printf_indent("Malware found, name \"%s\"\n", malware_name);
1854 else if (Ustrcmp(result_tag, "ERROR") == 0)
1855 return m_panic_defer(scanent, CUS callout_address,
1856 string_sprintf("ClamAV returned: %s", av_buffer));
1858 else if (Ustrcmp(result_tag, "OK") == 0)
1860 /* Everything should be OK */
1861 malware_name = NULL;
1862 DEBUG(D_acl) debug_printf_indent("Malware not found\n");
1866 return m_panic_defer(scanent, CUS callout_address,
1867 string_sprintf("unparseable response from ClamAV: {%s}", av_buffer));
1873 #ifndef DISABLE_MAL_SOCK
1874 case M_SOCK: /* "sock" scanner type ------------------------------------- */
1875 /* This code was derived by Martin Poole from the clamd code contributed
1876 by David Saez and the cmdline code
1880 uschar * commandline;
1881 uschar av_buffer[1024];
1882 uschar * linebuffer;
1883 uschar * sockline_scanner;
1884 uschar sockline_scanner_default[] = "%s\n";
1885 const pcre2_code *sockline_trig_re;
1886 const pcre2_code *sockline_name_re;
1888 /* find scanner command line */
1889 if ( (sockline_scanner = string_nextinlist(&av_scanner_work, &sep,
1891 && *sockline_scanner
1893 { /* check for no expansions apart from one %s */
1894 uschar * s = Ustrchr(sockline_scanner, '%');
1896 if ((*s != 's' && *s != '%') || Ustrchr(s+1, '%'))
1897 return m_panic_defer_3(scanent, NULL,
1898 US"unsafe sock scanner call spec", malware_daemon_ctx.sock);
1901 sockline_scanner = sockline_scanner_default;
1902 DEBUG(D_acl) debug_printf_indent("%15s%10s'%s'\n", "", "cmdline: ",
1903 string_printing(sockline_scanner));
1905 /* find scanner output trigger */
1906 sockline_trig_re = m_pcre_nextinlist(&av_scanner_work, &sep, av_scanner_textonly,
1907 "missing trigger specification", &errstr);
1908 if (!sockline_trig_re)
1909 return m_panic_defer_3(scanent, NULL, errstr, malware_daemon_ctx.sock);
1911 /* find virus name regex */
1912 sockline_name_re = m_pcre_nextinlist(&av_scanner_work, &sep, av_scanner_textonly,
1913 "missing virus name regex specification", &errstr);
1914 if (!sockline_name_re)
1915 return m_panic_defer_3(scanent, NULL, errstr, malware_daemon_ctx.sock);
1917 /* prepare scanner call - security depends on expansions check above */
1918 commandline = string_sprintf( CS sockline_scanner, CS eml_filename);
1919 DEBUG(D_acl) debug_printf_indent("%15s%10s'%s'\n", "", "expanded: ",
1920 string_printing(commandline));
1922 /* Pass the command string to the socket */
1923 if (m_sock_send(malware_daemon_ctx.sock, commandline, Ustrlen(commandline), &errstr) < 0)
1924 return m_panic_defer(scanent, CUS callout_address, errstr);
1926 /* Read the result */
1927 bread = ip_recv(&malware_daemon_ctx, av_buffer, sizeof(av_buffer), tmo);
1930 return m_panic_defer_3(scanent, CUS callout_address,
1931 string_sprintf("unable to read from socket (%s)", strerror(errno)),
1932 malware_daemon_ctx.sock);
1934 if (bread == sizeof(av_buffer))
1935 return m_panic_defer_3(scanent, CUS callout_address,
1936 US"buffer too small", malware_daemon_ctx.sock);
1937 av_buffer[bread] = '\0';
1938 linebuffer = string_copy(av_buffer);
1939 DEBUG(D_acl) debug_printf_indent("%15s%10s'%s'\n", "", "answer: ",
1940 string_printing(linebuffer));
1942 /* try trigger match */
1943 if (regex_match_and_setup(sockline_trig_re, linebuffer, 0, -1))
1945 if (!(malware_name = m_pcre_exec(sockline_name_re, av_buffer)))
1946 malware_name = US "unknown";
1947 DEBUG(D_acl) debug_printf_indent("%15s%10s'%s'\n", "", "name: ",
1948 string_printing(malware_name));
1950 else /* no virus found */
1951 malware_name = NULL;
1956 #ifndef DISABLE_MAL_MKS
1957 case M_MKSD: /* "mksd" scanner type ------------------------------------- */
1959 char *mksd_options_end;
1960 int mksd_maxproc = 1; /* default, if no option supplied */
1963 if (scanner_options)
1965 mksd_maxproc = (int)strtol(CS scanner_options, &mksd_options_end, 10);
1966 if ( *scanner_options == '\0'
1967 || *mksd_options_end != '\0'
1969 || mksd_maxproc > 32
1971 return m_panic_defer(scanent, CUS callout_address,
1972 string_sprintf("invalid option '%s'", scanner_options));
1975 if((malware_daemon_ctx.sock = ip_unixsocket(US "/var/run/mksd/socket", &errstr)) < 0)
1976 return m_panic_defer(scanent, CUS callout_address, errstr);
1978 malware_name = NULL;
1980 DEBUG(D_acl) debug_printf_indent("Malware scan: issuing %s scan\n", scanner_name);
1982 if ((retval = mksd_scan_packed(scanent, malware_daemon_ctx.sock, eml_filename, tmo)) != OK)
1984 close (malware_daemon_ctx.sock);
1991 #ifndef DISABLE_MAL_AVAST
1992 case M_AVAST: /* "avast" scanner type ----------------------------------- */
1995 uschar * scanrequest;
1996 enum {AVA_HELO, AVA_OPT, AVA_RSP, AVA_DONE} avast_stage;
1998 uschar * error_message = NULL;
1999 BOOL more_data = FALSE;
2002 /* According to Martin Tuma @avast the protocol uses "escaped
2003 whitespace", that is, every embedded whitespace is backslash
2004 escaped, as well as backslash is protected by backslash.
2005 The returned lines contain the name of the scanned file, a tab
2009 [E] - some error occurred
2010 Such marker follows the first non-escaped TAB. For more information
2011 see avast-protocol(5)
2013 We observed two cases:
2015 <- /file [E]0.0 Error 13 Permission denied
2016 <- 451 SCAN Engine error 13 permission denied
2019 <- /file… [E]3.0 Error 41120 The file is a decompression bomb
2021 <- /file… [+]2.0 0 Eicar Test Virus!!!
2024 If the scanner returns 4xx, DEFER is a good decision, combined
2025 with a panic log entry, to get the admin's attention.
2027 If the scanner returns 200, we reject it as malware, if found any,
2028 or, in case of an error, we set the malware message to the error
2031 Some of the >= 42000 errors are message related - usually some
2032 broken archives etc, but some of them are e.g. license related.
2033 Once the license expires the engine starts returning errors for
2034 every scanning attempt. I¹ have the full list of the error codes
2035 but it is not a public API and is subject to change. It is hard
2036 for me to say what you should do in case of an engine error. You
2037 can have a “Treat * unscanned file as infection” policy or “Treat
2038 unscanned file as clean” policy. ¹) Jakub Bednar
2042 if ( ( !ava_re_clean
2043 && !(ava_re_clean = m_pcre_compile(ava_re_clean_str, FALSE, &errstr)))
2045 && !(ava_re_virus = m_pcre_compile(ava_re_virus_str, FALSE, &errstr)))
2047 && !(ava_re_error = m_pcre_compile(ava_re_error_str, FALSE, &errstr)))
2049 return malware_panic_defer(errstr);
2051 /* wait for result */
2052 for (avast_stage = AVA_HELO;
2053 (nread = recv_line(malware_daemon_ctx.sock, buf, sizeof(buf), tmo)) > 0;
2056 int slen = Ustrlen(buf);
2060 /* Multi line responses are bracketed between 210 … and nnn … */
2061 if (Ustrncmp(buf, "210", 3) == 0)
2066 else if (more_data && isdigit(buf[0])) more_data = 0;
2068 switch (avast_stage)
2071 if (more_data) continue;
2072 if (Ustrncmp(buf, "220", 3) != 0)
2073 goto endloop; /* require a 220 */
2077 if (more_data) continue;
2078 if (Ustrncmp(buf, "200", 3) != 0)
2079 goto endloop; /* require a 200 */
2084 /* Check for another option to send. Newline-terminate it. */
2085 if ((scanrequest = string_nextinlist(&av_scanner_work, &sep,
2088 if (Ustrcmp(scanrequest, "pass_unscanned") == 0)
2090 DEBUG(D_acl) debug_printf_indent("pass unscanned files as clean\n");
2094 scanrequest = string_sprintf("%s\n", scanrequest);
2095 avast_stage = AVA_OPT; /* just sent option */
2096 DEBUG(D_acl) debug_printf_indent("send to avast OPTION: %s", scanrequest);
2100 scanrequest = string_sprintf("SCAN %s\n", eml_dir);
2101 avast_stage = AVA_RSP; /* just sent command */
2102 DEBUG(D_acl) debug_printf_indent("send to avast REQUEST: SCAN %s\n", eml_dir);
2105 /* send config-cmd or scan-request to socket */
2106 len = Ustrlen(scanrequest);
2107 if (send(malware_daemon_ctx.sock, scanrequest, len, 0) == -1)
2109 scanrequest[len-1] = '\0';
2110 return m_panic_defer_3(scanent, CUS callout_address, string_sprintf(
2111 "unable to send request '%s' to socket (%s): %s",
2112 scanrequest, scanner_options, strerror(errno)), malware_daemon_ctx.sock);
2119 if (isdigit(buf[0])) /* We're done */
2122 if (malware_name) /* Nothing else matters, just read on */
2125 if (regex_match(ava_re_clean, buf, slen, NULL))
2128 if ((malware_name = m_pcre_exec(ava_re_virus, buf)))
2130 unescape(malware_name);
2132 debug_printf_indent("unescaped malware name: '%s'\n", malware_name);
2136 if (strict) /* treat scanner errors as malware */
2138 if ((malware_name = m_pcre_exec(ava_re_error, buf)))
2140 unescape(malware_name);
2142 debug_printf_indent("unescaped error message: '%s'\n", malware_name);
2146 else if (regex_match(ava_re_error, buf, slen, NULL))
2148 log_write(0, LOG_MAIN, "internal scanner error (ignored): %s", buf);
2152 /* here also for any unexpected response from the scanner */
2153 DEBUG(D_acl) debug_printf("avast response not handled: '%s'\n", buf);
2157 default: log_write(0, LOG_PANIC, "%s:%d:%s: should not happen",
2158 __FILE__, __LINE__, __FUNCTION__);
2165 if (nread == -1) error_message = US"EOF from scanner";
2166 else if (nread < 0) error_message = US"timeout from scanner";
2167 else if (nread == 0) error_message = US"got nothing from scanner";
2168 else if (buf[0] != '2') error_message = buf;
2170 DEBUG(D_acl) debug_printf_indent("sent to avast QUIT\n");
2171 if (send(malware_daemon_ctx.sock, "QUIT\n", 5, 0) == -1)
2172 return m_panic_defer_3(scanent, CUS callout_address,
2173 string_sprintf("unable to send quit request to socket (%s): %s",
2174 scanner_options, strerror(errno)), malware_daemon_ctx.sock);
2177 return m_panic_defer_3(scanent, CUS callout_address, error_message, malware_daemon_ctx.sock);
2181 } /* scanner type switch */
2183 if (malware_daemon_ctx.sock >= 0)
2184 (void) close (malware_daemon_ctx.sock);
2185 malware_ok = TRUE; /* set "been here, done that" marker */
2188 /* match virus name against pattern (caseless ------->----------v) */
2189 if (malware_name && regex_match_and_setup(re, malware_name, 0, -1))
2191 DEBUG(D_acl) debug_printf_indent(
2192 "Matched regex to malware [%s] [%s]\n", malware_re, malware_name);
2200 /*************************************************
2201 * Scan an email for malware *
2202 *************************************************/
2204 /* This is the normal interface for scanning an email, which doesn't need a
2205 filename; it's a wrapper around the malware_file function.
2208 malware_re match condition for "malware="
2209 cacheable the RE did not use any dynamic elements during expansion
2210 timeout if nonzero, timeout in seconds
2212 Returns: Exim message processing code (OK, FAIL, DEFER, ...)
2213 where true means malware was found (condition applies)
2216 malware(const uschar * malware_re, BOOL cacheable, int timeout)
2218 int ret = malware_internal(malware_re, cacheable, NULL, timeout);
2220 if (ret == DEFER) av_failed = TRUE;
2225 /*************************************************
2226 * Scan a file for malware *
2227 *************************************************/
2229 /* This is a test wrapper for scanning an email, which is not used in
2230 normal processing. Scan any file, using the Exim scanning interface.
2231 This function tampers with various global variables so is unsafe to use
2232 in any other context.
2235 eml_filename a file holding the message to be scanned
2237 Returns: Exim message processing code (OK, FAIL, DEFER, ...)
2238 where true means malware was found (condition applies)
2241 malware_in_file(uschar *eml_filename)
2243 uschar message_id_buf[64];
2246 /* spool_mbox() assumes various parameters exist, when creating
2247 the relevant directory and the email within */
2249 (void) string_format(message_id_buf, sizeof(message_id_buf),
2250 "dummy-%d", vaguely_random_number(INT_MAX));
2251 message_id = message_id_buf;
2252 sender_address = US"malware-sender@example.net";
2254 recipients_list = NULL;
2255 receive_add_recipient(US"malware-victim@example.net", -1);
2256 f.enable_dollar_recipients = TRUE;
2258 ret = malware_internal(US"*", TRUE, eml_filename, 0);
2260 Ustrncpy(spooled_message_id, message_id, sizeof(spooled_message_id));
2263 /* don't set no_mbox_unspool; at present, there's no way for it to become
2264 set, but if that changes, then it should apply to these tests too */
2268 /* silence static analysis tools */
2278 if (!malware_default_re)
2279 malware_default_re = regex_must_compile(malware_regex_default, MCS_NOFLAGS, TRUE);
2281 #ifndef DISABLE_MAL_DRWEB
2283 drweb_re = regex_must_compile(drweb_re_str, MCS_NOFLAGS, TRUE);
2285 #ifndef DISABLE_MAL_FSECURE
2287 fsec_re = regex_must_compile(fsec_re_str, MCS_NOFLAGS, TRUE);
2289 #ifndef DISABLE_MAL_KAV
2291 kav_re_sus = regex_must_compile(kav_re_sus_str, MCS_NOFLAGS, TRUE);
2293 kav_re_inf = regex_must_compile(kav_re_inf_str, MCS_NOFLAGS, TRUE);
2295 #ifndef DISABLE_MAL_AVAST
2297 ava_re_clean = regex_must_compile(ava_re_clean_str, MCS_NOFLAGS, TRUE);
2299 ava_re_virus = regex_must_compile(ava_re_virus_str, MCS_NOFLAGS, TRUE);
2301 ava_re_error = regex_must_compile(ava_re_error_str, MCS_NOFLAGS, TRUE);
2303 #ifndef DISABLE_MAL_FFROT6D
2304 if (!fprot6d_re_error)
2305 fprot6d_re_error = regex_must_compile(fprot6d_re_error_str, MCS_NOFLAGS, TRUE);
2306 if (!fprot6d_re_virus)
2307 fprot6d_re_virus = regex_must_compile(fprot6d_re_virus_str, MCS_NOFLAGS, TRUE);
2313 malware_show_supported(gstring * g)
2315 g = string_cat(g, US"Malware:");
2316 for (struct scan * sc = m_scans; sc->scancode != (scanner_t)-1; sc++)
2317 g = string_fmt_append(g, " %s", sc->name);
2318 return string_cat(g, US"\n");
2322 # endif /*!MACRO_PREDEF*/
2323 #endif /*WITH_CONTENT_SCAN*/