1 /*************************************************
2 * Exim - an Internet mail transport agent *
3 *************************************************/
5 /* Copyright (c) Tom Kistner <tom@duncanthrax.net> 2003-2013 */
8 /* Code for calling virus (malware) scanners. Called from acl.c. */
11 #ifdef WITH_CONTENT_SCAN
13 /* The maximum number of clamd servers that are supported in the configuration */
14 #define MAX_CLAMD_SERVERS 32
15 #define MAX_CLAMD_SERVERS_S "32"
16 /* Maximum length of the hostname that can be specified in the clamd address list */
17 #define MAX_CLAMD_ADDRESS_LENGTH 64
18 #define MAX_CLAMD_ADDRESS_LENGTH_S "64"
20 typedef struct clamd_address_container {
21 uschar tcp_addr[MAX_CLAMD_ADDRESS_LENGTH];
22 unsigned int tcp_port;
23 } clamd_address_container;
25 /* declaration of private routines */
26 static int mksd_scan_packed(int sock, uschar *scan_filename);
27 static int malware_internal(uschar **listptr, uschar *eml_filename, BOOL faking);
29 /* SHUT_WR seems to be undefined on Unixware? */
35 # define nelements(arr) (sizeof(arr) / sizeof(arr[0]))
39 #define MALWARE_TIMEOUT 120
42 #define DRWEBD_SCAN_CMD (1) /* scan file, buffer or diskfile */
43 #define DRWEBD_RETURN_VIRUSES (1<<0) /* ask daemon return to us viruses names from report */
44 #define DRWEBD_IS_MAIL (1<<19) /* say to daemon that format is "archive MAIL" */
46 #define DERR_READ_ERR (1<<0) /* read error */
47 #define DERR_NOMEMORY (1<<2) /* no memory */
48 #define DERR_TIMEOUT (1<<9) /* scan timeout has run out */
49 #define DERR_BAD_CALL (1<<15) /* wrong command */
51 /* Routine to check whether a system is big- or litte-endian.
52 Ripped from http://www.faqs.org/faqs/graphics/fileformats-faq/part4/section-7.html
53 Needed for proper kavdaemon implementation. Sigh. */
54 #define BIG_MY_ENDIAN 0
55 #define LITTLE_MY_ENDIAN 1
56 int test_byte_order(void);
60 short int word = 0x0001;
61 char *byte = (char *) &word;
62 return(byte[0] ? LITTLE_MY_ENDIAN : BIG_MY_ENDIAN);
65 static uschar * malware_name_internal = NULL;
68 /* Gross hacks for the -bmalware option; perhaps we should just create
69 the scan directory normally for that case, but look into rigging up the
70 needed header variables if not already set on the command-line? */
71 extern int spool_mbox_ok;
72 extern uschar spooled_message_id[17];
74 /*************************************************
75 * Scan an email for malware *
76 *************************************************/
78 /* This is the normal interface for scanning an email, which doesn't need a
79 filename; it's a wrapper around the malware_file function.
82 listptr the list of options to the "malware = ..." ACL condition
84 Returns: Exim message processing code (OK, FAIL, DEFER, ...)
85 where true means malware was found (condition applies)
88 malware(uschar **listptr)
90 uschar * scan_filename;
93 scan_filename = string_sprintf("%s/scan/%s/%s.eml",
94 spool_directory, message_id, message_id);
95 ret = malware_internal(listptr, scan_filename, FALSE);
96 if (ret == DEFER) av_failed = TRUE;
102 /*************************************************
103 * Scan a file for malware *
104 *************************************************/
106 /* This is a test wrapper for scanning an email, which is not used in
107 normal processing. Scan any file, using the Exim scanning interface.
108 This function tampers with various global variables so is unsafe to use
109 in any other context.
112 eml_filename a file holding the message to be scanned
114 Returns: Exim message processing code (OK, FAIL, DEFER, ...)
115 where true means malware was found (condition applies)
118 malware_in_file(uschar *eml_filename)
120 uschar *scan_options[2];
121 uschar message_id_buf[64];
124 scan_options[0] = US"*";
125 scan_options[1] = NULL;
127 /* spool_mbox() assumes various parameters exist, when creating
128 the relevant directory and the email within */
129 (void) string_format(message_id_buf, sizeof(message_id_buf),
130 "dummy-%d", vaguely_random_number(INT_MAX));
131 message_id = message_id_buf;
132 sender_address = US"malware-sender@example.net";
134 recipients_list = NULL;
135 receive_add_recipient(US"malware-victim@example.net", -1);
136 enable_dollar_recipients = TRUE;
138 ret = malware_internal(scan_options, eml_filename, TRUE);
140 Ustrncpy(spooled_message_id, message_id, sizeof(spooled_message_id));
142 /* don't set no_mbox_unspool; at present, there's no way for it to become
143 set, but if that changes, then it should apply to these tests too */
146 /* silence static analysis tools */
154 malware_errlog(const uschar * str)
156 log_write(0, LOG_MAIN|LOG_PANIC, "malware acl condition: %s", str);
159 malware_errlog_defer(const uschar * str)
166 m_scanner_errlog_defer(const uschar * scanner, const uschar * str)
168 return malware_errlog_defer(string_sprintf("%s: %s", scanner, str));
172 fprotd_errlog_defer(const uschar * str)
174 return m_scanner_errlog_defer("f-protd", str);
177 drweb_errlog_defer(const uschar * str)
179 return m_scanner_errlog_defer("drweb", str);
182 aves_errlog_defer(const uschar * str)
184 return m_scanner_errlog_defer("aveserver", str);
187 fsec_errlog_defer(const uschar * str)
189 return m_scanner_errlog_defer("fsecure", str);
192 kavd_errlog_defer(const uschar * str)
194 return m_scanner_errlog_defer("kavdaemon", str);
197 cmdl_errlog_defer(const uschar * str)
199 return m_scanner_errlog_defer("commandline", str);
202 soph_errlog_defer(const uschar * str)
204 return m_scanner_errlog_defer("sophie", str);
207 clmd_errlog_defer(const uschar * str)
209 return m_scanner_errlog_defer("clamd", str);
212 mksd_errlog_defer(const uschar * str)
214 return m_scanner_errlog_defer("mksd", str);
217 sock_errlog_defer(const uschar * str)
219 return m_scanner_errlog_defer("sock", str);
223 clmd_errlog(const uschar * str)
225 log_write(0, LOG_MAIN|LOG_PANIC, "malware acl condition: clamd: %s", str);
228 /*************************************************/
231 m_streamsocket(const uschar * hostname, struct in_addr * inp, uschar ** errstr)
236 /* Lookup the host */
237 if(!(he = gethostbyname(CS hostname))) {
238 *errstr = string_sprintf("failed to lookup host '%s'", hostname);
241 *inp = *(struct in_addr *) he->h_addr_list[0];
243 /* Create the TCP socket */
244 if ((sock = ip_socket(SOCK_STREAM, AF_INET)) >= 0)
247 *errstr = string_sprintf("unable to acquire socket (%s)", strerror(errno));
252 m_tcpsocket(const uschar * hostname, unsigned int port,
253 struct in_addr * inp, uschar ** errstr)
257 if ((sock = m_streamsocket(hostname, inp, errstr)) < 0)
260 if (ip_connect(sock, AF_INET, (uschar*)inet_ntoa(*inp), port, 5) >= 0)
263 *errstr = string_sprintf("connection to %s, port %u failed (%s)",
264 inet_ntoa(*inp), port, strerror(errno));
270 m_tcpsocket_fromdef(const uschar * hostport, uschar ** errstr)
273 uschar hostname[256];
274 unsigned int port, portlow, porthigh;
279 /* extract host and port part */
280 scan = sscanf(CS hostport, "%255s %u-%u", hostname, &portlow, &porthigh);
283 *errstr = string_sprintf("invalid socket '%s'", hostport);
289 if ((sock = m_streamsocket(hostname, &in, errstr)) < 0)
292 /* Try to connect to all ports low-high until connection is established */
293 for (port = portlow; port <= porthigh; port++)
294 if (ip_connect(sock, AF_INET, (uschar*)inet_ntoa(in), port, 5) >= 0)
297 *errstr = string_sprintf("connection to %s, port %u-%u failed (%s)",
298 inet_ntoa(in), portlow, porthigh, strerror(errno));
304 m_unixsocket(const uschar * path, uschar ** errstr)
307 struct sockaddr_un server;
309 if ((sock = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
310 *errstr = "can't open UNIX socket.";
314 server.sun_family = AF_UNIX;
315 Ustrcpy(server.sun_path, path);
316 if (connect(sock, (struct sockaddr *) &server, sizeof(server)) < 0) {
319 *errstr = string_sprintf("unable to connect to UNIX socket (%s): %s",
320 path, strerror(err));
327 m_sock_send(int sock, uschar * buf, int cnt, uschar ** errstr)
329 if (send(sock, buf, cnt, 0) < 0) {
332 *errstr = string_sprintf("unable to send to socket (%s): %s",
339 /*************************************************
340 * Scan content for malware *
341 *************************************************/
343 typedef enum {M_FPROTD, M_DRWEB, M_AVES, M_FSEC, M_KAVD, M_CMDL,
344 M_SOPHIE, M_CLAMD, M_SOCK, M_MKSD} scanner_t;
349 const char * options_default;
352 { M_FPROTD, "f-protd", "localhost 10200-10204" },
353 { M_DRWEB, "drweb", "/usr/local/drweb/run/drwebd.sock" },
354 { M_AVES, "aveserver", "/var/run/aveserver" },
355 { M_FSEC, "fsecure", "/var/run/.fsav" },
356 { M_KAVD, "kavdaemon", "/var/run/AvpCtl" },
357 { M_CMDL, "cmdline", NULL },
358 { M_SOPHIE, "sophie", "/var/run/sophie" },
359 { M_CLAMD, "clamd", "/tmp/clamd" },
360 { M_SOCK, "sock", "/tmp/malware.sock" },
361 { M_MKSD, "mksd", NULL },
362 { -1, NULL, NULL } /* end-marker */
365 /* This is an internal interface for scanning an email; the normal interface
366 is via malware(), or there's malware_in_file() used for testing/debugging.
369 listptr the list of options to the "malware = ..." ACL condition
370 eml_filename the file holding the email to be scanned
371 faking whether or not we're faking this up for the -bmalware test
373 Returns: Exim message processing code (OK, FAIL, DEFER, ...)
374 where true means malware was found (condition applies)
377 malware_internal(uschar **listptr, uschar *eml_filename, BOOL faking)
380 uschar *list = *listptr;
381 uschar *av_scanner_work = av_scanner;
382 uschar *scanner_name;
383 uschar *malware_regex;
384 uschar malware_regex_default[] = ".+";
385 unsigned long mbox_size;
389 const uschar *rerror;
391 struct scan * scanent;
392 const uschar * scanner_options;
394 /* make sure the eml mbox file is spooled up */
395 mbox_file = spool_mbox(&mbox_size, faking ? eml_filename : NULL);
396 if (mbox_file == NULL) /* error while spooling */
397 return malware_errlog_defer("error while creating mbox spool file");
399 /* none of our current scanners need the mbox
400 file as a stream, so we can close it right away */
401 (void)fclose(mbox_file);
403 /* extract the malware regex to match against from the option list */
404 if (!(malware_regex = string_nextinlist(&list, &sep, NULL, 0))) {
406 /* parse 1st option */
407 if ( (strcmpic(malware_regex,US"false") == 0) ||
408 (Ustrcmp(malware_regex,"0") == 0) ) {
409 /* explicitly no matching */
413 /* special cases (match anything except empty) */
414 if ( (strcmpic(malware_regex,US"true") == 0) ||
415 (Ustrcmp(malware_regex,"*") == 0) ||
416 (Ustrcmp(malware_regex,"1") == 0) ) {
417 malware_regex = malware_regex_default;
420 else /* empty means "don't match anything" */
423 /* Reset sep that is set by previous string_nextinlist() call */
426 /* compile the regex, see if it works */
427 re = pcre_compile(CS malware_regex, PCRE_COPT, (const char **)&rerror, &roffset, NULL);
429 return malware_errlog_defer(
430 string_sprintf("regular expression error in '%s': %s at offset %d",
431 malware_regex, rerror, roffset));
433 /* if av_scanner starts with a dollar, expand it first */
434 if (*av_scanner == '$') {
435 av_scanner_work = expand_string(av_scanner);
436 if (!av_scanner_work)
437 return malware_errlog_defer(
438 string_sprintf("av_scanner starts with $, but expansion failed: %s",
439 expand_string_message));
441 debug_printf("Expanded av_scanner global: %s\n", av_scanner_work);
442 /* disable result caching in this case */
448 /* Do not scan twice. */
449 if (malware_ok == 0) {
451 /* find the scanner type from the av_scanner option */
452 if (!(scanner_name = string_nextinlist(&av_scanner_work, &sep, NULL, 0)))
453 return malware_errlog_defer("av_scanner configuration variable is empty");
455 for (scanent = m_scans; ; scanent++) {
457 return malware_errlog_defer(string_sprintf("unknown scanner type '%s'",
459 if (strcmpic(scanner_name, US scanent->name) == 0)
462 if (!(scanner_options = string_nextinlist(&av_scanner_work, &sep, NULL, 0)))
463 scanner_options = scanent->options_default;
465 switch (scanent->scancode) {
466 case M_FPROTD: /* "f-protd" scanner type -------------------------------- */
468 uschar *fp_scan_option;
469 unsigned int detected=0, par_count=0;
471 uschar * scanrequest;
472 uschar buf[32768], *strhelper, *strhelper2;
474 if ((sock = m_tcpsocket_fromdef(scanner_options, &errstr)) < 0)
475 return fprotd_errlog_defer(errstr);
477 DEBUG(D_acl) debug_printf("Malware scan: issuing %s GET\n", scanner_name);
478 scanrequest = string_sprintf("GET %s", eml_filename);
480 while ((fp_scan_option = string_nextinlist(&av_scanner_work, &sep,
482 scanrequest = string_sprintf("%s%s%s", scanrequest,
483 par_count ? "%20" : "?", fp_scan_option);
486 scanrequest = string_sprintf("%s HTTP/1.0\r\n\r\n", scanrequest);
488 /* send scan request */
489 if (m_sock_send(sock, scanrequest, Ustrlen(scanrequest)+1, &errstr) < 0)
490 return fprotd_errlog_defer(errstr);
492 /* We get a lot of empty lines, so we need this hack to check for any data at all */
493 while( recv(sock, buf, 1, MSG_PEEK) > 0 ) {
494 if ( recv_line(sock, buf, 32768) > 0) {
495 if ( Ustrstr(buf, US"<detected type=\"") != NULL )
497 else if ( detected && (strhelper = Ustrstr(buf, US"<name>")) ) {
498 if ((strhelper2 = Ustrstr(buf, US"</name>")) != NULL) {
500 malware_name_internal = string_copy(strhelper+6);
502 } else if ( Ustrstr(buf, US"<summary code=\"") )
503 malware_name = Ustrstr(buf, US"<summary code=\"11\">")
504 ? malware_name_internal : NULL;
511 case M_DRWEB: /* "drweb" scanner type ----------------------------------- */
512 /* v0.1 - added support for tcp sockets */
513 /* v0.0 - initial release -- support for unix sockets */
515 struct sockaddr_un server;
516 int sock, result, ovector[10*3];
518 uschar * tmpbuf, *drweb_fbuf;
519 int drweb_rc, drweb_cmd, drweb_flags = 0x0000, drweb_fd,
520 drweb_vnum, drweb_slen, drweb_fin = 0x0000;
524 if (*scanner_options != '/') {
525 if ((sock = m_tcpsocket_fromdef(scanner_options, &errstr)) < 0)
526 return drweb_errlog_defer(errstr);
528 /* prepare variables */
529 drweb_cmd = htonl(DRWEBD_SCAN_CMD);
530 drweb_flags = htonl(DRWEBD_RETURN_VIRUSES | DRWEBD_IS_MAIL);
533 drweb_fd = open(CS eml_filename, O_RDONLY);
534 if (drweb_fd == -1) {
537 return drweb_errlog_defer(
538 string_sprintf("can't open spool file %s: %s",
539 eml_filename, strerror(err)));
541 fsize = lseek(drweb_fd, 0, SEEK_END);
545 (void)close(drweb_fd);
546 return drweb_errlog_defer(
547 string_sprintf("can't seek spool file %s: %s",
548 eml_filename, strerror(err)));
550 drweb_slen = htonl(fsize);
551 lseek(drweb_fd, 0, SEEK_SET);
553 DEBUG(D_acl) debug_printf("Malware scan: issuing %s remote scan [%s]\n",
554 scanner_name, scanner_options);
556 /* send scan request */
557 if ((send(sock, &drweb_cmd, sizeof(drweb_cmd), 0) < 0) ||
558 (send(sock, &drweb_flags, sizeof(drweb_flags), 0) < 0) ||
559 (send(sock, &drweb_fin, sizeof(drweb_fin), 0) < 0) ||
560 (send(sock, &drweb_slen, sizeof(drweb_slen), 0) < 0)) {
562 (void)close(drweb_fd);
563 return drweb_errlog_defer(
564 string_sprintf("unable to send commands to socket (%s)", scanner_options));
567 drweb_fbuf = (uschar *) malloc (fsize);
570 (void)close(drweb_fd);
571 return drweb_errlog_defer(
572 string_sprintf("unable to allocate memory %u for file (%s)",
573 fsize, eml_filename));
576 result = read (drweb_fd, drweb_fbuf, fsize);
580 (void)close(drweb_fd);
582 return drweb_errlog_defer(
583 string_sprintf("can't read spool file %s: %s",
584 eml_filename, strerror(err)));
586 (void)close(drweb_fd);
588 /* send file body to socket */
589 if (send(sock, drweb_fbuf, fsize, 0) < 0) {
592 return drweb_errlog_defer(
593 string_sprintf("unable to send file body to socket (%s)", scanner_options));
595 (void)close(drweb_fd);
598 if((sock = m_unixsocket(scanner_options, &errstr)) < 0)
599 return drweb_errlog_defer(errstr);
601 /* prepare variables */
602 drweb_cmd = htonl(DRWEBD_SCAN_CMD);
603 drweb_flags = htonl(DRWEBD_RETURN_VIRUSES | DRWEBD_IS_MAIL);
604 drweb_slen = htonl(Ustrlen(eml_filename));
606 DEBUG(D_acl) debug_printf("Malware scan: issuing %s local scan [%s]\n",
607 scanner_name, scanner_options);
609 /* send scan request */
610 if ((send(sock, &drweb_cmd, sizeof(drweb_cmd), 0) < 0) ||
611 (send(sock, &drweb_flags, sizeof(drweb_flags), 0) < 0) ||
612 (send(sock, &drweb_slen, sizeof(drweb_slen), 0) < 0) ||
613 (send(sock, eml_filename, Ustrlen(eml_filename), 0) < 0) ||
614 (send(sock, &drweb_fin, sizeof(drweb_fin), 0) < 0)) {
616 return drweb_errlog_defer(
617 string_sprintf("unable to send commands to socket (%s)", scanner_options));
621 /* wait for result */
622 if ((bread = recv(sock, &drweb_rc, sizeof(drweb_rc), 0) != sizeof(drweb_rc))) {
624 return drweb_errlog_defer("unable to read return code");
626 drweb_rc = ntohl(drweb_rc);
628 if ((bread = recv(sock, &drweb_vnum, sizeof(drweb_vnum), 0) != sizeof(drweb_vnum))) {
630 return drweb_errlog_defer("unable to read the number of viruses");
632 drweb_vnum = ntohl(drweb_vnum);
634 /* "virus(es) found" if virus number is > 0 */
638 /* setup default virus name */
639 malware_name_internal = "unknown";
640 malware_name = malware_name_internal;
642 /* set up match regex */
643 drweb_re = pcre_compile( "infected\\swith\\s*(.+?)$", PCRE_COPT,
644 (const char **)&rerror, &roffset, NULL );
646 /* read and concatenate virus names into one string */
647 for (i=0;i<drweb_vnum;i++)
649 int size = 0, off = 0;
650 /* read the size of report */
651 if ((bread = recv(sock, &drweb_slen, sizeof(drweb_slen), 0) != sizeof(drweb_slen))) {
653 return drweb_errlog_defer("cannot read report size");
655 drweb_slen = ntohl(drweb_slen);
656 tmpbuf = store_get(drweb_slen);
658 /* read report body */
659 if ((bread = recv(sock, tmpbuf, drweb_slen, 0)) != drweb_slen) {
661 return drweb_errlog_defer("cannot read report string");
663 tmpbuf[drweb_slen] = '\0';
665 /* try matcher on the line, grab substring */
666 result = pcre_exec(drweb_re, NULL, CS tmpbuf, Ustrlen(tmpbuf), 0, 0,
667 ovector, nelements(ovector));
669 const char * pre_malware_nb;
671 pcre_get_substring(CS tmpbuf, ovector, result, 1, &pre_malware_nb);
673 /* the first name we just copy to malware_name */
675 malware_name_internal = string_append(NULL, &size, &off,
678 /* concatenate each new virus name to previous */
679 malware_name_internal = string_append(malware_name_internal,
680 &size, &off, 2, "/", pre_malware_nb);
682 pcre_free_substring(pre_malware_nb);
687 const char *drweb_s = NULL;
689 if (drweb_rc & DERR_READ_ERR) drweb_s = "read error";
690 if (drweb_rc & DERR_NOMEMORY) drweb_s = "no memory";
691 if (drweb_rc & DERR_TIMEOUT) drweb_s = "timeout";
692 if (drweb_rc & DERR_BAD_CALL) drweb_s = "wrong command";
693 /* retcodes DERR_SYMLINK, DERR_NO_REGFILE, DERR_SKIPPED.
694 * DERR_TOO_BIG, DERR_TOO_COMPRESSED, DERR_SPAM,
695 * DERR_CRC_ERROR, DERR_READSOCKET, DERR_WRITE_ERR
696 * and others are ignored */
699 return drweb_errlog_defer(
700 string_sprintf("drweb daemon retcode 0x%x (%s)", drweb_rc, drweb_s));
709 case M_AVES: /* "aveserver" scanner type -------------------------------- */
712 struct sockaddr_un server;
716 if((sock = m_unixsocket(scanner_options, &errstr)) < 0)
717 return aves_errlog_defer(errstr);
719 /* read aveserver's greeting and see if it is ready (2xx greeting) */
720 recv_line(sock, buf, sizeof(buf));
723 /* aveserver is having problems */
725 return aves_errlog_defer(
726 string_sprintf("unavailable (Responded: %s).", ((buf[0] != 0) ? buf : (uschar *)"nothing") ));
729 /* prepare our command */
730 (void)string_format(buf, sizeof(buf), "SCAN bPQRSTUW %s\r\n",
733 DEBUG(D_acl) debug_printf("Malware scan: issuing %s SCAN\n", scanner_name);
736 if (m_sock_send(sock, buf, Ustrlen(buf), &errstr) < 0)
737 return aves_errlog_defer(errstr);
741 /* read response lines, find malware name and final response */
742 while (recv_line(sock, buf, sizeof(buf)) > 0) {
743 debug_printf("aveserver: %s\n", buf);
747 /* aveserver is having problems */
748 log_write(0, LOG_MAIN|LOG_PANIC,
749 "malware acl condition: unable to scan file %s (Responded: %s).",
753 } else if (Ustrncmp(buf,"322",3) == 0) {
754 uschar *p = Ustrchr(&buf[4],' ');
756 malware_name_internal = string_copy(&buf[4]);
757 malware_name = malware_name_internal;
761 /* prepare our command */
762 (void)string_format(buf, sizeof(buf), "quit\r\n");
765 if (m_sock_send(sock, buf, Ustrlen(buf), &errstr) < 0)
766 return aves_errlog_defer(errstr);
768 /* read aveserver's greeting and see if it is ready (2xx greeting) */
769 recv_line(sock, buf, sizeof(buf));
772 /* aveserver is having problems */
774 return aves_errlog_defer(
775 string_sprintf("unable to quit dialogue (Responded: %s).",
776 ((buf[0] != 0) ? buf : (uschar *)"nothing") ));
781 if (result == DEFER) return DEFER;
785 case M_FSEC: /* "fsecure" scanner type ---------------------------------- */
787 struct sockaddr_un server;
788 int sock, i, j, bread = 0;
790 uschar av_buffer[1024];
792 static uschar *cmdopt[] = { US"CONFIGURE\tARCHIVE\t1\n",
793 US"CONFIGURE\tTIMEOUT\t0\n",
794 US"CONFIGURE\tMAXARCH\t5\n",
795 US"CONFIGURE\tMIME\t1\n" };
799 if((sock = m_unixsocket(scanner_options, &errstr)) < 0)
800 return fsec_errlog_defer(errstr);
802 DEBUG(D_acl) debug_printf("Malware scan: issuing %s scan [%s]\n",
803 scanner_name, scanner_options);
806 memset(av_buffer, 0, sizeof(av_buffer));
807 for (i=0; i != nelements(cmdopt); i++) {
808 /* debug_printf("send option \"%s\"",cmdopt[i]); */
810 if (m_sock_send(sock, cmdopt[i], Ustrlen(cmdopt[i]), &errstr) < 0)
811 return fsec_errlog_defer(errstr);
813 bread = ip_recv(sock, av_buffer, sizeof(av_buffer), MALWARE_TIMEOUT);
814 if (bread >0) av_buffer[bread]='\0';
818 return fsec_errlog_defer(
819 string_sprintf("unable to read answer %d (%s)", i, strerror(errno)));
821 for (j=0;j<bread;j++)
822 if((av_buffer[j]=='\r')||(av_buffer[j]=='\n'))
824 /* debug_printf("read answer %d read=%d \"%s\"\n", i, bread, av_buffer ); */
825 /* while (Ustrstr(av_buffer, "OK\tServer configured.@") == NULL); */
828 /* pass the mailfile to fsecure */
829 file_name = string_sprintf("SCAN\t%s\n", eml_filename);
831 /* debug_printf("send scan %s", file_name); */
832 if (m_sock_send(sock, file_name, Ustrlen(file_name), &errstr) < 0)
833 return fsec_errlog_defer(errstr);
836 /* todo also SUSPICION\t */
837 fs_inf = pcre_compile("\\S{0,5}INFECTED\\t[^\\t]*\\t([^\\t]+)\\t\\S*$", PCRE_COPT, (const char **)&rerror, &roffset, NULL);
839 /* read report, linewise */
843 memset(av_buffer, 0, sizeof(av_buffer));
845 bread=ip_recv(sock, &av_buffer[i], 1, MALWARE_TIMEOUT);
849 return fsec_errlog_defer(
850 string_sprintf("unable to read result (%s)", strerror(err)));
854 while ((i < sizeof(av_buffer)-1 ) && (av_buffer[i-1] != '\n'));
855 av_buffer[i-1] = '\0';
856 /* debug_printf("got line \"%s\"\n",av_buffer); */
858 /* Really search for virus again? */
859 if (malware_name == NULL) {
860 /* try matcher on the line, grab substring */
861 i = pcre_exec(fs_inf, NULL, CS av_buffer, Ustrlen(av_buffer), 0, 0,
862 ovector, nelements(ovector));
865 pcre_get_substring(CS av_buffer, ovector, i, 1,
866 (const char **) &malware_name_internal);
867 malware_name = malware_name_internal;
871 while (Ustrstr(av_buffer, "OK\tScan ok.") == NULL);
876 case M_KAVD: /* "kavdaemon" scanner type -------------------------------- */
878 struct sockaddr_un server;
882 uschar * scanrequest;
884 unsigned long kav_reportlen, bread;
888 if((sock = m_unixsocket(scanner_options, &errstr)) < 0)
889 return kavd_errlog_defer(errstr);
891 /* get current date and time, build scan request */
893 /* pdp note: before the eml_filename parameter, this scanned the
894 directory; not finding documentation, so we'll strip off the directory.
895 The side-effect is that the test framework scanning may end up in
896 scanning more than was requested, but for the normal interface, this is
898 strftime(CS tmpbuf, sizeof(tmpbuf), "%d %b %H:%M:%S", localtime(&t));
899 scanrequest = string_sprintf("<0>%s:%s", CS tmpbuf, eml_filename);
900 p = Ustrrchr(scanrequest, '/');
904 DEBUG(D_acl) debug_printf("Malware scan: issuing %s scan [%s]\n",
905 scanner_name, scanner_options);
907 /* send scan request */
908 if (m_sock_send(sock, scanrequest, Ustrlen(scanrequest)+1, &errstr) < 0)
909 return kavd_errlog_defer(errstr);
911 /* wait for result */
912 if ((bread = recv(sock, tmpbuf, 2, 0) != 2)) {
914 return kavd_errlog_defer("unable to read 2 bytes from socket.");
917 /* get errorcode from one nibble */
918 kav_rc = tmpbuf[ test_byte_order()==LITTLE_MY_ENDIAN ? 0 : 1 ] & 0x0F;
920 /* improper kavdaemon configuration */
921 if ( (kav_rc == 5) || (kav_rc == 6) ) {
923 return kavd_errlog_defer("please reconfigure kavdaemon to NOT disinfect or remove infected files.");
928 return kavd_errlog_defer("reported 'scanning not completed' (code 1).");
933 return kavd_errlog_defer("reported 'kavdaemon damaged' (code 7).");
936 /* code 8 is not handled, since it is ambigous. It appears mostly on
937 bounces where part of a file has been cut off */
939 /* "virus found" return codes (2-4) */
940 if ((kav_rc > 1) && (kav_rc < 5)) {
943 /* setup default virus name */
944 malware_name_internal = string_copy("unknown");
945 malware_name = malware_name_internal;
947 report_flag = tmpbuf[ test_byte_order() == LITTLE_MY_ENDIAN ? 1 : 0 ];
949 /* read the report, if available */
950 if( report_flag == 1 ) {
951 /* read report size */
952 if ((bread = recv(sock, &kav_reportlen, 4, 0)) != 4) {
954 return kavd_errlog_defer("cannot read report size");
957 /* it's possible that avp returns av_buffer[1] == 1 but the
958 reportsize is 0 (!?) */
959 if (kav_reportlen > 0) {
960 /* set up match regex, depends on retcode */
961 kav_re = pcre_compile( kav_rc == 3
962 ? "suspicion:\\s*(.+?)\\s*$"
963 : "infected:\\s*(.+?)\\s*$",
965 (const char **)&rerror,
969 /* read report, linewise */
970 while (kav_reportlen > 0) {
975 while ( recv(sock, &tmpbuf[bread], 1, 0) == 1 ) {
977 if ( (tmpbuf[bread] == '\n') || (bread > 1021) ) break;
981 tmpbuf[bread] = '\0';
983 /* try matcher on the line, grab substring */
984 result = pcre_exec(kav_re, NULL, CS tmpbuf, Ustrlen(tmpbuf), 0, 0,
985 ovector, nelements(ovector));
987 pcre_get_substring(CS tmpbuf, ovector, result, 1,
988 (const char **) &malware_name_internal);
995 else /* no virus found */
1002 case M_CMDL: /* "cmdline" scanner type ---------------------------------- */
1004 const uschar *cmdline_scanner = scanner_options;
1005 uschar *cmdline_trigger;
1006 const pcre *cmdline_trigger_re;
1007 uschar *cmdline_regex;
1008 const pcre *cmdline_regex_re;
1010 uschar * commandline;
1011 void (*eximsigchld)(int);
1012 void (*eximsigpipe)(int);
1013 FILE *scanner_out = NULL;
1014 FILE *scanner_record = NULL;
1015 uschar linebuffer[32767];
1022 if (!cmdline_scanner)
1023 return cmdl_errlog_defer("missing commandline specification");
1025 /* find scanner output trigger */
1026 if (!(cmdline_trigger = string_nextinlist(&av_scanner_work, &sep, NULL, 0)))
1027 return cmdl_errlog_defer("missing trigger specification");
1029 /* precompile trigger regex */
1030 cmdline_trigger_re = pcre_compile(CS cmdline_trigger, PCRE_COPT, (const char **)&rerror, &roffset, NULL);
1031 if (cmdline_trigger_re == NULL)
1032 return cmdl_errlog_defer(
1033 string_sprintf("regular expression error in '%s': %s at offset %d",
1034 cmdline_trigger, rerror, roffset));
1036 /* find scanner name regex */
1037 if (!(cmdline_regex = string_nextinlist(&av_scanner_work, &sep, NULL, 0)))
1038 return cmdl_errlog_defer("missing virus name regex specification");
1040 /* precompile name regex */
1041 cmdline_regex_re = pcre_compile(CS cmdline_regex, PCRE_COPT, (const char **)&rerror, &roffset, NULL);
1042 if (cmdline_regex_re == NULL)
1043 return cmdl_errlog_defer(
1044 string_sprintf("regular expression error in '%s': %s at offset %d",
1045 cmdline_regex, rerror, roffset));
1047 /* prepare scanner call; despite the naming, file_name holds a directory
1048 name which is documented as the value given to %s. */
1050 file_name = string_copy(eml_filename);
1051 p = Ustrrchr(file_name, '/');
1054 commandline = string_sprintf(CS cmdline_scanner, file_name);
1056 /* redirect STDERR too */
1057 commandline = string_sprintf("%s 2>&1", commandline);
1059 DEBUG(D_acl) debug_printf("Malware scan: issuing %s scan [%s]\n", scanner_name, commandline);
1061 /* store exims signal handlers */
1062 eximsigchld = signal(SIGCHLD,SIG_DFL);
1063 eximsigpipe = signal(SIGPIPE,SIG_DFL);
1065 scanner_out = popen(CS commandline,"r");
1066 if (scanner_out == NULL) {
1068 signal(SIGCHLD,eximsigchld);
1069 signal(SIGPIPE,eximsigpipe);
1070 return cmdl_errlog_defer(
1071 string_sprintf("call (%s) failed: %s.", commandline, strerror(err)));
1074 file_name = string_sprintf("%s/scan/%s/%s_scanner_output",
1075 spool_directory, message_id, message_id);
1076 scanner_record = modefopen(file_name, "wb", SPOOL_MODE);
1078 if (scanner_record == NULL) {
1080 pclose(scanner_out);
1081 signal(SIGCHLD,eximsigchld);
1082 signal(SIGPIPE,eximsigpipe);
1083 return cmdl_errlog_defer(
1084 string_sprintf("opening scanner output file (%s) failed: %s.",
1085 file_name, strerror(err)));
1088 /* look for trigger while recording output */
1089 while(fgets(CS linebuffer, sizeof(linebuffer), scanner_out) != NULL) {
1090 if ( Ustrlen(linebuffer) > fwrite(linebuffer, 1, Ustrlen(linebuffer), scanner_record) ) {
1092 pclose(scanner_out);
1093 signal(SIGCHLD,eximsigchld);
1094 signal(SIGPIPE,eximsigpipe);
1095 return cmdl_errlog_defer(
1096 string_sprintf("short write on scanner output file (%s).", file_name));
1098 /* try trigger match */
1099 if (!trigger && regex_match_and_setup(cmdline_trigger_re, linebuffer, 0, -1))
1103 (void)fclose(scanner_record);
1104 pclose(scanner_out);
1105 signal(SIGCHLD,eximsigchld);
1106 signal(SIGPIPE,eximsigpipe);
1109 /* setup default virus name */
1110 malware_name = US"unknown";
1112 /* re-open the scanner output file, look for name match */
1113 scanner_record = fopen(CS file_name, "rb");
1114 while(fgets(CS linebuffer,32767,scanner_record) != NULL) {
1116 result = pcre_exec(cmdline_regex_re, NULL,
1117 CS linebuffer, Ustrlen(linebuffer), 0, 0,
1118 ovector, nelements(ovector));
1120 pcre_get_substring(CS linebuffer, ovector, result, 1,
1121 (const char **) &malware_name_internal);
1123 (void)fclose(scanner_record);
1125 else /* no virus found */
1126 malware_name = NULL;
1130 case M_SOPHIE: /* "sophie" scanner type --------------------------------- */
1133 struct sockaddr_un server;
1137 uschar av_buffer[1024];
1139 if((sock = m_unixsocket(scanner_options, &errstr)) < 0)
1140 return soph_errlog_defer(errstr);
1142 /* pass the scan directory to sophie */
1143 file_name = string_copy(eml_filename);
1144 if ((p = Ustrrchr(file_name, '/')))
1147 DEBUG(D_acl) debug_printf("Malware scan: issuing %s scan [%s]\n",
1148 scanner_name, scanner_options);
1150 if ( write(sock, file_name, Ustrlen(file_name)) < 0
1151 || write(sock, "\n", 1) != 1
1154 return soph_errlog_defer(
1155 string_sprintf("unable to write to UNIX socket (%s)", scanner_options));
1158 /* wait for result */
1159 memset(av_buffer, 0, sizeof(av_buffer));
1160 if ((!(bread = ip_recv(sock, av_buffer, sizeof(av_buffer), MALWARE_TIMEOUT)) > 0)) {
1162 return soph_errlog_defer(
1163 string_sprintf("unable to read from UNIX socket (%s)", scanner_options));
1169 if (av_buffer[0] == '1') {
1170 if (Ustrchr(av_buffer, '\n')) *Ustrchr(av_buffer, '\n') = '\0';
1171 malware_name_internal = string_copy(&av_buffer[2]);
1172 malware_name = malware_name_internal;
1174 else if (!strncmp(CS av_buffer, "-1", 2))
1175 return soph_errlog_defer("scanner reported error");
1176 else /* all ok, no virus */
1177 malware_name = NULL;
1181 case M_CLAMD: /* "clamd" scanner type ----------------------------------- */
1183 /* This code was originally contributed by David Saez */
1184 /* There are three scanning methods available to us:
1185 * (1) Use the SCAN command, pointing to a file in the filesystem
1186 * (2) Use the STREAM command, send the data on a separate port
1187 * (3) Use the zINSTREAM command, send the data inline
1188 * The zINSTREAM command was introduced with ClamAV 0.95, which marked
1189 * STREAM deprecated; see: http://wiki.clamav.net/bin/view/Main/UpgradeNotes095
1190 * In Exim, we use SCAN if using a Unix-domain socket or explicitly told that
1191 * the TCP-connected daemon is actually local; otherwise we use zINSTREAM unless
1192 * WITH_OLD_CLAMAV_STREAM is defined.
1193 * See Exim bug 926 for details. */
1195 uschar *p, *vname, *result_tag, *response_end;
1196 struct sockaddr_un server;
1200 uschar av_buffer[1024];
1201 uschar *hostname = "";
1203 uschar *clamav_fbuf;
1204 int clam_fd, result;
1206 BOOL use_scan_command = FALSE;
1207 clamd_address_container * clamd_address_vector[MAX_CLAMD_SERVERS];
1209 int num_servers = 0;
1210 #ifdef WITH_OLD_CLAMAV_STREAM
1211 uschar av_buffer2[1024];
1214 uint32_t send_size, send_final_zeroblock;
1217 if (*scanner_options == '/')
1218 /* Local file; so we def want to use_scan_command and don't want to try
1219 * passing IP/port combinations */
1220 use_scan_command = TRUE;
1222 const uschar *address = scanner_options;
1223 uschar address_buffer[MAX_CLAMD_ADDRESS_LENGTH + 20];
1225 /* Go through the rest of the list of host/port and construct an array
1226 * of servers to try. The first one is the bit we just passed from
1227 * scanner_options so process that first and then scan the remainder of
1228 * the address buffer */
1230 clamd_address_container *this_clamd;
1232 /* The 'local' option means use the SCAN command over the network
1233 * socket (ie common file storage in use) */
1234 if (strcmpic(address,US"local") == 0) {
1235 use_scan_command = TRUE;
1239 /* XXX: If unsuccessful we should free this memory */
1241 (clamd_address_container *)store_get(sizeof(clamd_address_container));
1243 /* extract host and port part */
1244 if( sscanf(CS address, "%" MAX_CLAMD_ADDRESS_LENGTH_S "s %u",
1245 this_clamd->tcp_addr, &(this_clamd->tcp_port)) != 2 ) {
1246 clmd_errlog(string_sprintf("invalid address '%s'", address));
1250 clamd_address_vector[num_servers] = this_clamd;
1252 if (num_servers >= MAX_CLAMD_SERVERS) {
1253 clmd_errlog("More than " MAX_CLAMD_SERVERS_S " clamd servers "
1254 "specified; only using the first " MAX_CLAMD_SERVERS_S );
1257 } while ((address = string_nextinlist(&av_scanner_work, &sep,
1259 sizeof(address_buffer))) != NULL);
1261 /* check if we have at least one server */
1263 return clmd_errlog_defer("no useable server addresses in malware configuration option.");
1266 /* See the discussion of response formats below to see why we really don't
1267 like colons in filenames when passing filenames to ClamAV. */
1268 if (use_scan_command && Ustrchr(eml_filename, ':'))
1269 return clmd_errlog_defer(
1270 string_sprintf("local/SCAN mode incompatible with" \
1271 " : in path to email filename [%s]", eml_filename));
1273 /* We have some network servers specified */
1276 /* Confirmed in ClamAV source (0.95.3) that the TCPAddr option of clamd
1277 * only supports AF_INET, but we should probably be looking to the
1278 * future and rewriting this to be protocol-independent anyway. */
1280 while ( num_servers > 0 ) {
1281 /* Randomly pick a server to start with */
1282 current_server = random_number( num_servers );
1284 debug_printf("trying server name %s, port %u\n",
1285 clamd_address_vector[current_server]->tcp_addr,
1286 clamd_address_vector[current_server]->tcp_port);
1288 /* Lookup the host. This is to ensure that we connect to the same IP
1289 * on both connections (as one host could resolve to multiple ips) */
1290 sock= m_tcpsocket(CS clamd_address_vector[current_server]->tcp_addr,
1291 clamd_address_vector[current_server]->tcp_port,
1294 /* Connection successfully established with a server */
1295 hostname = clamd_address_vector[current_server]->tcp_addr;
1299 clmd_errlog(errstr);
1301 /* Remove the server from the list. XXX We should free the memory */
1304 for( i = current_server; i < num_servers; i++ )
1305 clamd_address_vector[i] = clamd_address_vector[i+1];
1308 if ( num_servers == 0 )
1309 return clmd_errlog_defer("all servers failed");
1312 if((sock = m_unixsocket(scanner_options, &errstr)) < 0)
1313 return clmd_errlog_defer(errstr);
1316 /* have socket in variable "sock"; command to use is semi-independent of
1317 * the socket protocol. We use SCAN if is local (either Unix/local
1318 * domain socket, or explicitly told local) else we stream the data.
1319 * How we stream the data depends upon how we were built. */
1321 if (!use_scan_command) {
1323 #ifdef WITH_OLD_CLAMAV_STREAM
1324 /* "STREAM\n" command, get back a "PORT <N>\n" response, send data to
1325 * that port on a second connection; then in the scan-method-neutral
1326 * part, read the response back on the original connection. */
1328 DEBUG(D_acl) debug_printf("Malware scan: issuing %s old-style remote scan (PORT)\n",
1331 /* Pass the string to ClamAV (7 = "STREAM\n") */
1332 if (m_sock_send(sock, US"STREAM\n", 7, &errstr) < 0)
1333 return clmd_errlog_defer(errstr);
1335 memset(av_buffer2, 0, sizeof(av_buffer2));
1336 bread = ip_recv(sock, av_buffer2, sizeof(av_buffer2), MALWARE_TIMEOUT);
1341 return clmd_errlog_defer(
1342 string_sprintf("unable to read PORT from socket (%s)",
1346 if (bread == sizeof(av_buffer2)) {
1348 return clmd_errlog_defer("buffer too small");
1351 if (!(*av_buffer2)) {
1353 return clmd_errlog_defer("ClamAV returned null");
1356 av_buffer2[bread] = '\0';
1357 if( sscanf(CS av_buffer2, "PORT %u\n", &port) != 1 ) {
1359 return clmd_errlog_defer(
1360 string_sprintf("Expected port information from clamd, got '%s'",
1364 if ( (sockData = ip_socket(SOCK_STREAM, AF_INET)) < 0) {
1367 return clmd_errlog_defer(
1368 string_sprintf("unable to acquire socket (%s)", strerror(err)));
1371 if (ip_connect(sockData, AF_INET, (uschar*)inet_ntoa(in), port, 5) < 0) {
1373 (void)close(sockData); (void)close(sock);
1374 return clmd_errlog_defer(
1375 string_sprintf("connection to %s, port %u failed (%s)",
1376 inet_ntoa(in), port, strerror(err)));
1379 #define CLOSE_SOCKDATA (void)close(sockData)
1380 #else /* WITH_OLD_CLAMAV_STREAM not defined */
1381 /* New protocol: "zINSTREAM\n" followed by a sequence of <length><data>
1382 chunks, <n> a 4-byte number (network order), terminated by a zero-length
1385 DEBUG(D_acl) debug_printf("Malware scan: issuing %s new-style remote scan (zINSTREAM)\n",
1388 /* Pass the string to ClamAV (10 = "zINSTREAM\0") */
1389 if (send(sock, "zINSTREAM", 10, 0) < 0) {
1392 return clmd_errlog_defer(
1393 string_sprintf("unable to send zINSTREAM to socket (%s)",
1397 #define CLOSE_SOCKDATA /**/
1400 /* calc file size */
1401 clam_fd = open(CS eml_filename, O_RDONLY);
1402 if (clam_fd == -1) {
1404 CLOSE_SOCKDATA; (void)close(sock);
1405 return clmd_errlog_defer(
1406 string_sprintf("can't open spool file %s: %s",
1407 eml_filename, strerror(err)));
1409 fsize = lseek(clam_fd, 0, SEEK_END);
1412 CLOSE_SOCKDATA; (void)close(sock); (void)close(clam_fd);
1413 return clmd_errlog_defer(
1414 string_sprintf("can't seek spool file %s: %s",
1415 eml_filename, strerror(errno)));
1417 lseek(clam_fd, 0, SEEK_SET);
1419 clamav_fbuf = (uschar *) malloc (fsize);
1421 CLOSE_SOCKDATA; (void)close(sock); (void)close(clam_fd);
1422 return clmd_errlog_defer(
1423 string_sprintf("unable to allocate memory %u for file (%s)",
1424 fsize, eml_filename));
1427 result = read (clam_fd, clamav_fbuf, fsize);
1430 CLOSE_SOCKDATA; (void)close(sock); (void)close(clam_fd);
1432 return clmd_errlog_defer(
1433 string_sprintf("can't read spool file %s: %s",
1434 eml_filename, strerror(err)));
1436 (void)close(clam_fd);
1438 /* send file body to socket */
1439 #ifdef WITH_OLD_CLAMAV_STREAM
1440 if (send(sockData, clamav_fbuf, fsize, 0) < 0) {
1441 CLOSE_SOCKDATA; (void)close(sock);
1443 return clmd_errlog_defer(
1444 string_sprintf("unable to send file body to socket (%s:%u)",
1448 send_size = htonl(fsize);
1449 send_final_zeroblock = 0;
1450 if ((send(sock, &send_size, sizeof(send_size), 0) < 0) ||
1451 (send(sock, clamav_fbuf, fsize, 0) < 0) ||
1452 (send(sock, &send_final_zeroblock, sizeof(send_final_zeroblock), 0) < 0))
1456 return clmd_errlog_defer(
1457 string_sprintf("unable to send file body to socket (%s:%u)",
1465 #undef CLOSE_SOCKDATA
1467 } else { /* use scan command */
1468 /* Send a SCAN command pointing to a filename; then in the then in the
1469 * scan-method-neutral part, read the response back */
1471 /* ================================================================= */
1473 /* Prior to the reworking post-Exim-4.72, this scanned a directory,
1474 which dates to when ClamAV needed us to break apart the email into the
1475 MIME parts (eg, with the now deprecated demime condition coming first).
1476 Some time back, ClamAV gained the ability to deconstruct the emails, so
1477 doing this would actually have resulted in the mail attachments being
1478 scanned twice, in the broken out files and from the original .eml.
1479 Since ClamAV now handles emails (and has for quite some time) we can
1480 just use the email file itself. */
1481 /* Pass the string to ClamAV (7 = "SCAN \n" + \0) */
1482 file_name = string_sprintf("SCAN %s\n", eml_filename);
1484 DEBUG(D_acl) debug_printf("Malware scan: issuing %s local-path scan [%s]\n",
1485 scanner_name, scanner_options);
1487 if (send(sock, file_name, Ustrlen(file_name), 0) < 0) {
1490 return clmd_errlog_defer(
1491 string_sprintf("unable to write to socket (%s)", strerror(err)));
1494 /* Do not shut down the socket for writing; a user report noted that
1495 * clamd 0.70 does not react well to this. */
1497 /* Commands have been sent, no matter which scan method or connection
1498 * type we're using; now just read the result, independent of method. */
1500 /* Read the result */
1501 memset(av_buffer, 0, sizeof(av_buffer));
1502 bread = ip_recv(sock, av_buffer, sizeof(av_buffer), MALWARE_TIMEOUT);
1506 return clmd_errlog_defer(
1507 string_sprintf("unable to read from socket (%s)", strerror(errno)));
1509 if (bread == sizeof(av_buffer))
1510 return clmd_errlog_defer("buffer too small");
1511 /* We're now assured of a NULL at the end of av_buffer */
1513 /* Check the result. ClamAV returns one of two result formats.
1514 In the basic mode, the response is of the form:
1515 infected: -> "<filename>: <virusname> FOUND"
1516 not-infected: -> "<filename>: OK"
1517 error: -> "<filename>: <errcode> ERROR
1518 If the ExtendedDetectionInfo option has been turned on, then we get:
1519 "<filename>: <virusname>(<virushash>:<virussize>) FOUND"
1520 for the infected case. Compare:
1521 /tmp/eicar.com: Eicar-Test-Signature FOUND
1522 /tmp/eicar.com: Eicar-Test-Signature(44d88612fea8a8f36de82e1278abb02f:68) FOUND
1524 In the streaming case, clamd uses the filename "stream" which you should
1525 be able to verify with { ktrace clamdscan --stream /tmp/eicar.com }. (The
1526 client app will replace "stream" with the original filename before returning
1527 results to stdout, but the trace shows the data).
1529 We will assume that the pathname passed to clamd from Exim does not contain
1530 a colon. We will have whined loudly above if the eml_filename does (and we're
1531 passing a filename to clamd). */
1534 return clmd_errlog_defer("ClamAV returned null");
1536 /* strip newline at the end (won't be present for zINSTREAM)
1537 (also any trailing whitespace, which shouldn't exist, but we depend upon
1538 this below, so double-check) */
1539 p = av_buffer + Ustrlen(av_buffer) - 1;
1540 if (*p == '\n') *p = '\0';
1542 DEBUG(D_acl) debug_printf("Malware response: %s\n", av_buffer);
1544 while (isspace(*--p) && (p > av_buffer))
1549 /* colon in returned output? */
1550 if((p = Ustrchr(av_buffer,':')) == NULL)
1551 return clmd_errlog_defer(
1552 string_sprintf("ClamAV returned malformed result (missing colon): %s",
1555 /* strip filename */
1556 while (*p && isspace(*++p)) /**/;
1559 /* It would be bad to encounter a virus with "FOUND" in part of the name,
1560 but we should at least be resistant to it. */
1561 p = Ustrrchr(vname, ' ');
1562 result_tag = p ? p+1 : vname;
1564 if (Ustrcmp(result_tag, "FOUND") == 0) {
1565 /* p should still be the whitespace before the result_tag */
1566 while (isspace(*p)) --p;
1568 /* Strip off the extended information too, which will be in parens
1569 after the virus name, with no intervening whitespace. */
1571 /* "(hash:size)", so previous '(' will do; if not found, we have
1572 a curious virus name, but not an error. */
1573 p = Ustrrchr(vname, '(');
1577 malware_name_internal = string_copy(vname);
1578 malware_name = malware_name_internal;
1579 DEBUG(D_acl) debug_printf("Malware found, name \"%s\"\n", malware_name);
1581 } else if (Ustrcmp(result_tag, "ERROR") == 0)
1582 return clmd_errlog_defer(
1583 string_sprintf("ClamAV returned: %s", av_buffer));
1585 else if (Ustrcmp(result_tag, "OK") == 0) {
1586 /* Everything should be OK */
1587 malware_name = NULL;
1588 DEBUG(D_acl) debug_printf("Malware not found\n");
1591 return clmd_errlog_defer(
1592 string_sprintf("unparseable response from ClamAV: {%s}", av_buffer));
1597 case M_SOCK: /* "sock" scanner type ------------------------------------- */
1598 /* This code was derived by Martin Poole from the clamd code contributed
1599 by David Saez and the cmdline code
1603 uschar * commandline;
1604 uschar av_buffer[1024];
1605 uschar * linebuffer;
1606 uschar *sockline_scanner;
1607 uschar sockline_scanner_default[] = "%s\n";
1608 uschar *sockline_trigger;
1609 const pcre *sockline_trigger_re;
1610 uschar *sockline_regex;
1611 const pcre *sockline_regex_re;
1615 /* find scanner command line */
1616 if (!(sockline_scanner = string_nextinlist(&av_scanner_work, &sep,
1618 sockline_scanner = sockline_scanner_default;
1620 /* find scanner output trigger */
1621 if (!(sockline_trigger = string_nextinlist(&av_scanner_work, &sep,
1623 return sock_errlog_defer("missing trigger specification");
1625 /* precompile trigger regex */
1626 sockline_trigger_re = pcre_compile(CS sockline_trigger, PCRE_COPT, (const char **)&rerror, &roffset, NULL);
1627 if (sockline_trigger_re == NULL)
1628 return sock_errlog_defer(
1629 string_sprintf("regular expression error in '%s': %s at offset %d",
1630 sockline_trigger, rerror, roffset));
1632 /* find virus name regex */
1633 if (!(sockline_regex = string_nextinlist(&av_scanner_work, &sep,
1635 return sock_errlog_defer("missing virus name regex specification");
1637 /* precompile name regex */
1638 sockline_regex_re = pcre_compile(CS sockline_regex, PCRE_COPT, (const char **)&rerror, &roffset, NULL);
1639 if (sockline_regex_re == NULL)
1640 return sock_errlog_defer(
1641 string_sprintf("regular expression error in '%s': %s at offset %d",
1642 sockline_regex, rerror, roffset));
1644 /* prepare scanner call */
1645 commandline = string_sprintf("%s/scan/%s/%s.eml", spool_directory, message_id, message_id);
1646 commandline = string_sprintf( CS sockline_scanner, CS commandline);
1649 /* socket does not start with '/' -> network socket */
1650 sock = *scanner_options != '/'
1651 ? m_tcpsocket_fromdef(scanner_options, &errstr)
1652 : m_unixsocket(scanner_options, &errstr);
1654 return sock_errlog_defer(errstr);
1656 /* Pass the command string to the socket */
1657 if (m_sock_send(sock, commandline, Ustrlen(commandline), &errstr) < 0)
1658 return sock_errlog_defer(errstr);
1660 /* We're done sending, close socket for writing. */
1661 /* shutdown(sock, SHUT_WR); */
1663 /* Read the result */
1664 memset(av_buffer, 0, sizeof(av_buffer));
1665 bread = read(sock, av_buffer, sizeof(av_buffer));
1669 return sock_errlog_defer(
1670 string_sprintf("unable to read from socket (%s)", strerror(errno)));
1672 if (bread == sizeof(av_buffer))
1673 return sock_errlog_defer("buffer too small");
1674 linebuffer = string_copy(av_buffer);
1676 /* try trigger match */
1677 if (regex_match_and_setup(sockline_trigger_re, linebuffer, 0, -1)) {
1678 result = pcre_exec(sockline_regex_re, NULL,
1679 CS av_buffer, Ustrlen(av_buffer), 0, 0,
1680 ovector, nelements(ovector));
1682 pcre_get_substring(CS av_buffer, ovector, result, 1,
1683 (const char **)&malware_name);
1685 malware_name = US "unknown";
1687 else /* no virus found */
1688 malware_name = NULL;
1691 case M_MKSD: /* "mksd" scanner type ------------------------------------- */
1693 char *mksd_options_end;
1694 int mksd_maxproc = 1; /* default, if no option supplied */
1698 if (scanner_options) {
1699 mksd_maxproc = (int)strtol(CS scanner_options, &mksd_options_end, 10);
1700 if ( *scanner_options == '\0'
1701 || *mksd_options_end != '\0'
1703 || mksd_maxproc > 32
1705 return mksd_errlog_defer(
1706 string_sprintf("invalid option '%s'", scanner_options));
1709 if((sock = m_unixsocket(US "/var/run/mksd/socket", &errstr)) < 0)
1710 return mksd_errlog_defer(errstr);
1712 malware_name = NULL;
1714 DEBUG(D_acl) debug_printf("Malware scan: issuing %s scan\n", scanner_name);
1716 retval = mksd_scan_packed(sock, eml_filename);
1724 /* set "been here, done that" marker */
1728 /* match virus name against pattern (caseless ------->----------v) */
1729 if ( (malware_name != NULL) &&
1730 (regex_match_and_setup(re, malware_name, 0, -1)) ) {
1731 DEBUG(D_acl) debug_printf("Matched regex to malware [%s] [%s]\n", malware_regex, malware_name);
1739 /* simple wrapper for reading lines from sockets */
1741 recv_line(int sock, uschar *buffer, int size)
1745 memset(buffer,0,size);
1747 while(recv(sock,p,1,0) > -1) {
1748 if ((p-buffer) > (size-2)) break;
1749 if (*p == '\n') break;
1750 if (*p != '\r') p++;
1758 /* ============= private routines for the "mksd" scanner type ============== */
1760 #include <sys/uio.h>
1763 mksd_writev (int sock, struct iovec *iov, int iovcnt)
1769 i = writev (sock, iov, iovcnt);
1770 while ((i < 0) && (errno == EINTR));
1773 malware_errlog("unable to write to mksd UNIX socket (/var/run/mksd/socket)");
1778 if (i >= iov->iov_len) {
1785 iov->iov_base = CS iov->iov_base + i;
1792 mksd_read_lines (int sock, uschar *av_buffer, int av_buffer_size)
1798 if ((i = recv (sock, av_buffer+offset, av_buffer_size-offset, 0)) <= 0) {
1800 malware_errlog("unable to read from mksd UNIX socket (/var/run/mksd/socket)");
1805 /* offset == av_buffer_size -> buffer full */
1806 if (offset == av_buffer_size) {
1808 malware_errlog("malformed reply received from mksd");
1811 } while (av_buffer[offset-1] != '\n');
1813 av_buffer[offset] = '\0';
1818 mksd_parse_line (char *line)
1828 if ((p = strchr (line, '\n')) != NULL)
1830 return mksd_errlog_defer(string_sprintf("scanner failed: %s", line));
1833 if ((p = strchr (line, '\n')) != NULL) {
1835 if (((p-line) > 5) && (line[3] == ' '))
1836 if (((p = strchr (line+4, ' ')) != NULL) && ((p-line) > 4)) {
1838 malware_name_internal = string_copy(line+4);
1839 malware_name = malware_name_internal;
1843 return mksd_errlog_defer(
1844 string_sprintf("malformed reply received from mksd: %s", line));
1849 mksd_scan_packed(int sock, uschar *scan_filename)
1851 struct iovec iov[3];
1852 const char *cmd = "MSQ\n";
1853 uschar av_buffer[1024];
1855 iov[0].iov_base = (void *) cmd;
1857 iov[1].iov_base = CS scan_filename;
1858 iov[1].iov_len = Ustrlen(scan_filename);
1859 iov[2].iov_base = (void *) (cmd + 3);
1862 if (mksd_writev (sock, iov, 3) < 0)
1865 if (mksd_read_lines (sock, av_buffer, sizeof (av_buffer)) < 0)
1870 return mksd_parse_line (CS av_buffer);
1873 #endif /*WITH_CONTENT_SCAN*/