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