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