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