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