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