malware: fix ClamAV file send corking
[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       int clam_fd, result;
1457       unsigned int fsize_uint;
1458       BOOL use_scan_command = FALSE;
1459       clamd_address * cv[MAX_CLAMD_SERVERS];
1460       int num_servers = 0;
1461       uint32_t send_size, send_final_zeroblock;
1462       blob cmd_str;
1463
1464       /*XXX if unixdomain socket, only one server supported. Needs fixing;
1465       there's no reason we should not mix local and remote servers */
1466
1467       if (*scanner_options == '/')
1468         {
1469         clamd_address * cd;
1470         const uschar * sublist;
1471         int subsep = ' ';
1472
1473         /* Local file; so we def want to use_scan_command and don't want to try
1474          * passing IP/port combinations */
1475         use_scan_command = TRUE;
1476         cd = (clamd_address *) store_get(sizeof(clamd_address), FALSE);
1477
1478         /* extract socket-path part */
1479         sublist = scanner_options;
1480         cd->hostspec = string_nextinlist(&sublist, &subsep, NULL, 0);
1481
1482         /* parse options */
1483         if (clamd_option(cd, sublist, &subsep) != OK)
1484           return m_panic_defer(scanent, NULL,
1485             string_sprintf("bad option '%s'", scanner_options));
1486         cv[0] = cd;
1487         }
1488       else
1489         {
1490         /* Go through the rest of the list of host/port and construct an array
1491          * of servers to try. The first one is the bit we just passed from
1492          * scanner_options so process that first and then scan the remainder of
1493          * the address buffer */
1494         do
1495           {
1496           clamd_address * cd;
1497           const uschar * sublist;
1498           int subsep = ' ';
1499           uschar * s;
1500
1501           /* The 'local' option means use the SCAN command over the network
1502            * socket (ie common file storage in use) */
1503           /*XXX we could accept this also as a local option? */
1504           if (strcmpic(scanner_options, US"local") == 0)
1505             {
1506             use_scan_command = TRUE;
1507             continue;
1508             }
1509
1510           cd = (clamd_address *) store_get(sizeof(clamd_address), FALSE);
1511
1512           /* extract host and port part */
1513           sublist = scanner_options;
1514           if (!(cd->hostspec = string_nextinlist(&sublist, &subsep, NULL, 0)))
1515             {
1516             (void) m_panic_defer(scanent, NULL,
1517                       string_sprintf("missing address: '%s'", scanner_options));
1518             continue;
1519             }
1520           if (!(s = string_nextinlist(&sublist, &subsep, NULL, 0)))
1521             {
1522             (void) m_panic_defer(scanent, NULL,
1523                       string_sprintf("missing port: '%s'", scanner_options));
1524             continue;
1525             }
1526           cd->tcp_port = atoi(CS s);
1527
1528           /* parse options */
1529           /*XXX should these options be common over scanner types? */
1530           if (clamd_option(cd, sublist, &subsep) != OK)
1531             return m_panic_defer(scanent, NULL,
1532               string_sprintf("bad option '%s'", scanner_options));
1533
1534           cv[num_servers++] = cd;
1535           if (num_servers >= MAX_CLAMD_SERVERS)
1536             {
1537             (void) m_panic_defer(scanent, NULL,
1538                   US"More than " MAX_CLAMD_SERVERS_S " clamd servers "
1539                   "specified; only using the first " MAX_CLAMD_SERVERS_S );
1540             break;
1541             }
1542           } while ((scanner_options = string_nextinlist(&av_scanner_work, &sep,
1543                                         NULL, 0)));
1544
1545         /* check if we have at least one server */
1546         if (!num_servers)
1547           return m_panic_defer(scanent, NULL,
1548             US"no useable server addresses in malware configuration option.");
1549         }
1550
1551       /* See the discussion of response formats below to see why we really
1552       don't like colons in filenames when passing filenames to ClamAV. */
1553       if (use_scan_command && Ustrchr(eml_filename, ':'))
1554         return m_panic_defer(scanent, NULL,
1555           string_sprintf("local/SCAN mode incompatible with" \
1556             " : in path to email filename [%s]", eml_filename));
1557
1558       /* Set up the very first data we will be sending */
1559       if (!use_scan_command)
1560         { cmd_str.data = US"zINSTREAM"; cmd_str.len = 10; }
1561       else
1562         {
1563         cmd_str.data = string_sprintf("SCAN %s\n", eml_filename);
1564         cmd_str.len = Ustrlen(cmd_str.data);
1565         }
1566
1567       /* We have some network servers specified */
1568       if (num_servers)
1569         {
1570         /* Confirmed in ClamAV source (0.95.3) that the TCPAddr option of clamd
1571         only supports AF_INET, but we should probably be looking to the
1572         future and rewriting this to be protocol-independent anyway. */
1573
1574         while (num_servers > 0)
1575           {
1576           int i = random_number(num_servers);
1577           clamd_address * cd = cv[i];
1578
1579           DEBUG(D_acl) debug_printf_indent("trying server name %s, port %u\n",
1580                          cd->hostspec, cd->tcp_port);
1581
1582           /* Lookup the host. This is to ensure that we connect to the same IP
1583           on both connections (as one host could resolve to multiple ips) */
1584           for (;;)
1585             {
1586             /*XXX we trust that the cmd_str is ideempotent */
1587             if ((malware_daemon_ctx.sock = m_tcpsocket(cd->hostspec, cd->tcp_port,
1588                                     &connhost, &errstr, &cmd_str)) >= 0)
1589               {
1590               /* Connection successfully established with a server */
1591               hostname = cd->hostspec;
1592               cmd_str.len = 0;
1593               break;
1594               }
1595             if (cd->retry <= 0) break;
1596             while (cd->retry > 0) cd->retry = sleep(cd->retry);
1597             }
1598           if (malware_daemon_ctx.sock >= 0)
1599             break;
1600
1601           (void) m_panic_defer(scanent, CUS callout_address, errstr);
1602
1603           /* Remove the server from the list. XXX We should free the memory */
1604           num_servers--;
1605           for (; i < num_servers; i++)
1606             cv[i] = cv[i+1];
1607           }
1608
1609         if (num_servers == 0)
1610           return m_panic_defer(scanent, NULL, US"all servers failed");
1611         }
1612       else
1613         for (;;)
1614           {
1615           if ((malware_daemon_ctx.sock = ip_unixsocket(cv[0]->hostspec, &errstr)) >= 0)
1616             {
1617             hostname = cv[0]->hostspec;
1618             break;
1619             }
1620           if (cv[0]->retry <= 0)
1621             return m_panic_defer(scanent, CUS callout_address, errstr);
1622           while (cv[0]->retry > 0) cv[0]->retry = sleep(cv[0]->retry);
1623           }
1624
1625       /* have socket in variable "sock"; command to use is semi-independent of
1626       the socket protocol.  We use SCAN if is local (either Unix/local
1627       domain socket, or explicitly told local) else we stream the data.
1628       How we stream the data depends upon how we were built.  */
1629
1630       if (!use_scan_command)
1631         {
1632         struct stat st;
1633 #ifdef EXIM_TCP_CORK
1634         BOOL corked = TRUE;
1635 #endif
1636         /* New protocol: "zINSTREAM\n" followed by a sequence of <length><data>
1637         chunks, <n> a 4-byte number (network order), terminated by a zero-length
1638         chunk. */
1639
1640         DEBUG(D_acl) debug_printf_indent(
1641             "Malware scan: issuing %s new-style remote scan (zINSTREAM)\n",
1642             scanner_name);
1643
1644         /* Pass the string to ClamAV (10 = "zINSTREAM\0"), if not already sent */
1645         if (cmd_str.len)
1646           if (send(malware_daemon_ctx.sock, cmd_str.data, cmd_str.len, 0) < 0)
1647             return m_panic_defer_3(scanent, CUS hostname,
1648               string_sprintf("unable to send zINSTREAM to socket (%s)",
1649                 strerror(errno)),
1650               malware_daemon_ctx.sock);
1651
1652         if ((clam_fd = exim_open2(CS eml_filename, O_RDONLY)) < 0)
1653           {
1654           int err = errno;
1655           return m_panic_defer_3(scanent, NULL,
1656             string_sprintf("can't open spool file %s: %s",
1657               eml_filename, strerror(err)),
1658             malware_daemon_ctx.sock);
1659           }
1660         if (fstat(clam_fd, &st) < 0)
1661           {
1662           int err = errno;
1663           (void)close(clam_fd);
1664           return m_panic_defer_3(scanent, NULL,
1665             string_sprintf("can't stat spool file %s: %s",
1666               eml_filename, strerror(err)),
1667             malware_daemon_ctx.sock);
1668           }
1669         fsize_uint = (unsigned int) st.st_size;
1670         if ((off_t)fsize_uint != st.st_size)
1671           {
1672           (void)close(clam_fd);
1673           return m_panic_defer_3(scanent, NULL,
1674             string_sprintf("stat spool file %s, size overflow", eml_filename),
1675             malware_daemon_ctx.sock);
1676           }
1677
1678         /* send file size */
1679 #ifdef EXIM_TCP_CORK
1680         (void) setsockopt(malware_daemon_ctx.sock, IPPROTO_TCP, EXIM_TCP_CORK,
1681                           US &on, sizeof(on));
1682 #endif
1683         send_size = htonl(fsize_uint);
1684         if (send(malware_daemon_ctx.sock, &send_size, sizeof(send_size), 0) < 0)
1685           return m_panic_defer_3(scanent, NULL,
1686             string_sprintf("unable to send file size to socket (%s)", hostname),
1687             malware_daemon_ctx.sock);
1688
1689         /* send file body */
1690         /*XXX sendfile? */
1691         while (fsize_uint)
1692           {
1693           unsigned n = MIN(fsize_uint, big_buffer_size);
1694           if ((n = read(clam_fd, big_buffer, n)) < 0)
1695             return m_panic_defer_3(scanent, NULL,
1696               string_sprintf("can't read spool file %s: %s",
1697                 eml_filename, strerror(errno)),
1698               malware_daemon_ctx.sock);
1699           if (send(malware_daemon_ctx.sock, big_buffer, (size_t)n, 0) < 0)
1700             return m_panic_defer_3(scanent, NULL,
1701               string_sprintf("unable to send file body to socket (%s): %s", hostname, strerror(errno)),
1702               malware_daemon_ctx.sock);
1703           fsize_uint -= n;
1704 #ifdef EXIM_TCP_CORK
1705           if (corked)
1706             {
1707             corked = FALSE;
1708             (void) setsockopt(malware_daemon_ctx.sock, IPPROTO_TCP, EXIM_TCP_CORK,
1709                               US &off, sizeof(off));
1710             }
1711 #endif
1712           }
1713
1714         send_final_zeroblock = 0;
1715         if (send(malware_daemon_ctx.sock, &send_final_zeroblock, sizeof(send_final_zeroblock), 0) < 0)
1716           return m_panic_defer_3(scanent, NULL,
1717             string_sprintf("unable to send file terminator to socket (%s)", hostname),
1718             malware_daemon_ctx.sock);
1719         }
1720       else
1721         { /* use scan command */
1722         /* Send a SCAN command pointing to a filename; then in the then in the
1723          * scan-method-neutral part, read the response back */
1724
1725 /* ================================================================= */
1726
1727         /* Prior to the reworking post-Exim-4.72, this scanned a directory,
1728         which dates to when ClamAV needed us to break apart the email into the
1729         MIME parts (eg, with the now deprecated demime condition coming first).
1730         Some time back, ClamAV gained the ability to deconstruct the emails, so
1731         doing this would actually have resulted in the mail attachments being
1732         scanned twice, in the broken out files and from the original .eml.
1733         Since ClamAV now handles emails (and has for quite some time) we can
1734         just use the email file itself. */
1735         /* Pass the string to ClamAV (7 = "SCAN \n" + \0), if not already sent */
1736
1737         DEBUG(D_acl) debug_printf_indent(
1738             "Malware scan: issuing %s local-path scan [%s]\n",
1739             scanner_name, scanner_options);
1740
1741         if (cmd_str.len)
1742           if (send(malware_daemon_ctx.sock, cmd_str.data, cmd_str.len, 0) < 0)
1743             return m_panic_defer_3(scanent, CUS callout_address,
1744               string_sprintf("unable to write to socket (%s)", strerror(errno)),
1745               malware_daemon_ctx.sock);
1746
1747         /* Do not shut down the socket for writing; a user report noted that
1748         clamd 0.70 does not react well to this. */
1749         }
1750       /* Commands have been sent, no matter which scan method or connection
1751       type we're using; now just read the result, independent of method. */
1752
1753       /* Read the result */
1754       memset(av_buffer, 0, sizeof(av_buffer));
1755       bread = ip_recv(&malware_daemon_ctx, av_buffer, sizeof(av_buffer), tmo);
1756       (void)close(malware_daemon_ctx.sock);
1757       malware_daemon_ctx.sock = -1;
1758       malware_daemon_ctx.tls_ctx = NULL;
1759
1760       if (bread <= 0)
1761         return m_panic_defer(scanent, CUS callout_address,
1762           string_sprintf("unable to read from socket (%s)",
1763           errno == 0 ? "EOF" : strerror(errno)));
1764
1765       if (bread == sizeof(av_buffer))
1766         return m_panic_defer(scanent, CUS callout_address,
1767                 US"buffer too small");
1768       /* We're now assured of a NULL at the end of av_buffer */
1769
1770       /* Check the result. ClamAV returns one of two result formats.
1771       In the basic mode, the response is of the form:
1772         infected: -> "<filename>: <virusname> FOUND"
1773         not-infected: -> "<filename>: OK"
1774         error: -> "<filename>: <errcode> ERROR
1775       If the ExtendedDetectionInfo option has been turned on, then we get:
1776         "<filename>: <virusname>(<virushash>:<virussize>) FOUND"
1777       for the infected case.  Compare:
1778 /tmp/eicar.com: Eicar-Test-Signature FOUND
1779 /tmp/eicar.com: Eicar-Test-Signature(44d88612fea8a8f36de82e1278abb02f:68) FOUND
1780
1781       In the streaming case, clamd uses the filename "stream" which you should
1782       be able to verify with { ktrace clamdscan --stream /tmp/eicar.com }.  (The
1783       client app will replace "stream" with the original filename before returning
1784       results to stdout, but the trace shows the data).
1785
1786       We will assume that the pathname passed to clamd from Exim does not contain
1787       a colon.  We will have whined loudly above if the eml_filename does (and we're
1788       passing a filename to clamd). */
1789
1790       if (!(*av_buffer))
1791         return m_panic_defer(scanent, CUS callout_address,
1792                 US"ClamAV returned null");
1793
1794       /* strip newline at the end (won't be present for zINSTREAM)
1795       (also any trailing whitespace, which shouldn't exist, but we depend upon
1796       this below, so double-check) */
1797
1798       p = av_buffer + Ustrlen(av_buffer) - 1;
1799       if (*p == '\n') *p = '\0';
1800
1801       DEBUG(D_acl) debug_printf_indent("Malware response: %s\n", av_buffer);
1802
1803       while (isspace(*--p) && (p > av_buffer))
1804         *p = '\0';
1805       if (*p) ++p;
1806
1807       /* colon in returned output? */
1808       if(!(p = Ustrchr(av_buffer,':')))
1809         return m_panic_defer(scanent, CUS callout_address, string_sprintf(
1810                   "ClamAV returned malformed result (missing colon): %s",
1811                   av_buffer));
1812
1813       /* strip filename */
1814       while (*p && isspace(*++p)) /**/;
1815       vname = p;
1816
1817       /* It would be bad to encounter a virus with "FOUND" in part of the name,
1818       but we should at least be resistant to it. */
1819       p = Ustrrchr(vname, ' ');
1820       result_tag = p ? p+1 : vname;
1821
1822       if (Ustrcmp(result_tag, "FOUND") == 0)
1823         {
1824         /* p should still be the whitespace before the result_tag */
1825         while (isspace(*p)) --p;
1826         *++p = '\0';
1827         /* Strip off the extended information too, which will be in parens
1828         after the virus name, with no intervening whitespace. */
1829         if (*--p == ')')
1830           {
1831           /* "(hash:size)", so previous '(' will do; if not found, we have
1832           a curious virus name, but not an error. */
1833           p = Ustrrchr(vname, '(');
1834           if (p)
1835             *p = '\0';
1836           }
1837         malware_name = string_copy(vname);
1838         DEBUG(D_acl) debug_printf_indent("Malware found, name \"%s\"\n", malware_name);
1839
1840         }
1841       else if (Ustrcmp(result_tag, "ERROR") == 0)
1842         return m_panic_defer(scanent, CUS callout_address,
1843           string_sprintf("ClamAV returned: %s", av_buffer));
1844
1845       else if (Ustrcmp(result_tag, "OK") == 0)
1846         {
1847         /* Everything should be OK */
1848         malware_name = NULL;
1849         DEBUG(D_acl) debug_printf_indent("Malware not found\n");
1850
1851         }
1852       else
1853         return m_panic_defer(scanent, CUS callout_address,
1854           string_sprintf("unparseable response from ClamAV: {%s}", av_buffer));
1855
1856       break;
1857       } /* clamd */
1858 #endif
1859
1860 #ifndef DISABLE_MAL_SOCK
1861     case M_SOCK: /* "sock" scanner type ------------------------------------- */
1862     /* This code was derived by Martin Poole from the clamd code contributed
1863        by David Saez and the cmdline code
1864     */
1865       {
1866       int bread;
1867       uschar * commandline;
1868       uschar av_buffer[1024];
1869       uschar * linebuffer;
1870       uschar * sockline_scanner;
1871       uschar sockline_scanner_default[] = "%s\n";
1872       const pcre *sockline_trig_re;
1873       const pcre *sockline_name_re;
1874
1875       /* find scanner command line */
1876       if (  (sockline_scanner = string_nextinlist(&av_scanner_work, &sep,
1877                                           NULL, 0))
1878          && *sockline_scanner
1879          )
1880       { /* check for no expansions apart from one %s */
1881         uschar * s = Ustrchr(sockline_scanner, '%');
1882         if (s++)
1883           if ((*s != 's' && *s != '%') || Ustrchr(s+1, '%'))
1884             return m_panic_defer_3(scanent, NULL,
1885                                   US"unsafe sock scanner call spec", malware_daemon_ctx.sock);
1886       }
1887       else
1888         sockline_scanner = sockline_scanner_default;
1889       DEBUG(D_acl) debug_printf_indent("%15s%10s'%s'\n", "", "cmdline: ",
1890         string_printing(sockline_scanner));
1891
1892       /* find scanner output trigger */
1893       sockline_trig_re = m_pcre_nextinlist(&av_scanner_work, &sep,
1894                                 "missing trigger specification", &errstr);
1895       if (!sockline_trig_re)
1896         return m_panic_defer_3(scanent, NULL, errstr, malware_daemon_ctx.sock);
1897
1898       /* find virus name regex */
1899       sockline_name_re = m_pcre_nextinlist(&av_scanner_work, &sep,
1900                           "missing virus name regex specification", &errstr);
1901       if (!sockline_name_re)
1902         return m_panic_defer_3(scanent, NULL, errstr, malware_daemon_ctx.sock);
1903
1904       /* prepare scanner call - security depends on expansions check above */
1905       commandline = string_sprintf( CS sockline_scanner, CS eml_filename);
1906       DEBUG(D_acl) debug_printf_indent("%15s%10s'%s'\n", "", "expanded: ",
1907         string_printing(commandline));
1908
1909       /* Pass the command string to the socket */
1910       if (m_sock_send(malware_daemon_ctx.sock, commandline, Ustrlen(commandline), &errstr) < 0)
1911         return m_panic_defer(scanent, CUS callout_address, errstr);
1912
1913       /* Read the result */
1914       bread = ip_recv(&malware_daemon_ctx, av_buffer, sizeof(av_buffer), tmo);
1915
1916       if (bread <= 0)
1917         return m_panic_defer_3(scanent, CUS callout_address,
1918           string_sprintf("unable to read from socket (%s)", strerror(errno)),
1919           malware_daemon_ctx.sock);
1920
1921       if (bread == sizeof(av_buffer))
1922         return m_panic_defer_3(scanent, CUS callout_address,
1923                 US"buffer too small", malware_daemon_ctx.sock);
1924       av_buffer[bread] = '\0';
1925       linebuffer = string_copy(av_buffer);
1926       DEBUG(D_acl) debug_printf_indent("%15s%10s'%s'\n", "", "answer: ",
1927         string_printing(linebuffer));
1928
1929       /* try trigger match */
1930       if (regex_match_and_setup(sockline_trig_re, linebuffer, 0, -1))
1931         {
1932         if (!(malware_name = m_pcre_exec(sockline_name_re, av_buffer)))
1933           malware_name = US "unknown";
1934         DEBUG(D_acl) debug_printf_indent("%15s%10s'%s'\n", "", "name: ",
1935           string_printing(malware_name));
1936         }
1937       else /* no virus found */
1938         malware_name = NULL;
1939       break;
1940       }
1941 #endif
1942
1943 #ifndef DISABLE_MAL_MKS
1944     case M_MKSD: /* "mksd" scanner type ------------------------------------- */
1945       {
1946       char *mksd_options_end;
1947       int mksd_maxproc = 1;  /* default, if no option supplied */
1948       int retval;
1949
1950       if (scanner_options)
1951         {
1952         mksd_maxproc = (int)strtol(CS scanner_options, &mksd_options_end, 10);
1953         if (  *scanner_options == '\0'
1954            || *mksd_options_end != '\0'
1955            || mksd_maxproc < 1
1956            || mksd_maxproc > 32
1957            )
1958           return m_panic_defer(scanent, CUS callout_address,
1959             string_sprintf("invalid option '%s'", scanner_options));
1960         }
1961
1962       if((malware_daemon_ctx.sock = ip_unixsocket(US "/var/run/mksd/socket", &errstr)) < 0)
1963         return m_panic_defer(scanent, CUS callout_address, errstr);
1964
1965       malware_name = NULL;
1966
1967       DEBUG(D_acl) debug_printf_indent("Malware scan: issuing %s scan\n", scanner_name);
1968
1969       if ((retval = mksd_scan_packed(scanent, malware_daemon_ctx.sock, eml_filename, tmo)) != OK)
1970         {
1971         close (malware_daemon_ctx.sock);
1972         return retval;
1973         }
1974       break;
1975       }
1976 #endif
1977
1978 #ifndef DISABLE_MAL_AVAST
1979     case M_AVAST: /* "avast" scanner type ----------------------------------- */
1980       {
1981       uschar buf[1024];
1982       uschar * scanrequest;
1983       enum {AVA_HELO, AVA_OPT, AVA_RSP, AVA_DONE} avast_stage;
1984       int nread;
1985       uschar * error_message = NULL;
1986       BOOL more_data = FALSE;
1987       BOOL strict = TRUE;
1988
1989       /* According to Martin Tuma @avast the protocol uses "escaped
1990       whitespace", that is, every embedded whitespace is backslash
1991       escaped, as well as backslash is protected by backslash.
1992       The returned lines contain the name of the scanned file, a tab
1993       and the [ ] marker.
1994       [+] - not infected
1995       [L] - infected
1996       [E] - some error occurred
1997       Such marker follows the first non-escaped TAB.  For more information
1998       see avast-protocol(5)
1999
2000       We observed two cases:
2001       -> SCAN /file
2002       <- /file [E]0.0 Error 13 Permission denied
2003       <- 451 SCAN Engine error 13 permission denied
2004
2005       -> SCAN /file
2006       <- /file… [E]3.0 Error 41120 The file is a decompression bomb
2007       <- /file… [+]2.0
2008       <- /file… [+]2.0 0 Eicar Test Virus!!!
2009       <- 200 SCAN OK
2010
2011       If the scanner returns 4xx, DEFER is a good decision, combined
2012       with a panic log entry, to get the admin's attention.
2013
2014       If the scanner returns 200, we reject it as malware, if found any,
2015       or, in case of an error, we set the malware message to the error
2016       string.
2017
2018       Some of the >= 42000 errors are message related - usually some
2019       broken archives etc, but some of them are e.g. license related.
2020       Once the license expires the engine starts returning errors for
2021       every scanning attempt.  I¹ have the full list of the error codes
2022       but it is not a public API and is subject to change. It is hard
2023       for me to say what you should do in case of an engine error. You
2024       can have a “Treat * unscanned file as infection” policy or “Treat
2025       unscanned file as clean” policy.  ¹) Jakub Bednar
2026
2027        */
2028
2029       if (  (  !ava_re_clean
2030             && !(ava_re_clean = m_pcre_compile(ava_re_clean_str, &errstr)))
2031          || (  !ava_re_virus
2032             && !(ava_re_virus = m_pcre_compile(ava_re_virus_str, &errstr)))
2033          || (  !ava_re_error
2034             && !(ava_re_error = m_pcre_compile(ava_re_error_str, &errstr)))
2035          )
2036         return malware_panic_defer(errstr);
2037
2038       /* wait for result */
2039       for (avast_stage = AVA_HELO;
2040            (nread = recv_line(malware_daemon_ctx.sock, buf, sizeof(buf), tmo)) > 0;
2041           )
2042         {
2043         int slen = Ustrlen(buf);
2044         if (slen >= 1)
2045           {
2046
2047           /* Multi line responses are bracketed between 210 … and nnn … */
2048           if (Ustrncmp(buf, "210", 3) == 0)
2049             {
2050             more_data = 1;
2051             continue;
2052             }
2053           else if (more_data && isdigit(buf[0])) more_data = 0;
2054
2055           switch (avast_stage)
2056             {
2057             case AVA_HELO:
2058               if (more_data) continue;
2059               if (Ustrncmp(buf, "220", 3) != 0)
2060                 goto endloop;                   /* require a 220 */
2061               goto sendreq;
2062
2063             case AVA_OPT:
2064               if (more_data) continue;
2065               if (Ustrncmp(buf, "200", 3) != 0)
2066                 goto endloop;                   /* require a 200 */
2067
2068             sendreq:
2069               {
2070               int len;
2071               /* Check for another option to send. Newline-terminate it. */
2072               if ((scanrequest = string_nextinlist(&av_scanner_work, &sep,
2073                                 NULL, 0)))
2074                 {
2075                 if (Ustrcmp(scanrequest, "pass_unscanned") == 0)
2076                   {
2077                   DEBUG(D_acl) debug_printf_indent("pass unscanned files as clean\n");
2078                   strict = FALSE;
2079                   goto sendreq;
2080                   }
2081                 scanrequest = string_sprintf("%s\n", scanrequest);
2082                 avast_stage = AVA_OPT;          /* just sent option */
2083                 DEBUG(D_acl) debug_printf_indent("send to avast OPTION: %s", scanrequest);
2084                 }
2085               else
2086                 {
2087                 scanrequest = string_sprintf("SCAN %s\n", eml_dir);
2088                 avast_stage = AVA_RSP;          /* just sent command */
2089                 DEBUG(D_acl) debug_printf_indent("send to avast REQUEST: SCAN %s\n", eml_dir);
2090                 }
2091
2092               /* send config-cmd or scan-request to socket */
2093               len = Ustrlen(scanrequest);
2094               if (send(malware_daemon_ctx.sock, scanrequest, len, 0) == -1)
2095                 {
2096                 scanrequest[len-1] = '\0';
2097                 return m_panic_defer_3(scanent, CUS callout_address, string_sprintf(
2098                       "unable to send request '%s' to socket (%s): %s",
2099                       scanrequest, scanner_options, strerror(errno)), malware_daemon_ctx.sock);
2100                 }
2101               break;
2102               }
2103
2104             case AVA_RSP:
2105
2106               if (isdigit(buf[0]))  /* We're done */
2107                 goto endloop;
2108
2109               if (malware_name)     /* Nothing else matters, just read on */
2110                 break;
2111
2112               if (pcre_exec(ava_re_clean, NULL, CS buf, slen, 0, 0, NULL, 0) == 0)
2113                 break;
2114
2115               if ((malware_name = m_pcre_exec(ava_re_virus, buf)))
2116                 {
2117                 unescape(malware_name);
2118                 DEBUG(D_acl)
2119                   debug_printf_indent("unescaped malware name: '%s'\n", malware_name);
2120                 break;
2121                 }
2122
2123               if (strict)           /* treat scanner errors as malware */
2124                 {
2125                 if ((malware_name = m_pcre_exec(ava_re_error, buf)))
2126                   {
2127                   unescape(malware_name);
2128                   DEBUG(D_acl)
2129                     debug_printf_indent("unescaped error message: '%s'\n", malware_name);
2130                   break;
2131                   }
2132                 }
2133               else if (pcre_exec(ava_re_error, NULL, CS buf, slen, 0, 0, NULL, 0) == 0)
2134                 {
2135                 log_write(0, LOG_MAIN, "internal scanner error (ignored): %s", buf);
2136                 break;
2137                 }
2138
2139               /* here also for any unexpected response from the scanner */
2140               DEBUG(D_acl) debug_printf("avast response not handled: '%s'\n", buf);
2141
2142               goto endloop;
2143
2144             default:    log_write(0, LOG_PANIC, "%s:%d:%s: should not happen",
2145                             __FILE__, __LINE__, __FUNCTION__);
2146             }
2147           }
2148         }
2149
2150       endloop:
2151
2152       if (nread == -1) error_message = US"EOF from scanner";
2153       else if (nread < 0) error_message = US"timeout from scanner";
2154       else if (nread == 0) error_message = US"got nothing from scanner";
2155       else if (buf[0] != '2') error_message = buf;
2156
2157       DEBUG(D_acl) debug_printf_indent("sent to avast QUIT\n");
2158       if (send(malware_daemon_ctx.sock, "QUIT\n", 5, 0) == -1)
2159         return m_panic_defer_3(scanent, CUS callout_address,
2160           string_sprintf("unable to send quit request to socket (%s): %s",
2161             scanner_options, strerror(errno)), malware_daemon_ctx.sock);
2162
2163       if (error_message)
2164         return m_panic_defer_3(scanent, CUS callout_address, error_message, malware_daemon_ctx.sock);
2165
2166       }
2167 #endif
2168   }     /* scanner type switch */
2169
2170   if (malware_daemon_ctx.sock >= 0)
2171     (void) close (malware_daemon_ctx.sock);
2172   malware_ok = TRUE;                    /* set "been here, done that" marker */
2173   }
2174
2175 /* match virus name against pattern (caseless ------->----------v) */
2176 if (malware_name && regex_match_and_setup(re, malware_name, 0, -1))
2177   {
2178   DEBUG(D_acl) debug_printf_indent(
2179       "Matched regex to malware [%s] [%s]\n", malware_re, malware_name);
2180   return OK;
2181   }
2182 else
2183   return FAIL;
2184 }
2185
2186
2187 /*************************************************
2188 *          Scan an email for malware             *
2189 *************************************************/
2190
2191 /* This is the normal interface for scanning an email, which doesn't need a
2192 filename; it's a wrapper around the malware_file function.
2193
2194 Arguments:
2195   malware_re  match condition for "malware="
2196   timeout     if nonzero, timeout in seconds
2197
2198 Returns:      Exim message processing code (OK, FAIL, DEFER, ...)
2199               where true means malware was found (condition applies)
2200 */
2201 int
2202 malware(const uschar * malware_re, int timeout)
2203 {
2204 int ret = malware_internal(malware_re, NULL, timeout);
2205
2206 if (ret == DEFER) av_failed = TRUE;
2207 return ret;
2208 }
2209
2210
2211 /*************************************************
2212 *          Scan a file for malware               *
2213 *************************************************/
2214
2215 /* This is a test wrapper for scanning an email, which is not used in
2216 normal processing.  Scan any file, using the Exim scanning interface.
2217 This function tampers with various global variables so is unsafe to use
2218 in any other context.
2219
2220 Arguments:
2221   eml_filename  a file holding the message to be scanned
2222
2223 Returns:        Exim message processing code (OK, FAIL, DEFER, ...)
2224                 where true means malware was found (condition applies)
2225 */
2226 int
2227 malware_in_file(uschar *eml_filename)
2228 {
2229 uschar message_id_buf[64];
2230 int ret;
2231
2232 /* spool_mbox() assumes various parameters exist, when creating
2233 the relevant directory and the email within */
2234
2235 (void) string_format(message_id_buf, sizeof(message_id_buf),
2236     "dummy-%d", vaguely_random_number(INT_MAX));
2237 message_id = message_id_buf;
2238 sender_address = US"malware-sender@example.net";
2239 return_path = US"";
2240 recipients_list = NULL;
2241 receive_add_recipient(US"malware-victim@example.net", -1);
2242 f.enable_dollar_recipients = TRUE;
2243
2244 ret = malware_internal(US"*", eml_filename, 0);
2245
2246 Ustrncpy(spooled_message_id, message_id, sizeof(spooled_message_id));
2247 spool_mbox_ok = 1;
2248
2249 /* don't set no_mbox_unspool; at present, there's no way for it to become
2250 set, but if that changes, then it should apply to these tests too */
2251
2252 unspool_mbox();
2253
2254 /* silence static analysis tools */
2255 message_id = NULL;
2256
2257 return ret;
2258 }
2259
2260
2261 void
2262 malware_init(void)
2263 {
2264 if (!malware_default_re)
2265   malware_default_re = regex_must_compile(malware_regex_default, FALSE, TRUE);
2266
2267 #ifndef DISABLE_MAL_DRWEB
2268 if (!drweb_re)
2269   drweb_re = regex_must_compile(drweb_re_str, FALSE, TRUE);
2270 #endif
2271 #ifndef DISABLE_MAL_FSECURE
2272 if (!fsec_re)
2273   fsec_re = regex_must_compile(fsec_re_str, FALSE, TRUE);
2274 #endif
2275 #ifndef DISABLE_MAL_KAV
2276 if (!kav_re_sus)
2277   kav_re_sus = regex_must_compile(kav_re_sus_str, FALSE, TRUE);
2278 if (!kav_re_inf)
2279   kav_re_inf = regex_must_compile(kav_re_inf_str, FALSE, TRUE);
2280 #endif
2281 #ifndef DISABLE_MAL_AVAST
2282 if (!ava_re_clean)
2283   ava_re_clean = regex_must_compile(ava_re_clean_str, FALSE, TRUE);
2284 if (!ava_re_virus)
2285   ava_re_virus = regex_must_compile(ava_re_virus_str, FALSE, TRUE);
2286 if (!ava_re_error)
2287   ava_re_error = regex_must_compile(ava_re_error_str, FALSE, TRUE);
2288 #endif
2289 #ifndef DISABLE_MAL_FFROT6D
2290 if (!fprot6d_re_error)
2291   fprot6d_re_error = regex_must_compile(fprot6d_re_error_str, FALSE, TRUE);
2292 if (!fprot6d_re_virus)
2293   fprot6d_re_virus = regex_must_compile(fprot6d_re_virus_str, FALSE, TRUE);
2294 #endif
2295 }
2296
2297
2298 gstring *
2299 malware_show_supported(gstring * g)
2300 {
2301 g = string_cat(g, US"Malware:");
2302 for (struct scan * sc = m_scans; sc->scancode != (scanner_t)-1; sc++)
2303   g = string_fmt_append(g, " %s", sc->name);
2304 return string_cat(g, US"\n");
2305 }
2306
2307
2308 # endif /*!MACRO_PREDEF*/
2309 #endif /*WITH_CONTENT_SCAN*/
2310 /*
2311  * vi: aw ai sw=2
2312  */