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