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 - 2021
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 pcre2_code * 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 pcre2_code * 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 pcre2_code * 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 pcre2_code * kav_re_sus = NULL;
171 static const pcre2_code * 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 pcre2_code * ava_re_clean = NULL;
179 static const pcre2_code * ava_re_virus = NULL;
180 static const pcre2_code * 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 pcre2_code * fprot6d_re_error = NULL;
187 static const pcre2_code * 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. */
280 (void) poll_one_fd(fd, POLLOUT, 5 * 1000);
287 m_sock_send(int sock, uschar * buf, int cnt, uschar ** errstr)
289 if (send(sock, buf, cnt, 0) < 0)
293 *errstr = string_sprintf("unable to send to socket (%s): %s",
300 static const pcre2_code *
301 m_pcre_compile(const uschar * re, uschar ** errstr)
305 const pcre2_code * cre;
307 if (!(cre = pcre2_compile((PCRE2_SPTR)re, PCRE2_ZERO_TERMINATED,
308 PCRE_COPT, &err, &roffset, pcre_cmp_ctx)))
311 pcre2_get_error_message(err, errbuf, sizeof(errbuf));
312 *errstr= string_sprintf("regular expression error in '%s': %s at offset %ld",
313 re, errbuf, (long)roffset);
319 m_pcre_exec(const pcre2_code * cre, uschar * text)
321 pcre2_match_data * md = pcre2_match_data_create(2, pcre_gen_ctx);
322 int i = pcre2_match(cre, text, PCRE2_ZERO_TERMINATED, 0, 0, md, pcre_mtc_ctx);
323 PCRE2_UCHAR * substr = NULL;
326 if (i >= 2) /* Got it */
327 pcre2_substring_get_bynumber(md, 1, &substr, &slen);
331 static const pcre2_code *
332 m_pcre_nextinlist(const uschar ** list, int * sep,
333 char * listerr, uschar ** errstr)
335 const uschar * list_ele;
336 const pcre2_code * cre = NULL;
338 if (!(list_ele = string_nextinlist(list, sep, NULL, 0)))
339 *errstr = US listerr;
342 DEBUG(D_acl) debug_printf_indent("%15s%10s'%s'\n", "", "RE: ",
343 string_printing(list_ele));
344 cre = m_pcre_compile(CUS list_ele, errstr);
351 Simple though inefficient wrapper for reading a line. Drop CRs and the
352 trailing newline. Can return early on buffer full. Null-terminate.
353 Apply initial timeout if no data ready.
355 Return: number of chars - zero for an empty line
357 -2 on timeout or error
360 recv_line(int fd, uschar * buffer, int bsize, time_t tmo)
366 if (!fd_ready(fd, tmo))
369 /*XXX tmo handling assumes we always get a whole line */
372 while ((rcv = read(fd, p, 1)) > 0)
375 if (p-buffer > bsize-2) break;
376 if (*p == '\n') break;
383 debug_printf_indent("Malware scan: read %s (%s)\n",
384 rcv==0 ? "EOF" : "error", strerror(errno));
385 debug_print_socket(fd);
387 return rcv==0 ? -1 : -2;
391 DEBUG(D_acl) debug_printf_indent("Malware scan: read '%s'\n", buffer);
395 /* return TRUE iff size as requested */
396 #ifndef DISABLE_MAL_DRWEB
398 recv_len(int sock, void * buf, int size, time_t tmo)
400 return fd_ready(sock, tmo)
401 ? recv(sock, buf, size, 0) == size
408 #ifndef DISABLE_MAL_MKS
409 /* ============= private routines for the "mksd" scanner type ============== */
411 # include <sys/uio.h>
414 mksd_writev (int sock, struct iovec * iov, int iovcnt)
421 i = writev (sock, iov, iovcnt);
422 while (i < 0 && errno == EINTR);
425 (void) malware_panic_defer(
426 US"unable to write to mksd UNIX socket (/var/run/mksd/socket)");
429 for (;;) /* check for short write */
430 if (i >= iov->iov_len)
440 iov->iov_base = CS iov->iov_base + i;
447 mksd_read_lines (int sock, uschar *av_buffer, int av_buffer_size, time_t tmo)
449 client_conn_ctx cctx = {.sock = sock};
455 i = ip_recv(&cctx, av_buffer+offset, av_buffer_size-offset, tmo);
458 (void) malware_panic_defer(US"unable to read from mksd UNIX socket (/var/run/mksd/socket)");
463 /* offset == av_buffer_size -> buffer full */
464 if (offset == av_buffer_size)
466 (void) malware_panic_defer(US"malformed reply received from mksd");
469 } while (av_buffer[offset-1] != '\n');
471 av_buffer[offset] = '\0';
476 mksd_parse_line(struct scan * scanent, char * line)
487 if ((p = strchr (line, '\n')) != NULL)
489 return m_panic_defer(scanent, NULL,
490 string_sprintf("scanner failed: %s", line));
493 if ((p = strchr (line, '\n')) != NULL)
498 && (p = strchr(line+4, ' ')) != NULL
503 malware_name = string_copy(US line+4);
507 return m_panic_defer(scanent, NULL,
508 string_sprintf("malformed reply received: %s", line));
513 mksd_scan_packed(struct scan * scanent, int sock, const uschar * scan_filename,
517 const char *cmd = "MSQ\n";
518 uschar av_buffer[1024];
520 iov[0].iov_base = (void *) cmd;
522 iov[1].iov_base = (void *) scan_filename;
523 iov[1].iov_len = Ustrlen(scan_filename);
524 iov[2].iov_base = (void *) (cmd + 3);
527 if (mksd_writev (sock, iov, 3) < 0)
530 if (mksd_read_lines (sock, av_buffer, sizeof (av_buffer), tmo) < 0)
533 return mksd_parse_line (scanent, CS av_buffer);
538 #ifndef DISABLE_MAL_CLAM
540 clamd_option(clamd_address * cd, const uschar * optstr, int * subsep)
545 while ((s = string_nextinlist(&optstr, subsep, NULL, 0)))
546 if (Ustrncmp(s, "retry=", 6) == 0)
548 int sec = readconf_readtime((s += 6), '\0', FALSE);
561 /*************************************************
562 * Scan content for malware *
563 *************************************************/
565 /* This is an internal interface for scanning an email; the normal interface
566 is via malware(), or there's malware_in_file() used for testing/debugging.
569 malware_re match condition for "malware="
570 scan_filename the file holding the email to be scanned, if we're faking
571 this up for the -bmalware test, else NULL
572 timeout if nonzero, non-default timeoutl
574 Returns: Exim message processing code (OK, FAIL, DEFER, ...)
575 where true means malware was found (condition applies)
578 malware_internal(const uschar * malware_re, const uschar * scan_filename,
582 const uschar *av_scanner_work = av_scanner;
583 uschar *scanner_name;
584 unsigned long mbox_size;
586 const pcre2_code *re;
588 struct scan * scanent;
589 const uschar * scanner_options;
590 client_conn_ctx malware_daemon_ctx = {.sock = -1};
592 uschar * eml_filename, * eml_dir;
595 return FAIL; /* empty means "don't match anything" */
597 /* Ensure the eml mbox file is spooled up */
599 if (!(mbox_file = spool_mbox(&mbox_size, scan_filename, &eml_filename)))
600 return malware_panic_defer(US"error while creating mbox spool file");
602 /* None of our current scanners need the mbox file as a stream (they use
603 the name), so we can close it right away. Get the directory too. */
605 (void) fclose(mbox_file);
606 eml_dir = string_copyn(eml_filename, Ustrrchr(eml_filename, '/') - eml_filename);
608 /* parse 1st option */
609 if (strcmpic(malware_re, US"false") == 0 || Ustrcmp(malware_re,"0") == 0)
610 return FAIL; /* explicitly no matching */
612 /* special cases (match anything except empty) */
613 if ( strcmpic(malware_re,US"true") == 0
614 || Ustrcmp(malware_re,"*") == 0
615 || Ustrcmp(malware_re,"1") == 0
618 if ( !malware_default_re
619 && !(malware_default_re = m_pcre_compile(malware_regex_default, &errstr)))
620 return malware_panic_defer(errstr);
621 malware_re = malware_regex_default;
622 re = malware_default_re;
625 /* compile the regex, see if it works */
626 else if (!(re = m_pcre_compile(malware_re, &errstr)))
627 return malware_panic_defer(errstr);
629 /* if av_scanner starts with a dollar, expand it first */
630 if (*av_scanner == '$')
632 if (!(av_scanner_work = expand_string(av_scanner)))
633 return malware_panic_defer(
634 string_sprintf("av_scanner starts with $, but expansion failed: %s",
635 expand_string_message));
638 debug_printf_indent("Expanded av_scanner global: %s\n", av_scanner_work);
639 /* disable result caching in this case */
644 /* Do not scan twice (unless av_scanner is dynamic). */
647 /* find the scanner type from the av_scanner option */
648 if (!(scanner_name = string_nextinlist(&av_scanner_work, &sep, NULL, 0)))
649 return malware_panic_defer(US"av_scanner configuration variable is empty");
650 if (!timeout) timeout = MALWARE_TIMEOUT;
651 tmo = time(NULL) + timeout;
653 for (scanent = m_scans; ; scanent++)
656 return malware_panic_defer(string_sprintf("unknown scanner type '%s'",
658 if (strcmpic(scanner_name, US scanent->name) != 0)
660 DEBUG(D_acl) debug_printf_indent("Malware scan: %s tmo=%s\n",
661 scanner_name, readconf_printtime(timeout));
663 if (!(scanner_options = string_nextinlist(&av_scanner_work, &sep, NULL, 0)))
664 scanner_options = scanent->options_default;
665 if (scanent->conn == MC_NONE)
668 DEBUG(D_acl) debug_printf_indent("%15s%10s%s\n", "", "socket: ", scanner_options);
669 switch(scanent->conn)
672 malware_daemon_ctx.sock = ip_tcpsocket(scanner_options, &errstr, 5, NULL); break;
674 malware_daemon_ctx.sock = ip_unixsocket(scanner_options, &errstr); break;
676 malware_daemon_ctx.sock = ip_streamsocket(scanner_options, &errstr, 5, NULL); break;
678 /* compiler quietening */ break;
680 if (malware_daemon_ctx.sock < 0)
681 return m_panic_defer(scanent, CUS callout_address, errstr);
685 switch (scanent->scancode)
687 #ifndef DISABLE_MAL_FFROTD
688 case M_FPROTD: /* "f-protd" scanner type -------------------------------- */
690 uschar *fp_scan_option;
691 unsigned int detected=0, par_count=0;
692 uschar * scanrequest;
693 uschar buf[32768], *strhelper, *strhelper2;
694 uschar * malware_name_internal = NULL;
697 scanrequest = string_sprintf("GET %s", eml_filename);
699 while ((fp_scan_option = string_nextinlist(&av_scanner_work, &sep,
702 scanrequest = string_sprintf("%s%s%s", scanrequest,
703 par_count ? "%20" : "?", fp_scan_option);
706 scanrequest = string_sprintf("%s HTTP/1.0\r\n\r\n", scanrequest);
707 DEBUG(D_acl) debug_printf_indent("Malware scan: issuing %s: %s\n",
708 scanner_name, scanrequest);
710 /* send scan request */
711 if (m_sock_send(malware_daemon_ctx.sock, scanrequest, Ustrlen(scanrequest)+1, &errstr) < 0)
712 return m_panic_defer(scanent, CUS callout_address, errstr);
714 while ((len = recv_line(malware_daemon_ctx.sock, buf, sizeof(buf), tmo)) >= 0)
717 if (Ustrstr(buf, US"<detected type=\"") != NULL)
719 else if (detected && (strhelper = Ustrstr(buf, US"<name>")))
721 if ((strhelper2 = Ustrstr(buf, US"</name>")) != NULL)
724 malware_name_internal = string_copy(strhelper+6);
727 else if (Ustrstr(buf, US"<summary code=\""))
729 malware_name = Ustrstr(buf, US"<summary code=\"11\">")
730 ? malware_name_internal : NULL;
736 (void)close(malware_daemon_ctx.sock);
743 #ifndef DISABLE_MAL_FFROT6D
744 case M_FPROT6D: /* "f-prot6d" scanner type ----------------------------------- */
749 uschar * scanrequest;
750 uschar av_buffer[1024];
752 if ((!fprot6d_re_virus && !(fprot6d_re_virus = m_pcre_compile(fprot6d_re_virus_str, &errstr)))
753 || (!fprot6d_re_error && !(fprot6d_re_error = m_pcre_compile(fprot6d_re_error_str, &errstr))))
754 return malware_panic_defer(errstr);
756 scanrequest = string_sprintf("SCAN FILE %s\n", eml_filename);
757 DEBUG(D_acl) debug_printf_indent("Malware scan: issuing %s: %s\n",
758 scanner_name, scanrequest);
760 if (m_sock_send(malware_daemon_ctx.sock, scanrequest, Ustrlen(scanrequest), &errstr) < 0)
761 return m_panic_defer(scanent, CUS callout_address, errstr);
763 bread = ip_recv(&malware_daemon_ctx, av_buffer, sizeof(av_buffer), tmo);
766 return m_panic_defer_3(scanent, CUS callout_address,
767 string_sprintf("unable to read from socket (%s)", strerror(errno)),
768 malware_daemon_ctx.sock);
770 if (bread == sizeof(av_buffer))
771 return m_panic_defer_3(scanent, CUS callout_address,
772 US"buffer too small", malware_daemon_ctx.sock);
774 av_buffer[bread] = '\0';
775 linebuffer = string_copy(av_buffer);
777 m_sock_send(malware_daemon_ctx.sock, US"QUIT\n", 5, 0);
779 if ((e = m_pcre_exec(fprot6d_re_error, linebuffer)))
780 return m_panic_defer_3(scanent, CUS callout_address,
781 string_sprintf("scanner reported error (%s)", e), malware_daemon_ctx.sock);
783 if (!(malware_name = m_pcre_exec(fprot6d_re_virus, linebuffer)))
790 #ifndef DISABLE_MAL_DRWEB
791 case M_DRWEB: /* "drweb" scanner type ----------------------------------- */
792 /* v0.1 - added support for tcp sockets */
793 /* v0.0 - initial release -- support for unix sockets */
797 unsigned int fsize_uint;
798 uschar * tmpbuf, *drweb_fbuf;
799 int drweb_rc, drweb_cmd, drweb_flags = 0x0000, drweb_fd,
800 drweb_vnum, drweb_slen, drweb_fin = 0x0000;
802 /* prepare variables */
803 drweb_cmd = htonl(DRWEBD_SCAN_CMD);
804 drweb_flags = htonl(DRWEBD_RETURN_VIRUSES | DRWEBD_IS_MAIL);
806 if (*scanner_options != '/')
809 if ((drweb_fd = exim_open2(CCS eml_filename, O_RDONLY)) == -1)
810 return m_panic_defer_3(scanent, NULL,
811 string_sprintf("can't open spool file %s: %s",
812 eml_filename, strerror(errno)),
813 malware_daemon_ctx.sock);
815 if ((fsize = lseek(drweb_fd, 0, SEEK_END)) == -1)
818 badseek: err = errno;
819 (void)close(drweb_fd);
820 return m_panic_defer_3(scanent, NULL,
821 string_sprintf("can't seek spool file %s: %s",
822 eml_filename, strerror(err)),
823 malware_daemon_ctx.sock);
825 fsize_uint = (unsigned int) fsize;
826 if ((off_t)fsize_uint != fsize)
828 (void)close(drweb_fd);
829 return m_panic_defer_3(scanent, NULL,
830 string_sprintf("seeking spool file %s, size overflow",
832 malware_daemon_ctx.sock);
834 drweb_slen = htonl(fsize);
835 if (lseek(drweb_fd, 0, SEEK_SET) < 0)
838 DEBUG(D_acl) debug_printf_indent("Malware scan: issuing %s remote scan [%s]\n",
839 scanner_name, scanner_options);
841 /* send scan request */
842 if ((send(malware_daemon_ctx.sock, &drweb_cmd, sizeof(drweb_cmd), 0) < 0) ||
843 (send(malware_daemon_ctx.sock, &drweb_flags, sizeof(drweb_flags), 0) < 0) ||
844 (send(malware_daemon_ctx.sock, &drweb_fin, sizeof(drweb_fin), 0) < 0) ||
845 (send(malware_daemon_ctx.sock, &drweb_slen, sizeof(drweb_slen), 0) < 0))
847 (void)close(drweb_fd);
848 return m_panic_defer_3(scanent, CUS callout_address, string_sprintf(
849 "unable to send commands to socket (%s)", scanner_options),
850 malware_daemon_ctx.sock);
853 if (!(drweb_fbuf = store_malloc(fsize_uint)))
855 (void)close(drweb_fd);
856 return m_panic_defer_3(scanent, NULL,
857 string_sprintf("unable to allocate memory %u for file (%s)",
858 fsize_uint, eml_filename),
859 malware_daemon_ctx.sock);
862 if ((result = read (drweb_fd, drweb_fbuf, fsize)) == -1)
865 (void)close(drweb_fd);
866 store_free(drweb_fbuf);
867 return m_panic_defer_3(scanent, NULL,
868 string_sprintf("can't read spool file %s: %s",
869 eml_filename, strerror(err)),
870 malware_daemon_ctx.sock);
872 (void)close(drweb_fd);
874 /* send file body to socket */
875 if (send(malware_daemon_ctx.sock, drweb_fbuf, fsize, 0) < 0)
877 store_free(drweb_fbuf);
878 return m_panic_defer_3(scanent, CUS callout_address, string_sprintf(
879 "unable to send file body to socket (%s)", scanner_options),
880 malware_daemon_ctx.sock);
882 store_free(drweb_fbuf);
886 drweb_slen = htonl(Ustrlen(eml_filename));
888 DEBUG(D_acl) debug_printf_indent("Malware scan: issuing %s local scan [%s]\n",
889 scanner_name, scanner_options);
891 /* send scan request */
892 if ((send(malware_daemon_ctx.sock, &drweb_cmd, sizeof(drweb_cmd), 0) < 0) ||
893 (send(malware_daemon_ctx.sock, &drweb_flags, sizeof(drweb_flags), 0) < 0) ||
894 (send(malware_daemon_ctx.sock, &drweb_slen, sizeof(drweb_slen), 0) < 0) ||
895 (send(malware_daemon_ctx.sock, eml_filename, Ustrlen(eml_filename), 0) < 0) ||
896 (send(malware_daemon_ctx.sock, &drweb_fin, sizeof(drweb_fin), 0) < 0))
897 return m_panic_defer_3(scanent, CUS callout_address, string_sprintf(
898 "unable to send commands to socket (%s)", scanner_options),
899 malware_daemon_ctx.sock);
902 /* wait for result */
903 if (!recv_len(malware_daemon_ctx.sock, &drweb_rc, sizeof(drweb_rc), tmo))
904 return m_panic_defer_3(scanent, CUS callout_address,
905 US"unable to read return code", malware_daemon_ctx.sock);
906 drweb_rc = ntohl(drweb_rc);
908 if (!recv_len(malware_daemon_ctx.sock, &drweb_vnum, sizeof(drweb_vnum), tmo))
909 return m_panic_defer_3(scanent, CUS callout_address,
910 US"unable to read the number of viruses", malware_daemon_ctx.sock);
911 drweb_vnum = ntohl(drweb_vnum);
913 /* "virus(es) found" if virus number is > 0 */
918 /* setup default virus name */
919 malware_name = US"unknown";
921 /* set up match regex */
923 drweb_re = m_pcre_compile(drweb_re_str, &errstr);
925 /* read and concatenate virus names into one string */
926 for (int i = 0; i < drweb_vnum; i++)
928 pcre2_match_data * md = pcre2_match_data_create(2, pcre_gen_ctx);
930 /* read the size of report */
931 if (!recv_len(malware_daemon_ctx.sock, &drweb_slen, sizeof(drweb_slen), tmo))
932 return m_panic_defer_3(scanent, CUS callout_address,
933 US"cannot read report size", malware_daemon_ctx.sock);
934 drweb_slen = ntohl(drweb_slen);
936 /* assume tainted, since it is external input */
937 tmpbuf = store_get(drweb_slen, TRUE);
939 /* read report body */
940 if (!recv_len(malware_daemon_ctx.sock, tmpbuf, drweb_slen, tmo))
941 return m_panic_defer_3(scanent, CUS callout_address,
942 US"cannot read report string", malware_daemon_ctx.sock);
943 tmpbuf[drweb_slen] = '\0';
945 /* try matcher on the line, grab substring */
946 result = pcre2_match(drweb_re, (PCRE2_SPTR)tmpbuf, PCRE2_ZERO_TERMINATED,
947 0, 0, md, pcre_mtc_ctx);
950 PCRE2_SIZE * ovec = pcre2_get_ovector_pointer(md);
952 if (i==0) /* the first name we just copy to malware_name */
953 g = string_catn(NULL, US ovec[2], ovec[3] - ovec[2]);
955 else /* concatenate each new virus name to previous */
957 g = string_catn(g, US"/", 1);
958 g = string_catn(g, US ovec[2], ovec[3] - ovec[2]);
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;
1152 const pcre2_code *kav_re;
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 pcre2_code *cmdline_trigger_re;
1262 const pcre2_code *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"";
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 pcre2_code *sockline_trig_re;
1888 const pcre2_code *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 (regex_match(ava_re_clean, buf, slen, NULL))
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 (regex_match(ava_re_error, buf, slen, NULL))
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*/