afe9e00028d3903ea8e70614b29347e121f6fad6
[users/heiko/exim.git] / src / src / malware.c
1 /*************************************************
2 *     Exim - an Internet mail transport agent    *
3 *************************************************/
4
5 /* Copyright (c) Tom Kistner <tom@duncanthrax.net> 2003 - 2015
6  * License: GPL
7  * Copyright (c) The Exim Maintainers 2015 - 2018
8  */
9
10 /* Code for calling virus (malware) scanners. Called from acl.c. */
11
12 #include "exim.h"
13 #ifdef WITH_CONTENT_SCAN        /* entire file */
14
15 typedef enum {
16 #ifndef DISABLE_MAL_FFROTD
17         M_FPROTD,
18 #endif
19 #ifndef DISABLE_MAL_FFROT6D
20         M_FPROT6D,
21 #endif
22 #ifndef DISABLE_MAL_DRWEB
23         M_DRWEB,
24 #endif
25 #ifndef DISABLE_MAL_AVE
26         M_AVES,
27 #endif
28 #ifndef DISABLE_MAL_FSECURE
29         M_FSEC,
30 #endif
31 #ifndef DISABLE_MAL_KAV
32         M_KAVD,
33 #endif
34 #ifndef DISABLE_MAL_SOPHIE
35         M_SOPHIE,
36 #endif
37 #ifndef DISABLE_MAL_CLAM
38         M_CLAMD,
39 #endif
40 #ifndef DISABLE_MAL_MKS
41         M_MKSD,
42 #endif
43 #ifndef DISABLE_MAL_AVAST
44         M_AVAST,
45 #endif
46 #ifndef DISABLE_MAL_SOCK
47         M_SOCK,
48 #endif
49 #ifndef DISABLE_MAL_CMDLINE
50         M_CMDL,
51 #endif
52         M_DUMMY
53         } scanner_t;
54 typedef enum {MC_NONE, MC_TCP, MC_UNIX, MC_STRM} contype_t;
55 static struct scan
56 {
57   scanner_t     scancode;
58   const uschar * name;
59   const uschar * options_default;
60   contype_t     conn;
61 } m_scans[] =
62 {
63 #ifndef DISABLE_MAL_FFROTD
64   { M_FPROTD,   US"f-protd",    US"localhost 10200-10204",            MC_TCP },
65 #endif
66 #ifndef DISABLE_MAL_FFROT6D
67   { M_FPROT6D,  US"f-prot6d",   US"localhost 10200",                  MC_TCP },
68 #endif
69 #ifndef DISABLE_MAL_DRWEB
70   { M_DRWEB,    US"drweb",      US"/usr/local/drweb/run/drwebd.sock", MC_STRM },
71 #endif
72 #ifndef DISABLE_MAL_AVE
73   { M_AVES,     US"aveserver",  US"/var/run/aveserver",               MC_UNIX },
74 #endif
75 #ifndef DISABLE_MAL_FSECURE
76   { M_FSEC,     US"fsecure",    US"/var/run/.fsav",                   MC_UNIX },
77 #endif
78 #ifndef DISABLE_MAL_KAV
79   { M_KAVD,     US"kavdaemon",  US"/var/run/AvpCtl",                  MC_UNIX },
80 #endif
81 #ifndef DISABLE_MAL_SOPHIE
82   { M_SOPHIE,   US"sophie",     US"/var/run/sophie",                  MC_UNIX },
83 #endif
84 #ifndef DISABLE_MAL_CLAM
85   { M_CLAMD,    US"clamd",      US"/tmp/clamd",                       MC_NONE },
86 #endif
87 #ifndef DISABLE_MAL_MKS
88   { M_MKSD,     US"mksd",       NULL,                                 MC_NONE },
89 #endif
90 #ifndef DISABLE_MAL_AVAST
91   { M_AVAST,    US"avast",      US"/var/run/avast/scan.sock",         MC_STRM },
92 #endif
93 #ifndef DISABLE_MAL_SOCK
94   { M_SOCK,     US"sock",       US"/tmp/malware.sock",                MC_STRM },
95 #endif
96 #ifndef DISABLE_MAL_CMDLINE
97   { M_CMDL,     US"cmdline",    NULL,                                 MC_NONE },
98 #endif
99   { -1,         NULL,           NULL, MC_NONE }         /* end-marker */
100 };
101
102 /******************************************************************************/
103 # ifdef MACRO_PREDEF            /* build solely to predefine macros */
104
105 #  include "macro_predef.h"
106
107 void
108 features_malware(void)
109 {
110 const uschar * s;
111 uschar * t;
112 uschar buf[64];
113
114 spf(buf, sizeof(buf), US"_HAVE_MALWARE_");
115
116 for (const struct scan * sc = m_scans; sc->scancode != -1; sc++)
117   {
118   for (s = sc->name, t = buf+14; *s; s++) if (*s != '-')
119     *t++ = toupper(*s);
120   *t = '\0';
121   builtin_macro_create(buf);
122   }
123 }
124
125 /******************************************************************************/
126 # else  /*!MACRO_PREDEF, main build*/
127
128
129 #define MALWARE_TIMEOUT 120     /* default timeout, seconds */
130
131 static const uschar * malware_regex_default = US ".+";
132 static const pcre * malware_default_re = NULL;
133
134
135
136 #ifndef DISABLE_MAL_CLAM
137 /* The maximum number of clamd servers that are supported in the configuration */
138 # define MAX_CLAMD_SERVERS 32
139 # define MAX_CLAMD_SERVERS_S "32"
140
141 typedef struct clamd_address {
142   uschar * hostspec;
143   unsigned tcp_port;
144   unsigned retry;
145 } clamd_address;
146 #endif
147
148
149 #ifndef DISABLE_MAL_DRWEB
150 # define DRWEBD_SCAN_CMD             (1)     /* scan file, buffer or diskfile */
151 # define DRWEBD_RETURN_VIRUSES       (1<<0)   /* ask daemon return to us viruses names from report */
152 # define DRWEBD_IS_MAIL              (1<<19)  /* say to daemon that format is "archive MAIL" */
153
154 # define DERR_READ_ERR               (1<<0)   /* read error */
155 # define DERR_NOMEMORY               (1<<2)   /* no memory */
156 # define DERR_TIMEOUT                (1<<9)   /* scan timeout has run out */
157 # define DERR_BAD_CALL               (1<<15)  /* wrong command */
158
159 static const uschar * drweb_re_str = US "infected\\swith\\s*(.+?)$";
160 static const pcre * drweb_re = NULL;
161 #endif
162
163 #ifndef DISABLE_MAL_FSECURE
164 static const uschar * fsec_re_str = US "\\S{0,5}INFECTED\\t[^\\t]*\\t([^\\t]+)\\t\\S*$";
165 static const pcre * fsec_re = NULL;
166 #endif
167
168 #ifndef DISABLE_MAL_KAV
169 static const uschar * kav_re_sus_str = US "suspicion:\\s*(.+?)\\s*$";
170 static const uschar * kav_re_inf_str = US "infected:\\s*(.+?)\\s*$";
171 static const pcre * kav_re_sus = NULL;
172 static const pcre * kav_re_inf = NULL;
173 #endif
174
175 #ifndef DISABLE_MAL_AVAST
176 static const uschar * ava_re_clean_str = US "(?!\\\\)\\t\\[\\+\\]";
177 static const uschar * ava_re_virus_str = US "(?!\\\\)\\t\\[L\\]\\d+\\.0\\t0\\s(.*)";
178 static const uschar * ava_re_error_str = US "(?!\\\\)\\t\\[E\\]\\d+\\.0\\tError\\s\\d+\\s(.*)";
179 static const pcre * ava_re_clean = NULL;
180 static const pcre * ava_re_virus = NULL;
181 static const pcre * ava_re_error = NULL;
182 #endif
183
184 #ifndef DISABLE_MAL_FFROT6D
185 static const uschar * fprot6d_re_error_str = US "^\\d+\\s<(.+?)>$";
186 static const uschar * fprot6d_re_virus_str = US "^\\d+\\s<infected:\\s+(.+?)>\\s+.+$";
187 static const pcre * fprot6d_re_error = NULL;
188 static const pcre * fprot6d_re_virus = NULL;
189 #endif
190
191
192
193 /******************************************************************************/
194
195 #ifndef DISABLE_MAL_KAV
196 /* Routine to check whether a system is big- or little-endian.
197    Ripped from http://www.faqs.org/faqs/graphics/fileformats-faq/part4/section-7.html
198    Needed for proper kavdaemon implementation. Sigh. */
199 # define BIG_MY_ENDIAN      0
200 # define LITTLE_MY_ENDIAN   1
201 static int test_byte_order(void);
202 static inline int
203 test_byte_order()
204 {
205   short int word = 0x0001;
206   char *byte = CS  &word;
207   return(byte[0] ? LITTLE_MY_ENDIAN : BIG_MY_ENDIAN);
208 }
209 #endif
210
211 BOOL malware_ok = FALSE;
212
213 /* Gross hacks for the -bmalware option; perhaps we should just create
214 the scan directory normally for that case, but look into rigging up the
215 needed header variables if not already set on the command-line? */
216 extern int spool_mbox_ok;
217 extern uschar spooled_message_id[MESSAGE_ID_LENGTH+1];
218
219
220 /* Some (currently avast only) use backslash escaped whitespace,
221 this function undoes these escapes */
222
223 static inline void
224 unescape(uschar *p)
225 {
226 uschar *p0;
227 for (; *p; ++p)
228   if (*p == '\\' && (isspace(p[1]) || p[1] == '\\'))
229     for (p0 = p; *p0; ++p0) *p0 = p0[1];
230 }
231
232 /* --- malware_*_defer --- */
233 static inline int
234 malware_panic_defer(const uschar * str)
235 {
236 log_write(0, LOG_MAIN|LOG_PANIC, "malware acl condition: %s", str);
237 return DEFER;
238 }
239 static inline int
240 malware_log_defer(const uschar * str)
241 {
242 log_write(0, LOG_MAIN, "malware acl condition: %s", str);
243 return DEFER;
244 }
245 /* --- m_*_defer --- */
246 static inline int
247 m_panic_defer(struct scan * scanent, const uschar * hostport,
248   const uschar * str)
249 {
250 return malware_panic_defer(string_sprintf("%s %s : %s",
251   scanent->name, hostport ? hostport : CUS"", str));
252 }
253 static inline int
254 m_log_defer(struct scan * scanent, const uschar * hostport,
255   const uschar * str)
256 {
257 return malware_log_defer(string_sprintf("%s %s : %s",
258   scanent->name, hostport ? hostport : CUS"", str));
259 }
260 /* --- m_*_defer_3 */
261 static inline int
262 m_panic_defer_3(struct scan * scanent, const uschar * hostport,
263   const uschar * str, int fd_to_close)
264 {
265 (void) close(fd_to_close);
266 return m_panic_defer(scanent, hostport, str);
267 }
268
269 /*************************************************/
270
271 #ifndef DISABLE_MAL_CLAM
272 /* Only used by the Clamav code, which is working from a list of servers and
273 uses the returned in_addr to get a second connection to the same system.
274 */
275 static inline int
276 m_tcpsocket(const uschar * hostname, unsigned int port,
277         host_item * host, uschar ** errstr, const blob * fastopen_blob)
278 {
279 return ip_connectedsocket(SOCK_STREAM, hostname, port, port, 5,
280                           host, errstr, fastopen_blob);
281 }
282 #endif
283
284 static int
285 m_sock_send(int sock, uschar * buf, int cnt, uschar ** errstr)
286 {
287 if (send(sock, buf, cnt, 0) < 0)
288   {
289   int err = errno;
290   (void)close(sock);
291   *errstr = string_sprintf("unable to send to socket (%s): %s",
292          buf, strerror(err));
293   return -1;
294   }
295 return sock;
296 }
297
298 static const pcre *
299 m_pcre_compile(const uschar * re, uschar ** errstr)
300 {
301 const uschar * rerror;
302 int roffset;
303 const pcre * cre;
304
305 if (!(cre = pcre_compile(CS re, PCRE_COPT, CCSS &rerror, &roffset, NULL)))
306   *errstr= string_sprintf("regular expression error in '%s': %s at offset %d",
307       re, rerror, roffset);
308 return cre;
309 }
310
311 uschar *
312 m_pcre_exec(const pcre * cre, uschar * text)
313 {
314 int ovector[10*3];
315 int i = pcre_exec(cre, NULL, CS text, Ustrlen(text), 0, 0,
316               ovector, nelem(ovector));
317 uschar * substr = NULL;
318 if (i >= 2)                             /* Got it */
319   pcre_get_substring(CS text, ovector, i, 1, CCSS &substr);
320 return substr;
321 }
322
323 static const pcre *
324 m_pcre_nextinlist(const uschar ** list, int * sep,
325  char * listerr, uschar ** errstr)
326 {
327 const uschar * list_ele;
328 const pcre * cre = NULL;
329
330 if (!(list_ele = string_nextinlist(list, sep, NULL, 0)))
331   *errstr = US listerr;
332 else
333   {
334   DEBUG(D_acl) debug_printf_indent("%15s%10s'%s'\n", "", "RE: ",
335     string_printing(list_ele));
336   cre = m_pcre_compile(CUS list_ele, errstr);
337   }
338 return cre;
339 }
340
341 /*
342  Simple though inefficient wrapper for reading a line.  Drop CRs and the
343  trailing newline. Can return early on buffer full. Null-terminate.
344  Apply initial timeout if no data ready.
345
346  Return: number of chars - zero for an empty line
347          -1 on EOF
348          -2 on timeout or error
349 */
350 static int
351 recv_line(int fd, uschar * buffer, int bsize, time_t tmo)
352 {
353 uschar * p = buffer;
354 ssize_t rcv;
355 BOOL ok = FALSE;
356
357 if (!fd_ready(fd, tmo))
358   return -2;
359
360 /*XXX tmo handling assumes we always get a whole line */
361 /* read until \n */
362 errno = 0;
363 while ((rcv = read(fd, p, 1)) > 0)
364   {
365   ok = TRUE;
366   if (p-buffer > bsize-2) break;
367   if (*p == '\n') break;
368   if (*p != '\r') p++;
369   }
370 if (!ok)
371   {
372   DEBUG(D_acl) debug_printf_indent("Malware scan: read %s (%s)\n",
373                 rcv==0 ? "EOF" : "error", strerror(errno));
374   return rcv==0 ? -1 : -2;
375   }
376 *p = '\0';
377
378 DEBUG(D_acl) debug_printf_indent("Malware scan: read '%s'\n", buffer);
379 return p - buffer;
380 }
381
382 /* return TRUE iff size as requested */
383 static BOOL
384 recv_len(int sock, void * buf, int size, time_t tmo)
385 {
386 return fd_ready(sock, tmo)
387   ? recv(sock, buf, size, 0) == size
388   : FALSE;
389 }
390
391
392
393 #ifndef DISABLE_MAL_MKS
394 /* ============= private routines for the "mksd" scanner type ============== */
395
396 # include <sys/uio.h>
397
398 static inline int
399 mksd_writev (int sock, struct iovec * iov, int iovcnt)
400 {
401 int i;
402
403 for (;;)
404   {
405   do
406     i = writev (sock, iov, iovcnt);
407   while (i < 0 && errno == EINTR);
408   if (i <= 0)
409     {
410     (void) malware_panic_defer(
411             US"unable to write to mksd UNIX socket (/var/run/mksd/socket)");
412     return -1;
413     }
414   for (;;)      /* check for short write */
415     if (i >= iov->iov_len)
416       {
417       if (--iovcnt == 0)
418         return 0;
419       i -= iov->iov_len;
420       iov++;
421       }
422     else
423       {
424       iov->iov_len -= i;
425       iov->iov_base = CS iov->iov_base + i;
426       break;
427       }
428   }
429 }
430
431 static inline int
432 mksd_read_lines (int sock, uschar *av_buffer, int av_buffer_size, time_t tmo)
433 {
434 client_conn_ctx cctx = {.sock = sock};
435 int offset = 0;
436 int i;
437
438 do
439   {
440   i = ip_recv(&cctx, av_buffer+offset, av_buffer_size-offset, tmo);
441   if (i <= 0)
442     {
443     (void) malware_panic_defer(US"unable to read from mksd UNIX socket (/var/run/mksd/socket)");
444     return -1;
445     }
446
447   offset += i;
448   /* offset == av_buffer_size -> buffer full */
449   if (offset == av_buffer_size)
450     {
451     (void) malware_panic_defer(US"malformed reply received from mksd");
452     return -1;
453     }
454   } while (av_buffer[offset-1] != '\n');
455
456 av_buffer[offset] = '\0';
457 return offset;
458 }
459
460 static inline int
461 mksd_parse_line(struct scan * scanent, char * line)
462 {
463 char *p;
464
465 switch (*line)
466   {
467   case 'O': /* OK */
468     return OK;
469
470   case 'E':
471   case 'A': /* ERR */
472     if ((p = strchr (line, '\n')) != NULL)
473       *p = '\0';
474     return m_panic_defer(scanent, NULL,
475       string_sprintf("scanner failed: %s", line));
476
477   default: /* VIR */
478     if ((p = strchr (line, '\n')) != NULL)
479       {
480       *p = '\0';
481       if (  p-line > 5
482          && line[3] == ' '
483          && (p = strchr(line+4, ' ')) != NULL
484          && p-line > 4
485          )
486         {
487         *p = '\0';
488         malware_name = string_copy(US line+4);
489         return OK;
490         }
491       }
492     return m_panic_defer(scanent, NULL,
493       string_sprintf("malformed reply received: %s", line));
494   }
495 }
496
497 static int
498 mksd_scan_packed(struct scan * scanent, int sock, const uschar * scan_filename,
499   time_t tmo)
500 {
501 struct iovec iov[3];
502 const char *cmd = "MSQ\n";
503 uschar av_buffer[1024];
504
505 iov[0].iov_base = (void *) cmd;
506 iov[0].iov_len = 3;
507 iov[1].iov_base = (void *) scan_filename;
508 iov[1].iov_len = Ustrlen(scan_filename);
509 iov[2].iov_base = (void *) (cmd + 3);
510 iov[2].iov_len = 1;
511
512 if (mksd_writev (sock, iov, 3) < 0)
513   return DEFER;
514
515 if (mksd_read_lines (sock, av_buffer, sizeof (av_buffer), tmo) < 0)
516   return DEFER;
517
518 return mksd_parse_line (scanent, CS av_buffer);
519 }
520 #endif  /* MKSD */
521
522
523 #ifndef DISABLE_MAL_CLAM
524 static int
525 clamd_option(clamd_address * cd, const uschar * optstr, int * subsep)
526 {
527 uschar * s;
528
529 cd->retry = 0;
530 while ((s = string_nextinlist(&optstr, subsep, NULL, 0)))
531   if (Ustrncmp(s, "retry=", 6) == 0)
532     {
533     int sec = readconf_readtime((s += 6), '\0', FALSE);
534     if (sec < 0)
535       return FAIL;
536     cd->retry = sec;
537     }
538   else
539     return FAIL;
540 return OK;
541 }
542 #endif
543
544
545
546 /*************************************************
547 *          Scan content for malware              *
548 *************************************************/
549
550 /* This is an internal interface for scanning an email; the normal interface
551 is via malware(), or there's malware_in_file() used for testing/debugging.
552
553 Arguments:
554   malware_re    match condition for "malware="
555   scan_filename  the file holding the email to be scanned, if we're faking
556                 this up for the -bmalware test, else NULL
557   timeout       if nonzero, non-default timeoutl
558
559 Returns:        Exim message processing code (OK, FAIL, DEFER, ...)
560                 where true means malware was found (condition applies)
561 */
562 static int
563 malware_internal(const uschar * malware_re, const uschar * scan_filename,
564   int timeout)
565 {
566 int sep = 0;
567 const uschar *av_scanner_work = av_scanner;
568 uschar *scanner_name;
569 unsigned long mbox_size;
570 FILE *mbox_file;
571 const pcre *re;
572 uschar * errstr;
573 struct scan * scanent;
574 const uschar * scanner_options;
575 client_conn_ctx malware_daemon_ctx = {.sock = -1};
576 time_t tmo;
577 uschar * eml_filename, * eml_dir;
578
579 if (!malware_re)
580   return FAIL;          /* empty means "don't match anything" */
581
582 /* Ensure the eml mbox file is spooled up */
583
584 if (!(mbox_file = spool_mbox(&mbox_size, scan_filename, &eml_filename)))
585   return malware_panic_defer(US"error while creating mbox spool file");
586
587 /* None of our current scanners need the mbox file as a stream (they use
588 the name), so we can close it right away.  Get the directory too. */
589
590 (void) fclose(mbox_file);
591 eml_dir = string_copyn(eml_filename, Ustrrchr(eml_filename, '/') - eml_filename);
592
593 /* parse 1st option */
594 if (strcmpic(malware_re, US"false") == 0  ||  Ustrcmp(malware_re,"0") == 0)
595   return FAIL;          /* explicitly no matching */
596
597 /* special cases (match anything except empty) */
598 if (  strcmpic(malware_re,US"true") == 0
599    || Ustrcmp(malware_re,"*") == 0
600    || Ustrcmp(malware_re,"1") == 0
601    )
602   {
603   if (  !malware_default_re
604      && !(malware_default_re = m_pcre_compile(malware_regex_default, &errstr)))
605     return malware_panic_defer(errstr);
606   malware_re = malware_regex_default;
607   re = malware_default_re;
608   }
609
610 /* compile the regex, see if it works */
611 else if (!(re = m_pcre_compile(malware_re, &errstr)))
612   return malware_panic_defer(errstr);
613
614 /* if av_scanner starts with a dollar, expand it first */
615 if (*av_scanner == '$')
616   {
617   if (!(av_scanner_work = expand_string(av_scanner)))
618     return malware_panic_defer(
619          string_sprintf("av_scanner starts with $, but expansion failed: %s",
620          expand_string_message));
621
622   DEBUG(D_acl)
623     debug_printf_indent("Expanded av_scanner global: %s\n", av_scanner_work);
624   /* disable result caching in this case */
625   malware_name = NULL;
626   malware_ok = FALSE;
627   }
628
629 /* Do not scan twice (unless av_scanner is dynamic). */
630 if (!malware_ok)
631   {
632   /* find the scanner type from the av_scanner option */
633   if (!(scanner_name = string_nextinlist(&av_scanner_work, &sep, NULL, 0)))
634     return malware_panic_defer(US"av_scanner configuration variable is empty");
635   if (!timeout) timeout = MALWARE_TIMEOUT;
636   tmo = time(NULL) + timeout;
637
638   for (scanent = m_scans; ; scanent++)
639     {
640     if (!scanent->name)
641       return malware_panic_defer(string_sprintf("unknown scanner type '%s'",
642         scanner_name));
643     if (strcmpic(scanner_name, US scanent->name) != 0)
644       continue;
645     DEBUG(D_acl) debug_printf_indent("Malware scan:  %s tmo=%s\n",
646       scanner_name, readconf_printtime(timeout));
647
648     if (!(scanner_options = string_nextinlist(&av_scanner_work, &sep, NULL, 0)))
649       scanner_options = scanent->options_default;
650     if (scanent->conn == MC_NONE)
651       break;
652
653     DEBUG(D_acl) debug_printf_indent("%15s%10s%s\n", "", "socket: ", scanner_options);
654     switch(scanent->conn)
655     {
656     case MC_TCP:
657       malware_daemon_ctx.sock = ip_tcpsocket(scanner_options, &errstr, 5, NULL); break;
658     case MC_UNIX:
659       malware_daemon_ctx.sock = ip_unixsocket(scanner_options, &errstr);        break;
660     case MC_STRM:
661       malware_daemon_ctx.sock = ip_streamsocket(scanner_options, &errstr, 5, NULL); break;
662     default:
663       /* compiler quietening */ break;
664     }
665     if (malware_daemon_ctx.sock < 0)
666       return m_panic_defer(scanent, CUS callout_address, errstr);
667     break;
668   }
669
670   switch (scanent->scancode)
671     {
672 #ifndef DISABLE_MAL_FFROTD
673     case M_FPROTD: /* "f-protd" scanner type -------------------------------- */
674       {
675       uschar *fp_scan_option;
676       unsigned int detected=0, par_count=0;
677       uschar * scanrequest;
678       uschar buf[32768], *strhelper, *strhelper2;
679       uschar * malware_name_internal = NULL;
680       int len;
681
682       scanrequest = string_sprintf("GET %s", eml_filename);
683
684       while ((fp_scan_option = string_nextinlist(&av_scanner_work, &sep,
685                             NULL, 0)))
686         {
687         scanrequest = string_sprintf("%s%s%s", scanrequest,
688                                   par_count ? "%20" : "?", fp_scan_option);
689         par_count++;
690         }
691       scanrequest = string_sprintf("%s HTTP/1.0\r\n\r\n", scanrequest);
692       DEBUG(D_acl) debug_printf_indent("Malware scan: issuing %s: %s\n",
693         scanner_name, scanrequest);
694
695       /* send scan request */
696       if (m_sock_send(malware_daemon_ctx.sock, scanrequest, Ustrlen(scanrequest)+1, &errstr) < 0)
697         return m_panic_defer(scanent, CUS callout_address, errstr);
698
699       while ((len = recv_line(malware_daemon_ctx.sock, buf, sizeof(buf), tmo)) >= 0)
700         if (len > 0)
701           {
702           if (Ustrstr(buf, US"<detected type=\"") != NULL)
703             detected = 1;
704           else if (detected && (strhelper = Ustrstr(buf, US"<name>")))
705             {
706             if ((strhelper2 = Ustrstr(buf, US"</name>")) != NULL)
707               {
708               *strhelper2 = '\0';
709               malware_name_internal = string_copy(strhelper+6);
710               }
711             }
712           else if (Ustrstr(buf, US"<summary code=\""))
713             {
714             malware_name = Ustrstr(buf, US"<summary code=\"11\">")
715                 ? malware_name_internal : NULL;
716             break;
717             }
718           }
719       if (len < -1)
720         {
721         (void)close(malware_daemon_ctx.sock);
722         return DEFER;
723         }
724       break;
725       } /* f-protd */
726 #endif
727
728 #ifndef DISABLE_MAL_FFROT6D
729     case M_FPROT6D: /* "f-prot6d" scanner type ----------------------------------- */
730       {
731       int bread;
732       uschar * e;
733       uschar * linebuffer;
734       uschar * scanrequest;
735       uschar av_buffer[1024];
736
737       if ((!fprot6d_re_virus && !(fprot6d_re_virus = m_pcre_compile(fprot6d_re_virus_str, &errstr)))
738         || (!fprot6d_re_error && !(fprot6d_re_error = m_pcre_compile(fprot6d_re_error_str, &errstr))))
739         return malware_panic_defer(errstr);
740
741       scanrequest = string_sprintf("SCAN FILE %s\n", eml_filename);
742       DEBUG(D_acl) debug_printf_indent("Malware scan: issuing %s: %s\n",
743         scanner_name, scanrequest);
744
745       if (m_sock_send(malware_daemon_ctx.sock, scanrequest, Ustrlen(scanrequest), &errstr) < 0)
746         return m_panic_defer(scanent, CUS callout_address, errstr);
747
748       bread = ip_recv(&malware_daemon_ctx, av_buffer, sizeof(av_buffer), tmo);
749
750       if (bread <= 0)
751         return m_panic_defer_3(scanent, CUS callout_address,
752           string_sprintf("unable to read from socket (%s)", strerror(errno)),
753           malware_daemon_ctx.sock);
754
755       if (bread == sizeof(av_buffer))
756         return m_panic_defer_3(scanent, CUS callout_address,
757           US"buffer too small", malware_daemon_ctx.sock);
758
759       av_buffer[bread] = '\0';
760       linebuffer = string_copy(av_buffer);
761
762       m_sock_send(malware_daemon_ctx.sock, US"QUIT\n", 5, 0);
763
764       if ((e = m_pcre_exec(fprot6d_re_error, linebuffer)))
765         return m_panic_defer_3(scanent, CUS callout_address,
766           string_sprintf("scanner reported error (%s)", e), malware_daemon_ctx.sock);
767
768       if (!(malware_name = m_pcre_exec(fprot6d_re_virus, linebuffer)))
769         malware_name = NULL;
770
771       break;
772       }  /* f-prot6d */
773 #endif
774
775 #ifndef DISABLE_MAL_DRWEB
776     case M_DRWEB: /* "drweb" scanner type ----------------------------------- */
777   /* v0.1 - added support for tcp sockets          */
778   /* v0.0 - initial release -- support for unix sockets      */
779       {
780       int result;
781       off_t fsize;
782       unsigned int fsize_uint;
783       uschar * tmpbuf, *drweb_fbuf;
784       int drweb_rc, drweb_cmd, drweb_flags = 0x0000, drweb_fd,
785           drweb_vnum, drweb_slen, drweb_fin = 0x0000;
786
787       /* prepare variables */
788       drweb_cmd = htonl(DRWEBD_SCAN_CMD);
789       drweb_flags = htonl(DRWEBD_RETURN_VIRUSES | DRWEBD_IS_MAIL);
790
791       if (*scanner_options != '/')
792         {
793         /* calc file size */
794         if ((drweb_fd = exim_open2(CCS eml_filename, O_RDONLY)) == -1)
795           return m_panic_defer_3(scanent, NULL,
796             string_sprintf("can't open spool file %s: %s",
797               eml_filename, strerror(errno)),
798             malware_daemon_ctx.sock);
799
800         if ((fsize = lseek(drweb_fd, 0, SEEK_END)) == -1)
801           {
802           int err;
803 badseek:  err = errno;
804           (void)close(drweb_fd);
805           return m_panic_defer_3(scanent, NULL,
806             string_sprintf("can't seek spool file %s: %s",
807               eml_filename, strerror(err)),
808             malware_daemon_ctx.sock);
809           }
810         fsize_uint = (unsigned int) fsize;
811         if ((off_t)fsize_uint != fsize)
812           {
813           (void)close(drweb_fd);
814           return m_panic_defer_3(scanent, NULL,
815             string_sprintf("seeking spool file %s, size overflow",
816               eml_filename),
817             malware_daemon_ctx.sock);
818           }
819         drweb_slen = htonl(fsize);
820         if (lseek(drweb_fd, 0, SEEK_SET) < 0)
821           goto badseek;
822
823         DEBUG(D_acl) debug_printf_indent("Malware scan: issuing %s remote scan [%s]\n",
824             scanner_name, scanner_options);
825
826         /* send scan request */
827         if ((send(malware_daemon_ctx.sock, &drweb_cmd, sizeof(drweb_cmd), 0) < 0) ||
828             (send(malware_daemon_ctx.sock, &drweb_flags, sizeof(drweb_flags), 0) < 0) ||
829             (send(malware_daemon_ctx.sock, &drweb_fin, sizeof(drweb_fin), 0) < 0) ||
830             (send(malware_daemon_ctx.sock, &drweb_slen, sizeof(drweb_slen), 0) < 0))
831           {
832           (void)close(drweb_fd);
833           return m_panic_defer_3(scanent, CUS callout_address, string_sprintf(
834             "unable to send commands to socket (%s)", scanner_options),
835             malware_daemon_ctx.sock);
836           }
837
838         if (!(drweb_fbuf = store_malloc(fsize_uint)))
839           {
840           (void)close(drweb_fd);
841           return m_panic_defer_3(scanent, NULL,
842             string_sprintf("unable to allocate memory %u for file (%s)",
843               fsize_uint, eml_filename),
844             malware_daemon_ctx.sock);
845           }
846
847         if ((result = read (drweb_fd, drweb_fbuf, fsize)) == -1)
848           {
849           int err = errno;
850           (void)close(drweb_fd);
851           store_free(drweb_fbuf);
852           return m_panic_defer_3(scanent, NULL,
853             string_sprintf("can't read spool file %s: %s",
854               eml_filename, strerror(err)),
855             malware_daemon_ctx.sock);
856           }
857         (void)close(drweb_fd);
858
859         /* send file body to socket */
860         if (send(malware_daemon_ctx.sock, drweb_fbuf, fsize, 0) < 0)
861           {
862           store_free(drweb_fbuf);
863           return m_panic_defer_3(scanent, CUS callout_address, string_sprintf(
864             "unable to send file body to socket (%s)", scanner_options),
865             malware_daemon_ctx.sock);
866           }
867         store_free(drweb_fbuf);
868         }
869       else
870         {
871         drweb_slen = htonl(Ustrlen(eml_filename));
872
873         DEBUG(D_acl) debug_printf_indent("Malware scan: issuing %s local scan [%s]\n",
874             scanner_name, scanner_options);
875
876         /* send scan request */
877         if ((send(malware_daemon_ctx.sock, &drweb_cmd, sizeof(drweb_cmd), 0) < 0) ||
878             (send(malware_daemon_ctx.sock, &drweb_flags, sizeof(drweb_flags), 0) < 0) ||
879             (send(malware_daemon_ctx.sock, &drweb_slen, sizeof(drweb_slen), 0) < 0) ||
880             (send(malware_daemon_ctx.sock, eml_filename, Ustrlen(eml_filename), 0) < 0) ||
881             (send(malware_daemon_ctx.sock, &drweb_fin, sizeof(drweb_fin), 0) < 0))
882           return m_panic_defer_3(scanent, CUS callout_address, string_sprintf(
883             "unable to send commands to socket (%s)", scanner_options),
884             malware_daemon_ctx.sock);
885         }
886
887       /* wait for result */
888       if (!recv_len(malware_daemon_ctx.sock, &drweb_rc, sizeof(drweb_rc), tmo))
889         return m_panic_defer_3(scanent, CUS callout_address,
890                     US"unable to read return code", malware_daemon_ctx.sock);
891       drweb_rc = ntohl(drweb_rc);
892
893       if (!recv_len(malware_daemon_ctx.sock, &drweb_vnum, sizeof(drweb_vnum), tmo))
894         return m_panic_defer_3(scanent, CUS callout_address,
895                             US"unable to read the number of viruses", malware_daemon_ctx.sock);
896       drweb_vnum = ntohl(drweb_vnum);
897
898       /* "virus(es) found" if virus number is > 0 */
899       if (drweb_vnum)
900         {
901         gstring * g = NULL;
902
903         /* setup default virus name */
904         malware_name = US"unknown";
905
906         /* set up match regex */
907         if (!drweb_re)
908           drweb_re = m_pcre_compile(drweb_re_str, &errstr);
909
910         /* read and concatenate virus names into one string */
911         for (int i = 0; i < drweb_vnum; i++)
912           {
913           int ovector[10*3];
914
915           /* read the size of report */
916           if (!recv_len(malware_daemon_ctx.sock, &drweb_slen, sizeof(drweb_slen), tmo))
917             return m_panic_defer_3(scanent, CUS callout_address,
918                               US"cannot read report size", malware_daemon_ctx.sock);
919           drweb_slen = ntohl(drweb_slen);
920
921           /* assume tainted, since it is external input */
922           tmpbuf = store_get(drweb_slen, TRUE);
923
924           /* read report body */
925           if (!recv_len(malware_daemon_ctx.sock, tmpbuf, drweb_slen, tmo))
926             return m_panic_defer_3(scanent, CUS callout_address,
927                               US"cannot read report string", malware_daemon_ctx.sock);
928           tmpbuf[drweb_slen] = '\0';
929
930           /* try matcher on the line, grab substring */
931           result = pcre_exec(drweb_re, NULL, CS tmpbuf, Ustrlen(tmpbuf), 0, 0,
932                                   ovector, nelem(ovector));
933           if (result >= 2)
934             {
935             const char * pre_malware_nb;
936
937             pcre_get_substring(CS tmpbuf, ovector, result, 1, &pre_malware_nb);
938
939             if (i==0)   /* the first name we just copy to malware_name */
940               g = string_cat(NULL, US pre_malware_nb);
941
942             /*XXX could be string_append_listele? */
943             else        /* concatenate each new virus name to previous */
944               g = string_append(g, 2, "/", pre_malware_nb);
945
946             pcre_free_substring(pre_malware_nb);
947             }
948           }
949           malware_name = string_from_gstring(g);
950         }
951       else
952         {
953         const char *drweb_s = NULL;
954
955         if (drweb_rc & DERR_READ_ERR) drweb_s = "read error";
956         if (drweb_rc & DERR_NOMEMORY) drweb_s = "no memory";
957         if (drweb_rc & DERR_TIMEOUT)  drweb_s = "timeout";
958         if (drweb_rc & DERR_BAD_CALL) drweb_s = "wrong command";
959         /* retcodes DERR_SYMLINK, DERR_NO_REGFILE, DERR_SKIPPED.
960          * DERR_TOO_BIG, DERR_TOO_COMPRESSED, DERR_SPAM,
961          * DERR_CRC_ERROR, DERR_READSOCKET, DERR_WRITE_ERR
962          * and others are ignored */
963         if (drweb_s)
964           return m_panic_defer_3(scanent, CUS callout_address,
965             string_sprintf("drweb daemon retcode 0x%x (%s)", drweb_rc, drweb_s),
966             malware_daemon_ctx.sock);
967
968         /* no virus found */
969         malware_name = NULL;
970         }
971       break;
972       } /* drweb */
973 #endif
974
975 #ifndef DISABLE_MAL_AVE
976     case M_AVES: /* "aveserver" scanner type -------------------------------- */
977       {
978       uschar buf[32768];
979       int result;
980
981       /* read aveserver's greeting and see if it is ready (2xx greeting) */
982       buf[0] = 0;
983       recv_line(malware_daemon_ctx.sock, buf, sizeof(buf), tmo);
984
985       if (buf[0] != '2')                /* aveserver is having problems */
986         return m_panic_defer_3(scanent, CUS callout_address,
987           string_sprintf("unavailable (Responded: %s).",
988                           ((buf[0] != 0) ? buf : US "nothing") ),
989           malware_daemon_ctx.sock);
990
991       /* prepare our command */
992       (void)string_format(buf, sizeof(buf), "SCAN bPQRSTUW %s\r\n",
993                                                 eml_filename);
994
995       /* and send it */
996       DEBUG(D_acl) debug_printf_indent("Malware scan: issuing %s %s\n",
997         scanner_name, buf);
998       if (m_sock_send(malware_daemon_ctx.sock, buf, Ustrlen(buf), &errstr) < 0)
999         return m_panic_defer(scanent, CUS callout_address, errstr);
1000
1001       malware_name = NULL;
1002       result = 0;
1003       /* read response lines, find malware name and final response */
1004       while (recv_line(malware_daemon_ctx.sock, buf, sizeof(buf), tmo) > 0)
1005         {
1006         if (buf[0] == '2')
1007           break;
1008         if (buf[0] == '5')              /* aveserver is having problems */
1009           {
1010           result = m_panic_defer(scanent, CUS callout_address,
1011              string_sprintf("unable to scan file %s (Responded: %s).",
1012                              eml_filename, buf));
1013           break;
1014           }
1015         if (Ustrncmp(buf,"322",3) == 0)
1016           {
1017           uschar *p = Ustrchr(&buf[4], ' ');
1018           *p = '\0';
1019           malware_name = string_copy(&buf[4]);
1020           }
1021         }
1022
1023       if (m_sock_send(malware_daemon_ctx.sock, US"quit\r\n", 6, &errstr) < 0)
1024         return m_panic_defer(scanent, CUS callout_address, errstr);
1025
1026       /* read aveserver's greeting and see if it is ready (2xx greeting) */
1027       buf[0] = 0;
1028       recv_line(malware_daemon_ctx.sock, buf, sizeof(buf), tmo);
1029
1030       if (buf[0] != '2')                /* aveserver is having problems */
1031         return m_panic_defer_3(scanent, CUS callout_address,
1032           string_sprintf("unable to quit dialogue (Responded: %s).",
1033                         ((buf[0] != 0) ? buf : US "nothing") ),
1034           malware_daemon_ctx.sock);
1035
1036       if (result == DEFER)
1037         {
1038         (void)close(malware_daemon_ctx.sock);
1039         return DEFER;
1040         }
1041       break;
1042       } /* aveserver */
1043 #endif
1044
1045 #ifndef DISABLE_MAL_FSECURE
1046     case M_FSEC: /* "fsecure" scanner type ---------------------------------- */
1047       {
1048       int i, bread = 0;
1049       uschar * file_name;
1050       uschar av_buffer[1024];
1051       static uschar *cmdopt[] = { US"CONFIGURE\tARCHIVE\t1\n",
1052                                       US"CONFIGURE\tTIMEOUT\t0\n",
1053                                       US"CONFIGURE\tMAXARCH\t5\n",
1054                                       US"CONFIGURE\tMIME\t1\n" };
1055
1056       malware_name = NULL;
1057
1058       DEBUG(D_acl) debug_printf_indent("Malware scan: issuing %s scan [%s]\n",
1059           scanner_name, scanner_options);
1060       /* pass options */
1061       memset(av_buffer, 0, sizeof(av_buffer));
1062       for (i = 0; i != nelem(cmdopt); i++)
1063         {
1064
1065         if (m_sock_send(malware_daemon_ctx.sock, cmdopt[i], Ustrlen(cmdopt[i]), &errstr) < 0)
1066           return m_panic_defer(scanent, CUS callout_address, errstr);
1067
1068         bread = ip_recv(&malware_daemon_ctx, av_buffer, sizeof(av_buffer), tmo);
1069         if (bread > 0) av_buffer[bread]='\0';
1070         if (bread < 0)
1071           return m_panic_defer_3(scanent, CUS callout_address,
1072             string_sprintf("unable to read answer %d (%s)", i, strerror(errno)),
1073             malware_daemon_ctx.sock);
1074         for (int j = 0; j < bread; j++)
1075           if (av_buffer[j] == '\r' || av_buffer[j] == '\n')
1076             av_buffer[j] ='@';
1077         }
1078
1079       /* pass the mailfile to fsecure */
1080       file_name = string_sprintf("SCAN\t%s\n", eml_filename);
1081
1082       if (m_sock_send(malware_daemon_ctx.sock, file_name, Ustrlen(file_name), &errstr) < 0)
1083         return m_panic_defer(scanent, CUS callout_address, errstr);
1084
1085       /* set up match */
1086       /* todo also SUSPICION\t */
1087       if (!fsec_re)
1088         fsec_re = m_pcre_compile(fsec_re_str, &errstr);
1089
1090       /* read report, linewise. Apply a timeout as the Fsecure daemon
1091       sometimes wants an answer to "PING" but they won't tell us what */
1092         {
1093         uschar * p = av_buffer;
1094         uschar * q;
1095
1096         for (;;)
1097           {
1098           errno = ETIMEDOUT;
1099           i =  av_buffer+sizeof(av_buffer)-p;
1100           if ((bread= ip_recv(&malware_daemon_ctx, p, i-1, tmo)) < 0)
1101             return m_panic_defer_3(scanent, CUS callout_address,
1102               string_sprintf("unable to read result (%s)", strerror(errno)),
1103               malware_daemon_ctx.sock);
1104
1105           for (p[bread] = '\0'; (q = Ustrchr(p, '\n')); p = q+1)
1106             {
1107             *q = '\0';
1108
1109             /* Really search for virus again? */
1110             if (!malware_name)
1111               /* try matcher on the line, grab substring */
1112               malware_name = m_pcre_exec(fsec_re, p);
1113
1114             if (Ustrstr(p, "OK\tScan ok."))
1115               goto fsec_found;
1116             }
1117
1118           /* copy down the trailing partial line then read another chunk */
1119           i =  av_buffer+sizeof(av_buffer)-p;
1120           memmove(av_buffer, p, i);
1121           p = av_buffer+i;
1122           }
1123         }
1124
1125       fsec_found:
1126         break;
1127       } /* fsecure */
1128 #endif
1129
1130 #ifndef DISABLE_MAL_KAV
1131     case M_KAVD: /* "kavdaemon" scanner type -------------------------------- */
1132       {
1133       time_t t;
1134       uschar tmpbuf[1024];
1135       uschar * scanrequest;
1136       int kav_rc;
1137       unsigned long kav_reportlen;
1138       int bread;
1139       const pcre *kav_re;
1140       uschar *p;
1141
1142       /* get current date and time, build scan request */
1143       time(&t);
1144       /* pdp note: before the eml_filename parameter, this scanned the
1145       directory; not finding documentation, so we'll strip off the directory.
1146       The side-effect is that the test framework scanning may end up in
1147       scanning more than was requested, but for the normal interface, this is
1148       fine. */
1149
1150       strftime(CS tmpbuf, sizeof(tmpbuf), "%d %b %H:%M:%S", localtime(&t));
1151       scanrequest = string_sprintf("<0>%s:%s", CS tmpbuf, eml_filename);
1152       p = Ustrrchr(scanrequest, '/');
1153       if (p)
1154         *p = '\0';
1155
1156       DEBUG(D_acl) debug_printf_indent("Malware scan: issuing %s scan [%s]\n",
1157           scanner_name, scanner_options);
1158
1159       /* send scan request */
1160       if (m_sock_send(malware_daemon_ctx.sock, scanrequest, Ustrlen(scanrequest)+1, &errstr) < 0)
1161         return m_panic_defer(scanent, CUS callout_address, errstr);
1162
1163       /* wait for result */
1164       if (!recv_len(malware_daemon_ctx.sock, tmpbuf, 2, tmo))
1165         return m_panic_defer_3(scanent, CUS callout_address,
1166                             US"unable to read 2 bytes from socket.", malware_daemon_ctx.sock);
1167
1168       /* get errorcode from one nibble */
1169       kav_rc = tmpbuf[ test_byte_order()==LITTLE_MY_ENDIAN ? 0 : 1 ] & 0x0F;
1170       switch(kav_rc)
1171       {
1172       case 5: case 6: /* improper kavdaemon configuration */
1173         return m_panic_defer_3(scanent, CUS callout_address,
1174                 US"please reconfigure kavdaemon to NOT disinfect or remove infected files.",
1175                 malware_daemon_ctx.sock);
1176       case 1:
1177         return m_panic_defer_3(scanent, CUS callout_address,
1178                 US"reported 'scanning not completed' (code 1).", malware_daemon_ctx.sock);
1179       case 7:
1180         return m_panic_defer_3(scanent, CUS callout_address,
1181                 US"reported 'kavdaemon damaged' (code 7).", malware_daemon_ctx.sock);
1182       }
1183
1184       /* code 8 is not handled, since it is ambiguous. It appears mostly on
1185       bounces where part of a file has been cut off */
1186
1187       /* "virus found" return codes (2-4) */
1188       if (kav_rc > 1 && kav_rc < 5)
1189         {
1190         int report_flag = 0;
1191
1192         /* setup default virus name */
1193         malware_name = US"unknown";
1194
1195         report_flag = tmpbuf[ test_byte_order() == LITTLE_MY_ENDIAN ? 1 : 0 ];
1196
1197         /* read the report, if available */
1198         if (report_flag == 1)
1199           {
1200           /* read report size */
1201           if (!recv_len(malware_daemon_ctx.sock, &kav_reportlen, 4, tmo))
1202             return m_panic_defer_3(scanent, CUS callout_address,
1203                   US"cannot read report size", malware_daemon_ctx.sock);
1204
1205           /* it's possible that avp returns av_buffer[1] == 1 but the
1206           reportsize is 0 (!?) */
1207           if (kav_reportlen > 0)
1208             {
1209             /* set up match regex, depends on retcode */
1210             if (kav_rc == 3)
1211               {
1212               if (!kav_re_sus) kav_re_sus = m_pcre_compile(kav_re_sus_str, &errstr);
1213               kav_re = kav_re_sus;
1214               }
1215             else
1216               {
1217               if (!kav_re_inf) kav_re_inf = m_pcre_compile(kav_re_inf_str, &errstr);
1218               kav_re = kav_re_inf;
1219               }
1220
1221             /* read report, linewise.  Using size from stream to read amount of data
1222             from same stream is safe enough. */
1223             /* coverity[tainted_data] */
1224             while (kav_reportlen > 0)
1225               {
1226               if ((bread = recv_line(malware_daemon_ctx.sock, tmpbuf, sizeof(tmpbuf), tmo)) < 0)
1227                 break;
1228               kav_reportlen -= bread+1;
1229
1230               /* try matcher on the line, grab substring */
1231               if ((malware_name = m_pcre_exec(kav_re, tmpbuf)))
1232                 break;
1233               }
1234             }
1235           }
1236         }
1237       else /* no virus found */
1238         malware_name = NULL;
1239
1240       break;
1241       }
1242 #endif
1243
1244 #ifndef DISABLE_MAL_CMDLINE
1245     case M_CMDL: /* "cmdline" scanner type ---------------------------------- */
1246       {
1247       const uschar *cmdline_scanner = scanner_options;
1248       const pcre *cmdline_trigger_re;
1249       const pcre *cmdline_regex_re;
1250       uschar * file_name;
1251       uschar * commandline;
1252       void (*eximsigchld)(int);
1253       void (*eximsigpipe)(int);
1254       FILE *scanner_out = NULL;
1255       int scanner_fd;
1256       FILE *scanner_record = NULL;
1257       uschar linebuffer[32767];
1258       int rcnt;
1259       int trigger = 0;
1260       uschar *p;
1261
1262       if (!cmdline_scanner)
1263         return m_panic_defer(scanent, NULL, errstr);
1264
1265       /* find scanner output trigger */
1266       cmdline_trigger_re = m_pcre_nextinlist(&av_scanner_work, &sep,
1267                                 "missing trigger specification", &errstr);
1268       if (!cmdline_trigger_re)
1269         return m_panic_defer(scanent, NULL, errstr);
1270
1271       /* find scanner name regex */
1272       cmdline_regex_re = m_pcre_nextinlist(&av_scanner_work, &sep,
1273                           "missing virus name regex specification", &errstr);
1274       if (!cmdline_regex_re)
1275         return m_panic_defer(scanent, NULL, errstr);
1276
1277       /* prepare scanner call; despite the naming, file_name holds a directory
1278       name which is documented as the value given to %s. */
1279
1280       file_name = string_copy(eml_filename);
1281       p = Ustrrchr(file_name, '/');
1282       if (p)
1283         *p = '\0';
1284       commandline = string_sprintf(CS cmdline_scanner, file_name);
1285
1286       /* redirect STDERR too */
1287       commandline = string_sprintf("%s 2>&1", commandline);
1288
1289       DEBUG(D_acl) debug_printf_indent("Malware scan: issuing %s scan [%s]\n",
1290               scanner_name, commandline);
1291
1292       /* store exims signal handlers */
1293       eximsigchld = signal(SIGCHLD,SIG_DFL);
1294       eximsigpipe = signal(SIGPIPE,SIG_DFL);
1295
1296       if (!(scanner_out = popen(CS commandline,"r")))
1297         {
1298         int err = errno;
1299         signal(SIGCHLD,eximsigchld); signal(SIGPIPE,eximsigpipe);
1300         return m_panic_defer(scanent, NULL,
1301           string_sprintf("call (%s) failed: %s.", commandline, strerror(err)));
1302         }
1303       scanner_fd = fileno(scanner_out);
1304
1305       file_name = string_sprintf("%s/%s_scanner_output", eml_dir, message_id);
1306
1307       if (!(scanner_record = modefopen(file_name, "wb", SPOOL_MODE)))
1308         {
1309         int err = errno;
1310         (void) pclose(scanner_out);
1311         signal(SIGCHLD,eximsigchld); signal(SIGPIPE,eximsigpipe);
1312         return m_panic_defer(scanent, NULL, string_sprintf(
1313             "opening scanner output file (%s) failed: %s.",
1314             file_name, strerror(err)));
1315         }
1316
1317       /* look for trigger while recording output */
1318       while ((rcnt = recv_line(scanner_fd, linebuffer,
1319                       sizeof(linebuffer), tmo)))
1320         {
1321         if (rcnt < 0)
1322           {
1323           int err = errno;
1324           if (rcnt == -1)
1325             break;
1326           (void) pclose(scanner_out);
1327           signal(SIGCHLD,eximsigchld); signal(SIGPIPE,eximsigpipe);
1328           return m_panic_defer(scanent, NULL, string_sprintf(
1329               "unable to read from scanner (%s): %s",
1330               commandline, strerror(err)));
1331           }
1332
1333         if (Ustrlen(linebuffer) > fwrite(linebuffer, 1, Ustrlen(linebuffer), scanner_record))
1334           {
1335           /* short write */
1336           (void) pclose(scanner_out);
1337           signal(SIGCHLD,eximsigchld); signal(SIGPIPE,eximsigpipe);
1338           return m_panic_defer(scanent, NULL, string_sprintf(
1339             "short write on scanner output file (%s).", file_name));
1340           }
1341         putc('\n', scanner_record);
1342         /* try trigger match */
1343         if (  !trigger
1344            && regex_match_and_setup(cmdline_trigger_re, linebuffer, 0, -1)
1345            )
1346           trigger = 1;
1347         }
1348
1349       (void)fclose(scanner_record);
1350       sep = pclose(scanner_out);
1351       signal(SIGCHLD,eximsigchld); signal(SIGPIPE,eximsigpipe);
1352       if (sep != 0)
1353           return m_panic_defer(scanent, NULL,
1354               sep == -1
1355               ? string_sprintf("running scanner failed: %s", strerror(sep))
1356               : string_sprintf("scanner returned error code: %d", sep));
1357
1358       if (trigger)
1359         {
1360         uschar * s;
1361         /* setup default virus name */
1362         malware_name = US"unknown";
1363
1364         /* re-open the scanner output file, look for name match */
1365         scanner_record = Ufopen(file_name, "rb");
1366         while (Ufgets(linebuffer, sizeof(linebuffer), scanner_record))
1367           if ((s = m_pcre_exec(cmdline_regex_re, linebuffer))) /* try match */
1368             malware_name = s;
1369         (void)fclose(scanner_record);
1370         }
1371       else /* no virus found */
1372         malware_name = NULL;
1373       break;
1374       } /* cmdline */
1375 #endif
1376
1377 #ifndef DISABLE_MAL_SOPHIE
1378     case M_SOPHIE: /* "sophie" scanner type --------------------------------- */
1379       {
1380       int bread = 0;
1381       uschar *p;
1382       uschar * file_name;
1383       uschar av_buffer[1024];
1384
1385       /* pass the scan directory to sophie */
1386       file_name = string_copy(eml_filename);
1387       if ((p = Ustrrchr(file_name, '/')))
1388         *p = '\0';
1389
1390       DEBUG(D_acl) debug_printf_indent("Malware scan: issuing %s scan [%s]\n",
1391           scanner_name, scanner_options);
1392
1393       if (  write(malware_daemon_ctx.sock, file_name, Ustrlen(file_name)) < 0
1394          || write(malware_daemon_ctx.sock, "\n", 1) != 1
1395          )
1396         return m_panic_defer_3(scanent, CUS callout_address,
1397           string_sprintf("unable to write to UNIX socket (%s)", scanner_options),
1398           malware_daemon_ctx.sock);
1399
1400       /* wait for result */
1401       memset(av_buffer, 0, sizeof(av_buffer));
1402       if ((bread = ip_recv(&malware_daemon_ctx, av_buffer, sizeof(av_buffer), tmo)) <= 0)
1403         return m_panic_defer_3(scanent, CUS callout_address,
1404           string_sprintf("unable to read from UNIX socket (%s)", scanner_options),
1405           malware_daemon_ctx.sock);
1406
1407       /* infected ? */
1408       if (av_buffer[0] == '1') {
1409         uschar * s = Ustrchr(av_buffer, '\n');
1410         if (s)
1411           *s = '\0';
1412         malware_name = string_copy(&av_buffer[2]);
1413       }
1414       else if (!strncmp(CS av_buffer, "-1", 2))
1415         return m_panic_defer_3(scanent, CUS callout_address,
1416                 US"scanner reported error", malware_daemon_ctx.sock);
1417       else /* all ok, no virus */
1418         malware_name = NULL;
1419
1420       break;
1421       }
1422 #endif
1423
1424 #ifndef DISABLE_MAL_CLAM
1425     case M_CLAMD: /* "clamd" scanner type ----------------------------------- */
1426       {
1427 /* This code was originally contributed by David Saez */
1428 /* There are three scanning methods available to us:
1429 *  (1) Use the SCAN command, pointing to a file in the filesystem
1430 *  (2) Use the STREAM command, send the data on a separate port
1431 *  (3) Use the zINSTREAM command, send the data inline
1432 * The zINSTREAM command was introduced with ClamAV 0.95, which marked
1433 * STREAM deprecated; see: http://wiki.clamav.net/bin/view/Main/UpgradeNotes095
1434 * In Exim, we use SCAN if using a Unix-domain socket or explicitly told that
1435 * the TCP-connected daemon is actually local; otherwise we use zINSTREAM
1436 * See Exim bug 926 for details.  */
1437
1438       uschar *p, *vname, *result_tag;
1439       int bread=0;
1440       uschar av_buffer[1024];
1441       uschar *hostname = US"";
1442       host_item connhost;
1443       uschar *clamav_fbuf;
1444       int clam_fd, result;
1445       off_t fsize;
1446       unsigned int fsize_uint;
1447       BOOL use_scan_command = FALSE;
1448       clamd_address * cv[MAX_CLAMD_SERVERS];
1449       int num_servers = 0;
1450       uint32_t send_size, send_final_zeroblock;
1451       blob cmd_str;
1452
1453       /*XXX if unixdomain socket, only one server supported. Needs fixing;
1454       there's no reason we should not mix local and remote servers */
1455
1456       if (*scanner_options == '/')
1457         {
1458         clamd_address * cd;
1459         const uschar * sublist;
1460         int subsep = ' ';
1461
1462         /* Local file; so we def want to use_scan_command and don't want to try
1463          * passing IP/port combinations */
1464         use_scan_command = TRUE;
1465         cd = (clamd_address *) store_get(sizeof(clamd_address), FALSE);
1466
1467         /* extract socket-path part */
1468         sublist = scanner_options;
1469         cd->hostspec = string_nextinlist(&sublist, &subsep, NULL, 0);
1470
1471         /* parse options */
1472         if (clamd_option(cd, sublist, &subsep) != OK)
1473           return m_panic_defer(scanent, NULL,
1474             string_sprintf("bad option '%s'", scanner_options));
1475         cv[0] = cd;
1476         }
1477       else
1478         {
1479         /* Go through the rest of the list of host/port and construct an array
1480          * of servers to try. The first one is the bit we just passed from
1481          * scanner_options so process that first and then scan the remainder of
1482          * the address buffer */
1483         do
1484           {
1485           clamd_address * cd;
1486           const uschar * sublist;
1487           int subsep = ' ';
1488           uschar * s;
1489
1490           /* The 'local' option means use the SCAN command over the network
1491            * socket (ie common file storage in use) */
1492           /*XXX we could accept this also as a local option? */
1493           if (strcmpic(scanner_options, US"local") == 0)
1494             {
1495             use_scan_command = TRUE;
1496             continue;
1497             }
1498
1499           cd = (clamd_address *) store_get(sizeof(clamd_address), FALSE);
1500
1501           /* extract host and port part */
1502           sublist = scanner_options;
1503           if (!(cd->hostspec = string_nextinlist(&sublist, &subsep, NULL, 0)))
1504             {
1505             (void) m_panic_defer(scanent, NULL,
1506                       string_sprintf("missing address: '%s'", scanner_options));
1507             continue;
1508             }
1509           if (!(s = string_nextinlist(&sublist, &subsep, NULL, 0)))
1510             {
1511             (void) m_panic_defer(scanent, NULL,
1512                       string_sprintf("missing port: '%s'", scanner_options));
1513             continue;
1514             }
1515           cd->tcp_port = atoi(CS s);
1516
1517           /* parse options */
1518           /*XXX should these options be common over scanner types? */
1519           if (clamd_option(cd, sublist, &subsep) != OK)
1520             return m_panic_defer(scanent, NULL,
1521               string_sprintf("bad option '%s'", scanner_options));
1522
1523           cv[num_servers++] = cd;
1524           if (num_servers >= MAX_CLAMD_SERVERS)
1525             {
1526             (void) m_panic_defer(scanent, NULL,
1527                   US"More than " MAX_CLAMD_SERVERS_S " clamd servers "
1528                   "specified; only using the first " MAX_CLAMD_SERVERS_S );
1529             break;
1530             }
1531           } while ((scanner_options = string_nextinlist(&av_scanner_work, &sep,
1532                                         NULL, 0)));
1533
1534         /* check if we have at least one server */
1535         if (!num_servers)
1536           return m_panic_defer(scanent, NULL,
1537             US"no useable server addresses in malware configuration option.");
1538         }
1539
1540       /* See the discussion of response formats below to see why we really
1541       don't like colons in filenames when passing filenames to ClamAV. */
1542       if (use_scan_command && Ustrchr(eml_filename, ':'))
1543         return m_panic_defer(scanent, NULL,
1544           string_sprintf("local/SCAN mode incompatible with" \
1545             " : in path to email filename [%s]", eml_filename));
1546
1547       /* Set up the very first data we will be sending */
1548       if (!use_scan_command)
1549         { cmd_str.data = US"zINSTREAM"; cmd_str.len = 10; }
1550       else
1551         {
1552         cmd_str.data = string_sprintf("SCAN %s\n", eml_filename);
1553         cmd_str.len = Ustrlen(cmd_str.data);
1554         }
1555
1556       /* We have some network servers specified */
1557       if (num_servers)
1558         {
1559         /* Confirmed in ClamAV source (0.95.3) that the TCPAddr option of clamd
1560          * only supports AF_INET, but we should probably be looking to the
1561          * future and rewriting this to be protocol-independent anyway. */
1562
1563         while (num_servers > 0)
1564           {
1565           int i = random_number(num_servers);
1566           clamd_address * cd = cv[i];
1567
1568           DEBUG(D_acl) debug_printf_indent("trying server name %s, port %u\n",
1569                          cd->hostspec, cd->tcp_port);
1570
1571           /* Lookup the host. This is to ensure that we connect to the same IP
1572            * on both connections (as one host could resolve to multiple ips) */
1573           for (;;)
1574             {
1575             /*XXX we trust that the cmd_str is ideempotent */
1576             if ((malware_daemon_ctx.sock = m_tcpsocket(cd->hostspec, cd->tcp_port,
1577                                     &connhost, &errstr, &cmd_str)) >= 0)
1578               {
1579               /* Connection successfully established with a server */
1580               hostname = cd->hostspec;
1581               cmd_str.len = 0;
1582               break;
1583               }
1584             if (cd->retry <= 0) break;
1585             while (cd->retry > 0) cd->retry = sleep(cd->retry);
1586             }
1587           if (malware_daemon_ctx.sock >= 0)
1588             break;
1589
1590           (void) m_panic_defer(scanent, CUS callout_address, errstr);
1591
1592           /* Remove the server from the list. XXX We should free the memory */
1593           num_servers--;
1594           for (; i < num_servers; i++)
1595             cv[i] = cv[i+1];
1596           }
1597
1598         if (num_servers == 0)
1599           return m_panic_defer(scanent, NULL, US"all servers failed");
1600         }
1601       else
1602         for (;;)
1603           {
1604           if ((malware_daemon_ctx.sock = ip_unixsocket(cv[0]->hostspec, &errstr)) >= 0)
1605             {
1606             hostname = cv[0]->hostspec;
1607             break;
1608             }
1609           if (cv[0]->retry <= 0)
1610             return m_panic_defer(scanent, CUS callout_address, errstr);
1611           while (cv[0]->retry > 0) cv[0]->retry = sleep(cv[0]->retry);
1612           }
1613
1614       /* have socket in variable "sock"; command to use is semi-independent of
1615        * the socket protocol.  We use SCAN if is local (either Unix/local
1616        * domain socket, or explicitly told local) else we stream the data.
1617        * How we stream the data depends upon how we were built.  */
1618
1619       if (!use_scan_command)
1620         {
1621         /* New protocol: "zINSTREAM\n" followed by a sequence of <length><data>
1622         chunks, <n> a 4-byte number (network order), terminated by a zero-length
1623         chunk. */
1624
1625         DEBUG(D_acl) debug_printf_indent(
1626             "Malware scan: issuing %s new-style remote scan (zINSTREAM)\n",
1627             scanner_name);
1628
1629         /* Pass the string to ClamAV (10 = "zINSTREAM\0"), if not already sent */
1630         if (cmd_str.len)
1631           if (send(malware_daemon_ctx.sock, cmd_str.data, cmd_str.len, 0) < 0)
1632             return m_panic_defer_3(scanent, CUS hostname,
1633               string_sprintf("unable to send zINSTREAM to socket (%s)",
1634                 strerror(errno)),
1635               malware_daemon_ctx.sock);
1636
1637         /* calc file size */
1638         if ((clam_fd = exim_open2(CS eml_filename, O_RDONLY)) < 0)
1639           {
1640           int err = errno;
1641           return m_panic_defer_3(scanent, NULL,
1642             string_sprintf("can't open spool file %s: %s",
1643               eml_filename, strerror(err)),
1644             malware_daemon_ctx.sock);
1645           }
1646         if ((fsize = lseek(clam_fd, 0, SEEK_END)) < 0)
1647           {
1648           int err;
1649 b_seek:   err = errno;
1650           (void)close(clam_fd);
1651           return m_panic_defer_3(scanent, NULL,
1652             string_sprintf("can't seek spool file %s: %s",
1653               eml_filename, strerror(err)),
1654             malware_daemon_ctx.sock);
1655           }
1656         fsize_uint = (unsigned int) fsize;
1657         if ((off_t)fsize_uint != fsize)
1658           {
1659           (void)close(clam_fd);
1660           return m_panic_defer_3(scanent, NULL,
1661             string_sprintf("seeking spool file %s, size overflow",
1662               eml_filename),
1663             malware_daemon_ctx.sock);
1664           }
1665         if (lseek(clam_fd, 0, SEEK_SET) < 0)
1666           goto b_seek;
1667
1668         if (!(clamav_fbuf = store_malloc(fsize_uint)))
1669           {
1670           (void)close(clam_fd);
1671           return m_panic_defer_3(scanent, NULL,
1672             string_sprintf("unable to allocate memory %u for file (%s)",
1673               fsize_uint, eml_filename),
1674             malware_daemon_ctx.sock);
1675           }
1676
1677         if ((result = read(clam_fd, clamav_fbuf, fsize_uint)) < 0)
1678           {
1679           int err = errno;
1680           store_free(clamav_fbuf); (void)close(clam_fd);
1681           return m_panic_defer_3(scanent, NULL,
1682             string_sprintf("can't read spool file %s: %s",
1683               eml_filename, strerror(err)),
1684             malware_daemon_ctx.sock);
1685           }
1686         (void)close(clam_fd);
1687
1688         /* send file body to socket */
1689         send_size = htonl(fsize_uint);
1690         send_final_zeroblock = 0;
1691         if ((send(malware_daemon_ctx.sock, &send_size, sizeof(send_size), 0) < 0) ||
1692             (send(malware_daemon_ctx.sock, clamav_fbuf, fsize_uint, 0) < 0) ||
1693             (send(malware_daemon_ctx.sock, &send_final_zeroblock, sizeof(send_final_zeroblock), 0) < 0))
1694           {
1695           store_free(clamav_fbuf);
1696           return m_panic_defer_3(scanent, NULL,
1697             string_sprintf("unable to send file body to socket (%s)", hostname),
1698             malware_daemon_ctx.sock);
1699           }
1700         store_free(clamav_fbuf);
1701         }
1702       else
1703         { /* use scan command */
1704         /* Send a SCAN command pointing to a filename; then in the then in the
1705          * scan-method-neutral part, read the response back */
1706
1707 /* ================================================================= */
1708
1709         /* Prior to the reworking post-Exim-4.72, this scanned a directory,
1710         which dates to when ClamAV needed us to break apart the email into the
1711         MIME parts (eg, with the now deprecated demime condition coming first).
1712         Some time back, ClamAV gained the ability to deconstruct the emails, so
1713         doing this would actually have resulted in the mail attachments being
1714         scanned twice, in the broken out files and from the original .eml.
1715         Since ClamAV now handles emails (and has for quite some time) we can
1716         just use the email file itself. */
1717         /* Pass the string to ClamAV (7 = "SCAN \n" + \0), if not already sent */
1718
1719         DEBUG(D_acl) debug_printf_indent(
1720             "Malware scan: issuing %s local-path scan [%s]\n",
1721             scanner_name, scanner_options);
1722
1723         if (cmd_str.len)
1724           if (send(malware_daemon_ctx.sock, cmd_str.data, cmd_str.len, 0) < 0)
1725             return m_panic_defer_3(scanent, CUS callout_address,
1726               string_sprintf("unable to write to socket (%s)", strerror(errno)),
1727               malware_daemon_ctx.sock);
1728
1729         /* Do not shut down the socket for writing; a user report noted that
1730          * clamd 0.70 does not react well to this. */
1731         }
1732       /* Commands have been sent, no matter which scan method or connection
1733        * type we're using; now just read the result, independent of method. */
1734
1735       /* Read the result */
1736       memset(av_buffer, 0, sizeof(av_buffer));
1737       bread = ip_recv(&malware_daemon_ctx, av_buffer, sizeof(av_buffer), tmo);
1738       (void)close(malware_daemon_ctx.sock);
1739       malware_daemon_ctx.sock = -1;
1740       malware_daemon_ctx.tls_ctx = NULL;
1741
1742       if (bread <= 0)
1743         return m_panic_defer(scanent, CUS callout_address,
1744           string_sprintf("unable to read from socket (%s)",
1745           errno == 0 ? "EOF" : strerror(errno)));
1746
1747       if (bread == sizeof(av_buffer))
1748         return m_panic_defer(scanent, CUS callout_address,
1749                 US"buffer too small");
1750       /* We're now assured of a NULL at the end of av_buffer */
1751
1752       /* Check the result. ClamAV returns one of two result formats.
1753       In the basic mode, the response is of the form:
1754         infected: -> "<filename>: <virusname> FOUND"
1755         not-infected: -> "<filename>: OK"
1756         error: -> "<filename>: <errcode> ERROR
1757       If the ExtendedDetectionInfo option has been turned on, then we get:
1758         "<filename>: <virusname>(<virushash>:<virussize>) FOUND"
1759       for the infected case.  Compare:
1760 /tmp/eicar.com: Eicar-Test-Signature FOUND
1761 /tmp/eicar.com: Eicar-Test-Signature(44d88612fea8a8f36de82e1278abb02f:68) FOUND
1762
1763       In the streaming case, clamd uses the filename "stream" which you should
1764       be able to verify with { ktrace clamdscan --stream /tmp/eicar.com }.  (The
1765       client app will replace "stream" with the original filename before returning
1766       results to stdout, but the trace shows the data).
1767
1768       We will assume that the pathname passed to clamd from Exim does not contain
1769       a colon.  We will have whined loudly above if the eml_filename does (and we're
1770       passing a filename to clamd). */
1771
1772       if (!(*av_buffer))
1773         return m_panic_defer(scanent, CUS callout_address,
1774                 US"ClamAV returned null");
1775
1776       /* strip newline at the end (won't be present for zINSTREAM)
1777       (also any trailing whitespace, which shouldn't exist, but we depend upon
1778       this below, so double-check) */
1779       p = av_buffer + Ustrlen(av_buffer) - 1;
1780       if (*p == '\n') *p = '\0';
1781
1782       DEBUG(D_acl) debug_printf_indent("Malware response: %s\n", av_buffer);
1783
1784       while (isspace(*--p) && (p > av_buffer))
1785         *p = '\0';
1786       if (*p) ++p;
1787
1788       /* colon in returned output? */
1789       if(!(p = Ustrchr(av_buffer,':')))
1790         return m_panic_defer(scanent, CUS callout_address, string_sprintf(
1791                   "ClamAV returned malformed result (missing colon): %s",
1792                   av_buffer));
1793
1794       /* strip filename */
1795       while (*p && isspace(*++p)) /**/;
1796       vname = p;
1797
1798       /* It would be bad to encounter a virus with "FOUND" in part of the name,
1799       but we should at least be resistant to it. */
1800       p = Ustrrchr(vname, ' ');
1801       result_tag = p ? p+1 : vname;
1802
1803       if (Ustrcmp(result_tag, "FOUND") == 0)
1804         {
1805         /* p should still be the whitespace before the result_tag */
1806         while (isspace(*p)) --p;
1807         *++p = '\0';
1808         /* Strip off the extended information too, which will be in parens
1809         after the virus name, with no intervening whitespace. */
1810         if (*--p == ')')
1811           {
1812           /* "(hash:size)", so previous '(' will do; if not found, we have
1813           a curious virus name, but not an error. */
1814           p = Ustrrchr(vname, '(');
1815           if (p)
1816             *p = '\0';
1817           }
1818         malware_name = string_copy(vname);
1819         DEBUG(D_acl) debug_printf_indent("Malware found, name \"%s\"\n", malware_name);
1820
1821         }
1822       else if (Ustrcmp(result_tag, "ERROR") == 0)
1823         return m_panic_defer(scanent, CUS callout_address,
1824           string_sprintf("ClamAV returned: %s", av_buffer));
1825
1826       else if (Ustrcmp(result_tag, "OK") == 0)
1827         {
1828         /* Everything should be OK */
1829         malware_name = NULL;
1830         DEBUG(D_acl) debug_printf_indent("Malware not found\n");
1831
1832         }
1833       else
1834         return m_panic_defer(scanent, CUS callout_address,
1835           string_sprintf("unparseable response from ClamAV: {%s}", av_buffer));
1836
1837       break;
1838       } /* clamd */
1839 #endif
1840
1841 #ifndef DISABLE_MAL_SOCK
1842     case M_SOCK: /* "sock" scanner type ------------------------------------- */
1843     /* This code was derived by Martin Poole from the clamd code contributed
1844        by David Saez and the cmdline code
1845     */
1846       {
1847       int bread;
1848       uschar * commandline;
1849       uschar av_buffer[1024];
1850       uschar * linebuffer;
1851       uschar * sockline_scanner;
1852       uschar sockline_scanner_default[] = "%s\n";
1853       const pcre *sockline_trig_re;
1854       const pcre *sockline_name_re;
1855
1856       /* find scanner command line */
1857       if (  (sockline_scanner = string_nextinlist(&av_scanner_work, &sep,
1858                                           NULL, 0))
1859          && *sockline_scanner
1860          )
1861       { /* check for no expansions apart from one %s */
1862         uschar * s = Ustrchr(sockline_scanner, '%');
1863         if (s++)
1864           if ((*s != 's' && *s != '%') || Ustrchr(s+1, '%'))
1865             return m_panic_defer_3(scanent, NULL,
1866                                   US"unsafe sock scanner call spec", malware_daemon_ctx.sock);
1867       }
1868       else
1869         sockline_scanner = sockline_scanner_default;
1870       DEBUG(D_acl) debug_printf_indent("%15s%10s'%s'\n", "", "cmdline: ",
1871         string_printing(sockline_scanner));
1872
1873       /* find scanner output trigger */
1874       sockline_trig_re = m_pcre_nextinlist(&av_scanner_work, &sep,
1875                                 "missing trigger specification", &errstr);
1876       if (!sockline_trig_re)
1877         return m_panic_defer_3(scanent, NULL, errstr, malware_daemon_ctx.sock);
1878
1879       /* find virus name regex */
1880       sockline_name_re = m_pcre_nextinlist(&av_scanner_work, &sep,
1881                           "missing virus name regex specification", &errstr);
1882       if (!sockline_name_re)
1883         return m_panic_defer_3(scanent, NULL, errstr, malware_daemon_ctx.sock);
1884
1885       /* prepare scanner call - security depends on expansions check above */
1886       commandline = string_sprintf( CS sockline_scanner, CS eml_filename);
1887       DEBUG(D_acl) debug_printf_indent("%15s%10s'%s'\n", "", "expanded: ",
1888         string_printing(commandline));
1889
1890       /* Pass the command string to the socket */
1891       if (m_sock_send(malware_daemon_ctx.sock, commandline, Ustrlen(commandline), &errstr) < 0)
1892         return m_panic_defer(scanent, CUS callout_address, errstr);
1893
1894       /* Read the result */
1895       bread = ip_recv(&malware_daemon_ctx, av_buffer, sizeof(av_buffer), tmo);
1896
1897       if (bread <= 0)
1898         return m_panic_defer_3(scanent, CUS callout_address,
1899           string_sprintf("unable to read from socket (%s)", strerror(errno)),
1900           malware_daemon_ctx.sock);
1901
1902       if (bread == sizeof(av_buffer))
1903         return m_panic_defer_3(scanent, CUS callout_address,
1904                 US"buffer too small", malware_daemon_ctx.sock);
1905       av_buffer[bread] = '\0';
1906       linebuffer = string_copy(av_buffer);
1907       DEBUG(D_acl) debug_printf_indent("%15s%10s'%s'\n", "", "answer: ",
1908         string_printing(linebuffer));
1909
1910       /* try trigger match */
1911       if (regex_match_and_setup(sockline_trig_re, linebuffer, 0, -1))
1912         {
1913         if (!(malware_name = m_pcre_exec(sockline_name_re, av_buffer)))
1914           malware_name = US "unknown";
1915         DEBUG(D_acl) debug_printf_indent("%15s%10s'%s'\n", "", "name: ",
1916           string_printing(malware_name));
1917         }
1918       else /* no virus found */
1919         malware_name = NULL;
1920       break;
1921       }
1922 #endif
1923
1924 #ifndef DISABLE_MAL_MKS
1925     case M_MKSD: /* "mksd" scanner type ------------------------------------- */
1926       {
1927       char *mksd_options_end;
1928       int mksd_maxproc = 1;  /* default, if no option supplied */
1929       int retval;
1930
1931       if (scanner_options)
1932         {
1933         mksd_maxproc = (int)strtol(CS scanner_options, &mksd_options_end, 10);
1934         if (  *scanner_options == '\0'
1935            || *mksd_options_end != '\0'
1936            || mksd_maxproc < 1
1937            || mksd_maxproc > 32
1938            )
1939           return m_panic_defer(scanent, CUS callout_address,
1940             string_sprintf("invalid option '%s'", scanner_options));
1941         }
1942
1943       if((malware_daemon_ctx.sock = ip_unixsocket(US "/var/run/mksd/socket", &errstr)) < 0)
1944         return m_panic_defer(scanent, CUS callout_address, errstr);
1945
1946       malware_name = NULL;
1947
1948       DEBUG(D_acl) debug_printf_indent("Malware scan: issuing %s scan\n", scanner_name);
1949
1950       if ((retval = mksd_scan_packed(scanent, malware_daemon_ctx.sock, eml_filename, tmo)) != OK)
1951         {
1952         close (malware_daemon_ctx.sock);
1953         return retval;
1954         }
1955       break;
1956       }
1957 #endif
1958
1959 #ifndef DISABLE_MAL_AVAST
1960     case M_AVAST: /* "avast" scanner type ----------------------------------- */
1961       {
1962       uschar buf[1024];
1963       uschar * scanrequest;
1964       enum {AVA_HELO, AVA_OPT, AVA_RSP, AVA_DONE} avast_stage;
1965       int nread;
1966       uschar * error_message = NULL;
1967       BOOL more_data = FALSE;
1968       BOOL strict = TRUE;
1969
1970       /* According to Martin Tuma @avast the protocol uses "escaped
1971       whitespace", that is, every embedded whitespace is backslash
1972       escaped, as well as backslash is protected by backslash.
1973       The returned lines contain the name of the scanned file, a tab
1974       and the [ ] marker.
1975       [+] - not infected
1976       [L] - infected
1977       [E] - some error occurred
1978       Such marker follows the first non-escaped TAB.  For more information
1979       see avast-protocol(5)
1980
1981       We observed two cases:
1982       -> SCAN /file
1983       <- /file [E]0.0 Error 13 Permission denied
1984       <- 451 SCAN Engine error 13 permission denied
1985
1986       -> SCAN /file
1987       <- /file… [E]3.0 Error 41120 The file is a decompression bomb
1988       <- /file… [+]2.0
1989       <- /file… [+]2.0 0 Eicar Test Virus!!!
1990       <- 200 SCAN OK
1991
1992       If the scanner returns 4xx, DEFER is a good decision, combined
1993       with a panic log entry, to get the admin's attention.
1994
1995       If the scanner returns 200, we reject it as malware, if found any,
1996       or, in case of an error, we set the malware message to the error
1997       string.
1998
1999       Some of the >= 42000 errors are message related - usually some
2000       broken archives etc, but some of them are e.g. license related.
2001       Once the license expires the engine starts returning errors for
2002       every scanning attempt.  I¹ have the full list of the error codes
2003       but it is not a public API and is subject to change. It is hard
2004       for me to say what you should do in case of an engine error. You
2005       can have a “Treat * unscanned file as infection” policy or “Treat
2006       unscanned file as clean” policy.  ¹) Jakub Bednar
2007
2008        */
2009
2010       if (  (  !ava_re_clean
2011             && !(ava_re_clean = m_pcre_compile(ava_re_clean_str, &errstr)))
2012          || (  !ava_re_virus
2013             && !(ava_re_virus = m_pcre_compile(ava_re_virus_str, &errstr)))
2014          || (  !ava_re_error
2015             && !(ava_re_error = m_pcre_compile(ava_re_error_str, &errstr)))
2016          )
2017         return malware_panic_defer(errstr);
2018
2019       /* wait for result */
2020       for (avast_stage = AVA_HELO;
2021            (nread = recv_line(malware_daemon_ctx.sock, buf, sizeof(buf), tmo)) > 0;
2022           )
2023         {
2024         int slen = Ustrlen(buf);
2025         if (slen >= 1)
2026           {
2027
2028           /* Multi line responses are bracketed between 210 … and nnn … */
2029           if (Ustrncmp(buf, "210", 3) == 0)
2030             {
2031             more_data = 1;
2032             continue;
2033             }
2034           else if (more_data && isdigit(buf[0])) more_data = 0;
2035
2036           switch (avast_stage)
2037             {
2038             case AVA_HELO:
2039               if (more_data) continue;
2040               if (Ustrncmp(buf, "220", 3) != 0)
2041                 goto endloop;                   /* require a 220 */
2042               goto sendreq;
2043
2044             case AVA_OPT:
2045               if (more_data) continue;
2046               if (Ustrncmp(buf, "200", 3) != 0)
2047                 goto endloop;                   /* require a 200 */
2048
2049             sendreq:
2050               {
2051               int len;
2052               /* Check for another option to send. Newline-terminate it. */
2053               if ((scanrequest = string_nextinlist(&av_scanner_work, &sep,
2054                                 NULL, 0)))
2055                 {
2056                 if (Ustrcmp(scanrequest, "pass_unscanned") == 0)
2057                   {
2058                   DEBUG(D_acl) debug_printf_indent("pass unscanned files as clean\n");
2059                   strict = FALSE;
2060                   goto sendreq;
2061                   }
2062                 scanrequest = string_sprintf("%s\n", scanrequest);
2063                 avast_stage = AVA_OPT;          /* just sent option */
2064                 DEBUG(D_acl) debug_printf_indent("send to avast OPTION: %s", scanrequest);
2065                 }
2066               else
2067                 {
2068                 scanrequest = string_sprintf("SCAN %s\n", eml_dir);
2069                 avast_stage = AVA_RSP;          /* just sent command */
2070                 DEBUG(D_acl) debug_printf_indent("send to avast REQUEST: SCAN %s\n", eml_dir);
2071                 }
2072
2073               /* send config-cmd or scan-request to socket */
2074               len = Ustrlen(scanrequest);
2075               if (send(malware_daemon_ctx.sock, scanrequest, len, 0) == -1)
2076                 {
2077                 scanrequest[len-1] = '\0';
2078                 return m_panic_defer_3(scanent, CUS callout_address, string_sprintf(
2079                       "unable to send request '%s' to socket (%s): %s",
2080                       scanrequest, scanner_options, strerror(errno)), malware_daemon_ctx.sock);
2081                 }
2082               break;
2083               }
2084
2085             case AVA_RSP:
2086
2087               if (isdigit(buf[0]))  /* We're done */
2088                 goto endloop;
2089
2090               if (malware_name)     /* Nothing else matters, just read on */
2091                 break;
2092
2093               if (pcre_exec(ava_re_clean, NULL, CS buf, slen, 0, 0, NULL, 0) == 0)
2094                 break;
2095
2096               if ((malware_name = m_pcre_exec(ava_re_virus, buf)))
2097                 {
2098                 unescape(malware_name);
2099                 DEBUG(D_acl)
2100                   debug_printf_indent("unescaped malware name: '%s'\n", malware_name);
2101                 break;
2102                 }
2103
2104               if (strict)           /* treat scanner errors as malware */
2105                 {
2106                 if ((malware_name = m_pcre_exec(ava_re_error, buf)))
2107                   {
2108                   unescape(malware_name);
2109                   DEBUG(D_acl)
2110                     debug_printf_indent("unescaped error message: '%s'\n", malware_name);
2111                   break;
2112                   }
2113                 }
2114               else if (pcre_exec(ava_re_error, NULL, CS buf, slen, 0, 0, NULL, 0) == 0)
2115                 {
2116                 log_write(0, LOG_MAIN, "internal scanner error (ignored): %s", buf);
2117                 break;
2118                 }
2119
2120               /* here also for any unexpected response from the scanner */
2121               DEBUG(D_acl) debug_printf("avast response not handled: '%s'\n", buf);
2122
2123               goto endloop;
2124
2125             default:    log_write(0, LOG_PANIC, "%s:%d:%s: should not happen",
2126                             __FILE__, __LINE__, __FUNCTION__);
2127             }
2128           }
2129         }
2130
2131       endloop:
2132
2133       if (nread == -1) error_message = US"EOF from scanner";
2134       else if (nread < 0) error_message = US"timeout from scanner";
2135       else if (nread == 0) error_message = US"got nothing from scanner";
2136       else if (buf[0] != '2') error_message = buf;
2137
2138       DEBUG(D_acl) debug_printf_indent("sent to avast QUIT\n");
2139       if (send(malware_daemon_ctx.sock, "QUIT\n", 5, 0) == -1)
2140         return m_panic_defer_3(scanent, CUS callout_address,
2141           string_sprintf("unable to send quit request to socket (%s): %s",
2142             scanner_options, strerror(errno)), malware_daemon_ctx.sock);
2143
2144       if (error_message)
2145         return m_panic_defer_3(scanent, CUS callout_address, error_message, malware_daemon_ctx.sock);
2146
2147       }
2148 #endif
2149   }     /* scanner type switch */
2150
2151   if (malware_daemon_ctx.sock >= 0)
2152     (void) close (malware_daemon_ctx.sock);
2153   malware_ok = TRUE;                    /* set "been here, done that" marker */
2154   }
2155
2156 /* match virus name against pattern (caseless ------->----------v) */
2157 if (malware_name && regex_match_and_setup(re, malware_name, 0, -1))
2158   {
2159   DEBUG(D_acl) debug_printf_indent(
2160       "Matched regex to malware [%s] [%s]\n", malware_re, malware_name);
2161   return OK;
2162   }
2163 else
2164   return FAIL;
2165 }
2166
2167
2168 /*************************************************
2169 *          Scan an email for malware             *
2170 *************************************************/
2171
2172 /* This is the normal interface for scanning an email, which doesn't need a
2173 filename; it's a wrapper around the malware_file function.
2174
2175 Arguments:
2176   malware_re  match condition for "malware="
2177   timeout     if nonzero, timeout in seconds
2178
2179 Returns:      Exim message processing code (OK, FAIL, DEFER, ...)
2180               where true means malware was found (condition applies)
2181 */
2182 int
2183 malware(const uschar * malware_re, int timeout)
2184 {
2185 int ret = malware_internal(malware_re, NULL, timeout);
2186
2187 if (ret == DEFER) av_failed = TRUE;
2188 return ret;
2189 }
2190
2191
2192 /*************************************************
2193 *          Scan a file for malware               *
2194 *************************************************/
2195
2196 /* This is a test wrapper for scanning an email, which is not used in
2197 normal processing.  Scan any file, using the Exim scanning interface.
2198 This function tampers with various global variables so is unsafe to use
2199 in any other context.
2200
2201 Arguments:
2202   eml_filename  a file holding the message to be scanned
2203
2204 Returns:        Exim message processing code (OK, FAIL, DEFER, ...)
2205                 where true means malware was found (condition applies)
2206 */
2207 int
2208 malware_in_file(uschar *eml_filename)
2209 {
2210 uschar message_id_buf[64];
2211 int ret;
2212
2213 /* spool_mbox() assumes various parameters exist, when creating
2214 the relevant directory and the email within */
2215
2216 (void) string_format(message_id_buf, sizeof(message_id_buf),
2217     "dummy-%d", vaguely_random_number(INT_MAX));
2218 message_id = message_id_buf;
2219 sender_address = US"malware-sender@example.net";
2220 return_path = US"";
2221 recipients_list = NULL;
2222 receive_add_recipient(US"malware-victim@example.net", -1);
2223 f.enable_dollar_recipients = TRUE;
2224
2225 ret = malware_internal(US"*", eml_filename, 0);
2226
2227 Ustrncpy(spooled_message_id, message_id, sizeof(spooled_message_id));
2228 spool_mbox_ok = 1;
2229
2230 /* don't set no_mbox_unspool; at present, there's no way for it to become
2231 set, but if that changes, then it should apply to these tests too */
2232
2233 unspool_mbox();
2234
2235 /* silence static analysis tools */
2236 message_id = NULL;
2237
2238 return ret;
2239 }
2240
2241
2242 void
2243 malware_init(void)
2244 {
2245 if (!malware_default_re)
2246   malware_default_re = regex_must_compile(malware_regex_default, FALSE, TRUE);
2247
2248 #ifndef DISABLE_MAL_DRWEB
2249 if (!drweb_re)
2250   drweb_re = regex_must_compile(drweb_re_str, FALSE, TRUE);
2251 #endif
2252 #ifndef DISABLE_MAL_FSECURE
2253 if (!fsec_re)
2254   fsec_re = regex_must_compile(fsec_re_str, FALSE, TRUE);
2255 #endif
2256 #ifndef DISABLE_MAL_KAV
2257 if (!kav_re_sus)
2258   kav_re_sus = regex_must_compile(kav_re_sus_str, FALSE, TRUE);
2259 if (!kav_re_inf)
2260   kav_re_inf = regex_must_compile(kav_re_inf_str, FALSE, TRUE);
2261 #endif
2262 #ifndef DISABLE_MAL_AVAST
2263 if (!ava_re_clean)
2264   ava_re_clean = regex_must_compile(ava_re_clean_str, FALSE, TRUE);
2265 if (!ava_re_virus)
2266   ava_re_virus = regex_must_compile(ava_re_virus_str, FALSE, TRUE);
2267 if (!ava_re_error)
2268   ava_re_error = regex_must_compile(ava_re_error_str, FALSE, TRUE);
2269 #endif
2270 #ifndef DISABLE_MAL_FFROT6D
2271 if (!fprot6d_re_error)
2272   fprot6d_re_error = regex_must_compile(fprot6d_re_error_str, FALSE, TRUE);
2273 if (!fprot6d_re_virus)
2274   fprot6d_re_virus = regex_must_compile(fprot6d_re_virus_str, FALSE, TRUE);
2275 #endif
2276 }
2277
2278
2279 gstring *
2280 malware_show_supported(gstring * g)
2281 {
2282 g = string_cat(g, US"Malware:");
2283 for (struct scan * sc = m_scans; sc->scancode != (scanner_t)-1; sc++)
2284   g = string_fmt_append(g, " %s", sc->name);
2285 return string_cat(g, US"\n");
2286 }
2287
2288
2289 # endif /*!MACRO_PREDEF*/
2290 #endif /*WITH_CONTENT_SCAN*/
2291 /*
2292  * vi: aw ai sw=2
2293  */