1 /*************************************************
2 * Exim - an Internet mail transport agent *
3 *************************************************/
5 /* Copyright (c) Tom Kistner <tom@duncanthrax.net> 2003 - 2015
7 * Copyright (c) The Exim Maintainers 2015 - 2020
10 /* Code for calling virus (malware) scanners. Called from acl.c. */
13 #ifdef WITH_CONTENT_SCAN /* entire file */
16 #ifndef DISABLE_MAL_FFROTD
19 #ifndef DISABLE_MAL_FFROT6D
22 #ifndef DISABLE_MAL_DRWEB
25 #ifndef DISABLE_MAL_AVE
28 #ifndef DISABLE_MAL_FSECURE
31 #ifndef DISABLE_MAL_KAV
34 #ifndef DISABLE_MAL_SOPHIE
37 #ifndef DISABLE_MAL_CLAM
40 #ifndef DISABLE_MAL_MKS
43 #ifndef DISABLE_MAL_AVAST
46 #ifndef DISABLE_MAL_SOCK
49 #ifndef DISABLE_MAL_CMDLINE
54 typedef enum {MC_NONE, MC_TCP, MC_UNIX, MC_STRM} contype_t;
59 const uschar * options_default;
63 #ifndef DISABLE_MAL_FFROTD
64 { M_FPROTD, US"f-protd", US"localhost 10200-10204", MC_TCP },
66 #ifndef DISABLE_MAL_FFROT6D
67 { M_FPROT6D, US"f-prot6d", US"localhost 10200", MC_TCP },
69 #ifndef DISABLE_MAL_DRWEB
70 { M_DRWEB, US"drweb", US"/usr/local/drweb/run/drwebd.sock", MC_STRM },
72 #ifndef DISABLE_MAL_AVE
73 { M_AVES, US"aveserver", US"/var/run/aveserver", MC_UNIX },
75 #ifndef DISABLE_MAL_FSECURE
76 { M_FSEC, US"fsecure", US"/var/run/.fsav", MC_UNIX },
78 #ifndef DISABLE_MAL_KAV
79 { M_KAVD, US"kavdaemon", US"/var/run/AvpCtl", MC_UNIX },
81 #ifndef DISABLE_MAL_SOPHIE
82 { M_SOPHIE, US"sophie", US"/var/run/sophie", MC_UNIX },
84 #ifndef DISABLE_MAL_CLAM
85 { M_CLAMD, US"clamd", US"/tmp/clamd", MC_NONE },
87 #ifndef DISABLE_MAL_MKS
88 { M_MKSD, US"mksd", NULL, MC_NONE },
90 #ifndef DISABLE_MAL_AVAST
91 { M_AVAST, US"avast", US"/var/run/avast/scan.sock", MC_STRM },
93 #ifndef DISABLE_MAL_SOCK
94 { M_SOCK, US"sock", US"/tmp/malware.sock", MC_STRM },
96 #ifndef DISABLE_MAL_CMDLINE
97 { M_CMDL, US"cmdline", NULL, MC_NONE },
99 { -1, NULL, NULL, MC_NONE } /* end-marker */
102 /******************************************************************************/
103 # ifdef MACRO_PREDEF /* build solely to predefine macros */
105 # include "macro_predef.h"
108 features_malware(void)
112 uschar buf[EXIM_DRIVERNAME_MAX];
114 spf(buf, sizeof(buf), US"_HAVE_MALWARE_");
116 for (const struct scan * sc = m_scans; sc->scancode != -1; sc++)
118 for (s = sc->name, t = buf+14; *s; s++) if (*s != '-')
121 builtin_macro_create(buf);
125 /******************************************************************************/
126 # else /*!MACRO_PREDEF, main build*/
129 #define MALWARE_TIMEOUT 120 /* default timeout, seconds */
131 static const uschar * malware_regex_default = US ".+";
132 static const 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. */
281 struct timeval tv = {.tv_sec = 5};
283 FD_ZERO(&fds); FD_SET(fd, &fds); (void) select(fd+1, NULL, &fds, NULL, &tv);
291 m_sock_send(int sock, uschar * buf, int cnt, uschar ** errstr)
293 if (send(sock, buf, cnt, 0) < 0)
297 *errstr = string_sprintf("unable to send to socket (%s): %s",
304 static const pcre2_code *
305 m_pcre_compile(const uschar * re, uschar ** errstr)
309 const pcre2_code * cre;
311 if (!(cre = pcre2_compile((PCRE2_SPTR)re, PCRE2_ZERO_TERMINATED,
312 PCRE_COPT, &err, &roffset, pcre_cmp_ctx)))
315 pcre2_get_error_message(err, errbuf, sizeof(errbuf));
316 *errstr= string_sprintf("regular expression error in '%s': %s at offset %ld",
317 re, errbuf, (long)roffset);
323 m_pcre_exec(const pcre2_code * cre, uschar * text)
325 pcre2_match_data * md = pcre2_match_data_create(2, pcre_gen_ctx);
326 int i = pcre2_match(cre, text, PCRE2_ZERO_TERMINATED, 0, 0, md, pcre_mtc_ctx);
327 PCRE2_UCHAR * substr = NULL;
330 if (i >= 2) /* Got it */
331 pcre2_substring_get_bynumber(md, 1, &substr, &slen);
335 static const pcre2_code *
336 m_pcre_nextinlist(const uschar ** list, int * sep,
337 char * listerr, uschar ** errstr)
339 const uschar * list_ele;
340 const pcre2_code * cre = NULL;
342 if (!(list_ele = string_nextinlist(list, sep, NULL, 0)))
343 *errstr = US listerr;
346 DEBUG(D_acl) debug_printf_indent("%15s%10s'%s'\n", "", "RE: ",
347 string_printing(list_ele));
348 cre = m_pcre_compile(CUS list_ele, errstr);
355 Simple though inefficient wrapper for reading a line. Drop CRs and the
356 trailing newline. Can return early on buffer full. Null-terminate.
357 Apply initial timeout if no data ready.
359 Return: number of chars - zero for an empty line
361 -2 on timeout or error
364 recv_line(int fd, uschar * buffer, int bsize, time_t tmo)
370 if (!fd_ready(fd, tmo))
373 /*XXX tmo handling assumes we always get a whole line */
376 while ((rcv = read(fd, p, 1)) > 0)
379 if (p-buffer > bsize-2) break;
380 if (*p == '\n') break;
387 debug_printf_indent("Malware scan: read %s (%s)\n",
388 rcv==0 ? "EOF" : "error", strerror(errno));
389 debug_print_socket(fd);
391 return rcv==0 ? -1 : -2;
395 DEBUG(D_acl) debug_printf_indent("Malware scan: read '%s'\n", buffer);
399 /* return TRUE iff size as requested */
400 #ifndef DISABLE_MAL_DRWEB
402 recv_len(int sock, void * buf, int size, time_t tmo)
404 return fd_ready(sock, tmo)
405 ? recv(sock, buf, size, 0) == size
412 #ifndef DISABLE_MAL_MKS
413 /* ============= private routines for the "mksd" scanner type ============== */
415 # include <sys/uio.h>
418 mksd_writev (int sock, struct iovec * iov, int iovcnt)
425 i = writev (sock, iov, iovcnt);
426 while (i < 0 && errno == EINTR);
429 (void) malware_panic_defer(
430 US"unable to write to mksd UNIX socket (/var/run/mksd/socket)");
433 for (;;) /* check for short write */
434 if (i >= iov->iov_len)
444 iov->iov_base = CS iov->iov_base + i;
451 mksd_read_lines (int sock, uschar *av_buffer, int av_buffer_size, time_t tmo)
453 client_conn_ctx cctx = {.sock = sock};
459 i = ip_recv(&cctx, av_buffer+offset, av_buffer_size-offset, tmo);
462 (void) malware_panic_defer(US"unable to read from mksd UNIX socket (/var/run/mksd/socket)");
467 /* offset == av_buffer_size -> buffer full */
468 if (offset == av_buffer_size)
470 (void) malware_panic_defer(US"malformed reply received from mksd");
473 } while (av_buffer[offset-1] != '\n');
475 av_buffer[offset] = '\0';
480 mksd_parse_line(struct scan * scanent, char * line)
491 if ((p = strchr (line, '\n')) != NULL)
493 return m_panic_defer(scanent, NULL,
494 string_sprintf("scanner failed: %s", line));
497 if ((p = strchr (line, '\n')) != NULL)
502 && (p = strchr(line+4, ' ')) != NULL
507 malware_name = string_copy(US line+4);
511 return m_panic_defer(scanent, NULL,
512 string_sprintf("malformed reply received: %s", line));
517 mksd_scan_packed(struct scan * scanent, int sock, const uschar * scan_filename,
521 const char *cmd = "MSQ\n";
522 uschar av_buffer[1024];
524 iov[0].iov_base = (void *) cmd;
526 iov[1].iov_base = (void *) scan_filename;
527 iov[1].iov_len = Ustrlen(scan_filename);
528 iov[2].iov_base = (void *) (cmd + 3);
531 if (mksd_writev (sock, iov, 3) < 0)
534 if (mksd_read_lines (sock, av_buffer, sizeof (av_buffer), tmo) < 0)
537 return mksd_parse_line (scanent, CS av_buffer);
542 #ifndef DISABLE_MAL_CLAM
544 clamd_option(clamd_address * cd, const uschar * optstr, int * subsep)
549 while ((s = string_nextinlist(&optstr, subsep, NULL, 0)))
550 if (Ustrncmp(s, "retry=", 6) == 0)
552 int sec = readconf_readtime((s += 6), '\0', FALSE);
565 /*************************************************
566 * Scan content for malware *
567 *************************************************/
569 /* This is an internal interface for scanning an email; the normal interface
570 is via malware(), or there's malware_in_file() used for testing/debugging.
573 malware_re match condition for "malware="
574 scan_filename the file holding the email to be scanned, if we're faking
575 this up for the -bmalware test, else NULL
576 timeout if nonzero, non-default timeoutl
578 Returns: Exim message processing code (OK, FAIL, DEFER, ...)
579 where true means malware was found (condition applies)
582 malware_internal(const uschar * malware_re, const uschar * scan_filename,
586 const uschar *av_scanner_work = av_scanner;
587 uschar *scanner_name;
588 unsigned long mbox_size;
590 const pcre2_code *re;
592 struct scan * scanent;
593 const uschar * scanner_options;
594 client_conn_ctx malware_daemon_ctx = {.sock = -1};
596 uschar * eml_filename, * eml_dir;
599 return FAIL; /* empty means "don't match anything" */
601 /* Ensure the eml mbox file is spooled up */
603 if (!(mbox_file = spool_mbox(&mbox_size, scan_filename, &eml_filename)))
604 return malware_panic_defer(US"error while creating mbox spool file");
606 /* None of our current scanners need the mbox file as a stream (they use
607 the name), so we can close it right away. Get the directory too. */
609 (void) fclose(mbox_file);
610 eml_dir = string_copyn(eml_filename, Ustrrchr(eml_filename, '/') - eml_filename);
612 /* parse 1st option */
613 if (strcmpic(malware_re, US"false") == 0 || Ustrcmp(malware_re,"0") == 0)
614 return FAIL; /* explicitly no matching */
616 /* special cases (match anything except empty) */
617 if ( strcmpic(malware_re,US"true") == 0
618 || Ustrcmp(malware_re,"*") == 0
619 || Ustrcmp(malware_re,"1") == 0
622 if ( !malware_default_re
623 && !(malware_default_re = m_pcre_compile(malware_regex_default, &errstr)))
624 return malware_panic_defer(errstr);
625 malware_re = malware_regex_default;
626 re = malware_default_re;
629 /* compile the regex, see if it works */
630 else if (!(re = m_pcre_compile(malware_re, &errstr)))
631 return malware_panic_defer(errstr);
633 /* if av_scanner starts with a dollar, expand it first */
634 if (*av_scanner == '$')
636 if (!(av_scanner_work = expand_string(av_scanner)))
637 return malware_panic_defer(
638 string_sprintf("av_scanner starts with $, but expansion failed: %s",
639 expand_string_message));
642 debug_printf_indent("Expanded av_scanner global: %s\n", av_scanner_work);
643 /* disable result caching in this case */
648 /* Do not scan twice (unless av_scanner is dynamic). */
651 /* find the scanner type from the av_scanner option */
652 if (!(scanner_name = string_nextinlist(&av_scanner_work, &sep, NULL, 0)))
653 return malware_panic_defer(US"av_scanner configuration variable is empty");
654 if (!timeout) timeout = MALWARE_TIMEOUT;
655 tmo = time(NULL) + timeout;
657 for (scanent = m_scans; ; scanent++)
660 return malware_panic_defer(string_sprintf("unknown scanner type '%s'",
662 if (strcmpic(scanner_name, US scanent->name) != 0)
664 DEBUG(D_acl) debug_printf_indent("Malware scan: %s tmo=%s\n",
665 scanner_name, readconf_printtime(timeout));
667 if (!(scanner_options = string_nextinlist(&av_scanner_work, &sep, NULL, 0)))
668 scanner_options = scanent->options_default;
669 if (scanent->conn == MC_NONE)
672 DEBUG(D_acl) debug_printf_indent("%15s%10s%s\n", "", "socket: ", scanner_options);
673 switch(scanent->conn)
676 malware_daemon_ctx.sock = ip_tcpsocket(scanner_options, &errstr, 5, NULL); break;
678 malware_daemon_ctx.sock = ip_unixsocket(scanner_options, &errstr); break;
680 malware_daemon_ctx.sock = ip_streamsocket(scanner_options, &errstr, 5, NULL); break;
682 /* compiler quietening */ break;
684 if (malware_daemon_ctx.sock < 0)
685 return m_panic_defer(scanent, CUS callout_address, errstr);
689 switch (scanent->scancode)
691 #ifndef DISABLE_MAL_FFROTD
692 case M_FPROTD: /* "f-protd" scanner type -------------------------------- */
694 uschar *fp_scan_option;
695 unsigned int detected=0, par_count=0;
696 uschar * scanrequest;
697 uschar buf[32768], *strhelper, *strhelper2;
698 uschar * malware_name_internal = NULL;
701 scanrequest = string_sprintf("GET %s", eml_filename);
703 while ((fp_scan_option = string_nextinlist(&av_scanner_work, &sep,
706 scanrequest = string_sprintf("%s%s%s", scanrequest,
707 par_count ? "%20" : "?", fp_scan_option);
710 scanrequest = string_sprintf("%s HTTP/1.0\r\n\r\n", scanrequest);
711 DEBUG(D_acl) debug_printf_indent("Malware scan: issuing %s: %s\n",
712 scanner_name, scanrequest);
714 /* send scan request */
715 if (m_sock_send(malware_daemon_ctx.sock, scanrequest, Ustrlen(scanrequest)+1, &errstr) < 0)
716 return m_panic_defer(scanent, CUS callout_address, errstr);
718 while ((len = recv_line(malware_daemon_ctx.sock, buf, sizeof(buf), tmo)) >= 0)
721 if (Ustrstr(buf, US"<detected type=\"") != NULL)
723 else if (detected && (strhelper = Ustrstr(buf, US"<name>")))
725 if ((strhelper2 = Ustrstr(buf, US"</name>")) != NULL)
728 malware_name_internal = string_copy(strhelper+6);
731 else if (Ustrstr(buf, US"<summary code=\""))
733 malware_name = Ustrstr(buf, US"<summary code=\"11\">")
734 ? malware_name_internal : NULL;
740 (void)close(malware_daemon_ctx.sock);
747 #ifndef DISABLE_MAL_FFROT6D
748 case M_FPROT6D: /* "f-prot6d" scanner type ----------------------------------- */
753 uschar * scanrequest;
754 uschar av_buffer[1024];
756 if ((!fprot6d_re_virus && !(fprot6d_re_virus = m_pcre_compile(fprot6d_re_virus_str, &errstr)))
757 || (!fprot6d_re_error && !(fprot6d_re_error = m_pcre_compile(fprot6d_re_error_str, &errstr))))
758 return malware_panic_defer(errstr);
760 scanrequest = string_sprintf("SCAN FILE %s\n", eml_filename);
761 DEBUG(D_acl) debug_printf_indent("Malware scan: issuing %s: %s\n",
762 scanner_name, scanrequest);
764 if (m_sock_send(malware_daemon_ctx.sock, scanrequest, Ustrlen(scanrequest), &errstr) < 0)
765 return m_panic_defer(scanent, CUS callout_address, errstr);
767 bread = ip_recv(&malware_daemon_ctx, av_buffer, sizeof(av_buffer), tmo);
770 return m_panic_defer_3(scanent, CUS callout_address,
771 string_sprintf("unable to read from socket (%s)", strerror(errno)),
772 malware_daemon_ctx.sock);
774 if (bread == sizeof(av_buffer))
775 return m_panic_defer_3(scanent, CUS callout_address,
776 US"buffer too small", malware_daemon_ctx.sock);
778 av_buffer[bread] = '\0';
779 linebuffer = string_copy(av_buffer);
781 m_sock_send(malware_daemon_ctx.sock, US"QUIT\n", 5, 0);
783 if ((e = m_pcre_exec(fprot6d_re_error, linebuffer)))
784 return m_panic_defer_3(scanent, CUS callout_address,
785 string_sprintf("scanner reported error (%s)", e), malware_daemon_ctx.sock);
787 if (!(malware_name = m_pcre_exec(fprot6d_re_virus, linebuffer)))
794 #ifndef DISABLE_MAL_DRWEB
795 case M_DRWEB: /* "drweb" scanner type ----------------------------------- */
796 /* v0.1 - added support for tcp sockets */
797 /* v0.0 - initial release -- support for unix sockets */
801 unsigned int fsize_uint;
802 uschar * tmpbuf, *drweb_fbuf;
803 int drweb_rc, drweb_cmd, drweb_flags = 0x0000, drweb_fd,
804 drweb_vnum, drweb_slen, drweb_fin = 0x0000;
806 /* prepare variables */
807 drweb_cmd = htonl(DRWEBD_SCAN_CMD);
808 drweb_flags = htonl(DRWEBD_RETURN_VIRUSES | DRWEBD_IS_MAIL);
810 if (*scanner_options != '/')
813 if ((drweb_fd = exim_open2(CCS eml_filename, O_RDONLY)) == -1)
814 return m_panic_defer_3(scanent, NULL,
815 string_sprintf("can't open spool file %s: %s",
816 eml_filename, strerror(errno)),
817 malware_daemon_ctx.sock);
819 if ((fsize = lseek(drweb_fd, 0, SEEK_END)) == -1)
822 badseek: err = errno;
823 (void)close(drweb_fd);
824 return m_panic_defer_3(scanent, NULL,
825 string_sprintf("can't seek spool file %s: %s",
826 eml_filename, strerror(err)),
827 malware_daemon_ctx.sock);
829 fsize_uint = (unsigned int) fsize;
830 if ((off_t)fsize_uint != fsize)
832 (void)close(drweb_fd);
833 return m_panic_defer_3(scanent, NULL,
834 string_sprintf("seeking spool file %s, size overflow",
836 malware_daemon_ctx.sock);
838 drweb_slen = htonl(fsize);
839 if (lseek(drweb_fd, 0, SEEK_SET) < 0)
842 DEBUG(D_acl) debug_printf_indent("Malware scan: issuing %s remote scan [%s]\n",
843 scanner_name, scanner_options);
845 /* send scan request */
846 if ((send(malware_daemon_ctx.sock, &drweb_cmd, sizeof(drweb_cmd), 0) < 0) ||
847 (send(malware_daemon_ctx.sock, &drweb_flags, sizeof(drweb_flags), 0) < 0) ||
848 (send(malware_daemon_ctx.sock, &drweb_fin, sizeof(drweb_fin), 0) < 0) ||
849 (send(malware_daemon_ctx.sock, &drweb_slen, sizeof(drweb_slen), 0) < 0))
851 (void)close(drweb_fd);
852 return m_panic_defer_3(scanent, CUS callout_address, string_sprintf(
853 "unable to send commands to socket (%s)", scanner_options),
854 malware_daemon_ctx.sock);
857 if (!(drweb_fbuf = store_malloc(fsize_uint)))
859 (void)close(drweb_fd);
860 return m_panic_defer_3(scanent, NULL,
861 string_sprintf("unable to allocate memory %u for file (%s)",
862 fsize_uint, eml_filename),
863 malware_daemon_ctx.sock);
866 if ((result = read (drweb_fd, drweb_fbuf, fsize)) == -1)
869 (void)close(drweb_fd);
870 store_free(drweb_fbuf);
871 return m_panic_defer_3(scanent, NULL,
872 string_sprintf("can't read spool file %s: %s",
873 eml_filename, strerror(err)),
874 malware_daemon_ctx.sock);
876 (void)close(drweb_fd);
878 /* send file body to socket */
879 if (send(malware_daemon_ctx.sock, drweb_fbuf, fsize, 0) < 0)
881 store_free(drweb_fbuf);
882 return m_panic_defer_3(scanent, CUS callout_address, string_sprintf(
883 "unable to send file body to socket (%s)", scanner_options),
884 malware_daemon_ctx.sock);
886 store_free(drweb_fbuf);
890 drweb_slen = htonl(Ustrlen(eml_filename));
892 DEBUG(D_acl) debug_printf_indent("Malware scan: issuing %s local scan [%s]\n",
893 scanner_name, scanner_options);
895 /* send scan request */
896 if ((send(malware_daemon_ctx.sock, &drweb_cmd, sizeof(drweb_cmd), 0) < 0) ||
897 (send(malware_daemon_ctx.sock, &drweb_flags, sizeof(drweb_flags), 0) < 0) ||
898 (send(malware_daemon_ctx.sock, &drweb_slen, sizeof(drweb_slen), 0) < 0) ||
899 (send(malware_daemon_ctx.sock, eml_filename, Ustrlen(eml_filename), 0) < 0) ||
900 (send(malware_daemon_ctx.sock, &drweb_fin, sizeof(drweb_fin), 0) < 0))
901 return m_panic_defer_3(scanent, CUS callout_address, string_sprintf(
902 "unable to send commands to socket (%s)", scanner_options),
903 malware_daemon_ctx.sock);
906 /* wait for result */
907 if (!recv_len(malware_daemon_ctx.sock, &drweb_rc, sizeof(drweb_rc), tmo))
908 return m_panic_defer_3(scanent, CUS callout_address,
909 US"unable to read return code", malware_daemon_ctx.sock);
910 drweb_rc = ntohl(drweb_rc);
912 if (!recv_len(malware_daemon_ctx.sock, &drweb_vnum, sizeof(drweb_vnum), tmo))
913 return m_panic_defer_3(scanent, CUS callout_address,
914 US"unable to read the number of viruses", malware_daemon_ctx.sock);
915 drweb_vnum = ntohl(drweb_vnum);
917 /* "virus(es) found" if virus number is > 0 */
922 /* setup default virus name */
923 malware_name = US"unknown";
925 /* set up match regex */
927 drweb_re = m_pcre_compile(drweb_re_str, &errstr);
929 /* read and concatenate virus names into one string */
930 for (int i = 0; i < drweb_vnum; i++)
932 pcre2_match_data * md = pcre2_match_data_create(2, pcre_gen_ctx);
934 /* read the size of report */
935 if (!recv_len(malware_daemon_ctx.sock, &drweb_slen, sizeof(drweb_slen), tmo))
936 return m_panic_defer_3(scanent, CUS callout_address,
937 US"cannot read report size", malware_daemon_ctx.sock);
938 drweb_slen = ntohl(drweb_slen);
940 /* assume tainted, since it is external input */
941 tmpbuf = store_get(drweb_slen, TRUE);
943 /* read report body */
944 if (!recv_len(malware_daemon_ctx.sock, tmpbuf, drweb_slen, tmo))
945 return m_panic_defer_3(scanent, CUS callout_address,
946 US"cannot read report string", malware_daemon_ctx.sock);
947 tmpbuf[drweb_slen] = '\0';
949 /* try matcher on the line, grab substring */
950 result = pcre2_match(drweb_re, (PCRE2_SPTR)tmpbuf, PCRE2_ZERO_TERMINATED,
951 0, 0, md, pcre_mtc_ctx);
954 PCRE2_SIZE * ovec = pcre2_get_ovector_pointer(md);
956 if (i==0) /* the first name we just copy to malware_name */
957 g = string_catn(NULL, US ovec[2], ovec[3] - ovec[2]);
959 else /* concatenate each new virus name to previous */
961 g = string_catn(g, US"/", 1);
962 g = string_catn(g, US ovec[2], ovec[3] - ovec[2]);
966 malware_name = string_from_gstring(g);
970 const char *drweb_s = NULL;
972 if (drweb_rc & DERR_READ_ERR) drweb_s = "read error";
973 if (drweb_rc & DERR_NOMEMORY) drweb_s = "no memory";
974 if (drweb_rc & DERR_TIMEOUT) drweb_s = "timeout";
975 if (drweb_rc & DERR_BAD_CALL) drweb_s = "wrong command";
976 /* retcodes DERR_SYMLINK, DERR_NO_REGFILE, DERR_SKIPPED.
977 * DERR_TOO_BIG, DERR_TOO_COMPRESSED, DERR_SPAM,
978 * DERR_CRC_ERROR, DERR_READSOCKET, DERR_WRITE_ERR
979 * and others are ignored */
981 return m_panic_defer_3(scanent, CUS callout_address,
982 string_sprintf("drweb daemon retcode 0x%x (%s)", drweb_rc, drweb_s),
983 malware_daemon_ctx.sock);
992 #ifndef DISABLE_MAL_AVE
993 case M_AVES: /* "aveserver" scanner type -------------------------------- */
998 /* read aveserver's greeting and see if it is ready (2xx greeting) */
1000 recv_line(malware_daemon_ctx.sock, buf, sizeof(buf), tmo);
1002 if (buf[0] != '2') /* aveserver is having problems */
1003 return m_panic_defer_3(scanent, CUS callout_address,
1004 string_sprintf("unavailable (Responded: %s).",
1005 ((buf[0] != 0) ? buf : US "nothing") ),
1006 malware_daemon_ctx.sock);
1008 /* prepare our command */
1009 (void)string_format(buf, sizeof(buf), "SCAN bPQRSTUW %s\r\n",
1013 DEBUG(D_acl) debug_printf_indent("Malware scan: issuing %s %s\n",
1015 if (m_sock_send(malware_daemon_ctx.sock, buf, Ustrlen(buf), &errstr) < 0)
1016 return m_panic_defer(scanent, CUS callout_address, errstr);
1018 malware_name = NULL;
1020 /* read response lines, find malware name and final response */
1021 while (recv_line(malware_daemon_ctx.sock, buf, sizeof(buf), tmo) > 0)
1025 if (buf[0] == '5') /* aveserver is having problems */
1027 result = m_panic_defer(scanent, CUS callout_address,
1028 string_sprintf("unable to scan file %s (Responded: %s).",
1029 eml_filename, buf));
1032 if (Ustrncmp(buf,"322",3) == 0)
1034 uschar *p = Ustrchr(&buf[4], ' ');
1036 malware_name = string_copy(&buf[4]);
1040 if (m_sock_send(malware_daemon_ctx.sock, US"quit\r\n", 6, &errstr) < 0)
1041 return m_panic_defer(scanent, CUS callout_address, errstr);
1043 /* read aveserver's greeting and see if it is ready (2xx greeting) */
1045 recv_line(malware_daemon_ctx.sock, buf, sizeof(buf), tmo);
1047 if (buf[0] != '2') /* aveserver is having problems */
1048 return m_panic_defer_3(scanent, CUS callout_address,
1049 string_sprintf("unable to quit dialogue (Responded: %s).",
1050 ((buf[0] != 0) ? buf : US "nothing") ),
1051 malware_daemon_ctx.sock);
1053 if (result == DEFER)
1055 (void)close(malware_daemon_ctx.sock);
1062 #ifndef DISABLE_MAL_FSECURE
1063 case M_FSEC: /* "fsecure" scanner type ---------------------------------- */
1067 uschar av_buffer[1024];
1068 static uschar *cmdopt[] = { US"CONFIGURE\tARCHIVE\t1\n",
1069 US"CONFIGURE\tTIMEOUT\t0\n",
1070 US"CONFIGURE\tMAXARCH\t5\n",
1071 US"CONFIGURE\tMIME\t1\n" };
1073 malware_name = NULL;
1075 DEBUG(D_acl) debug_printf_indent("Malware scan: issuing %s scan [%s]\n",
1076 scanner_name, scanner_options);
1078 memset(av_buffer, 0, sizeof(av_buffer));
1079 for (i = 0; i != nelem(cmdopt); i++)
1082 if (m_sock_send(malware_daemon_ctx.sock, cmdopt[i], Ustrlen(cmdopt[i]), &errstr) < 0)
1083 return m_panic_defer(scanent, CUS callout_address, errstr);
1085 bread = ip_recv(&malware_daemon_ctx, av_buffer, sizeof(av_buffer), tmo);
1086 if (bread > 0) av_buffer[bread]='\0';
1088 return m_panic_defer_3(scanent, CUS callout_address,
1089 string_sprintf("unable to read answer %d (%s)", i, strerror(errno)),
1090 malware_daemon_ctx.sock);
1091 for (int j = 0; j < bread; j++)
1092 if (av_buffer[j] == '\r' || av_buffer[j] == '\n')
1096 /* pass the mailfile to fsecure */
1097 file_name = string_sprintf("SCAN\t%s\n", eml_filename);
1099 if (m_sock_send(malware_daemon_ctx.sock, file_name, Ustrlen(file_name), &errstr) < 0)
1100 return m_panic_defer(scanent, CUS callout_address, errstr);
1103 /* todo also SUSPICION\t */
1105 fsec_re = m_pcre_compile(fsec_re_str, &errstr);
1107 /* read report, linewise. Apply a timeout as the Fsecure daemon
1108 sometimes wants an answer to "PING" but they won't tell us what */
1110 uschar * p = av_buffer;
1116 i = av_buffer+sizeof(av_buffer)-p;
1117 if ((bread= ip_recv(&malware_daemon_ctx, p, i-1, tmo)) < 0)
1118 return m_panic_defer_3(scanent, CUS callout_address,
1119 string_sprintf("unable to read result (%s)", strerror(errno)),
1120 malware_daemon_ctx.sock);
1122 for (p[bread] = '\0'; (q = Ustrchr(p, '\n')); p = q+1)
1126 /* Really search for virus again? */
1128 /* try matcher on the line, grab substring */
1129 malware_name = m_pcre_exec(fsec_re, p);
1131 if (Ustrstr(p, "OK\tScan ok."))
1135 /* copy down the trailing partial line then read another chunk */
1136 i = av_buffer+sizeof(av_buffer)-p;
1137 memmove(av_buffer, p, i);
1147 #ifndef DISABLE_MAL_KAV
1148 case M_KAVD: /* "kavdaemon" scanner type -------------------------------- */
1151 uschar tmpbuf[1024];
1152 uschar * scanrequest;
1154 unsigned long kav_reportlen;
1156 const pcre2_code *kav_re;
1159 /* get current date and time, build scan request */
1161 /* pdp note: before the eml_filename parameter, this scanned the
1162 directory; not finding documentation, so we'll strip off the directory.
1163 The side-effect is that the test framework scanning may end up in
1164 scanning more than was requested, but for the normal interface, this is
1167 strftime(CS tmpbuf, sizeof(tmpbuf), "%d %b %H:%M:%S", localtime(&t));
1168 scanrequest = string_sprintf("<0>%s:%s", CS tmpbuf, eml_filename);
1169 p = Ustrrchr(scanrequest, '/');
1173 DEBUG(D_acl) debug_printf_indent("Malware scan: issuing %s scan [%s]\n",
1174 scanner_name, scanner_options);
1176 /* send scan request */
1177 if (m_sock_send(malware_daemon_ctx.sock, scanrequest, Ustrlen(scanrequest)+1, &errstr) < 0)
1178 return m_panic_defer(scanent, CUS callout_address, errstr);
1180 /* wait for result */
1181 if (!recv_len(malware_daemon_ctx.sock, tmpbuf, 2, tmo))
1182 return m_panic_defer_3(scanent, CUS callout_address,
1183 US"unable to read 2 bytes from socket.", malware_daemon_ctx.sock);
1185 /* get errorcode from one nibble */
1186 kav_rc = tmpbuf[ test_byte_order()==LITTLE_MY_ENDIAN ? 0 : 1 ] & 0x0F;
1189 case 5: case 6: /* improper kavdaemon configuration */
1190 return m_panic_defer_3(scanent, CUS callout_address,
1191 US"please reconfigure kavdaemon to NOT disinfect or remove infected files.",
1192 malware_daemon_ctx.sock);
1194 return m_panic_defer_3(scanent, CUS callout_address,
1195 US"reported 'scanning not completed' (code 1).", malware_daemon_ctx.sock);
1197 return m_panic_defer_3(scanent, CUS callout_address,
1198 US"reported 'kavdaemon damaged' (code 7).", malware_daemon_ctx.sock);
1201 /* code 8 is not handled, since it is ambiguous. It appears mostly on
1202 bounces where part of a file has been cut off */
1204 /* "virus found" return codes (2-4) */
1205 if (kav_rc > 1 && kav_rc < 5)
1207 int report_flag = 0;
1209 /* setup default virus name */
1210 malware_name = US"unknown";
1212 report_flag = tmpbuf[ test_byte_order() == LITTLE_MY_ENDIAN ? 1 : 0 ];
1214 /* read the report, if available */
1215 if (report_flag == 1)
1217 /* read report size */
1218 if (!recv_len(malware_daemon_ctx.sock, &kav_reportlen, 4, tmo))
1219 return m_panic_defer_3(scanent, CUS callout_address,
1220 US"cannot read report size", malware_daemon_ctx.sock);
1222 /* it's possible that avp returns av_buffer[1] == 1 but the
1223 reportsize is 0 (!?) */
1224 if (kav_reportlen > 0)
1226 /* set up match regex, depends on retcode */
1229 if (!kav_re_sus) kav_re_sus = m_pcre_compile(kav_re_sus_str, &errstr);
1230 kav_re = kav_re_sus;
1234 if (!kav_re_inf) kav_re_inf = m_pcre_compile(kav_re_inf_str, &errstr);
1235 kav_re = kav_re_inf;
1238 /* read report, linewise. Using size from stream to read amount of data
1239 from same stream is safe enough. */
1240 /* coverity[tainted_data] */
1241 while (kav_reportlen > 0)
1243 if ((bread = recv_line(malware_daemon_ctx.sock, tmpbuf, sizeof(tmpbuf), tmo)) < 0)
1245 kav_reportlen -= bread+1;
1247 /* try matcher on the line, grab substring */
1248 if ((malware_name = m_pcre_exec(kav_re, tmpbuf)))
1254 else /* no virus found */
1255 malware_name = NULL;
1261 #ifndef DISABLE_MAL_CMDLINE
1262 case M_CMDL: /* "cmdline" scanner type ---------------------------------- */
1264 const uschar *cmdline_scanner = scanner_options;
1265 const pcre2_code *cmdline_trigger_re;
1266 const pcre2_code *cmdline_regex_re;
1268 uschar * commandline;
1269 void (*eximsigchld)(int);
1270 void (*eximsigpipe)(int);
1271 FILE *scanner_out = NULL;
1273 FILE *scanner_record = NULL;
1274 uschar linebuffer[32767];
1279 if (!cmdline_scanner)
1280 return m_panic_defer(scanent, NULL, errstr);
1282 /* find scanner output trigger */
1283 cmdline_trigger_re = m_pcre_nextinlist(&av_scanner_work, &sep,
1284 "missing trigger specification", &errstr);
1285 if (!cmdline_trigger_re)
1286 return m_panic_defer(scanent, NULL, errstr);
1288 /* find scanner name regex */
1289 cmdline_regex_re = m_pcre_nextinlist(&av_scanner_work, &sep,
1290 "missing virus name regex specification", &errstr);
1291 if (!cmdline_regex_re)
1292 return m_panic_defer(scanent, NULL, errstr);
1294 /* prepare scanner call; despite the naming, file_name holds a directory
1295 name which is documented as the value given to %s. */
1297 file_name = string_copy(eml_filename);
1298 p = Ustrrchr(file_name, '/');
1301 commandline = string_sprintf(CS cmdline_scanner, file_name);
1303 /* redirect STDERR too */
1304 commandline = string_sprintf("%s 2>&1", commandline);
1306 DEBUG(D_acl) debug_printf_indent("Malware scan: issuing %s scan [%s]\n",
1307 scanner_name, commandline);
1309 /* store exims signal handlers */
1310 eximsigchld = signal(SIGCHLD,SIG_DFL);
1311 eximsigpipe = signal(SIGPIPE,SIG_DFL);
1313 if (!(scanner_out = popen(CS commandline,"r")))
1316 signal(SIGCHLD,eximsigchld); signal(SIGPIPE,eximsigpipe);
1317 return m_panic_defer(scanent, NULL,
1318 string_sprintf("call (%s) failed: %s.", commandline, strerror(err)));
1320 scanner_fd = fileno(scanner_out);
1322 file_name = string_sprintf("%s/%s_scanner_output", eml_dir, message_id);
1324 if (!(scanner_record = modefopen(file_name, "wb", SPOOL_MODE)))
1327 (void) pclose(scanner_out);
1328 signal(SIGCHLD,eximsigchld); signal(SIGPIPE,eximsigpipe);
1329 return m_panic_defer(scanent, NULL, string_sprintf(
1330 "opening scanner output file (%s) failed: %s.",
1331 file_name, strerror(err)));
1334 /* look for trigger while recording output */
1335 while ((rcnt = recv_line(scanner_fd, linebuffer,
1336 sizeof(linebuffer), tmo)))
1343 (void) pclose(scanner_out);
1344 signal(SIGCHLD,eximsigchld); signal(SIGPIPE,eximsigpipe);
1345 return m_panic_defer(scanent, NULL, string_sprintf(
1346 "unable to read from scanner (%s): %s",
1347 commandline, strerror(err)));
1350 if (Ustrlen(linebuffer) > fwrite(linebuffer, 1, Ustrlen(linebuffer), scanner_record))
1353 (void) pclose(scanner_out);
1354 signal(SIGCHLD,eximsigchld); signal(SIGPIPE,eximsigpipe);
1355 return m_panic_defer(scanent, NULL, string_sprintf(
1356 "short write on scanner output file (%s).", file_name));
1358 putc('\n', scanner_record);
1359 /* try trigger match */
1361 && regex_match_and_setup(cmdline_trigger_re, linebuffer, 0, -1)
1366 (void)fclose(scanner_record);
1367 sep = pclose(scanner_out);
1368 signal(SIGCHLD,eximsigchld); signal(SIGPIPE,eximsigpipe);
1370 return m_panic_defer(scanent, NULL,
1372 ? string_sprintf("running scanner failed: %s", strerror(sep))
1373 : string_sprintf("scanner returned error code: %d", sep));
1378 /* setup default virus name */
1379 malware_name = US"unknown";
1381 /* re-open the scanner output file, look for name match */
1382 scanner_record = Ufopen(file_name, "rb");
1383 while (Ufgets(linebuffer, sizeof(linebuffer), scanner_record))
1384 if ((s = m_pcre_exec(cmdline_regex_re, linebuffer))) /* try match */
1386 (void)fclose(scanner_record);
1388 else /* no virus found */
1389 malware_name = NULL;
1394 #ifndef DISABLE_MAL_SOPHIE
1395 case M_SOPHIE: /* "sophie" scanner type --------------------------------- */
1400 uschar av_buffer[1024];
1402 /* pass the scan directory to sophie */
1403 file_name = string_copy(eml_filename);
1404 if ((p = Ustrrchr(file_name, '/')))
1407 DEBUG(D_acl) debug_printf_indent("Malware scan: issuing %s scan [%s]\n",
1408 scanner_name, scanner_options);
1410 if ( write(malware_daemon_ctx.sock, file_name, Ustrlen(file_name)) < 0
1411 || write(malware_daemon_ctx.sock, "\n", 1) != 1
1413 return m_panic_defer_3(scanent, CUS callout_address,
1414 string_sprintf("unable to write to UNIX socket (%s)", scanner_options),
1415 malware_daemon_ctx.sock);
1417 /* wait for result */
1418 memset(av_buffer, 0, sizeof(av_buffer));
1419 if ((bread = ip_recv(&malware_daemon_ctx, av_buffer, sizeof(av_buffer), tmo)) <= 0)
1420 return m_panic_defer_3(scanent, CUS callout_address,
1421 string_sprintf("unable to read from UNIX socket (%s)", scanner_options),
1422 malware_daemon_ctx.sock);
1425 if (av_buffer[0] == '1') {
1426 uschar * s = Ustrchr(av_buffer, '\n');
1429 malware_name = string_copy(&av_buffer[2]);
1431 else if (!strncmp(CS av_buffer, "-1", 2))
1432 return m_panic_defer_3(scanent, CUS callout_address,
1433 US"scanner reported error", malware_daemon_ctx.sock);
1434 else /* all ok, no virus */
1435 malware_name = NULL;
1441 #ifndef DISABLE_MAL_CLAM
1442 case M_CLAMD: /* "clamd" scanner type ----------------------------------- */
1444 /* This code was originally contributed by David Saez */
1445 /* There are three scanning methods available to us:
1446 * (1) Use the SCAN command, pointing to a file in the filesystem
1447 * (2) Use the STREAM command, send the data on a separate port
1448 * (3) Use the zINSTREAM command, send the data inline
1449 * The zINSTREAM command was introduced with ClamAV 0.95, which marked
1450 * STREAM deprecated; see: http://wiki.clamav.net/bin/view/Main/UpgradeNotes095
1451 * In Exim, we use SCAN if using a Unix-domain socket or explicitly told that
1452 * the TCP-connected daemon is actually local; otherwise we use zINSTREAM
1453 * See Exim bug 926 for details. */
1455 uschar *p, *vname, *result_tag;
1457 uschar av_buffer[1024];
1458 uschar *hostname = US"";
1460 int clam_fd, result;
1461 unsigned int fsize_uint;
1462 BOOL use_scan_command = FALSE;
1463 clamd_address * cv[MAX_CLAMD_SERVERS];
1464 int num_servers = 0;
1465 uint32_t send_size, send_final_zeroblock;
1468 /*XXX if unixdomain socket, only one server supported. Needs fixing;
1469 there's no reason we should not mix local and remote servers */
1471 if (*scanner_options == '/')
1474 const uschar * sublist;
1477 /* Local file; so we def want to use_scan_command and don't want to try
1478 * passing IP/port combinations */
1479 use_scan_command = TRUE;
1480 cd = (clamd_address *) store_get(sizeof(clamd_address), FALSE);
1482 /* extract socket-path part */
1483 sublist = scanner_options;
1484 cd->hostspec = string_nextinlist(&sublist, &subsep, NULL, 0);
1487 if (clamd_option(cd, sublist, &subsep) != OK)
1488 return m_panic_defer(scanent, NULL,
1489 string_sprintf("bad option '%s'", scanner_options));
1494 /* Go through the rest of the list of host/port and construct an array
1495 * of servers to try. The first one is the bit we just passed from
1496 * scanner_options so process that first and then scan the remainder of
1497 * the address buffer */
1501 const uschar * sublist;
1505 /* The 'local' option means use the SCAN command over the network
1506 * socket (ie common file storage in use) */
1507 /*XXX we could accept this also as a local option? */
1508 if (strcmpic(scanner_options, US"local") == 0)
1510 use_scan_command = TRUE;
1514 cd = (clamd_address *) store_get(sizeof(clamd_address), FALSE);
1516 /* extract host and port part */
1517 sublist = scanner_options;
1518 if (!(cd->hostspec = string_nextinlist(&sublist, &subsep, NULL, 0)))
1520 (void) m_panic_defer(scanent, NULL,
1521 string_sprintf("missing address: '%s'", scanner_options));
1524 if (!(s = string_nextinlist(&sublist, &subsep, NULL, 0)))
1526 (void) m_panic_defer(scanent, NULL,
1527 string_sprintf("missing port: '%s'", scanner_options));
1530 cd->tcp_port = atoi(CS s);
1533 /*XXX should these options be common over scanner types? */
1534 if (clamd_option(cd, sublist, &subsep) != OK)
1535 return m_panic_defer(scanent, NULL,
1536 string_sprintf("bad option '%s'", scanner_options));
1538 cv[num_servers++] = cd;
1539 if (num_servers >= MAX_CLAMD_SERVERS)
1541 (void) m_panic_defer(scanent, NULL,
1542 US"More than " MAX_CLAMD_SERVERS_S " clamd servers "
1543 "specified; only using the first " MAX_CLAMD_SERVERS_S );
1546 } while ((scanner_options = string_nextinlist(&av_scanner_work, &sep,
1549 /* check if we have at least one server */
1551 return m_panic_defer(scanent, NULL,
1552 US"no useable server addresses in malware configuration option.");
1555 /* See the discussion of response formats below to see why we really
1556 don't like colons in filenames when passing filenames to ClamAV. */
1557 if (use_scan_command && Ustrchr(eml_filename, ':'))
1558 return m_panic_defer(scanent, NULL,
1559 string_sprintf("local/SCAN mode incompatible with" \
1560 " : in path to email filename [%s]", eml_filename));
1562 /* Set up the very first data we will be sending */
1563 if (!use_scan_command)
1564 { cmd_str.data = US"zINSTREAM"; cmd_str.len = 10; }
1566 cmd_str.data = string_sprintf("SCAN %s\n%n", eml_filename, &cmd_str.len);
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*/