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