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