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