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
11 /* Code for calling virus (malware) scanners. Called from acl.c. */
14 #ifdef WITH_CONTENT_SCAN /* entire file */
17 #ifndef DISABLE_MAL_FFROTD
20 #ifndef DISABLE_MAL_FFROT6D
23 #ifndef DISABLE_MAL_DRWEB
26 #ifndef DISABLE_MAL_AVE
29 #ifndef DISABLE_MAL_FSECURE
32 #ifndef DISABLE_MAL_KAV
35 #ifndef DISABLE_MAL_SOPHIE
38 #ifndef DISABLE_MAL_CLAM
41 #ifndef DISABLE_MAL_MKS
44 #ifndef DISABLE_MAL_AVAST
47 #ifndef DISABLE_MAL_SOCK
50 #ifndef DISABLE_MAL_CMDLINE
55 typedef enum {MC_NONE, MC_TCP, MC_UNIX, MC_STRM} contype_t;
60 const uschar * options_default;
64 #ifndef DISABLE_MAL_FFROTD
65 { M_FPROTD, US"f-protd", US"localhost 10200-10204", MC_TCP },
67 #ifndef DISABLE_MAL_FFROT6D
68 { M_FPROT6D, US"f-prot6d", US"localhost 10200", MC_TCP },
70 #ifndef DISABLE_MAL_DRWEB
71 { M_DRWEB, US"drweb", US"/usr/local/drweb/run/drwebd.sock", MC_STRM },
73 #ifndef DISABLE_MAL_AVE
74 { M_AVES, US"aveserver", US"/var/run/aveserver", MC_UNIX },
76 #ifndef DISABLE_MAL_FSECURE
77 { M_FSEC, US"fsecure", US"/var/run/.fsav", MC_UNIX },
79 #ifndef DISABLE_MAL_KAV
80 { M_KAVD, US"kavdaemon", US"/var/run/AvpCtl", MC_UNIX },
82 #ifndef DISABLE_MAL_SOPHIE
83 { M_SOPHIE, US"sophie", US"/var/run/sophie", MC_UNIX },
85 #ifndef DISABLE_MAL_CLAM
86 { M_CLAMD, US"clamd", US"/tmp/clamd", MC_NONE },
88 #ifndef DISABLE_MAL_MKS
89 { M_MKSD, US"mksd", NULL, MC_NONE },
91 #ifndef DISABLE_MAL_AVAST
92 { M_AVAST, US"avast", US"/var/run/avast/scan.sock", MC_STRM },
94 #ifndef DISABLE_MAL_SOCK
95 { M_SOCK, US"sock", US"/tmp/malware.sock", MC_STRM },
97 #ifndef DISABLE_MAL_CMDLINE
98 { M_CMDL, US"cmdline", NULL, MC_NONE },
100 { -1, NULL, NULL, MC_NONE } /* end-marker */
103 /******************************************************************************/
104 # ifdef MACRO_PREDEF /* build solely to predefine macros */
106 # include "macro_predef.h"
109 features_malware(void)
113 uschar buf[EXIM_DRIVERNAME_MAX];
115 spf(buf, sizeof(buf), US"_HAVE_MALWARE_");
117 for (const struct scan * sc = m_scans; sc->scancode != -1; sc++)
119 for (s = sc->name, t = buf+14; *s; s++) if (*s != '-')
122 builtin_macro_create(buf);
126 /******************************************************************************/
127 # else /*!MACRO_PREDEF, main build*/
130 #define MALWARE_TIMEOUT 120 /* default timeout, seconds */
132 static const uschar * malware_regex_default = US ".+";
133 static const pcre2_code * malware_default_re = NULL;
136 #ifndef DISABLE_MAL_CLAM
137 /* The maximum number of clamd servers that are supported in the configuration */
138 # define MAX_CLAMD_SERVERS 32
139 # define MAX_CLAMD_SERVERS_S "32"
141 typedef struct clamd_address {
149 #ifndef DISABLE_MAL_DRWEB
150 # define DRWEBD_SCAN_CMD (1) /* scan file, buffer or diskfile */
151 # define DRWEBD_RETURN_VIRUSES (1<<0) /* ask daemon return to us viruses names from report */
152 # define DRWEBD_IS_MAIL (1<<19) /* say to daemon that format is "archive MAIL" */
154 # define DERR_READ_ERR (1<<0) /* read error */
155 # define DERR_NOMEMORY (1<<2) /* no memory */
156 # define DERR_TIMEOUT (1<<9) /* scan timeout has run out */
157 # define DERR_BAD_CALL (1<<15) /* wrong command */
159 static const uschar * drweb_re_str = US "infected\\swith\\s*(.+?)$";
160 static const pcre2_code * drweb_re = NULL;
163 #ifndef DISABLE_MAL_FSECURE
164 static const uschar * fsec_re_str = US "\\S{0,5}INFECTED\\t[^\\t]*\\t([^\\t]+)\\t\\S*$";
165 static const pcre2_code * fsec_re = NULL;
168 #ifndef DISABLE_MAL_KAV
169 static const uschar * kav_re_sus_str = US "suspicion:\\s*(.+?)\\s*$";
170 static const uschar * kav_re_inf_str = US "infected:\\s*(.+?)\\s*$";
171 static const pcre2_code * kav_re_sus = NULL;
172 static const pcre2_code * kav_re_inf = NULL;
175 #ifndef DISABLE_MAL_AVAST
176 static const uschar * ava_re_clean_str = US "(?!\\\\)\\t\\[\\+\\]";
177 static const uschar * ava_re_virus_str = US "(?!\\\\)\\t\\[L\\]\\d+\\.0\\t0\\s(.*)";
178 static const uschar * ava_re_error_str = US "(?!\\\\)\\t\\[E\\]\\d+\\.0\\tError\\s\\d+\\s(.*)";
179 static const pcre2_code * ava_re_clean = NULL;
180 static const pcre2_code * ava_re_virus = NULL;
181 static const pcre2_code * ava_re_error = NULL;
184 #ifndef DISABLE_MAL_FFROT6D
185 static const uschar * fprot6d_re_error_str = US "^\\d+\\s<(.+?)>$";
186 static const uschar * fprot6d_re_virus_str = US "^\\d+\\s<infected:\\s+(.+?)>\\s+.+$";
187 static const pcre2_code * fprot6d_re_error = NULL;
188 static const pcre2_code * fprot6d_re_virus = NULL;
193 /******************************************************************************/
195 #ifndef DISABLE_MAL_KAV
196 /* Routine to check whether a system is big- or little-endian.
197 Ripped from http://www.faqs.org/faqs/graphics/fileformats-faq/part4/section-7.html
198 Needed for proper kavdaemon implementation. Sigh. */
199 # define BIG_MY_ENDIAN 0
200 # define LITTLE_MY_ENDIAN 1
201 static int test_byte_order(void);
205 short int word = 0x0001;
206 char *byte = CS &word;
207 return(byte[0] ? LITTLE_MY_ENDIAN : BIG_MY_ENDIAN);
211 BOOL malware_ok = FALSE;
213 /* Gross hacks for the -bmalware option; perhaps we should just create
214 the scan directory normally for that case, but look into rigging up the
215 needed header variables if not already set on the command-line? */
216 extern int spool_mbox_ok;
217 extern uschar spooled_message_id[MESSAGE_ID_LENGTH+1];
220 /* Some (currently avast only) use backslash escaped whitespace,
221 this function undoes these escapes */
223 #ifndef DISABLE_MAL_AVAST
229 if (*p == '\\' && (isspace(p[1]) || p[1] == '\\'))
230 for (p0 = p; *p0; ++p0) *p0 = p0[1];
234 /* --- malware_*_defer --- */
236 malware_panic_defer(const uschar * str)
238 log_write(0, LOG_MAIN|LOG_PANIC, "malware acl condition: %s", str);
242 malware_log_defer(const uschar * str)
244 log_write(0, LOG_MAIN, "malware acl condition: %s", str);
247 /* --- m_*_defer --- */
249 m_panic_defer(struct scan * scanent, const uschar * hostport,
252 return malware_panic_defer(string_sprintf("%s %s : %s",
253 scanent->name, hostport ? hostport : CUS"", str));
255 /* --- m_*_defer_3 */
257 m_panic_defer_3(struct scan * scanent, const uschar * hostport,
258 const uschar * str, int fd_to_close)
260 DEBUG(D_acl) debug_print_socket(fd_to_close);
261 (void) close(fd_to_close);
262 return m_panic_defer(scanent, hostport, str);
265 /*************************************************/
267 #ifndef DISABLE_MAL_CLAM
268 /* Only used by the Clamav code, which is working from a list of servers and
269 uses the returned in_addr to get a second connection to the same system.
272 m_tcpsocket(const uschar * hostname, unsigned int port,
273 host_item * host, uschar ** errstr, const blob * fastopen_blob)
275 int fd = ip_connectedsocket(SOCK_STREAM, hostname, port, port, 5,
276 host, errstr, fastopen_blob);
277 #ifdef EXIM_TFO_FREEBSD
278 /* Under some fault conditions, FreeBSD 12.2 seen to send a (non-TFO) SYN
279 and, getting no response, wait for a long time. Impose a 5s max. */
281 (void) poll_one_fd(fd, POLLOUT, 5 * 1000);
288 m_sock_send(int sock, uschar * buf, int cnt, uschar ** errstr)
290 if (send(sock, buf, cnt, 0) < 0)
294 *errstr = string_sprintf("unable to send to socket (%s): %s",
301 static const pcre2_code *
302 m_pcre_compile(const uschar * re, uschar ** errstr)
306 const pcre2_code * cre;
308 if (!(cre = pcre2_compile((PCRE2_SPTR)re, PCRE2_ZERO_TERMINATED,
309 PCRE_COPT, &err, &roffset, pcre_cmp_ctx)))
312 pcre2_get_error_message(err, errbuf, sizeof(errbuf));
313 *errstr= string_sprintf("regular expression error in '%s': %s at offset %ld",
314 re, errbuf, (long)roffset);
320 m_pcre_exec(const pcre2_code * cre, uschar * text)
322 pcre2_match_data * md = pcre2_match_data_create(2, pcre_gen_ctx);
323 int i = pcre2_match(cre, text, PCRE2_ZERO_TERMINATED, 0, 0, md, pcre_mtc_ctx);
324 PCRE2_UCHAR * substr = NULL;
327 if (i >= 2) /* Got it */
328 pcre2_substring_get_bynumber(md, 1, &substr, &slen);
332 static const pcre2_code *
333 m_pcre_nextinlist(const uschar ** list, int * sep,
334 char * listerr, uschar ** errstr)
336 const uschar * list_ele;
337 const pcre2_code * cre = NULL;
339 if (!(list_ele = string_nextinlist(list, sep, NULL, 0)))
340 *errstr = US listerr;
343 DEBUG(D_acl) debug_printf_indent("%15s%10s'%s'\n", "", "RE: ",
344 string_printing(list_ele));
345 cre = m_pcre_compile(CUS list_ele, errstr);
352 Simple though inefficient wrapper for reading a line. Drop CRs and the
353 trailing newline. Can return early on buffer full. Null-terminate.
354 Apply initial timeout if no data ready.
356 Return: number of chars - zero for an empty line
358 -2 on timeout or error
361 recv_line(int fd, uschar * buffer, int bsize, time_t tmo)
367 if (!fd_ready(fd, tmo))
370 /*XXX tmo handling assumes we always get a whole line */
373 while ((rcv = read(fd, p, 1)) > 0)
376 if (p-buffer > bsize-2) break;
377 if (*p == '\n') break;
384 debug_printf_indent("Malware scan: read %s (%s)\n",
385 rcv==0 ? "EOF" : "error", strerror(errno));
386 debug_print_socket(fd);
388 return rcv==0 ? -1 : -2;
392 DEBUG(D_acl) debug_printf_indent("Malware scan: read '%s'\n", buffer);
396 /* return TRUE iff size as requested */
397 #ifndef DISABLE_MAL_DRWEB
399 recv_len(int sock, void * buf, int size, time_t tmo)
401 return fd_ready(sock, tmo)
402 ? recv(sock, buf, size, 0) == size
409 #ifndef DISABLE_MAL_MKS
410 /* ============= private routines for the "mksd" scanner type ============== */
412 # include <sys/uio.h>
415 mksd_writev (int sock, struct iovec * iov, int iovcnt)
422 i = writev (sock, iov, iovcnt);
423 while (i < 0 && errno == EINTR);
426 (void) malware_panic_defer(
427 US"unable to write to mksd UNIX socket (/var/run/mksd/socket)");
430 for (;;) /* check for short write */
431 if (i >= iov->iov_len)
441 iov->iov_base = CS iov->iov_base + i;
448 mksd_read_lines (int sock, uschar *av_buffer, int av_buffer_size, time_t tmo)
450 client_conn_ctx cctx = {.sock = sock};
456 i = ip_recv(&cctx, av_buffer+offset, av_buffer_size-offset, tmo);
459 (void) malware_panic_defer(US"unable to read from mksd UNIX socket (/var/run/mksd/socket)");
464 /* offset == av_buffer_size -> buffer full */
465 if (offset == av_buffer_size)
467 (void) malware_panic_defer(US"malformed reply received from mksd");
470 } while (av_buffer[offset-1] != '\n');
472 av_buffer[offset] = '\0';
477 mksd_parse_line(struct scan * scanent, char * line)
488 if ((p = strchr (line, '\n')) != NULL)
490 return m_panic_defer(scanent, NULL,
491 string_sprintf("scanner failed: %s", line));
494 if ((p = strchr (line, '\n')) != NULL)
499 && (p = strchr(line+4, ' ')) != NULL
504 malware_name = string_copy(US line+4);
508 return m_panic_defer(scanent, NULL,
509 string_sprintf("malformed reply received: %s", line));
514 mksd_scan_packed(struct scan * scanent, int sock, const uschar * scan_filename,
518 const char *cmd = "MSQ\n";
519 uschar av_buffer[1024];
521 iov[0].iov_base = (void *) cmd;
523 iov[1].iov_base = (void *) scan_filename;
524 iov[1].iov_len = Ustrlen(scan_filename);
525 iov[2].iov_base = (void *) (cmd + 3);
528 if (mksd_writev (sock, iov, 3) < 0)
531 if (mksd_read_lines (sock, av_buffer, sizeof (av_buffer), tmo) < 0)
534 return mksd_parse_line (scanent, CS av_buffer);
539 #ifndef DISABLE_MAL_CLAM
541 clamd_option(clamd_address * cd, const uschar * optstr, int * subsep)
546 while ((s = string_nextinlist(&optstr, subsep, NULL, 0)))
547 if (Ustrncmp(s, "retry=", 6) == 0)
549 int sec = readconf_readtime((s += 6), '\0', FALSE);
562 /*************************************************
563 * Scan content for malware *
564 *************************************************/
566 /* This is an internal interface for scanning an email; the normal interface
567 is via malware(), or there's malware_in_file() used for testing/debugging.
570 malware_re match condition for "malware="
571 scan_filename the file holding the email to be scanned, if we're faking
572 this up for the -bmalware test, else NULL
573 timeout if nonzero, non-default timeoutl
575 Returns: Exim message processing code (OK, FAIL, DEFER, ...)
576 where true means malware was found (condition applies)
579 malware_internal(const uschar * malware_re, const uschar * scan_filename,
583 const uschar *av_scanner_work = av_scanner;
584 uschar *scanner_name;
585 unsigned long mbox_size;
587 const pcre2_code *re;
589 struct scan * scanent;
590 const uschar * scanner_options;
591 client_conn_ctx malware_daemon_ctx = {.sock = -1};
593 uschar * eml_filename, * eml_dir;
596 return FAIL; /* empty means "don't match anything" */
598 /* Ensure the eml mbox file is spooled up */
600 if (!(mbox_file = spool_mbox(&mbox_size, scan_filename, &eml_filename)))
601 return malware_panic_defer(US"error while creating mbox spool file");
603 /* None of our current scanners need the mbox file as a stream (they use
604 the name), so we can close it right away. Get the directory too. */
606 (void) fclose(mbox_file);
607 eml_dir = string_copyn(eml_filename, Ustrrchr(eml_filename, '/') - eml_filename);
609 /* parse 1st option */
610 if (strcmpic(malware_re, US"false") == 0 || Ustrcmp(malware_re,"0") == 0)
611 return FAIL; /* explicitly no matching */
613 /* special cases (match anything except empty) */
614 if ( strcmpic(malware_re,US"true") == 0
615 || Ustrcmp(malware_re,"*") == 0
616 || Ustrcmp(malware_re,"1") == 0
619 if ( !malware_default_re
620 && !(malware_default_re = m_pcre_compile(malware_regex_default, &errstr)))
621 return malware_panic_defer(errstr);
622 malware_re = malware_regex_default;
623 re = malware_default_re;
626 /* compile the regex, see if it works */
627 else if (!(re = m_pcre_compile(malware_re, &errstr)))
628 return malware_panic_defer(errstr);
630 /* if av_scanner starts with a dollar, expand it first */
631 if (*av_scanner == '$')
633 if (!(av_scanner_work = expand_string(av_scanner)))
634 return malware_panic_defer(
635 string_sprintf("av_scanner starts with $, but expansion failed: %s",
636 expand_string_message));
639 debug_printf_indent("Expanded av_scanner global: %s\n", av_scanner_work);
640 /* disable result caching in this case */
645 /* Do not scan twice (unless av_scanner is dynamic). */
648 /* find the scanner type from the av_scanner option */
649 if (!(scanner_name = string_nextinlist(&av_scanner_work, &sep, NULL, 0)))
650 return malware_panic_defer(US"av_scanner configuration variable is empty");
651 if (!timeout) timeout = MALWARE_TIMEOUT;
652 tmo = time(NULL) + timeout;
654 for (scanent = m_scans; ; scanent++)
657 return malware_panic_defer(string_sprintf("unknown scanner type '%s'",
659 if (strcmpic(scanner_name, US scanent->name) != 0)
661 DEBUG(D_acl) debug_printf_indent("Malware scan: %s tmo=%s\n",
662 scanner_name, readconf_printtime(timeout));
664 if (!(scanner_options = string_nextinlist(&av_scanner_work, &sep, NULL, 0)))
665 scanner_options = scanent->options_default;
666 if (scanent->conn == MC_NONE)
669 DEBUG(D_acl) debug_printf_indent("%15s%10s%s\n", "", "socket: ", scanner_options);
670 switch(scanent->conn)
673 malware_daemon_ctx.sock = ip_tcpsocket(scanner_options, &errstr, 5, NULL); break;
675 malware_daemon_ctx.sock = ip_unixsocket(scanner_options, &errstr); break;
677 malware_daemon_ctx.sock = ip_streamsocket(scanner_options, &errstr, 5, NULL); break;
679 /* compiler quietening */ break;
681 if (malware_daemon_ctx.sock < 0)
682 return m_panic_defer(scanent, CUS callout_address, errstr);
686 switch (scanent->scancode)
688 #ifndef DISABLE_MAL_FFROTD
689 case M_FPROTD: /* "f-protd" scanner type -------------------------------- */
691 uschar *fp_scan_option;
692 unsigned int detected=0, par_count=0;
693 uschar * scanrequest;
694 uschar buf[32768], *strhelper, *strhelper2;
695 uschar * malware_name_internal = NULL;
698 scanrequest = string_sprintf("GET %s", eml_filename);
700 while ((fp_scan_option = string_nextinlist(&av_scanner_work, &sep,
703 scanrequest = string_sprintf("%s%s%s", scanrequest,
704 par_count ? "%20" : "?", fp_scan_option);
707 scanrequest = string_sprintf("%s HTTP/1.0\r\n\r\n", scanrequest);
708 DEBUG(D_acl) debug_printf_indent("Malware scan: issuing %s: %s\n",
709 scanner_name, scanrequest);
711 /* send scan request */
712 if (m_sock_send(malware_daemon_ctx.sock, scanrequest, Ustrlen(scanrequest)+1, &errstr) < 0)
713 return m_panic_defer(scanent, CUS callout_address, errstr);
715 while ((len = recv_line(malware_daemon_ctx.sock, buf, sizeof(buf), tmo)) >= 0)
718 if (Ustrstr(buf, US"<detected type=\"") != NULL)
720 else if (detected && (strhelper = Ustrstr(buf, US"<name>")))
722 if ((strhelper2 = Ustrstr(buf, US"</name>")) != NULL)
725 malware_name_internal = string_copy(strhelper+6);
728 else if (Ustrstr(buf, US"<summary code=\""))
730 malware_name = Ustrstr(buf, US"<summary code=\"11\">")
731 ? malware_name_internal : NULL;
737 (void)close(malware_daemon_ctx.sock);
744 #ifndef DISABLE_MAL_FFROT6D
745 case M_FPROT6D: /* "f-prot6d" scanner type ----------------------------------- */
750 uschar * scanrequest;
751 uschar av_buffer[1024];
753 if ((!fprot6d_re_virus && !(fprot6d_re_virus = m_pcre_compile(fprot6d_re_virus_str, &errstr)))
754 || (!fprot6d_re_error && !(fprot6d_re_error = m_pcre_compile(fprot6d_re_error_str, &errstr))))
755 return malware_panic_defer(errstr);
757 scanrequest = string_sprintf("SCAN FILE %s\n", eml_filename);
758 DEBUG(D_acl) debug_printf_indent("Malware scan: issuing %s: %s\n",
759 scanner_name, scanrequest);
761 if (m_sock_send(malware_daemon_ctx.sock, scanrequest, Ustrlen(scanrequest), &errstr) < 0)
762 return m_panic_defer(scanent, CUS callout_address, errstr);
764 bread = ip_recv(&malware_daemon_ctx, av_buffer, sizeof(av_buffer), tmo);
767 return m_panic_defer_3(scanent, CUS callout_address,
768 string_sprintf("unable to read from socket (%s)", strerror(errno)),
769 malware_daemon_ctx.sock);
771 if (bread == sizeof(av_buffer))
772 return m_panic_defer_3(scanent, CUS callout_address,
773 US"buffer too small", malware_daemon_ctx.sock);
775 av_buffer[bread] = '\0';
776 linebuffer = string_copy(av_buffer);
778 m_sock_send(malware_daemon_ctx.sock, US"QUIT\n", 5, 0);
780 if ((e = m_pcre_exec(fprot6d_re_error, linebuffer)))
781 return m_panic_defer_3(scanent, CUS callout_address,
782 string_sprintf("scanner reported error (%s)", e), malware_daemon_ctx.sock);
784 if (!(malware_name = m_pcre_exec(fprot6d_re_virus, linebuffer)))
791 #ifndef DISABLE_MAL_DRWEB
792 case M_DRWEB: /* "drweb" scanner type ----------------------------------- */
793 /* v0.1 - added support for tcp sockets */
794 /* v0.0 - initial release -- support for unix sockets */
798 unsigned int fsize_uint;
799 uschar * tmpbuf, *drweb_fbuf;
800 int drweb_rc, drweb_cmd, drweb_flags = 0x0000, drweb_fd,
801 drweb_vnum, drweb_slen, drweb_fin = 0x0000;
803 /* prepare variables */
804 drweb_cmd = htonl(DRWEBD_SCAN_CMD);
805 drweb_flags = htonl(DRWEBD_RETURN_VIRUSES | DRWEBD_IS_MAIL);
807 if (*scanner_options != '/')
810 if ((drweb_fd = exim_open2(CCS eml_filename, O_RDONLY)) == -1)
811 return m_panic_defer_3(scanent, NULL,
812 string_sprintf("can't open spool file %s: %s",
813 eml_filename, strerror(errno)),
814 malware_daemon_ctx.sock);
816 if ((fsize = lseek(drweb_fd, 0, SEEK_END)) == -1)
819 badseek: err = errno;
820 (void)close(drweb_fd);
821 return m_panic_defer_3(scanent, NULL,
822 string_sprintf("can't seek spool file %s: %s",
823 eml_filename, strerror(err)),
824 malware_daemon_ctx.sock);
826 fsize_uint = (unsigned int) fsize;
827 if ((off_t)fsize_uint != fsize)
829 (void)close(drweb_fd);
830 return m_panic_defer_3(scanent, NULL,
831 string_sprintf("seeking spool file %s, size overflow",
833 malware_daemon_ctx.sock);
835 drweb_slen = htonl(fsize);
836 if (lseek(drweb_fd, 0, SEEK_SET) < 0)
839 DEBUG(D_acl) debug_printf_indent("Malware scan: issuing %s remote scan [%s]\n",
840 scanner_name, scanner_options);
842 /* send scan request */
843 if ((send(malware_daemon_ctx.sock, &drweb_cmd, sizeof(drweb_cmd), 0) < 0) ||
844 (send(malware_daemon_ctx.sock, &drweb_flags, sizeof(drweb_flags), 0) < 0) ||
845 (send(malware_daemon_ctx.sock, &drweb_fin, sizeof(drweb_fin), 0) < 0) ||
846 (send(malware_daemon_ctx.sock, &drweb_slen, sizeof(drweb_slen), 0) < 0))
848 (void)close(drweb_fd);
849 return m_panic_defer_3(scanent, CUS callout_address, string_sprintf(
850 "unable to send commands to socket (%s)", scanner_options),
851 malware_daemon_ctx.sock);
854 if (!(drweb_fbuf = store_malloc(fsize_uint)))
856 (void)close(drweb_fd);
857 return m_panic_defer_3(scanent, NULL,
858 string_sprintf("unable to allocate memory %u for file (%s)",
859 fsize_uint, eml_filename),
860 malware_daemon_ctx.sock);
863 if ((result = read (drweb_fd, drweb_fbuf, fsize)) == -1)
866 (void)close(drweb_fd);
867 store_free(drweb_fbuf);
868 return m_panic_defer_3(scanent, NULL,
869 string_sprintf("can't read spool file %s: %s",
870 eml_filename, strerror(err)),
871 malware_daemon_ctx.sock);
873 (void)close(drweb_fd);
875 /* send file body to socket */
876 if (send(malware_daemon_ctx.sock, drweb_fbuf, fsize, 0) < 0)
878 store_free(drweb_fbuf);
879 return m_panic_defer_3(scanent, CUS callout_address, string_sprintf(
880 "unable to send file body to socket (%s)", scanner_options),
881 malware_daemon_ctx.sock);
883 store_free(drweb_fbuf);
887 drweb_slen = htonl(Ustrlen(eml_filename));
889 DEBUG(D_acl) debug_printf_indent("Malware scan: issuing %s local scan [%s]\n",
890 scanner_name, scanner_options);
892 /* send scan request */
893 if ((send(malware_daemon_ctx.sock, &drweb_cmd, sizeof(drweb_cmd), 0) < 0) ||
894 (send(malware_daemon_ctx.sock, &drweb_flags, sizeof(drweb_flags), 0) < 0) ||
895 (send(malware_daemon_ctx.sock, &drweb_slen, sizeof(drweb_slen), 0) < 0) ||
896 (send(malware_daemon_ctx.sock, eml_filename, Ustrlen(eml_filename), 0) < 0) ||
897 (send(malware_daemon_ctx.sock, &drweb_fin, sizeof(drweb_fin), 0) < 0))
898 return m_panic_defer_3(scanent, CUS callout_address, string_sprintf(
899 "unable to send commands to socket (%s)", scanner_options),
900 malware_daemon_ctx.sock);
903 /* wait for result */
904 if (!recv_len(malware_daemon_ctx.sock, &drweb_rc, sizeof(drweb_rc), tmo))
905 return m_panic_defer_3(scanent, CUS callout_address,
906 US"unable to read return code", malware_daemon_ctx.sock);
907 drweb_rc = ntohl(drweb_rc);
909 if (!recv_len(malware_daemon_ctx.sock, &drweb_vnum, sizeof(drweb_vnum), tmo))
910 return m_panic_defer_3(scanent, CUS callout_address,
911 US"unable to read the number of viruses", malware_daemon_ctx.sock);
912 drweb_vnum = ntohl(drweb_vnum);
914 /* "virus(es) found" if virus number is > 0 */
919 /* setup default virus name */
920 malware_name = US"unknown";
922 /* set up match regex */
924 drweb_re = m_pcre_compile(drweb_re_str, &errstr);
926 /* read and concatenate virus names into one string */
927 for (int i = 0; i < drweb_vnum; i++)
929 pcre2_match_data * md = pcre2_match_data_create(2, pcre_gen_ctx);
931 /* read the size of report */
932 if (!recv_len(malware_daemon_ctx.sock, &drweb_slen, sizeof(drweb_slen), tmo))
933 return m_panic_defer_3(scanent, CUS callout_address,
934 US"cannot read report size", malware_daemon_ctx.sock);
935 drweb_slen = ntohl(drweb_slen);
937 /* assume tainted, since it is external input */
938 tmpbuf = store_get(drweb_slen, GET_TAINTED);
940 /* read report body */
941 if (!recv_len(malware_daemon_ctx.sock, tmpbuf, drweb_slen, tmo))
942 return m_panic_defer_3(scanent, CUS callout_address,
943 US"cannot read report string", malware_daemon_ctx.sock);
944 tmpbuf[drweb_slen] = '\0';
946 /* try matcher on the line, grab substring */
947 result = pcre2_match(drweb_re, (PCRE2_SPTR)tmpbuf, PCRE2_ZERO_TERMINATED,
948 0, 0, md, pcre_mtc_ctx);
951 PCRE2_SIZE * ovec = pcre2_get_ovector_pointer(md);
953 if (i==0) /* the first name we just copy to malware_name */
954 g = string_catn(NULL, US ovec[2], ovec[3] - ovec[2]);
956 else /* concatenate each new virus name to previous */
958 g = string_catn(g, US"/", 1);
959 g = string_catn(g, US ovec[2], ovec[3] - ovec[2]);
963 malware_name = string_from_gstring(g);
967 const char *drweb_s = NULL;
969 if (drweb_rc & DERR_READ_ERR) drweb_s = "read error";
970 if (drweb_rc & DERR_NOMEMORY) drweb_s = "no memory";
971 if (drweb_rc & DERR_TIMEOUT) drweb_s = "timeout";
972 if (drweb_rc & DERR_BAD_CALL) drweb_s = "wrong command";
973 /* retcodes DERR_SYMLINK, DERR_NO_REGFILE, DERR_SKIPPED.
974 * DERR_TOO_BIG, DERR_TOO_COMPRESSED, DERR_SPAM,
975 * DERR_CRC_ERROR, DERR_READSOCKET, DERR_WRITE_ERR
976 * and others are ignored */
978 return m_panic_defer_3(scanent, CUS callout_address,
979 string_sprintf("drweb daemon retcode 0x%x (%s)", drweb_rc, drweb_s),
980 malware_daemon_ctx.sock);
989 #ifndef DISABLE_MAL_AVE
990 case M_AVES: /* "aveserver" scanner type -------------------------------- */
995 /* read aveserver's greeting and see if it is ready (2xx greeting) */
997 recv_line(malware_daemon_ctx.sock, buf, sizeof(buf), tmo);
999 if (buf[0] != '2') /* aveserver is having problems */
1000 return m_panic_defer_3(scanent, CUS callout_address,
1001 string_sprintf("unavailable (Responded: %s).",
1002 ((buf[0] != 0) ? buf : US "nothing") ),
1003 malware_daemon_ctx.sock);
1005 /* prepare our command */
1006 (void)string_format(buf, sizeof(buf), "SCAN bPQRSTUW %s\r\n",
1010 DEBUG(D_acl) debug_printf_indent("Malware scan: issuing %s %s\n",
1012 if (m_sock_send(malware_daemon_ctx.sock, buf, Ustrlen(buf), &errstr) < 0)
1013 return m_panic_defer(scanent, CUS callout_address, errstr);
1015 malware_name = NULL;
1017 /* read response lines, find malware name and final response */
1018 while (recv_line(malware_daemon_ctx.sock, buf, sizeof(buf), tmo) > 0)
1022 if (buf[0] == '5') /* aveserver is having problems */
1024 result = m_panic_defer(scanent, CUS callout_address,
1025 string_sprintf("unable to scan file %s (Responded: %s).",
1026 eml_filename, buf));
1029 if (Ustrncmp(buf,"322",3) == 0)
1031 uschar *p = Ustrchr(&buf[4], ' ');
1033 malware_name = string_copy(&buf[4]);
1037 if (m_sock_send(malware_daemon_ctx.sock, US"quit\r\n", 6, &errstr) < 0)
1038 return m_panic_defer(scanent, CUS callout_address, errstr);
1040 /* read aveserver's greeting and see if it is ready (2xx greeting) */
1042 recv_line(malware_daemon_ctx.sock, buf, sizeof(buf), tmo);
1044 if (buf[0] != '2') /* aveserver is having problems */
1045 return m_panic_defer_3(scanent, CUS callout_address,
1046 string_sprintf("unable to quit dialogue (Responded: %s).",
1047 ((buf[0] != 0) ? buf : US "nothing") ),
1048 malware_daemon_ctx.sock);
1050 if (result == DEFER)
1052 (void)close(malware_daemon_ctx.sock);
1059 #ifndef DISABLE_MAL_FSECURE
1060 case M_FSEC: /* "fsecure" scanner type ---------------------------------- */
1064 uschar av_buffer[1024];
1065 static uschar *cmdopt[] = { US"CONFIGURE\tARCHIVE\t1\n",
1066 US"CONFIGURE\tTIMEOUT\t0\n",
1067 US"CONFIGURE\tMAXARCH\t5\n",
1068 US"CONFIGURE\tMIME\t1\n" };
1070 malware_name = NULL;
1072 DEBUG(D_acl) debug_printf_indent("Malware scan: issuing %s scan [%s]\n",
1073 scanner_name, scanner_options);
1075 memset(av_buffer, 0, sizeof(av_buffer));
1076 for (i = 0; i != nelem(cmdopt); i++)
1079 if (m_sock_send(malware_daemon_ctx.sock, cmdopt[i], Ustrlen(cmdopt[i]), &errstr) < 0)
1080 return m_panic_defer(scanent, CUS callout_address, errstr);
1082 bread = ip_recv(&malware_daemon_ctx, av_buffer, sizeof(av_buffer), tmo);
1083 if (bread > 0) av_buffer[bread]='\0';
1085 return m_panic_defer_3(scanent, CUS callout_address,
1086 string_sprintf("unable to read answer %d (%s)", i, strerror(errno)),
1087 malware_daemon_ctx.sock);
1088 for (int j = 0; j < bread; j++)
1089 if (av_buffer[j] == '\r' || av_buffer[j] == '\n')
1093 /* pass the mailfile to fsecure */
1094 file_name = string_sprintf("SCAN\t%s\n", eml_filename);
1096 if (m_sock_send(malware_daemon_ctx.sock, file_name, Ustrlen(file_name), &errstr) < 0)
1097 return m_panic_defer(scanent, CUS callout_address, errstr);
1100 /* todo also SUSPICION\t */
1102 fsec_re = m_pcre_compile(fsec_re_str, &errstr);
1104 /* read report, linewise. Apply a timeout as the Fsecure daemon
1105 sometimes wants an answer to "PING" but they won't tell us what */
1107 uschar * p = av_buffer;
1113 i = av_buffer+sizeof(av_buffer)-p;
1114 if ((bread= ip_recv(&malware_daemon_ctx, p, i-1, tmo)) < 0)
1115 return m_panic_defer_3(scanent, CUS callout_address,
1116 string_sprintf("unable to read result (%s)", strerror(errno)),
1117 malware_daemon_ctx.sock);
1119 for (p[bread] = '\0'; (q = Ustrchr(p, '\n')); p = q+1)
1123 /* Really search for virus again? */
1125 /* try matcher on the line, grab substring */
1126 malware_name = m_pcre_exec(fsec_re, p);
1128 if (Ustrstr(p, "OK\tScan ok."))
1132 /* copy down the trailing partial line then read another chunk */
1133 i = av_buffer+sizeof(av_buffer)-p;
1134 memmove(av_buffer, p, i);
1144 #ifndef DISABLE_MAL_KAV
1145 case M_KAVD: /* "kavdaemon" scanner type -------------------------------- */
1148 uschar tmpbuf[1024];
1149 uschar * scanrequest;
1151 unsigned long kav_reportlen;
1153 const pcre2_code *kav_re;
1156 /* get current date and time, build scan request */
1158 /* pdp note: before the eml_filename parameter, this scanned the
1159 directory; not finding documentation, so we'll strip off the directory.
1160 The side-effect is that the test framework scanning may end up in
1161 scanning more than was requested, but for the normal interface, this is
1164 strftime(CS tmpbuf, sizeof(tmpbuf), "%d %b %H:%M:%S", localtime(&t));
1165 scanrequest = string_sprintf("<0>%s:%s", CS tmpbuf, eml_filename);
1166 p = Ustrrchr(scanrequest, '/');
1170 DEBUG(D_acl) debug_printf_indent("Malware scan: issuing %s scan [%s]\n",
1171 scanner_name, scanner_options);
1173 /* send scan request */
1174 if (m_sock_send(malware_daemon_ctx.sock, scanrequest, Ustrlen(scanrequest)+1, &errstr) < 0)
1175 return m_panic_defer(scanent, CUS callout_address, errstr);
1177 /* wait for result */
1178 if (!recv_len(malware_daemon_ctx.sock, tmpbuf, 2, tmo))
1179 return m_panic_defer_3(scanent, CUS callout_address,
1180 US"unable to read 2 bytes from socket.", malware_daemon_ctx.sock);
1182 /* get errorcode from one nibble */
1183 kav_rc = tmpbuf[ test_byte_order()==LITTLE_MY_ENDIAN ? 0 : 1 ] & 0x0F;
1186 case 5: case 6: /* improper kavdaemon configuration */
1187 return m_panic_defer_3(scanent, CUS callout_address,
1188 US"please reconfigure kavdaemon to NOT disinfect or remove infected files.",
1189 malware_daemon_ctx.sock);
1191 return m_panic_defer_3(scanent, CUS callout_address,
1192 US"reported 'scanning not completed' (code 1).", malware_daemon_ctx.sock);
1194 return m_panic_defer_3(scanent, CUS callout_address,
1195 US"reported 'kavdaemon damaged' (code 7).", malware_daemon_ctx.sock);
1198 /* code 8 is not handled, since it is ambiguous. It appears mostly on
1199 bounces where part of a file has been cut off */
1201 /* "virus found" return codes (2-4) */
1202 if (kav_rc > 1 && kav_rc < 5)
1204 int report_flag = 0;
1206 /* setup default virus name */
1207 malware_name = US"unknown";
1209 report_flag = tmpbuf[ test_byte_order() == LITTLE_MY_ENDIAN ? 1 : 0 ];
1211 /* read the report, if available */
1212 if (report_flag == 1)
1214 /* read report size */
1215 if (!recv_len(malware_daemon_ctx.sock, &kav_reportlen, 4, tmo))
1216 return m_panic_defer_3(scanent, CUS callout_address,
1217 US"cannot read report size", malware_daemon_ctx.sock);
1219 /* it's possible that avp returns av_buffer[1] == 1 but the
1220 reportsize is 0 (!?) */
1221 if (kav_reportlen > 0)
1223 /* set up match regex, depends on retcode */
1226 if (!kav_re_sus) kav_re_sus = m_pcre_compile(kav_re_sus_str, &errstr);
1227 kav_re = kav_re_sus;
1231 if (!kav_re_inf) kav_re_inf = m_pcre_compile(kav_re_inf_str, &errstr);
1232 kav_re = kav_re_inf;
1235 /* read report, linewise. Using size from stream to read amount of data
1236 from same stream is safe enough. */
1237 /* coverity[tainted_data] */
1238 while (kav_reportlen > 0)
1240 if ((bread = recv_line(malware_daemon_ctx.sock, tmpbuf, sizeof(tmpbuf), tmo)) < 0)
1242 kav_reportlen -= bread+1;
1244 /* try matcher on the line, grab substring */
1245 if ((malware_name = m_pcre_exec(kav_re, tmpbuf)))
1251 else /* no virus found */
1252 malware_name = NULL;
1258 #ifndef DISABLE_MAL_CMDLINE
1259 case M_CMDL: /* "cmdline" scanner type ---------------------------------- */
1261 const uschar *cmdline_scanner = scanner_options;
1262 const pcre2_code *cmdline_trigger_re;
1263 const pcre2_code *cmdline_regex_re;
1265 uschar * commandline;
1266 void (*eximsigchld)(int);
1267 void (*eximsigpipe)(int);
1268 FILE *scanner_out = NULL;
1270 FILE *scanner_record = NULL;
1271 uschar linebuffer[32767];
1276 if (!cmdline_scanner)
1277 return m_panic_defer(scanent, NULL, errstr);
1279 /* find scanner output trigger */
1280 cmdline_trigger_re = m_pcre_nextinlist(&av_scanner_work, &sep,
1281 "missing trigger specification", &errstr);
1282 if (!cmdline_trigger_re)
1283 return m_panic_defer(scanent, NULL, errstr);
1285 /* find scanner name regex */
1286 cmdline_regex_re = m_pcre_nextinlist(&av_scanner_work, &sep,
1287 "missing virus name regex specification", &errstr);
1288 if (!cmdline_regex_re)
1289 return m_panic_defer(scanent, NULL, errstr);
1291 /* prepare scanner call; despite the naming, file_name holds a directory
1292 name which is documented as the value given to %s. */
1294 file_name = string_copy(eml_filename);
1295 p = Ustrrchr(file_name, '/');
1298 commandline = string_sprintf(CS cmdline_scanner, file_name);
1300 /* redirect STDERR too */
1301 commandline = string_sprintf("%s 2>&1", commandline);
1303 DEBUG(D_acl) debug_printf_indent("Malware scan: issuing %s scan [%s]\n",
1304 scanner_name, commandline);
1306 /* store exims signal handlers */
1307 eximsigchld = signal(SIGCHLD,SIG_DFL);
1308 eximsigpipe = signal(SIGPIPE,SIG_DFL);
1310 if (!(scanner_out = popen(CS commandline,"r")))
1313 signal(SIGCHLD,eximsigchld); signal(SIGPIPE,eximsigpipe);
1314 return m_panic_defer(scanent, NULL,
1315 string_sprintf("call (%s) failed: %s.", commandline, strerror(err)));
1317 scanner_fd = fileno(scanner_out);
1319 file_name = string_sprintf("%s/%s_scanner_output", eml_dir, message_id);
1321 if (!(scanner_record = modefopen(file_name, "wb", SPOOL_MODE)))
1324 (void) pclose(scanner_out);
1325 signal(SIGCHLD,eximsigchld); signal(SIGPIPE,eximsigpipe);
1326 return m_panic_defer(scanent, NULL, string_sprintf(
1327 "opening scanner output file (%s) failed: %s.",
1328 file_name, strerror(err)));
1331 /* look for trigger while recording output */
1332 while ((rcnt = recv_line(scanner_fd, linebuffer,
1333 sizeof(linebuffer), tmo)))
1340 (void) pclose(scanner_out);
1341 signal(SIGCHLD,eximsigchld); signal(SIGPIPE,eximsigpipe);
1342 return m_panic_defer(scanent, NULL, string_sprintf(
1343 "unable to read from scanner (%s): %s",
1344 commandline, strerror(err)));
1347 if (Ustrlen(linebuffer) > fwrite(linebuffer, 1, Ustrlen(linebuffer), scanner_record))
1350 (void) pclose(scanner_out);
1351 signal(SIGCHLD,eximsigchld); signal(SIGPIPE,eximsigpipe);
1352 return m_panic_defer(scanent, NULL, string_sprintf(
1353 "short write on scanner output file (%s).", file_name));
1355 putc('\n', scanner_record);
1356 /* try trigger match */
1358 && regex_match_and_setup(cmdline_trigger_re, linebuffer, 0, -1)
1363 (void)fclose(scanner_record);
1364 sep = pclose(scanner_out);
1365 signal(SIGCHLD,eximsigchld); signal(SIGPIPE,eximsigpipe);
1367 return m_panic_defer(scanent, NULL,
1369 ? string_sprintf("running scanner failed: %s", strerror(sep))
1370 : string_sprintf("scanner returned error code: %d", sep));
1375 /* setup default virus name */
1376 malware_name = US"unknown";
1378 /* re-open the scanner output file, look for name match */
1379 scanner_record = Ufopen(file_name, "rb");
1380 while (Ufgets(linebuffer, sizeof(linebuffer), scanner_record))
1381 if ((s = m_pcre_exec(cmdline_regex_re, linebuffer))) /* try match */
1383 (void)fclose(scanner_record);
1385 else /* no virus found */
1386 malware_name = NULL;
1391 #ifndef DISABLE_MAL_SOPHIE
1392 case M_SOPHIE: /* "sophie" scanner type --------------------------------- */
1397 uschar av_buffer[1024];
1399 /* pass the scan directory to sophie */
1400 file_name = string_copy(eml_filename);
1401 if ((p = Ustrrchr(file_name, '/')))
1404 DEBUG(D_acl) debug_printf_indent("Malware scan: issuing %s scan [%s]\n",
1405 scanner_name, scanner_options);
1407 if ( write(malware_daemon_ctx.sock, file_name, Ustrlen(file_name)) < 0
1408 || write(malware_daemon_ctx.sock, "\n", 1) != 1
1410 return m_panic_defer_3(scanent, CUS callout_address,
1411 string_sprintf("unable to write to UNIX socket (%s)", scanner_options),
1412 malware_daemon_ctx.sock);
1414 /* wait for result */
1415 memset(av_buffer, 0, sizeof(av_buffer));
1416 if ((bread = ip_recv(&malware_daemon_ctx, av_buffer, sizeof(av_buffer), tmo)) <= 0)
1417 return m_panic_defer_3(scanent, CUS callout_address,
1418 string_sprintf("unable to read from UNIX socket (%s)", scanner_options),
1419 malware_daemon_ctx.sock);
1422 if (av_buffer[0] == '1') {
1423 uschar * s = Ustrchr(av_buffer, '\n');
1426 malware_name = string_copy(&av_buffer[2]);
1428 else if (!strncmp(CS av_buffer, "-1", 2))
1429 return m_panic_defer_3(scanent, CUS callout_address,
1430 US"scanner reported error", malware_daemon_ctx.sock);
1431 else /* all ok, no virus */
1432 malware_name = NULL;
1438 #ifndef DISABLE_MAL_CLAM
1439 case M_CLAMD: /* "clamd" scanner type ----------------------------------- */
1441 /* This code was originally contributed by David Saez */
1442 /* There are three scanning methods available to us:
1443 * (1) Use the SCAN command, pointing to a file in the filesystem
1444 * (2) Use the STREAM command, send the data on a separate port
1445 * (3) Use the zINSTREAM command, send the data inline
1446 * The zINSTREAM command was introduced with ClamAV 0.95, which marked
1447 * STREAM deprecated; see: http://wiki.clamav.net/bin/view/Main/UpgradeNotes095
1448 * In Exim, we use SCAN if using a Unix-domain socket or explicitly told that
1449 * the TCP-connected daemon is actually local; otherwise we use zINSTREAM
1450 * See Exim bug 926 for details. */
1452 uschar *p, *vname, *result_tag;
1454 uschar av_buffer[1024];
1455 uschar *hostname = US"";
1458 unsigned int fsize_uint;
1459 BOOL use_scan_command = FALSE;
1460 clamd_address * cv[MAX_CLAMD_SERVERS];
1461 int num_servers = 0;
1462 uint32_t send_size, send_final_zeroblock;
1465 /*XXX if unixdomain socket, only one server supported. Needs fixing;
1466 there's no reason we should not mix local and remote servers */
1468 if (*scanner_options == '/')
1471 const uschar * sublist;
1474 /* Local file; so we def want to use_scan_command and don't want to try
1475 passing IP/port combinations */
1476 use_scan_command = TRUE;
1477 cd = (clamd_address *) store_get(sizeof(clamd_address), GET_UNTAINTED);
1479 /* extract socket-path part */
1480 sublist = scanner_options;
1481 cd->hostspec = string_nextinlist(&sublist, &subsep, NULL, 0);
1484 if (clamd_option(cd, sublist, &subsep) != OK)
1485 return m_panic_defer(scanent, NULL,
1486 string_sprintf("bad option '%s'", scanner_options));
1491 /* Go through the rest of the list of host/port and construct an array
1492 * of servers to try. The first one is the bit we just passed from
1493 * scanner_options so process that first and then scan the remainder of
1494 * the address buffer */
1498 const uschar * sublist;
1502 /* The 'local' option means use the SCAN command over the network
1503 * socket (ie common file storage in use) */
1504 /*XXX we could accept this also as a local option? */
1505 if (strcmpic(scanner_options, US"local") == 0)
1507 use_scan_command = TRUE;
1511 cd = (clamd_address *) store_get(sizeof(clamd_address), GET_UNTAINTED);
1513 /* extract host and port part */
1514 sublist = scanner_options;
1515 if (!(cd->hostspec = string_nextinlist(&sublist, &subsep, NULL, 0)))
1517 (void) m_panic_defer(scanent, NULL,
1518 string_sprintf("missing address: '%s'", scanner_options));
1521 if (!(s = string_nextinlist(&sublist, &subsep, NULL, 0)))
1523 (void) m_panic_defer(scanent, NULL,
1524 string_sprintf("missing port: '%s'", scanner_options));
1527 cd->tcp_port = atoi(CS s);
1530 /*XXX should these options be common over scanner types? */
1531 if (clamd_option(cd, sublist, &subsep) != OK)
1532 return m_panic_defer(scanent, NULL,
1533 string_sprintf("bad option '%s'", scanner_options));
1535 cv[num_servers++] = cd;
1536 if (num_servers >= MAX_CLAMD_SERVERS)
1538 (void) m_panic_defer(scanent, NULL,
1539 US"More than " MAX_CLAMD_SERVERS_S " clamd servers "
1540 "specified; only using the first " MAX_CLAMD_SERVERS_S );
1543 } while ((scanner_options = string_nextinlist(&av_scanner_work, &sep,
1546 /* check if we have at least one server */
1548 return m_panic_defer(scanent, NULL,
1549 US"no useable server addresses in malware configuration option.");
1552 /* See the discussion of response formats below to see why we really
1553 don't like colons in filenames when passing filenames to ClamAV. */
1554 if (use_scan_command && Ustrchr(eml_filename, ':'))
1555 return m_panic_defer(scanent, NULL,
1556 string_sprintf("local/SCAN mode incompatible with" \
1557 " : in path to email filename [%s]", eml_filename));
1559 /* Set up the very first data we will be sending */
1560 if (!use_scan_command)
1561 { cmd_str.data = US"zINSTREAM"; cmd_str.len = 10; }
1565 cmd_str.data = string_sprintf("SCAN %s\n%n", eml_filename, &n);
1566 cmd_str.len = n; /* .len is a size_t */
1569 /* We have some network servers specified */
1572 /* Confirmed in ClamAV source (0.95.3) that the TCPAddr option of clamd
1573 only supports AF_INET, but we should probably be looking to the
1574 future and rewriting this to be protocol-independent anyway. */
1576 while (num_servers > 0)
1578 int i = random_number(num_servers);
1579 clamd_address * cd = cv[i];
1581 DEBUG(D_acl) debug_printf_indent("trying server name %s, port %u\n",
1582 cd->hostspec, cd->tcp_port);
1584 /* Lookup the host. This is to ensure that we connect to the same IP
1585 on both connections (as one host could resolve to multiple ips) */
1588 /*XXX we trust that the cmd_str is idempotent */
1589 if ((malware_daemon_ctx.sock = m_tcpsocket(cd->hostspec, cd->tcp_port,
1591 use_scan_command ? &cmd_str : NULL)) >= 0)
1593 /* Connection successfully established with a server */
1594 hostname = cd->hostspec;
1595 if (use_scan_command) cmd_str.len = 0;
1598 if (cd->retry <= 0) break;
1599 while (cd->retry > 0) cd->retry = sleep(cd->retry);
1601 if (malware_daemon_ctx.sock >= 0)
1604 (void) m_panic_defer(scanent, CUS callout_address, errstr);
1606 /* Remove the server from the list. XXX We should free the memory */
1608 for (; i < num_servers; i++)
1612 if (num_servers == 0)
1613 return m_panic_defer(scanent, NULL, US"all servers failed");
1618 if ((malware_daemon_ctx.sock = ip_unixsocket(cv[0]->hostspec, &errstr)) >= 0)
1620 hostname = cv[0]->hostspec;
1623 if (cv[0]->retry <= 0)
1624 return m_panic_defer(scanent, CUS callout_address, errstr);
1625 while (cv[0]->retry > 0) cv[0]->retry = sleep(cv[0]->retry);
1628 /* have socket in variable "sock"; command to use is semi-independent of
1629 the socket protocol. We use SCAN if is local (either Unix/local
1630 domain socket, or explicitly told local) else we stream the data.
1631 How we stream the data depends upon how we were built. */
1633 if (!use_scan_command)
1636 #if defined(EXIM_TCP_CORK) && !defined(OS_SENDFILE)
1639 /* New protocol: "zINSTREAM\n" followed by a sequence of <length><data>
1640 chunks, <n> a 4-byte number (network order), terminated by a zero-length
1641 chunk. We only send one chunk. */
1643 DEBUG(D_acl) debug_printf_indent(
1644 "Malware scan: issuing %s new-style remote scan (zINSTREAM)\n",
1647 #if defined(EXIM_TCP_CORK)
1648 (void) setsockopt(malware_daemon_ctx.sock, IPPROTO_TCP, EXIM_TCP_CORK,
1649 US &on, sizeof(on));
1651 /* Pass the string to ClamAV (10 = "zINSTREAM\0"), if not already sent */
1653 if (send(malware_daemon_ctx.sock, cmd_str.data, cmd_str.len, 0) < 0)
1654 return m_panic_defer_3(scanent, CUS hostname,
1655 string_sprintf("unable to send zINSTREAM to socket (%s)",
1657 malware_daemon_ctx.sock);
1659 if ((clam_fd = exim_open2(CS eml_filename, O_RDONLY)) < 0)
1662 return m_panic_defer_3(scanent, NULL,
1663 string_sprintf("can't open spool file %s: %s",
1664 eml_filename, strerror(err)),
1665 malware_daemon_ctx.sock);
1667 if (fstat(clam_fd, &st) < 0)
1670 (void)close(clam_fd);
1671 return m_panic_defer_3(scanent, NULL,
1672 string_sprintf("can't stat spool file %s: %s",
1673 eml_filename, strerror(err)),
1674 malware_daemon_ctx.sock);
1676 fsize_uint = (unsigned int) st.st_size;
1677 if ((off_t)fsize_uint != st.st_size)
1679 (void)close(clam_fd);
1680 return m_panic_defer_3(scanent, NULL,
1681 string_sprintf("stat spool file %s, size overflow", eml_filename),
1682 malware_daemon_ctx.sock);
1685 /* send file size */
1686 send_size = htonl(fsize_uint);
1687 if (send(malware_daemon_ctx.sock, &send_size, sizeof(send_size), 0) < 0)
1688 return m_panic_defer_3(scanent, NULL,
1689 string_sprintf("unable to send file size to socket (%s)", hostname),
1690 malware_daemon_ctx.sock);
1692 /* send file body */
1696 int n = os_sendfile(malware_daemon_ctx.sock, clam_fd, NULL, (size_t)fsize_uint);
1698 return m_panic_defer_3(scanent, NULL,
1699 string_sprintf("unable to send file body to socket (%s): %s", hostname, strerror(errno)),
1700 malware_daemon_ctx.sock);
1703 int n = MIN(fsize_uint, big_buffer_size);
1704 if ((n = read(clam_fd, big_buffer, n)) < 0)
1705 return m_panic_defer_3(scanent, NULL,
1706 string_sprintf("can't read spool file %s: %s",
1707 eml_filename, strerror(errno)),
1708 malware_daemon_ctx.sock);
1709 if (send(malware_daemon_ctx.sock, big_buffer, (size_t)n, 0) < 0)
1710 return m_panic_defer_3(scanent, NULL,
1711 string_sprintf("unable to send file body to socket (%s): %s", hostname, strerror(errno)),
1712 malware_daemon_ctx.sock);
1714 # ifdef EXIM_TCP_CORK
1718 (void) setsockopt(malware_daemon_ctx.sock, IPPROTO_TCP, EXIM_TCP_CORK,
1719 US &off, sizeof(off));
1722 #endif /*!OS_SENDFILE*/
1726 send_final_zeroblock = 0;
1727 if (send(malware_daemon_ctx.sock, &send_final_zeroblock, sizeof(send_final_zeroblock), 0) < 0)
1728 return m_panic_defer_3(scanent, NULL,
1729 string_sprintf("unable to send file terminator to socket (%s)", hostname),
1730 malware_daemon_ctx.sock);
1732 (void) setsockopt(malware_daemon_ctx.sock, IPPROTO_TCP, EXIM_TCP_CORK,
1733 US &off, sizeof(off));
1737 { /* use scan command */
1738 /* Send a SCAN command pointing to a filename; then in the then in the
1739 scan-method-neutral part, read the response back */
1741 /* ================================================================= */
1743 /* Prior to the reworking post-Exim-4.72, this scanned a directory,
1744 which dates to when ClamAV needed us to break apart the email into the
1745 MIME parts (eg, with the now deprecated demime condition coming first).
1746 Some time back, ClamAV gained the ability to deconstruct the emails, so
1747 doing this would actually have resulted in the mail attachments being
1748 scanned twice, in the broken out files and from the original .eml.
1749 Since ClamAV now handles emails (and has for quite some time) we can
1750 just use the email file itself. */
1751 /* Pass the string to ClamAV (7 = "SCAN \n" + \0), if not already sent */
1753 DEBUG(D_acl) debug_printf_indent(
1754 "Malware scan: issuing %s local-path scan [%s]\n",
1755 scanner_name, scanner_options);
1758 if (send(malware_daemon_ctx.sock, cmd_str.data, cmd_str.len, 0) < 0)
1759 return m_panic_defer_3(scanent, CUS callout_address,
1760 string_sprintf("unable to write to socket (%s)", strerror(errno)),
1761 malware_daemon_ctx.sock);
1763 /* Do not shut down the socket for writing; a user report noted that
1764 clamd 0.70 does not react well to this. */
1766 /* Commands have been sent, no matter which scan method or connection
1767 type we're using; now just read the result, independent of method. */
1769 /* Read the result */
1770 memset(av_buffer, 0, sizeof(av_buffer));
1771 bread = ip_recv(&malware_daemon_ctx, av_buffer, sizeof(av_buffer), tmo);
1772 (void)close(malware_daemon_ctx.sock);
1773 malware_daemon_ctx.sock = -1;
1774 malware_daemon_ctx.tls_ctx = NULL;
1777 return m_panic_defer(scanent, CUS callout_address,
1778 string_sprintf("unable to read from socket (%s)",
1779 errno == 0 ? "EOF" : strerror(errno)));
1781 if (bread == sizeof(av_buffer))
1782 return m_panic_defer(scanent, CUS callout_address,
1783 US"buffer too small");
1784 /* We're now assured of a NULL at the end of av_buffer */
1786 /* Check the result. ClamAV returns one of two result formats.
1787 In the basic mode, the response is of the form:
1788 infected: -> "<filename>: <virusname> FOUND"
1789 not-infected: -> "<filename>: OK"
1790 error: -> "<filename>: <errcode> ERROR
1791 If the ExtendedDetectionInfo option has been turned on, then we get:
1792 "<filename>: <virusname>(<virushash>:<virussize>) FOUND"
1793 for the infected case. Compare:
1794 /tmp/eicar.com: Eicar-Test-Signature FOUND
1795 /tmp/eicar.com: Eicar-Test-Signature(44d88612fea8a8f36de82e1278abb02f:68) FOUND
1797 In the streaming case, clamd uses the filename "stream" which you should
1798 be able to verify with { ktrace clamdscan --stream /tmp/eicar.com }. (The
1799 client app will replace "stream" with the original filename before returning
1800 results to stdout, but the trace shows the data).
1802 We will assume that the pathname passed to clamd from Exim does not contain
1803 a colon. We will have whined loudly above if the eml_filename does (and we're
1804 passing a filename to clamd). */
1807 return m_panic_defer(scanent, CUS callout_address,
1808 US"ClamAV returned null");
1810 /* strip newline at the end (won't be present for zINSTREAM)
1811 (also any trailing whitespace, which shouldn't exist, but we depend upon
1812 this below, so double-check) */
1814 p = av_buffer + Ustrlen(av_buffer) - 1;
1815 if (*p == '\n') *p = '\0';
1817 DEBUG(D_acl) debug_printf_indent("Malware response: %s\n", av_buffer);
1819 while (isspace(*--p) && (p > av_buffer))
1823 /* colon in returned output? */
1824 if (!(p = Ustrchr(av_buffer,':')))
1825 return m_panic_defer(scanent, CUS callout_address, string_sprintf(
1826 "ClamAV returned malformed result (missing colon): %s",
1829 /* strip filename */
1830 while (*p && isspace(*++p)) /**/;
1833 /* It would be bad to encounter a virus with "FOUND" in part of the name,
1834 but we should at least be resistant to it. */
1835 p = Ustrrchr(vname, ' ');
1836 result_tag = p ? p+1 : vname;
1838 if (Ustrcmp(result_tag, "FOUND") == 0)
1840 /* p should still be the whitespace before the result_tag */
1841 while (isspace(*p)) --p;
1843 /* Strip off the extended information too, which will be in parens
1844 after the virus name, with no intervening whitespace. */
1847 /* "(hash:size)", so previous '(' will do; if not found, we have
1848 a curious virus name, but not an error. */
1849 p = Ustrrchr(vname, '(');
1853 malware_name = string_copy(vname);
1854 DEBUG(D_acl) debug_printf_indent("Malware found, name \"%s\"\n", malware_name);
1857 else if (Ustrcmp(result_tag, "ERROR") == 0)
1858 return m_panic_defer(scanent, CUS callout_address,
1859 string_sprintf("ClamAV returned: %s", av_buffer));
1861 else if (Ustrcmp(result_tag, "OK") == 0)
1863 /* Everything should be OK */
1864 malware_name = NULL;
1865 DEBUG(D_acl) debug_printf_indent("Malware not found\n");
1869 return m_panic_defer(scanent, CUS callout_address,
1870 string_sprintf("unparseable response from ClamAV: {%s}", av_buffer));
1876 #ifndef DISABLE_MAL_SOCK
1877 case M_SOCK: /* "sock" scanner type ------------------------------------- */
1878 /* This code was derived by Martin Poole from the clamd code contributed
1879 by David Saez and the cmdline code
1883 uschar * commandline;
1884 uschar av_buffer[1024];
1885 uschar * linebuffer;
1886 uschar * sockline_scanner;
1887 uschar sockline_scanner_default[] = "%s\n";
1888 const pcre2_code *sockline_trig_re;
1889 const pcre2_code *sockline_name_re;
1891 /* find scanner command line */
1892 if ( (sockline_scanner = string_nextinlist(&av_scanner_work, &sep,
1894 && *sockline_scanner
1896 { /* check for no expansions apart from one %s */
1897 uschar * s = Ustrchr(sockline_scanner, '%');
1899 if ((*s != 's' && *s != '%') || Ustrchr(s+1, '%'))
1900 return m_panic_defer_3(scanent, NULL,
1901 US"unsafe sock scanner call spec", malware_daemon_ctx.sock);
1904 sockline_scanner = sockline_scanner_default;
1905 DEBUG(D_acl) debug_printf_indent("%15s%10s'%s'\n", "", "cmdline: ",
1906 string_printing(sockline_scanner));
1908 /* find scanner output trigger */
1909 sockline_trig_re = m_pcre_nextinlist(&av_scanner_work, &sep,
1910 "missing trigger specification", &errstr);
1911 if (!sockline_trig_re)
1912 return m_panic_defer_3(scanent, NULL, errstr, malware_daemon_ctx.sock);
1914 /* find virus name regex */
1915 sockline_name_re = m_pcre_nextinlist(&av_scanner_work, &sep,
1916 "missing virus name regex specification", &errstr);
1917 if (!sockline_name_re)
1918 return m_panic_defer_3(scanent, NULL, errstr, malware_daemon_ctx.sock);
1920 /* prepare scanner call - security depends on expansions check above */
1921 commandline = string_sprintf( CS sockline_scanner, CS eml_filename);
1922 DEBUG(D_acl) debug_printf_indent("%15s%10s'%s'\n", "", "expanded: ",
1923 string_printing(commandline));
1925 /* Pass the command string to the socket */
1926 if (m_sock_send(malware_daemon_ctx.sock, commandline, Ustrlen(commandline), &errstr) < 0)
1927 return m_panic_defer(scanent, CUS callout_address, errstr);
1929 /* Read the result */
1930 bread = ip_recv(&malware_daemon_ctx, av_buffer, sizeof(av_buffer), tmo);
1933 return m_panic_defer_3(scanent, CUS callout_address,
1934 string_sprintf("unable to read from socket (%s)", strerror(errno)),
1935 malware_daemon_ctx.sock);
1937 if (bread == sizeof(av_buffer))
1938 return m_panic_defer_3(scanent, CUS callout_address,
1939 US"buffer too small", malware_daemon_ctx.sock);
1940 av_buffer[bread] = '\0';
1941 linebuffer = string_copy(av_buffer);
1942 DEBUG(D_acl) debug_printf_indent("%15s%10s'%s'\n", "", "answer: ",
1943 string_printing(linebuffer));
1945 /* try trigger match */
1946 if (regex_match_and_setup(sockline_trig_re, linebuffer, 0, -1))
1948 if (!(malware_name = m_pcre_exec(sockline_name_re, av_buffer)))
1949 malware_name = US "unknown";
1950 DEBUG(D_acl) debug_printf_indent("%15s%10s'%s'\n", "", "name: ",
1951 string_printing(malware_name));
1953 else /* no virus found */
1954 malware_name = NULL;
1959 #ifndef DISABLE_MAL_MKS
1960 case M_MKSD: /* "mksd" scanner type ------------------------------------- */
1962 char *mksd_options_end;
1963 int mksd_maxproc = 1; /* default, if no option supplied */
1966 if (scanner_options)
1968 mksd_maxproc = (int)strtol(CS scanner_options, &mksd_options_end, 10);
1969 if ( *scanner_options == '\0'
1970 || *mksd_options_end != '\0'
1972 || mksd_maxproc > 32
1974 return m_panic_defer(scanent, CUS callout_address,
1975 string_sprintf("invalid option '%s'", scanner_options));
1978 if((malware_daemon_ctx.sock = ip_unixsocket(US "/var/run/mksd/socket", &errstr)) < 0)
1979 return m_panic_defer(scanent, CUS callout_address, errstr);
1981 malware_name = NULL;
1983 DEBUG(D_acl) debug_printf_indent("Malware scan: issuing %s scan\n", scanner_name);
1985 if ((retval = mksd_scan_packed(scanent, malware_daemon_ctx.sock, eml_filename, tmo)) != OK)
1987 close (malware_daemon_ctx.sock);
1994 #ifndef DISABLE_MAL_AVAST
1995 case M_AVAST: /* "avast" scanner type ----------------------------------- */
1998 uschar * scanrequest;
1999 enum {AVA_HELO, AVA_OPT, AVA_RSP, AVA_DONE} avast_stage;
2001 uschar * error_message = NULL;
2002 BOOL more_data = FALSE;
2005 /* According to Martin Tuma @avast the protocol uses "escaped
2006 whitespace", that is, every embedded whitespace is backslash
2007 escaped, as well as backslash is protected by backslash.
2008 The returned lines contain the name of the scanned file, a tab
2012 [E] - some error occurred
2013 Such marker follows the first non-escaped TAB. For more information
2014 see avast-protocol(5)
2016 We observed two cases:
2018 <- /file [E]0.0 Error 13 Permission denied
2019 <- 451 SCAN Engine error 13 permission denied
2022 <- /file… [E]3.0 Error 41120 The file is a decompression bomb
2024 <- /file… [+]2.0 0 Eicar Test Virus!!!
2027 If the scanner returns 4xx, DEFER is a good decision, combined
2028 with a panic log entry, to get the admin's attention.
2030 If the scanner returns 200, we reject it as malware, if found any,
2031 or, in case of an error, we set the malware message to the error
2034 Some of the >= 42000 errors are message related - usually some
2035 broken archives etc, but some of them are e.g. license related.
2036 Once the license expires the engine starts returning errors for
2037 every scanning attempt. I¹ have the full list of the error codes
2038 but it is not a public API and is subject to change. It is hard
2039 for me to say what you should do in case of an engine error. You
2040 can have a “Treat * unscanned file as infection” policy or “Treat
2041 unscanned file as clean” policy. ¹) Jakub Bednar
2045 if ( ( !ava_re_clean
2046 && !(ava_re_clean = m_pcre_compile(ava_re_clean_str, &errstr)))
2048 && !(ava_re_virus = m_pcre_compile(ava_re_virus_str, &errstr)))
2050 && !(ava_re_error = m_pcre_compile(ava_re_error_str, &errstr)))
2052 return malware_panic_defer(errstr);
2054 /* wait for result */
2055 for (avast_stage = AVA_HELO;
2056 (nread = recv_line(malware_daemon_ctx.sock, buf, sizeof(buf), tmo)) > 0;
2059 int slen = Ustrlen(buf);
2063 /* Multi line responses are bracketed between 210 … and nnn … */
2064 if (Ustrncmp(buf, "210", 3) == 0)
2069 else if (more_data && isdigit(buf[0])) more_data = 0;
2071 switch (avast_stage)
2074 if (more_data) continue;
2075 if (Ustrncmp(buf, "220", 3) != 0)
2076 goto endloop; /* require a 220 */
2080 if (more_data) continue;
2081 if (Ustrncmp(buf, "200", 3) != 0)
2082 goto endloop; /* require a 200 */
2087 /* Check for another option to send. Newline-terminate it. */
2088 if ((scanrequest = string_nextinlist(&av_scanner_work, &sep,
2091 if (Ustrcmp(scanrequest, "pass_unscanned") == 0)
2093 DEBUG(D_acl) debug_printf_indent("pass unscanned files as clean\n");
2097 scanrequest = string_sprintf("%s\n", scanrequest);
2098 avast_stage = AVA_OPT; /* just sent option */
2099 DEBUG(D_acl) debug_printf_indent("send to avast OPTION: %s", scanrequest);
2103 scanrequest = string_sprintf("SCAN %s\n", eml_dir);
2104 avast_stage = AVA_RSP; /* just sent command */
2105 DEBUG(D_acl) debug_printf_indent("send to avast REQUEST: SCAN %s\n", eml_dir);
2108 /* send config-cmd or scan-request to socket */
2109 len = Ustrlen(scanrequest);
2110 if (send(malware_daemon_ctx.sock, scanrequest, len, 0) == -1)
2112 scanrequest[len-1] = '\0';
2113 return m_panic_defer_3(scanent, CUS callout_address, string_sprintf(
2114 "unable to send request '%s' to socket (%s): %s",
2115 scanrequest, scanner_options, strerror(errno)), malware_daemon_ctx.sock);
2122 if (isdigit(buf[0])) /* We're done */
2125 if (malware_name) /* Nothing else matters, just read on */
2128 if (regex_match(ava_re_clean, buf, slen, NULL))
2131 if ((malware_name = m_pcre_exec(ava_re_virus, buf)))
2133 unescape(malware_name);
2135 debug_printf_indent("unescaped malware name: '%s'\n", malware_name);
2139 if (strict) /* treat scanner errors as malware */
2141 if ((malware_name = m_pcre_exec(ava_re_error, buf)))
2143 unescape(malware_name);
2145 debug_printf_indent("unescaped error message: '%s'\n", malware_name);
2149 else if (regex_match(ava_re_error, buf, slen, NULL))
2151 log_write(0, LOG_MAIN, "internal scanner error (ignored): %s", buf);
2155 /* here also for any unexpected response from the scanner */
2156 DEBUG(D_acl) debug_printf("avast response not handled: '%s'\n", buf);
2160 default: log_write(0, LOG_PANIC, "%s:%d:%s: should not happen",
2161 __FILE__, __LINE__, __FUNCTION__);
2168 if (nread == -1) error_message = US"EOF from scanner";
2169 else if (nread < 0) error_message = US"timeout from scanner";
2170 else if (nread == 0) error_message = US"got nothing from scanner";
2171 else if (buf[0] != '2') error_message = buf;
2173 DEBUG(D_acl) debug_printf_indent("sent to avast QUIT\n");
2174 if (send(malware_daemon_ctx.sock, "QUIT\n", 5, 0) == -1)
2175 return m_panic_defer_3(scanent, CUS callout_address,
2176 string_sprintf("unable to send quit request to socket (%s): %s",
2177 scanner_options, strerror(errno)), malware_daemon_ctx.sock);
2180 return m_panic_defer_3(scanent, CUS callout_address, error_message, malware_daemon_ctx.sock);
2184 } /* scanner type switch */
2186 if (malware_daemon_ctx.sock >= 0)
2187 (void) close (malware_daemon_ctx.sock);
2188 malware_ok = TRUE; /* set "been here, done that" marker */
2191 /* match virus name against pattern (caseless ------->----------v) */
2192 if (malware_name && regex_match_and_setup(re, malware_name, 0, -1))
2194 DEBUG(D_acl) debug_printf_indent(
2195 "Matched regex to malware [%s] [%s]\n", malware_re, malware_name);
2203 /*************************************************
2204 * Scan an email for malware *
2205 *************************************************/
2207 /* This is the normal interface for scanning an email, which doesn't need a
2208 filename; it's a wrapper around the malware_file function.
2211 malware_re match condition for "malware="
2212 timeout if nonzero, timeout in seconds
2214 Returns: Exim message processing code (OK, FAIL, DEFER, ...)
2215 where true means malware was found (condition applies)
2218 malware(const uschar * malware_re, int timeout)
2220 int ret = malware_internal(malware_re, NULL, timeout);
2222 if (ret == DEFER) av_failed = TRUE;
2227 /*************************************************
2228 * Scan a file for malware *
2229 *************************************************/
2231 /* This is a test wrapper for scanning an email, which is not used in
2232 normal processing. Scan any file, using the Exim scanning interface.
2233 This function tampers with various global variables so is unsafe to use
2234 in any other context.
2237 eml_filename a file holding the message to be scanned
2239 Returns: Exim message processing code (OK, FAIL, DEFER, ...)
2240 where true means malware was found (condition applies)
2243 malware_in_file(uschar *eml_filename)
2245 uschar message_id_buf[64];
2248 /* spool_mbox() assumes various parameters exist, when creating
2249 the relevant directory and the email within */
2251 (void) string_format(message_id_buf, sizeof(message_id_buf),
2252 "dummy-%d", vaguely_random_number(INT_MAX));
2253 message_id = message_id_buf;
2254 sender_address = US"malware-sender@example.net";
2256 recipients_list = NULL;
2257 receive_add_recipient(US"malware-victim@example.net", -1);
2258 f.enable_dollar_recipients = TRUE;
2260 ret = malware_internal(US"*", eml_filename, 0);
2262 Ustrncpy(spooled_message_id, message_id, sizeof(spooled_message_id));
2265 /* don't set no_mbox_unspool; at present, there's no way for it to become
2266 set, but if that changes, then it should apply to these tests too */
2270 /* silence static analysis tools */
2280 if (!malware_default_re)
2281 malware_default_re = regex_must_compile(malware_regex_default, FALSE, TRUE);
2283 #ifndef DISABLE_MAL_DRWEB
2285 drweb_re = regex_must_compile(drweb_re_str, FALSE, TRUE);
2287 #ifndef DISABLE_MAL_FSECURE
2289 fsec_re = regex_must_compile(fsec_re_str, FALSE, TRUE);
2291 #ifndef DISABLE_MAL_KAV
2293 kav_re_sus = regex_must_compile(kav_re_sus_str, FALSE, TRUE);
2295 kav_re_inf = regex_must_compile(kav_re_inf_str, FALSE, TRUE);
2297 #ifndef DISABLE_MAL_AVAST
2299 ava_re_clean = regex_must_compile(ava_re_clean_str, FALSE, TRUE);
2301 ava_re_virus = regex_must_compile(ava_re_virus_str, FALSE, TRUE);
2303 ava_re_error = regex_must_compile(ava_re_error_str, FALSE, TRUE);
2305 #ifndef DISABLE_MAL_FFROT6D
2306 if (!fprot6d_re_error)
2307 fprot6d_re_error = regex_must_compile(fprot6d_re_error_str, FALSE, TRUE);
2308 if (!fprot6d_re_virus)
2309 fprot6d_re_virus = regex_must_compile(fprot6d_re_virus_str, FALSE, TRUE);
2315 malware_show_supported(gstring * g)
2317 g = string_cat(g, US"Malware:");
2318 for (struct scan * sc = m_scans; sc->scancode != (scanner_t)-1; sc++)
2319 g = string_fmt_append(g, " %s", sc->name);
2320 return string_cat(g, US"\n");
2324 # endif /*!MACRO_PREDEF*/
2325 #endif /*WITH_CONTENT_SCAN*/