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