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