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