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