ebd360cd2f8fbc697e08f352b098d33025aaa7cd
[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[EXIM_DRIVERNAME_MAX];
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 pcre2_code * malware_default_re = NULL;
133
134
135 #ifndef DISABLE_MAL_CLAM
136 /* The maximum number of clamd servers that are supported in the configuration */
137 # define MAX_CLAMD_SERVERS 32
138 # define MAX_CLAMD_SERVERS_S "32"
139
140 typedef struct clamd_address {
141   uschar * hostspec;
142   unsigned tcp_port;
143   unsigned retry;
144 } clamd_address;
145 #endif
146
147
148 #ifndef DISABLE_MAL_DRWEB
149 # define DRWEBD_SCAN_CMD             (1)     /* scan file, buffer or diskfile */
150 # define DRWEBD_RETURN_VIRUSES       (1<<0)   /* ask daemon return to us viruses names from report */
151 # define DRWEBD_IS_MAIL              (1<<19)  /* say to daemon that format is "archive MAIL" */
152
153 # define DERR_READ_ERR               (1<<0)   /* read error */
154 # define DERR_NOMEMORY               (1<<2)   /* no memory */
155 # define DERR_TIMEOUT                (1<<9)   /* scan timeout has run out */
156 # define DERR_BAD_CALL               (1<<15)  /* wrong command */
157
158 static const uschar * drweb_re_str = US "infected\\swith\\s*(.+?)$";
159 static const pcre2_code * drweb_re = NULL;
160 #endif
161
162 #ifndef DISABLE_MAL_FSECURE
163 static const uschar * fsec_re_str = US "\\S{0,5}INFECTED\\t[^\\t]*\\t([^\\t]+)\\t\\S*$";
164 static const pcre2_code * fsec_re = NULL;
165 #endif
166
167 #ifndef DISABLE_MAL_KAV
168 static const uschar * kav_re_sus_str = US "suspicion:\\s*(.+?)\\s*$";
169 static const uschar * kav_re_inf_str = US "infected:\\s*(.+?)\\s*$";
170 static const pcre2_code * kav_re_sus = NULL;
171 static const pcre2_code * kav_re_inf = NULL;
172 #endif
173
174 #ifndef DISABLE_MAL_AVAST
175 static const uschar * ava_re_clean_str = US "(?!\\\\)\\t\\[\\+\\]";
176 static const uschar * ava_re_virus_str = US "(?!\\\\)\\t\\[L\\]\\d+\\.0\\t0\\s(.*)";
177 static const uschar * ava_re_error_str = US "(?!\\\\)\\t\\[E\\]\\d+\\.0\\tError\\s\\d+\\s(.*)";
178 static const pcre2_code * ava_re_clean = NULL;
179 static const pcre2_code * ava_re_virus = NULL;
180 static const pcre2_code * ava_re_error = NULL;
181 #endif
182
183 #ifndef DISABLE_MAL_FFROT6D
184 static const uschar * fprot6d_re_error_str = US "^\\d+\\s<(.+?)>$";
185 static const uschar * fprot6d_re_virus_str = US "^\\d+\\s<infected:\\s+(.+?)>\\s+.+$";
186 static const pcre2_code * fprot6d_re_error = NULL;
187 static const pcre2_code * fprot6d_re_virus = NULL;
188 #endif
189
190
191
192 /******************************************************************************/
193
194 #ifndef DISABLE_MAL_KAV
195 /* Routine to check whether a system is big- or little-endian.
196    Ripped from http://www.faqs.org/faqs/graphics/fileformats-faq/part4/section-7.html
197    Needed for proper kavdaemon implementation. Sigh. */
198 # define BIG_MY_ENDIAN      0
199 # define LITTLE_MY_ENDIAN   1
200 static int test_byte_order(void);
201 static inline int
202 test_byte_order()
203 {
204   short int word = 0x0001;
205   char *byte = CS  &word;
206   return(byte[0] ? LITTLE_MY_ENDIAN : BIG_MY_ENDIAN);
207 }
208 #endif
209
210 BOOL malware_ok = FALSE;
211
212 /* Gross hacks for the -bmalware option; perhaps we should just create
213 the scan directory normally for that case, but look into rigging up the
214 needed header variables if not already set on the command-line? */
215 extern int spool_mbox_ok;
216 extern uschar spooled_message_id[MESSAGE_ID_LENGTH+1];
217
218
219 /* Some (currently avast only) use backslash escaped whitespace,
220 this function undoes these escapes */
221
222 #ifndef DISABLE_MAL_AVAST
223 static inline void
224 unescape(uschar *p)
225 {
226 uschar *p0;
227 for (; *p; ++p)
228   if (*p == '\\' && (isspace(p[1]) || p[1] == '\\'))
229     for (p0 = p; *p0; ++p0) *p0 = p0[1];
230 }
231 #endif
232
233 /* --- malware_*_defer --- */
234 static inline int
235 malware_panic_defer(const uschar * str)
236 {
237 log_write(0, LOG_MAIN|LOG_PANIC, "malware acl condition: %s", str);
238 return DEFER;
239 }
240 static inline int
241 malware_log_defer(const uschar * str)
242 {
243 log_write(0, LOG_MAIN, "malware acl condition: %s", str);
244 return DEFER;
245 }
246 /* --- m_*_defer --- */
247 static inline int
248 m_panic_defer(struct scan * scanent, const uschar * hostport,
249   const uschar * str)
250 {
251 return malware_panic_defer(string_sprintf("%s %s : %s",
252   scanent->name, hostport ? hostport : CUS"", str));
253 }
254 /* --- m_*_defer_3 */
255 static inline int
256 m_panic_defer_3(struct scan * scanent, const uschar * hostport,
257   const uschar * str, int fd_to_close)
258 {
259 DEBUG(D_acl) debug_print_socket(fd_to_close);
260 (void) close(fd_to_close);
261 return m_panic_defer(scanent, hostport, str);
262 }
263
264 /*************************************************/
265
266 #ifndef DISABLE_MAL_CLAM
267 /* Only used by the Clamav code, which is working from a list of servers and
268 uses the returned in_addr to get a second connection to the same system.
269 */
270 static inline int
271 m_tcpsocket(const uschar * hostname, unsigned int port,
272         host_item * host, uschar ** errstr, const blob * fastopen_blob)
273 {
274 int fd = ip_connectedsocket(SOCK_STREAM, hostname, port, port, 5,
275                           host, errstr, fastopen_blob);
276 #ifdef EXIM_TFO_FREEBSD
277 /* Under some fault conditions, FreeBSD 12.2 seen to send a (non-TFO) SYN
278 and, getting no response, wait for a long time.  Impose a 5s max. */
279 if (fd >= 0)
280   {
281   struct timeval tv = {.tv_sec = 5};
282   fd_set fds;
283   FD_ZERO(&fds); FD_SET(fd, &fds); (void) select(fd+1, NULL, &fds, NULL, &tv);
284   }
285 #endif
286 return fd;
287 }
288 #endif
289
290 static int
291 m_sock_send(int sock, uschar * buf, int cnt, uschar ** errstr)
292 {
293 if (send(sock, buf, cnt, 0) < 0)
294   {
295   int err = errno;
296   (void)close(sock);
297   *errstr = string_sprintf("unable to send to socket (%s): %s",
298          buf, strerror(err));
299   return -1;
300   }
301 return sock;
302 }
303
304 static const pcre2_code *
305 m_pcre_compile(const uschar * re, uschar ** errstr)
306 {
307 int err;
308 PCRE2_SIZE roffset;
309 const pcre2_code * cre;
310
311 if (!(cre = pcre2_compile((PCRE2_SPTR)re, PCRE2_ZERO_TERMINATED,
312               PCRE_COPT, &err, &roffset, pcre_cmp_ctx)))
313   {
314   uschar errbuf[128];
315   pcre2_get_error_message(err, errbuf, sizeof(errbuf));
316   *errstr= string_sprintf("regular expression error in '%s': %s at offset %ld",
317       re, errbuf, (long)roffset);
318   }
319 return cre;
320 }
321
322 uschar *
323 m_pcre_exec(const pcre2_code * cre, uschar * text)
324 {
325 pcre2_match_data * md = pcre2_match_data_create(2, pcre_gen_ctx);
326 int i = pcre2_match(cre, text, PCRE2_ZERO_TERMINATED, 0, 0, md, pcre_mtc_ctx);
327 PCRE2_UCHAR * substr = NULL;
328 PCRE2_SIZE slen;
329
330 if (i >= 2)                             /* Got it */
331   pcre2_substring_get_bynumber(md, 1, &substr, &slen);
332 return US substr;
333 }
334
335 static const pcre2_code *
336 m_pcre_nextinlist(const uschar ** list, int * sep,
337  char * listerr, uschar ** errstr)
338 {
339 const uschar * list_ele;
340 const pcre2_code * 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 pcre2_code *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           pcre2_match_data * md = pcre2_match_data_create(2, pcre_gen_ctx);
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 = pcre2_match(drweb_re, (PCRE2_SPTR)tmpbuf, PCRE2_ZERO_TERMINATED,
951                                 0, 0, md, pcre_mtc_ctx);
952           if (result >= 2)
953             {
954             PCRE2_SIZE * ovec = pcre2_get_ovector_pointer(md);
955
956             if (i==0)   /* the first name we just copy to malware_name */
957               g = string_catn(NULL, US ovec[2], ovec[3] - ovec[2]);
958
959             else        /* concatenate each new virus name to previous */
960               {
961               g = string_catn(g, US"/", 1);
962               g = string_catn(g, US ovec[2], ovec[3] - ovec[2]);
963               }
964             }
965           }
966           malware_name = string_from_gstring(g);
967         }
968       else
969         {
970         const char *drweb_s = NULL;
971
972         if (drweb_rc & DERR_READ_ERR) drweb_s = "read error";
973         if (drweb_rc & DERR_NOMEMORY) drweb_s = "no memory";
974         if (drweb_rc & DERR_TIMEOUT)  drweb_s = "timeout";
975         if (drweb_rc & DERR_BAD_CALL) drweb_s = "wrong command";
976         /* retcodes DERR_SYMLINK, DERR_NO_REGFILE, DERR_SKIPPED.
977          * DERR_TOO_BIG, DERR_TOO_COMPRESSED, DERR_SPAM,
978          * DERR_CRC_ERROR, DERR_READSOCKET, DERR_WRITE_ERR
979          * and others are ignored */
980         if (drweb_s)
981           return m_panic_defer_3(scanent, CUS callout_address,
982             string_sprintf("drweb daemon retcode 0x%x (%s)", drweb_rc, drweb_s),
983             malware_daemon_ctx.sock);
984
985         /* no virus found */
986         malware_name = NULL;
987         }
988       break;
989       } /* drweb */
990 #endif
991
992 #ifndef DISABLE_MAL_AVE
993     case M_AVES: /* "aveserver" scanner type -------------------------------- */
994       {
995       uschar buf[32768];
996       int result;
997
998       /* read aveserver's greeting and see if it is ready (2xx greeting) */
999       buf[0] = 0;
1000       recv_line(malware_daemon_ctx.sock, buf, sizeof(buf), tmo);
1001
1002       if (buf[0] != '2')                /* aveserver is having problems */
1003         return m_panic_defer_3(scanent, CUS callout_address,
1004           string_sprintf("unavailable (Responded: %s).",
1005                           ((buf[0] != 0) ? buf : US "nothing") ),
1006           malware_daemon_ctx.sock);
1007
1008       /* prepare our command */
1009       (void)string_format(buf, sizeof(buf), "SCAN bPQRSTUW %s\r\n",
1010                                                 eml_filename);
1011
1012       /* and send it */
1013       DEBUG(D_acl) debug_printf_indent("Malware scan: issuing %s %s\n",
1014         scanner_name, buf);
1015       if (m_sock_send(malware_daemon_ctx.sock, buf, Ustrlen(buf), &errstr) < 0)
1016         return m_panic_defer(scanent, CUS callout_address, errstr);
1017
1018       malware_name = NULL;
1019       result = 0;
1020       /* read response lines, find malware name and final response */
1021       while (recv_line(malware_daemon_ctx.sock, buf, sizeof(buf), tmo) > 0)
1022         {
1023         if (buf[0] == '2')
1024           break;
1025         if (buf[0] == '5')              /* aveserver is having problems */
1026           {
1027           result = m_panic_defer(scanent, CUS callout_address,
1028              string_sprintf("unable to scan file %s (Responded: %s).",
1029                              eml_filename, buf));
1030           break;
1031           }
1032         if (Ustrncmp(buf,"322",3) == 0)
1033           {
1034           uschar *p = Ustrchr(&buf[4], ' ');
1035           *p = '\0';
1036           malware_name = string_copy(&buf[4]);
1037           }
1038         }
1039
1040       if (m_sock_send(malware_daemon_ctx.sock, US"quit\r\n", 6, &errstr) < 0)
1041         return m_panic_defer(scanent, CUS callout_address, errstr);
1042
1043       /* read aveserver's greeting and see if it is ready (2xx greeting) */
1044       buf[0] = 0;
1045       recv_line(malware_daemon_ctx.sock, buf, sizeof(buf), tmo);
1046
1047       if (buf[0] != '2')                /* aveserver is having problems */
1048         return m_panic_defer_3(scanent, CUS callout_address,
1049           string_sprintf("unable to quit dialogue (Responded: %s).",
1050                         ((buf[0] != 0) ? buf : US "nothing") ),
1051           malware_daemon_ctx.sock);
1052
1053       if (result == DEFER)
1054         {
1055         (void)close(malware_daemon_ctx.sock);
1056         return DEFER;
1057         }
1058       break;
1059       } /* aveserver */
1060 #endif
1061
1062 #ifndef DISABLE_MAL_FSECURE
1063     case M_FSEC: /* "fsecure" scanner type ---------------------------------- */
1064       {
1065       int i, bread = 0;
1066       uschar * file_name;
1067       uschar av_buffer[1024];
1068       static uschar *cmdopt[] = { US"CONFIGURE\tARCHIVE\t1\n",
1069                                       US"CONFIGURE\tTIMEOUT\t0\n",
1070                                       US"CONFIGURE\tMAXARCH\t5\n",
1071                                       US"CONFIGURE\tMIME\t1\n" };
1072
1073       malware_name = NULL;
1074
1075       DEBUG(D_acl) debug_printf_indent("Malware scan: issuing %s scan [%s]\n",
1076           scanner_name, scanner_options);
1077       /* pass options */
1078       memset(av_buffer, 0, sizeof(av_buffer));
1079       for (i = 0; i != nelem(cmdopt); i++)
1080         {
1081
1082         if (m_sock_send(malware_daemon_ctx.sock, cmdopt[i], Ustrlen(cmdopt[i]), &errstr) < 0)
1083           return m_panic_defer(scanent, CUS callout_address, errstr);
1084
1085         bread = ip_recv(&malware_daemon_ctx, av_buffer, sizeof(av_buffer), tmo);
1086         if (bread > 0) av_buffer[bread]='\0';
1087         if (bread < 0)
1088           return m_panic_defer_3(scanent, CUS callout_address,
1089             string_sprintf("unable to read answer %d (%s)", i, strerror(errno)),
1090             malware_daemon_ctx.sock);
1091         for (int j = 0; j < bread; j++)
1092           if (av_buffer[j] == '\r' || av_buffer[j] == '\n')
1093             av_buffer[j] ='@';
1094         }
1095
1096       /* pass the mailfile to fsecure */
1097       file_name = string_sprintf("SCAN\t%s\n", eml_filename);
1098
1099       if (m_sock_send(malware_daemon_ctx.sock, file_name, Ustrlen(file_name), &errstr) < 0)
1100         return m_panic_defer(scanent, CUS callout_address, errstr);
1101
1102       /* set up match */
1103       /* todo also SUSPICION\t */
1104       if (!fsec_re)
1105         fsec_re = m_pcre_compile(fsec_re_str, &errstr);
1106
1107       /* read report, linewise. Apply a timeout as the Fsecure daemon
1108       sometimes wants an answer to "PING" but they won't tell us what */
1109         {
1110         uschar * p = av_buffer;
1111         uschar * q;
1112
1113         for (;;)
1114           {
1115           errno = ETIMEDOUT;
1116           i =  av_buffer+sizeof(av_buffer)-p;
1117           if ((bread= ip_recv(&malware_daemon_ctx, p, i-1, tmo)) < 0)
1118             return m_panic_defer_3(scanent, CUS callout_address,
1119               string_sprintf("unable to read result (%s)", strerror(errno)),
1120               malware_daemon_ctx.sock);
1121
1122           for (p[bread] = '\0'; (q = Ustrchr(p, '\n')); p = q+1)
1123             {
1124             *q = '\0';
1125
1126             /* Really search for virus again? */
1127             if (!malware_name)
1128               /* try matcher on the line, grab substring */
1129               malware_name = m_pcre_exec(fsec_re, p);
1130
1131             if (Ustrstr(p, "OK\tScan ok."))
1132               goto fsec_found;
1133             }
1134
1135           /* copy down the trailing partial line then read another chunk */
1136           i =  av_buffer+sizeof(av_buffer)-p;
1137           memmove(av_buffer, p, i);
1138           p = av_buffer+i;
1139           }
1140         }
1141
1142       fsec_found:
1143         break;
1144       } /* fsecure */
1145 #endif
1146
1147 #ifndef DISABLE_MAL_KAV
1148     case M_KAVD: /* "kavdaemon" scanner type -------------------------------- */
1149       {
1150       time_t t;
1151       uschar tmpbuf[1024];
1152       uschar * scanrequest;
1153       int kav_rc;
1154       unsigned long kav_reportlen;
1155       int bread;
1156       const pcre2_code *kav_re;
1157       uschar *p;
1158
1159       /* get current date and time, build scan request */
1160       time(&t);
1161       /* pdp note: before the eml_filename parameter, this scanned the
1162       directory; not finding documentation, so we'll strip off the directory.
1163       The side-effect is that the test framework scanning may end up in
1164       scanning more than was requested, but for the normal interface, this is
1165       fine. */
1166
1167       strftime(CS tmpbuf, sizeof(tmpbuf), "%d %b %H:%M:%S", localtime(&t));
1168       scanrequest = string_sprintf("<0>%s:%s", CS tmpbuf, eml_filename);
1169       p = Ustrrchr(scanrequest, '/');
1170       if (p)
1171         *p = '\0';
1172
1173       DEBUG(D_acl) debug_printf_indent("Malware scan: issuing %s scan [%s]\n",
1174           scanner_name, scanner_options);
1175
1176       /* send scan request */
1177       if (m_sock_send(malware_daemon_ctx.sock, scanrequest, Ustrlen(scanrequest)+1, &errstr) < 0)
1178         return m_panic_defer(scanent, CUS callout_address, errstr);
1179
1180       /* wait for result */
1181       if (!recv_len(malware_daemon_ctx.sock, tmpbuf, 2, tmo))
1182         return m_panic_defer_3(scanent, CUS callout_address,
1183                             US"unable to read 2 bytes from socket.", malware_daemon_ctx.sock);
1184
1185       /* get errorcode from one nibble */
1186       kav_rc = tmpbuf[ test_byte_order()==LITTLE_MY_ENDIAN ? 0 : 1 ] & 0x0F;
1187       switch(kav_rc)
1188       {
1189       case 5: case 6: /* improper kavdaemon configuration */
1190         return m_panic_defer_3(scanent, CUS callout_address,
1191                 US"please reconfigure kavdaemon to NOT disinfect or remove infected files.",
1192                 malware_daemon_ctx.sock);
1193       case 1:
1194         return m_panic_defer_3(scanent, CUS callout_address,
1195                 US"reported 'scanning not completed' (code 1).", malware_daemon_ctx.sock);
1196       case 7:
1197         return m_panic_defer_3(scanent, CUS callout_address,
1198                 US"reported 'kavdaemon damaged' (code 7).", malware_daemon_ctx.sock);
1199       }
1200
1201       /* code 8 is not handled, since it is ambiguous. It appears mostly on
1202       bounces where part of a file has been cut off */
1203
1204       /* "virus found" return codes (2-4) */
1205       if (kav_rc > 1 && kav_rc < 5)
1206         {
1207         int report_flag = 0;
1208
1209         /* setup default virus name */
1210         malware_name = US"unknown";
1211
1212         report_flag = tmpbuf[ test_byte_order() == LITTLE_MY_ENDIAN ? 1 : 0 ];
1213
1214         /* read the report, if available */
1215         if (report_flag == 1)
1216           {
1217           /* read report size */
1218           if (!recv_len(malware_daemon_ctx.sock, &kav_reportlen, 4, tmo))
1219             return m_panic_defer_3(scanent, CUS callout_address,
1220                   US"cannot read report size", malware_daemon_ctx.sock);
1221
1222           /* it's possible that avp returns av_buffer[1] == 1 but the
1223           reportsize is 0 (!?) */
1224           if (kav_reportlen > 0)
1225             {
1226             /* set up match regex, depends on retcode */
1227             if (kav_rc == 3)
1228               {
1229               if (!kav_re_sus) kav_re_sus = m_pcre_compile(kav_re_sus_str, &errstr);
1230               kav_re = kav_re_sus;
1231               }
1232             else
1233               {
1234               if (!kav_re_inf) kav_re_inf = m_pcre_compile(kav_re_inf_str, &errstr);
1235               kav_re = kav_re_inf;
1236               }
1237
1238             /* read report, linewise.  Using size from stream to read amount of data
1239             from same stream is safe enough. */
1240             /* coverity[tainted_data] */
1241             while (kav_reportlen > 0)
1242               {
1243               if ((bread = recv_line(malware_daemon_ctx.sock, tmpbuf, sizeof(tmpbuf), tmo)) < 0)
1244                 break;
1245               kav_reportlen -= bread+1;
1246
1247               /* try matcher on the line, grab substring */
1248               if ((malware_name = m_pcre_exec(kav_re, tmpbuf)))
1249                 break;
1250               }
1251             }
1252           }
1253         }
1254       else /* no virus found */
1255         malware_name = NULL;
1256
1257       break;
1258       }
1259 #endif
1260
1261 #ifndef DISABLE_MAL_CMDLINE
1262     case M_CMDL: /* "cmdline" scanner type ---------------------------------- */
1263       {
1264       const uschar *cmdline_scanner = scanner_options;
1265       const pcre2_code *cmdline_trigger_re;
1266       const pcre2_code *cmdline_regex_re;
1267       uschar * file_name;
1268       uschar * commandline;
1269       void (*eximsigchld)(int);
1270       void (*eximsigpipe)(int);
1271       FILE *scanner_out = NULL;
1272       int scanner_fd;
1273       FILE *scanner_record = NULL;
1274       uschar linebuffer[32767];
1275       int rcnt;
1276       int trigger = 0;
1277       uschar *p;
1278
1279       if (!cmdline_scanner)
1280         return m_panic_defer(scanent, NULL, errstr);
1281
1282       /* find scanner output trigger */
1283       cmdline_trigger_re = m_pcre_nextinlist(&av_scanner_work, &sep,
1284                                 "missing trigger specification", &errstr);
1285       if (!cmdline_trigger_re)
1286         return m_panic_defer(scanent, NULL, errstr);
1287
1288       /* find scanner name regex */
1289       cmdline_regex_re = m_pcre_nextinlist(&av_scanner_work, &sep,
1290                           "missing virus name regex specification", &errstr);
1291       if (!cmdline_regex_re)
1292         return m_panic_defer(scanent, NULL, errstr);
1293
1294       /* prepare scanner call; despite the naming, file_name holds a directory
1295       name which is documented as the value given to %s. */
1296
1297       file_name = string_copy(eml_filename);
1298       p = Ustrrchr(file_name, '/');
1299       if (p)
1300         *p = '\0';
1301       commandline = string_sprintf(CS cmdline_scanner, file_name);
1302
1303       /* redirect STDERR too */
1304       commandline = string_sprintf("%s 2>&1", commandline);
1305
1306       DEBUG(D_acl) debug_printf_indent("Malware scan: issuing %s scan [%s]\n",
1307               scanner_name, commandline);
1308
1309       /* store exims signal handlers */
1310       eximsigchld = signal(SIGCHLD,SIG_DFL);
1311       eximsigpipe = signal(SIGPIPE,SIG_DFL);
1312
1313       if (!(scanner_out = popen(CS commandline,"r")))
1314         {
1315         int err = errno;
1316         signal(SIGCHLD,eximsigchld); signal(SIGPIPE,eximsigpipe);
1317         return m_panic_defer(scanent, NULL,
1318           string_sprintf("call (%s) failed: %s.", commandline, strerror(err)));
1319         }
1320       scanner_fd = fileno(scanner_out);
1321
1322       file_name = string_sprintf("%s/%s_scanner_output", eml_dir, message_id);
1323
1324       if (!(scanner_record = modefopen(file_name, "wb", SPOOL_MODE)))
1325         {
1326         int err = errno;
1327         (void) pclose(scanner_out);
1328         signal(SIGCHLD,eximsigchld); signal(SIGPIPE,eximsigpipe);
1329         return m_panic_defer(scanent, NULL, string_sprintf(
1330             "opening scanner output file (%s) failed: %s.",
1331             file_name, strerror(err)));
1332         }
1333
1334       /* look for trigger while recording output */
1335       while ((rcnt = recv_line(scanner_fd, linebuffer,
1336                       sizeof(linebuffer), tmo)))
1337         {
1338         if (rcnt < 0)
1339           {
1340           int err = errno;
1341           if (rcnt == -1)
1342             break;
1343           (void) pclose(scanner_out);
1344           signal(SIGCHLD,eximsigchld); signal(SIGPIPE,eximsigpipe);
1345           return m_panic_defer(scanent, NULL, string_sprintf(
1346               "unable to read from scanner (%s): %s",
1347               commandline, strerror(err)));
1348           }
1349
1350         if (Ustrlen(linebuffer) > fwrite(linebuffer, 1, Ustrlen(linebuffer), scanner_record))
1351           {
1352           /* short write */
1353           (void) pclose(scanner_out);
1354           signal(SIGCHLD,eximsigchld); signal(SIGPIPE,eximsigpipe);
1355           return m_panic_defer(scanent, NULL, string_sprintf(
1356             "short write on scanner output file (%s).", file_name));
1357           }
1358         putc('\n', scanner_record);
1359         /* try trigger match */
1360         if (  !trigger
1361            && regex_match_and_setup(cmdline_trigger_re, linebuffer, 0, -1)
1362            )
1363           trigger = 1;
1364         }
1365
1366       (void)fclose(scanner_record);
1367       sep = pclose(scanner_out);
1368       signal(SIGCHLD,eximsigchld); signal(SIGPIPE,eximsigpipe);
1369       if (sep != 0)
1370           return m_panic_defer(scanent, NULL,
1371               sep == -1
1372               ? string_sprintf("running scanner failed: %s", strerror(sep))
1373               : string_sprintf("scanner returned error code: %d", sep));
1374
1375       if (trigger)
1376         {
1377         uschar * s;
1378         /* setup default virus name */
1379         malware_name = US"unknown";
1380
1381         /* re-open the scanner output file, look for name match */
1382         scanner_record = Ufopen(file_name, "rb");
1383         while (Ufgets(linebuffer, sizeof(linebuffer), scanner_record))
1384           if ((s = m_pcre_exec(cmdline_regex_re, linebuffer))) /* try match */
1385             malware_name = s;
1386         (void)fclose(scanner_record);
1387         }
1388       else /* no virus found */
1389         malware_name = NULL;
1390       break;
1391       } /* cmdline */
1392 #endif
1393
1394 #ifndef DISABLE_MAL_SOPHIE
1395     case M_SOPHIE: /* "sophie" scanner type --------------------------------- */
1396       {
1397       int bread = 0;
1398       uschar *p;
1399       uschar * file_name;
1400       uschar av_buffer[1024];
1401
1402       /* pass the scan directory to sophie */
1403       file_name = string_copy(eml_filename);
1404       if ((p = Ustrrchr(file_name, '/')))
1405         *p = '\0';
1406
1407       DEBUG(D_acl) debug_printf_indent("Malware scan: issuing %s scan [%s]\n",
1408           scanner_name, scanner_options);
1409
1410       if (  write(malware_daemon_ctx.sock, file_name, Ustrlen(file_name)) < 0
1411          || write(malware_daemon_ctx.sock, "\n", 1) != 1
1412          )
1413         return m_panic_defer_3(scanent, CUS callout_address,
1414           string_sprintf("unable to write to UNIX socket (%s)", scanner_options),
1415           malware_daemon_ctx.sock);
1416
1417       /* wait for result */
1418       memset(av_buffer, 0, sizeof(av_buffer));
1419       if ((bread = ip_recv(&malware_daemon_ctx, av_buffer, sizeof(av_buffer), tmo)) <= 0)
1420         return m_panic_defer_3(scanent, CUS callout_address,
1421           string_sprintf("unable to read from UNIX socket (%s)", scanner_options),
1422           malware_daemon_ctx.sock);
1423
1424       /* infected ? */
1425       if (av_buffer[0] == '1') {
1426         uschar * s = Ustrchr(av_buffer, '\n');
1427         if (s)
1428           *s = '\0';
1429         malware_name = string_copy(&av_buffer[2]);
1430       }
1431       else if (!strncmp(CS av_buffer, "-1", 2))
1432         return m_panic_defer_3(scanent, CUS callout_address,
1433                 US"scanner reported error", malware_daemon_ctx.sock);
1434       else /* all ok, no virus */
1435         malware_name = NULL;
1436
1437       break;
1438       }
1439 #endif
1440
1441 #ifndef DISABLE_MAL_CLAM
1442     case M_CLAMD: /* "clamd" scanner type ----------------------------------- */
1443       {
1444 /* This code was originally contributed by David Saez */
1445 /* There are three scanning methods available to us:
1446 *  (1) Use the SCAN command, pointing to a file in the filesystem
1447 *  (2) Use the STREAM command, send the data on a separate port
1448 *  (3) Use the zINSTREAM command, send the data inline
1449 * The zINSTREAM command was introduced with ClamAV 0.95, which marked
1450 * STREAM deprecated; see: http://wiki.clamav.net/bin/view/Main/UpgradeNotes095
1451 * In Exim, we use SCAN if using a Unix-domain socket or explicitly told that
1452 * the TCP-connected daemon is actually local; otherwise we use zINSTREAM
1453 * See Exim bug 926 for details.  */
1454
1455       uschar *p, *vname, *result_tag;
1456       int bread=0;
1457       uschar av_buffer[1024];
1458       uschar *hostname = US"";
1459       host_item connhost;
1460       int clam_fd, result;
1461       unsigned int fsize_uint;
1462       BOOL use_scan_command = FALSE;
1463       clamd_address * cv[MAX_CLAMD_SERVERS];
1464       int num_servers = 0;
1465       uint32_t send_size, send_final_zeroblock;
1466       blob cmd_str;
1467
1468       /*XXX if unixdomain socket, only one server supported. Needs fixing;
1469       there's no reason we should not mix local and remote servers */
1470
1471       if (*scanner_options == '/')
1472         {
1473         clamd_address * cd;
1474         const uschar * sublist;
1475         int subsep = ' ';
1476
1477         /* Local file; so we def want to use_scan_command and don't want to try
1478          * passing IP/port combinations */
1479         use_scan_command = TRUE;
1480         cd = (clamd_address *) store_get(sizeof(clamd_address), FALSE);
1481
1482         /* extract socket-path part */
1483         sublist = scanner_options;
1484         cd->hostspec = string_nextinlist(&sublist, &subsep, NULL, 0);
1485
1486         /* parse options */
1487         if (clamd_option(cd, sublist, &subsep) != OK)
1488           return m_panic_defer(scanent, NULL,
1489             string_sprintf("bad option '%s'", scanner_options));
1490         cv[0] = cd;
1491         }
1492       else
1493         {
1494         /* Go through the rest of the list of host/port and construct an array
1495          * of servers to try. The first one is the bit we just passed from
1496          * scanner_options so process that first and then scan the remainder of
1497          * the address buffer */
1498         do
1499           {
1500           clamd_address * cd;
1501           const uschar * sublist;
1502           int subsep = ' ';
1503           uschar * s;
1504
1505           /* The 'local' option means use the SCAN command over the network
1506            * socket (ie common file storage in use) */
1507           /*XXX we could accept this also as a local option? */
1508           if (strcmpic(scanner_options, US"local") == 0)
1509             {
1510             use_scan_command = TRUE;
1511             continue;
1512             }
1513
1514           cd = (clamd_address *) store_get(sizeof(clamd_address), FALSE);
1515
1516           /* extract host and port part */
1517           sublist = scanner_options;
1518           if (!(cd->hostspec = string_nextinlist(&sublist, &subsep, NULL, 0)))
1519             {
1520             (void) m_panic_defer(scanent, NULL,
1521                       string_sprintf("missing address: '%s'", scanner_options));
1522             continue;
1523             }
1524           if (!(s = string_nextinlist(&sublist, &subsep, NULL, 0)))
1525             {
1526             (void) m_panic_defer(scanent, NULL,
1527                       string_sprintf("missing port: '%s'", scanner_options));
1528             continue;
1529             }
1530           cd->tcp_port = atoi(CS s);
1531
1532           /* parse options */
1533           /*XXX should these options be common over scanner types? */
1534           if (clamd_option(cd, sublist, &subsep) != OK)
1535             return m_panic_defer(scanent, NULL,
1536               string_sprintf("bad option '%s'", scanner_options));
1537
1538           cv[num_servers++] = cd;
1539           if (num_servers >= MAX_CLAMD_SERVERS)
1540             {
1541             (void) m_panic_defer(scanent, NULL,
1542                   US"More than " MAX_CLAMD_SERVERS_S " clamd servers "
1543                   "specified; only using the first " MAX_CLAMD_SERVERS_S );
1544             break;
1545             }
1546           } while ((scanner_options = string_nextinlist(&av_scanner_work, &sep,
1547                                         NULL, 0)));
1548
1549         /* check if we have at least one server */
1550         if (!num_servers)
1551           return m_panic_defer(scanent, NULL,
1552             US"no useable server addresses in malware configuration option.");
1553         }
1554
1555       /* See the discussion of response formats below to see why we really
1556       don't like colons in filenames when passing filenames to ClamAV. */
1557       if (use_scan_command && Ustrchr(eml_filename, ':'))
1558         return m_panic_defer(scanent, NULL,
1559           string_sprintf("local/SCAN mode incompatible with" \
1560             " : in path to email filename [%s]", eml_filename));
1561
1562       /* Set up the very first data we will be sending */
1563       if (!use_scan_command)
1564         { cmd_str.data = US"zINSTREAM"; cmd_str.len = 10; }
1565       else
1566         {
1567         int n;
1568         cmd_str.data = string_sprintf("SCAN %s\n%n", eml_filename, &n);
1569         cmd_str.len = n;                /* .len is a size_t */
1570         }
1571
1572       /* We have some network servers specified */
1573       if (num_servers)
1574         {
1575         /* Confirmed in ClamAV source (0.95.3) that the TCPAddr option of clamd
1576         only supports AF_INET, but we should probably be looking to the
1577         future and rewriting this to be protocol-independent anyway. */
1578
1579         while (num_servers > 0)
1580           {
1581           int i = random_number(num_servers);
1582           clamd_address * cd = cv[i];
1583
1584           DEBUG(D_acl) debug_printf_indent("trying server name %s, port %u\n",
1585                          cd->hostspec, cd->tcp_port);
1586
1587           /* Lookup the host. This is to ensure that we connect to the same IP
1588           on both connections (as one host could resolve to multiple ips) */
1589           for (;;)
1590             {
1591             /*XXX we trust that the cmd_str is idempotent */
1592             if ((malware_daemon_ctx.sock = m_tcpsocket(cd->hostspec, cd->tcp_port,
1593                                     &connhost, &errstr,
1594                                     use_scan_command ? &cmd_str : NULL)) >= 0)
1595               {
1596               /* Connection successfully established with a server */
1597               hostname = cd->hostspec;
1598               if (use_scan_command) cmd_str.len = 0;
1599               break;
1600               }
1601             if (cd->retry <= 0) break;
1602             while (cd->retry > 0) cd->retry = sleep(cd->retry);
1603             }
1604           if (malware_daemon_ctx.sock >= 0)
1605             break;
1606
1607           (void) m_panic_defer(scanent, CUS callout_address, errstr);
1608
1609           /* Remove the server from the list. XXX We should free the memory */
1610           num_servers--;
1611           for (; i < num_servers; i++)
1612             cv[i] = cv[i+1];
1613           }
1614
1615         if (num_servers == 0)
1616           return m_panic_defer(scanent, NULL, US"all servers failed");
1617         }
1618       else
1619         for (;;)
1620           {
1621           if ((malware_daemon_ctx.sock = ip_unixsocket(cv[0]->hostspec, &errstr)) >= 0)
1622             {
1623             hostname = cv[0]->hostspec;
1624             break;
1625             }
1626           if (cv[0]->retry <= 0)
1627             return m_panic_defer(scanent, CUS callout_address, errstr);
1628           while (cv[0]->retry > 0) cv[0]->retry = sleep(cv[0]->retry);
1629           }
1630
1631       /* have socket in variable "sock"; command to use is semi-independent of
1632       the socket protocol.  We use SCAN if is local (either Unix/local
1633       domain socket, or explicitly told local) else we stream the data.
1634       How we stream the data depends upon how we were built.  */
1635
1636       if (!use_scan_command)
1637         {
1638         struct stat st;
1639 #if defined(EXIM_TCP_CORK) && !defined(OS_SENDFILE)
1640         BOOL corked = TRUE;
1641 #endif
1642         /* New protocol: "zINSTREAM\n" followed by a sequence of <length><data>
1643         chunks, <n> a 4-byte number (network order), terminated by a zero-length
1644         chunk. We only send one chunk. */
1645
1646         DEBUG(D_acl) debug_printf_indent(
1647             "Malware scan: issuing %s new-style remote scan (zINSTREAM)\n",
1648             scanner_name);
1649
1650 #if defined(EXIM_TCP_CORK)
1651         (void) setsockopt(malware_daemon_ctx.sock, IPPROTO_TCP, EXIM_TCP_CORK,
1652                           US &on, sizeof(on));
1653 #endif
1654         /* Pass the string to ClamAV (10 = "zINSTREAM\0"), if not already sent */
1655         if (cmd_str.len)
1656           if (send(malware_daemon_ctx.sock, cmd_str.data, cmd_str.len, 0) < 0)
1657             return m_panic_defer_3(scanent, CUS hostname,
1658               string_sprintf("unable to send zINSTREAM to socket (%s)",
1659                 strerror(errno)),
1660               malware_daemon_ctx.sock);
1661
1662         if ((clam_fd = exim_open2(CS eml_filename, O_RDONLY)) < 0)
1663           {
1664           int err = errno;
1665           return m_panic_defer_3(scanent, NULL,
1666             string_sprintf("can't open spool file %s: %s",
1667               eml_filename, strerror(err)),
1668             malware_daemon_ctx.sock);
1669           }
1670         if (fstat(clam_fd, &st) < 0)
1671           {
1672           int err = errno;
1673           (void)close(clam_fd);
1674           return m_panic_defer_3(scanent, NULL,
1675             string_sprintf("can't stat spool file %s: %s",
1676               eml_filename, strerror(err)),
1677             malware_daemon_ctx.sock);
1678           }
1679         fsize_uint = (unsigned int) st.st_size;
1680         if ((off_t)fsize_uint != st.st_size)
1681           {
1682           (void)close(clam_fd);
1683           return m_panic_defer_3(scanent, NULL,
1684             string_sprintf("stat spool file %s, size overflow", eml_filename),
1685             malware_daemon_ctx.sock);
1686           }
1687
1688         /* send file size */
1689         send_size = htonl(fsize_uint);
1690         if (send(malware_daemon_ctx.sock, &send_size, sizeof(send_size), 0) < 0)
1691           return m_panic_defer_3(scanent, NULL,
1692             string_sprintf("unable to send file size to socket (%s)", hostname),
1693             malware_daemon_ctx.sock);
1694
1695         /* send file body */
1696         while (fsize_uint)
1697           {
1698 #ifdef OS_SENDFILE
1699           int n = os_sendfile(malware_daemon_ctx.sock, clam_fd, NULL, (size_t)fsize_uint);
1700           if (n < 0)
1701             return m_panic_defer_3(scanent, NULL,
1702               string_sprintf("unable to send file body to socket (%s): %s", hostname, strerror(errno)),
1703               malware_daemon_ctx.sock);
1704           fsize_uint -= n;
1705 #else
1706           int n = MIN(fsize_uint, big_buffer_size);
1707           if ((n = read(clam_fd, big_buffer, n)) < 0)
1708             return m_panic_defer_3(scanent, NULL,
1709               string_sprintf("can't read spool file %s: %s",
1710                 eml_filename, strerror(errno)),
1711               malware_daemon_ctx.sock);
1712           if (send(malware_daemon_ctx.sock, big_buffer, (size_t)n, 0) < 0)
1713             return m_panic_defer_3(scanent, NULL,
1714               string_sprintf("unable to send file body to socket (%s): %s", hostname, strerror(errno)),
1715               malware_daemon_ctx.sock);
1716           fsize_uint -= n;
1717 # ifdef EXIM_TCP_CORK
1718           if (corked)
1719             {
1720             corked = FALSE;
1721             (void) setsockopt(malware_daemon_ctx.sock, IPPROTO_TCP, EXIM_TCP_CORK,
1722                               US &off, sizeof(off));
1723             }
1724 # endif
1725 #endif  /*!OS_SENDFILE*/
1726
1727           }
1728
1729         send_final_zeroblock = 0;
1730         if (send(malware_daemon_ctx.sock, &send_final_zeroblock, sizeof(send_final_zeroblock), 0) < 0)
1731           return m_panic_defer_3(scanent, NULL,
1732             string_sprintf("unable to send file terminator to socket (%s)", hostname),
1733             malware_daemon_ctx.sock);
1734 #ifdef OS_SENDFILE
1735         (void) setsockopt(malware_daemon_ctx.sock, IPPROTO_TCP, EXIM_TCP_CORK,
1736                           US &off, sizeof(off));
1737 #endif
1738         }
1739       else
1740         { /* use scan command */
1741         /* Send a SCAN command pointing to a filename; then in the then in the
1742         scan-method-neutral part, read the response back */
1743
1744 /* ================================================================= */
1745
1746         /* Prior to the reworking post-Exim-4.72, this scanned a directory,
1747         which dates to when ClamAV needed us to break apart the email into the
1748         MIME parts (eg, with the now deprecated demime condition coming first).
1749         Some time back, ClamAV gained the ability to deconstruct the emails, so
1750         doing this would actually have resulted in the mail attachments being
1751         scanned twice, in the broken out files and from the original .eml.
1752         Since ClamAV now handles emails (and has for quite some time) we can
1753         just use the email file itself. */
1754         /* Pass the string to ClamAV (7 = "SCAN \n" + \0), if not already sent */
1755
1756         DEBUG(D_acl) debug_printf_indent(
1757             "Malware scan: issuing %s local-path scan [%s]\n",
1758             scanner_name, scanner_options);
1759
1760         if (cmd_str.len)
1761           if (send(malware_daemon_ctx.sock, cmd_str.data, cmd_str.len, 0) < 0)
1762             return m_panic_defer_3(scanent, CUS callout_address,
1763               string_sprintf("unable to write to socket (%s)", strerror(errno)),
1764               malware_daemon_ctx.sock);
1765
1766         /* Do not shut down the socket for writing; a user report noted that
1767         clamd 0.70 does not react well to this. */
1768         }
1769       /* Commands have been sent, no matter which scan method or connection
1770       type we're using; now just read the result, independent of method. */
1771
1772       /* Read the result */
1773       memset(av_buffer, 0, sizeof(av_buffer));
1774       bread = ip_recv(&malware_daemon_ctx, av_buffer, sizeof(av_buffer), tmo);
1775       (void)close(malware_daemon_ctx.sock);
1776       malware_daemon_ctx.sock = -1;
1777       malware_daemon_ctx.tls_ctx = NULL;
1778
1779       if (bread <= 0)
1780         return m_panic_defer(scanent, CUS callout_address,
1781           string_sprintf("unable to read from socket (%s)",
1782           errno == 0 ? "EOF" : strerror(errno)));
1783
1784       if (bread == sizeof(av_buffer))
1785         return m_panic_defer(scanent, CUS callout_address,
1786                 US"buffer too small");
1787       /* We're now assured of a NULL at the end of av_buffer */
1788
1789       /* Check the result. ClamAV returns one of two result formats.
1790       In the basic mode, the response is of the form:
1791         infected: -> "<filename>: <virusname> FOUND"
1792         not-infected: -> "<filename>: OK"
1793         error: -> "<filename>: <errcode> ERROR
1794       If the ExtendedDetectionInfo option has been turned on, then we get:
1795         "<filename>: <virusname>(<virushash>:<virussize>) FOUND"
1796       for the infected case.  Compare:
1797 /tmp/eicar.com: Eicar-Test-Signature FOUND
1798 /tmp/eicar.com: Eicar-Test-Signature(44d88612fea8a8f36de82e1278abb02f:68) FOUND
1799
1800       In the streaming case, clamd uses the filename "stream" which you should
1801       be able to verify with { ktrace clamdscan --stream /tmp/eicar.com }.  (The
1802       client app will replace "stream" with the original filename before returning
1803       results to stdout, but the trace shows the data).
1804
1805       We will assume that the pathname passed to clamd from Exim does not contain
1806       a colon.  We will have whined loudly above if the eml_filename does (and we're
1807       passing a filename to clamd). */
1808
1809       if (!(*av_buffer))
1810         return m_panic_defer(scanent, CUS callout_address,
1811                 US"ClamAV returned null");
1812
1813       /* strip newline at the end (won't be present for zINSTREAM)
1814       (also any trailing whitespace, which shouldn't exist, but we depend upon
1815       this below, so double-check) */
1816
1817       p = av_buffer + Ustrlen(av_buffer) - 1;
1818       if (*p == '\n') *p = '\0';
1819
1820       DEBUG(D_acl) debug_printf_indent("Malware response: %s\n", av_buffer);
1821
1822       while (isspace(*--p) && (p > av_buffer))
1823         *p = '\0';
1824       if (*p) ++p;
1825
1826       /* colon in returned output? */
1827       if (!(p = Ustrchr(av_buffer,':')))
1828         return m_panic_defer(scanent, CUS callout_address, string_sprintf(
1829                   "ClamAV returned malformed result (missing colon): %s",
1830                   av_buffer));
1831
1832       /* strip filename */
1833       while (*p && isspace(*++p)) /**/;
1834       vname = p;
1835
1836       /* It would be bad to encounter a virus with "FOUND" in part of the name,
1837       but we should at least be resistant to it. */
1838       p = Ustrrchr(vname, ' ');
1839       result_tag = p ? p+1 : vname;
1840
1841       if (Ustrcmp(result_tag, "FOUND") == 0)
1842         {
1843         /* p should still be the whitespace before the result_tag */
1844         while (isspace(*p)) --p;
1845         *++p = '\0';
1846         /* Strip off the extended information too, which will be in parens
1847         after the virus name, with no intervening whitespace. */
1848         if (*--p == ')')
1849           {
1850           /* "(hash:size)", so previous '(' will do; if not found, we have
1851           a curious virus name, but not an error. */
1852           p = Ustrrchr(vname, '(');
1853           if (p)
1854             *p = '\0';
1855           }
1856         malware_name = string_copy(vname);
1857         DEBUG(D_acl) debug_printf_indent("Malware found, name \"%s\"\n", malware_name);
1858
1859         }
1860       else if (Ustrcmp(result_tag, "ERROR") == 0)
1861         return m_panic_defer(scanent, CUS callout_address,
1862           string_sprintf("ClamAV returned: %s", av_buffer));
1863
1864       else if (Ustrcmp(result_tag, "OK") == 0)
1865         {
1866         /* Everything should be OK */
1867         malware_name = NULL;
1868         DEBUG(D_acl) debug_printf_indent("Malware not found\n");
1869
1870         }
1871       else
1872         return m_panic_defer(scanent, CUS callout_address,
1873           string_sprintf("unparseable response from ClamAV: {%s}", av_buffer));
1874
1875       break;
1876       } /* clamd */
1877 #endif
1878
1879 #ifndef DISABLE_MAL_SOCK
1880     case M_SOCK: /* "sock" scanner type ------------------------------------- */
1881     /* This code was derived by Martin Poole from the clamd code contributed
1882        by David Saez and the cmdline code
1883     */
1884       {
1885       int bread;
1886       uschar * commandline;
1887       uschar av_buffer[1024];
1888       uschar * linebuffer;
1889       uschar * sockline_scanner;
1890       uschar sockline_scanner_default[] = "%s\n";
1891       const pcre2_code *sockline_trig_re;
1892       const pcre2_code *sockline_name_re;
1893
1894       /* find scanner command line */
1895       if (  (sockline_scanner = string_nextinlist(&av_scanner_work, &sep,
1896                                           NULL, 0))
1897          && *sockline_scanner
1898          )
1899       { /* check for no expansions apart from one %s */
1900         uschar * s = Ustrchr(sockline_scanner, '%');
1901         if (s++)
1902           if ((*s != 's' && *s != '%') || Ustrchr(s+1, '%'))
1903             return m_panic_defer_3(scanent, NULL,
1904                                   US"unsafe sock scanner call spec", malware_daemon_ctx.sock);
1905       }
1906       else
1907         sockline_scanner = sockline_scanner_default;
1908       DEBUG(D_acl) debug_printf_indent("%15s%10s'%s'\n", "", "cmdline: ",
1909         string_printing(sockline_scanner));
1910
1911       /* find scanner output trigger */
1912       sockline_trig_re = m_pcre_nextinlist(&av_scanner_work, &sep,
1913                                 "missing trigger specification", &errstr);
1914       if (!sockline_trig_re)
1915         return m_panic_defer_3(scanent, NULL, errstr, malware_daemon_ctx.sock);
1916
1917       /* find virus name regex */
1918       sockline_name_re = m_pcre_nextinlist(&av_scanner_work, &sep,
1919                           "missing virus name regex specification", &errstr);
1920       if (!sockline_name_re)
1921         return m_panic_defer_3(scanent, NULL, errstr, malware_daemon_ctx.sock);
1922
1923       /* prepare scanner call - security depends on expansions check above */
1924       commandline = string_sprintf( CS sockline_scanner, CS eml_filename);
1925       DEBUG(D_acl) debug_printf_indent("%15s%10s'%s'\n", "", "expanded: ",
1926         string_printing(commandline));
1927
1928       /* Pass the command string to the socket */
1929       if (m_sock_send(malware_daemon_ctx.sock, commandline, Ustrlen(commandline), &errstr) < 0)
1930         return m_panic_defer(scanent, CUS callout_address, errstr);
1931
1932       /* Read the result */
1933       bread = ip_recv(&malware_daemon_ctx, av_buffer, sizeof(av_buffer), tmo);
1934
1935       if (bread <= 0)
1936         return m_panic_defer_3(scanent, CUS callout_address,
1937           string_sprintf("unable to read from socket (%s)", strerror(errno)),
1938           malware_daemon_ctx.sock);
1939
1940       if (bread == sizeof(av_buffer))
1941         return m_panic_defer_3(scanent, CUS callout_address,
1942                 US"buffer too small", malware_daemon_ctx.sock);
1943       av_buffer[bread] = '\0';
1944       linebuffer = string_copy(av_buffer);
1945       DEBUG(D_acl) debug_printf_indent("%15s%10s'%s'\n", "", "answer: ",
1946         string_printing(linebuffer));
1947
1948       /* try trigger match */
1949       if (regex_match_and_setup(sockline_trig_re, linebuffer, 0, -1))
1950         {
1951         if (!(malware_name = m_pcre_exec(sockline_name_re, av_buffer)))
1952           malware_name = US "unknown";
1953         DEBUG(D_acl) debug_printf_indent("%15s%10s'%s'\n", "", "name: ",
1954           string_printing(malware_name));
1955         }
1956       else /* no virus found */
1957         malware_name = NULL;
1958       break;
1959       }
1960 #endif
1961
1962 #ifndef DISABLE_MAL_MKS
1963     case M_MKSD: /* "mksd" scanner type ------------------------------------- */
1964       {
1965       char *mksd_options_end;
1966       int mksd_maxproc = 1;  /* default, if no option supplied */
1967       int retval;
1968
1969       if (scanner_options)
1970         {
1971         mksd_maxproc = (int)strtol(CS scanner_options, &mksd_options_end, 10);
1972         if (  *scanner_options == '\0'
1973            || *mksd_options_end != '\0'
1974            || mksd_maxproc < 1
1975            || mksd_maxproc > 32
1976            )
1977           return m_panic_defer(scanent, CUS callout_address,
1978             string_sprintf("invalid option '%s'", scanner_options));
1979         }
1980
1981       if((malware_daemon_ctx.sock = ip_unixsocket(US "/var/run/mksd/socket", &errstr)) < 0)
1982         return m_panic_defer(scanent, CUS callout_address, errstr);
1983
1984       malware_name = NULL;
1985
1986       DEBUG(D_acl) debug_printf_indent("Malware scan: issuing %s scan\n", scanner_name);
1987
1988       if ((retval = mksd_scan_packed(scanent, malware_daemon_ctx.sock, eml_filename, tmo)) != OK)
1989         {
1990         close (malware_daemon_ctx.sock);
1991         return retval;
1992         }
1993       break;
1994       }
1995 #endif
1996
1997 #ifndef DISABLE_MAL_AVAST
1998     case M_AVAST: /* "avast" scanner type ----------------------------------- */
1999       {
2000       uschar buf[1024];
2001       uschar * scanrequest;
2002       enum {AVA_HELO, AVA_OPT, AVA_RSP, AVA_DONE} avast_stage;
2003       int nread;
2004       uschar * error_message = NULL;
2005       BOOL more_data = FALSE;
2006       BOOL strict = TRUE;
2007
2008       /* According to Martin Tuma @avast the protocol uses "escaped
2009       whitespace", that is, every embedded whitespace is backslash
2010       escaped, as well as backslash is protected by backslash.
2011       The returned lines contain the name of the scanned file, a tab
2012       and the [ ] marker.
2013       [+] - not infected
2014       [L] - infected
2015       [E] - some error occurred
2016       Such marker follows the first non-escaped TAB.  For more information
2017       see avast-protocol(5)
2018
2019       We observed two cases:
2020       -> SCAN /file
2021       <- /file [E]0.0 Error 13 Permission denied
2022       <- 451 SCAN Engine error 13 permission denied
2023
2024       -> SCAN /file
2025       <- /file… [E]3.0 Error 41120 The file is a decompression bomb
2026       <- /file… [+]2.0
2027       <- /file… [+]2.0 0 Eicar Test Virus!!!
2028       <- 200 SCAN OK
2029
2030       If the scanner returns 4xx, DEFER is a good decision, combined
2031       with a panic log entry, to get the admin's attention.
2032
2033       If the scanner returns 200, we reject it as malware, if found any,
2034       or, in case of an error, we set the malware message to the error
2035       string.
2036
2037       Some of the >= 42000 errors are message related - usually some
2038       broken archives etc, but some of them are e.g. license related.
2039       Once the license expires the engine starts returning errors for
2040       every scanning attempt.  I¹ have the full list of the error codes
2041       but it is not a public API and is subject to change. It is hard
2042       for me to say what you should do in case of an engine error. You
2043       can have a “Treat * unscanned file as infection” policy or “Treat
2044       unscanned file as clean” policy.  ¹) Jakub Bednar
2045
2046        */
2047
2048       if (  (  !ava_re_clean
2049             && !(ava_re_clean = m_pcre_compile(ava_re_clean_str, &errstr)))
2050          || (  !ava_re_virus
2051             && !(ava_re_virus = m_pcre_compile(ava_re_virus_str, &errstr)))
2052          || (  !ava_re_error
2053             && !(ava_re_error = m_pcre_compile(ava_re_error_str, &errstr)))
2054          )
2055         return malware_panic_defer(errstr);
2056
2057       /* wait for result */
2058       for (avast_stage = AVA_HELO;
2059            (nread = recv_line(malware_daemon_ctx.sock, buf, sizeof(buf), tmo)) > 0;
2060           )
2061         {
2062         int slen = Ustrlen(buf);
2063         if (slen >= 1)
2064           {
2065
2066           /* Multi line responses are bracketed between 210 … and nnn … */
2067           if (Ustrncmp(buf, "210", 3) == 0)
2068             {
2069             more_data = 1;
2070             continue;
2071             }
2072           else if (more_data && isdigit(buf[0])) more_data = 0;
2073
2074           switch (avast_stage)
2075             {
2076             case AVA_HELO:
2077               if (more_data) continue;
2078               if (Ustrncmp(buf, "220", 3) != 0)
2079                 goto endloop;                   /* require a 220 */
2080               goto sendreq;
2081
2082             case AVA_OPT:
2083               if (more_data) continue;
2084               if (Ustrncmp(buf, "200", 3) != 0)
2085                 goto endloop;                   /* require a 200 */
2086
2087             sendreq:
2088               {
2089               int len;
2090               /* Check for another option to send. Newline-terminate it. */
2091               if ((scanrequest = string_nextinlist(&av_scanner_work, &sep,
2092                                 NULL, 0)))
2093                 {
2094                 if (Ustrcmp(scanrequest, "pass_unscanned") == 0)
2095                   {
2096                   DEBUG(D_acl) debug_printf_indent("pass unscanned files as clean\n");
2097                   strict = FALSE;
2098                   goto sendreq;
2099                   }
2100                 scanrequest = string_sprintf("%s\n", scanrequest);
2101                 avast_stage = AVA_OPT;          /* just sent option */
2102                 DEBUG(D_acl) debug_printf_indent("send to avast OPTION: %s", scanrequest);
2103                 }
2104               else
2105                 {
2106                 scanrequest = string_sprintf("SCAN %s\n", eml_dir);
2107                 avast_stage = AVA_RSP;          /* just sent command */
2108                 DEBUG(D_acl) debug_printf_indent("send to avast REQUEST: SCAN %s\n", eml_dir);
2109                 }
2110
2111               /* send config-cmd or scan-request to socket */
2112               len = Ustrlen(scanrequest);
2113               if (send(malware_daemon_ctx.sock, scanrequest, len, 0) == -1)
2114                 {
2115                 scanrequest[len-1] = '\0';
2116                 return m_panic_defer_3(scanent, CUS callout_address, string_sprintf(
2117                       "unable to send request '%s' to socket (%s): %s",
2118                       scanrequest, scanner_options, strerror(errno)), malware_daemon_ctx.sock);
2119                 }
2120               break;
2121               }
2122
2123             case AVA_RSP:
2124
2125               if (isdigit(buf[0]))  /* We're done */
2126                 goto endloop;
2127
2128               if (malware_name)     /* Nothing else matters, just read on */
2129                 break;
2130
2131               if (regex_match(ava_re_clean, buf, slen, NULL))
2132                 break;
2133
2134               if ((malware_name = m_pcre_exec(ava_re_virus, buf)))
2135                 {
2136                 unescape(malware_name);
2137                 DEBUG(D_acl)
2138                   debug_printf_indent("unescaped malware name: '%s'\n", malware_name);
2139                 break;
2140                 }
2141
2142               if (strict)           /* treat scanner errors as malware */
2143                 {
2144                 if ((malware_name = m_pcre_exec(ava_re_error, buf)))
2145                   {
2146                   unescape(malware_name);
2147                   DEBUG(D_acl)
2148                     debug_printf_indent("unescaped error message: '%s'\n", malware_name);
2149                   break;
2150                   }
2151                 }
2152               else if (regex_match(ava_re_error, buf, slen, NULL))
2153                 {
2154                 log_write(0, LOG_MAIN, "internal scanner error (ignored): %s", buf);
2155                 break;
2156                 }
2157
2158               /* here also for any unexpected response from the scanner */
2159               DEBUG(D_acl) debug_printf("avast response not handled: '%s'\n", buf);
2160
2161               goto endloop;
2162
2163             default:    log_write(0, LOG_PANIC, "%s:%d:%s: should not happen",
2164                             __FILE__, __LINE__, __FUNCTION__);
2165             }
2166           }
2167         }
2168
2169       endloop:
2170
2171       if (nread == -1) error_message = US"EOF from scanner";
2172       else if (nread < 0) error_message = US"timeout from scanner";
2173       else if (nread == 0) error_message = US"got nothing from scanner";
2174       else if (buf[0] != '2') error_message = buf;
2175
2176       DEBUG(D_acl) debug_printf_indent("sent to avast QUIT\n");
2177       if (send(malware_daemon_ctx.sock, "QUIT\n", 5, 0) == -1)
2178         return m_panic_defer_3(scanent, CUS callout_address,
2179           string_sprintf("unable to send quit request to socket (%s): %s",
2180             scanner_options, strerror(errno)), malware_daemon_ctx.sock);
2181
2182       if (error_message)
2183         return m_panic_defer_3(scanent, CUS callout_address, error_message, malware_daemon_ctx.sock);
2184
2185       }
2186 #endif
2187   }     /* scanner type switch */
2188
2189   if (malware_daemon_ctx.sock >= 0)
2190     (void) close (malware_daemon_ctx.sock);
2191   malware_ok = TRUE;                    /* set "been here, done that" marker */
2192   }
2193
2194 /* match virus name against pattern (caseless ------->----------v) */
2195 if (malware_name && regex_match_and_setup(re, malware_name, 0, -1))
2196   {
2197   DEBUG(D_acl) debug_printf_indent(
2198       "Matched regex to malware [%s] [%s]\n", malware_re, malware_name);
2199   return OK;
2200   }
2201 else
2202   return FAIL;
2203 }
2204
2205
2206 /*************************************************
2207 *          Scan an email for malware             *
2208 *************************************************/
2209
2210 /* This is the normal interface for scanning an email, which doesn't need a
2211 filename; it's a wrapper around the malware_file function.
2212
2213 Arguments:
2214   malware_re  match condition for "malware="
2215   timeout     if nonzero, timeout in seconds
2216
2217 Returns:      Exim message processing code (OK, FAIL, DEFER, ...)
2218               where true means malware was found (condition applies)
2219 */
2220 int
2221 malware(const uschar * malware_re, int timeout)
2222 {
2223 int ret = malware_internal(malware_re, NULL, timeout);
2224
2225 if (ret == DEFER) av_failed = TRUE;
2226 return ret;
2227 }
2228
2229
2230 /*************************************************
2231 *          Scan a file for malware               *
2232 *************************************************/
2233
2234 /* This is a test wrapper for scanning an email, which is not used in
2235 normal processing.  Scan any file, using the Exim scanning interface.
2236 This function tampers with various global variables so is unsafe to use
2237 in any other context.
2238
2239 Arguments:
2240   eml_filename  a file holding the message to be scanned
2241
2242 Returns:        Exim message processing code (OK, FAIL, DEFER, ...)
2243                 where true means malware was found (condition applies)
2244 */
2245 int
2246 malware_in_file(uschar *eml_filename)
2247 {
2248 uschar message_id_buf[64];
2249 int ret;
2250
2251 /* spool_mbox() assumes various parameters exist, when creating
2252 the relevant directory and the email within */
2253
2254 (void) string_format(message_id_buf, sizeof(message_id_buf),
2255     "dummy-%d", vaguely_random_number(INT_MAX));
2256 message_id = message_id_buf;
2257 sender_address = US"malware-sender@example.net";
2258 return_path = US"";
2259 recipients_list = NULL;
2260 receive_add_recipient(US"malware-victim@example.net", -1);
2261 f.enable_dollar_recipients = TRUE;
2262
2263 ret = malware_internal(US"*", eml_filename, 0);
2264
2265 Ustrncpy(spooled_message_id, message_id, sizeof(spooled_message_id));
2266 spool_mbox_ok = 1;
2267
2268 /* don't set no_mbox_unspool; at present, there's no way for it to become
2269 set, but if that changes, then it should apply to these tests too */
2270
2271 unspool_mbox();
2272
2273 /* silence static analysis tools */
2274 message_id = NULL;
2275
2276 return ret;
2277 }
2278
2279
2280 void
2281 malware_init(void)
2282 {
2283 if (!malware_default_re)
2284   malware_default_re = regex_must_compile(malware_regex_default, FALSE, TRUE);
2285
2286 #ifndef DISABLE_MAL_DRWEB
2287 if (!drweb_re)
2288   drweb_re = regex_must_compile(drweb_re_str, FALSE, TRUE);
2289 #endif
2290 #ifndef DISABLE_MAL_FSECURE
2291 if (!fsec_re)
2292   fsec_re = regex_must_compile(fsec_re_str, FALSE, TRUE);
2293 #endif
2294 #ifndef DISABLE_MAL_KAV
2295 if (!kav_re_sus)
2296   kav_re_sus = regex_must_compile(kav_re_sus_str, FALSE, TRUE);
2297 if (!kav_re_inf)
2298   kav_re_inf = regex_must_compile(kav_re_inf_str, FALSE, TRUE);
2299 #endif
2300 #ifndef DISABLE_MAL_AVAST
2301 if (!ava_re_clean)
2302   ava_re_clean = regex_must_compile(ava_re_clean_str, FALSE, TRUE);
2303 if (!ava_re_virus)
2304   ava_re_virus = regex_must_compile(ava_re_virus_str, FALSE, TRUE);
2305 if (!ava_re_error)
2306   ava_re_error = regex_must_compile(ava_re_error_str, FALSE, TRUE);
2307 #endif
2308 #ifndef DISABLE_MAL_FFROT6D
2309 if (!fprot6d_re_error)
2310   fprot6d_re_error = regex_must_compile(fprot6d_re_error_str, FALSE, TRUE);
2311 if (!fprot6d_re_virus)
2312   fprot6d_re_virus = regex_must_compile(fprot6d_re_virus_str, FALSE, TRUE);
2313 #endif
2314 }
2315
2316
2317 gstring *
2318 malware_show_supported(gstring * g)
2319 {
2320 g = string_cat(g, US"Malware:");
2321 for (struct scan * sc = m_scans; sc->scancode != (scanner_t)-1; sc++)
2322   g = string_fmt_append(g, " %s", sc->name);
2323 return string_cat(g, US"\n");
2324 }
2325
2326
2327 # endif /*!MACRO_PREDEF*/
2328 #endif /*WITH_CONTENT_SCAN*/
2329 /*
2330  * vi: aw ai sw=2
2331  */