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