wip - see failed-summary.log.list_match_value. Pretty much ok.
[exim.git] / src / src / malware.c
CommitLineData
8523533c
TK
1/*************************************************
2* Exim - an Internet mail transport agent *
3*************************************************/
4
80fea873
JH
5/* Copyright (c) Tom Kistner <tom@duncanthrax.net> 2003 - 2015
6 * License: GPL
1e1ddfac 7 * Copyright (c) The Exim Maintainers 2015 - 2020
80fea873 8 */
8523533c
TK
9
10/* Code for calling virus (malware) scanners. Called from acl.c. */
11
12#include "exim.h"
c11d665d 13#ifdef WITH_CONTENT_SCAN /* entire file */
8523533c 14
c8599aad
JH
15typedef 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
4e559e21 52 M_DUMMY
c8599aad 53 } scanner_t;
b1f8e4f8
JH
54typedef enum {MC_NONE, MC_TCP, MC_UNIX, MC_STRM} contype_t;
55static struct scan
56{
57 scanner_t scancode;
58 const uschar * name;
59 const uschar * options_default;
60 contype_t conn;
61} m_scans[] =
62{
c11d665d 63#ifndef DISABLE_MAL_FFROTD
b1f8e4f8 64 { M_FPROTD, US"f-protd", US"localhost 10200-10204", MC_TCP },
c11d665d
JH
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
b1f8e4f8 70 { M_DRWEB, US"drweb", US"/usr/local/drweb/run/drwebd.sock", MC_STRM },
c11d665d
JH
71#endif
72#ifndef DISABLE_MAL_AVE
b1f8e4f8 73 { M_AVES, US"aveserver", US"/var/run/aveserver", MC_UNIX },
c11d665d
JH
74#endif
75#ifndef DISABLE_MAL_FSECURE
b1f8e4f8 76 { M_FSEC, US"fsecure", US"/var/run/.fsav", MC_UNIX },
c11d665d
JH
77#endif
78#ifndef DISABLE_MAL_KAV
b1f8e4f8 79 { M_KAVD, US"kavdaemon", US"/var/run/AvpCtl", MC_UNIX },
c11d665d
JH
80#endif
81#ifndef DISABLE_MAL_SOPHIE
b1f8e4f8 82 { M_SOPHIE, US"sophie", US"/var/run/sophie", MC_UNIX },
c11d665d
JH
83#endif
84#ifndef DISABLE_MAL_CLAM
b1f8e4f8 85 { M_CLAMD, US"clamd", US"/tmp/clamd", MC_NONE },
c11d665d
JH
86#endif
87#ifndef DISABLE_MAL_MKS
b1f8e4f8 88 { M_MKSD, US"mksd", NULL, MC_NONE },
c11d665d
JH
89#endif
90#ifndef DISABLE_MAL_AVAST
b6fbf22d 91 { M_AVAST, US"avast", US"/var/run/avast/scan.sock", MC_STRM },
c11d665d
JH
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
b1f8e4f8
JH
99 { -1, NULL, NULL, MC_NONE } /* end-marker */
100};
101
c11d665d
JH
102/******************************************************************************/
103# ifdef MACRO_PREDEF /* build solely to predefine macros */
104
105# include "macro_predef.h"
106
107void
108features_malware(void)
109{
2b8b8e4e
JH
110const uschar * s;
111uschar * t;
c11d665d
JH
112uschar buf[64];
113
114spf(buf, sizeof(buf), US"_HAVE_MALWARE_");
115
d7978c0f 116for (const struct scan * sc = m_scans; sc->scancode != -1; sc++)
c11d665d 117 {
d7978c0f
JH
118 for (s = sc->name, t = buf+14; *s; s++) if (*s != '-')
119 *t++ = toupper(*s);
c11d665d
JH
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
131static const uschar * malware_regex_default = US ".+";
132static const pcre * malware_default_re = NULL;
133
134
135
136#ifndef DISABLE_MAL_CLAM
599dcb77 137/* The maximum number of clamd servers that are supported in the configuration */
c11d665d
JH
138# define MAX_CLAMD_SERVERS 32
139# define MAX_CLAMD_SERVERS_S "32"
599dcb77 140
8a512ed5
JH
141typedef struct clamd_address {
142 uschar * hostspec;
143 unsigned tcp_port;
144 unsigned retry;
145} clamd_address;
8523533c
TK
146#endif
147
a9ccd69a 148
c11d665d
JH
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" */
8523533c 153
c11d665d
JH
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 */
476be7e2
JH
158
159static const uschar * drweb_re_str = US "infected\\swith\\s*(.+?)$";
160static const pcre * drweb_re = NULL;
c11d665d 161#endif
476be7e2 162
c11d665d 163#ifndef DISABLE_MAL_FSECURE
476be7e2
JH
164static const uschar * fsec_re_str = US "\\S{0,5}INFECTED\\t[^\\t]*\\t([^\\t]+)\\t\\S*$";
165static const pcre * fsec_re = NULL;
c11d665d 166#endif
476be7e2 167
c11d665d 168#ifndef DISABLE_MAL_KAV
476be7e2
JH
169static const uschar * kav_re_sus_str = US "suspicion:\\s*(.+?)\\s*$";
170static const uschar * kav_re_inf_str = US "infected:\\s*(.+?)\\s*$";
171static const pcre * kav_re_sus = NULL;
172static const pcre * kav_re_inf = NULL;
c11d665d 173#endif
476be7e2 174
c11d665d 175#ifndef DISABLE_MAL_AVAST
476be7e2 176static const uschar * ava_re_clean_str = US "(?!\\\\)\\t\\[\\+\\]";
ad93c40f
HSHR
177static const uschar * ava_re_virus_str = US "(?!\\\\)\\t\\[L\\]\\d+\\.0\\t0\\s(.*)";
178static const uschar * ava_re_error_str = US "(?!\\\\)\\t\\[E\\]\\d+\\.0\\tError\\s\\d+\\s(.*)";
476be7e2
JH
179static const pcre * ava_re_clean = NULL;
180static const pcre * ava_re_virus = NULL;
ad93c40f 181static const pcre * ava_re_error = NULL;
c11d665d 182#endif
476be7e2 183
c11d665d 184#ifndef DISABLE_MAL_FFROT6D
71b32d41
ACK
185static const uschar * fprot6d_re_error_str = US "^\\d+\\s<(.+?)>$";
186static const uschar * fprot6d_re_virus_str = US "^\\d+\\s<infected:\\s+(.+?)>\\s+.+$";
187static const pcre * fprot6d_re_error = NULL;
188static const pcre * fprot6d_re_virus = NULL;
c11d665d 189#endif
71b32d41 190
476be7e2
JH
191
192
193/******************************************************************************/
194
059f2ace 195#ifndef DISABLE_MAL_KAV
b1f8e4f8 196/* Routine to check whether a system is big- or little-endian.
8523533c
TK
197 Ripped from http://www.faqs.org/faqs/graphics/fileformats-faq/part4/section-7.html
198 Needed for proper kavdaemon implementation. Sigh. */
059f2ace
JH
199# define BIG_MY_ENDIAN 0
200# define LITTLE_MY_ENDIAN 1
b1f8e4f8
JH
201static int test_byte_order(void);
202static inline int
203test_byte_order()
204{
205 short int word = 0x0001;
5903c6ff 206 char *byte = CS &word;
b1f8e4f8 207 return(byte[0] ? LITTLE_MY_ENDIAN : BIG_MY_ENDIAN);
8523533c 208}
059f2ace 209#endif
8523533c 210
b1f8e4f8 211BOOL malware_ok = FALSE;
8523533c 212
8544e77a
PP
213/* Gross hacks for the -bmalware option; perhaps we should just create
214the scan directory normally for that case, but look into rigging up the
215needed header variables if not already set on the command-line? */
216extern int spool_mbox_ok;
55240832 217extern uschar spooled_message_id[MESSAGE_ID_LENGTH+1];
8544e77a 218
8544e77a 219
ad93c40f
HSHR
220/* Some (currently avast only) use backslash escaped whitespace,
221this function undoes these escapes */
46682a67 222
ad93c40f 223static inline void
46682a67
JH
224unescape(uschar *p)
225{
226uschar *p0;
227for (; *p; ++p)
228 if (*p == '\\' && (isspace(p[1]) || p[1] == '\\'))
229 for (p0 = p; *p0; ++p0) *p0 = p0[1];
ad93c40f 230}
8544e77a 231
ad93c40f 232/* --- malware_*_defer --- */
b1f8e4f8 233static inline int
ad93c40f 234malware_panic_defer(const uschar * str)
b1f8e4f8 235{
8a512ed5
JH
236log_write(0, LOG_MAIN|LOG_PANIC, "malware acl condition: %s", str);
237return DEFER;
b1f8e4f8 238}
ad93c40f
HSHR
239static inline int
240malware_log_defer(const uschar * str)
241{
242log_write(0, LOG_MAIN, "malware acl condition: %s", str);
243return DEFER;
244}
245/* --- m_*_defer --- */
246static inline int
247m_panic_defer(struct scan * scanent, const uschar * hostport,
0ea02355 248 const uschar * str)
b1f8e4f8 249{
ad93c40f 250return malware_panic_defer(string_sprintf("%s %s : %s",
0ea02355 251 scanent->name, hostport ? hostport : CUS"", str));
b1f8e4f8 252}
ad93c40f
HSHR
253static inline int
254m_log_defer(struct scan * scanent, const uschar * hostport,
255 const uschar * str)
256{
257return malware_log_defer(string_sprintf("%s %s : %s",
258 scanent->name, hostport ? hostport : CUS"", str));
259}
260/* --- m_*_defer_3 */
261static inline int
262m_panic_defer_3(struct scan * scanent, const uschar * hostport,
263 const uschar * str, int fd_to_close)
264{
a8e46b3b 265DEBUG(D_acl) debug_print_socket(fd_to_close);
ad93c40f
HSHR
266(void) close(fd_to_close);
267return m_panic_defer(scanent, hostport, str);
268}
b1f8e4f8
JH
269
270/*************************************************/
271
c11d665d 272#ifndef DISABLE_MAL_CLAM
b1f8e4f8
JH
273/* Only used by the Clamav code, which is working from a list of servers and
274uses the returned in_addr to get a second connection to the same system.
275*/
276static inline int
277m_tcpsocket(const uschar * hostname, unsigned int port,
10ac8d7f 278 host_item * host, uschar ** errstr, const blob * fastopen_blob)
b1f8e4f8 279{
4a5cbaff 280return ip_connectedsocket(SOCK_STREAM, hostname, port, port, 5,
10ac8d7f 281 host, errstr, fastopen_blob);
b1f8e4f8 282}
c11d665d 283#endif
b1f8e4f8 284
b1f8e4f8
JH
285static int
286m_sock_send(int sock, uschar * buf, int cnt, uschar ** errstr)
287{
8a512ed5
JH
288if (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 }
296return sock;
b1f8e4f8
JH
297}
298
299static const pcre *
300m_pcre_compile(const uschar * re, uschar ** errstr)
301{
8a512ed5
JH
302const uschar * rerror;
303int roffset;
304const pcre * cre;
305
3d2e82c5 306if (!(cre = pcre_compile(CS re, PCRE_COPT, CCSS &rerror, &roffset, NULL)))
8a512ed5
JH
307 *errstr= string_sprintf("regular expression error in '%s': %s at offset %d",
308 re, rerror, roffset);
309return cre;
b1f8e4f8
JH
310}
311
312uschar *
313m_pcre_exec(const pcre * cre, uschar * text)
314{
8a512ed5
JH
315int ovector[10*3];
316int i = pcre_exec(cre, NULL, CS text, Ustrlen(text), 0, 0,
c11d665d 317 ovector, nelem(ovector));
8a512ed5
JH
318uschar * substr = NULL;
319if (i >= 2) /* Got it */
3d2e82c5 320 pcre_get_substring(CS text, ovector, i, 1, CCSS &substr);
8a512ed5 321return substr;
b1f8e4f8
JH
322}
323
324static const pcre *
55414b25
JH
325m_pcre_nextinlist(const uschar ** list, int * sep,
326 char * listerr, uschar ** errstr)
b1f8e4f8 327{
8a512ed5
JH
328const uschar * list_ele;
329const pcre * cre = NULL;
330
331if (!(list_ele = string_nextinlist(list, sep, NULL, 0)))
332 *errstr = US listerr;
333else
ac4d558b
JH
334 {
335 DEBUG(D_acl) debug_printf_indent("%15s%10s'%s'\n", "", "RE: ",
336 string_printing(list_ele));
8a512ed5 337 cre = m_pcre_compile(CUS list_ele, errstr);
ac4d558b 338 }
8a512ed5 339return cre;
b1f8e4f8
JH
340}
341
a8e46b3b 342
4e71661f
JH
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
0f0c8159
JH
348 Return: number of chars - zero for an empty line
349 -1 on EOF
350 -2 on timeout or error
4e71661f
JH
351*/
352static int
0a5441fc 353recv_line(int fd, uschar * buffer, int bsize, time_t tmo)
4e71661f
JH
354{
355uschar * p = buffer;
356ssize_t rcv;
0f0c8159 357BOOL ok = FALSE;
4e71661f 358
0a5441fc 359if (!fd_ready(fd, tmo))
0f0c8159 360 return -2;
4e71661f 361
0f0c8159 362/*XXX tmo handling assumes we always get a whole line */
4e71661f 363/* read until \n */
0f0c8159 364errno = 0;
4e71661f
JH
365while ((rcv = read(fd, p, 1)) > 0)
366 {
0f0c8159 367 ok = TRUE;
4e71661f
JH
368 if (p-buffer > bsize-2) break;
369 if (*p == '\n') break;
370 if (*p != '\r') p++;
371 }
0f0c8159
JH
372if (!ok)
373 {
a8e46b3b
JH
374 DEBUG(D_acl)
375 {
376 debug_printf_indent("Malware scan: read %s (%s)\n",
0f0c8159 377 rcv==0 ? "EOF" : "error", strerror(errno));
a8e46b3b
JH
378 debug_print_socket(fd);
379 }
0f0c8159
JH
380 return rcv==0 ? -1 : -2;
381 }
4e71661f
JH
382*p = '\0';
383
e1d04f48 384DEBUG(D_acl) debug_printf_indent("Malware scan: read '%s'\n", buffer);
4e71661f
JH
385return p - buffer;
386}
387
388/* return TRUE iff size as requested */
389static BOOL
0a5441fc 390recv_len(int sock, void * buf, int size, time_t tmo)
4e71661f 391{
0a5441fc 392return fd_ready(sock, tmo)
4e71661f
JH
393 ? recv(sock, buf, size, 0) == size
394 : FALSE;
395}
396
397
0f0c8159 398
c11d665d 399#ifndef DISABLE_MAL_MKS
0f0c8159
JH
400/* ============= private routines for the "mksd" scanner type ============== */
401
c11d665d 402# include <sys/uio.h>
0f0c8159
JH
403
404static inline int
405mksd_writev (int sock, struct iovec * iov, int iovcnt)
406{
407int i;
408
409for (;;)
410 {
411 do
412 i = writev (sock, iov, iovcnt);
413 while (i < 0 && errno == EINTR);
414 if (i <= 0)
415 {
ad93c40f 416 (void) malware_panic_defer(
0f0c8159
JH
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
437static inline int
0a5441fc 438mksd_read_lines (int sock, uschar *av_buffer, int av_buffer_size, time_t tmo)
0f0c8159 439{
74f1a423 440client_conn_ctx cctx = {.sock = sock};
0f0c8159
JH
441int offset = 0;
442int i;
443
444do
445 {
0a5441fc 446 i = ip_recv(&cctx, av_buffer+offset, av_buffer_size-offset, tmo);
0f0c8159
JH
447 if (i <= 0)
448 {
ad93c40f 449 (void) malware_panic_defer(US"unable to read from mksd UNIX socket (/var/run/mksd/socket)");
0f0c8159
JH
450 return -1;
451 }
452
453 offset += i;
454 /* offset == av_buffer_size -> buffer full */
455 if (offset == av_buffer_size)
456 {
ad93c40f 457 (void) malware_panic_defer(US"malformed reply received from mksd");
0f0c8159
JH
458 return -1;
459 }
460 } while (av_buffer[offset-1] != '\n');
461
462av_buffer[offset] = '\0';
463return offset;
464}
465
466static inline int
467mksd_parse_line(struct scan * scanent, char * line)
468{
469char *p;
470
471switch (*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';
ad93c40f 480 return m_panic_defer(scanent, NULL,
0f0c8159
JH
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 }
ad93c40f 498 return m_panic_defer(scanent, NULL,
0f0c8159
JH
499 string_sprintf("malformed reply received: %s", line));
500 }
501}
502
503static int
504mksd_scan_packed(struct scan * scanent, int sock, const uschar * scan_filename,
0a5441fc 505 time_t tmo)
0f0c8159
JH
506{
507struct iovec iov[3];
508const char *cmd = "MSQ\n";
509uschar av_buffer[1024];
510
511iov[0].iov_base = (void *) cmd;
512iov[0].iov_len = 3;
513iov[1].iov_base = (void *) scan_filename;
514iov[1].iov_len = Ustrlen(scan_filename);
515iov[2].iov_base = (void *) (cmd + 3);
516iov[2].iov_len = 1;
517
518if (mksd_writev (sock, iov, 3) < 0)
519 return DEFER;
520
521if (mksd_read_lines (sock, av_buffer, sizeof (av_buffer), tmo) < 0)
522 return DEFER;
523
524return mksd_parse_line (scanent, CS av_buffer);
525}
c11d665d 526#endif /* MKSD */
0f0c8159 527
8a512ed5 528
c11d665d 529#ifndef DISABLE_MAL_CLAM
8a512ed5
JH
530static int
531clamd_option(clamd_address * cd, const uschar * optstr, int * subsep)
532{
533uschar * s;
534
535cd->retry = 0;
536while ((s = string_nextinlist(&optstr, subsep, NULL, 0)))
8a512ed5
JH
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;
93a6fce2 546return OK;
8a512ed5 547}
c11d665d
JH
548#endif
549
550
8a512ed5 551
8544e77a
PP
552/*************************************************
553* Scan content for malware *
554*************************************************/
555
556/* This is an internal interface for scanning an email; the normal interface
557is via malware(), or there's malware_in_file() used for testing/debugging.
558
559Arguments:
0f0c8159 560 malware_re match condition for "malware="
040721f2
JH
561 scan_filename the file holding the email to be scanned, if we're faking
562 this up for the -bmalware test, else NULL
0f0c8159 563 timeout if nonzero, non-default timeoutl
8544e77a
PP
564
565Returns: Exim message processing code (OK, FAIL, DEFER, ...)
566 where true means malware was found (condition applies)
567*/
b1f8e4f8 568static int
040721f2
JH
569malware_internal(const uschar * malware_re, const uschar * scan_filename,
570 int timeout)
b1f8e4f8 571{
0f0c8159 572int sep = 0;
55414b25 573const uschar *av_scanner_work = av_scanner;
0f0c8159 574uschar *scanner_name;
0f0c8159
JH
575unsigned long mbox_size;
576FILE *mbox_file;
577const pcre *re;
578uschar * errstr;
579struct scan * scanent;
580const uschar * scanner_options;
74f1a423 581client_conn_ctx malware_daemon_ctx = {.sock = -1};
0f0c8159 582time_t tmo;
040721f2 583uschar * eml_filename, * eml_dir;
0f0c8159 584
040721f2
JH
585if (!malware_re)
586 return FAIL; /* empty means "don't match anything" */
587
588/* Ensure the eml mbox file is spooled up */
589
590if (!(mbox_file = spool_mbox(&mbox_size, scan_filename, &eml_filename)))
ad93c40f 591 return malware_panic_defer(US"error while creating mbox spool file");
0f0c8159 592
040721f2
JH
593/* None of our current scanners need the mbox file as a stream (they use
594the name), so we can close it right away. Get the directory too. */
0f0c8159 595
040721f2
JH
596(void) fclose(mbox_file);
597eml_dir = string_copyn(eml_filename, Ustrrchr(eml_filename, '/') - eml_filename);
0f0c8159
JH
598
599/* parse 1st option */
040721f2 600if (strcmpic(malware_re, US"false") == 0 || Ustrcmp(malware_re,"0") == 0)
0f0c8159
JH
601 return FAIL; /* explicitly no matching */
602
603/* special cases (match anything except empty) */
476be7e2
JH
604if ( 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)))
ad93c40f 611 return malware_panic_defer(errstr);
0f0c8159 612 malware_re = malware_regex_default;
476be7e2
JH
613 re = malware_default_re;
614 }
0f0c8159
JH
615
616/* compile the regex, see if it works */
476be7e2 617else if (!(re = m_pcre_compile(malware_re, &errstr)))
ad93c40f 618 return malware_panic_defer(errstr);
0f0c8159
JH
619
620/* if av_scanner starts with a dollar, expand it first */
621if (*av_scanner == '$')
622 {
623 if (!(av_scanner_work = expand_string(av_scanner)))
ad93c40f 624 return malware_panic_defer(
0f0c8159
JH
625 string_sprintf("av_scanner starts with $, but expansion failed: %s",
626 expand_string_message));
b1f8e4f8 627
0f0c8159 628 DEBUG(D_acl)
e1d04f48 629 debug_printf_indent("Expanded av_scanner global: %s\n", av_scanner_work);
0f0c8159
JH
630 /* disable result caching in this case */
631 malware_name = NULL;
632 malware_ok = FALSE;
8523533c
TK
633 }
634
0f0c8159
JH
635/* Do not scan twice (unless av_scanner is dynamic). */
636if (!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)))
ad93c40f 640 return malware_panic_defer(US"av_scanner configuration variable is empty");
0f0c8159
JH
641 if (!timeout) timeout = MALWARE_TIMEOUT;
642 tmo = time(NULL) + timeout;
643
3e60dd41
JH
644 for (scanent = m_scans; ; scanent++)
645 {
0f0c8159 646 if (!scanent->name)
ad93c40f 647 return malware_panic_defer(string_sprintf("unknown scanner type '%s'",
0f0c8159
JH
648 scanner_name));
649 if (strcmpic(scanner_name, US scanent->name) != 0)
650 continue;
ac4d558b
JH
651 DEBUG(D_acl) debug_printf_indent("Malware scan: %s tmo=%s\n",
652 scanner_name, readconf_printtime(timeout));
653
0f0c8159
JH
654 if (!(scanner_options = string_nextinlist(&av_scanner_work, &sep, NULL, 0)))
655 scanner_options = scanent->options_default;
656 if (scanent->conn == MC_NONE)
b1f8e4f8 657 break;
ac4d558b
JH
658
659 DEBUG(D_acl) debug_printf_indent("%15s%10s%s\n", "", "socket: ", scanner_options);
0f0c8159
JH
660 switch(scanent->conn)
661 {
74f1a423 662 case MC_TCP:
7d2f2d36 663 malware_daemon_ctx.sock = ip_tcpsocket(scanner_options, &errstr, 5, NULL); break;
74f1a423
JH
664 case MC_UNIX:
665 malware_daemon_ctx.sock = ip_unixsocket(scanner_options, &errstr); break;
666 case MC_STRM:
7d2f2d36 667 malware_daemon_ctx.sock = ip_streamsocket(scanner_options, &errstr, 5, NULL); break;
74f1a423
JH
668 default:
669 /* compiler quietening */ break;
384152a6 670 }
74f1a423 671 if (malware_daemon_ctx.sock < 0)
ad93c40f 672 return m_panic_defer(scanent, CUS callout_address, errstr);
0f0c8159
JH
673 break;
674 }
8e669ac1 675
0f0c8159
JH
676 switch (scanent->scancode)
677 {
c11d665d 678#ifndef DISABLE_MAL_FFROTD
0f0c8159 679 case M_FPROTD: /* "f-protd" scanner type -------------------------------- */
b1f8e4f8 680 {
0f0c8159
JH
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;
b1f8e4f8 687
0f0c8159 688 scanrequest = string_sprintf("GET %s", eml_filename);
b1f8e4f8 689
0f0c8159
JH
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);
e1d04f48 698 DEBUG(D_acl) debug_printf_indent("Malware scan: issuing %s: %s\n",
0f0c8159 699 scanner_name, scanrequest);
b1f8e4f8 700
0f0c8159 701 /* send scan request */
74f1a423 702 if (m_sock_send(malware_daemon_ctx.sock, scanrequest, Ustrlen(scanrequest)+1, &errstr) < 0)
ad93c40f 703 return m_panic_defer(scanent, CUS callout_address, errstr);
b1f8e4f8 704
74f1a423 705 while ((len = recv_line(malware_daemon_ctx.sock, buf, sizeof(buf), tmo)) >= 0)
0f0c8159
JH
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>")))
4e71661f 711 {
0f0c8159 712 if ((strhelper2 = Ustrstr(buf, US"</name>")) != NULL)
4e71661f 713 {
0f0c8159
JH
714 *strhelper2 = '\0';
715 malware_name_internal = string_copy(strhelper+6);
4e71661f
JH
716 }
717 }
0f0c8159 718 else if (Ustrstr(buf, US"<summary code=\""))
4e71661f 719 {
0f0c8159
JH
720 malware_name = Ustrstr(buf, US"<summary code=\"11\">")
721 ? malware_name_internal : NULL;
722 break;
4e71661f 723 }
0f0c8159
JH
724 }
725 if (len < -1)
726 {
74f1a423 727 (void)close(malware_daemon_ctx.sock);
0f0c8159
JH
728 return DEFER;
729 }
730 break;
731 } /* f-protd */
c11d665d
JH
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))))
ad93c40f 745 return malware_panic_defer(errstr);
c11d665d
JH
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
74f1a423 751 if (m_sock_send(malware_daemon_ctx.sock, scanrequest, Ustrlen(scanrequest), &errstr) < 0)
ad93c40f 752 return m_panic_defer(scanent, CUS callout_address, errstr);
c11d665d 753
0a5441fc 754 bread = ip_recv(&malware_daemon_ctx, av_buffer, sizeof(av_buffer), tmo);
c11d665d
JH
755
756 if (bread <= 0)
ad93c40f 757 return m_panic_defer_3(scanent, CUS callout_address,
c11d665d 758 string_sprintf("unable to read from socket (%s)", strerror(errno)),
74f1a423 759 malware_daemon_ctx.sock);
c11d665d
JH
760
761 if (bread == sizeof(av_buffer))
ad93c40f 762 return m_panic_defer_3(scanent, CUS callout_address,
74f1a423 763 US"buffer too small", malware_daemon_ctx.sock);
c11d665d
JH
764
765 av_buffer[bread] = '\0';
766 linebuffer = string_copy(av_buffer);
767
74f1a423 768 m_sock_send(malware_daemon_ctx.sock, US"QUIT\n", 5, 0);
c11d665d
JH
769
770 if ((e = m_pcre_exec(fprot6d_re_error, linebuffer)))
ad93c40f 771 return m_panic_defer_3(scanent, CUS callout_address,
74f1a423 772 string_sprintf("scanner reported error (%s)", e), malware_daemon_ctx.sock);
b1f8e4f8 773
c11d665d
JH
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
0f0c8159
JH
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;
0f0c8159
JH
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 */
c4141216 800 if ((drweb_fd = exim_open2(CCS eml_filename, O_RDONLY)) == -1)
ad93c40f 801 return m_panic_defer_3(scanent, NULL,
0f0c8159
JH
802 string_sprintf("can't open spool file %s: %s",
803 eml_filename, strerror(errno)),
74f1a423 804 malware_daemon_ctx.sock);
b1f8e4f8 805
0f0c8159
JH
806 if ((fsize = lseek(drweb_fd, 0, SEEK_END)) == -1)
807 {
d315eda1
JH
808 int err;
809badseek: err = errno;
b1f8e4f8 810 (void)close(drweb_fd);
ad93c40f 811 return m_panic_defer_3(scanent, NULL,
0f0c8159
JH
812 string_sprintf("can't seek spool file %s: %s",
813 eml_filename, strerror(err)),
74f1a423 814 malware_daemon_ctx.sock);
0f0c8159
JH
815 }
816 fsize_uint = (unsigned int) fsize;
817 if ((off_t)fsize_uint != fsize)
818 {
b1f8e4f8 819 (void)close(drweb_fd);
ad93c40f 820 return m_panic_defer_3(scanent, NULL,
0f0c8159
JH
821 string_sprintf("seeking spool file %s, size overflow",
822 eml_filename),
74f1a423 823 malware_daemon_ctx.sock);
4e71661f 824 }
0f0c8159 825 drweb_slen = htonl(fsize);
d315eda1
JH
826 if (lseek(drweb_fd, 0, SEEK_SET) < 0)
827 goto badseek;
0f0c8159 828
e1d04f48 829 DEBUG(D_acl) debug_printf_indent("Malware scan: issuing %s remote scan [%s]\n",
0f0c8159
JH
830 scanner_name, scanner_options);
831
832 /* send scan request */
74f1a423
JH
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))
4e71661f 837 {
0f0c8159 838 (void)close(drweb_fd);
ad93c40f 839 return m_panic_defer_3(scanent, CUS callout_address, string_sprintf(
0f0c8159 840 "unable to send commands to socket (%s)", scanner_options),
74f1a423 841 malware_daemon_ctx.sock);
4e71661f 842 }
8e669ac1 843
f3ebb786 844 if (!(drweb_fbuf = store_malloc(fsize_uint)))
0f0c8159
JH
845 {
846 (void)close(drweb_fd);
ad93c40f 847 return m_panic_defer_3(scanent, NULL,
0f0c8159
JH
848 string_sprintf("unable to allocate memory %u for file (%s)",
849 fsize_uint, eml_filename),
74f1a423 850 malware_daemon_ctx.sock);
0f0c8159 851 }
b1f8e4f8 852
0f0c8159
JH
853 if ((result = read (drweb_fd, drweb_fbuf, fsize)) == -1)
854 {
855 int err = errno;
856 (void)close(drweb_fd);
f3ebb786 857 store_free(drweb_fbuf);
ad93c40f 858 return m_panic_defer_3(scanent, NULL,
0f0c8159
JH
859 string_sprintf("can't read spool file %s: %s",
860 eml_filename, strerror(err)),
74f1a423 861 malware_daemon_ctx.sock);
0f0c8159
JH
862 }
863 (void)close(drweb_fd);
b1f8e4f8 864
0f0c8159 865 /* send file body to socket */
74f1a423 866 if (send(malware_daemon_ctx.sock, drweb_fbuf, fsize, 0) < 0)
4e71661f 867 {
f3ebb786 868 store_free(drweb_fbuf);
ad93c40f 869 return m_panic_defer_3(scanent, CUS callout_address, string_sprintf(
0f0c8159 870 "unable to send file body to socket (%s)", scanner_options),
74f1a423 871 malware_daemon_ctx.sock);
0f0c8159 872 }
f3ebb786 873 store_free(drweb_fbuf);
0f0c8159
JH
874 }
875 else
876 {
877 drweb_slen = htonl(Ustrlen(eml_filename));
b1f8e4f8 878
e1d04f48 879 DEBUG(D_acl) debug_printf_indent("Malware scan: issuing %s local scan [%s]\n",
0f0c8159 880 scanner_name, scanner_options);
b1f8e4f8 881
0f0c8159 882 /* send scan request */
74f1a423
JH
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))
ad93c40f 888 return m_panic_defer_3(scanent, CUS callout_address, string_sprintf(
0f0c8159 889 "unable to send commands to socket (%s)", scanner_options),
74f1a423 890 malware_daemon_ctx.sock);
0f0c8159 891 }
b1f8e4f8 892
0f0c8159 893 /* wait for result */
74f1a423 894 if (!recv_len(malware_daemon_ctx.sock, &drweb_rc, sizeof(drweb_rc), tmo))
ad93c40f 895 return m_panic_defer_3(scanent, CUS callout_address,
74f1a423 896 US"unable to read return code", malware_daemon_ctx.sock);
0f0c8159
JH
897 drweb_rc = ntohl(drweb_rc);
898
74f1a423 899 if (!recv_len(malware_daemon_ctx.sock, &drweb_vnum, sizeof(drweb_vnum), tmo))
ad93c40f 900 return m_panic_defer_3(scanent, CUS callout_address,
74f1a423 901 US"unable to read the number of viruses", malware_daemon_ctx.sock);
0f0c8159
JH
902 drweb_vnum = ntohl(drweb_vnum);
903
904 /* "virus(es) found" if virus number is > 0 */
905 if (drweb_vnum)
906 {
acec9514 907 gstring * g = NULL;
b1f8e4f8 908
0f0c8159
JH
909 /* setup default virus name */
910 malware_name = US"unknown";
b1f8e4f8 911
0f0c8159 912 /* set up match regex */
476be7e2
JH
913 if (!drweb_re)
914 drweb_re = m_pcre_compile(drweb_re_str, &errstr);
b1f8e4f8 915
0f0c8159 916 /* read and concatenate virus names into one string */
d7978c0f 917 for (int i = 0; i < drweb_vnum; i++)
4e71661f 918 {
acec9514
JH
919 int ovector[10*3];
920
0f0c8159 921 /* read the size of report */
74f1a423 922 if (!recv_len(malware_daemon_ctx.sock, &drweb_slen, sizeof(drweb_slen), tmo))
ad93c40f 923 return m_panic_defer_3(scanent, CUS callout_address,
74f1a423 924 US"cannot read report size", malware_daemon_ctx.sock);
0f0c8159 925 drweb_slen = ntohl(drweb_slen);
f3ebb786
JH
926
927 /* assume tainted, since it is external input */
928 tmpbuf = store_get(drweb_slen, TRUE);
b1f8e4f8 929
0f0c8159 930 /* read report body */
74f1a423 931 if (!recv_len(malware_daemon_ctx.sock, tmpbuf, drweb_slen, tmo))
ad93c40f 932 return m_panic_defer_3(scanent, CUS callout_address,
74f1a423 933 US"cannot read report string", malware_daemon_ctx.sock);
0f0c8159 934 tmpbuf[drweb_slen] = '\0';
8544e77a 935
0f0c8159
JH
936 /* try matcher on the line, grab substring */
937 result = pcre_exec(drweb_re, NULL, CS tmpbuf, Ustrlen(tmpbuf), 0, 0,
c11d665d 938 ovector, nelem(ovector));
0f0c8159
JH
939 if (result >= 2)
940 {
941 const char * pre_malware_nb;
8e669ac1 942
0f0c8159 943 pcre_get_substring(CS tmpbuf, ovector, result, 1, &pre_malware_nb);
8e669ac1 944
0f0c8159 945 if (i==0) /* the first name we just copy to malware_name */
b2bcdd35 946 g = string_cat(NULL, US pre_malware_nb);
8e669ac1 947
acec9514 948 /*XXX could be string_append_listele? */
0f0c8159 949 else /* concatenate each new virus name to previous */
acec9514 950 g = string_append(g, 2, "/", pre_malware_nb);
8e669ac1 951
0f0c8159
JH
952 pcre_free_substring(pre_malware_nb);
953 }
954 }
acec9514 955 malware_name = string_from_gstring(g);
0f0c8159
JH
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)
ad93c40f 970 return m_panic_defer_3(scanent, CUS callout_address,
0f0c8159 971 string_sprintf("drweb daemon retcode 0x%x (%s)", drweb_rc, drweb_s),
74f1a423 972 malware_daemon_ctx.sock);
8e669ac1 973
0f0c8159 974 /* no virus found */
b1f8e4f8 975 malware_name = NULL;
0f0c8159
JH
976 }
977 break;
978 } /* drweb */
c11d665d 979#endif
8e669ac1 980
c11d665d 981#ifndef DISABLE_MAL_AVE
0f0c8159
JH
982 case M_AVES: /* "aveserver" scanner type -------------------------------- */
983 {
984 uschar buf[32768];
985 int result;
8544e77a 986
0f0c8159
JH
987 /* read aveserver's greeting and see if it is ready (2xx greeting) */
988 buf[0] = 0;
74f1a423 989 recv_line(malware_daemon_ctx.sock, buf, sizeof(buf), tmo);
8544e77a 990
0f0c8159 991 if (buf[0] != '2') /* aveserver is having problems */
ad93c40f 992 return m_panic_defer_3(scanent, CUS callout_address,
0f0c8159 993 string_sprintf("unavailable (Responded: %s).",
5903c6ff 994 ((buf[0] != 0) ? buf : US "nothing") ),
74f1a423 995 malware_daemon_ctx.sock);
8e669ac1 996
0f0c8159
JH
997 /* prepare our command */
998 (void)string_format(buf, sizeof(buf), "SCAN bPQRSTUW %s\r\n",
999 eml_filename);
1000
1001 /* and send it */
e1d04f48 1002 DEBUG(D_acl) debug_printf_indent("Malware scan: issuing %s %s\n",
0f0c8159 1003 scanner_name, buf);
74f1a423 1004 if (m_sock_send(malware_daemon_ctx.sock, buf, Ustrlen(buf), &errstr) < 0)
ad93c40f 1005 return m_panic_defer(scanent, CUS callout_address, errstr);
0f0c8159
JH
1006
1007 malware_name = NULL;
1008 result = 0;
1009 /* read response lines, find malware name and final response */
74f1a423 1010 while (recv_line(malware_daemon_ctx.sock, buf, sizeof(buf), tmo) > 0)
0f0c8159
JH
1011 {
1012 if (buf[0] == '2')
1013 break;
1014 if (buf[0] == '5') /* aveserver is having problems */
4e71661f 1015 {
ad93c40f 1016 result = m_panic_defer(scanent, CUS callout_address,
0f0c8159
JH
1017 string_sprintf("unable to scan file %s (Responded: %s).",
1018 eml_filename, buf));
1019 break;
4e71661f 1020 }
0f0c8159
JH
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
74f1a423 1029 if (m_sock_send(malware_daemon_ctx.sock, US"quit\r\n", 6, &errstr) < 0)
ad93c40f 1030 return m_panic_defer(scanent, CUS callout_address, errstr);
0f0c8159
JH
1031
1032 /* read aveserver's greeting and see if it is ready (2xx greeting) */
1033 buf[0] = 0;
74f1a423 1034 recv_line(malware_daemon_ctx.sock, buf, sizeof(buf), tmo);
0f0c8159
JH
1035
1036 if (buf[0] != '2') /* aveserver is having problems */
ad93c40f 1037 return m_panic_defer_3(scanent, CUS callout_address,
0f0c8159 1038 string_sprintf("unable to quit dialogue (Responded: %s).",
5903c6ff 1039 ((buf[0] != 0) ? buf : US "nothing") ),
74f1a423 1040 malware_daemon_ctx.sock);
0f0c8159
JH
1041
1042 if (result == DEFER)
1043 {
74f1a423 1044 (void)close(malware_daemon_ctx.sock);
0f0c8159
JH
1045 return DEFER;
1046 }
1047 break;
b1f8e4f8 1048 } /* aveserver */
c11d665d 1049#endif
8e669ac1 1050
c11d665d 1051#ifndef DISABLE_MAL_FSECURE
b1f8e4f8
JH
1052 case M_FSEC: /* "fsecure" scanner type ---------------------------------- */
1053 {
d7978c0f 1054 int i, bread = 0;
0f0c8159
JH
1055 uschar * file_name;
1056 uschar av_buffer[1024];
0f0c8159
JH
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
e1d04f48 1064 DEBUG(D_acl) debug_printf_indent("Malware scan: issuing %s scan [%s]\n",
0f0c8159
JH
1065 scanner_name, scanner_options);
1066 /* pass options */
1067 memset(av_buffer, 0, sizeof(av_buffer));
c11d665d 1068 for (i = 0; i != nelem(cmdopt); i++)
0f0c8159 1069 {
b1f8e4f8 1070
74f1a423 1071 if (m_sock_send(malware_daemon_ctx.sock, cmdopt[i], Ustrlen(cmdopt[i]), &errstr) < 0)
ad93c40f 1072 return m_panic_defer(scanent, CUS callout_address, errstr);
b1f8e4f8 1073
0a5441fc 1074 bread = ip_recv(&malware_daemon_ctx, av_buffer, sizeof(av_buffer), tmo);
0f0c8159
JH
1075 if (bread > 0) av_buffer[bread]='\0';
1076 if (bread < 0)
ad93c40f 1077 return m_panic_defer_3(scanent, CUS callout_address,
0f0c8159 1078 string_sprintf("unable to read answer %d (%s)", i, strerror(errno)),
74f1a423 1079 malware_daemon_ctx.sock);
d7978c0f 1080 for (int j = 0; j < bread; j++)
0f0c8159
JH
1081 if (av_buffer[j] == '\r' || av_buffer[j] == '\n')
1082 av_buffer[j] ='@';
1083 }
8e669ac1 1084
0f0c8159
JH
1085 /* pass the mailfile to fsecure */
1086 file_name = string_sprintf("SCAN\t%s\n", eml_filename);
b1f8e4f8 1087
74f1a423 1088 if (m_sock_send(malware_daemon_ctx.sock, file_name, Ustrlen(file_name), &errstr) < 0)
ad93c40f 1089 return m_panic_defer(scanent, CUS callout_address, errstr);
b1f8e4f8 1090
0f0c8159
JH
1091 /* set up match */
1092 /* todo also SUSPICION\t */
476be7e2
JH
1093 if (!fsec_re)
1094 fsec_re = m_pcre_compile(fsec_re_str, &errstr);
b1f8e4f8 1095
0f0c8159
JH
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 */
fd02dc0e 1098 {
0f0c8159
JH
1099 uschar * p = av_buffer;
1100 uschar * q;
fd02dc0e 1101
0f0c8159
JH
1102 for (;;)
1103 {
80591636 1104 errno = ETIMEDOUT;
0f0c8159 1105 i = av_buffer+sizeof(av_buffer)-p;
0a5441fc 1106 if ((bread= ip_recv(&malware_daemon_ctx, p, i-1, tmo)) < 0)
ad93c40f 1107 return m_panic_defer_3(scanent, CUS callout_address,
0f0c8159 1108 string_sprintf("unable to read result (%s)", strerror(errno)),
74f1a423 1109 malware_daemon_ctx.sock);
b1f8e4f8 1110
755762fd 1111 for (p[bread] = '\0'; (q = Ustrchr(p, '\n')); p = q+1)
0f0c8159
JH
1112 {
1113 *q = '\0';
fd02dc0e 1114
0f0c8159
JH
1115 /* Really search for virus again? */
1116 if (!malware_name)
1117 /* try matcher on the line, grab substring */
476be7e2 1118 malware_name = m_pcre_exec(fsec_re, p);
fd02dc0e 1119
0f0c8159
JH
1120 if (Ustrstr(p, "OK\tScan ok."))
1121 goto fsec_found;
1122 }
fd02dc0e 1123
0f0c8159
JH
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;
fd02dc0e 1128 }
b1f8e4f8 1129 }
fd02dc0e
JH
1130
1131 fsec_found:
b1f8e4f8
JH
1132 break;
1133 } /* fsecure */
c11d665d 1134#endif
599dcb77 1135
c11d665d 1136#ifndef DISABLE_MAL_KAV
b1f8e4f8
JH
1137 case M_KAVD: /* "kavdaemon" scanner type -------------------------------- */
1138 {
0f0c8159
JH
1139 time_t t;
1140 uschar tmpbuf[1024];
1141 uschar * scanrequest;
1142 int kav_rc;
2decbec9
RC
1143 unsigned long kav_reportlen;
1144 int bread;
0f0c8159
JH
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
e1d04f48 1162 DEBUG(D_acl) debug_printf_indent("Malware scan: issuing %s scan [%s]\n",
0f0c8159
JH
1163 scanner_name, scanner_options);
1164
1165 /* send scan request */
74f1a423 1166 if (m_sock_send(malware_daemon_ctx.sock, scanrequest, Ustrlen(scanrequest)+1, &errstr) < 0)
ad93c40f 1167 return m_panic_defer(scanent, CUS callout_address, errstr);
b1f8e4f8 1168
0f0c8159 1169 /* wait for result */
74f1a423 1170 if (!recv_len(malware_daemon_ctx.sock, tmpbuf, 2, tmo))
ad93c40f 1171 return m_panic_defer_3(scanent, CUS callout_address,
74f1a423 1172 US"unable to read 2 bytes from socket.", malware_daemon_ctx.sock);
b1f8e4f8 1173
0f0c8159
JH
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 */
ad93c40f 1179 return m_panic_defer_3(scanent, CUS callout_address,
0f0c8159 1180 US"please reconfigure kavdaemon to NOT disinfect or remove infected files.",
74f1a423 1181 malware_daemon_ctx.sock);
0f0c8159 1182 case 1:
ad93c40f 1183 return m_panic_defer_3(scanent, CUS callout_address,
74f1a423 1184 US"reported 'scanning not completed' (code 1).", malware_daemon_ctx.sock);
0f0c8159 1185 case 7:
ad93c40f 1186 return m_panic_defer_3(scanent, CUS callout_address,
74f1a423 1187 US"reported 'kavdaemon damaged' (code 7).", malware_daemon_ctx.sock);
0f0c8159 1188 }
b1f8e4f8 1189
4c04137d 1190 /* code 8 is not handled, since it is ambiguous. It appears mostly on
0f0c8159 1191 bounces where part of a file has been cut off */
b1f8e4f8 1192
0f0c8159
JH
1193 /* "virus found" return codes (2-4) */
1194 if (kav_rc > 1 && kav_rc < 5)
b1f8e4f8 1195 {
0f0c8159 1196 int report_flag = 0;
599dcb77 1197
0f0c8159
JH
1198 /* setup default virus name */
1199 malware_name = US"unknown";
b1f8e4f8 1200
0f0c8159 1201 report_flag = tmpbuf[ test_byte_order() == LITTLE_MY_ENDIAN ? 1 : 0 ];
b1f8e4f8 1202
0f0c8159
JH
1203 /* read the report, if available */
1204 if (report_flag == 1)
1205 {
1206 /* read report size */
74f1a423 1207 if (!recv_len(malware_daemon_ctx.sock, &kav_reportlen, 4, tmo))
ad93c40f 1208 return m_panic_defer_3(scanent, CUS callout_address,
74f1a423 1209 US"cannot read report size", malware_daemon_ctx.sock);
b1f8e4f8 1210
0f0c8159
JH
1211 /* it's possible that avp returns av_buffer[1] == 1 but the
1212 reportsize is 0 (!?) */
1213 if (kav_reportlen > 0)
4e71661f 1214 {
0f0c8159 1215 /* set up match regex, depends on retcode */
476be7e2
JH
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 }
0f0c8159 1226
6e92b3ae
JH
1227 /* read report, linewise. Using size from stream to read amount of data
1228 from same stream is safe enough. */
1229 /* coverity[tainted_data] */
0f0c8159 1230 while (kav_reportlen > 0)
4e71661f 1231 {
74f1a423 1232 if ((bread = recv_line(malware_daemon_ctx.sock, tmpbuf, sizeof(tmpbuf), tmo)) < 0)
0f0c8159
JH
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;
b1f8e4f8
JH
1239 }
1240 }
1241 }
0f0c8159
JH
1242 }
1243 else /* no virus found */
1244 malware_name = NULL;
599dcb77 1245
0f0c8159 1246 break;
599dcb77 1247 }
c11d665d 1248#endif
8544e77a 1249
c11d665d 1250#ifndef DISABLE_MAL_CMDLINE
b1f8e4f8
JH
1251 case M_CMDL: /* "cmdline" scanner type ---------------------------------- */
1252 {
0f0c8159
JH
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)
ad93c40f 1269 return m_panic_defer(scanent, NULL, errstr);
b1f8e4f8 1270
0f0c8159
JH
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)
ad93c40f 1275 return m_panic_defer(scanent, NULL, errstr);
b1f8e4f8 1276
0f0c8159
JH
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)
ad93c40f 1281 return m_panic_defer(scanent, NULL, errstr);
b1f8e4f8 1282
0f0c8159
JH
1283 /* prepare scanner call; despite the naming, file_name holds a directory
1284 name which is documented as the value given to %s. */
b1f8e4f8 1285
0f0c8159
JH
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);
b1f8e4f8 1291
0f0c8159
JH
1292 /* redirect STDERR too */
1293 commandline = string_sprintf("%s 2>&1", commandline);
b1f8e4f8 1294
e1d04f48 1295 DEBUG(D_acl) debug_printf_indent("Malware scan: issuing %s scan [%s]\n",
0f0c8159 1296 scanner_name, commandline);
b1f8e4f8 1297
0f0c8159
JH
1298 /* store exims signal handlers */
1299 eximsigchld = signal(SIGCHLD,SIG_DFL);
1300 eximsigpipe = signal(SIGPIPE,SIG_DFL);
b1f8e4f8 1301
0f0c8159
JH
1302 if (!(scanner_out = popen(CS commandline,"r")))
1303 {
1304 int err = errno;
1305 signal(SIGCHLD,eximsigchld); signal(SIGPIPE,eximsigpipe);
ad93c40f 1306 return m_panic_defer(scanent, NULL,
0f0c8159
JH
1307 string_sprintf("call (%s) failed: %s.", commandline, strerror(err)));
1308 }
1309 scanner_fd = fileno(scanner_out);
94a18f28 1310
040721f2 1311 file_name = string_sprintf("%s/%s_scanner_output", eml_dir, message_id);
b1f8e4f8 1312
0f0c8159
JH
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);
ad93c40f 1318 return m_panic_defer(scanent, NULL, string_sprintf(
0f0c8159
JH
1319 "opening scanner output file (%s) failed: %s.",
1320 file_name, strerror(err)));
1321 }
8e669ac1 1322
0f0c8159
JH
1323 /* look for trigger while recording output */
1324 while ((rcnt = recv_line(scanner_fd, linebuffer,
1325 sizeof(linebuffer), tmo)))
1326 {
1327 if (rcnt < 0)
93a6fce2
JH
1328 {
1329 int err = errno;
0f0c8159
JH
1330 if (rcnt == -1)
1331 break;
93a6fce2
JH
1332 (void) pclose(scanner_out);
1333 signal(SIGCHLD,eximsigchld); signal(SIGPIPE,eximsigpipe);
ad93c40f 1334 return m_panic_defer(scanent, NULL, string_sprintf(
93a6fce2
JH
1335 "unable to read from scanner (%s): %s",
1336 commandline, strerror(err)));
1337 }
4e71661f 1338
0f0c8159
JH
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);
ad93c40f 1344 return m_panic_defer(scanent, NULL, string_sprintf(
0f0c8159 1345 "short write on scanner output file (%s).", file_name));
4e71661f 1346 }
0f0c8159
JH
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 }
8e669ac1 1354
0f0c8159
JH
1355 (void)fclose(scanner_record);
1356 sep = pclose(scanner_out);
1357 signal(SIGCHLD,eximsigchld); signal(SIGPIPE,eximsigpipe);
1358 if (sep != 0)
ad93c40f 1359 return m_panic_defer(scanent, NULL,
0f0c8159
JH
1360 sep == -1
1361 ? string_sprintf("running scanner failed: %s", strerror(sep))
1362 : string_sprintf("scanner returned error code: %d", sep));
b1f8e4f8 1363
0f0c8159
JH
1364 if (trigger)
1365 {
1366 uschar * s;
1367 /* setup default virus name */
1368 malware_name = US"unknown";
b1f8e4f8 1369
0f0c8159 1370 /* re-open the scanner output file, look for name match */
9e21ce8f
JH
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 */
0f0c8159 1374 malware_name = s;
0f0c8159
JH
1375 (void)fclose(scanner_record);
1376 }
1377 else /* no virus found */
1378 malware_name = NULL;
1379 break;
b1f8e4f8 1380 } /* cmdline */
c11d665d 1381#endif
599dcb77 1382
c11d665d 1383#ifndef DISABLE_MAL_SOPHIE
b1f8e4f8
JH
1384 case M_SOPHIE: /* "sophie" scanner type --------------------------------- */
1385 {
0f0c8159
JH
1386 int bread = 0;
1387 uschar *p;
1388 uschar * file_name;
1389 uschar av_buffer[1024];
b1f8e4f8 1390
0f0c8159
JH
1391 /* pass the scan directory to sophie */
1392 file_name = string_copy(eml_filename);
1393 if ((p = Ustrrchr(file_name, '/')))
1394 *p = '\0';
b1f8e4f8 1395
e1d04f48 1396 DEBUG(D_acl) debug_printf_indent("Malware scan: issuing %s scan [%s]\n",
0f0c8159 1397 scanner_name, scanner_options);
b1f8e4f8 1398
74f1a423
JH
1399 if ( write(malware_daemon_ctx.sock, file_name, Ustrlen(file_name)) < 0
1400 || write(malware_daemon_ctx.sock, "\n", 1) != 1
0f0c8159 1401 )
ad93c40f 1402 return m_panic_defer_3(scanent, CUS callout_address,
0f0c8159 1403 string_sprintf("unable to write to UNIX socket (%s)", scanner_options),
74f1a423 1404 malware_daemon_ctx.sock);
b1f8e4f8 1405
0f0c8159
JH
1406 /* wait for result */
1407 memset(av_buffer, 0, sizeof(av_buffer));
0a5441fc 1408 if ((bread = ip_recv(&malware_daemon_ctx, av_buffer, sizeof(av_buffer), tmo)) <= 0)
ad93c40f 1409 return m_panic_defer_3(scanent, CUS callout_address,
0f0c8159 1410 string_sprintf("unable to read from UNIX socket (%s)", scanner_options),
74f1a423 1411 malware_daemon_ctx.sock);
0f0c8159
JH
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))
ad93c40f 1421 return m_panic_defer_3(scanent, CUS callout_address,
74f1a423 1422 US"scanner reported error", malware_daemon_ctx.sock);
0f0c8159
JH
1423 else /* all ok, no virus */
1424 malware_name = NULL;
599dcb77 1425
0f0c8159 1426 break;
8544e77a 1427 }
c11d665d 1428#endif
8e669ac1 1429
c11d665d 1430#ifndef DISABLE_MAL_CLAM
b1f8e4f8
JH
1431 case M_CLAMD: /* "clamd" scanner type ----------------------------------- */
1432 {
0f0c8159
JH
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
e5ba8aa7 1441* the TCP-connected daemon is actually local; otherwise we use zINSTREAM
0f0c8159
JH
1442* See Exim bug 926 for details. */
1443
80591636 1444 uschar *p, *vname, *result_tag;
0f0c8159 1445 int bread=0;
0f0c8159
JH
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;
8a512ed5 1454 clamd_address * cv[MAX_CLAMD_SERVERS];
0f0c8159 1455 int num_servers = 0;
0f0c8159 1456 uint32_t send_size, send_final_zeroblock;
4a5cbaff 1457 blob cmd_str;
0f0c8159 1458
8a512ed5
JH
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
0f0c8159 1462 if (*scanner_options == '/')
8a512ed5
JH
1463 {
1464 clamd_address * cd;
1465 const uschar * sublist;
1466 int subsep = ' ';
1467
0f0c8159
JH
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;
f3ebb786 1471 cd = (clamd_address *) store_get(sizeof(clamd_address), FALSE);
8a512ed5
JH
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)
ad93c40f 1479 return m_panic_defer(scanent, NULL,
8a512ed5
JH
1480 string_sprintf("bad option '%s'", scanner_options));
1481 cv[0] = cd;
1482 }
0f0c8159
JH
1483 else
1484 {
0f0c8159
JH
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
4e71661f 1490 {
8a512ed5
JH
1491 clamd_address * cd;
1492 const uschar * sublist;
1493 int subsep = ' ';
1494 uschar * s;
b1f8e4f8 1495
0f0c8159
JH
1496 /* The 'local' option means use the SCAN command over the network
1497 * socket (ie common file storage in use) */
8a512ed5
JH
1498 /*XXX we could accept this also as a local option? */
1499 if (strcmpic(scanner_options, US"local") == 0)
0f0c8159
JH
1500 {
1501 use_scan_command = TRUE;
1502 continue;
1503 }
b1f8e4f8 1504
f3ebb786 1505 cd = (clamd_address *) store_get(sizeof(clamd_address), FALSE);
b1f8e4f8 1506
0f0c8159 1507 /* extract host and port part */
8a512ed5
JH
1508 sublist = scanner_options;
1509 if (!(cd->hostspec = string_nextinlist(&sublist, &subsep, NULL, 0)))
1510 {
ad93c40f 1511 (void) m_panic_defer(scanent, NULL,
8a512ed5
JH
1512 string_sprintf("missing address: '%s'", scanner_options));
1513 continue;
1514 }
1515 if (!(s = string_nextinlist(&sublist, &subsep, NULL, 0)))
0f0c8159 1516 {
ad93c40f 1517 (void) m_panic_defer(scanent, NULL,
8a512ed5
JH
1518 string_sprintf("missing port: '%s'", scanner_options));
1519 continue;
1520 }
93a6fce2 1521 cd->tcp_port = atoi(CS s);
8a512ed5
JH
1522
1523 /* parse options */
1524 /*XXX should these options be common over scanner types? */
1525 if (clamd_option(cd, sublist, &subsep) != OK)
ad93c40f 1526 return m_panic_defer(scanent, NULL,
8a512ed5 1527 string_sprintf("bad option '%s'", scanner_options));
b1f8e4f8 1528
8a512ed5 1529 cv[num_servers++] = cd;
0f0c8159
JH
1530 if (num_servers >= MAX_CLAMD_SERVERS)
1531 {
ad93c40f 1532 (void) m_panic_defer(scanent, NULL,
0f0c8159
JH
1533 US"More than " MAX_CLAMD_SERVERS_S " clamd servers "
1534 "specified; only using the first " MAX_CLAMD_SERVERS_S );
1535 break;
1536 }
8a512ed5
JH
1537 } while ((scanner_options = string_nextinlist(&av_scanner_work, &sep,
1538 NULL, 0)));
8e669ac1 1539
0f0c8159
JH
1540 /* check if we have at least one server */
1541 if (!num_servers)
ad93c40f 1542 return m_panic_defer(scanent, NULL,
0f0c8159
JH
1543 US"no useable server addresses in malware configuration option.");
1544 }
b1f8e4f8 1545
0f0c8159
JH
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, ':'))
ad93c40f 1549 return m_panic_defer(scanent, NULL,
0f0c8159
JH
1550 string_sprintf("local/SCAN mode incompatible with" \
1551 " : in path to email filename [%s]", eml_filename));
b1f8e4f8 1552
4a5cbaff
JH
1553 /* Set up the very first data we will be sending */
1554 if (!use_scan_command)
4a5cbaff 1555 { cmd_str.data = US"zINSTREAM"; cmd_str.len = 10; }
4a5cbaff
JH
1556 else
1557 {
1558 cmd_str.data = string_sprintf("SCAN %s\n", eml_filename);
1559 cmd_str.len = Ustrlen(cmd_str.data);
1560 }
1561
0f0c8159
JH
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. */
b1f8e4f8 1568
0f0c8159
JH
1569 while (num_servers > 0)
1570 {
4a5cbaff 1571 int i = random_number(num_servers);
8a512ed5 1572 clamd_address * cd = cv[i];
b1f8e4f8 1573
e1d04f48 1574 DEBUG(D_acl) debug_printf_indent("trying server name %s, port %u\n",
8a512ed5 1575 cd->hostspec, cd->tcp_port);
0f0c8159
JH
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) */
8a512ed5 1579 for (;;)
0f0c8159 1580 {
b536a578 1581 /*XXX we trust that the cmd_str is ideempotent */
74f1a423 1582 if ((malware_daemon_ctx.sock = m_tcpsocket(cd->hostspec, cd->tcp_port,
4a5cbaff 1583 &connhost, &errstr, &cmd_str)) >= 0)
8a512ed5
JH
1584 {
1585 /* Connection successfully established with a server */
1586 hostname = cd->hostspec;
4a5cbaff 1587 cmd_str.len = 0;
8a512ed5
JH
1588 break;
1589 }
1590 if (cd->retry <= 0) break;
1591 while (cd->retry > 0) cd->retry = sleep(cd->retry);
4e71661f 1592 }
74f1a423 1593 if (malware_daemon_ctx.sock >= 0)
8a512ed5 1594 break;
b1f8e4f8 1595
ad93c40f 1596 (void) m_panic_defer(scanent, CUS callout_address, errstr);
0f0c8159
JH
1597
1598 /* Remove the server from the list. XXX We should free the memory */
1599 num_servers--;
8a512ed5
JH
1600 for (; i < num_servers; i++)
1601 cv[i] = cv[i+1];
4e71661f 1602 }
8e669ac1 1603
0f0c8159 1604 if (num_servers == 0)
ad93c40f 1605 return m_panic_defer(scanent, NULL, US"all servers failed");
0f0c8159
JH
1606 }
1607 else
8a512ed5
JH
1608 for (;;)
1609 {
74f1a423 1610 if ((malware_daemon_ctx.sock = ip_unixsocket(cv[0]->hostspec, &errstr)) >= 0)
8a512ed5
JH
1611 {
1612 hostname = cv[0]->hostspec;
1613 break;
1614 }
1615 if (cv[0]->retry <= 0)
ad93c40f 1616 return m_panic_defer(scanent, CUS callout_address, errstr);
8a512ed5
JH
1617 while (cv[0]->retry > 0) cv[0]->retry = sleep(cv[0]->retry);
1618 }
b1f8e4f8 1619
0f0c8159
JH
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 {
0f0c8159
JH
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. */
b1f8e4f8 1630
e1d04f48 1631 DEBUG(D_acl) debug_printf_indent(
0f0c8159
JH
1632 "Malware scan: issuing %s new-style remote scan (zINSTREAM)\n",
1633 scanner_name);
b1f8e4f8 1634
4a5cbaff
JH
1635 /* Pass the string to ClamAV (10 = "zINSTREAM\0"), if not already sent */
1636 if (cmd_str.len)
74f1a423 1637 if (send(malware_daemon_ctx.sock, cmd_str.data, cmd_str.len, 0) < 0)
ad93c40f 1638 return m_panic_defer_3(scanent, CUS hostname,
4a5cbaff
JH
1639 string_sprintf("unable to send zINSTREAM to socket (%s)",
1640 strerror(errno)),
74f1a423 1641 malware_daemon_ctx.sock);
b1f8e4f8 1642
0f0c8159 1643 /* calc file size */
9e21ce8f 1644 if ((clam_fd = exim_open2(CS eml_filename, O_RDONLY)) < 0)
0f0c8159
JH
1645 {
1646 int err = errno;
ad93c40f 1647 return m_panic_defer_3(scanent, NULL,
0f0c8159
JH
1648 string_sprintf("can't open spool file %s: %s",
1649 eml_filename, strerror(err)),
74f1a423 1650 malware_daemon_ctx.sock);
0f0c8159
JH
1651 }
1652 if ((fsize = lseek(clam_fd, 0, SEEK_END)) < 0)
1653 {
d315eda1
JH
1654 int err;
1655b_seek: err = errno;
e5ba8aa7 1656 (void)close(clam_fd);
ad93c40f 1657 return m_panic_defer_3(scanent, NULL,
0f0c8159
JH
1658 string_sprintf("can't seek spool file %s: %s",
1659 eml_filename, strerror(err)),
74f1a423 1660 malware_daemon_ctx.sock);
0f0c8159
JH
1661 }
1662 fsize_uint = (unsigned int) fsize;
1663 if ((off_t)fsize_uint != fsize)
1664 {
e5ba8aa7 1665 (void)close(clam_fd);
ad93c40f 1666 return m_panic_defer_3(scanent, NULL,
0f0c8159
JH
1667 string_sprintf("seeking spool file %s, size overflow",
1668 eml_filename),
74f1a423 1669 malware_daemon_ctx.sock);
0f0c8159 1670 }
d315eda1
JH
1671 if (lseek(clam_fd, 0, SEEK_SET) < 0)
1672 goto b_seek;
b1f8e4f8 1673
f3ebb786 1674 if (!(clamav_fbuf = store_malloc(fsize_uint)))
0f0c8159 1675 {
e5ba8aa7 1676 (void)close(clam_fd);
ad93c40f 1677 return m_panic_defer_3(scanent, NULL,
0f0c8159
JH
1678 string_sprintf("unable to allocate memory %u for file (%s)",
1679 fsize_uint, eml_filename),
74f1a423 1680 malware_daemon_ctx.sock);
0f0c8159 1681 }
b1f8e4f8 1682
0f0c8159
JH
1683 if ((result = read(clam_fd, clamav_fbuf, fsize_uint)) < 0)
1684 {
1685 int err = errno;
f3ebb786 1686 store_free(clamav_fbuf); (void)close(clam_fd);
ad93c40f 1687 return m_panic_defer_3(scanent, NULL,
0f0c8159
JH
1688 string_sprintf("can't read spool file %s: %s",
1689 eml_filename, strerror(err)),
74f1a423 1690 malware_daemon_ctx.sock);
0f0c8159
JH
1691 }
1692 (void)close(clam_fd);
b1f8e4f8 1693
0f0c8159 1694 /* send file body to socket */
0f0c8159
JH
1695 send_size = htonl(fsize_uint);
1696 send_final_zeroblock = 0;
74f1a423
JH
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))
0f0c8159 1700 {
f3ebb786 1701 store_free(clamav_fbuf);
ad93c40f 1702 return m_panic_defer_3(scanent, NULL,
0f0c8159 1703 string_sprintf("unable to send file body to socket (%s)", hostname),
74f1a423 1704 malware_daemon_ctx.sock);
4e71661f 1705 }
f3ebb786 1706 store_free(clamav_fbuf);
0f0c8159
JH
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. */
4a5cbaff 1723 /* Pass the string to ClamAV (7 = "SCAN \n" + \0), if not already sent */
0f0c8159 1724
e1d04f48 1725 DEBUG(D_acl) debug_printf_indent(
0f0c8159
JH
1726 "Malware scan: issuing %s local-path scan [%s]\n",
1727 scanner_name, scanner_options);
b1f8e4f8 1728
4a5cbaff 1729 if (cmd_str.len)
74f1a423 1730 if (send(malware_daemon_ctx.sock, cmd_str.data, cmd_str.len, 0) < 0)
ad93c40f 1731 return m_panic_defer_3(scanent, CUS callout_address,
4a5cbaff 1732 string_sprintf("unable to write to socket (%s)", strerror(errno)),
74f1a423 1733 malware_daemon_ctx.sock);
b1f8e4f8 1734
0f0c8159
JH
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));
0a5441fc 1743 bread = ip_recv(&malware_daemon_ctx, av_buffer, sizeof(av_buffer), tmo);
74f1a423
JH
1744 (void)close(malware_daemon_ctx.sock);
1745 malware_daemon_ctx.sock = -1;
1746 malware_daemon_ctx.tls_ctx = NULL;
0f0c8159
JH
1747
1748 if (bread <= 0)
ad93c40f 1749 return m_panic_defer(scanent, CUS callout_address,
0f0c8159
JH
1750 string_sprintf("unable to read from socket (%s)",
1751 errno == 0 ? "EOF" : strerror(errno)));
1752
1753 if (bread == sizeof(av_buffer))
ad93c40f 1754 return m_panic_defer(scanent, CUS callout_address,
0ea02355 1755 US"buffer too small");
0f0c8159
JH
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))
ad93c40f 1779 return m_panic_defer(scanent, CUS callout_address,
0ea02355 1780 US"ClamAV returned null");
0f0c8159
JH
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
e1d04f48 1788 DEBUG(D_acl) debug_printf_indent("Malware response: %s\n", av_buffer);
0f0c8159
JH
1789
1790 while (isspace(*--p) && (p > av_buffer))
1791 *p = '\0';
1792 if (*p) ++p;
0f0c8159
JH
1793
1794 /* colon in returned output? */
80591636 1795 if(!(p = Ustrchr(av_buffer,':')))
ad93c40f 1796 return m_panic_defer(scanent, CUS callout_address, string_sprintf(
0f0c8159
JH
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 == ')')
4e71661f 1817 {
0f0c8159
JH
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';
4e71661f 1823 }
0f0c8159 1824 malware_name = string_copy(vname);
e1d04f48 1825 DEBUG(D_acl) debug_printf_indent("Malware found, name \"%s\"\n", malware_name);
b1f8e4f8 1826
0f0c8159
JH
1827 }
1828 else if (Ustrcmp(result_tag, "ERROR") == 0)
ad93c40f 1829 return m_panic_defer(scanent, CUS callout_address,
0f0c8159 1830 string_sprintf("ClamAV returned: %s", av_buffer));
b1f8e4f8 1831
0f0c8159
JH
1832 else if (Ustrcmp(result_tag, "OK") == 0)
1833 {
1834 /* Everything should be OK */
1835 malware_name = NULL;
e1d04f48 1836 DEBUG(D_acl) debug_printf_indent("Malware not found\n");
b1f8e4f8 1837
0f0c8159
JH
1838 }
1839 else
ad93c40f 1840 return m_panic_defer(scanent, CUS callout_address,
0f0c8159
JH
1841 string_sprintf("unparseable response from ClamAV: {%s}", av_buffer));
1842
1843 break;
b1f8e4f8 1844 } /* clamd */
c11d665d 1845#endif
b1f8e4f8 1846
c11d665d 1847#ifndef DISABLE_MAL_SOCK
b1f8e4f8 1848 case M_SOCK: /* "sock" scanner type ------------------------------------- */
0f0c8159
JH
1849 /* This code was derived by Martin Poole from the clamd code contributed
1850 by David Saez and the cmdline code
1851 */
b1f8e4f8 1852 {
0f0c8159
JH
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 */
ac4d558b
JH
1863 if ( (sockline_scanner = string_nextinlist(&av_scanner_work, &sep,
1864 NULL, 0))
1865 && *sockline_scanner
1866 )
0f0c8159
JH
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, '%'))
ad93c40f 1871 return m_panic_defer_3(scanent, NULL,
74f1a423 1872 US"unsafe sock scanner call spec", malware_daemon_ctx.sock);
0f0c8159
JH
1873 }
1874 else
1875 sockline_scanner = sockline_scanner_default;
ac4d558b
JH
1876 DEBUG(D_acl) debug_printf_indent("%15s%10s'%s'\n", "", "cmdline: ",
1877 string_printing(sockline_scanner));
b1f8e4f8 1878
0f0c8159
JH
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)
74f1a423 1883 return m_panic_defer_3(scanent, NULL, errstr, malware_daemon_ctx.sock);
b1f8e4f8 1884
0f0c8159
JH
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)
74f1a423 1889 return m_panic_defer_3(scanent, NULL, errstr, malware_daemon_ctx.sock);
b1f8e4f8 1890
0f0c8159 1891 /* prepare scanner call - security depends on expansions check above */
040721f2 1892 commandline = string_sprintf( CS sockline_scanner, CS eml_filename);
ac4d558b
JH
1893 DEBUG(D_acl) debug_printf_indent("%15s%10s'%s'\n", "", "expanded: ",
1894 string_printing(commandline));
b1f8e4f8 1895
0f0c8159 1896 /* Pass the command string to the socket */
74f1a423 1897 if (m_sock_send(malware_daemon_ctx.sock, commandline, Ustrlen(commandline), &errstr) < 0)
ad93c40f 1898 return m_panic_defer(scanent, CUS callout_address, errstr);
b1f8e4f8 1899
0f0c8159 1900 /* Read the result */
0a5441fc 1901 bread = ip_recv(&malware_daemon_ctx, av_buffer, sizeof(av_buffer), tmo);
b1f8e4f8 1902
0f0c8159 1903 if (bread <= 0)
ad93c40f 1904 return m_panic_defer_3(scanent, CUS callout_address,
0f0c8159 1905 string_sprintf("unable to read from socket (%s)", strerror(errno)),
74f1a423 1906 malware_daemon_ctx.sock);
b1f8e4f8 1907
0f0c8159 1908 if (bread == sizeof(av_buffer))
ad93c40f 1909 return m_panic_defer_3(scanent, CUS callout_address,
74f1a423 1910 US"buffer too small", malware_daemon_ctx.sock);
0f0c8159
JH
1911 av_buffer[bread] = '\0';
1912 linebuffer = string_copy(av_buffer);
ac4d558b
JH
1913 DEBUG(D_acl) debug_printf_indent("%15s%10s'%s'\n", "", "answer: ",
1914 string_printing(linebuffer));
b1f8e4f8 1915
0f0c8159
JH
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";
ac4d558b
JH
1921 DEBUG(D_acl) debug_printf_indent("%15s%10s'%s'\n", "", "name: ",
1922 string_printing(malware_name));
b1f8e4f8 1923 }
0f0c8159
JH
1924 else /* no virus found */
1925 malware_name = NULL;
1926 break;
8523533c 1927 }
c11d665d 1928#endif
8e669ac1 1929
c11d665d 1930#ifndef DISABLE_MAL_MKS
b1f8e4f8
JH
1931 case M_MKSD: /* "mksd" scanner type ------------------------------------- */
1932 {
0f0c8159
JH
1933 char *mksd_options_end;
1934 int mksd_maxproc = 1; /* default, if no option supplied */
1935 int retval;
b1f8e4f8 1936
0f0c8159
JH
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 )
ad93c40f 1945 return m_panic_defer(scanent, CUS callout_address,
0f0c8159
JH
1946 string_sprintf("invalid option '%s'", scanner_options));
1947 }
8e669ac1 1948
74f1a423 1949 if((malware_daemon_ctx.sock = ip_unixsocket(US "/var/run/mksd/socket", &errstr)) < 0)
ad93c40f 1950 return m_panic_defer(scanent, CUS callout_address, errstr);
8e669ac1 1951
0f0c8159 1952 malware_name = NULL;
94a18f28 1953
e1d04f48 1954 DEBUG(D_acl) debug_printf_indent("Malware scan: issuing %s scan\n", scanner_name);
94a18f28 1955
74f1a423 1956 if ((retval = mksd_scan_packed(scanent, malware_daemon_ctx.sock, eml_filename, tmo)) != OK)
0f0c8159 1957 {
74f1a423 1958 close (malware_daemon_ctx.sock);
0f0c8159
JH
1959 return retval;
1960 }
1961 break;
8523533c 1962 }
c11d665d 1963#endif
0f0c8159 1964
c11d665d 1965#ifndef DISABLE_MAL_AVAST
b6fbf22d
JH
1966 case M_AVAST: /* "avast" scanner type ----------------------------------- */
1967 {
b6fbf22d
JH
1968 uschar buf[1024];
1969 uschar * scanrequest;
b6fbf22d 1970 enum {AVA_HELO, AVA_OPT, AVA_RSP, AVA_DONE} avast_stage;
0f0c8159 1971 int nread;
ad93c40f 1972 uschar * error_message = NULL;
4fe93c4b
HSHR
1973 BOOL more_data = FALSE;
1974 BOOL strict = TRUE;
b6fbf22d
JH
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
aded2255 1983 [E] - some error occurred
71bb51e0
HSHR
1984 Such marker follows the first non-escaped TAB. For more information
1985 see avast-protocol(5)
ad93c40f
HSHR
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
476be7e2
JH
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)))
ad93c40f
HSHR
2020 || ( !ava_re_error
2021 && !(ava_re_error = m_pcre_compile(ava_re_error_str, &errstr)))
b6fbf22d 2022 )
ad93c40f 2023 return malware_panic_defer(errstr);
b6fbf22d
JH
2024
2025 /* wait for result */
0f0c8159 2026 for (avast_stage = AVA_HELO;
74f1a423 2027 (nread = recv_line(malware_daemon_ctx.sock, buf, sizeof(buf), tmo)) > 0;
0f0c8159 2028 )
b6fbf22d
JH
2029 {
2030 int slen = Ustrlen(buf);
94431adb 2031 if (slen >= 1)
b6fbf22d 2032 {
71bb51e0
HSHR
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
b6fbf22d
JH
2042 switch (avast_stage)
2043 {
2044 case AVA_HELO:
71bb51e0 2045 if (more_data) continue;
b6fbf22d
JH
2046 if (Ustrncmp(buf, "220", 3) != 0)
2047 goto endloop; /* require a 220 */
2048 goto sendreq;
2049
2050 case AVA_OPT:
71bb51e0 2051 if (more_data) continue;
b6fbf22d
JH
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 {
eb445b04
HSHR
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 }
b6fbf22d
JH
2068 scanrequest = string_sprintf("%s\n", scanrequest);
2069 avast_stage = AVA_OPT; /* just sent option */
71bb51e0 2070 DEBUG(D_acl) debug_printf_indent("send to avast OPTION: %s", scanrequest);
b6fbf22d
JH
2071 }
2072 else
2073 {
040721f2 2074 scanrequest = string_sprintf("SCAN %s\n", eml_dir);
b6fbf22d 2075 avast_stage = AVA_RSP; /* just sent command */
71bb51e0 2076 DEBUG(D_acl) debug_printf_indent("send to avast REQUEST: SCAN %s\n", eml_dir);
b6fbf22d
JH
2077 }
2078
2079 /* send config-cmd or scan-request to socket */
2080 len = Ustrlen(scanrequest);
74f1a423 2081 if (send(malware_daemon_ctx.sock, scanrequest, len, 0) == -1)
b6fbf22d
JH
2082 {
2083 scanrequest[len-1] = '\0';
ad93c40f 2084 return m_panic_defer_3(scanent, CUS callout_address, string_sprintf(
b6fbf22d 2085 "unable to send request '%s' to socket (%s): %s",
74f1a423 2086 scanrequest, scanner_options, strerror(errno)), malware_daemon_ctx.sock);
b6fbf22d
JH
2087 }
2088 break;
2089 }
2090
2091 case AVA_RSP:
71bb51e0 2092
91e974f5 2093 if (isdigit(buf[0])) /* We're done */
71bb51e0 2094 goto endloop;
71bb51e0 2095
91e974f5 2096 if (malware_name) /* Nothing else matters, just read on */
ad93c40f 2097 break;
b6fbf22d 2098
91e974f5 2099 if (pcre_exec(ava_re_clean, NULL, CS buf, slen, 0, 0, NULL, 0) == 0)
b6fbf22d
JH
2100 break;
2101
4fe93c4b 2102 if ((malware_name = m_pcre_exec(ava_re_virus, buf)))
ad93c40f
HSHR
2103 {
2104 unescape(malware_name);
2105 DEBUG(D_acl)
2106 debug_printf_indent("unescaped malware name: '%s'\n", malware_name);
2107 break;
2108 }
2109
91e974f5 2110 if (strict) /* treat scanner errors as malware */
ad93c40f 2111 {
4fe93c4b 2112 if ((malware_name = m_pcre_exec(ava_re_error, buf)))
91e974f5
HSHR
2113 {
2114 unescape(malware_name);
2115 DEBUG(D_acl)
2116 debug_printf_indent("unescaped error message: '%s'\n", malware_name);
2117 break;
2118 }
ad93c40f 2119 }
91e974f5 2120 else if (pcre_exec(ava_re_error, NULL, CS buf, slen, 0, 0, NULL, 0) == 0)
eb445b04
HSHR
2121 {
2122 log_write(0, LOG_MAIN, "internal scanner error (ignored): %s", buf);
2123 break;
2124 }
2125
83d2a861 2126 /* here also for any unexpected response from the scanner */
eb445b04 2127 DEBUG(D_acl) debug_printf("avast response not handled: '%s'\n", buf);
91e974f5 2128
b6fbf22d 2129 goto endloop;
755762fd 2130
83d2a861 2131 default: log_write(0, LOG_PANIC, "%s:%d:%s: should not happen",
755762fd 2132 __FILE__, __LINE__, __FUNCTION__);
b6fbf22d 2133 }
0f0c8159 2134 }
b6fbf22d 2135 }
91e974f5 2136
b6fbf22d
JH
2137 endloop:
2138
46682a67
JH
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";
91e974f5 2142 else if (buf[0] != '2') error_message = buf;
ad93c40f 2143
91e974f5 2144 DEBUG(D_acl) debug_printf_indent("sent to avast QUIT\n");
74f1a423 2145 if (send(malware_daemon_ctx.sock, "QUIT\n", 5, 0) == -1)
91e974f5
HSHR
2146 return m_panic_defer_3(scanent, CUS callout_address,
2147 string_sprintf("unable to send quit request to socket (%s): %s",
74f1a423 2148 scanner_options, strerror(errno)), malware_daemon_ctx.sock);
91e974f5
HSHR
2149
2150 if (error_message)
74f1a423 2151 return m_panic_defer_3(scanent, CUS callout_address, error_message, malware_daemon_ctx.sock);
ad93c40f 2152
b6fbf22d 2153 }
c11d665d 2154#endif
0f0c8159 2155 } /* scanner type switch */
8523533c 2156
74f1a423
JH
2157 if (malware_daemon_ctx.sock >= 0)
2158 (void) close (malware_daemon_ctx.sock);
0f0c8159 2159 malware_ok = TRUE; /* set "been here, done that" marker */
b1f8e4f8 2160 }
8523533c 2161
0f0c8159
JH
2162/* match virus name against pattern (caseless ------->----------v) */
2163if (malware_name && regex_match_and_setup(re, malware_name, 0, -1))
2164 {
e1d04f48 2165 DEBUG(D_acl) debug_printf_indent(
0f0c8159
JH
2166 "Matched regex to malware [%s] [%s]\n", malware_re, malware_name);
2167 return OK;
2168 }
2169else
2170 return FAIL;
8523533c
TK
2171}
2172
2173
0f0c8159
JH
2174/*************************************************
2175* Scan an email for malware *
2176*************************************************/
8523533c 2177
0f0c8159
JH
2178/* This is the normal interface for scanning an email, which doesn't need a
2179filename; it's a wrapper around the malware_file function.
8523533c 2180
0f0c8159
JH
2181Arguments:
2182 malware_re match condition for "malware="
2183 timeout if nonzero, timeout in seconds
2184
2185Returns: Exim message processing code (OK, FAIL, DEFER, ...)
2186 where true means malware was found (condition applies)
2187*/
2188int
2189malware(const uschar * malware_re, int timeout)
8523533c 2190{
040721f2 2191int ret = malware_internal(malware_re, NULL, timeout);
8e669ac1 2192
55240832 2193if (ret == DEFER) av_failed = TRUE;
55240832 2194return ret;
8523533c
TK
2195}
2196
8e669ac1 2197
0f0c8159
JH
2198/*************************************************
2199* Scan a file for malware *
2200*************************************************/
8e669ac1 2201
0f0c8159
JH
2202/* This is a test wrapper for scanning an email, which is not used in
2203normal processing. Scan any file, using the Exim scanning interface.
2204This function tampers with various global variables so is unsafe to use
2205in any other context.
8e669ac1 2206
0f0c8159
JH
2207Arguments:
2208 eml_filename a file holding the message to be scanned
8523533c 2209
0f0c8159
JH
2210Returns: Exim message processing code (OK, FAIL, DEFER, ...)
2211 where true means malware was found (condition applies)
2212*/
2213int
2214malware_in_file(uschar *eml_filename)
8523533c 2215{
55240832
JH
2216uschar message_id_buf[64];
2217int ret;
2218
2219/* spool_mbox() assumes various parameters exist, when creating
2220the 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));
2224message_id = message_id_buf;
2225sender_address = US"malware-sender@example.net";
2226return_path = US"";
2227recipients_list = NULL;
2228receive_add_recipient(US"malware-victim@example.net", -1);
8768d548 2229f.enable_dollar_recipients = TRUE;
55240832 2230
040721f2 2231ret = malware_internal(US"*", eml_filename, 0);
55240832
JH
2232
2233Ustrncpy(spooled_message_id, message_id, sizeof(spooled_message_id));
2234spool_mbox_ok = 1;
2235
2236/* don't set no_mbox_unspool; at present, there's no way for it to become
2237set, but if that changes, then it should apply to these tests too */
2238
2239unspool_mbox();
2240
2241/* silence static analysis tools */
2242message_id = NULL;
2243
2244return ret;
8523533c
TK
2245}
2246
476be7e2
JH
2247
2248void
2249malware_init(void)
2250{
2251if (!malware_default_re)
2252 malware_default_re = regex_must_compile(malware_regex_default, FALSE, TRUE);
c11d665d
JH
2253
2254#ifndef DISABLE_MAL_DRWEB
476be7e2
JH
2255if (!drweb_re)
2256 drweb_re = regex_must_compile(drweb_re_str, FALSE, TRUE);
c11d665d
JH
2257#endif
2258#ifndef DISABLE_MAL_FSECURE
476be7e2
JH
2259if (!fsec_re)
2260 fsec_re = regex_must_compile(fsec_re_str, FALSE, TRUE);
c11d665d
JH
2261#endif
2262#ifndef DISABLE_MAL_KAV
476be7e2
JH
2263if (!kav_re_sus)
2264 kav_re_sus = regex_must_compile(kav_re_sus_str, FALSE, TRUE);
2265if (!kav_re_inf)
2266 kav_re_inf = regex_must_compile(kav_re_inf_str, FALSE, TRUE);
c11d665d 2267#endif
fd5a6ffb 2268#ifndef DISABLE_MAL_AVAST
476be7e2
JH
2269if (!ava_re_clean)
2270 ava_re_clean = regex_must_compile(ava_re_clean_str, FALSE, TRUE);
2271if (!ava_re_virus)
2272 ava_re_virus = regex_must_compile(ava_re_virus_str, FALSE, TRUE);
ad93c40f
HSHR
2273if (!ava_re_error)
2274 ava_re_error = regex_must_compile(ava_re_error_str, FALSE, TRUE);
c11d665d 2275#endif
fd5a6ffb 2276#ifndef DISABLE_MAL_FFROT6D
71b32d41
ACK
2277if (!fprot6d_re_error)
2278 fprot6d_re_error = regex_must_compile(fprot6d_re_error_str, FALSE, TRUE);
2279if (!fprot6d_re_virus)
2280 fprot6d_re_virus = regex_must_compile(fprot6d_re_virus_str, FALSE, TRUE);
c11d665d
JH
2281#endif
2282}
2283
2284
ab1604ea
JH
2285gstring *
2286malware_show_supported(gstring * g)
c11d665d 2287{
ab1604ea
JH
2288g = string_cat(g, US"Malware:");
2289for (struct scan * sc = m_scans; sc->scancode != (scanner_t)-1; sc++)
2290 g = string_fmt_append(g, " %s", sc->name);
2291return string_cat(g, US"\n");
476be7e2
JH
2292}
2293
c11d665d
JH
2294
2295# endif /*!MACRO_PREDEF*/
b1f8e4f8
JH
2296#endif /*WITH_CONTENT_SCAN*/
2297/*
2298 * vi: aw ai sw=2
2299 */