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