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