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; }
1568 cmd_str.data = string_sprintf("SCAN %s\n%n", eml_filename, &n);
1569 cmd_str.len = n; /* .len is a size_t */
1572 /* We have some network servers specified */
1575 /* Confirmed in ClamAV source (0.95.3) that the TCPAddr option of clamd
1576 only supports AF_INET, but we should probably be looking to the
1577 future and rewriting this to be protocol-independent anyway. */
1579 while (num_servers > 0)
1581 int i = random_number(num_servers);
1582 clamd_address * cd = cv[i];
1584 DEBUG(D_acl) debug_printf_indent("trying server name %s, port %u\n",
1585 cd->hostspec, cd->tcp_port);
1587 /* Lookup the host. This is to ensure that we connect to the same IP
1588 on both connections (as one host could resolve to multiple ips) */
1591 /*XXX we trust that the cmd_str is idempotent */
1592 if ((malware_daemon_ctx.sock = m_tcpsocket(cd->hostspec, cd->tcp_port,
1594 use_scan_command ? &cmd_str : NULL)) >= 0)
1596 /* Connection successfully established with a server */
1597 hostname = cd->hostspec;
1598 if (use_scan_command) cmd_str.len = 0;
1601 if (cd->retry <= 0) break;
1602 while (cd->retry > 0) cd->retry = sleep(cd->retry);
1604 if (malware_daemon_ctx.sock >= 0)
1607 (void) m_panic_defer(scanent, CUS callout_address, errstr);
1609 /* Remove the server from the list. XXX We should free the memory */
1611 for (; i < num_servers; i++)
1615 if (num_servers == 0)
1616 return m_panic_defer(scanent, NULL, US"all servers failed");
1621 if ((malware_daemon_ctx.sock = ip_unixsocket(cv[0]->hostspec, &errstr)) >= 0)
1623 hostname = cv[0]->hostspec;
1626 if (cv[0]->retry <= 0)
1627 return m_panic_defer(scanent, CUS callout_address, errstr);
1628 while (cv[0]->retry > 0) cv[0]->retry = sleep(cv[0]->retry);
1631 /* have socket in variable "sock"; command to use is semi-independent of
1632 the socket protocol. We use SCAN if is local (either Unix/local
1633 domain socket, or explicitly told local) else we stream the data.
1634 How we stream the data depends upon how we were built. */
1636 if (!use_scan_command)
1639 #if defined(EXIM_TCP_CORK) && !defined(OS_SENDFILE)
1642 /* New protocol: "zINSTREAM\n" followed by a sequence of <length><data>
1643 chunks, <n> a 4-byte number (network order), terminated by a zero-length
1644 chunk. We only send one chunk. */
1646 DEBUG(D_acl) debug_printf_indent(
1647 "Malware scan: issuing %s new-style remote scan (zINSTREAM)\n",
1650 #if defined(EXIM_TCP_CORK)
1651 (void) setsockopt(malware_daemon_ctx.sock, IPPROTO_TCP, EXIM_TCP_CORK,
1652 US &on, sizeof(on));
1654 /* Pass the string to ClamAV (10 = "zINSTREAM\0"), if not already sent */
1656 if (send(malware_daemon_ctx.sock, cmd_str.data, cmd_str.len, 0) < 0)
1657 return m_panic_defer_3(scanent, CUS hostname,
1658 string_sprintf("unable to send zINSTREAM to socket (%s)",
1660 malware_daemon_ctx.sock);
1662 if ((clam_fd = exim_open2(CS eml_filename, O_RDONLY)) < 0)
1665 return m_panic_defer_3(scanent, NULL,
1666 string_sprintf("can't open spool file %s: %s",
1667 eml_filename, strerror(err)),
1668 malware_daemon_ctx.sock);
1670 if (fstat(clam_fd, &st) < 0)
1673 (void)close(clam_fd);
1674 return m_panic_defer_3(scanent, NULL,
1675 string_sprintf("can't stat spool file %s: %s",
1676 eml_filename, strerror(err)),
1677 malware_daemon_ctx.sock);
1679 fsize_uint = (unsigned int) st.st_size;
1680 if ((off_t)fsize_uint != st.st_size)
1682 (void)close(clam_fd);
1683 return m_panic_defer_3(scanent, NULL,
1684 string_sprintf("stat spool file %s, size overflow", eml_filename),
1685 malware_daemon_ctx.sock);
1688 /* send file size */
1689 send_size = htonl(fsize_uint);
1690 if (send(malware_daemon_ctx.sock, &send_size, sizeof(send_size), 0) < 0)
1691 return m_panic_defer_3(scanent, NULL,
1692 string_sprintf("unable to send file size to socket (%s)", hostname),
1693 malware_daemon_ctx.sock);
1695 /* send file body */
1699 int n = os_sendfile(malware_daemon_ctx.sock, clam_fd, NULL, (size_t)fsize_uint);
1701 return m_panic_defer_3(scanent, NULL,
1702 string_sprintf("unable to send file body to socket (%s): %s", hostname, strerror(errno)),
1703 malware_daemon_ctx.sock);
1706 int n = MIN(fsize_uint, big_buffer_size);
1707 if ((n = read(clam_fd, big_buffer, n)) < 0)
1708 return m_panic_defer_3(scanent, NULL,
1709 string_sprintf("can't read spool file %s: %s",
1710 eml_filename, strerror(errno)),
1711 malware_daemon_ctx.sock);
1712 if (send(malware_daemon_ctx.sock, big_buffer, (size_t)n, 0) < 0)
1713 return m_panic_defer_3(scanent, NULL,
1714 string_sprintf("unable to send file body to socket (%s): %s", hostname, strerror(errno)),
1715 malware_daemon_ctx.sock);
1717 # ifdef EXIM_TCP_CORK
1721 (void) setsockopt(malware_daemon_ctx.sock, IPPROTO_TCP, EXIM_TCP_CORK,
1722 US &off, sizeof(off));
1725 #endif /*!OS_SENDFILE*/
1729 send_final_zeroblock = 0;
1730 if (send(malware_daemon_ctx.sock, &send_final_zeroblock, sizeof(send_final_zeroblock), 0) < 0)
1731 return m_panic_defer_3(scanent, NULL,
1732 string_sprintf("unable to send file terminator to socket (%s)", hostname),
1733 malware_daemon_ctx.sock);
1735 (void) setsockopt(malware_daemon_ctx.sock, IPPROTO_TCP, EXIM_TCP_CORK,
1736 US &off, sizeof(off));
1740 { /* use scan command */
1741 /* Send a SCAN command pointing to a filename; then in the then in the
1742 scan-method-neutral part, read the response back */
1744 /* ================================================================= */
1746 /* Prior to the reworking post-Exim-4.72, this scanned a directory,
1747 which dates to when ClamAV needed us to break apart the email into the
1748 MIME parts (eg, with the now deprecated demime condition coming first).
1749 Some time back, ClamAV gained the ability to deconstruct the emails, so
1750 doing this would actually have resulted in the mail attachments being
1751 scanned twice, in the broken out files and from the original .eml.
1752 Since ClamAV now handles emails (and has for quite some time) we can
1753 just use the email file itself. */
1754 /* Pass the string to ClamAV (7 = "SCAN \n" + \0), if not already sent */
1756 DEBUG(D_acl) debug_printf_indent(
1757 "Malware scan: issuing %s local-path scan [%s]\n",
1758 scanner_name, scanner_options);
1761 if (send(malware_daemon_ctx.sock, cmd_str.data, cmd_str.len, 0) < 0)
1762 return m_panic_defer_3(scanent, CUS callout_address,
1763 string_sprintf("unable to write to socket (%s)", strerror(errno)),
1764 malware_daemon_ctx.sock);
1766 /* Do not shut down the socket for writing; a user report noted that
1767 clamd 0.70 does not react well to this. */
1769 /* Commands have been sent, no matter which scan method or connection
1770 type we're using; now just read the result, independent of method. */
1772 /* Read the result */
1773 memset(av_buffer, 0, sizeof(av_buffer));
1774 bread = ip_recv(&malware_daemon_ctx, av_buffer, sizeof(av_buffer), tmo);
1775 (void)close(malware_daemon_ctx.sock);
1776 malware_daemon_ctx.sock = -1;
1777 malware_daemon_ctx.tls_ctx = NULL;
1780 return m_panic_defer(scanent, CUS callout_address,
1781 string_sprintf("unable to read from socket (%s)",
1782 errno == 0 ? "EOF" : strerror(errno)));
1784 if (bread == sizeof(av_buffer))
1785 return m_panic_defer(scanent, CUS callout_address,
1786 US"buffer too small");
1787 /* We're now assured of a NULL at the end of av_buffer */
1789 /* Check the result. ClamAV returns one of two result formats.
1790 In the basic mode, the response is of the form:
1791 infected: -> "<filename>: <virusname> FOUND"
1792 not-infected: -> "<filename>: OK"
1793 error: -> "<filename>: <errcode> ERROR
1794 If the ExtendedDetectionInfo option has been turned on, then we get:
1795 "<filename>: <virusname>(<virushash>:<virussize>) FOUND"
1796 for the infected case. Compare:
1797 /tmp/eicar.com: Eicar-Test-Signature FOUND
1798 /tmp/eicar.com: Eicar-Test-Signature(44d88612fea8a8f36de82e1278abb02f:68) FOUND
1800 In the streaming case, clamd uses the filename "stream" which you should
1801 be able to verify with { ktrace clamdscan --stream /tmp/eicar.com }. (The
1802 client app will replace "stream" with the original filename before returning
1803 results to stdout, but the trace shows the data).
1805 We will assume that the pathname passed to clamd from Exim does not contain
1806 a colon. We will have whined loudly above if the eml_filename does (and we're
1807 passing a filename to clamd). */
1810 return m_panic_defer(scanent, CUS callout_address,
1811 US"ClamAV returned null");
1813 /* strip newline at the end (won't be present for zINSTREAM)
1814 (also any trailing whitespace, which shouldn't exist, but we depend upon
1815 this below, so double-check) */
1817 p = av_buffer + Ustrlen(av_buffer) - 1;
1818 if (*p == '\n') *p = '\0';
1820 DEBUG(D_acl) debug_printf_indent("Malware response: %s\n", av_buffer);
1822 while (isspace(*--p) && (p > av_buffer))
1826 /* colon in returned output? */
1827 if (!(p = Ustrchr(av_buffer,':')))
1828 return m_panic_defer(scanent, CUS callout_address, string_sprintf(
1829 "ClamAV returned malformed result (missing colon): %s",
1832 /* strip filename */
1833 while (*p && isspace(*++p)) /**/;
1836 /* It would be bad to encounter a virus with "FOUND" in part of the name,
1837 but we should at least be resistant to it. */
1838 p = Ustrrchr(vname, ' ');
1839 result_tag = p ? p+1 : vname;
1841 if (Ustrcmp(result_tag, "FOUND") == 0)
1843 /* p should still be the whitespace before the result_tag */
1844 while (isspace(*p)) --p;
1846 /* Strip off the extended information too, which will be in parens
1847 after the virus name, with no intervening whitespace. */
1850 /* "(hash:size)", so previous '(' will do; if not found, we have
1851 a curious virus name, but not an error. */
1852 p = Ustrrchr(vname, '(');
1856 malware_name = string_copy(vname);
1857 DEBUG(D_acl) debug_printf_indent("Malware found, name \"%s\"\n", malware_name);
1860 else if (Ustrcmp(result_tag, "ERROR") == 0)
1861 return m_panic_defer(scanent, CUS callout_address,
1862 string_sprintf("ClamAV returned: %s", av_buffer));
1864 else if (Ustrcmp(result_tag, "OK") == 0)
1866 /* Everything should be OK */
1867 malware_name = NULL;
1868 DEBUG(D_acl) debug_printf_indent("Malware not found\n");
1872 return m_panic_defer(scanent, CUS callout_address,
1873 string_sprintf("unparseable response from ClamAV: {%s}", av_buffer));
1879 #ifndef DISABLE_MAL_SOCK
1880 case M_SOCK: /* "sock" scanner type ------------------------------------- */
1881 /* This code was derived by Martin Poole from the clamd code contributed
1882 by David Saez and the cmdline code
1886 uschar * commandline;
1887 uschar av_buffer[1024];
1888 uschar * linebuffer;
1889 uschar * sockline_scanner;
1890 uschar sockline_scanner_default[] = "%s\n";
1891 const pcre2_code *sockline_trig_re;
1892 const pcre2_code *sockline_name_re;
1894 /* find scanner command line */
1895 if ( (sockline_scanner = string_nextinlist(&av_scanner_work, &sep,
1897 && *sockline_scanner
1899 { /* check for no expansions apart from one %s */
1900 uschar * s = Ustrchr(sockline_scanner, '%');
1902 if ((*s != 's' && *s != '%') || Ustrchr(s+1, '%'))
1903 return m_panic_defer_3(scanent, NULL,
1904 US"unsafe sock scanner call spec", malware_daemon_ctx.sock);
1907 sockline_scanner = sockline_scanner_default;
1908 DEBUG(D_acl) debug_printf_indent("%15s%10s'%s'\n", "", "cmdline: ",
1909 string_printing(sockline_scanner));
1911 /* find scanner output trigger */
1912 sockline_trig_re = m_pcre_nextinlist(&av_scanner_work, &sep,
1913 "missing trigger specification", &errstr);
1914 if (!sockline_trig_re)
1915 return m_panic_defer_3(scanent, NULL, errstr, malware_daemon_ctx.sock);
1917 /* find virus name regex */
1918 sockline_name_re = m_pcre_nextinlist(&av_scanner_work, &sep,
1919 "missing virus name regex specification", &errstr);
1920 if (!sockline_name_re)
1921 return m_panic_defer_3(scanent, NULL, errstr, malware_daemon_ctx.sock);
1923 /* prepare scanner call - security depends on expansions check above */
1924 commandline = string_sprintf( CS sockline_scanner, CS eml_filename);
1925 DEBUG(D_acl) debug_printf_indent("%15s%10s'%s'\n", "", "expanded: ",
1926 string_printing(commandline));
1928 /* Pass the command string to the socket */
1929 if (m_sock_send(malware_daemon_ctx.sock, commandline, Ustrlen(commandline), &errstr) < 0)
1930 return m_panic_defer(scanent, CUS callout_address, errstr);
1932 /* Read the result */
1933 bread = ip_recv(&malware_daemon_ctx, av_buffer, sizeof(av_buffer), tmo);
1936 return m_panic_defer_3(scanent, CUS callout_address,
1937 string_sprintf("unable to read from socket (%s)", strerror(errno)),
1938 malware_daemon_ctx.sock);
1940 if (bread == sizeof(av_buffer))
1941 return m_panic_defer_3(scanent, CUS callout_address,
1942 US"buffer too small", malware_daemon_ctx.sock);
1943 av_buffer[bread] = '\0';
1944 linebuffer = string_copy(av_buffer);
1945 DEBUG(D_acl) debug_printf_indent("%15s%10s'%s'\n", "", "answer: ",
1946 string_printing(linebuffer));
1948 /* try trigger match */
1949 if (regex_match_and_setup(sockline_trig_re, linebuffer, 0, -1))
1951 if (!(malware_name = m_pcre_exec(sockline_name_re, av_buffer)))
1952 malware_name = US "unknown";
1953 DEBUG(D_acl) debug_printf_indent("%15s%10s'%s'\n", "", "name: ",
1954 string_printing(malware_name));
1956 else /* no virus found */
1957 malware_name = NULL;
1962 #ifndef DISABLE_MAL_MKS
1963 case M_MKSD: /* "mksd" scanner type ------------------------------------- */
1965 char *mksd_options_end;
1966 int mksd_maxproc = 1; /* default, if no option supplied */
1969 if (scanner_options)
1971 mksd_maxproc = (int)strtol(CS scanner_options, &mksd_options_end, 10);
1972 if ( *scanner_options == '\0'
1973 || *mksd_options_end != '\0'
1975 || mksd_maxproc > 32
1977 return m_panic_defer(scanent, CUS callout_address,
1978 string_sprintf("invalid option '%s'", scanner_options));
1981 if((malware_daemon_ctx.sock = ip_unixsocket(US "/var/run/mksd/socket", &errstr)) < 0)
1982 return m_panic_defer(scanent, CUS callout_address, errstr);
1984 malware_name = NULL;
1986 DEBUG(D_acl) debug_printf_indent("Malware scan: issuing %s scan\n", scanner_name);
1988 if ((retval = mksd_scan_packed(scanent, malware_daemon_ctx.sock, eml_filename, tmo)) != OK)
1990 close (malware_daemon_ctx.sock);
1997 #ifndef DISABLE_MAL_AVAST
1998 case M_AVAST: /* "avast" scanner type ----------------------------------- */
2001 uschar * scanrequest;
2002 enum {AVA_HELO, AVA_OPT, AVA_RSP, AVA_DONE} avast_stage;
2004 uschar * error_message = NULL;
2005 BOOL more_data = FALSE;
2008 /* According to Martin Tuma @avast the protocol uses "escaped
2009 whitespace", that is, every embedded whitespace is backslash
2010 escaped, as well as backslash is protected by backslash.
2011 The returned lines contain the name of the scanned file, a tab
2015 [E] - some error occurred
2016 Such marker follows the first non-escaped TAB. For more information
2017 see avast-protocol(5)
2019 We observed two cases:
2021 <- /file [E]0.0 Error 13 Permission denied
2022 <- 451 SCAN Engine error 13 permission denied
2025 <- /file… [E]3.0 Error 41120 The file is a decompression bomb
2027 <- /file… [+]2.0 0 Eicar Test Virus!!!
2030 If the scanner returns 4xx, DEFER is a good decision, combined
2031 with a panic log entry, to get the admin's attention.
2033 If the scanner returns 200, we reject it as malware, if found any,
2034 or, in case of an error, we set the malware message to the error
2037 Some of the >= 42000 errors are message related - usually some
2038 broken archives etc, but some of them are e.g. license related.
2039 Once the license expires the engine starts returning errors for
2040 every scanning attempt. I¹ have the full list of the error codes
2041 but it is not a public API and is subject to change. It is hard
2042 for me to say what you should do in case of an engine error. You
2043 can have a “Treat * unscanned file as infection” policy or “Treat
2044 unscanned file as clean” policy. ¹) Jakub Bednar
2048 if ( ( !ava_re_clean
2049 && !(ava_re_clean = m_pcre_compile(ava_re_clean_str, &errstr)))
2051 && !(ava_re_virus = m_pcre_compile(ava_re_virus_str, &errstr)))
2053 && !(ava_re_error = m_pcre_compile(ava_re_error_str, &errstr)))
2055 return malware_panic_defer(errstr);
2057 /* wait for result */
2058 for (avast_stage = AVA_HELO;
2059 (nread = recv_line(malware_daemon_ctx.sock, buf, sizeof(buf), tmo)) > 0;
2062 int slen = Ustrlen(buf);
2066 /* Multi line responses are bracketed between 210 … and nnn … */
2067 if (Ustrncmp(buf, "210", 3) == 0)
2072 else if (more_data && isdigit(buf[0])) more_data = 0;
2074 switch (avast_stage)
2077 if (more_data) continue;
2078 if (Ustrncmp(buf, "220", 3) != 0)
2079 goto endloop; /* require a 220 */
2083 if (more_data) continue;
2084 if (Ustrncmp(buf, "200", 3) != 0)
2085 goto endloop; /* require a 200 */
2090 /* Check for another option to send. Newline-terminate it. */
2091 if ((scanrequest = string_nextinlist(&av_scanner_work, &sep,
2094 if (Ustrcmp(scanrequest, "pass_unscanned") == 0)
2096 DEBUG(D_acl) debug_printf_indent("pass unscanned files as clean\n");
2100 scanrequest = string_sprintf("%s\n", scanrequest);
2101 avast_stage = AVA_OPT; /* just sent option */
2102 DEBUG(D_acl) debug_printf_indent("send to avast OPTION: %s", scanrequest);
2106 scanrequest = string_sprintf("SCAN %s\n", eml_dir);
2107 avast_stage = AVA_RSP; /* just sent command */
2108 DEBUG(D_acl) debug_printf_indent("send to avast REQUEST: SCAN %s\n", eml_dir);
2111 /* send config-cmd or scan-request to socket */
2112 len = Ustrlen(scanrequest);
2113 if (send(malware_daemon_ctx.sock, scanrequest, len, 0) == -1)
2115 scanrequest[len-1] = '\0';
2116 return m_panic_defer_3(scanent, CUS callout_address, string_sprintf(
2117 "unable to send request '%s' to socket (%s): %s",
2118 scanrequest, scanner_options, strerror(errno)), malware_daemon_ctx.sock);
2125 if (isdigit(buf[0])) /* We're done */
2128 if (malware_name) /* Nothing else matters, just read on */
2131 if (regex_match(ava_re_clean, buf, slen, NULL))
2134 if ((malware_name = m_pcre_exec(ava_re_virus, buf)))
2136 unescape(malware_name);
2138 debug_printf_indent("unescaped malware name: '%s'\n", malware_name);
2142 if (strict) /* treat scanner errors as malware */
2144 if ((malware_name = m_pcre_exec(ava_re_error, buf)))
2146 unescape(malware_name);
2148 debug_printf_indent("unescaped error message: '%s'\n", malware_name);
2152 else if (regex_match(ava_re_error, buf, slen, NULL))
2154 log_write(0, LOG_MAIN, "internal scanner error (ignored): %s", buf);
2158 /* here also for any unexpected response from the scanner */
2159 DEBUG(D_acl) debug_printf("avast response not handled: '%s'\n", buf);
2163 default: log_write(0, LOG_PANIC, "%s:%d:%s: should not happen",
2164 __FILE__, __LINE__, __FUNCTION__);
2171 if (nread == -1) error_message = US"EOF from scanner";
2172 else if (nread < 0) error_message = US"timeout from scanner";
2173 else if (nread == 0) error_message = US"got nothing from scanner";
2174 else if (buf[0] != '2') error_message = buf;
2176 DEBUG(D_acl) debug_printf_indent("sent to avast QUIT\n");
2177 if (send(malware_daemon_ctx.sock, "QUIT\n", 5, 0) == -1)
2178 return m_panic_defer_3(scanent, CUS callout_address,
2179 string_sprintf("unable to send quit request to socket (%s): %s",
2180 scanner_options, strerror(errno)), malware_daemon_ctx.sock);
2183 return m_panic_defer_3(scanent, CUS callout_address, error_message, malware_daemon_ctx.sock);
2187 } /* scanner type switch */
2189 if (malware_daemon_ctx.sock >= 0)
2190 (void) close (malware_daemon_ctx.sock);
2191 malware_ok = TRUE; /* set "been here, done that" marker */
2194 /* match virus name against pattern (caseless ------->----------v) */
2195 if (malware_name && regex_match_and_setup(re, malware_name, 0, -1))
2197 DEBUG(D_acl) debug_printf_indent(
2198 "Matched regex to malware [%s] [%s]\n", malware_re, malware_name);
2206 /*************************************************
2207 * Scan an email for malware *
2208 *************************************************/
2210 /* This is the normal interface for scanning an email, which doesn't need a
2211 filename; it's a wrapper around the malware_file function.
2214 malware_re match condition for "malware="
2215 timeout if nonzero, timeout in seconds
2217 Returns: Exim message processing code (OK, FAIL, DEFER, ...)
2218 where true means malware was found (condition applies)
2221 malware(const uschar * malware_re, int timeout)
2223 int ret = malware_internal(malware_re, NULL, timeout);
2225 if (ret == DEFER) av_failed = TRUE;
2230 /*************************************************
2231 * Scan a file for malware *
2232 *************************************************/
2234 /* This is a test wrapper for scanning an email, which is not used in
2235 normal processing. Scan any file, using the Exim scanning interface.
2236 This function tampers with various global variables so is unsafe to use
2237 in any other context.
2240 eml_filename a file holding the message to be scanned
2242 Returns: Exim message processing code (OK, FAIL, DEFER, ...)
2243 where true means malware was found (condition applies)
2246 malware_in_file(uschar *eml_filename)
2248 uschar message_id_buf[64];
2251 /* spool_mbox() assumes various parameters exist, when creating
2252 the relevant directory and the email within */
2254 (void) string_format(message_id_buf, sizeof(message_id_buf),
2255 "dummy-%d", vaguely_random_number(INT_MAX));
2256 message_id = message_id_buf;
2257 sender_address = US"malware-sender@example.net";
2259 recipients_list = NULL;
2260 receive_add_recipient(US"malware-victim@example.net", -1);
2261 f.enable_dollar_recipients = TRUE;
2263 ret = malware_internal(US"*", eml_filename, 0);
2265 Ustrncpy(spooled_message_id, message_id, sizeof(spooled_message_id));
2268 /* don't set no_mbox_unspool; at present, there's no way for it to become
2269 set, but if that changes, then it should apply to these tests too */
2273 /* silence static analysis tools */
2283 if (!malware_default_re)
2284 malware_default_re = regex_must_compile(malware_regex_default, FALSE, TRUE);
2286 #ifndef DISABLE_MAL_DRWEB
2288 drweb_re = regex_must_compile(drweb_re_str, FALSE, TRUE);
2290 #ifndef DISABLE_MAL_FSECURE
2292 fsec_re = regex_must_compile(fsec_re_str, FALSE, TRUE);
2294 #ifndef DISABLE_MAL_KAV
2296 kav_re_sus = regex_must_compile(kav_re_sus_str, FALSE, TRUE);
2298 kav_re_inf = regex_must_compile(kav_re_inf_str, FALSE, TRUE);
2300 #ifndef DISABLE_MAL_AVAST
2302 ava_re_clean = regex_must_compile(ava_re_clean_str, FALSE, TRUE);
2304 ava_re_virus = regex_must_compile(ava_re_virus_str, FALSE, TRUE);
2306 ava_re_error = regex_must_compile(ava_re_error_str, FALSE, TRUE);
2308 #ifndef DISABLE_MAL_FFROT6D
2309 if (!fprot6d_re_error)
2310 fprot6d_re_error = regex_must_compile(fprot6d_re_error_str, FALSE, TRUE);
2311 if (!fprot6d_re_virus)
2312 fprot6d_re_virus = regex_must_compile(fprot6d_re_virus_str, FALSE, TRUE);
2318 malware_show_supported(gstring * g)
2320 g = string_cat(g, US"Malware:");
2321 for (struct scan * sc = m_scans; sc->scancode != (scanner_t)-1; sc++)
2322 g = string_fmt_append(g, " %s", sc->name);
2323 return string_cat(g, US"\n");
2327 # endif /*!MACRO_PREDEF*/
2328 #endif /*WITH_CONTENT_SCAN*/