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