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