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