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