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