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