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