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