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