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