doc: string2 not expanded, don't imply otherwise
[exim.git] / src / src / malware.c
... / ...
CommitLineData
1/*************************************************
2* Exim - an Internet mail transport agent *
3*************************************************/
4
5/* Copyright (c) Tom Kistner <tom@duncanthrax.net> 2003 - 2015
6 * License: GPL
7 * Copyright (c) The Exim Maintainers 2016
8 */
9
10/* Code for calling virus (malware) scanners. Called from acl.c. */
11
12#include "exim.h"
13#ifdef WITH_CONTENT_SCAN
14
15typedef enum {M_FPROTD, M_DRWEB, M_AVES, M_FSEC, M_KAVD, M_CMDL,
16 M_SOPHIE, M_CLAMD, M_SOCK, M_MKSD, M_AVAST, M_FPROT6D} scanner_t;
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 },
36 { M_AVAST, US"avast", US"/var/run/avast/scan.sock", MC_STRM },
37 { M_FPROT6D, US"f-prot6d", US"localhost 10200", MC_TCP },
38 { -1, NULL, NULL, MC_NONE } /* end-marker */
39};
40
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"
44
45typedef struct clamd_address {
46 uschar * hostspec;
47 unsigned tcp_port;
48 unsigned retry;
49} clamd_address;
50
51#ifndef nelements
52# define nelements(arr) (sizeof(arr) / sizeof(arr[0]))
53#endif
54
55
56#define MALWARE_TIMEOUT 120 /* default timeout, seconds */
57
58
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
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
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
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
93
94
95/******************************************************************************/
96
97/* Routine to check whether a system is big- or little-endian.
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
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);
109}
110
111BOOL malware_ok = FALSE;
112
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;
117extern uschar spooled_message_id[MESSAGE_ID_LENGTH+1];
118
119
120
121static inline int
122malware_errlog_defer(const uschar * str)
123{
124log_write(0, LOG_MAIN|LOG_PANIC, "malware acl condition: %s", str);
125return DEFER;
126}
127
128static int
129m_errlog_defer(struct scan * scanent, const uschar * hostport,
130 const uschar * str)
131{
132return malware_errlog_defer(string_sprintf("%s %s : %s",
133 scanent->name, hostport ? hostport : CUS"", str));
134}
135static int
136m_errlog_defer_3(struct scan * scanent, const uschar * hostport,
137 const uschar * str, int fd_to_close)
138{
139(void) close(fd_to_close);
140return m_errlog_defer(scanent, hostport, str);
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{
152return ip_connectedsocket(SOCK_STREAM, hostname, port, port, 5, host, errstr);
153}
154
155static int
156m_sock_send(int sock, uschar * buf, int cnt, uschar ** errstr)
157{
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;
167}
168
169static const pcre *
170m_pcre_compile(const uschar * re, uschar ** errstr)
171{
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;
181}
182
183uschar *
184m_pcre_exec(const pcre * cre, uschar * text)
185{
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;
193}
194
195static const pcre *
196m_pcre_nextinlist(const uschar ** list, int * sep,
197 char * listerr, uschar ** errstr)
198{
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;
207}
208
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
214 Return: number of chars - zero for an empty line
215 -1 on EOF
216 -2 on timeout or error
217*/
218static int
219recv_line(int fd, uschar * buffer, int bsize, int tmo)
220{
221uschar * p = buffer;
222ssize_t rcv;
223BOOL ok = FALSE;
224
225if (!fd_ready(fd, tmo-time(NULL)))
226 return -2;
227
228/*XXX tmo handling assumes we always get a whole line */
229/* read until \n */
230errno = 0;
231while ((rcv = read(fd, p, 1)) > 0)
232 {
233 ok = TRUE;
234 if (p-buffer > bsize-2) break;
235 if (*p == '\n') break;
236 if (*p != '\r') p++;
237 }
238if (!ok)
239 {
240 DEBUG(D_acl) debug_printf_indent("Malware scan: read %s (%s)\n",
241 rcv==0 ? "EOF" : "error", strerror(errno));
242 return rcv==0 ? -1 : -2;
243 }
244*p = '\0';
245
246DEBUG(D_acl) debug_printf_indent("Malware scan: read '%s'\n", buffer);
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
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';
340 return m_errlog_defer(scanent, NULL,
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 }
358 return m_errlog_defer(scanent, NULL,
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
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)))
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;
404return OK;
405}
406
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:
415 malware_re match condition for "malware="
416 scan_filename the file holding the email to be scanned, if we're faking
417 this up for the -bmalware test, else NULL
418 timeout if nonzero, non-default timeoutl
419
420Returns: Exim message processing code (OK, FAIL, DEFER, ...)
421 where true means malware was found (condition applies)
422*/
423static int
424malware_internal(const uschar * malware_re, const uschar * scan_filename,
425 int timeout)
426{
427int sep = 0;
428const uschar *av_scanner_work = av_scanner;
429uschar *scanner_name;
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;
438uschar * eml_filename, * eml_dir;
439
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)))
446 return malware_errlog_defer(US"error while creating mbox spool file");
447
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. */
450
451(void) fclose(mbox_file);
452eml_dir = string_copyn(eml_filename, Ustrrchr(eml_filename, '/') - eml_filename);
453
454/* parse 1st option */
455if (strcmpic(malware_re, US"false") == 0 || Ustrcmp(malware_re,"0") == 0)
456 return FAIL; /* explicitly no matching */
457
458/* special cases (match anything except empty) */
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);
467 malware_re = malware_regex_default;
468 re = malware_default_re;
469 }
470
471/* compile the regex, see if it works */
472else if (!(re = m_pcre_compile(malware_re, &errstr)))
473 return malware_errlog_defer(errstr);
474
475/* Reset sep that is set by previous string_nextinlist() call */
476sep = 0;
477
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));
485
486 DEBUG(D_acl)
487 debug_printf_indent("Expanded av_scanner global: %s\n", av_scanner_work);
488 /* disable result caching in this case */
489 malware_name = NULL;
490 malware_ok = FALSE;
491 }
492
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
502 for (scanent = m_scans; ; scanent++)
503 {
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)
512 break;
513 switch(scanent->conn)
514 {
515 case MC_TCP: sock = ip_tcpsocket(scanner_options, &errstr, 5); break;
516 case MC_UNIX: sock = ip_unixsocket(scanner_options, &errstr); break;
517 case MC_STRM: sock = ip_streamsocket(scanner_options, &errstr, 5); break;
518 default: /* compiler quietening */ break;
519 }
520 if (sock < 0)
521 return m_errlog_defer(scanent, CUS callout_address, errstr);
522 break;
523 }
524 DEBUG(D_acl) debug_printf_indent("Malware scan: %s tmo %s\n", scanner_name, readconf_printtime(timeout));
525
526 switch (scanent->scancode)
527 {
528 case M_FPROTD: /* "f-protd" scanner type -------------------------------- */
529 {
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;
536
537 scanrequest = string_sprintf("GET %s", eml_filename);
538
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);
547 DEBUG(D_acl) debug_printf_indent("Malware scan: issuing %s: %s\n",
548 scanner_name, scanrequest);
549
550 /* send scan request */
551 if (m_sock_send(sock, scanrequest, Ustrlen(scanrequest)+1, &errstr) < 0)
552 return m_errlog_defer(scanent, CUS callout_address, errstr);
553
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>")))
560 {
561 if ((strhelper2 = Ustrstr(buf, US"</name>")) != NULL)
562 {
563 *strhelper2 = '\0';
564 malware_name_internal = string_copy(strhelper+6);
565 }
566 }
567 else if (Ustrstr(buf, US"<summary code=\""))
568 {
569 malware_name = Ustrstr(buf, US"<summary code=\"11\">")
570 ? malware_name_internal : NULL;
571 break;
572 }
573 }
574 if (len < -1)
575 {
576 (void)close(sock);
577 return DEFER;
578 }
579 break;
580 } /* f-protd */
581
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;
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)
601 return m_errlog_defer_3(scanent, NULL,
602 string_sprintf("can't open spool file %s: %s",
603 eml_filename, strerror(errno)),
604 sock);
605
606 if ((fsize = lseek(drweb_fd, 0, SEEK_END)) == -1)
607 {
608 int err;
609badseek: err = errno;
610 (void)close(drweb_fd);
611 return m_errlog_defer_3(scanent, NULL,
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 {
619 (void)close(drweb_fd);
620 return m_errlog_defer_3(scanent, NULL,
621 string_sprintf("seeking spool file %s, size overflow",
622 eml_filename),
623 sock);
624 }
625 drweb_slen = htonl(fsize);
626 if (lseek(drweb_fd, 0, SEEK_SET) < 0)
627 goto badseek;
628
629 DEBUG(D_acl) debug_printf_indent("Malware scan: issuing %s remote scan [%s]\n",
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))
637 {
638 (void)close(drweb_fd);
639 return m_errlog_defer_3(scanent, CUS callout_address, string_sprintf(
640 "unable to send commands to socket (%s)", scanner_options),
641 sock);
642 }
643
644 if (!(drweb_fbuf = US malloc(fsize_uint)))
645 {
646 (void)close(drweb_fd);
647 return m_errlog_defer_3(scanent, NULL,
648 string_sprintf("unable to allocate memory %u for file (%s)",
649 fsize_uint, eml_filename),
650 sock);
651 }
652
653 if ((result = read (drweb_fd, drweb_fbuf, fsize)) == -1)
654 {
655 int err = errno;
656 (void)close(drweb_fd);
657 free(drweb_fbuf);
658 return m_errlog_defer_3(scanent, NULL,
659 string_sprintf("can't read spool file %s: %s",
660 eml_filename, strerror(err)),
661 sock);
662 }
663 (void)close(drweb_fd);
664
665 /* send file body to socket */
666 if (send(sock, drweb_fbuf, fsize, 0) < 0)
667 {
668 free(drweb_fbuf);
669 return m_errlog_defer_3(scanent, CUS callout_address, string_sprintf(
670 "unable to send file body to socket (%s)", scanner_options),
671 sock);
672 }
673 }
674 else
675 {
676 drweb_slen = htonl(Ustrlen(eml_filename));
677
678 DEBUG(D_acl) debug_printf_indent("Malware scan: issuing %s local scan [%s]\n",
679 scanner_name, scanner_options);
680
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))
687 return m_errlog_defer_3(scanent, CUS callout_address, string_sprintf(
688 "unable to send commands to socket (%s)", scanner_options),
689 sock);
690 }
691
692 /* wait for result */
693 if (!recv_len(sock, &drweb_rc, sizeof(drweb_rc), tmo))
694 return m_errlog_defer_3(scanent, CUS callout_address,
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))
699 return m_errlog_defer_3(scanent, CUS callout_address,
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;
707
708 /* setup default virus name */
709 malware_name = US"unknown";
710
711 /* set up match regex */
712 if (!drweb_re)
713 drweb_re = m_pcre_compile(drweb_re_str, &errstr);
714
715 /* read and concatenate virus names into one string */
716 for (i = 0; i < drweb_vnum; i++)
717 {
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))
721 return m_errlog_defer_3(scanent, CUS callout_address,
722 US"cannot read report size", sock);
723 drweb_slen = ntohl(drweb_slen);
724 tmpbuf = store_get(drweb_slen);
725
726 /* read report body */
727 if (!recv_len(sock, tmpbuf, drweb_slen, tmo))
728 return m_errlog_defer_3(scanent, CUS callout_address,
729 US"cannot read report string", sock);
730 tmpbuf[drweb_slen] = '\0';
731
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;
738
739 pcre_get_substring(CS tmpbuf, ovector, result, 1, &pre_malware_nb);
740
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);
744
745 else /* concatenate each new virus name to previous */
746 malware_name = string_append(malware_name, &size, &off,
747 2, "/", pre_malware_nb);
748
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)
766 return m_errlog_defer_3(scanent, CUS callout_address,
767 string_sprintf("drweb daemon retcode 0x%x (%s)", drweb_rc, drweb_s),
768 sock);
769
770 /* no virus found */
771 malware_name = NULL;
772 }
773 break;
774 } /* drweb */
775
776 case M_AVES: /* "aveserver" scanner type -------------------------------- */
777 {
778 uschar buf[32768];
779 int result;
780
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);
784
785 if (buf[0] != '2') /* aveserver is having problems */
786 return m_errlog_defer_3(scanent, CUS callout_address,
787 string_sprintf("unavailable (Responded: %s).",
788 ((buf[0] != 0) ? buf : (uschar *)"nothing") ),
789 sock);
790
791 /* prepare our command */
792 (void)string_format(buf, sizeof(buf), "SCAN bPQRSTUW %s\r\n",
793 eml_filename);
794
795 /* and send it */
796 DEBUG(D_acl) debug_printf_indent("Malware scan: issuing %s %s\n",
797 scanner_name, buf);
798 if (m_sock_send(sock, buf, Ustrlen(buf), &errstr) < 0)
799 return m_errlog_defer(scanent, CUS callout_address, errstr);
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 */
809 {
810 result = m_errlog_defer(scanent, CUS callout_address,
811 string_sprintf("unable to scan file %s (Responded: %s).",
812 eml_filename, buf));
813 break;
814 }
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)
824 return m_errlog_defer(scanent, CUS callout_address, errstr);
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 */
831 return m_errlog_defer_3(scanent, CUS callout_address,
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;
842 } /* aveserver */
843
844 case M_FSEC: /* "fsecure" scanner type ---------------------------------- */
845 {
846 int i, j, bread = 0;
847 uschar * file_name;
848 uschar av_buffer[1024];
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
856 DEBUG(D_acl) debug_printf_indent("Malware scan: issuing %s scan [%s]\n",
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 {
862
863 if (m_sock_send(sock, cmdopt[i], Ustrlen(cmdopt[i]), &errstr) < 0)
864 return m_errlog_defer(scanent, CUS callout_address, errstr);
865
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)
869 return m_errlog_defer_3(scanent, CUS callout_address,
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 }
876
877 /* pass the mailfile to fsecure */
878 file_name = string_sprintf("SCAN\t%s\n", eml_filename);
879
880 if (m_sock_send(sock, file_name, Ustrlen(file_name), &errstr) < 0)
881 return m_errlog_defer(scanent, CUS callout_address, errstr);
882
883 /* set up match */
884 /* todo also SUSPICION\t */
885 if (!fsec_re)
886 fsec_re = m_pcre_compile(fsec_re_str, &errstr);
887
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 */
890 {
891 uschar * p = av_buffer;
892 uschar * q;
893
894 for (;;)
895 {
896 errno = ETIMEDOUT;
897 i = av_buffer+sizeof(av_buffer)-p;
898 if ((bread= ip_recv(sock, p, i-1, tmo-time(NULL))) < 0)
899 return m_errlog_defer_3(scanent, CUS callout_address,
900 string_sprintf("unable to read result (%s)", strerror(errno)),
901 sock);
902
903 for (p[bread] = '\0'; (q = Ustrchr(p, '\n')); p = q+1)
904 {
905 *q = '\0';
906
907 /* Really search for virus again? */
908 if (!malware_name)
909 /* try matcher on the line, grab substring */
910 malware_name = m_pcre_exec(fsec_re, p);
911
912 if (Ustrstr(p, "OK\tScan ok."))
913 goto fsec_found;
914 }
915
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;
920 }
921 }
922
923 fsec_found:
924 break;
925 } /* fsecure */
926
927 case M_KAVD: /* "kavdaemon" scanner type -------------------------------- */
928 {
929 time_t t;
930 uschar tmpbuf[1024];
931 uschar * scanrequest;
932 int kav_rc;
933 unsigned long kav_reportlen;
934 int bread;
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
952 DEBUG(D_acl) debug_printf_indent("Malware scan: issuing %s scan [%s]\n",
953 scanner_name, scanner_options);
954
955 /* send scan request */
956 if (m_sock_send(sock, scanrequest, Ustrlen(scanrequest)+1, &errstr) < 0)
957 return m_errlog_defer(scanent, CUS callout_address, errstr);
958
959 /* wait for result */
960 if (!recv_len(sock, tmpbuf, 2, tmo))
961 return m_errlog_defer_3(scanent, CUS callout_address,
962 US"unable to read 2 bytes from socket.", sock);
963
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 */
969 return m_errlog_defer_3(scanent, CUS callout_address,
970 US"please reconfigure kavdaemon to NOT disinfect or remove infected files.",
971 sock);
972 case 1:
973 return m_errlog_defer_3(scanent, CUS callout_address,
974 US"reported 'scanning not completed' (code 1).", sock);
975 case 7:
976 return m_errlog_defer_3(scanent, CUS callout_address,
977 US"reported 'kavdaemon damaged' (code 7).", sock);
978 }
979
980 /* code 8 is not handled, since it is ambiguous. It appears mostly on
981 bounces where part of a file has been cut off */
982
983 /* "virus found" return codes (2-4) */
984 if (kav_rc > 1 && kav_rc < 5)
985 {
986 int report_flag = 0;
987
988 /* setup default virus name */
989 malware_name = US"unknown";
990
991 report_flag = tmpbuf[ test_byte_order() == LITTLE_MY_ENDIAN ? 1 : 0 ];
992
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))
998 return m_errlog_defer_3(scanent, CUS callout_address,
999 US"cannot read report size", sock);
1000
1001 /* it's possible that avp returns av_buffer[1] == 1 but the
1002 reportsize is 0 (!?) */
1003 if (kav_reportlen > 0)
1004 {
1005 /* set up match regex, depends on retcode */
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 }
1016
1017 /* read report, linewise. Using size from stream to read amount of data
1018 from same stream is safe enough. */
1019 /* coverity[tainted_data] */
1020 while (kav_reportlen > 0)
1021 {
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;
1029 }
1030 }
1031 }
1032 }
1033 else /* no virus found */
1034 malware_name = NULL;
1035
1036 break;
1037 }
1038
1039 case M_CMDL: /* "cmdline" scanner type ---------------------------------- */
1040 {
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)
1057 return m_errlog_defer(scanent, NULL, errstr);
1058
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)
1063 return m_errlog_defer(scanent, NULL, errstr);
1064
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)
1069 return m_errlog_defer(scanent, NULL, errstr);
1070
1071 /* prepare scanner call; despite the naming, file_name holds a directory
1072 name which is documented as the value given to %s. */
1073
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);
1079
1080 /* redirect STDERR too */
1081 commandline = string_sprintf("%s 2>&1", commandline);
1082
1083 DEBUG(D_acl) debug_printf_indent("Malware scan: issuing %s scan [%s]\n",
1084 scanner_name, commandline);
1085
1086 /* store exims signal handlers */
1087 eximsigchld = signal(SIGCHLD,SIG_DFL);
1088 eximsigpipe = signal(SIGPIPE,SIG_DFL);
1089
1090 if (!(scanner_out = popen(CS commandline,"r")))
1091 {
1092 int err = errno;
1093 signal(SIGCHLD,eximsigchld); signal(SIGPIPE,eximsigpipe);
1094 return m_errlog_defer(scanent, NULL,
1095 string_sprintf("call (%s) failed: %s.", commandline, strerror(err)));
1096 }
1097 scanner_fd = fileno(scanner_out);
1098
1099 file_name = string_sprintf("%s/%s_scanner_output", eml_dir, message_id);
1100
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);
1106 return m_errlog_defer(scanent, NULL, string_sprintf(
1107 "opening scanner output file (%s) failed: %s.",
1108 file_name, strerror(err)));
1109 }
1110
1111 /* look for trigger while recording output */
1112 while ((rcnt = recv_line(scanner_fd, linebuffer,
1113 sizeof(linebuffer), tmo)))
1114 {
1115 if (rcnt < 0)
1116 {
1117 int err = errno;
1118 if (rcnt == -1)
1119 break;
1120 (void) pclose(scanner_out);
1121 signal(SIGCHLD,eximsigchld); signal(SIGPIPE,eximsigpipe);
1122 return m_errlog_defer(scanent, NULL, string_sprintf(
1123 "unable to read from scanner (%s): %s",
1124 commandline, strerror(err)));
1125 }
1126
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);
1132 return m_errlog_defer(scanent, NULL, string_sprintf(
1133 "short write on scanner output file (%s).", file_name));
1134 }
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 }
1142
1143 (void)fclose(scanner_record);
1144 sep = pclose(scanner_out);
1145 signal(SIGCHLD,eximsigchld); signal(SIGPIPE,eximsigpipe);
1146 if (sep != 0)
1147 return m_errlog_defer(scanent, NULL,
1148 sep == -1
1149 ? string_sprintf("running scanner failed: %s", strerror(sep))
1150 : string_sprintf("scanner returned error code: %d", sep));
1151
1152 if (trigger)
1153 {
1154 uschar * s;
1155 /* setup default virus name */
1156 malware_name = US"unknown";
1157
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;
1165 }
1166 (void)fclose(scanner_record);
1167 }
1168 else /* no virus found */
1169 malware_name = NULL;
1170 break;
1171 } /* cmdline */
1172
1173 case M_SOPHIE: /* "sophie" scanner type --------------------------------- */
1174 {
1175 int bread = 0;
1176 uschar *p;
1177 uschar * file_name;
1178 uschar av_buffer[1024];
1179
1180 /* pass the scan directory to sophie */
1181 file_name = string_copy(eml_filename);
1182 if ((p = Ustrrchr(file_name, '/')))
1183 *p = '\0';
1184
1185 DEBUG(D_acl) debug_printf_indent("Malware scan: issuing %s scan [%s]\n",
1186 scanner_name, scanner_options);
1187
1188 if ( write(sock, file_name, Ustrlen(file_name)) < 0
1189 || write(sock, "\n", 1) != 1
1190 )
1191 return m_errlog_defer_3(scanent, CUS callout_address,
1192 string_sprintf("unable to write to UNIX socket (%s)", scanner_options),
1193 sock);
1194
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)
1198 return m_errlog_defer_3(scanent, CUS callout_address,
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))
1210 return m_errlog_defer_3(scanent, CUS callout_address,
1211 US"scanner reported error", sock);
1212 else /* all ok, no virus */
1213 malware_name = NULL;
1214
1215 break;
1216 }
1217
1218 case M_CLAMD: /* "clamd" scanner type ----------------------------------- */
1219 {
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
1232 uschar *p, *vname, *result_tag;
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;
1243 clamd_address * cv[MAX_CLAMD_SERVERS];
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
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
1256 if (*scanner_options == '/')
1257 {
1258 clamd_address * cd;
1259 const uschar * sublist;
1260 int subsep = ' ';
1261
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;
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)
1273 return m_errlog_defer(scanent, NULL,
1274 string_sprintf("bad option '%s'", scanner_options));
1275 cv[0] = cd;
1276 }
1277 else
1278 {
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
1284 {
1285 clamd_address * cd;
1286 const uschar * sublist;
1287 int subsep = ' ';
1288 uschar * s;
1289
1290 /* The 'local' option means use the SCAN command over the network
1291 * socket (ie common file storage in use) */
1292 /*XXX we could accept this also as a local option? */
1293 if (strcmpic(scanner_options, US"local") == 0)
1294 {
1295 use_scan_command = TRUE;
1296 continue;
1297 }
1298
1299 cd = (clamd_address *) store_get(sizeof(clamd_address));
1300
1301 /* extract host and port part */
1302 sublist = scanner_options;
1303 if (!(cd->hostspec = string_nextinlist(&sublist, &subsep, NULL, 0)))
1304 {
1305 (void) m_errlog_defer(scanent, NULL,
1306 string_sprintf("missing address: '%s'", scanner_options));
1307 continue;
1308 }
1309 if (!(s = string_nextinlist(&sublist, &subsep, NULL, 0)))
1310 {
1311 (void) m_errlog_defer(scanent, NULL,
1312 string_sprintf("missing port: '%s'", scanner_options));
1313 continue;
1314 }
1315 cd->tcp_port = atoi(CS s);
1316
1317 /* parse options */
1318 /*XXX should these options be common over scanner types? */
1319 if (clamd_option(cd, sublist, &subsep) != OK)
1320 return m_errlog_defer(scanent, NULL,
1321 string_sprintf("bad option '%s'", scanner_options));
1322
1323 cv[num_servers++] = cd;
1324 if (num_servers >= MAX_CLAMD_SERVERS)
1325 {
1326 (void) m_errlog_defer(scanent, NULL,
1327 US"More than " MAX_CLAMD_SERVERS_S " clamd servers "
1328 "specified; only using the first " MAX_CLAMD_SERVERS_S );
1329 break;
1330 }
1331 } while ((scanner_options = string_nextinlist(&av_scanner_work, &sep,
1332 NULL, 0)));
1333
1334 /* check if we have at least one server */
1335 if (!num_servers)
1336 return m_errlog_defer(scanent, NULL,
1337 US"no useable server addresses in malware configuration option.");
1338 }
1339
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, ':'))
1343 return m_errlog_defer(scanent, NULL,
1344 string_sprintf("local/SCAN mode incompatible with" \
1345 " : in path to email filename [%s]", eml_filename));
1346
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. */
1353
1354 while (num_servers > 0)
1355 {
1356 int i = random_number( num_servers );
1357 clamd_address * cd = cv[i];
1358
1359 DEBUG(D_acl) debug_printf_indent("trying server name %s, port %u\n",
1360 cd->hostspec, cd->tcp_port);
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) */
1364 for (;;)
1365 {
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);
1375 }
1376 if (sock >= 0)
1377 break;
1378
1379 (void) m_errlog_defer(scanent, CUS callout_address, errstr);
1380
1381 /* Remove the server from the list. XXX We should free the memory */
1382 num_servers--;
1383 for (; i < num_servers; i++)
1384 cv[i] = cv[i+1];
1385 }
1386
1387 if (num_servers == 0)
1388 return m_errlog_defer(scanent, NULL, US"all servers failed");
1389 }
1390 else
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)
1399 return m_errlog_defer(scanent, CUS callout_address, errstr);
1400 while (cv[0]->retry > 0) cv[0]->retry = sleep(cv[0]->retry);
1401 }
1402
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 {
1410#ifdef WITH_OLD_CLAMAV_STREAM
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. */
1414
1415 DEBUG(D_acl) debug_printf_indent(
1416 "Malware scan: issuing %s old-style remote scan (PORT)\n",
1417 scanner_name);
1418
1419 /* Pass the string to ClamAV (7 = "STREAM\n") */
1420 if (m_sock_send(sock, US"STREAM\n", 7, &errstr) < 0)
1421 return m_errlog_defer(scanent, CUS callout_address, errstr);
1422
1423 memset(av_buffer2, 0, sizeof(av_buffer2));
1424 bread = ip_recv(sock, av_buffer2, sizeof(av_buffer2), tmo-time(NULL));
1425
1426 if (bread < 0)
1427 return m_errlog_defer_3(scanent, CUS callout_address,
1428 string_sprintf("unable to read PORT from socket (%s)",
1429 strerror(errno)),
1430 sock);
1431
1432 if (bread == sizeof(av_buffer2))
1433 return m_errlog_defer_3(scanent, CUS callout_address,
1434 "buffer too small", sock);
1435
1436 if (!(*av_buffer2))
1437 return m_errlog_defer_3(scanent, CUS callout_address,
1438 "ClamAV returned null", sock);
1439
1440 av_buffer2[bread] = '\0';
1441 if( sscanf(CS av_buffer2, "PORT %u\n", &port) != 1 )
1442 return m_errlog_defer_3(scanent, CUS callout_address,
1443 string_sprintf("Expected port information from clamd, got '%s'",
1444 av_buffer2),
1445 sock);
1446
1447 sockData = m_tcpsocket(connhost.address, port, NULL, &errstr);
1448 if (sockData < 0)
1449 return m_errlog_defer_3(scanent, CUS callout_address, errstr, sock);
1450
1451# define CLOSE_SOCKDATA (void)close(sockData)
1452#else /* WITH_OLD_CLAMAV_STREAM not defined */
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. */
1456
1457 DEBUG(D_acl) debug_printf_indent(
1458 "Malware scan: issuing %s new-style remote scan (zINSTREAM)\n",
1459 scanner_name);
1460
1461 /* Pass the string to ClamAV (10 = "zINSTREAM\0") */
1462 if (send(sock, "zINSTREAM", 10, 0) < 0)
1463 return m_errlog_defer_3(scanent, CUS hostname,
1464 string_sprintf("unable to send zINSTREAM to socket (%s)",
1465 strerror(errno)),
1466 sock);
1467
1468# define CLOSE_SOCKDATA /**/
1469#endif
1470
1471 /* calc file size */
1472 if ((clam_fd = open(CS eml_filename, O_RDONLY)) < 0)
1473 {
1474 int err = errno;
1475 CLOSE_SOCKDATA;
1476 return m_errlog_defer_3(scanent, NULL,
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 {
1483 int err;
1484b_seek: err = errno;
1485 CLOSE_SOCKDATA; (void)close(clam_fd);
1486 return m_errlog_defer_3(scanent, NULL,
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);
1495 return m_errlog_defer_3(scanent, NULL,
1496 string_sprintf("seeking spool file %s, size overflow",
1497 eml_filename),
1498 sock);
1499 }
1500 if (lseek(clam_fd, 0, SEEK_SET) < 0)
1501 goto b_seek;
1502
1503 if (!(clamav_fbuf = US malloc(fsize_uint)))
1504 {
1505 CLOSE_SOCKDATA; (void)close(clam_fd);
1506 return m_errlog_defer_3(scanent, NULL,
1507 string_sprintf("unable to allocate memory %u for file (%s)",
1508 fsize_uint, eml_filename),
1509 sock);
1510 }
1511
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);
1516 return m_errlog_defer_3(scanent, NULL,
1517 string_sprintf("can't read spool file %s: %s",
1518 eml_filename, strerror(err)),
1519 sock);
1520 }
1521 (void)close(clam_fd);
1522
1523 /* send file body to socket */
1524#ifdef WITH_OLD_CLAMAV_STREAM
1525 if (send(sockData, clamav_fbuf, fsize_uint, 0) < 0)
1526 {
1527 free(clamav_fbuf); CLOSE_SOCKDATA;
1528 return m_errlog_defer_3(scanent, NULL,
1529 string_sprintf("unable to send file body to socket (%s:%u)",
1530 hostname, port),
1531 sock);
1532 }
1533#else
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 {
1540 free(clamav_fbuf);
1541 return m_errlog_defer_3(scanent, NULL,
1542 string_sprintf("unable to send file body to socket (%s)", hostname),
1543 sock);
1544 }
1545#endif
1546
1547 free(clamav_fbuf);
1548
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
1570 DEBUG(D_acl) debug_printf_indent(
1571 "Malware scan: issuing %s local-path scan [%s]\n",
1572 scanner_name, scanner_options);
1573
1574 if (send(sock, file_name, Ustrlen(file_name), 0) < 0)
1575 return m_errlog_defer_3(scanent, CUS callout_address,
1576 string_sprintf("unable to write to socket (%s)", strerror(errno)),
1577 sock);
1578
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)
1592 return m_errlog_defer(scanent, CUS callout_address,
1593 string_sprintf("unable to read from socket (%s)",
1594 errno == 0 ? "EOF" : strerror(errno)));
1595
1596 if (bread == sizeof(av_buffer))
1597 return m_errlog_defer(scanent, CUS callout_address,
1598 US"buffer too small");
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))
1622 return m_errlog_defer(scanent, CUS callout_address,
1623 US"ClamAV returned null");
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
1631 DEBUG(D_acl) debug_printf_indent("Malware response: %s\n", av_buffer);
1632
1633 while (isspace(*--p) && (p > av_buffer))
1634 *p = '\0';
1635 if (*p) ++p;
1636
1637 /* colon in returned output? */
1638 if(!(p = Ustrchr(av_buffer,':')))
1639 return m_errlog_defer(scanent, CUS callout_address, string_sprintf(
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 == ')')
1660 {
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';
1666 }
1667 malware_name = string_copy(vname);
1668 DEBUG(D_acl) debug_printf_indent("Malware found, name \"%s\"\n", malware_name);
1669
1670 }
1671 else if (Ustrcmp(result_tag, "ERROR") == 0)
1672 return m_errlog_defer(scanent, CUS callout_address,
1673 string_sprintf("ClamAV returned: %s", av_buffer));
1674
1675 else if (Ustrcmp(result_tag, "OK") == 0)
1676 {
1677 /* Everything should be OK */
1678 malware_name = NULL;
1679 DEBUG(D_acl) debug_printf_indent("Malware not found\n");
1680
1681 }
1682 else
1683 return m_errlog_defer(scanent, CUS callout_address,
1684 string_sprintf("unparseable response from ClamAV: {%s}", av_buffer));
1685
1686 break;
1687 } /* clamd */
1688
1689 case M_SOCK: /* "sock" scanner type ------------------------------------- */
1690 /* This code was derived by Martin Poole from the clamd code contributed
1691 by David Saez and the cmdline code
1692 */
1693 {
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, '%'))
1710 return m_errlog_defer_3(scanent, NULL,
1711 US"unsafe sock scanner call spec", sock);
1712 }
1713 else
1714 sockline_scanner = sockline_scanner_default;
1715
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)
1720 return m_errlog_defer_3(scanent, NULL, errstr, sock);
1721
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)
1726 return m_errlog_defer_3(scanent, NULL, errstr, sock);
1727
1728 /* prepare scanner call - security depends on expansions check above */
1729 commandline = string_sprintf( CS sockline_scanner, CS eml_filename);
1730
1731
1732 /* Pass the command string to the socket */
1733 if (m_sock_send(sock, commandline, Ustrlen(commandline), &errstr) < 0)
1734 return m_errlog_defer(scanent, CUS callout_address, errstr);
1735
1736 /* Read the result */
1737 bread = ip_recv(sock, av_buffer, sizeof(av_buffer), tmo-time(NULL));
1738
1739 if (bread <= 0)
1740 return m_errlog_defer_3(scanent, CUS callout_address,
1741 string_sprintf("unable to read from socket (%s)", strerror(errno)),
1742 sock);
1743
1744 if (bread == sizeof(av_buffer))
1745 return m_errlog_defer_3(scanent, CUS callout_address,
1746 US"buffer too small", sock);
1747 av_buffer[bread] = '\0';
1748 linebuffer = string_copy(av_buffer);
1749
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";
1755 }
1756 else /* no virus found */
1757 malware_name = NULL;
1758 break;
1759 }
1760
1761 case M_MKSD: /* "mksd" scanner type ------------------------------------- */
1762 {
1763 char *mksd_options_end;
1764 int mksd_maxproc = 1; /* default, if no option supplied */
1765 int retval;
1766
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 )
1775 return m_errlog_defer(scanent, CUS callout_address,
1776 string_sprintf("invalid option '%s'", scanner_options));
1777 }
1778
1779 if((sock = ip_unixsocket(US "/var/run/mksd/socket", &errstr)) < 0)
1780 return m_errlog_defer(scanent, CUS callout_address, errstr);
1781
1782 malware_name = NULL;
1783
1784 DEBUG(D_acl) debug_printf_indent("Malware scan: issuing %s scan\n", scanner_name);
1785
1786 if ((retval = mksd_scan_packed(scanent, sock, eml_filename, tmo)) != OK)
1787 {
1788 close (sock);
1789 return retval;
1790 }
1791 break;
1792 }
1793
1794 case M_AVAST: /* "avast" scanner type ----------------------------------- */
1795 {
1796 int ovector[1*3];
1797 uschar buf[1024];
1798 uschar * scanrequest;
1799 enum {AVA_HELO, AVA_OPT, AVA_RSP, AVA_DONE} avast_stage;
1800 int nread;
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. */
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)))
1815 )
1816 return malware_errlog_defer(errstr);
1817
1818 /* wait for result */
1819 for (avast_stage = AVA_HELO;
1820 (nread = recv_line(sock, buf, sizeof(buf), tmo)) > 0;
1821 )
1822 {
1823 int slen = Ustrlen(buf);
1824 if (slen >= 1)
1825 {
1826 DEBUG(D_acl) debug_printf_indent("got from avast: %s\n", buf);
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 {
1852 scanrequest = string_sprintf("SCAN %s\n", eml_dir);
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';
1861 return m_errlog_defer_3(scanent, CUS callout_address, string_sprintf(
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
1872 if (pcre_exec(ava_re_clean, NULL, CS buf, slen,
1873 0, 0, ovector, nelements(ovector)) > 0)
1874 break;
1875
1876 if ((malware_name = m_pcre_exec(ava_re_virus, buf)))
1877 { /* remove backslash in front of [whitespace|backslash] */
1878 uschar * p, * p0;
1879 for (p = malware_name; *p; ++p)
1880 if (*p == '\\' && (isspace(p[1]) || p[1] == '\\'))
1881 for (p0 = p; *p0; ++p0) *p0 = p0[1];
1882
1883 avast_stage = AVA_DONE;
1884 goto endloop;
1885 }
1886
1887 if (Ustrncmp(buf, "200 SCAN OK", 11) == 0)
1888 { /* we're done finally */
1889 if (send(sock, "QUIT\n", 5, 0) < 0) /* courtesy */
1890 return m_errlog_defer_3(scanent, CUS callout_address,
1891 string_sprintf(
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;
1902
1903 case AVA_DONE: log_write(0, LOG_PANIC, "%s:%d:%s: should not happen",
1904 __FILE__, __LINE__, __FUNCTION__);
1905 }
1906 }
1907 }
1908 endloop:
1909
1910 switch(avast_stage)
1911 {
1912 case AVA_HELO:
1913 case AVA_OPT:
1914 case AVA_RSP: return m_errlog_defer_3(scanent, CUS callout_address,
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);
1922 default: break;
1923 }
1924 break;
1925 }
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
1969 break;
1970 } /* f-prot6d */
1971 } /* scanner type switch */
1972
1973 if (sock >= 0)
1974 (void) close (sock);
1975 malware_ok = TRUE; /* set "been here, done that" marker */
1976 }
1977
1978/* match virus name against pattern (caseless ------->----------v) */
1979if (malware_name && regex_match_and_setup(re, malware_name, 0, -1))
1980 {
1981 DEBUG(D_acl) debug_printf_indent(
1982 "Matched regex to malware [%s] [%s]\n", malware_re, malware_name);
1983 return OK;
1984 }
1985else
1986 return FAIL;
1987}
1988
1989
1990/*************************************************
1991* Scan an email for malware *
1992*************************************************/
1993
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.
1996
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)
2006{
2007int ret = malware_internal(malware_re, NULL, timeout);
2008
2009if (ret == DEFER) av_failed = TRUE;
2010return ret;
2011}
2012
2013
2014/*************************************************
2015* Scan a file for malware *
2016*************************************************/
2017
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.
2022
2023Arguments:
2024 eml_filename a file holding the message to be scanned
2025
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)
2031{
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
2047ret = malware_internal(US"*", eml_filename, 0);
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;
2061}
2062
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);
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);
2085}
2086
2087#endif /*WITH_CONTENT_SCAN*/
2088/*
2089 * vi: aw ai sw=2
2090 */