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