Copyright updates:
[exim.git] / src / src / malware.c
1 /*************************************************
2 *     Exim - an Internet mail transport agent    *
3 *************************************************/
4
5 /*
6  * Copyright (c) The Exim Maintainers 2015 - 2022
7  * Copyright (c) Tom Kistner <tom@duncanthrax.net> 2003 - 2015
8  * License: GPL
9  */
10
11 /* Code for calling virus (malware) scanners. Called from acl.c. */
12
13 #include "exim.h"
14 #ifdef WITH_CONTENT_SCAN        /* entire file */
15
16 typedef enum {
17 #ifndef DISABLE_MAL_FFROTD
18         M_FPROTD,
19 #endif
20 #ifndef DISABLE_MAL_FFROT6D
21         M_FPROT6D,
22 #endif
23 #ifndef DISABLE_MAL_DRWEB
24         M_DRWEB,
25 #endif
26 #ifndef DISABLE_MAL_AVE
27         M_AVES,
28 #endif
29 #ifndef DISABLE_MAL_FSECURE
30         M_FSEC,
31 #endif
32 #ifndef DISABLE_MAL_KAV
33         M_KAVD,
34 #endif
35 #ifndef DISABLE_MAL_SOPHIE
36         M_SOPHIE,
37 #endif
38 #ifndef DISABLE_MAL_CLAM
39         M_CLAMD,
40 #endif
41 #ifndef DISABLE_MAL_MKS
42         M_MKSD,
43 #endif
44 #ifndef DISABLE_MAL_AVAST
45         M_AVAST,
46 #endif
47 #ifndef DISABLE_MAL_SOCK
48         M_SOCK,
49 #endif
50 #ifndef DISABLE_MAL_CMDLINE
51         M_CMDL,
52 #endif
53         M_DUMMY
54         } scanner_t;
55 typedef enum {MC_NONE, MC_TCP, MC_UNIX, MC_STRM} contype_t;
56 static struct scan
57 {
58   scanner_t     scancode;
59   const uschar * name;
60   const uschar * options_default;
61   contype_t     conn;
62 } m_scans[] =
63 {
64 #ifndef DISABLE_MAL_FFROTD
65   { M_FPROTD,   US"f-protd",    US"localhost 10200-10204",            MC_TCP },
66 #endif
67 #ifndef DISABLE_MAL_FFROT6D
68   { M_FPROT6D,  US"f-prot6d",   US"localhost 10200",                  MC_TCP },
69 #endif
70 #ifndef DISABLE_MAL_DRWEB
71   { M_DRWEB,    US"drweb",      US"/usr/local/drweb/run/drwebd.sock", MC_STRM },
72 #endif
73 #ifndef DISABLE_MAL_AVE
74   { M_AVES,     US"aveserver",  US"/var/run/aveserver",               MC_UNIX },
75 #endif
76 #ifndef DISABLE_MAL_FSECURE
77   { M_FSEC,     US"fsecure",    US"/var/run/.fsav",                   MC_UNIX },
78 #endif
79 #ifndef DISABLE_MAL_KAV
80   { M_KAVD,     US"kavdaemon",  US"/var/run/AvpCtl",                  MC_UNIX },
81 #endif
82 #ifndef DISABLE_MAL_SOPHIE
83   { M_SOPHIE,   US"sophie",     US"/var/run/sophie",                  MC_UNIX },
84 #endif
85 #ifndef DISABLE_MAL_CLAM
86   { M_CLAMD,    US"clamd",      US"/tmp/clamd",                       MC_NONE },
87 #endif
88 #ifndef DISABLE_MAL_MKS
89   { M_MKSD,     US"mksd",       NULL,                                 MC_NONE },
90 #endif
91 #ifndef DISABLE_MAL_AVAST
92   { M_AVAST,    US"avast",      US"/var/run/avast/scan.sock",         MC_STRM },
93 #endif
94 #ifndef DISABLE_MAL_SOCK
95   { M_SOCK,     US"sock",       US"/tmp/malware.sock",                MC_STRM },
96 #endif
97 #ifndef DISABLE_MAL_CMDLINE
98   { M_CMDL,     US"cmdline",    NULL,                                 MC_NONE },
99 #endif
100   { -1,         NULL,           NULL, MC_NONE }         /* end-marker */
101 };
102
103 /******************************************************************************/
104 # ifdef MACRO_PREDEF            /* build solely to predefine macros */
105
106 #  include "macro_predef.h"
107
108 void
109 features_malware(void)
110 {
111 const uschar * s;
112 uschar * t;
113 uschar buf[EXIM_DRIVERNAME_MAX];
114
115 spf(buf, sizeof(buf), US"_HAVE_MALWARE_");
116
117 for (const struct scan * sc = m_scans; sc->scancode != -1; sc++)
118   {
119   for (s = sc->name, t = buf+14; *s; s++) if (*s != '-')
120     *t++ = toupper(*s);
121   *t = '\0';
122   builtin_macro_create(buf);
123   }
124 }
125
126 /******************************************************************************/
127 # else  /*!MACRO_PREDEF, main build*/
128
129
130 #define MALWARE_TIMEOUT 120     /* default timeout, seconds */
131
132 static const uschar * malware_regex_default = US ".+";
133 static const pcre2_code * malware_default_re = NULL;
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 pcre2_code * 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 pcre2_code * 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 pcre2_code * kav_re_sus = NULL;
172 static const pcre2_code * 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 pcre2_code * ava_re_clean = NULL;
180 static const pcre2_code * ava_re_virus = NULL;
181 static const pcre2_code * 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 pcre2_code * fprot6d_re_error = NULL;
188 static const pcre2_code * 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   (void) poll_one_fd(fd, POLLOUT, 5 * 1000);
282 #endif
283 return fd;
284 }
285 #endif
286
287 static int
288 m_sock_send(int sock, uschar * buf, int cnt, uschar ** errstr)
289 {
290 if (send(sock, buf, cnt, 0) < 0)
291   {
292   int err = errno;
293   (void)close(sock);
294   *errstr = string_sprintf("unable to send to socket (%s): %s",
295          buf, strerror(err));
296   return -1;
297   }
298 return sock;
299 }
300
301 static const pcre2_code *
302 m_pcre_compile(const uschar * re, uschar ** errstr)
303 {
304 int err;
305 PCRE2_SIZE roffset;
306 const pcre2_code * cre;
307
308 if (!(cre = pcre2_compile((PCRE2_SPTR)re, PCRE2_ZERO_TERMINATED,
309               PCRE_COPT, &err, &roffset, pcre_cmp_ctx)))
310   {
311   uschar errbuf[128];
312   pcre2_get_error_message(err, errbuf, sizeof(errbuf));
313   *errstr= string_sprintf("regular expression error in '%s': %s at offset %ld",
314       re, errbuf, (long)roffset);
315   }
316 return cre;
317 }
318
319 uschar *
320 m_pcre_exec(const pcre2_code * cre, uschar * text)
321 {
322 pcre2_match_data * md = pcre2_match_data_create(2, pcre_gen_ctx);
323 int i = pcre2_match(cre, text, PCRE2_ZERO_TERMINATED, 0, 0, md, pcre_mtc_ctx);
324 PCRE2_UCHAR * substr = NULL;
325 PCRE2_SIZE slen;
326
327 if (i >= 2)                             /* Got it */
328   pcre2_substring_get_bynumber(md, 1, &substr, &slen);
329 return US substr;
330 }
331
332 static const pcre2_code *
333 m_pcre_nextinlist(const uschar ** list, int * sep,
334  char * listerr, uschar ** errstr)
335 {
336 const uschar * list_ele;
337 const pcre2_code * cre = NULL;
338
339 if (!(list_ele = string_nextinlist(list, sep, NULL, 0)))
340   *errstr = US listerr;
341 else
342   {
343   DEBUG(D_acl) debug_printf_indent("%15s%10s'%s'\n", "", "RE: ",
344     string_printing(list_ele));
345   cre = m_pcre_compile(CUS list_ele, errstr);
346   }
347 return cre;
348 }
349
350
351 /*
352  Simple though inefficient wrapper for reading a line.  Drop CRs and the
353  trailing newline. Can return early on buffer full. Null-terminate.
354  Apply initial timeout if no data ready.
355
356  Return: number of chars - zero for an empty line
357          -1 on EOF
358          -2 on timeout or error
359 */
360 static int
361 recv_line(int fd, uschar * buffer, int bsize, time_t tmo)
362 {
363 uschar * p = buffer;
364 ssize_t rcv;
365 BOOL ok = FALSE;
366
367 if (!fd_ready(fd, tmo))
368   return -2;
369
370 /*XXX tmo handling assumes we always get a whole line */
371 /* read until \n */
372 errno = 0;
373 while ((rcv = read(fd, p, 1)) > 0)
374   {
375   ok = TRUE;
376   if (p-buffer > bsize-2) break;
377   if (*p == '\n') break;
378   if (*p != '\r') p++;
379   }
380 if (!ok)
381   {
382   DEBUG(D_acl)
383     {
384     debug_printf_indent("Malware scan: read %s (%s)\n",
385                 rcv==0 ? "EOF" : "error", strerror(errno));
386     debug_print_socket(fd);
387     }
388   return rcv==0 ? -1 : -2;
389   }
390 *p = '\0';
391
392 DEBUG(D_acl) debug_printf_indent("Malware scan: read '%s'\n", buffer);
393 return p - buffer;
394 }
395
396 /* return TRUE iff size as requested */
397 #ifndef DISABLE_MAL_DRWEB
398 static BOOL
399 recv_len(int sock, void * buf, int size, time_t tmo)
400 {
401 return fd_ready(sock, tmo)
402   ? recv(sock, buf, size, 0) == size
403   : FALSE;
404 }
405 #endif
406
407
408
409 #ifndef DISABLE_MAL_MKS
410 /* ============= private routines for the "mksd" scanner type ============== */
411
412 # include <sys/uio.h>
413
414 static inline int
415 mksd_writev (int sock, struct iovec * iov, int iovcnt)
416 {
417 int i;
418
419 for (;;)
420   {
421   do
422     i = writev (sock, iov, iovcnt);
423   while (i < 0 && errno == EINTR);
424   if (i <= 0)
425     {
426     (void) malware_panic_defer(
427             US"unable to write to mksd UNIX socket (/var/run/mksd/socket)");
428     return -1;
429     }
430   for (;;)      /* check for short write */
431     if (i >= iov->iov_len)
432       {
433       if (--iovcnt == 0)
434         return 0;
435       i -= iov->iov_len;
436       iov++;
437       }
438     else
439       {
440       iov->iov_len -= i;
441       iov->iov_base = CS iov->iov_base + i;
442       break;
443       }
444   }
445 }
446
447 static inline int
448 mksd_read_lines (int sock, uschar *av_buffer, int av_buffer_size, time_t tmo)
449 {
450 client_conn_ctx cctx = {.sock = sock};
451 int offset = 0;
452 int i;
453
454 do
455   {
456   i = ip_recv(&cctx, av_buffer+offset, av_buffer_size-offset, tmo);
457   if (i <= 0)
458     {
459     (void) malware_panic_defer(US"unable to read from mksd UNIX socket (/var/run/mksd/socket)");
460     return -1;
461     }
462
463   offset += i;
464   /* offset == av_buffer_size -> buffer full */
465   if (offset == av_buffer_size)
466     {
467     (void) malware_panic_defer(US"malformed reply received from mksd");
468     return -1;
469     }
470   } while (av_buffer[offset-1] != '\n');
471
472 av_buffer[offset] = '\0';
473 return offset;
474 }
475
476 static inline int
477 mksd_parse_line(struct scan * scanent, char * line)
478 {
479 char *p;
480
481 switch (*line)
482   {
483   case 'O': /* OK */
484     return OK;
485
486   case 'E':
487   case 'A': /* ERR */
488     if ((p = strchr (line, '\n')) != NULL)
489       *p = '\0';
490     return m_panic_defer(scanent, NULL,
491       string_sprintf("scanner failed: %s", line));
492
493   default: /* VIR */
494     if ((p = strchr (line, '\n')) != NULL)
495       {
496       *p = '\0';
497       if (  p-line > 5
498          && line[3] == ' '
499          && (p = strchr(line+4, ' ')) != NULL
500          && p-line > 4
501          )
502         {
503         *p = '\0';
504         malware_name = string_copy(US line+4);
505         return OK;
506         }
507       }
508     return m_panic_defer(scanent, NULL,
509       string_sprintf("malformed reply received: %s", line));
510   }
511 }
512
513 static int
514 mksd_scan_packed(struct scan * scanent, int sock, const uschar * scan_filename,
515   time_t tmo)
516 {
517 struct iovec iov[3];
518 const char *cmd = "MSQ\n";
519 uschar av_buffer[1024];
520
521 iov[0].iov_base = (void *) cmd;
522 iov[0].iov_len = 3;
523 iov[1].iov_base = (void *) scan_filename;
524 iov[1].iov_len = Ustrlen(scan_filename);
525 iov[2].iov_base = (void *) (cmd + 3);
526 iov[2].iov_len = 1;
527
528 if (mksd_writev (sock, iov, 3) < 0)
529   return DEFER;
530
531 if (mksd_read_lines (sock, av_buffer, sizeof (av_buffer), tmo) < 0)
532   return DEFER;
533
534 return mksd_parse_line (scanent, CS av_buffer);
535 }
536 #endif  /* MKSD */
537
538
539 #ifndef DISABLE_MAL_CLAM
540 static int
541 clamd_option(clamd_address * cd, const uschar * optstr, int * subsep)
542 {
543 uschar * s;
544
545 cd->retry = 0;
546 while ((s = string_nextinlist(&optstr, subsep, NULL, 0)))
547   if (Ustrncmp(s, "retry=", 6) == 0)
548     {
549     int sec = readconf_readtime((s += 6), '\0', FALSE);
550     if (sec < 0)
551       return FAIL;
552     cd->retry = sec;
553     }
554   else
555     return FAIL;
556 return OK;
557 }
558 #endif
559
560
561
562 /*************************************************
563 *          Scan content for malware              *
564 *************************************************/
565
566 /* This is an internal interface for scanning an email; the normal interface
567 is via malware(), or there's malware_in_file() used for testing/debugging.
568
569 Arguments:
570   malware_re    match condition for "malware="
571   scan_filename  the file holding the email to be scanned, if we're faking
572                 this up for the -bmalware test, else NULL
573   timeout       if nonzero, non-default timeoutl
574
575 Returns:        Exim message processing code (OK, FAIL, DEFER, ...)
576                 where true means malware was found (condition applies)
577 */
578 static int
579 malware_internal(const uschar * malware_re, const uschar * scan_filename,
580   int timeout)
581 {
582 int sep = 0;
583 const uschar *av_scanner_work = av_scanner;
584 uschar *scanner_name;
585 unsigned long mbox_size;
586 FILE *mbox_file;
587 const pcre2_code *re;
588 uschar * errstr;
589 struct scan * scanent;
590 const uschar * scanner_options;
591 client_conn_ctx malware_daemon_ctx = {.sock = -1};
592 time_t tmo;
593 uschar * eml_filename, * eml_dir;
594
595 if (!malware_re)
596   return FAIL;          /* empty means "don't match anything" */
597
598 /* Ensure the eml mbox file is spooled up */
599
600 if (!(mbox_file = spool_mbox(&mbox_size, scan_filename, &eml_filename)))
601   return malware_panic_defer(US"error while creating mbox spool file");
602
603 /* None of our current scanners need the mbox file as a stream (they use
604 the name), so we can close it right away.  Get the directory too. */
605
606 (void) fclose(mbox_file);
607 eml_dir = string_copyn(eml_filename, Ustrrchr(eml_filename, '/') - eml_filename);
608
609 /* parse 1st option */
610 if (strcmpic(malware_re, US"false") == 0  ||  Ustrcmp(malware_re,"0") == 0)
611   return FAIL;          /* explicitly no matching */
612
613 /* special cases (match anything except empty) */
614 if (  strcmpic(malware_re,US"true") == 0
615    || Ustrcmp(malware_re,"*") == 0
616    || Ustrcmp(malware_re,"1") == 0
617    )
618   {
619   if (  !malware_default_re
620      && !(malware_default_re = m_pcre_compile(malware_regex_default, &errstr)))
621     return malware_panic_defer(errstr);
622   malware_re = malware_regex_default;
623   re = malware_default_re;
624   }
625
626 /* compile the regex, see if it works */
627 else if (!(re = m_pcre_compile(malware_re, &errstr)))
628   return malware_panic_defer(errstr);
629
630 /* if av_scanner starts with a dollar, expand it first */
631 if (*av_scanner == '$')
632   {
633   if (!(av_scanner_work = expand_string(av_scanner)))
634     return malware_panic_defer(
635          string_sprintf("av_scanner starts with $, but expansion failed: %s",
636          expand_string_message));
637
638   DEBUG(D_acl)
639     debug_printf_indent("Expanded av_scanner global: %s\n", av_scanner_work);
640   /* disable result caching in this case */
641   malware_name = NULL;
642   malware_ok = FALSE;
643   }
644
645 /* Do not scan twice (unless av_scanner is dynamic). */
646 if (!malware_ok)
647   {
648   /* find the scanner type from the av_scanner option */
649   if (!(scanner_name = string_nextinlist(&av_scanner_work, &sep, NULL, 0)))
650     return malware_panic_defer(US"av_scanner configuration variable is empty");
651   if (!timeout) timeout = MALWARE_TIMEOUT;
652   tmo = time(NULL) + timeout;
653
654   for (scanent = m_scans; ; scanent++)
655     {
656     if (!scanent->name)
657       return malware_panic_defer(string_sprintf("unknown scanner type '%s'",
658         scanner_name));
659     if (strcmpic(scanner_name, US scanent->name) != 0)
660       continue;
661     DEBUG(D_acl) debug_printf_indent("Malware scan:  %s tmo=%s\n",
662       scanner_name, readconf_printtime(timeout));
663
664     if (!(scanner_options = string_nextinlist(&av_scanner_work, &sep, NULL, 0)))
665       scanner_options = scanent->options_default;
666     if (scanent->conn == MC_NONE)
667       break;
668
669     DEBUG(D_acl) debug_printf_indent("%15s%10s%s\n", "", "socket: ", scanner_options);
670     switch(scanent->conn)
671     {
672     case MC_TCP:
673       malware_daemon_ctx.sock = ip_tcpsocket(scanner_options, &errstr, 5, NULL); break;
674     case MC_UNIX:
675       malware_daemon_ctx.sock = ip_unixsocket(scanner_options, &errstr);        break;
676     case MC_STRM:
677       malware_daemon_ctx.sock = ip_streamsocket(scanner_options, &errstr, 5, NULL); break;
678     default:
679       /* compiler quietening */ break;
680     }
681     if (malware_daemon_ctx.sock < 0)
682       return m_panic_defer(scanent, CUS callout_address, errstr);
683     break;
684   }
685
686   switch (scanent->scancode)
687     {
688 #ifndef DISABLE_MAL_FFROTD
689     case M_FPROTD: /* "f-protd" scanner type -------------------------------- */
690       {
691       uschar *fp_scan_option;
692       unsigned int detected=0, par_count=0;
693       uschar * scanrequest;
694       uschar buf[32768], *strhelper, *strhelper2;
695       uschar * malware_name_internal = NULL;
696       int len;
697
698       scanrequest = string_sprintf("GET %s", eml_filename);
699
700       while ((fp_scan_option = string_nextinlist(&av_scanner_work, &sep,
701                             NULL, 0)))
702         {
703         scanrequest = string_sprintf("%s%s%s", scanrequest,
704                                   par_count ? "%20" : "?", fp_scan_option);
705         par_count++;
706         }
707       scanrequest = string_sprintf("%s HTTP/1.0\r\n\r\n", scanrequest);
708       DEBUG(D_acl) debug_printf_indent("Malware scan: issuing %s: %s\n",
709         scanner_name, scanrequest);
710
711       /* send scan request */
712       if (m_sock_send(malware_daemon_ctx.sock, scanrequest, Ustrlen(scanrequest)+1, &errstr) < 0)
713         return m_panic_defer(scanent, CUS callout_address, errstr);
714
715       while ((len = recv_line(malware_daemon_ctx.sock, buf, sizeof(buf), tmo)) >= 0)
716         if (len > 0)
717           {
718           if (Ustrstr(buf, US"<detected type=\"") != NULL)
719             detected = 1;
720           else if (detected && (strhelper = Ustrstr(buf, US"<name>")))
721             {
722             if ((strhelper2 = Ustrstr(buf, US"</name>")) != NULL)
723               {
724               *strhelper2 = '\0';
725               malware_name_internal = string_copy(strhelper+6);
726               }
727             }
728           else if (Ustrstr(buf, US"<summary code=\""))
729             {
730             malware_name = Ustrstr(buf, US"<summary code=\"11\">")
731                 ? malware_name_internal : NULL;
732             break;
733             }
734           }
735       if (len < -1)
736         {
737         (void)close(malware_daemon_ctx.sock);
738         return DEFER;
739         }
740       break;
741       } /* f-protd */
742 #endif
743
744 #ifndef DISABLE_MAL_FFROT6D
745     case M_FPROT6D: /* "f-prot6d" scanner type ----------------------------------- */
746       {
747       int bread;
748       uschar * e;
749       uschar * linebuffer;
750       uschar * scanrequest;
751       uschar av_buffer[1024];
752
753       if ((!fprot6d_re_virus && !(fprot6d_re_virus = m_pcre_compile(fprot6d_re_virus_str, &errstr)))
754         || (!fprot6d_re_error && !(fprot6d_re_error = m_pcre_compile(fprot6d_re_error_str, &errstr))))
755         return malware_panic_defer(errstr);
756
757       scanrequest = string_sprintf("SCAN FILE %s\n", eml_filename);
758       DEBUG(D_acl) debug_printf_indent("Malware scan: issuing %s: %s\n",
759         scanner_name, scanrequest);
760
761       if (m_sock_send(malware_daemon_ctx.sock, scanrequest, Ustrlen(scanrequest), &errstr) < 0)
762         return m_panic_defer(scanent, CUS callout_address, errstr);
763
764       bread = ip_recv(&malware_daemon_ctx, av_buffer, sizeof(av_buffer), tmo);
765
766       if (bread <= 0)
767         return m_panic_defer_3(scanent, CUS callout_address,
768           string_sprintf("unable to read from socket (%s)", strerror(errno)),
769           malware_daemon_ctx.sock);
770
771       if (bread == sizeof(av_buffer))
772         return m_panic_defer_3(scanent, CUS callout_address,
773           US"buffer too small", malware_daemon_ctx.sock);
774
775       av_buffer[bread] = '\0';
776       linebuffer = string_copy(av_buffer);
777
778       m_sock_send(malware_daemon_ctx.sock, US"QUIT\n", 5, 0);
779
780       if ((e = m_pcre_exec(fprot6d_re_error, linebuffer)))
781         return m_panic_defer_3(scanent, CUS callout_address,
782           string_sprintf("scanner reported error (%s)", e), malware_daemon_ctx.sock);
783
784       if (!(malware_name = m_pcre_exec(fprot6d_re_virus, linebuffer)))
785         malware_name = NULL;
786
787       break;
788       }  /* f-prot6d */
789 #endif
790
791 #ifndef DISABLE_MAL_DRWEB
792     case M_DRWEB: /* "drweb" scanner type ----------------------------------- */
793   /* v0.1 - added support for tcp sockets          */
794   /* v0.0 - initial release -- support for unix sockets      */
795       {
796       int result;
797       off_t fsize;
798       unsigned int fsize_uint;
799       uschar * tmpbuf, *drweb_fbuf;
800       int drweb_rc, drweb_cmd, drweb_flags = 0x0000, drweb_fd,
801           drweb_vnum, drweb_slen, drweb_fin = 0x0000;
802
803       /* prepare variables */
804       drweb_cmd = htonl(DRWEBD_SCAN_CMD);
805       drweb_flags = htonl(DRWEBD_RETURN_VIRUSES | DRWEBD_IS_MAIL);
806
807       if (*scanner_options != '/')
808         {
809         /* calc file size */
810         if ((drweb_fd = exim_open2(CCS eml_filename, O_RDONLY)) == -1)
811           return m_panic_defer_3(scanent, NULL,
812             string_sprintf("can't open spool file %s: %s",
813               eml_filename, strerror(errno)),
814             malware_daemon_ctx.sock);
815
816         if ((fsize = lseek(drweb_fd, 0, SEEK_END)) == -1)
817           {
818           int err;
819 badseek:  err = errno;
820           (void)close(drweb_fd);
821           return m_panic_defer_3(scanent, NULL,
822             string_sprintf("can't seek spool file %s: %s",
823               eml_filename, strerror(err)),
824             malware_daemon_ctx.sock);
825           }
826         fsize_uint = (unsigned int) fsize;
827         if ((off_t)fsize_uint != fsize)
828           {
829           (void)close(drweb_fd);
830           return m_panic_defer_3(scanent, NULL,
831             string_sprintf("seeking spool file %s, size overflow",
832               eml_filename),
833             malware_daemon_ctx.sock);
834           }
835         drweb_slen = htonl(fsize);
836         if (lseek(drweb_fd, 0, SEEK_SET) < 0)
837           goto badseek;
838
839         DEBUG(D_acl) debug_printf_indent("Malware scan: issuing %s remote scan [%s]\n",
840             scanner_name, scanner_options);
841
842         /* send scan request */
843         if ((send(malware_daemon_ctx.sock, &drweb_cmd, sizeof(drweb_cmd), 0) < 0) ||
844             (send(malware_daemon_ctx.sock, &drweb_flags, sizeof(drweb_flags), 0) < 0) ||
845             (send(malware_daemon_ctx.sock, &drweb_fin, sizeof(drweb_fin), 0) < 0) ||
846             (send(malware_daemon_ctx.sock, &drweb_slen, sizeof(drweb_slen), 0) < 0))
847           {
848           (void)close(drweb_fd);
849           return m_panic_defer_3(scanent, CUS callout_address, string_sprintf(
850             "unable to send commands to socket (%s)", scanner_options),
851             malware_daemon_ctx.sock);
852           }
853
854         if (!(drweb_fbuf = store_malloc(fsize_uint)))
855           {
856           (void)close(drweb_fd);
857           return m_panic_defer_3(scanent, NULL,
858             string_sprintf("unable to allocate memory %u for file (%s)",
859               fsize_uint, eml_filename),
860             malware_daemon_ctx.sock);
861           }
862
863         if ((result = read (drweb_fd, drweb_fbuf, fsize)) == -1)
864           {
865           int err = errno;
866           (void)close(drweb_fd);
867           store_free(drweb_fbuf);
868           return m_panic_defer_3(scanent, NULL,
869             string_sprintf("can't read spool file %s: %s",
870               eml_filename, strerror(err)),
871             malware_daemon_ctx.sock);
872           }
873         (void)close(drweb_fd);
874
875         /* send file body to socket */
876         if (send(malware_daemon_ctx.sock, drweb_fbuf, fsize, 0) < 0)
877           {
878           store_free(drweb_fbuf);
879           return m_panic_defer_3(scanent, CUS callout_address, string_sprintf(
880             "unable to send file body to socket (%s)", scanner_options),
881             malware_daemon_ctx.sock);
882           }
883         store_free(drweb_fbuf);
884         }
885       else
886         {
887         drweb_slen = htonl(Ustrlen(eml_filename));
888
889         DEBUG(D_acl) debug_printf_indent("Malware scan: issuing %s local scan [%s]\n",
890             scanner_name, scanner_options);
891
892         /* send scan request */
893         if ((send(malware_daemon_ctx.sock, &drweb_cmd, sizeof(drweb_cmd), 0) < 0) ||
894             (send(malware_daemon_ctx.sock, &drweb_flags, sizeof(drweb_flags), 0) < 0) ||
895             (send(malware_daemon_ctx.sock, &drweb_slen, sizeof(drweb_slen), 0) < 0) ||
896             (send(malware_daemon_ctx.sock, eml_filename, Ustrlen(eml_filename), 0) < 0) ||
897             (send(malware_daemon_ctx.sock, &drweb_fin, sizeof(drweb_fin), 0) < 0))
898           return m_panic_defer_3(scanent, CUS callout_address, string_sprintf(
899             "unable to send commands to socket (%s)", scanner_options),
900             malware_daemon_ctx.sock);
901         }
902
903       /* wait for result */
904       if (!recv_len(malware_daemon_ctx.sock, &drweb_rc, sizeof(drweb_rc), tmo))
905         return m_panic_defer_3(scanent, CUS callout_address,
906                     US"unable to read return code", malware_daemon_ctx.sock);
907       drweb_rc = ntohl(drweb_rc);
908
909       if (!recv_len(malware_daemon_ctx.sock, &drweb_vnum, sizeof(drweb_vnum), tmo))
910         return m_panic_defer_3(scanent, CUS callout_address,
911                             US"unable to read the number of viruses", malware_daemon_ctx.sock);
912       drweb_vnum = ntohl(drweb_vnum);
913
914       /* "virus(es) found" if virus number is > 0 */
915       if (drweb_vnum)
916         {
917         gstring * g = NULL;
918
919         /* setup default virus name */
920         malware_name = US"unknown";
921
922         /* set up match regex */
923         if (!drweb_re)
924           drweb_re = m_pcre_compile(drweb_re_str, &errstr);
925
926         /* read and concatenate virus names into one string */
927         for (int i = 0; i < drweb_vnum; i++)
928           {
929           pcre2_match_data * md = pcre2_match_data_create(2, pcre_gen_ctx);
930
931           /* read the size of report */
932           if (!recv_len(malware_daemon_ctx.sock, &drweb_slen, sizeof(drweb_slen), tmo))
933             return m_panic_defer_3(scanent, CUS callout_address,
934                               US"cannot read report size", malware_daemon_ctx.sock);
935           drweb_slen = ntohl(drweb_slen);
936
937           /* assume tainted, since it is external input */
938           tmpbuf = store_get(drweb_slen, GET_TAINTED);
939
940           /* read report body */
941           if (!recv_len(malware_daemon_ctx.sock, tmpbuf, drweb_slen, tmo))
942             return m_panic_defer_3(scanent, CUS callout_address,
943                               US"cannot read report string", malware_daemon_ctx.sock);
944           tmpbuf[drweb_slen] = '\0';
945
946           /* try matcher on the line, grab substring */
947           result = pcre2_match(drweb_re, (PCRE2_SPTR)tmpbuf, PCRE2_ZERO_TERMINATED,
948                                 0, 0, md, pcre_mtc_ctx);
949           if (result >= 2)
950             {
951             PCRE2_SIZE * ovec = pcre2_get_ovector_pointer(md);
952
953             if (i==0)   /* the first name we just copy to malware_name */
954               g = string_catn(NULL, US ovec[2], ovec[3] - ovec[2]);
955
956             else        /* concatenate each new virus name to previous */
957               {
958               g = string_catn(g, US"/", 1);
959               g = string_catn(g, US ovec[2], ovec[3] - ovec[2]);
960               }
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 pcre2_code *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 pcre2_code *cmdline_trigger_re;
1263       const pcre2_code *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       int clam_fd;
1458       unsigned int fsize_uint;
1459       BOOL use_scan_command = FALSE;
1460       clamd_address * cv[MAX_CLAMD_SERVERS];
1461       int num_servers = 0;
1462       uint32_t send_size, send_final_zeroblock;
1463       blob cmd_str;
1464
1465       /*XXX if unixdomain socket, only one server supported. Needs fixing;
1466       there's no reason we should not mix local and remote servers */
1467
1468       if (*scanner_options == '/')
1469         {
1470         clamd_address * cd;
1471         const uschar * sublist;
1472         int subsep = ' ';
1473
1474         /* Local file; so we def want to use_scan_command and don't want to try
1475         passing IP/port combinations */
1476         use_scan_command = TRUE;
1477         cd = (clamd_address *) store_get(sizeof(clamd_address), GET_UNTAINTED);
1478
1479         /* extract socket-path part */
1480         sublist = scanner_options;
1481         cd->hostspec = string_nextinlist(&sublist, &subsep, NULL, 0);
1482
1483         /* parse options */
1484         if (clamd_option(cd, sublist, &subsep) != OK)
1485           return m_panic_defer(scanent, NULL,
1486             string_sprintf("bad option '%s'", scanner_options));
1487         cv[0] = cd;
1488         }
1489       else
1490         {
1491         /* Go through the rest of the list of host/port and construct an array
1492          * of servers to try. The first one is the bit we just passed from
1493          * scanner_options so process that first and then scan the remainder of
1494          * the address buffer */
1495         do
1496           {
1497           clamd_address * cd;
1498           const uschar * sublist;
1499           int subsep = ' ';
1500           uschar * s;
1501
1502           /* The 'local' option means use the SCAN command over the network
1503            * socket (ie common file storage in use) */
1504           /*XXX we could accept this also as a local option? */
1505           if (strcmpic(scanner_options, US"local") == 0)
1506             {
1507             use_scan_command = TRUE;
1508             continue;
1509             }
1510
1511           cd = (clamd_address *) store_get(sizeof(clamd_address), GET_UNTAINTED);
1512
1513           /* extract host and port part */
1514           sublist = scanner_options;
1515           if (!(cd->hostspec = string_nextinlist(&sublist, &subsep, NULL, 0)))
1516             {
1517             (void) m_panic_defer(scanent, NULL,
1518                       string_sprintf("missing address: '%s'", scanner_options));
1519             continue;
1520             }
1521           if (!(s = string_nextinlist(&sublist, &subsep, NULL, 0)))
1522             {
1523             (void) m_panic_defer(scanent, NULL,
1524                       string_sprintf("missing port: '%s'", scanner_options));
1525             continue;
1526             }
1527           cd->tcp_port = atoi(CS s);
1528
1529           /* parse options */
1530           /*XXX should these options be common over scanner types? */
1531           if (clamd_option(cd, sublist, &subsep) != OK)
1532             return m_panic_defer(scanent, NULL,
1533               string_sprintf("bad option '%s'", scanner_options));
1534
1535           cv[num_servers++] = cd;
1536           if (num_servers >= MAX_CLAMD_SERVERS)
1537             {
1538             (void) m_panic_defer(scanent, NULL,
1539                   US"More than " MAX_CLAMD_SERVERS_S " clamd servers "
1540                   "specified; only using the first " MAX_CLAMD_SERVERS_S );
1541             break;
1542             }
1543           } while ((scanner_options = string_nextinlist(&av_scanner_work, &sep,
1544                                         NULL, 0)));
1545
1546         /* check if we have at least one server */
1547         if (!num_servers)
1548           return m_panic_defer(scanent, NULL,
1549             US"no useable server addresses in malware configuration option.");
1550         }
1551
1552       /* See the discussion of response formats below to see why we really
1553       don't like colons in filenames when passing filenames to ClamAV. */
1554       if (use_scan_command && Ustrchr(eml_filename, ':'))
1555         return m_panic_defer(scanent, NULL,
1556           string_sprintf("local/SCAN mode incompatible with" \
1557             " : in path to email filename [%s]", eml_filename));
1558
1559       /* Set up the very first data we will be sending */
1560       if (!use_scan_command)
1561         { cmd_str.data = US"zINSTREAM"; cmd_str.len = 10; }
1562       else
1563         {
1564         int n;
1565         cmd_str.data = string_sprintf("SCAN %s\n%n", eml_filename, &n);
1566         cmd_str.len = n;                /* .len is a size_t */
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 idempotent */
1589             if ((malware_daemon_ctx.sock = m_tcpsocket(cd->hostspec, cd->tcp_port,
1590                                     &connhost, &errstr,
1591                                     use_scan_command ? &cmd_str : NULL)) >= 0)
1592               {
1593               /* Connection successfully established with a server */
1594               hostname = cd->hostspec;
1595               if (use_scan_command) cmd_str.len = 0;
1596               break;
1597               }
1598             if (cd->retry <= 0) break;
1599             while (cd->retry > 0) cd->retry = sleep(cd->retry);
1600             }
1601           if (malware_daemon_ctx.sock >= 0)
1602             break;
1603
1604           (void) m_panic_defer(scanent, CUS callout_address, errstr);
1605
1606           /* Remove the server from the list. XXX We should free the memory */
1607           num_servers--;
1608           for (; i < num_servers; i++)
1609             cv[i] = cv[i+1];
1610           }
1611
1612         if (num_servers == 0)
1613           return m_panic_defer(scanent, NULL, US"all servers failed");
1614         }
1615       else
1616         for (;;)
1617           {
1618           if ((malware_daemon_ctx.sock = ip_unixsocket(cv[0]->hostspec, &errstr)) >= 0)
1619             {
1620             hostname = cv[0]->hostspec;
1621             break;
1622             }
1623           if (cv[0]->retry <= 0)
1624             return m_panic_defer(scanent, CUS callout_address, errstr);
1625           while (cv[0]->retry > 0) cv[0]->retry = sleep(cv[0]->retry);
1626           }
1627
1628       /* have socket in variable "sock"; command to use is semi-independent of
1629       the socket protocol.  We use SCAN if is local (either Unix/local
1630       domain socket, or explicitly told local) else we stream the data.
1631       How we stream the data depends upon how we were built.  */
1632
1633       if (!use_scan_command)
1634         {
1635         struct stat st;
1636 #if defined(EXIM_TCP_CORK) && !defined(OS_SENDFILE)
1637         BOOL corked = TRUE;
1638 #endif
1639         /* New protocol: "zINSTREAM\n" followed by a sequence of <length><data>
1640         chunks, <n> a 4-byte number (network order), terminated by a zero-length
1641         chunk. We only send one chunk. */
1642
1643         DEBUG(D_acl) debug_printf_indent(
1644             "Malware scan: issuing %s new-style remote scan (zINSTREAM)\n",
1645             scanner_name);
1646
1647 #if defined(EXIM_TCP_CORK)
1648         (void) setsockopt(malware_daemon_ctx.sock, IPPROTO_TCP, EXIM_TCP_CORK,
1649                           US &on, sizeof(on));
1650 #endif
1651         /* Pass the string to ClamAV (10 = "zINSTREAM\0"), if not already sent */
1652         if (cmd_str.len)
1653           if (send(malware_daemon_ctx.sock, cmd_str.data, cmd_str.len, 0) < 0)
1654             return m_panic_defer_3(scanent, CUS hostname,
1655               string_sprintf("unable to send zINSTREAM to socket (%s)",
1656                 strerror(errno)),
1657               malware_daemon_ctx.sock);
1658
1659         if ((clam_fd = exim_open2(CS eml_filename, O_RDONLY)) < 0)
1660           {
1661           int err = errno;
1662           return m_panic_defer_3(scanent, NULL,
1663             string_sprintf("can't open spool file %s: %s",
1664               eml_filename, strerror(err)),
1665             malware_daemon_ctx.sock);
1666           }
1667         if (fstat(clam_fd, &st) < 0)
1668           {
1669           int err = errno;
1670           (void)close(clam_fd);
1671           return m_panic_defer_3(scanent, NULL,
1672             string_sprintf("can't stat spool file %s: %s",
1673               eml_filename, strerror(err)),
1674             malware_daemon_ctx.sock);
1675           }
1676         fsize_uint = (unsigned int) st.st_size;
1677         if ((off_t)fsize_uint != st.st_size)
1678           {
1679           (void)close(clam_fd);
1680           return m_panic_defer_3(scanent, NULL,
1681             string_sprintf("stat spool file %s, size overflow", eml_filename),
1682             malware_daemon_ctx.sock);
1683           }
1684
1685         /* send file size */
1686         send_size = htonl(fsize_uint);
1687         if (send(malware_daemon_ctx.sock, &send_size, sizeof(send_size), 0) < 0)
1688           return m_panic_defer_3(scanent, NULL,
1689             string_sprintf("unable to send file size to socket (%s)", hostname),
1690             malware_daemon_ctx.sock);
1691
1692         /* send file body */
1693         while (fsize_uint)
1694           {
1695 #ifdef OS_SENDFILE
1696           int n = os_sendfile(malware_daemon_ctx.sock, clam_fd, NULL, (size_t)fsize_uint);
1697           if (n < 0)
1698             return m_panic_defer_3(scanent, NULL,
1699               string_sprintf("unable to send file body to socket (%s): %s", hostname, strerror(errno)),
1700               malware_daemon_ctx.sock);
1701           fsize_uint -= n;
1702 #else
1703           int n = MIN(fsize_uint, big_buffer_size);
1704           if ((n = read(clam_fd, big_buffer, n)) < 0)
1705             return m_panic_defer_3(scanent, NULL,
1706               string_sprintf("can't read spool file %s: %s",
1707                 eml_filename, strerror(errno)),
1708               malware_daemon_ctx.sock);
1709           if (send(malware_daemon_ctx.sock, big_buffer, (size_t)n, 0) < 0)
1710             return m_panic_defer_3(scanent, NULL,
1711               string_sprintf("unable to send file body to socket (%s): %s", hostname, strerror(errno)),
1712               malware_daemon_ctx.sock);
1713           fsize_uint -= n;
1714 # ifdef EXIM_TCP_CORK
1715           if (corked)
1716             {
1717             corked = FALSE;
1718             (void) setsockopt(malware_daemon_ctx.sock, IPPROTO_TCP, EXIM_TCP_CORK,
1719                               US &off, sizeof(off));
1720             }
1721 # endif
1722 #endif  /*!OS_SENDFILE*/
1723
1724           }
1725
1726         send_final_zeroblock = 0;
1727         if (send(malware_daemon_ctx.sock, &send_final_zeroblock, sizeof(send_final_zeroblock), 0) < 0)
1728           return m_panic_defer_3(scanent, NULL,
1729             string_sprintf("unable to send file terminator to socket (%s)", hostname),
1730             malware_daemon_ctx.sock);
1731 #ifdef OS_SENDFILE
1732         (void) setsockopt(malware_daemon_ctx.sock, IPPROTO_TCP, EXIM_TCP_CORK,
1733                           US &off, sizeof(off));
1734 #endif
1735         }
1736       else
1737         { /* use scan command */
1738         /* Send a SCAN command pointing to a filename; then in the then in the
1739         scan-method-neutral part, read the response back */
1740
1741 /* ================================================================= */
1742
1743         /* Prior to the reworking post-Exim-4.72, this scanned a directory,
1744         which dates to when ClamAV needed us to break apart the email into the
1745         MIME parts (eg, with the now deprecated demime condition coming first).
1746         Some time back, ClamAV gained the ability to deconstruct the emails, so
1747         doing this would actually have resulted in the mail attachments being
1748         scanned twice, in the broken out files and from the original .eml.
1749         Since ClamAV now handles emails (and has for quite some time) we can
1750         just use the email file itself. */
1751         /* Pass the string to ClamAV (7 = "SCAN \n" + \0), if not already sent */
1752
1753         DEBUG(D_acl) debug_printf_indent(
1754             "Malware scan: issuing %s local-path scan [%s]\n",
1755             scanner_name, scanner_options);
1756
1757         if (cmd_str.len)
1758           if (send(malware_daemon_ctx.sock, cmd_str.data, cmd_str.len, 0) < 0)
1759             return m_panic_defer_3(scanent, CUS callout_address,
1760               string_sprintf("unable to write to socket (%s)", strerror(errno)),
1761               malware_daemon_ctx.sock);
1762
1763         /* Do not shut down the socket for writing; a user report noted that
1764         clamd 0.70 does not react well to this. */
1765         }
1766       /* Commands have been sent, no matter which scan method or connection
1767       type we're using; now just read the result, independent of method. */
1768
1769       /* Read the result */
1770       memset(av_buffer, 0, sizeof(av_buffer));
1771       bread = ip_recv(&malware_daemon_ctx, av_buffer, sizeof(av_buffer), tmo);
1772       (void)close(malware_daemon_ctx.sock);
1773       malware_daemon_ctx.sock = -1;
1774       malware_daemon_ctx.tls_ctx = NULL;
1775
1776       if (bread <= 0)
1777         return m_panic_defer(scanent, CUS callout_address,
1778           string_sprintf("unable to read from socket (%s)",
1779           errno == 0 ? "EOF" : strerror(errno)));
1780
1781       if (bread == sizeof(av_buffer))
1782         return m_panic_defer(scanent, CUS callout_address,
1783                 US"buffer too small");
1784       /* We're now assured of a NULL at the end of av_buffer */
1785
1786       /* Check the result. ClamAV returns one of two result formats.
1787       In the basic mode, the response is of the form:
1788         infected: -> "<filename>: <virusname> FOUND"
1789         not-infected: -> "<filename>: OK"
1790         error: -> "<filename>: <errcode> ERROR
1791       If the ExtendedDetectionInfo option has been turned on, then we get:
1792         "<filename>: <virusname>(<virushash>:<virussize>) FOUND"
1793       for the infected case.  Compare:
1794 /tmp/eicar.com: Eicar-Test-Signature FOUND
1795 /tmp/eicar.com: Eicar-Test-Signature(44d88612fea8a8f36de82e1278abb02f:68) FOUND
1796
1797       In the streaming case, clamd uses the filename "stream" which you should
1798       be able to verify with { ktrace clamdscan --stream /tmp/eicar.com }.  (The
1799       client app will replace "stream" with the original filename before returning
1800       results to stdout, but the trace shows the data).
1801
1802       We will assume that the pathname passed to clamd from Exim does not contain
1803       a colon.  We will have whined loudly above if the eml_filename does (and we're
1804       passing a filename to clamd). */
1805
1806       if (!(*av_buffer))
1807         return m_panic_defer(scanent, CUS callout_address,
1808                 US"ClamAV returned null");
1809
1810       /* strip newline at the end (won't be present for zINSTREAM)
1811       (also any trailing whitespace, which shouldn't exist, but we depend upon
1812       this below, so double-check) */
1813
1814       p = av_buffer + Ustrlen(av_buffer) - 1;
1815       if (*p == '\n') *p = '\0';
1816
1817       DEBUG(D_acl) debug_printf_indent("Malware response: %s\n", av_buffer);
1818
1819       while (isspace(*--p) && (p > av_buffer))
1820         *p = '\0';
1821       if (*p) ++p;
1822
1823       /* colon in returned output? */
1824       if (!(p = Ustrchr(av_buffer,':')))
1825         return m_panic_defer(scanent, CUS callout_address, string_sprintf(
1826                   "ClamAV returned malformed result (missing colon): %s",
1827                   av_buffer));
1828
1829       /* strip filename */
1830       while (*p && isspace(*++p)) /**/;
1831       vname = p;
1832
1833       /* It would be bad to encounter a virus with "FOUND" in part of the name,
1834       but we should at least be resistant to it. */
1835       p = Ustrrchr(vname, ' ');
1836       result_tag = p ? p+1 : vname;
1837
1838       if (Ustrcmp(result_tag, "FOUND") == 0)
1839         {
1840         /* p should still be the whitespace before the result_tag */
1841         while (isspace(*p)) --p;
1842         *++p = '\0';
1843         /* Strip off the extended information too, which will be in parens
1844         after the virus name, with no intervening whitespace. */
1845         if (*--p == ')')
1846           {
1847           /* "(hash:size)", so previous '(' will do; if not found, we have
1848           a curious virus name, but not an error. */
1849           p = Ustrrchr(vname, '(');
1850           if (p)
1851             *p = '\0';
1852           }
1853         malware_name = string_copy(vname);
1854         DEBUG(D_acl) debug_printf_indent("Malware found, name \"%s\"\n", malware_name);
1855
1856         }
1857       else if (Ustrcmp(result_tag, "ERROR") == 0)
1858         return m_panic_defer(scanent, CUS callout_address,
1859           string_sprintf("ClamAV returned: %s", av_buffer));
1860
1861       else if (Ustrcmp(result_tag, "OK") == 0)
1862         {
1863         /* Everything should be OK */
1864         malware_name = NULL;
1865         DEBUG(D_acl) debug_printf_indent("Malware not found\n");
1866
1867         }
1868       else
1869         return m_panic_defer(scanent, CUS callout_address,
1870           string_sprintf("unparseable response from ClamAV: {%s}", av_buffer));
1871
1872       break;
1873       } /* clamd */
1874 #endif
1875
1876 #ifndef DISABLE_MAL_SOCK
1877     case M_SOCK: /* "sock" scanner type ------------------------------------- */
1878     /* This code was derived by Martin Poole from the clamd code contributed
1879        by David Saez and the cmdline code
1880     */
1881       {
1882       int bread;
1883       uschar * commandline;
1884       uschar av_buffer[1024];
1885       uschar * linebuffer;
1886       uschar * sockline_scanner;
1887       uschar sockline_scanner_default[] = "%s\n";
1888       const pcre2_code *sockline_trig_re;
1889       const pcre2_code *sockline_name_re;
1890
1891       /* find scanner command line */
1892       if (  (sockline_scanner = string_nextinlist(&av_scanner_work, &sep,
1893                                           NULL, 0))
1894          && *sockline_scanner
1895          )
1896       { /* check for no expansions apart from one %s */
1897         uschar * s = Ustrchr(sockline_scanner, '%');
1898         if (s++)
1899           if ((*s != 's' && *s != '%') || Ustrchr(s+1, '%'))
1900             return m_panic_defer_3(scanent, NULL,
1901                                   US"unsafe sock scanner call spec", malware_daemon_ctx.sock);
1902       }
1903       else
1904         sockline_scanner = sockline_scanner_default;
1905       DEBUG(D_acl) debug_printf_indent("%15s%10s'%s'\n", "", "cmdline: ",
1906         string_printing(sockline_scanner));
1907
1908       /* find scanner output trigger */
1909       sockline_trig_re = m_pcre_nextinlist(&av_scanner_work, &sep,
1910                                 "missing trigger specification", &errstr);
1911       if (!sockline_trig_re)
1912         return m_panic_defer_3(scanent, NULL, errstr, malware_daemon_ctx.sock);
1913
1914       /* find virus name regex */
1915       sockline_name_re = m_pcre_nextinlist(&av_scanner_work, &sep,
1916                           "missing virus name regex specification", &errstr);
1917       if (!sockline_name_re)
1918         return m_panic_defer_3(scanent, NULL, errstr, malware_daemon_ctx.sock);
1919
1920       /* prepare scanner call - security depends on expansions check above */
1921       commandline = string_sprintf( CS sockline_scanner, CS eml_filename);
1922       DEBUG(D_acl) debug_printf_indent("%15s%10s'%s'\n", "", "expanded: ",
1923         string_printing(commandline));
1924
1925       /* Pass the command string to the socket */
1926       if (m_sock_send(malware_daemon_ctx.sock, commandline, Ustrlen(commandline), &errstr) < 0)
1927         return m_panic_defer(scanent, CUS callout_address, errstr);
1928
1929       /* Read the result */
1930       bread = ip_recv(&malware_daemon_ctx, av_buffer, sizeof(av_buffer), tmo);
1931
1932       if (bread <= 0)
1933         return m_panic_defer_3(scanent, CUS callout_address,
1934           string_sprintf("unable to read from socket (%s)", strerror(errno)),
1935           malware_daemon_ctx.sock);
1936
1937       if (bread == sizeof(av_buffer))
1938         return m_panic_defer_3(scanent, CUS callout_address,
1939                 US"buffer too small", malware_daemon_ctx.sock);
1940       av_buffer[bread] = '\0';
1941       linebuffer = string_copy(av_buffer);
1942       DEBUG(D_acl) debug_printf_indent("%15s%10s'%s'\n", "", "answer: ",
1943         string_printing(linebuffer));
1944
1945       /* try trigger match */
1946       if (regex_match_and_setup(sockline_trig_re, linebuffer, 0, -1))
1947         {
1948         if (!(malware_name = m_pcre_exec(sockline_name_re, av_buffer)))
1949           malware_name = US "unknown";
1950         DEBUG(D_acl) debug_printf_indent("%15s%10s'%s'\n", "", "name: ",
1951           string_printing(malware_name));
1952         }
1953       else /* no virus found */
1954         malware_name = NULL;
1955       break;
1956       }
1957 #endif
1958
1959 #ifndef DISABLE_MAL_MKS
1960     case M_MKSD: /* "mksd" scanner type ------------------------------------- */
1961       {
1962       char *mksd_options_end;
1963       int mksd_maxproc = 1;  /* default, if no option supplied */
1964       int retval;
1965
1966       if (scanner_options)
1967         {
1968         mksd_maxproc = (int)strtol(CS scanner_options, &mksd_options_end, 10);
1969         if (  *scanner_options == '\0'
1970            || *mksd_options_end != '\0'
1971            || mksd_maxproc < 1
1972            || mksd_maxproc > 32
1973            )
1974           return m_panic_defer(scanent, CUS callout_address,
1975             string_sprintf("invalid option '%s'", scanner_options));
1976         }
1977
1978       if((malware_daemon_ctx.sock = ip_unixsocket(US "/var/run/mksd/socket", &errstr)) < 0)
1979         return m_panic_defer(scanent, CUS callout_address, errstr);
1980
1981       malware_name = NULL;
1982
1983       DEBUG(D_acl) debug_printf_indent("Malware scan: issuing %s scan\n", scanner_name);
1984
1985       if ((retval = mksd_scan_packed(scanent, malware_daemon_ctx.sock, eml_filename, tmo)) != OK)
1986         {
1987         close (malware_daemon_ctx.sock);
1988         return retval;
1989         }
1990       break;
1991       }
1992 #endif
1993
1994 #ifndef DISABLE_MAL_AVAST
1995     case M_AVAST: /* "avast" scanner type ----------------------------------- */
1996       {
1997       uschar buf[1024];
1998       uschar * scanrequest;
1999       enum {AVA_HELO, AVA_OPT, AVA_RSP, AVA_DONE} avast_stage;
2000       int nread;
2001       uschar * error_message = NULL;
2002       BOOL more_data = FALSE;
2003       BOOL strict = TRUE;
2004
2005       /* According to Martin Tuma @avast the protocol uses "escaped
2006       whitespace", that is, every embedded whitespace is backslash
2007       escaped, as well as backslash is protected by backslash.
2008       The returned lines contain the name of the scanned file, a tab
2009       and the [ ] marker.
2010       [+] - not infected
2011       [L] - infected
2012       [E] - some error occurred
2013       Such marker follows the first non-escaped TAB.  For more information
2014       see avast-protocol(5)
2015
2016       We observed two cases:
2017       -> SCAN /file
2018       <- /file [E]0.0 Error 13 Permission denied
2019       <- 451 SCAN Engine error 13 permission denied
2020
2021       -> SCAN /file
2022       <- /file… [E]3.0 Error 41120 The file is a decompression bomb
2023       <- /file… [+]2.0
2024       <- /file… [+]2.0 0 Eicar Test Virus!!!
2025       <- 200 SCAN OK
2026
2027       If the scanner returns 4xx, DEFER is a good decision, combined
2028       with a panic log entry, to get the admin's attention.
2029
2030       If the scanner returns 200, we reject it as malware, if found any,
2031       or, in case of an error, we set the malware message to the error
2032       string.
2033
2034       Some of the >= 42000 errors are message related - usually some
2035       broken archives etc, but some of them are e.g. license related.
2036       Once the license expires the engine starts returning errors for
2037       every scanning attempt.  I¹ have the full list of the error codes
2038       but it is not a public API and is subject to change. It is hard
2039       for me to say what you should do in case of an engine error. You
2040       can have a “Treat * unscanned file as infection” policy or “Treat
2041       unscanned file as clean” policy.  ¹) Jakub Bednar
2042
2043        */
2044
2045       if (  (  !ava_re_clean
2046             && !(ava_re_clean = m_pcre_compile(ava_re_clean_str, &errstr)))
2047          || (  !ava_re_virus
2048             && !(ava_re_virus = m_pcre_compile(ava_re_virus_str, &errstr)))
2049          || (  !ava_re_error
2050             && !(ava_re_error = m_pcre_compile(ava_re_error_str, &errstr)))
2051          )
2052         return malware_panic_defer(errstr);
2053
2054       /* wait for result */
2055       for (avast_stage = AVA_HELO;
2056            (nread = recv_line(malware_daemon_ctx.sock, buf, sizeof(buf), tmo)) > 0;
2057           )
2058         {
2059         int slen = Ustrlen(buf);
2060         if (slen >= 1)
2061           {
2062
2063           /* Multi line responses are bracketed between 210 … and nnn … */
2064           if (Ustrncmp(buf, "210", 3) == 0)
2065             {
2066             more_data = 1;
2067             continue;
2068             }
2069           else if (more_data && isdigit(buf[0])) more_data = 0;
2070
2071           switch (avast_stage)
2072             {
2073             case AVA_HELO:
2074               if (more_data) continue;
2075               if (Ustrncmp(buf, "220", 3) != 0)
2076                 goto endloop;                   /* require a 220 */
2077               goto sendreq;
2078
2079             case AVA_OPT:
2080               if (more_data) continue;
2081               if (Ustrncmp(buf, "200", 3) != 0)
2082                 goto endloop;                   /* require a 200 */
2083
2084             sendreq:
2085               {
2086               int len;
2087               /* Check for another option to send. Newline-terminate it. */
2088               if ((scanrequest = string_nextinlist(&av_scanner_work, &sep,
2089                                 NULL, 0)))
2090                 {
2091                 if (Ustrcmp(scanrequest, "pass_unscanned") == 0)
2092                   {
2093                   DEBUG(D_acl) debug_printf_indent("pass unscanned files as clean\n");
2094                   strict = FALSE;
2095                   goto sendreq;
2096                   }
2097                 scanrequest = string_sprintf("%s\n", scanrequest);
2098                 avast_stage = AVA_OPT;          /* just sent option */
2099                 DEBUG(D_acl) debug_printf_indent("send to avast OPTION: %s", scanrequest);
2100                 }
2101               else
2102                 {
2103                 scanrequest = string_sprintf("SCAN %s\n", eml_dir);
2104                 avast_stage = AVA_RSP;          /* just sent command */
2105                 DEBUG(D_acl) debug_printf_indent("send to avast REQUEST: SCAN %s\n", eml_dir);
2106                 }
2107
2108               /* send config-cmd or scan-request to socket */
2109               len = Ustrlen(scanrequest);
2110               if (send(malware_daemon_ctx.sock, scanrequest, len, 0) == -1)
2111                 {
2112                 scanrequest[len-1] = '\0';
2113                 return m_panic_defer_3(scanent, CUS callout_address, string_sprintf(
2114                       "unable to send request '%s' to socket (%s): %s",
2115                       scanrequest, scanner_options, strerror(errno)), malware_daemon_ctx.sock);
2116                 }
2117               break;
2118               }
2119
2120             case AVA_RSP:
2121
2122               if (isdigit(buf[0]))  /* We're done */
2123                 goto endloop;
2124
2125               if (malware_name)     /* Nothing else matters, just read on */
2126                 break;
2127
2128               if (regex_match(ava_re_clean, buf, slen, NULL))
2129                 break;
2130
2131               if ((malware_name = m_pcre_exec(ava_re_virus, buf)))
2132                 {
2133                 unescape(malware_name);
2134                 DEBUG(D_acl)
2135                   debug_printf_indent("unescaped malware name: '%s'\n", malware_name);
2136                 break;
2137                 }
2138
2139               if (strict)           /* treat scanner errors as malware */
2140                 {
2141                 if ((malware_name = m_pcre_exec(ava_re_error, buf)))
2142                   {
2143                   unescape(malware_name);
2144                   DEBUG(D_acl)
2145                     debug_printf_indent("unescaped error message: '%s'\n", malware_name);
2146                   break;
2147                   }
2148                 }
2149               else if (regex_match(ava_re_error, buf, slen, NULL))
2150                 {
2151                 log_write(0, LOG_MAIN, "internal scanner error (ignored): %s", buf);
2152                 break;
2153                 }
2154
2155               /* here also for any unexpected response from the scanner */
2156               DEBUG(D_acl) debug_printf("avast response not handled: '%s'\n", buf);
2157
2158               goto endloop;
2159
2160             default:    log_write(0, LOG_PANIC, "%s:%d:%s: should not happen",
2161                             __FILE__, __LINE__, __FUNCTION__);
2162             }
2163           }
2164         }
2165
2166       endloop:
2167
2168       if (nread == -1) error_message = US"EOF from scanner";
2169       else if (nread < 0) error_message = US"timeout from scanner";
2170       else if (nread == 0) error_message = US"got nothing from scanner";
2171       else if (buf[0] != '2') error_message = buf;
2172
2173       DEBUG(D_acl) debug_printf_indent("sent to avast QUIT\n");
2174       if (send(malware_daemon_ctx.sock, "QUIT\n", 5, 0) == -1)
2175         return m_panic_defer_3(scanent, CUS callout_address,
2176           string_sprintf("unable to send quit request to socket (%s): %s",
2177             scanner_options, strerror(errno)), malware_daemon_ctx.sock);
2178
2179       if (error_message)
2180         return m_panic_defer_3(scanent, CUS callout_address, error_message, malware_daemon_ctx.sock);
2181
2182       }
2183 #endif
2184   }     /* scanner type switch */
2185
2186   if (malware_daemon_ctx.sock >= 0)
2187     (void) close (malware_daemon_ctx.sock);
2188   malware_ok = TRUE;                    /* set "been here, done that" marker */
2189   }
2190
2191 /* match virus name against pattern (caseless ------->----------v) */
2192 if (malware_name && regex_match_and_setup(re, malware_name, 0, -1))
2193   {
2194   DEBUG(D_acl) debug_printf_indent(
2195       "Matched regex to malware [%s] [%s]\n", malware_re, malware_name);
2196   return OK;
2197   }
2198 else
2199   return FAIL;
2200 }
2201
2202
2203 /*************************************************
2204 *          Scan an email for malware             *
2205 *************************************************/
2206
2207 /* This is the normal interface for scanning an email, which doesn't need a
2208 filename; it's a wrapper around the malware_file function.
2209
2210 Arguments:
2211   malware_re  match condition for "malware="
2212   timeout     if nonzero, timeout in seconds
2213
2214 Returns:      Exim message processing code (OK, FAIL, DEFER, ...)
2215               where true means malware was found (condition applies)
2216 */
2217 int
2218 malware(const uschar * malware_re, int timeout)
2219 {
2220 int ret = malware_internal(malware_re, NULL, timeout);
2221
2222 if (ret == DEFER) av_failed = TRUE;
2223 return ret;
2224 }
2225
2226
2227 /*************************************************
2228 *          Scan a file for malware               *
2229 *************************************************/
2230
2231 /* This is a test wrapper for scanning an email, which is not used in
2232 normal processing.  Scan any file, using the Exim scanning interface.
2233 This function tampers with various global variables so is unsafe to use
2234 in any other context.
2235
2236 Arguments:
2237   eml_filename  a file holding the message to be scanned
2238
2239 Returns:        Exim message processing code (OK, FAIL, DEFER, ...)
2240                 where true means malware was found (condition applies)
2241 */
2242 int
2243 malware_in_file(uschar *eml_filename)
2244 {
2245 uschar message_id_buf[64];
2246 int ret;
2247
2248 /* spool_mbox() assumes various parameters exist, when creating
2249 the relevant directory and the email within */
2250
2251 (void) string_format(message_id_buf, sizeof(message_id_buf),
2252     "dummy-%d", vaguely_random_number(INT_MAX));
2253 message_id = message_id_buf;
2254 sender_address = US"malware-sender@example.net";
2255 return_path = US"";
2256 recipients_list = NULL;
2257 receive_add_recipient(US"malware-victim@example.net", -1);
2258 f.enable_dollar_recipients = TRUE;
2259
2260 ret = malware_internal(US"*", eml_filename, 0);
2261
2262 Ustrncpy(spooled_message_id, message_id, sizeof(spooled_message_id));
2263 spool_mbox_ok = 1;
2264
2265 /* don't set no_mbox_unspool; at present, there's no way for it to become
2266 set, but if that changes, then it should apply to these tests too */
2267
2268 unspool_mbox();
2269
2270 /* silence static analysis tools */
2271 message_id = NULL;
2272
2273 return ret;
2274 }
2275
2276
2277 void
2278 malware_init(void)
2279 {
2280 if (!malware_default_re)
2281   malware_default_re = regex_must_compile(malware_regex_default, FALSE, TRUE);
2282
2283 #ifndef DISABLE_MAL_DRWEB
2284 if (!drweb_re)
2285   drweb_re = regex_must_compile(drweb_re_str, FALSE, TRUE);
2286 #endif
2287 #ifndef DISABLE_MAL_FSECURE
2288 if (!fsec_re)
2289   fsec_re = regex_must_compile(fsec_re_str, FALSE, TRUE);
2290 #endif
2291 #ifndef DISABLE_MAL_KAV
2292 if (!kav_re_sus)
2293   kav_re_sus = regex_must_compile(kav_re_sus_str, FALSE, TRUE);
2294 if (!kav_re_inf)
2295   kav_re_inf = regex_must_compile(kav_re_inf_str, FALSE, TRUE);
2296 #endif
2297 #ifndef DISABLE_MAL_AVAST
2298 if (!ava_re_clean)
2299   ava_re_clean = regex_must_compile(ava_re_clean_str, FALSE, TRUE);
2300 if (!ava_re_virus)
2301   ava_re_virus = regex_must_compile(ava_re_virus_str, FALSE, TRUE);
2302 if (!ava_re_error)
2303   ava_re_error = regex_must_compile(ava_re_error_str, FALSE, TRUE);
2304 #endif
2305 #ifndef DISABLE_MAL_FFROT6D
2306 if (!fprot6d_re_error)
2307   fprot6d_re_error = regex_must_compile(fprot6d_re_error_str, FALSE, TRUE);
2308 if (!fprot6d_re_virus)
2309   fprot6d_re_virus = regex_must_compile(fprot6d_re_virus_str, FALSE, TRUE);
2310 #endif
2311 }
2312
2313
2314 gstring *
2315 malware_show_supported(gstring * g)
2316 {
2317 g = string_cat(g, US"Malware:");
2318 for (struct scan * sc = m_scans; sc->scancode != (scanner_t)-1; sc++)
2319   g = string_fmt_append(g, " %s", sc->name);
2320 return string_cat(g, US"\n");
2321 }
2322
2323
2324 # endif /*!MACRO_PREDEF*/
2325 #endif /*WITH_CONTENT_SCAN*/
2326 /*
2327  * vi: aw ai sw=2
2328  */