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