tidying
[exim.git] / src / src / malware.c
1 /*************************************************
2 *     Exim - an Internet mail transport agent    *
3 *************************************************/
4
5 /* Copyright (c) Tom Kistner <tom@duncanthrax.net> 2003 - 2015
6  * License: GPL
7  * Copyright (c) The Exim Maintainers 2015 - 2020
8  */
9
10 /* Code for calling virus (malware) scanners. Called from acl.c. */
11
12 #include "exim.h"
13 #ifdef WITH_CONTENT_SCAN        /* entire file */
14
15 typedef enum {
16 #ifndef DISABLE_MAL_FFROTD
17         M_FPROTD,
18 #endif
19 #ifndef DISABLE_MAL_FFROT6D
20         M_FPROT6D,
21 #endif
22 #ifndef DISABLE_MAL_DRWEB
23         M_DRWEB,
24 #endif
25 #ifndef DISABLE_MAL_AVE
26         M_AVES,
27 #endif
28 #ifndef DISABLE_MAL_FSECURE
29         M_FSEC,
30 #endif
31 #ifndef DISABLE_MAL_KAV
32         M_KAVD,
33 #endif
34 #ifndef DISABLE_MAL_SOPHIE
35         M_SOPHIE,
36 #endif
37 #ifndef DISABLE_MAL_CLAM
38         M_CLAMD,
39 #endif
40 #ifndef DISABLE_MAL_MKS
41         M_MKSD,
42 #endif
43 #ifndef DISABLE_MAL_AVAST
44         M_AVAST,
45 #endif
46 #ifndef DISABLE_MAL_SOCK
47         M_SOCK,
48 #endif
49 #ifndef DISABLE_MAL_CMDLINE
50         M_CMDL,
51 #endif
52         M_DUMMY
53         } scanner_t;
54 typedef enum {MC_NONE, MC_TCP, MC_UNIX, MC_STRM} contype_t;
55 static struct scan
56 {
57   scanner_t     scancode;
58   const uschar * name;
59   const uschar * options_default;
60   contype_t     conn;
61 } m_scans[] =
62 {
63 #ifndef DISABLE_MAL_FFROTD
64   { M_FPROTD,   US"f-protd",    US"localhost 10200-10204",            MC_TCP },
65 #endif
66 #ifndef DISABLE_MAL_FFROT6D
67   { M_FPROT6D,  US"f-prot6d",   US"localhost 10200",                  MC_TCP },
68 #endif
69 #ifndef DISABLE_MAL_DRWEB
70   { M_DRWEB,    US"drweb",      US"/usr/local/drweb/run/drwebd.sock", MC_STRM },
71 #endif
72 #ifndef DISABLE_MAL_AVE
73   { M_AVES,     US"aveserver",  US"/var/run/aveserver",               MC_UNIX },
74 #endif
75 #ifndef DISABLE_MAL_FSECURE
76   { M_FSEC,     US"fsecure",    US"/var/run/.fsav",                   MC_UNIX },
77 #endif
78 #ifndef DISABLE_MAL_KAV
79   { M_KAVD,     US"kavdaemon",  US"/var/run/AvpCtl",                  MC_UNIX },
80 #endif
81 #ifndef DISABLE_MAL_SOPHIE
82   { M_SOPHIE,   US"sophie",     US"/var/run/sophie",                  MC_UNIX },
83 #endif
84 #ifndef DISABLE_MAL_CLAM
85   { M_CLAMD,    US"clamd",      US"/tmp/clamd",                       MC_NONE },
86 #endif
87 #ifndef DISABLE_MAL_MKS
88   { M_MKSD,     US"mksd",       NULL,                                 MC_NONE },
89 #endif
90 #ifndef DISABLE_MAL_AVAST
91   { M_AVAST,    US"avast",      US"/var/run/avast/scan.sock",         MC_STRM },
92 #endif
93 #ifndef DISABLE_MAL_SOCK
94   { M_SOCK,     US"sock",       US"/tmp/malware.sock",                MC_STRM },
95 #endif
96 #ifndef DISABLE_MAL_CMDLINE
97   { M_CMDL,     US"cmdline",    NULL,                                 MC_NONE },
98 #endif
99   { -1,         NULL,           NULL, MC_NONE }         /* end-marker */
100 };
101
102 /******************************************************************************/
103 # ifdef MACRO_PREDEF            /* build solely to predefine macros */
104
105 #  include "macro_predef.h"
106
107 void
108 features_malware(void)
109 {
110 const uschar * s;
111 uschar * t;
112 uschar buf[64];
113
114 spf(buf, sizeof(buf), US"_HAVE_MALWARE_");
115
116 for (const struct scan * sc = m_scans; sc->scancode != -1; sc++)
117   {
118   for (s = sc->name, t = buf+14; *s; s++) if (*s != '-')
119     *t++ = toupper(*s);
120   *t = '\0';
121   builtin_macro_create(buf);
122   }
123 }
124
125 /******************************************************************************/
126 # else  /*!MACRO_PREDEF, main build*/
127
128
129 #define MALWARE_TIMEOUT 120     /* default timeout, seconds */
130
131 static const uschar * malware_regex_default = US ".+";
132 static const pcre * malware_default_re = NULL;
133
134
135
136 #ifndef DISABLE_MAL_CLAM
137 /* The maximum number of clamd servers that are supported in the configuration */
138 # define MAX_CLAMD_SERVERS 32
139 # define MAX_CLAMD_SERVERS_S "32"
140
141 typedef struct clamd_address {
142   uschar * hostspec;
143   unsigned tcp_port;
144   unsigned retry;
145 } clamd_address;
146 #endif
147
148
149 #ifndef DISABLE_MAL_DRWEB
150 # define DRWEBD_SCAN_CMD             (1)     /* scan file, buffer or diskfile */
151 # define DRWEBD_RETURN_VIRUSES       (1<<0)   /* ask daemon return to us viruses names from report */
152 # define DRWEBD_IS_MAIL              (1<<19)  /* say to daemon that format is "archive MAIL" */
153
154 # define DERR_READ_ERR               (1<<0)   /* read error */
155 # define DERR_NOMEMORY               (1<<2)   /* no memory */
156 # define DERR_TIMEOUT                (1<<9)   /* scan timeout has run out */
157 # define DERR_BAD_CALL               (1<<15)  /* wrong command */
158
159 static const uschar * drweb_re_str = US "infected\\swith\\s*(.+?)$";
160 static const pcre * drweb_re = NULL;
161 #endif
162
163 #ifndef DISABLE_MAL_FSECURE
164 static const uschar * fsec_re_str = US "\\S{0,5}INFECTED\\t[^\\t]*\\t([^\\t]+)\\t\\S*$";
165 static const pcre * fsec_re = NULL;
166 #endif
167
168 #ifndef DISABLE_MAL_KAV
169 static const uschar * kav_re_sus_str = US "suspicion:\\s*(.+?)\\s*$";
170 static const uschar * kav_re_inf_str = US "infected:\\s*(.+?)\\s*$";
171 static const pcre * kav_re_sus = NULL;
172 static const pcre * kav_re_inf = NULL;
173 #endif
174
175 #ifndef DISABLE_MAL_AVAST
176 static const uschar * ava_re_clean_str = US "(?!\\\\)\\t\\[\\+\\]";
177 static const uschar * ava_re_virus_str = US "(?!\\\\)\\t\\[L\\]\\d+\\.0\\t0\\s(.*)";
178 static const uschar * ava_re_error_str = US "(?!\\\\)\\t\\[E\\]\\d+\\.0\\tError\\s\\d+\\s(.*)";
179 static const pcre * ava_re_clean = NULL;
180 static const pcre * ava_re_virus = NULL;
181 static const pcre * ava_re_error = NULL;
182 #endif
183
184 #ifndef DISABLE_MAL_FFROT6D
185 static const uschar * fprot6d_re_error_str = US "^\\d+\\s<(.+?)>$";
186 static const uschar * fprot6d_re_virus_str = US "^\\d+\\s<infected:\\s+(.+?)>\\s+.+$";
187 static const pcre * fprot6d_re_error = NULL;
188 static const pcre * fprot6d_re_virus = NULL;
189 #endif
190
191
192
193 /******************************************************************************/
194
195 #ifndef DISABLE_MAL_KAV
196 /* Routine to check whether a system is big- or little-endian.
197    Ripped from http://www.faqs.org/faqs/graphics/fileformats-faq/part4/section-7.html
198    Needed for proper kavdaemon implementation. Sigh. */
199 # define BIG_MY_ENDIAN      0
200 # define LITTLE_MY_ENDIAN   1
201 static int test_byte_order(void);
202 static inline int
203 test_byte_order()
204 {
205   short int word = 0x0001;
206   char *byte = CS  &word;
207   return(byte[0] ? LITTLE_MY_ENDIAN : BIG_MY_ENDIAN);
208 }
209 #endif
210
211 BOOL malware_ok = FALSE;
212
213 /* Gross hacks for the -bmalware option; perhaps we should just create
214 the scan directory normally for that case, but look into rigging up the
215 needed header variables if not already set on the command-line? */
216 extern int spool_mbox_ok;
217 extern uschar spooled_message_id[MESSAGE_ID_LENGTH+1];
218
219
220 /* Some (currently avast only) use backslash escaped whitespace,
221 this function undoes these escapes */
222
223 #ifndef DISABLE_MAL_AVAST
224 static inline void
225 unescape(uschar *p)
226 {
227 uschar *p0;
228 for (; *p; ++p)
229   if (*p == '\\' && (isspace(p[1]) || p[1] == '\\'))
230     for (p0 = p; *p0; ++p0) *p0 = p0[1];
231 }
232 #endif
233
234 /* --- malware_*_defer --- */
235 static inline int
236 malware_panic_defer(const uschar * str)
237 {
238 log_write(0, LOG_MAIN|LOG_PANIC, "malware acl condition: %s", str);
239 return DEFER;
240 }
241 static inline int
242 malware_log_defer(const uschar * str)
243 {
244 log_write(0, LOG_MAIN, "malware acl condition: %s", str);
245 return DEFER;
246 }
247 /* --- m_*_defer --- */
248 static inline int
249 m_panic_defer(struct scan * scanent, const uschar * hostport,
250   const uschar * str)
251 {
252 return malware_panic_defer(string_sprintf("%s %s : %s",
253   scanent->name, hostport ? hostport : CUS"", str));
254 }
255 /* --- m_*_defer_3 */
256 static inline int
257 m_panic_defer_3(struct scan * scanent, const uschar * hostport,
258   const uschar * str, int fd_to_close)
259 {
260 DEBUG(D_acl) debug_print_socket(fd_to_close);
261 (void) close(fd_to_close);
262 return m_panic_defer(scanent, hostport, str);
263 }
264
265 /*************************************************/
266
267 #ifndef DISABLE_MAL_CLAM
268 /* Only used by the Clamav code, which is working from a list of servers and
269 uses the returned in_addr to get a second connection to the same system.
270 */
271 static inline int
272 m_tcpsocket(const uschar * hostname, unsigned int port,
273         host_item * host, uschar ** errstr, const blob * fastopen_blob)
274 {
275 return ip_connectedsocket(SOCK_STREAM, hostname, port, port, 5,
276                           host, errstr, fastopen_blob);
277 }
278 #endif
279
280 static int
281 m_sock_send(int sock, uschar * buf, int cnt, uschar ** errstr)
282 {
283 if (send(sock, buf, cnt, 0) < 0)
284   {
285   int err = errno;
286   (void)close(sock);
287   *errstr = string_sprintf("unable to send to socket (%s): %s",
288          buf, strerror(err));
289   return -1;
290   }
291 return sock;
292 }
293
294 static const pcre *
295 m_pcre_compile(const uschar * re, uschar ** errstr)
296 {
297 const uschar * rerror;
298 int roffset;
299 const pcre * cre;
300
301 if (!(cre = pcre_compile(CS re, PCRE_COPT, CCSS &rerror, &roffset, NULL)))
302   *errstr= string_sprintf("regular expression error in '%s': %s at offset %d",
303       re, rerror, roffset);
304 return cre;
305 }
306
307 uschar *
308 m_pcre_exec(const pcre * cre, uschar * text)
309 {
310 int ovector[10*3];
311 int i = pcre_exec(cre, NULL, CS text, Ustrlen(text), 0, 0,
312               ovector, nelem(ovector));
313 uschar * substr = NULL;
314 if (i >= 2)                             /* Got it */
315   pcre_get_substring(CS text, ovector, i, 1, CCSS &substr);
316 return substr;
317 }
318
319 static const pcre *
320 m_pcre_nextinlist(const uschar ** list, int * sep,
321  char * listerr, uschar ** errstr)
322 {
323 const uschar * list_ele;
324 const pcre * cre = NULL;
325
326 if (!(list_ele = string_nextinlist(list, sep, NULL, 0)))
327   *errstr = US listerr;
328 else
329   {
330   DEBUG(D_acl) debug_printf_indent("%15s%10s'%s'\n", "", "RE: ",
331     string_printing(list_ele));
332   cre = m_pcre_compile(CUS list_ele, errstr);
333   }
334 return cre;
335 }
336
337
338 /*
339  Simple though inefficient wrapper for reading a line.  Drop CRs and the
340  trailing newline. Can return early on buffer full. Null-terminate.
341  Apply initial timeout if no data ready.
342
343  Return: number of chars - zero for an empty line
344          -1 on EOF
345          -2 on timeout or error
346 */
347 static int
348 recv_line(int fd, uschar * buffer, int bsize, time_t tmo)
349 {
350 uschar * p = buffer;
351 ssize_t rcv;
352 BOOL ok = FALSE;
353
354 if (!fd_ready(fd, tmo))
355   return -2;
356
357 /*XXX tmo handling assumes we always get a whole line */
358 /* read until \n */
359 errno = 0;
360 while ((rcv = read(fd, p, 1)) > 0)
361   {
362   ok = TRUE;
363   if (p-buffer > bsize-2) break;
364   if (*p == '\n') break;
365   if (*p != '\r') p++;
366   }
367 if (!ok)
368   {
369   DEBUG(D_acl)
370     {
371     debug_printf_indent("Malware scan: read %s (%s)\n",
372                 rcv==0 ? "EOF" : "error", strerror(errno));
373     debug_print_socket(fd);
374     }
375   return rcv==0 ? -1 : -2;
376   }
377 *p = '\0';
378
379 DEBUG(D_acl) debug_printf_indent("Malware scan: read '%s'\n", buffer);
380 return p - buffer;
381 }
382
383 /* return TRUE iff size as requested */
384 #ifndef DISABLE_MAL_DRWEB
385 static BOOL
386 recv_len(int sock, void * buf, int size, time_t tmo)
387 {
388 return fd_ready(sock, tmo)
389   ? recv(sock, buf, size, 0) == size
390   : FALSE;
391 }
392 #endif
393
394
395
396 #ifndef DISABLE_MAL_MKS
397 /* ============= private routines for the "mksd" scanner type ============== */
398
399 # include <sys/uio.h>
400
401 static inline int
402 mksd_writev (int sock, struct iovec * iov, int iovcnt)
403 {
404 int i;
405
406 for (;;)
407   {
408   do
409     i = writev (sock, iov, iovcnt);
410   while (i < 0 && errno == EINTR);
411   if (i <= 0)
412     {
413     (void) malware_panic_defer(
414             US"unable to write to mksd UNIX socket (/var/run/mksd/socket)");
415     return -1;
416     }
417   for (;;)      /* check for short write */
418     if (i >= iov->iov_len)
419       {
420       if (--iovcnt == 0)
421         return 0;
422       i -= iov->iov_len;
423       iov++;
424       }
425     else
426       {
427       iov->iov_len -= i;
428       iov->iov_base = CS iov->iov_base + i;
429       break;
430       }
431   }
432 }
433
434 static inline int
435 mksd_read_lines (int sock, uschar *av_buffer, int av_buffer_size, time_t tmo)
436 {
437 client_conn_ctx cctx = {.sock = sock};
438 int offset = 0;
439 int i;
440
441 do
442   {
443   i = ip_recv(&cctx, av_buffer+offset, av_buffer_size-offset, tmo);
444   if (i <= 0)
445     {
446     (void) malware_panic_defer(US"unable to read from mksd UNIX socket (/var/run/mksd/socket)");
447     return -1;
448     }
449
450   offset += i;
451   /* offset == av_buffer_size -> buffer full */
452   if (offset == av_buffer_size)
453     {
454     (void) malware_panic_defer(US"malformed reply received from mksd");
455     return -1;
456     }
457   } while (av_buffer[offset-1] != '\n');
458
459 av_buffer[offset] = '\0';
460 return offset;
461 }
462
463 static inline int
464 mksd_parse_line(struct scan * scanent, char * line)
465 {
466 char *p;
467
468 switch (*line)
469   {
470   case 'O': /* OK */
471     return OK;
472
473   case 'E':
474   case 'A': /* ERR */
475     if ((p = strchr (line, '\n')) != NULL)
476       *p = '\0';
477     return m_panic_defer(scanent, NULL,
478       string_sprintf("scanner failed: %s", line));
479
480   default: /* VIR */
481     if ((p = strchr (line, '\n')) != NULL)
482       {
483       *p = '\0';
484       if (  p-line > 5
485          && line[3] == ' '
486          && (p = strchr(line+4, ' ')) != NULL
487          && p-line > 4
488          )
489         {
490         *p = '\0';
491         malware_name = string_copy(US line+4);
492         return OK;
493         }
494       }
495     return m_panic_defer(scanent, NULL,
496       string_sprintf("malformed reply received: %s", line));
497   }
498 }
499
500 static int
501 mksd_scan_packed(struct scan * scanent, int sock, const uschar * scan_filename,
502   time_t tmo)
503 {
504 struct iovec iov[3];
505 const char *cmd = "MSQ\n";
506 uschar av_buffer[1024];
507
508 iov[0].iov_base = (void *) cmd;
509 iov[0].iov_len = 3;
510 iov[1].iov_base = (void *) scan_filename;
511 iov[1].iov_len = Ustrlen(scan_filename);
512 iov[2].iov_base = (void *) (cmd + 3);
513 iov[2].iov_len = 1;
514
515 if (mksd_writev (sock, iov, 3) < 0)
516   return DEFER;
517
518 if (mksd_read_lines (sock, av_buffer, sizeof (av_buffer), tmo) < 0)
519   return DEFER;
520
521 return mksd_parse_line (scanent, CS av_buffer);
522 }
523 #endif  /* MKSD */
524
525
526 #ifndef DISABLE_MAL_CLAM
527 static int
528 clamd_option(clamd_address * cd, const uschar * optstr, int * subsep)
529 {
530 uschar * s;
531
532 cd->retry = 0;
533 while ((s = string_nextinlist(&optstr, subsep, NULL, 0)))
534   if (Ustrncmp(s, "retry=", 6) == 0)
535     {
536     int sec = readconf_readtime((s += 6), '\0', FALSE);
537     if (sec < 0)
538       return FAIL;
539     cd->retry = sec;
540     }
541   else
542     return FAIL;
543 return OK;
544 }
545 #endif
546
547
548
549 /*************************************************
550 *          Scan content for malware              *
551 *************************************************/
552
553 /* This is an internal interface for scanning an email; the normal interface
554 is via malware(), or there's malware_in_file() used for testing/debugging.
555
556 Arguments:
557   malware_re    match condition for "malware="
558   scan_filename  the file holding the email to be scanned, if we're faking
559                 this up for the -bmalware test, else NULL
560   timeout       if nonzero, non-default timeoutl
561
562 Returns:        Exim message processing code (OK, FAIL, DEFER, ...)
563                 where true means malware was found (condition applies)
564 */
565 static int
566 malware_internal(const uschar * malware_re, const uschar * scan_filename,
567   int timeout)
568 {
569 int sep = 0;
570 const uschar *av_scanner_work = av_scanner;
571 uschar *scanner_name;
572 unsigned long mbox_size;
573 FILE *mbox_file;
574 const pcre *re;
575 uschar * errstr;
576 struct scan * scanent;
577 const uschar * scanner_options;
578 client_conn_ctx malware_daemon_ctx = {.sock = -1};
579 time_t tmo;
580 uschar * eml_filename, * eml_dir;
581
582 if (!malware_re)
583   return FAIL;          /* empty means "don't match anything" */
584
585 /* Ensure the eml mbox file is spooled up */
586
587 if (!(mbox_file = spool_mbox(&mbox_size, scan_filename, &eml_filename)))
588   return malware_panic_defer(US"error while creating mbox spool file");
589
590 /* None of our current scanners need the mbox file as a stream (they use
591 the name), so we can close it right away.  Get the directory too. */
592
593 (void) fclose(mbox_file);
594 eml_dir = string_copyn(eml_filename, Ustrrchr(eml_filename, '/') - eml_filename);
595
596 /* parse 1st option */
597 if (strcmpic(malware_re, US"false") == 0  ||  Ustrcmp(malware_re,"0") == 0)
598   return FAIL;          /* explicitly no matching */
599
600 /* special cases (match anything except empty) */
601 if (  strcmpic(malware_re,US"true") == 0
602    || Ustrcmp(malware_re,"*") == 0
603    || Ustrcmp(malware_re,"1") == 0
604    )
605   {
606   if (  !malware_default_re
607      && !(malware_default_re = m_pcre_compile(malware_regex_default, &errstr)))
608     return malware_panic_defer(errstr);
609   malware_re = malware_regex_default;
610   re = malware_default_re;
611   }
612
613 /* compile the regex, see if it works */
614 else if (!(re = m_pcre_compile(malware_re, &errstr)))
615   return malware_panic_defer(errstr);
616
617 /* if av_scanner starts with a dollar, expand it first */
618 if (*av_scanner == '$')
619   {
620   if (!(av_scanner_work = expand_string(av_scanner)))
621     return malware_panic_defer(
622          string_sprintf("av_scanner starts with $, but expansion failed: %s",
623          expand_string_message));
624
625   DEBUG(D_acl)
626     debug_printf_indent("Expanded av_scanner global: %s\n", av_scanner_work);
627   /* disable result caching in this case */
628   malware_name = NULL;
629   malware_ok = FALSE;
630   }
631
632 /* Do not scan twice (unless av_scanner is dynamic). */
633 if (!malware_ok)
634   {
635   /* find the scanner type from the av_scanner option */
636   if (!(scanner_name = string_nextinlist(&av_scanner_work, &sep, NULL, 0)))
637     return malware_panic_defer(US"av_scanner configuration variable is empty");
638   if (!timeout) timeout = MALWARE_TIMEOUT;
639   tmo = time(NULL) + timeout;
640
641   for (scanent = m_scans; ; scanent++)
642     {
643     if (!scanent->name)
644       return malware_panic_defer(string_sprintf("unknown scanner type '%s'",
645         scanner_name));
646     if (strcmpic(scanner_name, US scanent->name) != 0)
647       continue;
648     DEBUG(D_acl) debug_printf_indent("Malware scan:  %s tmo=%s\n",
649       scanner_name, readconf_printtime(timeout));
650
651     if (!(scanner_options = string_nextinlist(&av_scanner_work, &sep, NULL, 0)))
652       scanner_options = scanent->options_default;
653     if (scanent->conn == MC_NONE)
654       break;
655
656     DEBUG(D_acl) debug_printf_indent("%15s%10s%s\n", "", "socket: ", scanner_options);
657     switch(scanent->conn)
658     {
659     case MC_TCP:
660       malware_daemon_ctx.sock = ip_tcpsocket(scanner_options, &errstr, 5, NULL); break;
661     case MC_UNIX:
662       malware_daemon_ctx.sock = ip_unixsocket(scanner_options, &errstr);        break;
663     case MC_STRM:
664       malware_daemon_ctx.sock = ip_streamsocket(scanner_options, &errstr, 5, NULL); break;
665     default:
666       /* compiler quietening */ break;
667     }
668     if (malware_daemon_ctx.sock < 0)
669       return m_panic_defer(scanent, CUS callout_address, errstr);
670     break;
671   }
672
673   switch (scanent->scancode)
674     {
675 #ifndef DISABLE_MAL_FFROTD
676     case M_FPROTD: /* "f-protd" scanner type -------------------------------- */
677       {
678       uschar *fp_scan_option;
679       unsigned int detected=0, par_count=0;
680       uschar * scanrequest;
681       uschar buf[32768], *strhelper, *strhelper2;
682       uschar * malware_name_internal = NULL;
683       int len;
684
685       scanrequest = string_sprintf("GET %s", eml_filename);
686
687       while ((fp_scan_option = string_nextinlist(&av_scanner_work, &sep,
688                             NULL, 0)))
689         {
690         scanrequest = string_sprintf("%s%s%s", scanrequest,
691                                   par_count ? "%20" : "?", fp_scan_option);
692         par_count++;
693         }
694       scanrequest = string_sprintf("%s HTTP/1.0\r\n\r\n", scanrequest);
695       DEBUG(D_acl) debug_printf_indent("Malware scan: issuing %s: %s\n",
696         scanner_name, scanrequest);
697
698       /* send scan request */
699       if (m_sock_send(malware_daemon_ctx.sock, scanrequest, Ustrlen(scanrequest)+1, &errstr) < 0)
700         return m_panic_defer(scanent, CUS callout_address, errstr);
701
702       while ((len = recv_line(malware_daemon_ctx.sock, buf, sizeof(buf), tmo)) >= 0)
703         if (len > 0)
704           {
705           if (Ustrstr(buf, US"<detected type=\"") != NULL)
706             detected = 1;
707           else if (detected && (strhelper = Ustrstr(buf, US"<name>")))
708             {
709             if ((strhelper2 = Ustrstr(buf, US"</name>")) != NULL)
710               {
711               *strhelper2 = '\0';
712               malware_name_internal = string_copy(strhelper+6);
713               }
714             }
715           else if (Ustrstr(buf, US"<summary code=\""))
716             {
717             malware_name = Ustrstr(buf, US"<summary code=\"11\">")
718                 ? malware_name_internal : NULL;
719             break;
720             }
721           }
722       if (len < -1)
723         {
724         (void)close(malware_daemon_ctx.sock);
725         return DEFER;
726         }
727       break;
728       } /* f-protd */
729 #endif
730
731 #ifndef DISABLE_MAL_FFROT6D
732     case M_FPROT6D: /* "f-prot6d" scanner type ----------------------------------- */
733       {
734       int bread;
735       uschar * e;
736       uschar * linebuffer;
737       uschar * scanrequest;
738       uschar av_buffer[1024];
739
740       if ((!fprot6d_re_virus && !(fprot6d_re_virus = m_pcre_compile(fprot6d_re_virus_str, &errstr)))
741         || (!fprot6d_re_error && !(fprot6d_re_error = m_pcre_compile(fprot6d_re_error_str, &errstr))))
742         return malware_panic_defer(errstr);
743
744       scanrequest = string_sprintf("SCAN FILE %s\n", eml_filename);
745       DEBUG(D_acl) debug_printf_indent("Malware scan: issuing %s: %s\n",
746         scanner_name, scanrequest);
747
748       if (m_sock_send(malware_daemon_ctx.sock, scanrequest, Ustrlen(scanrequest), &errstr) < 0)
749         return m_panic_defer(scanent, CUS callout_address, errstr);
750
751       bread = ip_recv(&malware_daemon_ctx, av_buffer, sizeof(av_buffer), tmo);
752
753       if (bread <= 0)
754         return m_panic_defer_3(scanent, CUS callout_address,
755           string_sprintf("unable to read from socket (%s)", strerror(errno)),
756           malware_daemon_ctx.sock);
757
758       if (bread == sizeof(av_buffer))
759         return m_panic_defer_3(scanent, CUS callout_address,
760           US"buffer too small", malware_daemon_ctx.sock);
761
762       av_buffer[bread] = '\0';
763       linebuffer = string_copy(av_buffer);
764
765       m_sock_send(malware_daemon_ctx.sock, US"QUIT\n", 5, 0);
766
767       if ((e = m_pcre_exec(fprot6d_re_error, linebuffer)))
768         return m_panic_defer_3(scanent, CUS callout_address,
769           string_sprintf("scanner reported error (%s)", e), malware_daemon_ctx.sock);
770
771       if (!(malware_name = m_pcre_exec(fprot6d_re_virus, linebuffer)))
772         malware_name = NULL;
773
774       break;
775       }  /* f-prot6d */
776 #endif
777
778 #ifndef DISABLE_MAL_DRWEB
779     case M_DRWEB: /* "drweb" scanner type ----------------------------------- */
780   /* v0.1 - added support for tcp sockets          */
781   /* v0.0 - initial release -- support for unix sockets      */
782       {
783       int result;
784       off_t fsize;
785       unsigned int fsize_uint;
786       uschar * tmpbuf, *drweb_fbuf;
787       int drweb_rc, drweb_cmd, drweb_flags = 0x0000, drweb_fd,
788           drweb_vnum, drweb_slen, drweb_fin = 0x0000;
789
790       /* prepare variables */
791       drweb_cmd = htonl(DRWEBD_SCAN_CMD);
792       drweb_flags = htonl(DRWEBD_RETURN_VIRUSES | DRWEBD_IS_MAIL);
793
794       if (*scanner_options != '/')
795         {
796         /* calc file size */
797         if ((drweb_fd = exim_open2(CCS eml_filename, O_RDONLY)) == -1)
798           return m_panic_defer_3(scanent, NULL,
799             string_sprintf("can't open spool file %s: %s",
800               eml_filename, strerror(errno)),
801             malware_daemon_ctx.sock);
802
803         if ((fsize = lseek(drweb_fd, 0, SEEK_END)) == -1)
804           {
805           int err;
806 badseek:  err = errno;
807           (void)close(drweb_fd);
808           return m_panic_defer_3(scanent, NULL,
809             string_sprintf("can't seek spool file %s: %s",
810               eml_filename, strerror(err)),
811             malware_daemon_ctx.sock);
812           }
813         fsize_uint = (unsigned int) fsize;
814         if ((off_t)fsize_uint != fsize)
815           {
816           (void)close(drweb_fd);
817           return m_panic_defer_3(scanent, NULL,
818             string_sprintf("seeking spool file %s, size overflow",
819               eml_filename),
820             malware_daemon_ctx.sock);
821           }
822         drweb_slen = htonl(fsize);
823         if (lseek(drweb_fd, 0, SEEK_SET) < 0)
824           goto badseek;
825
826         DEBUG(D_acl) debug_printf_indent("Malware scan: issuing %s remote scan [%s]\n",
827             scanner_name, scanner_options);
828
829         /* send scan request */
830         if ((send(malware_daemon_ctx.sock, &drweb_cmd, sizeof(drweb_cmd), 0) < 0) ||
831             (send(malware_daemon_ctx.sock, &drweb_flags, sizeof(drweb_flags), 0) < 0) ||
832             (send(malware_daemon_ctx.sock, &drweb_fin, sizeof(drweb_fin), 0) < 0) ||
833             (send(malware_daemon_ctx.sock, &drweb_slen, sizeof(drweb_slen), 0) < 0))
834           {
835           (void)close(drweb_fd);
836           return m_panic_defer_3(scanent, CUS callout_address, string_sprintf(
837             "unable to send commands to socket (%s)", scanner_options),
838             malware_daemon_ctx.sock);
839           }
840
841         if (!(drweb_fbuf = store_malloc(fsize_uint)))
842           {
843           (void)close(drweb_fd);
844           return m_panic_defer_3(scanent, NULL,
845             string_sprintf("unable to allocate memory %u for file (%s)",
846               fsize_uint, eml_filename),
847             malware_daemon_ctx.sock);
848           }
849
850         if ((result = read (drweb_fd, drweb_fbuf, fsize)) == -1)
851           {
852           int err = errno;
853           (void)close(drweb_fd);
854           store_free(drweb_fbuf);
855           return m_panic_defer_3(scanent, NULL,
856             string_sprintf("can't read spool file %s: %s",
857               eml_filename, strerror(err)),
858             malware_daemon_ctx.sock);
859           }
860         (void)close(drweb_fd);
861
862         /* send file body to socket */
863         if (send(malware_daemon_ctx.sock, drweb_fbuf, fsize, 0) < 0)
864           {
865           store_free(drweb_fbuf);
866           return m_panic_defer_3(scanent, CUS callout_address, string_sprintf(
867             "unable to send file body to socket (%s)", scanner_options),
868             malware_daemon_ctx.sock);
869           }
870         store_free(drweb_fbuf);
871         }
872       else
873         {
874         drweb_slen = htonl(Ustrlen(eml_filename));
875
876         DEBUG(D_acl) debug_printf_indent("Malware scan: issuing %s local scan [%s]\n",
877             scanner_name, scanner_options);
878
879         /* send scan request */
880         if ((send(malware_daemon_ctx.sock, &drweb_cmd, sizeof(drweb_cmd), 0) < 0) ||
881             (send(malware_daemon_ctx.sock, &drweb_flags, sizeof(drweb_flags), 0) < 0) ||
882             (send(malware_daemon_ctx.sock, &drweb_slen, sizeof(drweb_slen), 0) < 0) ||
883             (send(malware_daemon_ctx.sock, eml_filename, Ustrlen(eml_filename), 0) < 0) ||
884             (send(malware_daemon_ctx.sock, &drweb_fin, sizeof(drweb_fin), 0) < 0))
885           return m_panic_defer_3(scanent, CUS callout_address, string_sprintf(
886             "unable to send commands to socket (%s)", scanner_options),
887             malware_daemon_ctx.sock);
888         }
889
890       /* wait for result */
891       if (!recv_len(malware_daemon_ctx.sock, &drweb_rc, sizeof(drweb_rc), tmo))
892         return m_panic_defer_3(scanent, CUS callout_address,
893                     US"unable to read return code", malware_daemon_ctx.sock);
894       drweb_rc = ntohl(drweb_rc);
895
896       if (!recv_len(malware_daemon_ctx.sock, &drweb_vnum, sizeof(drweb_vnum), tmo))
897         return m_panic_defer_3(scanent, CUS callout_address,
898                             US"unable to read the number of viruses", malware_daemon_ctx.sock);
899       drweb_vnum = ntohl(drweb_vnum);
900
901       /* "virus(es) found" if virus number is > 0 */
902       if (drweb_vnum)
903         {
904         gstring * g = NULL;
905
906         /* setup default virus name */
907         malware_name = US"unknown";
908
909         /* set up match regex */
910         if (!drweb_re)
911           drweb_re = m_pcre_compile(drweb_re_str, &errstr);
912
913         /* read and concatenate virus names into one string */
914         for (int i = 0; i < drweb_vnum; i++)
915           {
916           int ovector[10*3];
917
918           /* read the size of report */
919           if (!recv_len(malware_daemon_ctx.sock, &drweb_slen, sizeof(drweb_slen), tmo))
920             return m_panic_defer_3(scanent, CUS callout_address,
921                               US"cannot read report size", malware_daemon_ctx.sock);
922           drweb_slen = ntohl(drweb_slen);
923
924           /* assume tainted, since it is external input */
925           tmpbuf = store_get(drweb_slen, TRUE);
926
927           /* read report body */
928           if (!recv_len(malware_daemon_ctx.sock, tmpbuf, drweb_slen, tmo))
929             return m_panic_defer_3(scanent, CUS callout_address,
930                               US"cannot read report string", malware_daemon_ctx.sock);
931           tmpbuf[drweb_slen] = '\0';
932
933           /* try matcher on the line, grab substring */
934           result = pcre_exec(drweb_re, NULL, CS tmpbuf, Ustrlen(tmpbuf), 0, 0,
935                                   ovector, nelem(ovector));
936           if (result >= 2)
937             {
938             const char * pre_malware_nb;
939
940             pcre_get_substring(CS tmpbuf, ovector, result, 1, &pre_malware_nb);
941
942             if (i==0)   /* the first name we just copy to malware_name */
943               g = string_cat(NULL, US pre_malware_nb);
944
945             /*XXX could be string_append_listele? */
946             else        /* concatenate each new virus name to previous */
947               g = string_append(g, 2, "/", pre_malware_nb);
948
949             pcre_free_substring(pre_malware_nb);
950             }
951           }
952           malware_name = string_from_gstring(g);
953         }
954       else
955         {
956         const char *drweb_s = NULL;
957
958         if (drweb_rc & DERR_READ_ERR) drweb_s = "read error";
959         if (drweb_rc & DERR_NOMEMORY) drweb_s = "no memory";
960         if (drweb_rc & DERR_TIMEOUT)  drweb_s = "timeout";
961         if (drweb_rc & DERR_BAD_CALL) drweb_s = "wrong command";
962         /* retcodes DERR_SYMLINK, DERR_NO_REGFILE, DERR_SKIPPED.
963          * DERR_TOO_BIG, DERR_TOO_COMPRESSED, DERR_SPAM,
964          * DERR_CRC_ERROR, DERR_READSOCKET, DERR_WRITE_ERR
965          * and others are ignored */
966         if (drweb_s)
967           return m_panic_defer_3(scanent, CUS callout_address,
968             string_sprintf("drweb daemon retcode 0x%x (%s)", drweb_rc, drweb_s),
969             malware_daemon_ctx.sock);
970
971         /* no virus found */
972         malware_name = NULL;
973         }
974       break;
975       } /* drweb */
976 #endif
977
978 #ifndef DISABLE_MAL_AVE
979     case M_AVES: /* "aveserver" scanner type -------------------------------- */
980       {
981       uschar buf[32768];
982       int result;
983
984       /* read aveserver's greeting and see if it is ready (2xx greeting) */
985       buf[0] = 0;
986       recv_line(malware_daemon_ctx.sock, buf, sizeof(buf), tmo);
987
988       if (buf[0] != '2')                /* aveserver is having problems */
989         return m_panic_defer_3(scanent, CUS callout_address,
990           string_sprintf("unavailable (Responded: %s).",
991                           ((buf[0] != 0) ? buf : US "nothing") ),
992           malware_daemon_ctx.sock);
993
994       /* prepare our command */
995       (void)string_format(buf, sizeof(buf), "SCAN bPQRSTUW %s\r\n",
996                                                 eml_filename);
997
998       /* and send it */
999       DEBUG(D_acl) debug_printf_indent("Malware scan: issuing %s %s\n",
1000         scanner_name, buf);
1001       if (m_sock_send(malware_daemon_ctx.sock, buf, Ustrlen(buf), &errstr) < 0)
1002         return m_panic_defer(scanent, CUS callout_address, errstr);
1003
1004       malware_name = NULL;
1005       result = 0;
1006       /* read response lines, find malware name and final response */
1007       while (recv_line(malware_daemon_ctx.sock, buf, sizeof(buf), tmo) > 0)
1008         {
1009         if (buf[0] == '2')
1010           break;
1011         if (buf[0] == '5')              /* aveserver is having problems */
1012           {
1013           result = m_panic_defer(scanent, CUS callout_address,
1014              string_sprintf("unable to scan file %s (Responded: %s).",
1015                              eml_filename, buf));
1016           break;
1017           }
1018         if (Ustrncmp(buf,"322",3) == 0)
1019           {
1020           uschar *p = Ustrchr(&buf[4], ' ');
1021           *p = '\0';
1022           malware_name = string_copy(&buf[4]);
1023           }
1024         }
1025
1026       if (m_sock_send(malware_daemon_ctx.sock, US"quit\r\n", 6, &errstr) < 0)
1027         return m_panic_defer(scanent, CUS callout_address, errstr);
1028
1029       /* read aveserver's greeting and see if it is ready (2xx greeting) */
1030       buf[0] = 0;
1031       recv_line(malware_daemon_ctx.sock, buf, sizeof(buf), tmo);
1032
1033       if (buf[0] != '2')                /* aveserver is having problems */
1034         return m_panic_defer_3(scanent, CUS callout_address,
1035           string_sprintf("unable to quit dialogue (Responded: %s).",
1036                         ((buf[0] != 0) ? buf : US "nothing") ),
1037           malware_daemon_ctx.sock);
1038
1039       if (result == DEFER)
1040         {
1041         (void)close(malware_daemon_ctx.sock);
1042         return DEFER;
1043         }
1044       break;
1045       } /* aveserver */
1046 #endif
1047
1048 #ifndef DISABLE_MAL_FSECURE
1049     case M_FSEC: /* "fsecure" scanner type ---------------------------------- */
1050       {
1051       int i, bread = 0;
1052       uschar * file_name;
1053       uschar av_buffer[1024];
1054       static uschar *cmdopt[] = { US"CONFIGURE\tARCHIVE\t1\n",
1055                                       US"CONFIGURE\tTIMEOUT\t0\n",
1056                                       US"CONFIGURE\tMAXARCH\t5\n",
1057                                       US"CONFIGURE\tMIME\t1\n" };
1058
1059       malware_name = NULL;
1060
1061       DEBUG(D_acl) debug_printf_indent("Malware scan: issuing %s scan [%s]\n",
1062           scanner_name, scanner_options);
1063       /* pass options */
1064       memset(av_buffer, 0, sizeof(av_buffer));
1065       for (i = 0; i != nelem(cmdopt); i++)
1066         {
1067
1068         if (m_sock_send(malware_daemon_ctx.sock, cmdopt[i], Ustrlen(cmdopt[i]), &errstr) < 0)
1069           return m_panic_defer(scanent, CUS callout_address, errstr);
1070
1071         bread = ip_recv(&malware_daemon_ctx, av_buffer, sizeof(av_buffer), tmo);
1072         if (bread > 0) av_buffer[bread]='\0';
1073         if (bread < 0)
1074           return m_panic_defer_3(scanent, CUS callout_address,
1075             string_sprintf("unable to read answer %d (%s)", i, strerror(errno)),
1076             malware_daemon_ctx.sock);
1077         for (int j = 0; j < bread; j++)
1078           if (av_buffer[j] == '\r' || av_buffer[j] == '\n')
1079             av_buffer[j] ='@';
1080         }
1081
1082       /* pass the mailfile to fsecure */
1083       file_name = string_sprintf("SCAN\t%s\n", eml_filename);
1084
1085       if (m_sock_send(malware_daemon_ctx.sock, file_name, Ustrlen(file_name), &errstr) < 0)
1086         return m_panic_defer(scanent, CUS callout_address, errstr);
1087
1088       /* set up match */
1089       /* todo also SUSPICION\t */
1090       if (!fsec_re)
1091         fsec_re = m_pcre_compile(fsec_re_str, &errstr);
1092
1093       /* read report, linewise. Apply a timeout as the Fsecure daemon
1094       sometimes wants an answer to "PING" but they won't tell us what */
1095         {
1096         uschar * p = av_buffer;
1097         uschar * q;
1098
1099         for (;;)
1100           {
1101           errno = ETIMEDOUT;
1102           i =  av_buffer+sizeof(av_buffer)-p;
1103           if ((bread= ip_recv(&malware_daemon_ctx, p, i-1, tmo)) < 0)
1104             return m_panic_defer_3(scanent, CUS callout_address,
1105               string_sprintf("unable to read result (%s)", strerror(errno)),
1106               malware_daemon_ctx.sock);
1107
1108           for (p[bread] = '\0'; (q = Ustrchr(p, '\n')); p = q+1)
1109             {
1110             *q = '\0';
1111
1112             /* Really search for virus again? */
1113             if (!malware_name)
1114               /* try matcher on the line, grab substring */
1115               malware_name = m_pcre_exec(fsec_re, p);
1116
1117             if (Ustrstr(p, "OK\tScan ok."))
1118               goto fsec_found;
1119             }
1120
1121           /* copy down the trailing partial line then read another chunk */
1122           i =  av_buffer+sizeof(av_buffer)-p;
1123           memmove(av_buffer, p, i);
1124           p = av_buffer+i;
1125           }
1126         }
1127
1128       fsec_found:
1129         break;
1130       } /* fsecure */
1131 #endif
1132
1133 #ifndef DISABLE_MAL_KAV
1134     case M_KAVD: /* "kavdaemon" scanner type -------------------------------- */
1135       {
1136       time_t t;
1137       uschar tmpbuf[1024];
1138       uschar * scanrequest;
1139       int kav_rc;
1140       unsigned long kav_reportlen;
1141       int bread;
1142       const pcre *kav_re;
1143       uschar *p;
1144
1145       /* get current date and time, build scan request */
1146       time(&t);
1147       /* pdp note: before the eml_filename parameter, this scanned the
1148       directory; not finding documentation, so we'll strip off the directory.
1149       The side-effect is that the test framework scanning may end up in
1150       scanning more than was requested, but for the normal interface, this is
1151       fine. */
1152
1153       strftime(CS tmpbuf, sizeof(tmpbuf), "%d %b %H:%M:%S", localtime(&t));
1154       scanrequest = string_sprintf("<0>%s:%s", CS tmpbuf, eml_filename);
1155       p = Ustrrchr(scanrequest, '/');
1156       if (p)
1157         *p = '\0';
1158
1159       DEBUG(D_acl) debug_printf_indent("Malware scan: issuing %s scan [%s]\n",
1160           scanner_name, scanner_options);
1161
1162       /* send scan request */
1163       if (m_sock_send(malware_daemon_ctx.sock, scanrequest, Ustrlen(scanrequest)+1, &errstr) < 0)
1164         return m_panic_defer(scanent, CUS callout_address, errstr);
1165
1166       /* wait for result */
1167       if (!recv_len(malware_daemon_ctx.sock, tmpbuf, 2, tmo))
1168         return m_panic_defer_3(scanent, CUS callout_address,
1169                             US"unable to read 2 bytes from socket.", malware_daemon_ctx.sock);
1170
1171       /* get errorcode from one nibble */
1172       kav_rc = tmpbuf[ test_byte_order()==LITTLE_MY_ENDIAN ? 0 : 1 ] & 0x0F;
1173       switch(kav_rc)
1174       {
1175       case 5: case 6: /* improper kavdaemon configuration */
1176         return m_panic_defer_3(scanent, CUS callout_address,
1177                 US"please reconfigure kavdaemon to NOT disinfect or remove infected files.",
1178                 malware_daemon_ctx.sock);
1179       case 1:
1180         return m_panic_defer_3(scanent, CUS callout_address,
1181                 US"reported 'scanning not completed' (code 1).", malware_daemon_ctx.sock);
1182       case 7:
1183         return m_panic_defer_3(scanent, CUS callout_address,
1184                 US"reported 'kavdaemon damaged' (code 7).", malware_daemon_ctx.sock);
1185       }
1186
1187       /* code 8 is not handled, since it is ambiguous. It appears mostly on
1188       bounces where part of a file has been cut off */
1189
1190       /* "virus found" return codes (2-4) */
1191       if (kav_rc > 1 && kav_rc < 5)
1192         {
1193         int report_flag = 0;
1194
1195         /* setup default virus name */
1196         malware_name = US"unknown";
1197
1198         report_flag = tmpbuf[ test_byte_order() == LITTLE_MY_ENDIAN ? 1 : 0 ];
1199
1200         /* read the report, if available */
1201         if (report_flag == 1)
1202           {
1203           /* read report size */
1204           if (!recv_len(malware_daemon_ctx.sock, &kav_reportlen, 4, tmo))
1205             return m_panic_defer_3(scanent, CUS callout_address,
1206                   US"cannot read report size", malware_daemon_ctx.sock);
1207
1208           /* it's possible that avp returns av_buffer[1] == 1 but the
1209           reportsize is 0 (!?) */
1210           if (kav_reportlen > 0)
1211             {
1212             /* set up match regex, depends on retcode */
1213             if (kav_rc == 3)
1214               {
1215               if (!kav_re_sus) kav_re_sus = m_pcre_compile(kav_re_sus_str, &errstr);
1216               kav_re = kav_re_sus;
1217               }
1218             else
1219               {
1220               if (!kav_re_inf) kav_re_inf = m_pcre_compile(kav_re_inf_str, &errstr);
1221               kav_re = kav_re_inf;
1222               }
1223
1224             /* read report, linewise.  Using size from stream to read amount of data
1225             from same stream is safe enough. */
1226             /* coverity[tainted_data] */
1227             while (kav_reportlen > 0)
1228               {
1229               if ((bread = recv_line(malware_daemon_ctx.sock, tmpbuf, sizeof(tmpbuf), tmo)) < 0)
1230                 break;
1231               kav_reportlen -= bread+1;
1232
1233               /* try matcher on the line, grab substring */
1234               if ((malware_name = m_pcre_exec(kav_re, tmpbuf)))
1235                 break;
1236               }
1237             }
1238           }
1239         }
1240       else /* no virus found */
1241         malware_name = NULL;
1242
1243       break;
1244       }
1245 #endif
1246
1247 #ifndef DISABLE_MAL_CMDLINE
1248     case M_CMDL: /* "cmdline" scanner type ---------------------------------- */
1249       {
1250       const uschar *cmdline_scanner = scanner_options;
1251       const pcre *cmdline_trigger_re;
1252       const pcre *cmdline_regex_re;
1253       uschar * file_name;
1254       uschar * commandline;
1255       void (*eximsigchld)(int);
1256       void (*eximsigpipe)(int);
1257       FILE *scanner_out = NULL;
1258       int scanner_fd;
1259       FILE *scanner_record = NULL;
1260       uschar linebuffer[32767];
1261       int rcnt;
1262       int trigger = 0;
1263       uschar *p;
1264
1265       if (!cmdline_scanner)
1266         return m_panic_defer(scanent, NULL, errstr);
1267
1268       /* find scanner output trigger */
1269       cmdline_trigger_re = m_pcre_nextinlist(&av_scanner_work, &sep,
1270                                 "missing trigger specification", &errstr);
1271       if (!cmdline_trigger_re)
1272         return m_panic_defer(scanent, NULL, errstr);
1273
1274       /* find scanner name regex */
1275       cmdline_regex_re = m_pcre_nextinlist(&av_scanner_work, &sep,
1276                           "missing virus name regex specification", &errstr);
1277       if (!cmdline_regex_re)
1278         return m_panic_defer(scanent, NULL, errstr);
1279
1280       /* prepare scanner call; despite the naming, file_name holds a directory
1281       name which is documented as the value given to %s. */
1282
1283       file_name = string_copy(eml_filename);
1284       p = Ustrrchr(file_name, '/');
1285       if (p)
1286         *p = '\0';
1287       commandline = string_sprintf(CS cmdline_scanner, file_name);
1288
1289       /* redirect STDERR too */
1290       commandline = string_sprintf("%s 2>&1", commandline);
1291
1292       DEBUG(D_acl) debug_printf_indent("Malware scan: issuing %s scan [%s]\n",
1293               scanner_name, commandline);
1294
1295       /* store exims signal handlers */
1296       eximsigchld = signal(SIGCHLD,SIG_DFL);
1297       eximsigpipe = signal(SIGPIPE,SIG_DFL);
1298
1299       if (!(scanner_out = popen(CS commandline,"r")))
1300         {
1301         int err = errno;
1302         signal(SIGCHLD,eximsigchld); signal(SIGPIPE,eximsigpipe);
1303         return m_panic_defer(scanent, NULL,
1304           string_sprintf("call (%s) failed: %s.", commandline, strerror(err)));
1305         }
1306       scanner_fd = fileno(scanner_out);
1307
1308       file_name = string_sprintf("%s/%s_scanner_output", eml_dir, message_id);
1309
1310       if (!(scanner_record = modefopen(file_name, "wb", SPOOL_MODE)))
1311         {
1312         int err = errno;
1313         (void) pclose(scanner_out);
1314         signal(SIGCHLD,eximsigchld); signal(SIGPIPE,eximsigpipe);
1315         return m_panic_defer(scanent, NULL, string_sprintf(
1316             "opening scanner output file (%s) failed: %s.",
1317             file_name, strerror(err)));
1318         }
1319
1320       /* look for trigger while recording output */
1321       while ((rcnt = recv_line(scanner_fd, linebuffer,
1322                       sizeof(linebuffer), tmo)))
1323         {
1324         if (rcnt < 0)
1325           {
1326           int err = errno;
1327           if (rcnt == -1)
1328             break;
1329           (void) pclose(scanner_out);
1330           signal(SIGCHLD,eximsigchld); signal(SIGPIPE,eximsigpipe);
1331           return m_panic_defer(scanent, NULL, string_sprintf(
1332               "unable to read from scanner (%s): %s",
1333               commandline, strerror(err)));
1334           }
1335
1336         if (Ustrlen(linebuffer) > fwrite(linebuffer, 1, Ustrlen(linebuffer), scanner_record))
1337           {
1338           /* short write */
1339           (void) pclose(scanner_out);
1340           signal(SIGCHLD,eximsigchld); signal(SIGPIPE,eximsigpipe);
1341           return m_panic_defer(scanent, NULL, string_sprintf(
1342             "short write on scanner output file (%s).", file_name));
1343           }
1344         putc('\n', scanner_record);
1345         /* try trigger match */
1346         if (  !trigger
1347            && regex_match_and_setup(cmdline_trigger_re, linebuffer, 0, -1)
1348            )
1349           trigger = 1;
1350         }
1351
1352       (void)fclose(scanner_record);
1353       sep = pclose(scanner_out);
1354       signal(SIGCHLD,eximsigchld); signal(SIGPIPE,eximsigpipe);
1355       if (sep != 0)
1356           return m_panic_defer(scanent, NULL,
1357               sep == -1
1358               ? string_sprintf("running scanner failed: %s", strerror(sep))
1359               : string_sprintf("scanner returned error code: %d", sep));
1360
1361       if (trigger)
1362         {
1363         uschar * s;
1364         /* setup default virus name */
1365         malware_name = US"unknown";
1366
1367         /* re-open the scanner output file, look for name match */
1368         scanner_record = Ufopen(file_name, "rb");
1369         while (Ufgets(linebuffer, sizeof(linebuffer), scanner_record))
1370           if ((s = m_pcre_exec(cmdline_regex_re, linebuffer))) /* try match */
1371             malware_name = s;
1372         (void)fclose(scanner_record);
1373         }
1374       else /* no virus found */
1375         malware_name = NULL;
1376       break;
1377       } /* cmdline */
1378 #endif
1379
1380 #ifndef DISABLE_MAL_SOPHIE
1381     case M_SOPHIE: /* "sophie" scanner type --------------------------------- */
1382       {
1383       int bread = 0;
1384       uschar *p;
1385       uschar * file_name;
1386       uschar av_buffer[1024];
1387
1388       /* pass the scan directory to sophie */
1389       file_name = string_copy(eml_filename);
1390       if ((p = Ustrrchr(file_name, '/')))
1391         *p = '\0';
1392
1393       DEBUG(D_acl) debug_printf_indent("Malware scan: issuing %s scan [%s]\n",
1394           scanner_name, scanner_options);
1395
1396       if (  write(malware_daemon_ctx.sock, file_name, Ustrlen(file_name)) < 0
1397          || write(malware_daemon_ctx.sock, "\n", 1) != 1
1398          )
1399         return m_panic_defer_3(scanent, CUS callout_address,
1400           string_sprintf("unable to write to UNIX socket (%s)", scanner_options),
1401           malware_daemon_ctx.sock);
1402
1403       /* wait for result */
1404       memset(av_buffer, 0, sizeof(av_buffer));
1405       if ((bread = ip_recv(&malware_daemon_ctx, av_buffer, sizeof(av_buffer), tmo)) <= 0)
1406         return m_panic_defer_3(scanent, CUS callout_address,
1407           string_sprintf("unable to read from UNIX socket (%s)", scanner_options),
1408           malware_daemon_ctx.sock);
1409
1410       /* infected ? */
1411       if (av_buffer[0] == '1') {
1412         uschar * s = Ustrchr(av_buffer, '\n');
1413         if (s)
1414           *s = '\0';
1415         malware_name = string_copy(&av_buffer[2]);
1416       }
1417       else if (!strncmp(CS av_buffer, "-1", 2))
1418         return m_panic_defer_3(scanent, CUS callout_address,
1419                 US"scanner reported error", malware_daemon_ctx.sock);
1420       else /* all ok, no virus */
1421         malware_name = NULL;
1422
1423       break;
1424       }
1425 #endif
1426
1427 #ifndef DISABLE_MAL_CLAM
1428     case M_CLAMD: /* "clamd" scanner type ----------------------------------- */
1429       {
1430 /* This code was originally contributed by David Saez */
1431 /* There are three scanning methods available to us:
1432 *  (1) Use the SCAN command, pointing to a file in the filesystem
1433 *  (2) Use the STREAM command, send the data on a separate port
1434 *  (3) Use the zINSTREAM command, send the data inline
1435 * The zINSTREAM command was introduced with ClamAV 0.95, which marked
1436 * STREAM deprecated; see: http://wiki.clamav.net/bin/view/Main/UpgradeNotes095
1437 * In Exim, we use SCAN if using a Unix-domain socket or explicitly told that
1438 * the TCP-connected daemon is actually local; otherwise we use zINSTREAM
1439 * See Exim bug 926 for details.  */
1440
1441       uschar *p, *vname, *result_tag;
1442       int bread=0;
1443       uschar av_buffer[1024];
1444       uschar *hostname = US"";
1445       host_item connhost;
1446       uschar *clamav_fbuf;
1447       int clam_fd, result;
1448       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 = exim_open2(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 gstring *
2283 malware_show_supported(gstring * g)
2284 {
2285 g = string_cat(g, US"Malware:");
2286 for (struct scan * sc = m_scans; sc->scancode != (scanner_t)-1; sc++)
2287   g = string_fmt_append(g, " %s", sc->name);
2288 return string_cat(g, US"\n");
2289 }
2290
2291
2292 # endif /*!MACRO_PREDEF*/
2293 #endif /*WITH_CONTENT_SCAN*/
2294 /*
2295  * vi: aw ai sw=2
2296  */