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