Merge branch 'debian' into hcoop_489
[hcoop/debian/exim4.git] / src / malware.c
CommitLineData
420a0d19
CE
1/*************************************************
2* Exim - an Internet mail transport agent *
3*************************************************/
4
2813c06e
CE
5/* Copyright (c) Tom Kistner <tom@duncanthrax.net> 2003 - 2015
6 * License: GPL
7 * Copyright (c) The Exim Maintainers 2016
8 */
420a0d19
CE
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,
2813c06e 16 M_SOPHIE, M_CLAMD, M_SOCK, M_MKSD, M_AVAST} scanner_t;
420a0d19
CE
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 },
2813c06e 36 { M_AVAST, US"avast", US"/var/run/avast/scan.sock", MC_STRM },
420a0d19
CE
37 { -1, NULL, NULL, MC_NONE } /* end-marker */
38};
39
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"
420a0d19 43
2813c06e
CE
44typedef struct clamd_address {
45 uschar * hostspec;
46 unsigned tcp_port;
47 unsigned retry;
48} clamd_address;
420a0d19
CE
49
50#ifndef nelements
51# define nelements(arr) (sizeof(arr) / sizeof(arr[0]))
52#endif
53
54
2813c06e 55#define MALWARE_TIMEOUT 120 /* default timeout, seconds */
420a0d19
CE
56
57
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
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
2813c06e
CE
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
420a0d19
CE
91/* Routine to check whether a system is big- or little-endian.
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
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);
103}
104
105BOOL malware_ok = FALSE;
106
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;
2813c06e 111extern uschar spooled_message_id[MESSAGE_ID_LENGTH+1];
420a0d19 112
420a0d19
CE
113
114
115static inline int
116malware_errlog_defer(const uschar * str)
117{
2813c06e
CE
118log_write(0, LOG_MAIN|LOG_PANIC, "malware acl condition: %s", str);
119return DEFER;
420a0d19
CE
120}
121
122static int
2813c06e
CE
123m_errlog_defer(struct scan * scanent, const uschar * hostport,
124 const uschar * str)
420a0d19 125{
2813c06e
CE
126return malware_errlog_defer(string_sprintf("%s %s : %s",
127 scanent->name, hostport ? hostport : CUS"", str));
420a0d19
CE
128}
129static int
2813c06e
CE
130m_errlog_defer_3(struct scan * scanent, const uschar * hostport,
131 const uschar * str, int fd_to_close)
420a0d19 132{
2813c06e
CE
133(void) close(fd_to_close);
134return m_errlog_defer(scanent, hostport, str);
420a0d19
CE
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{
2813c06e 146return ip_connectedsocket(SOCK_STREAM, hostname, port, port, 5, host, errstr);
420a0d19
CE
147}
148
149static int
2813c06e 150m_sock_send(int sock, uschar * buf, int cnt, uschar ** errstr)
420a0d19 151{
2813c06e
CE
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;
420a0d19 159 }
2813c06e
CE
160return sock;
161}
420a0d19 162
2813c06e
CE
163static const pcre *
164m_pcre_compile(const uschar * re, uschar ** errstr)
165{
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;
420a0d19
CE
175}
176
2813c06e
CE
177uschar *
178m_pcre_exec(const pcre * cre, uschar * text)
420a0d19 179{
2813c06e
CE
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;
187}
420a0d19 188
2813c06e
CE
189static const pcre *
190m_pcre_nextinlist(const uschar ** list, int * sep,
191 char * listerr, uschar ** errstr)
192{
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;
201}
202
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
208 Return: number of chars - zero for an empty line
209 -1 on EOF
210 -2 on timeout or error
211*/
212static int
213recv_line(int fd, uschar * buffer, int bsize, int tmo)
214{
215uschar * p = buffer;
216ssize_t rcv;
217BOOL ok = FALSE;
218
219if (!fd_ready(fd, tmo-time(NULL)))
220 return -2;
221
222/*XXX tmo handling assumes we always get a whole line */
223/* read until \n */
224errno = 0;
225while ((rcv = read(fd, p, 1)) > 0)
226 {
227 ok = TRUE;
228 if (p-buffer > bsize-2) break;
229 if (*p == '\n') break;
230 if (*p != '\r') p++;
231 }
232if (!ok)
233 {
234 DEBUG(D_acl) debug_printf_indent("Malware scan: read %s (%s)\n",
235 rcv==0 ? "EOF" : "error", strerror(errno));
236 return rcv==0 ? -1 : -2;
420a0d19 237 }
2813c06e 238*p = '\0';
420a0d19 239
2813c06e
CE
240DEBUG(D_acl) debug_printf_indent("Malware scan: read '%s'\n", buffer);
241return p - buffer;
420a0d19
CE
242}
243
2813c06e
CE
244/* return TRUE iff size as requested */
245static BOOL
246recv_len(int sock, void * buf, int size, int tmo)
420a0d19 247{
2813c06e
CE
248return fd_ready(sock, tmo-time(NULL))
249 ? recv(sock, buf, size, 0) == size
250 : FALSE;
420a0d19
CE
251}
252
2813c06e
CE
253
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)
420a0d19 261{
2813c06e
CE
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)");
420a0d19
CE
273 return -1;
274 }
2813c06e
CE
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 }
420a0d19
CE
290}
291
2813c06e
CE
292static inline int
293mksd_read_lines (int sock, uschar *av_buffer, int av_buffer_size, int tmo)
420a0d19 294{
2813c06e
CE
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;
420a0d19
CE
318}
319
2813c06e
CE
320static inline int
321mksd_parse_line(struct scan * scanent, char * line)
420a0d19 322{
2813c06e
CE
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';
334 return m_errlog_defer(scanent, NULL,
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 }
352 return m_errlog_defer(scanent, NULL,
353 string_sprintf("malformed reply received: %s", line));
354 }
420a0d19
CE
355}
356
2813c06e
CE
357static int
358mksd_scan_packed(struct scan * scanent, int sock, const uschar * scan_filename,
359 int tmo)
420a0d19 360{
2813c06e
CE
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;
420a0d19 374
2813c06e
CE
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
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)))
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 }
420a0d19 396 else
2813c06e
CE
397 return FAIL;
398return OK;
420a0d19
CE
399}
400
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:
2813c06e 409 malware_re match condition for "malware="
420a0d19 410 eml_filename the file holding the email to be scanned
2813c06e 411 timeout if nonzero, non-default timeoutl
420a0d19
CE
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*/
417static int
2813c06e
CE
418malware_internal(const uschar * malware_re, const uschar * eml_filename,
419 int timeout, BOOL faking)
420a0d19 420{
2813c06e
CE
421int sep = 0;
422const uschar *av_scanner_work = av_scanner;
423uschar *scanner_name;
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) */
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)))
420a0d19 457 return malware_errlog_defer(errstr);
2813c06e
CE
458 malware_re = malware_regex_default;
459 re = malware_default_re;
460 }
420a0d19 461
2813c06e
CE
462/* compile the regex, see if it works */
463else if (!(re = m_pcre_compile(malware_re, &errstr)))
464 return malware_errlog_defer(errstr);
465
466/* Reset sep that is set by previous string_nextinlist() call */
467sep = 0;
468
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));
476
477 DEBUG(D_acl)
478 debug_printf_indent("Expanded av_scanner global: %s\n", av_scanner_work);
479 /* disable result caching in this case */
480 malware_name = NULL;
481 malware_ok = FALSE;
420a0d19
CE
482 }
483
2813c06e
CE
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
493 for (scanent = m_scans; ; scanent++)
494 {
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)
420a0d19 503 break;
2813c06e
CE
504 switch(scanent->conn)
505 {
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;
509 default: /* compiler quietening */ break;
420a0d19 510 }
2813c06e
CE
511 if (sock < 0)
512 return m_errlog_defer(scanent, CUS callout_address, errstr);
513 break;
514 }
515 DEBUG(D_acl) debug_printf_indent("Malware scan: %s tmo %s\n", scanner_name, readconf_printtime(timeout));
420a0d19 516
2813c06e
CE
517 switch (scanent->scancode)
518 {
420a0d19
CE
519 case M_FPROTD: /* "f-protd" scanner type -------------------------------- */
520 {
2813c06e
CE
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;
527
528 scanrequest = string_sprintf("GET %s", eml_filename);
529
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++;
420a0d19 536 }
2813c06e
CE
537 scanrequest = string_sprintf("%s HTTP/1.0\r\n\r\n", scanrequest);
538 DEBUG(D_acl) debug_printf_indent("Malware scan: issuing %s: %s\n",
539 scanner_name, scanrequest);
420a0d19 540
2813c06e
CE
541 /* send scan request */
542 if (m_sock_send(sock, scanrequest, Ustrlen(scanrequest)+1, &errstr) < 0)
543 return m_errlog_defer(scanent, CUS callout_address, errstr);
544
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>")))
551 {
552 if ((strhelper2 = Ustrstr(buf, US"</name>")) != NULL)
553 {
554 *strhelper2 = '\0';
555 malware_name_internal = string_copy(strhelper+6);
420a0d19 556 }
2813c06e
CE
557 }
558 else if (Ustrstr(buf, US"<summary code=\""))
559 {
560 malware_name = Ustrstr(buf, US"<summary code=\"11\">")
561 ? malware_name_internal : NULL;
562 break;
563 }
420a0d19 564 }
2813c06e
CE
565 if (len < -1)
566 {
567 (void)close(sock);
568 return DEFER;
420a0d19 569 }
2813c06e 570 break;
420a0d19
CE
571 } /* f-protd */
572
573 case M_DRWEB: /* "drweb" scanner type ----------------------------------- */
2813c06e
CE
574 /* v0.1 - added support for tcp sockets */
575 /* v0.0 - initial release -- support for unix sockets */
420a0d19 576 {
2813c06e
CE
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;
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)
592 return m_errlog_defer_3(scanent, NULL,
593 string_sprintf("can't open spool file %s: %s",
594 eml_filename, strerror(errno)),
595 sock);
420a0d19 596
2813c06e
CE
597 if ((fsize = lseek(drweb_fd, 0, SEEK_END)) == -1)
598 {
599 int err = errno;
600 (void)close(drweb_fd);
601 return m_errlog_defer_3(scanent, NULL,
602 string_sprintf("can't seek spool file %s: %s",
603 eml_filename, strerror(err)),
604 sock);
420a0d19 605 }
2813c06e
CE
606 fsize_uint = (unsigned int) fsize;
607 if ((off_t)fsize_uint != fsize)
608 {
609 (void)close(drweb_fd);
610 return m_errlog_defer_3(scanent, NULL,
611 string_sprintf("seeking spool file %s, size overflow",
612 eml_filename),
613 sock);
420a0d19 614 }
2813c06e
CE
615 drweb_slen = htonl(fsize);
616 lseek(drweb_fd, 0, SEEK_SET);
420a0d19 617
2813c06e
CE
618 DEBUG(D_acl) debug_printf_indent("Malware scan: issuing %s remote scan [%s]\n",
619 scanner_name, scanner_options);
420a0d19 620
2813c06e
CE
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))
626 {
420a0d19 627 (void)close(drweb_fd);
2813c06e
CE
628 return m_errlog_defer_3(scanent, CUS callout_address, string_sprintf(
629 "unable to send commands to socket (%s)", scanner_options),
420a0d19
CE
630 sock);
631 }
2813c06e
CE
632
633 if (!(drweb_fbuf = US malloc(fsize_uint)))
634 {
420a0d19 635 (void)close(drweb_fd);
2813c06e
CE
636 return m_errlog_defer_3(scanent, NULL,
637 string_sprintf("unable to allocate memory %u for file (%s)",
638 fsize_uint, eml_filename),
639 sock);
640 }
420a0d19 641
2813c06e
CE
642 if ((result = read (drweb_fd, drweb_fbuf, fsize)) == -1)
643 {
644 int err = errno;
645 (void)close(drweb_fd);
646 free(drweb_fbuf);
647 return m_errlog_defer_3(scanent, NULL,
648 string_sprintf("can't read spool file %s: %s",
649 eml_filename, strerror(err)),
650 sock);
651 }
652 (void)close(drweb_fd);
420a0d19 653
2813c06e
CE
654 /* send file body to socket */
655 if (send(sock, drweb_fbuf, fsize, 0) < 0)
656 {
657 free(drweb_fbuf);
658 return m_errlog_defer_3(scanent, CUS callout_address, string_sprintf(
659 "unable to send file body to socket (%s)", scanner_options),
660 sock);
661 }
662 }
663 else
664 {
665 drweb_slen = htonl(Ustrlen(eml_filename));
420a0d19 666
2813c06e
CE
667 DEBUG(D_acl) debug_printf_indent("Malware scan: issuing %s local scan [%s]\n",
668 scanner_name, scanner_options);
420a0d19 669
2813c06e
CE
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))
676 return m_errlog_defer_3(scanent, CUS callout_address, string_sprintf(
677 "unable to send commands to socket (%s)", scanner_options),
678 sock);
420a0d19
CE
679 }
680
2813c06e
CE
681 /* wait for result */
682 if (!recv_len(sock, &drweb_rc, sizeof(drweb_rc), tmo))
683 return m_errlog_defer_3(scanent, CUS callout_address,
684 US"unable to read return code", sock);
685 drweb_rc = ntohl(drweb_rc);
420a0d19 686
2813c06e
CE
687 if (!recv_len(sock, &drweb_vnum, sizeof(drweb_vnum), tmo))
688 return m_errlog_defer_3(scanent, CUS callout_address,
689 US"unable to read the number of viruses", sock);
690 drweb_vnum = ntohl(drweb_vnum);
420a0d19 691
2813c06e
CE
692 /* "virus(es) found" if virus number is > 0 */
693 if (drweb_vnum)
694 {
695 int i;
420a0d19 696
2813c06e
CE
697 /* setup default virus name */
698 malware_name = US"unknown";
420a0d19 699
2813c06e
CE
700 /* set up match regex */
701 if (!drweb_re)
702 drweb_re = m_pcre_compile(drweb_re_str, &errstr);
420a0d19 703
2813c06e
CE
704 /* read and concatenate virus names into one string */
705 for (i = 0; i < drweb_vnum; i++)
420a0d19 706 {
2813c06e
CE
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))
710 return m_errlog_defer_3(scanent, CUS callout_address,
711 US"cannot read report size", sock);
712 drweb_slen = ntohl(drweb_slen);
713 tmpbuf = store_get(drweb_slen);
714
715 /* read report body */
716 if (!recv_len(sock, tmpbuf, drweb_slen, tmo))
717 return m_errlog_defer_3(scanent, CUS callout_address,
718 US"cannot read report string", sock);
719 tmpbuf[drweb_slen] = '\0';
720
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;
727
728 pcre_get_substring(CS tmpbuf, ovector, result, 1, &pre_malware_nb);
729
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);
733
734 else /* concatenate each new virus name to previous */
735 malware_name = string_append(malware_name, &size, &off,
736 2, "/", pre_malware_nb);
737
738 pcre_free_substring(pre_malware_nb);
420a0d19
CE
739 }
740 }
741 }
2813c06e
CE
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)
755 return m_errlog_defer_3(scanent, CUS callout_address,
756 string_sprintf("drweb daemon retcode 0x%x (%s)", drweb_rc, drweb_s),
757 sock);
420a0d19 758
2813c06e
CE
759 /* no virus found */
760 malware_name = NULL;
420a0d19 761 }
2813c06e 762 break;
420a0d19
CE
763 } /* drweb */
764
765 case M_AVES: /* "aveserver" scanner type -------------------------------- */
766 {
2813c06e
CE
767 uschar buf[32768];
768 int result;
420a0d19 769
2813c06e
CE
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);
420a0d19 773
2813c06e
CE
774 if (buf[0] != '2') /* aveserver is having problems */
775 return m_errlog_defer_3(scanent, CUS callout_address,
776 string_sprintf("unavailable (Responded: %s).",
777 ((buf[0] != 0) ? buf : (uschar *)"nothing") ),
778 sock);
779
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_indent("Malware scan: issuing %s %s\n",
786 scanner_name, buf);
787 if (m_sock_send(sock, buf, Ustrlen(buf), &errstr) < 0)
788 return m_errlog_defer(scanent, CUS callout_address, errstr);
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 */
798 {
799 result = m_errlog_defer(scanent, CUS callout_address,
800 string_sprintf("unable to scan file %s (Responded: %s).",
801 eml_filename, buf));
802 break;
803 }
804 if (Ustrncmp(buf,"322",3) == 0)
805 {
806 uschar *p = Ustrchr(&buf[4], ' ');
807 *p = '\0';
808 malware_name = string_copy(&buf[4]);
420a0d19
CE
809 }
810 }
811
2813c06e
CE
812 if (m_sock_send(sock, US"quit\r\n", 6, &errstr) < 0)
813 return m_errlog_defer(scanent, CUS callout_address, errstr);
420a0d19 814
2813c06e
CE
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);
420a0d19 818
2813c06e
CE
819 if (buf[0] != '2') /* aveserver is having problems */
820 return m_errlog_defer_3(scanent, CUS callout_address,
821 string_sprintf("unable to quit dialogue (Responded: %s).",
822 ((buf[0] != 0) ? buf : (uschar *)"nothing") ),
823 sock);
420a0d19 824
2813c06e
CE
825 if (result == DEFER)
826 {
827 (void)close(sock);
828 return DEFER;
420a0d19 829 }
2813c06e 830 break;
420a0d19
CE
831 } /* aveserver */
832
833 case M_FSEC: /* "fsecure" scanner type ---------------------------------- */
834 {
2813c06e
CE
835 int i, j, bread = 0;
836 uschar * file_name;
837 uschar av_buffer[1024];
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_indent("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 {
420a0d19 851
2813c06e
CE
852 if (m_sock_send(sock, cmdopt[i], Ustrlen(cmdopt[i]), &errstr) < 0)
853 return m_errlog_defer(scanent, CUS callout_address, errstr);
420a0d19 854
2813c06e
CE
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)
858 return m_errlog_defer_3(scanent, CUS callout_address,
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 }
420a0d19 865
2813c06e
CE
866 /* pass the mailfile to fsecure */
867 file_name = string_sprintf("SCAN\t%s\n", eml_filename);
420a0d19 868
2813c06e
CE
869 if (m_sock_send(sock, file_name, Ustrlen(file_name), &errstr) < 0)
870 return m_errlog_defer(scanent, CUS callout_address, errstr);
420a0d19 871
2813c06e
CE
872 /* set up match */
873 /* todo also SUSPICION\t */
874 if (!fsec_re)
875 fsec_re = m_pcre_compile(fsec_re_str, &errstr);
420a0d19 876
2813c06e
CE
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 */
879 {
880 uschar * p = av_buffer;
881 uschar * q;
420a0d19 882
2813c06e
CE
883 for (;;)
884 {
885 errno = ETIMEDOUT;
886 i = av_buffer+sizeof(av_buffer)-p;
887 if ((bread= ip_recv(sock, p, i-1, tmo-time(NULL))) < 0)
888 return m_errlog_defer_3(scanent, CUS callout_address,
889 string_sprintf("unable to read result (%s)", strerror(errno)),
890 sock);
420a0d19 891
2813c06e
CE
892 for (p[bread] = '\0'; (q = Ustrchr(p, '\n')); p = q+1)
893 {
894 *q = '\0';
420a0d19 895
2813c06e
CE
896 /* Really search for virus again? */
897 if (!malware_name)
898 /* try matcher on the line, grab substring */
899 malware_name = m_pcre_exec(fsec_re, p);
420a0d19 900
2813c06e
CE
901 if (Ustrstr(p, "OK\tScan ok."))
902 goto fsec_found;
903 }
904
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;
909 }
420a0d19 910 }
2813c06e
CE
911
912 fsec_found:
420a0d19
CE
913 break;
914 } /* fsecure */
915
916 case M_KAVD: /* "kavdaemon" scanner type -------------------------------- */
917 {
2813c06e
CE
918 time_t t;
919 uschar tmpbuf[1024];
920 uschar * scanrequest;
921 int kav_rc;
922 unsigned long kav_reportlen;
923 int bread;
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_indent("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)
946 return m_errlog_defer(scanent, CUS callout_address, errstr);
947
948 /* wait for result */
949 if (!recv_len(sock, tmpbuf, 2, tmo))
950 return m_errlog_defer_3(scanent, CUS callout_address,
951 US"unable to read 2 bytes from socket.", sock);
952
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 */
958 return m_errlog_defer_3(scanent, CUS callout_address,
959 US"please reconfigure kavdaemon to NOT disinfect or remove infected files.",
960 sock);
961 case 1:
962 return m_errlog_defer_3(scanent, CUS callout_address,
963 US"reported 'scanning not completed' (code 1).", sock);
964 case 7:
965 return m_errlog_defer_3(scanent, CUS callout_address,
966 US"reported 'kavdaemon damaged' (code 7).", sock);
967 }
420a0d19 968
2813c06e
CE
969 /* code 8 is not handled, since it is ambiguous. It appears mostly on
970 bounces where part of a file has been cut off */
420a0d19 971
2813c06e
CE
972 /* "virus found" return codes (2-4) */
973 if (kav_rc > 1 && kav_rc < 5)
974 {
975 int report_flag = 0;
420a0d19 976
2813c06e
CE
977 /* setup default virus name */
978 malware_name = US"unknown";
420a0d19 979
2813c06e 980 report_flag = tmpbuf[ test_byte_order() == LITTLE_MY_ENDIAN ? 1 : 0 ];
420a0d19 981
2813c06e
CE
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))
987 return m_errlog_defer_3(scanent, CUS callout_address,
988 US"cannot read report size", sock);
989
990 /* it's possible that avp returns av_buffer[1] == 1 but the
991 reportsize is 0 (!?) */
992 if (kav_reportlen > 0)
993 {
994 /* set up match regex, depends on retcode */
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 }
420a0d19 1005
2813c06e
CE
1006 /* read report, linewise. Using size from stream to read amount of data
1007 from same stream is safe enough. */
1008 /* coverity[tainted_data] */
1009 while (kav_reportlen > 0)
1010 {
1011 if ((bread = recv_line(sock, tmpbuf, sizeof(tmpbuf), tmo)) < 0)
1012 break;
1013 kav_reportlen -= bread+1;
1014
1015 /* try matcher on the line, grab substring */
1016 if ((malware_name = m_pcre_exec(kav_re, tmpbuf)))
1017 break;
420a0d19
CE
1018 }
1019 }
1020 }
1021 }
2813c06e
CE
1022 else /* no virus found */
1023 malware_name = NULL;
420a0d19 1024
2813c06e 1025 break;
420a0d19
CE
1026 }
1027
1028 case M_CMDL: /* "cmdline" scanner type ---------------------------------- */
1029 {
2813c06e
CE
1030 const uschar *cmdline_scanner = scanner_options;
1031 const pcre *cmdline_trigger_re;
1032 const pcre *cmdline_regex_re;
1033 uschar * file_name;
1034 uschar * commandline;
1035 void (*eximsigchld)(int);
1036 void (*eximsigpipe)(int);
1037 FILE *scanner_out = NULL;
1038 int scanner_fd;
1039 FILE *scanner_record = NULL;
1040 uschar linebuffer[32767];
1041 int rcnt;
1042 int trigger = 0;
1043 uschar *p;
1044
1045 if (!cmdline_scanner)
1046 return m_errlog_defer(scanent, NULL, errstr);
1047
1048 /* find scanner output trigger */
1049 cmdline_trigger_re = m_pcre_nextinlist(&av_scanner_work, &sep,
1050 "missing trigger specification", &errstr);
1051 if (!cmdline_trigger_re)
1052 return m_errlog_defer(scanent, NULL, errstr);
1053
1054 /* find scanner name regex */
1055 cmdline_regex_re = m_pcre_nextinlist(&av_scanner_work, &sep,
1056 "missing virus name regex specification", &errstr);
1057 if (!cmdline_regex_re)
1058 return m_errlog_defer(scanent, NULL, errstr);
1059
1060 /* prepare scanner call; despite the naming, file_name holds a directory
1061 name which is documented as the value given to %s. */
1062
1063 file_name = string_copy(eml_filename);
1064 p = Ustrrchr(file_name, '/');
1065 if (p)
1066 *p = '\0';
1067 commandline = string_sprintf(CS cmdline_scanner, file_name);
1068
1069 /* redirect STDERR too */
1070 commandline = string_sprintf("%s 2>&1", commandline);
1071
1072 DEBUG(D_acl) debug_printf_indent("Malware scan: issuing %s scan [%s]\n",
1073 scanner_name, commandline);
1074
1075 /* store exims signal handlers */
1076 eximsigchld = signal(SIGCHLD,SIG_DFL);
1077 eximsigpipe = signal(SIGPIPE,SIG_DFL);
1078
1079 if (!(scanner_out = popen(CS commandline,"r")))
1080 {
1081 int err = errno;
1082 signal(SIGCHLD,eximsigchld); signal(SIGPIPE,eximsigpipe);
1083 return m_errlog_defer(scanent, NULL,
1084 string_sprintf("call (%s) failed: %s.", commandline, strerror(err)));
1085 }
1086 scanner_fd = fileno(scanner_out);
420a0d19 1087
2813c06e
CE
1088 file_name = string_sprintf("%s/scan/%s/%s_scanner_output",
1089 spool_directory, message_id, message_id);
420a0d19 1090
2813c06e
CE
1091 if (!(scanner_record = modefopen(file_name, "wb", SPOOL_MODE)))
1092 {
1093 int err = errno;
1094 (void) pclose(scanner_out);
1095 signal(SIGCHLD,eximsigchld); signal(SIGPIPE,eximsigpipe);
1096 return m_errlog_defer(scanent, NULL, string_sprintf(
1097 "opening scanner output file (%s) failed: %s.",
1098 file_name, strerror(err)));
420a0d19
CE
1099 }
1100
2813c06e
CE
1101 /* look for trigger while recording output */
1102 while ((rcnt = recv_line(scanner_fd, linebuffer,
1103 sizeof(linebuffer), tmo)))
1104 {
1105 if (rcnt < 0)
1106 {
420a0d19 1107 int err = errno;
2813c06e
CE
1108 if (rcnt == -1)
1109 break;
420a0d19
CE
1110 (void) pclose(scanner_out);
1111 signal(SIGCHLD,eximsigchld); signal(SIGPIPE,eximsigpipe);
2813c06e
CE
1112 return m_errlog_defer(scanent, NULL, string_sprintf(
1113 "unable to read from scanner (%s): %s",
1114 commandline, strerror(err)));
1115 }
420a0d19 1116
2813c06e
CE
1117 if (Ustrlen(linebuffer) > fwrite(linebuffer, 1, Ustrlen(linebuffer), scanner_record))
1118 {
1119 /* short write */
1120 (void) pclose(scanner_out);
1121 signal(SIGCHLD,eximsigchld); signal(SIGPIPE,eximsigpipe);
1122 return m_errlog_defer(scanent, NULL, string_sprintf(
1123 "short write on scanner output file (%s).", file_name));
420a0d19 1124 }
2813c06e
CE
1125 putc('\n', scanner_record);
1126 /* try trigger match */
1127 if ( !trigger
1128 && regex_match_and_setup(cmdline_trigger_re, linebuffer, 0, -1)
1129 )
1130 trigger = 1;
420a0d19
CE
1131 }
1132
2813c06e
CE
1133 (void)fclose(scanner_record);
1134 sep = pclose(scanner_out);
1135 signal(SIGCHLD,eximsigchld); signal(SIGPIPE,eximsigpipe);
1136 if (sep != 0)
1137 return m_errlog_defer(scanent, NULL,
1138 sep == -1
1139 ? string_sprintf("running scanner failed: %s", strerror(sep))
1140 : string_sprintf("scanner returned error code: %d", sep));
420a0d19 1141
2813c06e
CE
1142 if (trigger)
1143 {
1144 uschar * s;
1145 /* setup default virus name */
1146 malware_name = US"unknown";
1147
1148 /* re-open the scanner output file, look for name match */
1149 scanner_record = fopen(CS file_name, "rb");
1150 while (fgets(CS linebuffer, sizeof(linebuffer), scanner_record))
1151 {
1152 /* try match */
1153 if ((s = m_pcre_exec(cmdline_regex_re, linebuffer)))
1154 malware_name = s;
420a0d19 1155 }
2813c06e 1156 (void)fclose(scanner_record);
420a0d19 1157 }
2813c06e
CE
1158 else /* no virus found */
1159 malware_name = NULL;
1160 break;
420a0d19
CE
1161 } /* cmdline */
1162
1163 case M_SOPHIE: /* "sophie" scanner type --------------------------------- */
1164 {
2813c06e
CE
1165 int bread = 0;
1166 uschar *p;
1167 uschar * file_name;
1168 uschar av_buffer[1024];
1169
1170 /* pass the scan directory to sophie */
1171 file_name = string_copy(eml_filename);
1172 if ((p = Ustrrchr(file_name, '/')))
1173 *p = '\0';
1174
1175 DEBUG(D_acl) debug_printf_indent("Malware scan: issuing %s scan [%s]\n",
1176 scanner_name, scanner_options);
1177
1178 if ( write(sock, file_name, Ustrlen(file_name)) < 0
1179 || write(sock, "\n", 1) != 1
1180 )
1181 return m_errlog_defer_3(scanent, CUS callout_address,
1182 string_sprintf("unable to write to UNIX socket (%s)", scanner_options),
1183 sock);
1184
1185 /* wait for result */
1186 memset(av_buffer, 0, sizeof(av_buffer));
1187 if ((bread = ip_recv(sock, av_buffer, sizeof(av_buffer), tmo-time(NULL))) <= 0)
1188 return m_errlog_defer_3(scanent, CUS callout_address,
1189 string_sprintf("unable to read from UNIX socket (%s)", scanner_options),
1190 sock);
1191
1192 /* infected ? */
1193 if (av_buffer[0] == '1') {
1194 uschar * s = Ustrchr(av_buffer, '\n');
1195 if (s)
1196 *s = '\0';
1197 malware_name = string_copy(&av_buffer[2]);
1198 }
1199 else if (!strncmp(CS av_buffer, "-1", 2))
1200 return m_errlog_defer_3(scanent, CUS callout_address,
1201 US"scanner reported error", sock);
1202 else /* all ok, no virus */
1203 malware_name = NULL;
420a0d19 1204
2813c06e
CE
1205 break;
1206 }
420a0d19 1207
2813c06e
CE
1208 case M_CLAMD: /* "clamd" scanner type ----------------------------------- */
1209 {
1210/* This code was originally contributed by David Saez */
1211/* There are three scanning methods available to us:
1212* (1) Use the SCAN command, pointing to a file in the filesystem
1213* (2) Use the STREAM command, send the data on a separate port
1214* (3) Use the zINSTREAM command, send the data inline
1215* The zINSTREAM command was introduced with ClamAV 0.95, which marked
1216* STREAM deprecated; see: http://wiki.clamav.net/bin/view/Main/UpgradeNotes095
1217* In Exim, we use SCAN if using a Unix-domain socket or explicitly told that
1218* the TCP-connected daemon is actually local; otherwise we use zINSTREAM unless
1219* WITH_OLD_CLAMAV_STREAM is defined.
1220* See Exim bug 926 for details. */
1221
1222 uschar *p, *vname, *result_tag;
1223 int bread=0;
1224 uschar * file_name;
1225 uschar av_buffer[1024];
1226 uschar *hostname = US"";
1227 host_item connhost;
1228 uschar *clamav_fbuf;
1229 int clam_fd, result;
1230 off_t fsize;
1231 unsigned int fsize_uint;
1232 BOOL use_scan_command = FALSE;
1233 clamd_address * cv[MAX_CLAMD_SERVERS];
1234 int num_servers = 0;
1235#ifdef WITH_OLD_CLAMAV_STREAM
1236 unsigned int port;
1237 uschar av_buffer2[1024];
1238 int sockData;
1239#else
1240 uint32_t send_size, send_final_zeroblock;
1241#endif
420a0d19 1242
2813c06e
CE
1243 /*XXX if unixdomain socket, only one server supported. Needs fixing;
1244 there's no reason we should not mix local and remote servers */
420a0d19 1245
2813c06e
CE
1246 if (*scanner_options == '/')
1247 {
1248 clamd_address * cd;
1249 const uschar * sublist;
1250 int subsep = ' ';
1251
1252 /* Local file; so we def want to use_scan_command and don't want to try
1253 * passing IP/port combinations */
1254 use_scan_command = TRUE;
1255 cd = (clamd_address *) store_get(sizeof(clamd_address));
1256
1257 /* extract socket-path part */
1258 sublist = scanner_options;
1259 cd->hostspec = string_nextinlist(&sublist, &subsep, NULL, 0);
1260
1261 /* parse options */
1262 if (clamd_option(cd, sublist, &subsep) != OK)
1263 return m_errlog_defer(scanent, NULL,
1264 string_sprintf("bad option '%s'", scanner_options));
1265 cv[0] = cd;
420a0d19 1266 }
2813c06e
CE
1267 else
1268 {
1269 /* Go through the rest of the list of host/port and construct an array
1270 * of servers to try. The first one is the bit we just passed from
1271 * scanner_options so process that first and then scan the remainder of
1272 * the address buffer */
1273 do
1274 {
1275 clamd_address * cd;
1276 const uschar * sublist;
1277 int subsep = ' ';
1278 uschar * s;
420a0d19 1279
2813c06e
CE
1280 /* The 'local' option means use the SCAN command over the network
1281 * socket (ie common file storage in use) */
1282 /*XXX we could accept this also as a local option? */
1283 if (strcmpic(scanner_options, US"local") == 0)
1284 {
1285 use_scan_command = TRUE;
1286 continue;
420a0d19
CE
1287 }
1288
2813c06e 1289 cd = (clamd_address *) store_get(sizeof(clamd_address));
420a0d19 1290
2813c06e
CE
1291 /* extract host and port part */
1292 sublist = scanner_options;
1293 if (!(cd->hostspec = string_nextinlist(&sublist, &subsep, NULL, 0)))
1294 {
1295 (void) m_errlog_defer(scanent, NULL,
1296 string_sprintf("missing address: '%s'", scanner_options));
1297 continue;
420a0d19 1298 }
2813c06e
CE
1299 if (!(s = string_nextinlist(&sublist, &subsep, NULL, 0)))
1300 {
1301 (void) m_errlog_defer(scanent, NULL,
1302 string_sprintf("missing port: '%s'", scanner_options));
1303 continue;
1304 }
1305 cd->tcp_port = atoi(CS s);
420a0d19 1306
2813c06e
CE
1307 /* parse options */
1308 /*XXX should these options be common over scanner types? */
1309 if (clamd_option(cd, sublist, &subsep) != OK)
1310 return m_errlog_defer(scanent, NULL,
1311 string_sprintf("bad option '%s'", scanner_options));
1312
1313 cv[num_servers++] = cd;
1314 if (num_servers >= MAX_CLAMD_SERVERS)
1315 {
1316 (void) m_errlog_defer(scanent, NULL,
1317 US"More than " MAX_CLAMD_SERVERS_S " clamd servers "
1318 "specified; only using the first " MAX_CLAMD_SERVERS_S );
1319 break;
420a0d19 1320 }
2813c06e
CE
1321 } while ((scanner_options = string_nextinlist(&av_scanner_work, &sep,
1322 NULL, 0)));
1323
1324 /* check if we have at least one server */
1325 if (!num_servers)
1326 return m_errlog_defer(scanent, NULL,
1327 US"no useable server addresses in malware configuration option.");
420a0d19
CE
1328 }
1329
2813c06e
CE
1330 /* See the discussion of response formats below to see why we really
1331 don't like colons in filenames when passing filenames to ClamAV. */
1332 if (use_scan_command && Ustrchr(eml_filename, ':'))
1333 return m_errlog_defer(scanent, NULL,
1334 string_sprintf("local/SCAN mode incompatible with" \
1335 " : in path to email filename [%s]", eml_filename));
1336
1337 /* We have some network servers specified */
1338 if (num_servers)
1339 {
1340 /* Confirmed in ClamAV source (0.95.3) that the TCPAddr option of clamd
1341 * only supports AF_INET, but we should probably be looking to the
1342 * future and rewriting this to be protocol-independent anyway. */
1343
1344 while (num_servers > 0)
1345 {
1346 int i = random_number( num_servers );
1347 clamd_address * cd = cv[i];
1348
1349 DEBUG(D_acl) debug_printf_indent("trying server name %s, port %u\n",
1350 cd->hostspec, cd->tcp_port);
1351
1352 /* Lookup the host. This is to ensure that we connect to the same IP
1353 * on both connections (as one host could resolve to multiple ips) */
1354 for (;;)
1355 {
1356 sock= m_tcpsocket(cd->hostspec, cd->tcp_port, &connhost, &errstr);
1357 if (sock >= 0)
1358 {
420a0d19 1359 /* Connection successfully established with a server */
2813c06e 1360 hostname = cd->hostspec;
420a0d19 1361 break;
2813c06e
CE
1362 }
1363 if (cd->retry <= 0) break;
1364 while (cd->retry > 0) cd->retry = sleep(cd->retry);
420a0d19 1365 }
2813c06e
CE
1366 if (sock >= 0)
1367 break;
420a0d19 1368
2813c06e 1369 (void) m_errlog_defer(scanent, CUS callout_address, errstr);
420a0d19 1370
2813c06e
CE
1371 /* Remove the server from the list. XXX We should free the memory */
1372 num_servers--;
1373 for (; i < num_servers; i++)
1374 cv[i] = cv[i+1];
420a0d19
CE
1375 }
1376
2813c06e
CE
1377 if (num_servers == 0)
1378 return m_errlog_defer(scanent, NULL, US"all servers failed");
420a0d19 1379 }
2813c06e
CE
1380 else
1381 for (;;)
1382 {
1383 if ((sock = ip_unixsocket(cv[0]->hostspec, &errstr)) >= 0)
1384 {
1385 hostname = cv[0]->hostspec;
1386 break;
1387 }
1388 if (cv[0]->retry <= 0)
1389 return m_errlog_defer(scanent, CUS callout_address, errstr);
1390 while (cv[0]->retry > 0) cv[0]->retry = sleep(cv[0]->retry);
1391 }
420a0d19 1392
2813c06e
CE
1393 /* have socket in variable "sock"; command to use is semi-independent of
1394 * the socket protocol. We use SCAN if is local (either Unix/local
1395 * domain socket, or explicitly told local) else we stream the data.
1396 * How we stream the data depends upon how we were built. */
420a0d19 1397
2813c06e
CE
1398 if (!use_scan_command)
1399 {
1400#ifdef WITH_OLD_CLAMAV_STREAM
1401 /* "STREAM\n" command, get back a "PORT <N>\n" response, send data to
1402 * that port on a second connection; then in the scan-method-neutral
1403 * part, read the response back on the original connection. */
420a0d19 1404
2813c06e
CE
1405 DEBUG(D_acl) debug_printf_indent(
1406 "Malware scan: issuing %s old-style remote scan (PORT)\n",
1407 scanner_name);
420a0d19 1408
2813c06e
CE
1409 /* Pass the string to ClamAV (7 = "STREAM\n") */
1410 if (m_sock_send(sock, US"STREAM\n", 7, &errstr) < 0)
1411 return m_errlog_defer(scanent, CUS callout_address, errstr);
420a0d19 1412
2813c06e
CE
1413 memset(av_buffer2, 0, sizeof(av_buffer2));
1414 bread = ip_recv(sock, av_buffer2, sizeof(av_buffer2), tmo-time(NULL));
420a0d19 1415
2813c06e
CE
1416 if (bread < 0)
1417 return m_errlog_defer_3(scanent, CUS callout_address,
1418 string_sprintf("unable to read PORT from socket (%s)",
1419 strerror(errno)),
1420 sock);
420a0d19 1421
2813c06e
CE
1422 if (bread == sizeof(av_buffer2))
1423 return m_errlog_defer_3(scanent, CUS callout_address,
1424 "buffer too small", sock);
420a0d19 1425
2813c06e
CE
1426 if (!(*av_buffer2))
1427 return m_errlog_defer_3(scanent, CUS callout_address,
1428 "ClamAV returned null", sock);
420a0d19 1429
2813c06e
CE
1430 av_buffer2[bread] = '\0';
1431 if( sscanf(CS av_buffer2, "PORT %u\n", &port) != 1 )
1432 return m_errlog_defer_3(scanent, CUS callout_address,
1433 string_sprintf("Expected port information from clamd, got '%s'",
1434 av_buffer2),
1435 sock);
420a0d19 1436
2813c06e
CE
1437 sockData = m_tcpsocket(connhost.address, port, NULL, &errstr);
1438 if (sockData < 0)
1439 return m_errlog_defer_3(scanent, CUS callout_address, errstr, sock);
1440
1441# define CLOSE_SOCKDATA (void)close(sockData)
1442#else /* WITH_OLD_CLAMAV_STREAM not defined */
1443 /* New protocol: "zINSTREAM\n" followed by a sequence of <length><data>
1444 chunks, <n> a 4-byte number (network order), terminated by a zero-length
1445 chunk. */
1446
1447 DEBUG(D_acl) debug_printf_indent(
1448 "Malware scan: issuing %s new-style remote scan (zINSTREAM)\n",
1449 scanner_name);
1450
1451 /* Pass the string to ClamAV (10 = "zINSTREAM\0") */
1452 if (send(sock, "zINSTREAM", 10, 0) < 0)
1453 return m_errlog_defer_3(scanent, CUS hostname,
1454 string_sprintf("unable to send zINSTREAM to socket (%s)",
1455 strerror(errno)),
1456 sock);
420a0d19 1457
2813c06e
CE
1458# define CLOSE_SOCKDATA /**/
1459#endif
420a0d19 1460
2813c06e
CE
1461 /* calc file size */
1462 if ((clam_fd = open(CS eml_filename, O_RDONLY)) < 0)
1463 {
1464 int err = errno;
1465 CLOSE_SOCKDATA;
1466 return m_errlog_defer_3(scanent, NULL,
1467 string_sprintf("can't open spool file %s: %s",
1468 eml_filename, strerror(err)),
1469 sock);
420a0d19 1470 }
2813c06e
CE
1471 if ((fsize = lseek(clam_fd, 0, SEEK_END)) < 0)
1472 {
1473 int err = errno;
1474 CLOSE_SOCKDATA; (void)close(clam_fd);
1475 return m_errlog_defer_3(scanent, NULL,
1476 string_sprintf("can't seek spool file %s: %s",
1477 eml_filename, strerror(err)),
1478 sock);
420a0d19 1479 }
2813c06e
CE
1480 fsize_uint = (unsigned int) fsize;
1481 if ((off_t)fsize_uint != fsize)
1482 {
1483 CLOSE_SOCKDATA; (void)close(clam_fd);
1484 return m_errlog_defer_3(scanent, NULL,
1485 string_sprintf("seeking spool file %s, size overflow",
1486 eml_filename),
1487 sock);
420a0d19 1488 }
2813c06e 1489 lseek(clam_fd, 0, SEEK_SET);
420a0d19 1490
2813c06e
CE
1491 if (!(clamav_fbuf = US malloc(fsize_uint)))
1492 {
1493 CLOSE_SOCKDATA; (void)close(clam_fd);
1494 return m_errlog_defer_3(scanent, NULL,
1495 string_sprintf("unable to allocate memory %u for file (%s)",
1496 fsize_uint, eml_filename),
1497 sock);
420a0d19
CE
1498 }
1499
2813c06e
CE
1500 if ((result = read(clam_fd, clamav_fbuf, fsize_uint)) < 0)
1501 {
1502 int err = errno;
1503 free(clamav_fbuf); CLOSE_SOCKDATA; (void)close(clam_fd);
1504 return m_errlog_defer_3(scanent, NULL,
1505 string_sprintf("can't read spool file %s: %s",
1506 eml_filename, strerror(err)),
1507 sock);
420a0d19 1508 }
2813c06e 1509 (void)close(clam_fd);
420a0d19 1510
2813c06e
CE
1511 /* send file body to socket */
1512#ifdef WITH_OLD_CLAMAV_STREAM
1513 if (send(sockData, clamav_fbuf, fsize_uint, 0) < 0)
1514 {
1515 free(clamav_fbuf); CLOSE_SOCKDATA;
1516 return m_errlog_defer_3(scanent, NULL,
1517 string_sprintf("unable to send file body to socket (%s:%u)",
1518 hostname, port),
1519 sock);
1520 }
1521#else
1522 send_size = htonl(fsize_uint);
1523 send_final_zeroblock = 0;
1524 if ((send(sock, &send_size, sizeof(send_size), 0) < 0) ||
1525 (send(sock, clamav_fbuf, fsize_uint, 0) < 0) ||
1526 (send(sock, &send_final_zeroblock, sizeof(send_final_zeroblock), 0) < 0))
1527 {
420a0d19 1528 free(clamav_fbuf);
2813c06e
CE
1529 return m_errlog_defer_3(scanent, NULL,
1530 string_sprintf("unable to send file body to socket (%s)", hostname),
1531 sock);
1532 }
1533#endif
420a0d19 1534
2813c06e 1535 free(clamav_fbuf);
420a0d19 1536
2813c06e
CE
1537 CLOSE_SOCKDATA;
1538#undef CLOSE_SOCKDATA
420a0d19 1539 }
2813c06e
CE
1540 else
1541 { /* use scan command */
1542 /* Send a SCAN command pointing to a filename; then in the then in the
1543 * scan-method-neutral part, read the response back */
1544
1545/* ================================================================= */
1546
1547 /* Prior to the reworking post-Exim-4.72, this scanned a directory,
1548 which dates to when ClamAV needed us to break apart the email into the
1549 MIME parts (eg, with the now deprecated demime condition coming first).
1550 Some time back, ClamAV gained the ability to deconstruct the emails, so
1551 doing this would actually have resulted in the mail attachments being
1552 scanned twice, in the broken out files and from the original .eml.
1553 Since ClamAV now handles emails (and has for quite some time) we can
1554 just use the email file itself. */
1555 /* Pass the string to ClamAV (7 = "SCAN \n" + \0) */
1556 file_name = string_sprintf("SCAN %s\n", eml_filename);
1557
1558 DEBUG(D_acl) debug_printf_indent(
1559 "Malware scan: issuing %s local-path scan [%s]\n",
1560 scanner_name, scanner_options);
420a0d19 1561
2813c06e
CE
1562 if (send(sock, file_name, Ustrlen(file_name), 0) < 0)
1563 return m_errlog_defer_3(scanent, CUS callout_address,
1564 string_sprintf("unable to write to socket (%s)", strerror(errno)),
1565 sock);
1566
1567 /* Do not shut down the socket for writing; a user report noted that
1568 * clamd 0.70 does not react well to this. */
1569 }
1570 /* Commands have been sent, no matter which scan method or connection
1571 * type we're using; now just read the result, independent of method. */
1572
1573 /* Read the result */
1574 memset(av_buffer, 0, sizeof(av_buffer));
1575 bread = ip_recv(sock, av_buffer, sizeof(av_buffer), tmo-time(NULL));
1576 (void)close(sock);
1577 sock = -1;
1578
1579 if (bread <= 0)
1580 return m_errlog_defer(scanent, CUS callout_address,
1581 string_sprintf("unable to read from socket (%s)",
1582 errno == 0 ? "EOF" : strerror(errno)));
1583
1584 if (bread == sizeof(av_buffer))
1585 return m_errlog_defer(scanent, CUS callout_address,
1586 US"buffer too small");
1587 /* We're now assured of a NULL at the end of av_buffer */
1588
1589 /* Check the result. ClamAV returns one of two result formats.
1590 In the basic mode, the response is of the form:
1591 infected: -> "<filename>: <virusname> FOUND"
1592 not-infected: -> "<filename>: OK"
1593 error: -> "<filename>: <errcode> ERROR
1594 If the ExtendedDetectionInfo option has been turned on, then we get:
1595 "<filename>: <virusname>(<virushash>:<virussize>) FOUND"
1596 for the infected case. Compare:
1597/tmp/eicar.com: Eicar-Test-Signature FOUND
1598/tmp/eicar.com: Eicar-Test-Signature(44d88612fea8a8f36de82e1278abb02f:68) FOUND
1599
1600 In the streaming case, clamd uses the filename "stream" which you should
1601 be able to verify with { ktrace clamdscan --stream /tmp/eicar.com }. (The
1602 client app will replace "stream" with the original filename before returning
1603 results to stdout, but the trace shows the data).
1604
1605 We will assume that the pathname passed to clamd from Exim does not contain
1606 a colon. We will have whined loudly above if the eml_filename does (and we're
1607 passing a filename to clamd). */
1608
1609 if (!(*av_buffer))
1610 return m_errlog_defer(scanent, CUS callout_address,
1611 US"ClamAV returned null");
1612
1613 /* strip newline at the end (won't be present for zINSTREAM)
1614 (also any trailing whitespace, which shouldn't exist, but we depend upon
1615 this below, so double-check) */
1616 p = av_buffer + Ustrlen(av_buffer) - 1;
1617 if (*p == '\n') *p = '\0';
1618
1619 DEBUG(D_acl) debug_printf_indent("Malware response: %s\n", av_buffer);
1620
1621 while (isspace(*--p) && (p > av_buffer))
1622 *p = '\0';
1623 if (*p) ++p;
1624
1625 /* colon in returned output? */
1626 if(!(p = Ustrchr(av_buffer,':')))
1627 return m_errlog_defer(scanent, CUS callout_address, string_sprintf(
1628 "ClamAV returned malformed result (missing colon): %s",
1629 av_buffer));
1630
1631 /* strip filename */
1632 while (*p && isspace(*++p)) /**/;
1633 vname = p;
1634
1635 /* It would be bad to encounter a virus with "FOUND" in part of the name,
1636 but we should at least be resistant to it. */
1637 p = Ustrrchr(vname, ' ');
1638 result_tag = p ? p+1 : vname;
1639
1640 if (Ustrcmp(result_tag, "FOUND") == 0)
1641 {
1642 /* p should still be the whitespace before the result_tag */
1643 while (isspace(*p)) --p;
1644 *++p = '\0';
1645 /* Strip off the extended information too, which will be in parens
1646 after the virus name, with no intervening whitespace. */
1647 if (*--p == ')')
1648 {
1649 /* "(hash:size)", so previous '(' will do; if not found, we have
1650 a curious virus name, but not an error. */
1651 p = Ustrrchr(vname, '(');
1652 if (p)
1653 *p = '\0';
420a0d19 1654 }
2813c06e
CE
1655 malware_name = string_copy(vname);
1656 DEBUG(D_acl) debug_printf_indent("Malware found, name \"%s\"\n", malware_name);
420a0d19 1657
2813c06e
CE
1658 }
1659 else if (Ustrcmp(result_tag, "ERROR") == 0)
1660 return m_errlog_defer(scanent, CUS callout_address,
1661 string_sprintf("ClamAV returned: %s", av_buffer));
420a0d19 1662
2813c06e
CE
1663 else if (Ustrcmp(result_tag, "OK") == 0)
1664 {
1665 /* Everything should be OK */
1666 malware_name = NULL;
1667 DEBUG(D_acl) debug_printf_indent("Malware not found\n");
420a0d19 1668
2813c06e
CE
1669 }
1670 else
1671 return m_errlog_defer(scanent, CUS callout_address,
1672 string_sprintf("unparseable response from ClamAV: {%s}", av_buffer));
420a0d19 1673
2813c06e 1674 break;
420a0d19
CE
1675 } /* clamd */
1676
1677 case M_SOCK: /* "sock" scanner type ------------------------------------- */
2813c06e
CE
1678 /* This code was derived by Martin Poole from the clamd code contributed
1679 by David Saez and the cmdline code
1680 */
420a0d19 1681 {
2813c06e
CE
1682 int bread;
1683 uschar * commandline;
1684 uschar av_buffer[1024];
1685 uschar * linebuffer;
1686 uschar * sockline_scanner;
1687 uschar sockline_scanner_default[] = "%s\n";
1688 const pcre *sockline_trig_re;
1689 const pcre *sockline_name_re;
1690
1691 /* find scanner command line */
1692 if ((sockline_scanner = string_nextinlist(&av_scanner_work, &sep,
1693 NULL, 0)))
1694 { /* check for no expansions apart from one %s */
1695 uschar * s = Ustrchr(sockline_scanner, '%');
1696 if (s++)
1697 if ((*s != 's' && *s != '%') || Ustrchr(s+1, '%'))
1698 return m_errlog_defer_3(scanent, NULL,
1699 US"unsafe sock scanner call spec", sock);
1700 }
1701 else
1702 sockline_scanner = sockline_scanner_default;
1703
1704 /* find scanner output trigger */
1705 sockline_trig_re = m_pcre_nextinlist(&av_scanner_work, &sep,
1706 "missing trigger specification", &errstr);
1707 if (!sockline_trig_re)
1708 return m_errlog_defer_3(scanent, NULL, errstr, sock);
1709
1710 /* find virus name regex */
1711 sockline_name_re = m_pcre_nextinlist(&av_scanner_work, &sep,
1712 "missing virus name regex specification", &errstr);
1713 if (!sockline_name_re)
1714 return m_errlog_defer_3(scanent, NULL, errstr, sock);
1715
1716 /* prepare scanner call - security depends on expansions check above */
1717 commandline = string_sprintf("%s/scan/%s/%s.eml", spool_directory, message_id, message_id);
1718 commandline = string_sprintf( CS sockline_scanner, CS commandline);
1719
1720
1721 /* Pass the command string to the socket */
1722 if (m_sock_send(sock, commandline, Ustrlen(commandline), &errstr) < 0)
1723 return m_errlog_defer(scanent, CUS callout_address, errstr);
1724
1725 /* Read the result */
1726 bread = ip_recv(sock, av_buffer, sizeof(av_buffer), tmo-time(NULL));
1727
1728 if (bread <= 0)
1729 return m_errlog_defer_3(scanent, CUS callout_address,
1730 string_sprintf("unable to read from socket (%s)", strerror(errno)),
1731 sock);
1732
1733 if (bread == sizeof(av_buffer))
1734 return m_errlog_defer_3(scanent, CUS callout_address,
1735 US"buffer too small", sock);
1736 av_buffer[bread] = '\0';
1737 linebuffer = string_copy(av_buffer);
1738
1739 /* try trigger match */
1740 if (regex_match_and_setup(sockline_trig_re, linebuffer, 0, -1))
1741 {
1742 if (!(malware_name = m_pcre_exec(sockline_name_re, av_buffer)))
1743 malware_name = US "unknown";
420a0d19 1744 }
2813c06e
CE
1745 else /* no virus found */
1746 malware_name = NULL;
1747 break;
1748 }
420a0d19 1749
2813c06e
CE
1750 case M_MKSD: /* "mksd" scanner type ------------------------------------- */
1751 {
1752 char *mksd_options_end;
1753 int mksd_maxproc = 1; /* default, if no option supplied */
1754 int retval;
420a0d19 1755
2813c06e
CE
1756 if (scanner_options)
1757 {
1758 mksd_maxproc = (int)strtol(CS scanner_options, &mksd_options_end, 10);
1759 if ( *scanner_options == '\0'
1760 || *mksd_options_end != '\0'
1761 || mksd_maxproc < 1
1762 || mksd_maxproc > 32
1763 )
1764 return m_errlog_defer(scanent, CUS callout_address,
1765 string_sprintf("invalid option '%s'", scanner_options));
1766 }
420a0d19 1767
2813c06e
CE
1768 if((sock = ip_unixsocket(US "/var/run/mksd/socket", &errstr)) < 0)
1769 return m_errlog_defer(scanent, CUS callout_address, errstr);
420a0d19 1770
2813c06e 1771 malware_name = NULL;
420a0d19 1772
2813c06e 1773 DEBUG(D_acl) debug_printf_indent("Malware scan: issuing %s scan\n", scanner_name);
420a0d19 1774
2813c06e
CE
1775 if ((retval = mksd_scan_packed(scanent, sock, eml_filename, tmo)) != OK)
1776 {
1777 close (sock);
1778 return retval;
420a0d19 1779 }
2813c06e 1780 break;
420a0d19
CE
1781 }
1782
2813c06e 1783 case M_AVAST: /* "avast" scanner type ----------------------------------- */
420a0d19 1784 {
2813c06e
CE
1785 int ovector[1*3];
1786 uschar buf[1024];
1787 uschar * scanrequest;
1788 enum {AVA_HELO, AVA_OPT, AVA_RSP, AVA_DONE} avast_stage;
1789 int nread;
1790
1791 /* According to Martin Tuma @avast the protocol uses "escaped
1792 whitespace", that is, every embedded whitespace is backslash
1793 escaped, as well as backslash is protected by backslash.
1794 The returned lines contain the name of the scanned file, a tab
1795 and the [ ] marker.
1796 [+] - not infected
1797 [L] - infected
1798 [E] - some error occured
1799 Such marker follows the first non-escaped TAB. */
1800 if ( ( !ava_re_clean
1801 && !(ava_re_clean = m_pcre_compile(ava_re_clean_str, &errstr)))
1802 || ( !ava_re_virus
1803 && !(ava_re_virus = m_pcre_compile(ava_re_virus_str, &errstr)))
1804 )
1805 return malware_errlog_defer(errstr);
1806
1807 /* wait for result */
1808 for (avast_stage = AVA_HELO;
1809 (nread = recv_line(sock, buf, sizeof(buf), tmo)) > 0;
1810 )
1811 {
1812 int slen = Ustrlen(buf);
1813 if (slen >= 1)
1814 {
1815 DEBUG(D_acl) debug_printf_indent("got from avast: %s\n", buf);
1816 switch (avast_stage)
1817 {
1818 case AVA_HELO:
1819 if (Ustrncmp(buf, "220", 3) != 0)
1820 goto endloop; /* require a 220 */
1821 goto sendreq;
1822
1823 case AVA_OPT:
1824 if (Ustrncmp(buf, "210", 3) == 0)
1825 break; /* ignore 210 responses */
1826 if (Ustrncmp(buf, "200", 3) != 0)
1827 goto endloop; /* require a 200 */
1828
1829 sendreq:
1830 {
1831 int len;
1832 /* Check for another option to send. Newline-terminate it. */
1833 if ((scanrequest = string_nextinlist(&av_scanner_work, &sep,
1834 NULL, 0)))
1835 {
1836 scanrequest = string_sprintf("%s\n", scanrequest);
1837 avast_stage = AVA_OPT; /* just sent option */
1838 }
1839 else
1840 {
1841 scanrequest = string_sprintf("SCAN %s/scan/%s\n",
1842 spool_directory, message_id);
1843 avast_stage = AVA_RSP; /* just sent command */
1844 }
1845
1846 /* send config-cmd or scan-request to socket */
1847 len = Ustrlen(scanrequest);
1848 if (send(sock, scanrequest, len, 0) < 0)
1849 {
1850 scanrequest[len-1] = '\0';
1851 return m_errlog_defer_3(scanent, CUS callout_address, string_sprintf(
1852 "unable to send request '%s' to socket (%s): %s",
1853 scanrequest, scanner_options, strerror(errno)), sock);
1854 }
1855 break;
1856 }
420a0d19 1857
2813c06e
CE
1858 case AVA_RSP:
1859 if (Ustrncmp(buf, "210", 3) == 0)
1860 break; /* ignore the "210 SCAN DATA" message */
420a0d19 1861
2813c06e
CE
1862 if (pcre_exec(ava_re_clean, NULL, CS buf, slen,
1863 0, 0, ovector, nelements(ovector)) > 0)
1864 break;
420a0d19 1865
2813c06e
CE
1866 if ((malware_name = m_pcre_exec(ava_re_virus, buf)))
1867 { /* remove backslash in front of [whitespace|backslash] */
1868 uschar * p, * p0;
1869 for (p = malware_name; *p; ++p)
1870 if (*p == '\\' && (isspace(p[1]) || p[1] == '\\'))
1871 for (p0 = p; *p0; ++p0) *p0 = p0[1];
420a0d19 1872
2813c06e
CE
1873 avast_stage = AVA_DONE;
1874 goto endloop;
1875 }
1876
1877 if (Ustrncmp(buf, "200 SCAN OK", 11) == 0)
1878 { /* we're done finally */
1879 if (send(sock, "QUIT\n", 5, 0) < 0) /* courtesy */
1880 return m_errlog_defer_3(scanent, CUS callout_address,
1881 string_sprintf(
1882 "unable to send quit request to socket (%s): %s",
1883 scanner_options, strerror(errno)),
1884 sock);
1885 malware_name = NULL;
1886 avast_stage = AVA_DONE;
1887 goto endloop;
1888 }
1889
1890 /* here for any unexpected response from the scanner */
1891 goto endloop;
1892
1893 case AVA_DONE: log_write(0, LOG_PANIC, "%s:%d:%s: should not happen",
1894 __FILE__, __LINE__, __FUNCTION__);
1895 }
1896 }
1897 }
1898 endloop:
1899
1900 switch(avast_stage)
1901 {
1902 case AVA_HELO:
1903 case AVA_OPT:
1904 case AVA_RSP: return m_errlog_defer_3(scanent, CUS callout_address,
1905 nread >= 0
1906 ? string_sprintf(
1907 "invalid response from scanner: '%s'", buf)
1908 : nread == -1
1909 ? US"EOF from scanner"
1910 : US"timeout from scanner",
1911 sock);
1912 default: break;
420a0d19 1913 }
420a0d19 1914 }
2813c06e
CE
1915 break;
1916 } /* scanner type switch */
420a0d19 1917
2813c06e
CE
1918 if (sock >= 0)
1919 (void) close (sock);
1920 malware_ok = TRUE; /* set "been here, done that" marker */
420a0d19
CE
1921 }
1922
2813c06e
CE
1923/* match virus name against pattern (caseless ------->----------v) */
1924if (malware_name && regex_match_and_setup(re, malware_name, 0, -1))
1925 {
1926 DEBUG(D_acl) debug_printf_indent(
1927 "Matched regex to malware [%s] [%s]\n", malware_re, malware_name);
1928 return OK;
420a0d19 1929 }
2813c06e
CE
1930else
1931 return FAIL;
420a0d19
CE
1932}
1933
1934
2813c06e
CE
1935/*************************************************
1936* Scan an email for malware *
1937*************************************************/
1938
1939/* This is the normal interface for scanning an email, which doesn't need a
1940filename; it's a wrapper around the malware_file function.
1941
1942Arguments:
1943 malware_re match condition for "malware="
1944 timeout if nonzero, timeout in seconds
1945
1946Returns: Exim message processing code (OK, FAIL, DEFER, ...)
1947 where true means malware was found (condition applies)
1948*/
420a0d19 1949int
2813c06e 1950malware(const uschar * malware_re, int timeout)
420a0d19 1951{
2813c06e
CE
1952uschar * scan_filename;
1953int ret;
420a0d19 1954
2813c06e
CE
1955scan_filename = string_sprintf("%s/scan/%s/%s.eml",
1956 spool_directory, message_id, message_id);
1957ret = malware_internal(malware_re, scan_filename, timeout, FALSE);
1958if (ret == DEFER) av_failed = TRUE;
1959
1960return ret;
420a0d19
CE
1961}
1962
1963
2813c06e
CE
1964/*************************************************
1965* Scan a file for malware *
1966*************************************************/
420a0d19 1967
2813c06e
CE
1968/* This is a test wrapper for scanning an email, which is not used in
1969normal processing. Scan any file, using the Exim scanning interface.
1970This function tampers with various global variables so is unsafe to use
1971in any other context.
420a0d19 1972
2813c06e
CE
1973Arguments:
1974 eml_filename a file holding the message to be scanned
420a0d19 1975
2813c06e
CE
1976Returns: Exim message processing code (OK, FAIL, DEFER, ...)
1977 where true means malware was found (condition applies)
1978*/
1979int
1980malware_in_file(uschar *eml_filename)
420a0d19 1981{
2813c06e
CE
1982uschar message_id_buf[64];
1983int ret;
420a0d19 1984
2813c06e
CE
1985/* spool_mbox() assumes various parameters exist, when creating
1986the relevant directory and the email within */
420a0d19 1987
2813c06e
CE
1988(void) string_format(message_id_buf, sizeof(message_id_buf),
1989 "dummy-%d", vaguely_random_number(INT_MAX));
1990message_id = message_id_buf;
1991sender_address = US"malware-sender@example.net";
1992return_path = US"";
1993recipients_list = NULL;
1994receive_add_recipient(US"malware-victim@example.net", -1);
1995enable_dollar_recipients = TRUE;
420a0d19 1996
2813c06e 1997ret = malware_internal(US"*", eml_filename, 0, TRUE);
420a0d19 1998
2813c06e
CE
1999Ustrncpy(spooled_message_id, message_id, sizeof(spooled_message_id));
2000spool_mbox_ok = 1;
420a0d19 2001
2813c06e
CE
2002/* don't set no_mbox_unspool; at present, there's no way for it to become
2003set, but if that changes, then it should apply to these tests too */
420a0d19 2004
2813c06e 2005unspool_mbox();
420a0d19 2006
2813c06e
CE
2007/* silence static analysis tools */
2008message_id = NULL;
2009
2010return ret;
2011}
420a0d19 2012
420a0d19 2013
2813c06e
CE
2014void
2015malware_init(void)
2016{
2017if (!malware_default_re)
2018 malware_default_re = regex_must_compile(malware_regex_default, FALSE, TRUE);
2019if (!drweb_re)
2020 drweb_re = regex_must_compile(drweb_re_str, FALSE, TRUE);
2021if (!fsec_re)
2022 fsec_re = regex_must_compile(fsec_re_str, FALSE, TRUE);
2023if (!kav_re_sus)
2024 kav_re_sus = regex_must_compile(kav_re_sus_str, FALSE, TRUE);
2025if (!kav_re_inf)
2026 kav_re_inf = regex_must_compile(kav_re_inf_str, FALSE, TRUE);
2027if (!ava_re_clean)
2028 ava_re_clean = regex_must_compile(ava_re_clean_str, FALSE, TRUE);
2029if (!ava_re_virus)
2030 ava_re_virus = regex_must_compile(ava_re_virus_str, FALSE, TRUE);
420a0d19
CE
2031}
2032
2033#endif /*WITH_CONTENT_SCAN*/
2034/*
2035 * vi: aw ai sw=2
2036 */