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