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",
340 m_pcre_compile(const uschar * re, uschar ** errstr)
342 const uschar * rerror;
346 cre = pcre_compile(re, PCRE_COPT, (const char **)&rerror, &roffset, NULL);
348 *errstr= string_sprintf("regular expression error in '%s': %s at offset %d",
349 re, rerror, roffset);
354 m_pcre_exec(const pcre * cre, uschar * text)
357 int i = pcre_exec(cre, NULL, CS text, Ustrlen(text), 0, 0,
358 ovector, nelements(ovector));
359 uschar * substr = NULL;
360 if (i >= 2) /* Got it */
361 pcre_get_substring(CS text, ovector, i, 1, (const char **) &substr);
365 /*************************************************
366 * Scan content for malware *
367 *************************************************/
369 typedef enum {M_FPROTD, M_DRWEB, M_AVES, M_FSEC, M_KAVD, M_CMDL,
370 M_SOPHIE, M_CLAMD, M_SOCK, M_MKSD} scanner_t;
375 const char * options_default;
378 { M_FPROTD, "f-protd", "localhost 10200-10204" },
379 { M_DRWEB, "drweb", "/usr/local/drweb/run/drwebd.sock" },
380 { M_AVES, "aveserver", "/var/run/aveserver" },
381 { M_FSEC, "fsecure", "/var/run/.fsav" },
382 { M_KAVD, "kavdaemon", "/var/run/AvpCtl" },
383 { M_CMDL, "cmdline", NULL },
384 { M_SOPHIE, "sophie", "/var/run/sophie" },
385 { M_CLAMD, "clamd", "/tmp/clamd" },
386 { M_SOCK, "sock", "/tmp/malware.sock" },
387 { M_MKSD, "mksd", NULL },
388 { -1, NULL, NULL } /* end-marker */
391 /* This is an internal interface for scanning an email; the normal interface
392 is via malware(), or there's malware_in_file() used for testing/debugging.
395 listptr the list of options to the "malware = ..." ACL condition
396 eml_filename the file holding the email to be scanned
397 faking whether or not we're faking this up for the -bmalware test
399 Returns: Exim message processing code (OK, FAIL, DEFER, ...)
400 where true means malware was found (condition applies)
403 malware_internal(uschar **listptr, uschar *eml_filename, BOOL faking)
406 uschar *list = *listptr;
407 uschar *av_scanner_work = av_scanner;
408 uschar *scanner_name;
409 uschar *malware_regex;
410 uschar malware_regex_default[] = ".+";
411 unsigned long mbox_size;
415 struct scan * scanent;
416 const uschar * scanner_options;
418 /* make sure the eml mbox file is spooled up */
419 mbox_file = spool_mbox(&mbox_size, faking ? eml_filename : NULL);
420 if (mbox_file == NULL) /* error while spooling */
421 return malware_errlog_defer("error while creating mbox spool file");
423 /* none of our current scanners need the mbox
424 file as a stream, so we can close it right away */
425 (void)fclose(mbox_file);
427 /* extract the malware regex to match against from the option list */
428 if (!(malware_regex = string_nextinlist(&list, &sep, NULL, 0))) {
430 /* parse 1st option */
431 if ( (strcmpic(malware_regex,US"false") == 0) ||
432 (Ustrcmp(malware_regex,"0") == 0) ) {
433 /* explicitly no matching */
437 /* special cases (match anything except empty) */
438 if ( (strcmpic(malware_regex,US"true") == 0) ||
439 (Ustrcmp(malware_regex,"*") == 0) ||
440 (Ustrcmp(malware_regex,"1") == 0) ) {
441 malware_regex = malware_regex_default;
444 else /* empty means "don't match anything" */
447 /* Reset sep that is set by previous string_nextinlist() call */
450 /* compile the regex, see if it works */
451 if (!(re = m_pcre_compile(CS malware_regex, &errstr)))
452 return malware_errlog_defer(errstr);
454 /* if av_scanner starts with a dollar, expand it first */
455 if (*av_scanner == '$') {
456 av_scanner_work = expand_string(av_scanner);
457 if (!av_scanner_work)
458 return malware_errlog_defer(
459 string_sprintf("av_scanner starts with $, but expansion failed: %s",
460 expand_string_message));
462 debug_printf("Expanded av_scanner global: %s\n", av_scanner_work);
463 /* disable result caching in this case */
469 /* Do not scan twice. */
470 if (malware_ok == 0) {
472 /* find the scanner type from the av_scanner option */
473 if (!(scanner_name = string_nextinlist(&av_scanner_work, &sep, NULL, 0)))
474 return malware_errlog_defer("av_scanner configuration variable is empty");
476 for (scanent = m_scans; ; scanent++) {
478 return malware_errlog_defer(string_sprintf("unknown scanner type '%s'",
480 if (strcmpic(scanner_name, US scanent->name) == 0)
483 if (!(scanner_options = string_nextinlist(&av_scanner_work, &sep, NULL, 0)))
484 scanner_options = scanent->options_default;
486 switch (scanent->scancode) {
487 case M_FPROTD: /* "f-protd" scanner type -------------------------------- */
489 uschar *fp_scan_option;
490 unsigned int detected=0, par_count=0;
492 uschar * scanrequest;
493 uschar buf[32768], *strhelper, *strhelper2;
495 if ((sock = m_tcpsocket_fromdef(scanner_options, &errstr)) < 0)
496 return fprotd_errlog_defer(errstr);
498 DEBUG(D_acl) debug_printf("Malware scan: issuing %s GET\n", scanner_name);
499 scanrequest = string_sprintf("GET %s", eml_filename);
501 while ((fp_scan_option = string_nextinlist(&av_scanner_work, &sep,
503 scanrequest = string_sprintf("%s%s%s", scanrequest,
504 par_count ? "%20" : "?", fp_scan_option);
507 scanrequest = string_sprintf("%s HTTP/1.0\r\n\r\n", scanrequest);
509 /* send scan request */
510 if (m_sock_send(sock, scanrequest, Ustrlen(scanrequest)+1, &errstr) < 0)
511 return fprotd_errlog_defer(errstr);
513 /* We get a lot of empty lines, so we need this hack to check for any data at all */
514 while( recv(sock, buf, 1, MSG_PEEK) > 0 ) {
515 if ( recv_line(sock, buf, 32768) > 0) {
516 if ( Ustrstr(buf, US"<detected type=\"") != NULL )
518 else if ( detected && (strhelper = Ustrstr(buf, US"<name>")) ) {
519 if ((strhelper2 = Ustrstr(buf, US"</name>")) != NULL) {
521 malware_name_internal = string_copy(strhelper+6);
523 } else if ( Ustrstr(buf, US"<summary code=\"") )
524 malware_name = Ustrstr(buf, US"<summary code=\"11\">")
525 ? malware_name_internal : NULL;
532 case M_DRWEB: /* "drweb" scanner type ----------------------------------- */
533 /* v0.1 - added support for tcp sockets */
534 /* v0.0 - initial release -- support for unix sockets */
536 struct sockaddr_un server;
539 uschar * tmpbuf, *drweb_fbuf;
540 int drweb_rc, drweb_cmd, drweb_flags = 0x0000, drweb_fd,
541 drweb_vnum, drweb_slen, drweb_fin = 0x0000;
543 const pcre *drweb_re;
545 if (*scanner_options != '/') {
546 if ((sock = m_tcpsocket_fromdef(scanner_options, &errstr)) < 0)
547 return drweb_errlog_defer(errstr);
549 /* prepare variables */
550 drweb_cmd = htonl(DRWEBD_SCAN_CMD);
551 drweb_flags = htonl(DRWEBD_RETURN_VIRUSES | DRWEBD_IS_MAIL);
554 drweb_fd = open(CS eml_filename, O_RDONLY);
555 if (drweb_fd == -1) {
558 return drweb_errlog_defer(
559 string_sprintf("can't open spool file %s: %s",
560 eml_filename, strerror(err)));
562 fsize = lseek(drweb_fd, 0, SEEK_END);
566 (void)close(drweb_fd);
567 return drweb_errlog_defer(
568 string_sprintf("can't seek spool file %s: %s",
569 eml_filename, strerror(err)));
571 drweb_slen = htonl(fsize);
572 lseek(drweb_fd, 0, SEEK_SET);
574 DEBUG(D_acl) debug_printf("Malware scan: issuing %s remote scan [%s]\n",
575 scanner_name, scanner_options);
577 /* send scan request */
578 if ((send(sock, &drweb_cmd, sizeof(drweb_cmd), 0) < 0) ||
579 (send(sock, &drweb_flags, sizeof(drweb_flags), 0) < 0) ||
580 (send(sock, &drweb_fin, sizeof(drweb_fin), 0) < 0) ||
581 (send(sock, &drweb_slen, sizeof(drweb_slen), 0) < 0)) {
583 (void)close(drweb_fd);
584 return drweb_errlog_defer(
585 string_sprintf("unable to send commands to socket (%s)", scanner_options));
588 drweb_fbuf = (uschar *) malloc (fsize);
591 (void)close(drweb_fd);
592 return drweb_errlog_defer(
593 string_sprintf("unable to allocate memory %u for file (%s)",
594 fsize, eml_filename));
597 result = read (drweb_fd, drweb_fbuf, fsize);
601 (void)close(drweb_fd);
603 return drweb_errlog_defer(
604 string_sprintf("can't read spool file %s: %s",
605 eml_filename, strerror(err)));
607 (void)close(drweb_fd);
609 /* send file body to socket */
610 if (send(sock, drweb_fbuf, fsize, 0) < 0) {
613 return drweb_errlog_defer(
614 string_sprintf("unable to send file body to socket (%s)", scanner_options));
616 (void)close(drweb_fd);
619 if((sock = m_unixsocket(scanner_options, &errstr)) < 0)
620 return drweb_errlog_defer(errstr);
622 /* prepare variables */
623 drweb_cmd = htonl(DRWEBD_SCAN_CMD);
624 drweb_flags = htonl(DRWEBD_RETURN_VIRUSES | DRWEBD_IS_MAIL);
625 drweb_slen = htonl(Ustrlen(eml_filename));
627 DEBUG(D_acl) debug_printf("Malware scan: issuing %s local scan [%s]\n",
628 scanner_name, scanner_options);
630 /* send scan request */
631 if ((send(sock, &drweb_cmd, sizeof(drweb_cmd), 0) < 0) ||
632 (send(sock, &drweb_flags, sizeof(drweb_flags), 0) < 0) ||
633 (send(sock, &drweb_slen, sizeof(drweb_slen), 0) < 0) ||
634 (send(sock, eml_filename, Ustrlen(eml_filename), 0) < 0) ||
635 (send(sock, &drweb_fin, sizeof(drweb_fin), 0) < 0)) {
637 return drweb_errlog_defer(
638 string_sprintf("unable to send commands to socket (%s)", scanner_options));
642 /* wait for result */
643 if ((bread = recv(sock, &drweb_rc, sizeof(drweb_rc), 0) != sizeof(drweb_rc))) {
645 return drweb_errlog_defer("unable to read return code");
647 drweb_rc = ntohl(drweb_rc);
649 if ((bread = recv(sock, &drweb_vnum, sizeof(drweb_vnum), 0) != sizeof(drweb_vnum))) {
651 return drweb_errlog_defer("unable to read the number of viruses");
653 drweb_vnum = ntohl(drweb_vnum);
655 /* "virus(es) found" if virus number is > 0 */
659 /* setup default virus name */
660 malware_name_internal = "unknown";
661 malware_name = malware_name_internal;
663 /* set up match regex */
664 drweb_re = m_pcre_compile( "infected\\swith\\s*(.+?)$", &errstr);
666 /* read and concatenate virus names into one string */
667 for (i=0;i<drweb_vnum;i++)
669 int size = 0, off = 0, ovector[10*3];
670 /* read the size of report */
671 if ((bread = recv(sock, &drweb_slen, sizeof(drweb_slen), 0) != sizeof(drweb_slen))) {
673 return drweb_errlog_defer("cannot read report size");
675 drweb_slen = ntohl(drweb_slen);
676 tmpbuf = store_get(drweb_slen);
678 /* read report body */
679 if ((bread = recv(sock, tmpbuf, drweb_slen, 0)) != drweb_slen) {
681 return drweb_errlog_defer("cannot read report string");
683 tmpbuf[drweb_slen] = '\0';
685 /* try matcher on the line, grab substring */
686 result = pcre_exec(drweb_re, NULL, CS tmpbuf, Ustrlen(tmpbuf), 0, 0,
687 ovector, nelements(ovector));
689 const char * pre_malware_nb;
691 pcre_get_substring(CS tmpbuf, ovector, result, 1, &pre_malware_nb);
693 /* the first name we just copy to malware_name */
695 malware_name_internal = string_append(NULL, &size, &off,
698 /* concatenate each new virus name to previous */
699 malware_name_internal = string_append(malware_name_internal,
700 &size, &off, 2, "/", pre_malware_nb);
702 pcre_free_substring(pre_malware_nb);
707 const char *drweb_s = NULL;
709 if (drweb_rc & DERR_READ_ERR) drweb_s = "read error";
710 if (drweb_rc & DERR_NOMEMORY) drweb_s = "no memory";
711 if (drweb_rc & DERR_TIMEOUT) drweb_s = "timeout";
712 if (drweb_rc & DERR_BAD_CALL) drweb_s = "wrong command";
713 /* retcodes DERR_SYMLINK, DERR_NO_REGFILE, DERR_SKIPPED.
714 * DERR_TOO_BIG, DERR_TOO_COMPRESSED, DERR_SPAM,
715 * DERR_CRC_ERROR, DERR_READSOCKET, DERR_WRITE_ERR
716 * and others are ignored */
719 return drweb_errlog_defer(
720 string_sprintf("drweb daemon retcode 0x%x (%s)", drweb_rc, drweb_s));
729 case M_AVES: /* "aveserver" scanner type -------------------------------- */
732 struct sockaddr_un server;
736 if((sock = m_unixsocket(scanner_options, &errstr)) < 0)
737 return aves_errlog_defer(errstr);
739 /* read aveserver's greeting and see if it is ready (2xx greeting) */
740 recv_line(sock, buf, sizeof(buf));
743 /* aveserver is having problems */
745 return aves_errlog_defer(
746 string_sprintf("unavailable (Responded: %s).", ((buf[0] != 0) ? buf : (uschar *)"nothing") ));
749 /* prepare our command */
750 (void)string_format(buf, sizeof(buf), "SCAN bPQRSTUW %s\r\n",
753 DEBUG(D_acl) debug_printf("Malware scan: issuing %s SCAN\n", scanner_name);
756 if (m_sock_send(sock, buf, Ustrlen(buf), &errstr) < 0)
757 return aves_errlog_defer(errstr);
761 /* read response lines, find malware name and final response */
762 while (recv_line(sock, buf, sizeof(buf)) > 0) {
763 debug_printf("aveserver: %s\n", buf);
767 /* aveserver is having problems */
768 log_write(0, LOG_MAIN|LOG_PANIC,
769 "malware acl condition: unable to scan file %s (Responded: %s).",
773 } else if (Ustrncmp(buf,"322",3) == 0) {
774 uschar *p = Ustrchr(&buf[4],' ');
776 malware_name_internal = string_copy(&buf[4]);
777 malware_name = malware_name_internal;
781 /* prepare our command */
782 (void)string_format(buf, sizeof(buf), "quit\r\n");
785 if (m_sock_send(sock, buf, Ustrlen(buf), &errstr) < 0)
786 return aves_errlog_defer(errstr);
788 /* read aveserver's greeting and see if it is ready (2xx greeting) */
789 recv_line(sock, buf, sizeof(buf));
792 /* aveserver is having problems */
794 return aves_errlog_defer(
795 string_sprintf("unable to quit dialogue (Responded: %s).",
796 ((buf[0] != 0) ? buf : (uschar *)"nothing") ));
801 if (result == DEFER) return DEFER;
805 case M_FSEC: /* "fsecure" scanner type ---------------------------------- */
807 struct sockaddr_un server;
808 int sock, i, j, bread = 0;
810 uschar av_buffer[1024];
812 static uschar *cmdopt[] = { US"CONFIGURE\tARCHIVE\t1\n",
813 US"CONFIGURE\tTIMEOUT\t0\n",
814 US"CONFIGURE\tMAXARCH\t5\n",
815 US"CONFIGURE\tMIME\t1\n" };
819 if((sock = m_unixsocket(scanner_options, &errstr)) < 0)
820 return fsec_errlog_defer(errstr);
822 DEBUG(D_acl) debug_printf("Malware scan: issuing %s scan [%s]\n",
823 scanner_name, scanner_options);
826 memset(av_buffer, 0, sizeof(av_buffer));
827 for (i=0; i != nelements(cmdopt); i++) {
828 /* debug_printf("send option \"%s\"",cmdopt[i]); */
830 if (m_sock_send(sock, cmdopt[i], Ustrlen(cmdopt[i]), &errstr) < 0)
831 return fsec_errlog_defer(errstr);
833 bread = ip_recv(sock, av_buffer, sizeof(av_buffer), MALWARE_TIMEOUT);
834 if (bread >0) av_buffer[bread]='\0';
838 return fsec_errlog_defer(
839 string_sprintf("unable to read answer %d (%s)", i, strerror(errno)));
841 for (j=0;j<bread;j++)
842 if((av_buffer[j]=='\r')||(av_buffer[j]=='\n'))
844 /* debug_printf("read answer %d read=%d \"%s\"\n", i, bread, av_buffer ); */
845 /* while (Ustrstr(av_buffer, "OK\tServer configured.@") == NULL); */
848 /* pass the mailfile to fsecure */
849 file_name = string_sprintf("SCAN\t%s\n", eml_filename);
851 /* debug_printf("send scan %s", file_name); */
852 if (m_sock_send(sock, file_name, Ustrlen(file_name), &errstr) < 0)
853 return fsec_errlog_defer(errstr);
856 /* todo also SUSPICION\t */
857 fs_inf = m_pcre_compile("\\S{0,5}INFECTED\\t[^\\t]*\\t([^\\t]+)\\t\\S*$", &errstr);
859 /* read report, linewise */
862 memset(av_buffer, 0, sizeof(av_buffer));
864 bread=ip_recv(sock, &av_buffer[i], 1, MALWARE_TIMEOUT);
868 return fsec_errlog_defer(
869 string_sprintf("unable to read result (%s)", strerror(err)));
873 while ((i < sizeof(av_buffer)-1 ) && (av_buffer[i-1] != '\n'));
874 av_buffer[i-1] = '\0';
875 /* debug_printf("got line \"%s\"\n",av_buffer); */
877 /* Really search for virus again? */
878 if (malware_name == NULL)
879 /* try matcher on the line, grab substring */
880 malware_name = m_pcre_exec(fs_inf, av_buffer);
882 while (Ustrstr(av_buffer, "OK\tScan ok.") == NULL);
887 case M_KAVD: /* "kavdaemon" scanner type -------------------------------- */
889 struct sockaddr_un server;
893 uschar * scanrequest;
895 unsigned long kav_reportlen, bread;
899 if((sock = m_unixsocket(scanner_options, &errstr)) < 0)
900 return kavd_errlog_defer(errstr);
902 /* get current date and time, build scan request */
904 /* pdp note: before the eml_filename parameter, this scanned the
905 directory; not finding documentation, so we'll strip off the directory.
906 The side-effect is that the test framework scanning may end up in
907 scanning more than was requested, but for the normal interface, this is
909 strftime(CS tmpbuf, sizeof(tmpbuf), "%d %b %H:%M:%S", localtime(&t));
910 scanrequest = string_sprintf("<0>%s:%s", CS tmpbuf, eml_filename);
911 p = Ustrrchr(scanrequest, '/');
915 DEBUG(D_acl) debug_printf("Malware scan: issuing %s scan [%s]\n",
916 scanner_name, scanner_options);
918 /* send scan request */
919 if (m_sock_send(sock, scanrequest, Ustrlen(scanrequest)+1, &errstr) < 0)
920 return kavd_errlog_defer(errstr);
922 /* wait for result */
923 if ((bread = recv(sock, tmpbuf, 2, 0) != 2)) {
925 return kavd_errlog_defer("unable to read 2 bytes from socket.");
928 /* get errorcode from one nibble */
929 kav_rc = tmpbuf[ test_byte_order()==LITTLE_MY_ENDIAN ? 0 : 1 ] & 0x0F;
931 /* improper kavdaemon configuration */
932 if ( (kav_rc == 5) || (kav_rc == 6) ) {
934 return kavd_errlog_defer("please reconfigure kavdaemon to NOT disinfect or remove infected files.");
939 return kavd_errlog_defer("reported 'scanning not completed' (code 1).");
944 return kavd_errlog_defer("reported 'kavdaemon damaged' (code 7).");
947 /* code 8 is not handled, since it is ambigous. It appears mostly on
948 bounces where part of a file has been cut off */
950 /* "virus found" return codes (2-4) */
951 if ((kav_rc > 1) && (kav_rc < 5)) {
954 /* setup default virus name */
955 malware_name_internal = string_copy("unknown");
956 malware_name = malware_name_internal;
958 report_flag = tmpbuf[ test_byte_order() == LITTLE_MY_ENDIAN ? 1 : 0 ];
960 /* read the report, if available */
961 if( report_flag == 1 ) {
962 /* read report size */
963 if ((bread = recv(sock, &kav_reportlen, 4, 0)) != 4) {
965 return kavd_errlog_defer("cannot read report size");
968 /* it's possible that avp returns av_buffer[1] == 1 but the
969 reportsize is 0 (!?) */
970 if (kav_reportlen > 0) {
971 /* set up match regex, depends on retcode */
972 kav_re = m_pcre_compile( kav_rc == 3
973 ? "suspicion:\\s*(.+?)\\s*$"
974 : "infected:\\s*(.+?)\\s*$",
977 /* read report, linewise */
978 while (kav_reportlen > 0) {
980 while ( recv(sock, &tmpbuf[bread], 1, 0) == 1 ) {
982 if ( (tmpbuf[bread] == '\n') || (bread > 1021) ) break;
986 tmpbuf[bread] = '\0';
988 /* try matcher on the line, grab substring */
989 if ((malware_name = m_pcre_exec(kav_re, tmpbuf)))
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];
1020 if (!cmdline_scanner)
1021 return cmdl_errlog_defer("missing commandline specification");
1023 /* find scanner output trigger */
1024 if (!(cmdline_trigger = string_nextinlist(&av_scanner_work, &sep, NULL, 0)))
1025 return cmdl_errlog_defer("missing trigger specification");
1027 /* precompile trigger regex */
1028 if (!(cmdline_trigger_re = m_pcre_compile(CS cmdline_trigger, &errstr)))
1029 return cmdl_errlog_defer(errstr);
1031 /* find scanner name regex */
1032 if (!(cmdline_regex = string_nextinlist(&av_scanner_work, &sep, NULL, 0)))
1033 return cmdl_errlog_defer("missing virus name regex specification");
1035 /* precompile name regex */
1036 if (!(cmdline_regex_re = m_pcre_compile(CS cmdline_regex, &errstr)))
1037 return cmdl_errlog_defer(errstr);
1039 /* prepare scanner call; despite the naming, file_name holds a directory
1040 name which is documented as the value given to %s. */
1042 file_name = string_copy(eml_filename);
1043 p = Ustrrchr(file_name, '/');
1046 commandline = string_sprintf(CS cmdline_scanner, file_name);
1048 /* redirect STDERR too */
1049 commandline = string_sprintf("%s 2>&1", commandline);
1051 DEBUG(D_acl) debug_printf("Malware scan: issuing %s scan [%s]\n", scanner_name, commandline);
1053 /* store exims signal handlers */
1054 eximsigchld = signal(SIGCHLD,SIG_DFL);
1055 eximsigpipe = signal(SIGPIPE,SIG_DFL);
1057 scanner_out = popen(CS commandline,"r");
1058 if (scanner_out == NULL) {
1060 signal(SIGCHLD,eximsigchld);
1061 signal(SIGPIPE,eximsigpipe);
1062 return cmdl_errlog_defer(
1063 string_sprintf("call (%s) failed: %s.", commandline, strerror(err)));
1066 file_name = string_sprintf("%s/scan/%s/%s_scanner_output",
1067 spool_directory, message_id, message_id);
1068 scanner_record = modefopen(file_name, "wb", SPOOL_MODE);
1070 if (scanner_record == NULL) {
1072 pclose(scanner_out);
1073 signal(SIGCHLD,eximsigchld);
1074 signal(SIGPIPE,eximsigpipe);
1075 return cmdl_errlog_defer(
1076 string_sprintf("opening scanner output file (%s) failed: %s.",
1077 file_name, strerror(err)));
1080 /* look for trigger while recording output */
1081 while(fgets(CS linebuffer, sizeof(linebuffer), scanner_out) != NULL) {
1082 if ( Ustrlen(linebuffer) > fwrite(linebuffer, 1, Ustrlen(linebuffer), scanner_record) ) {
1084 pclose(scanner_out);
1085 signal(SIGCHLD,eximsigchld);
1086 signal(SIGPIPE,eximsigpipe);
1087 return cmdl_errlog_defer(
1088 string_sprintf("short write on scanner output file (%s).", file_name));
1090 /* try trigger match */
1091 if (!trigger && regex_match_and_setup(cmdline_trigger_re, linebuffer, 0, -1))
1095 (void)fclose(scanner_record);
1096 pclose(scanner_out);
1097 signal(SIGCHLD,eximsigchld);
1098 signal(SIGPIPE,eximsigpipe);
1102 /* setup default virus name */
1103 malware_name = US"unknown";
1105 /* re-open the scanner output file, look for name match */
1106 scanner_record = fopen(CS file_name, "rb");
1107 while(fgets(CS linebuffer,32767,scanner_record) != NULL) {
1109 if ((s = m_pcre_exec(cmdline_regex_re, linebuffer)))
1112 (void)fclose(scanner_record);
1114 else /* no virus found */
1115 malware_name = NULL;
1119 case M_SOPHIE: /* "sophie" scanner type --------------------------------- */
1122 struct sockaddr_un server;
1126 uschar av_buffer[1024];
1128 if((sock = m_unixsocket(scanner_options, &errstr)) < 0)
1129 return soph_errlog_defer(errstr);
1131 /* pass the scan directory to sophie */
1132 file_name = string_copy(eml_filename);
1133 if ((p = Ustrrchr(file_name, '/')))
1136 DEBUG(D_acl) debug_printf("Malware scan: issuing %s scan [%s]\n",
1137 scanner_name, scanner_options);
1139 if ( write(sock, file_name, Ustrlen(file_name)) < 0
1140 || write(sock, "\n", 1) != 1
1143 return soph_errlog_defer(
1144 string_sprintf("unable to write to UNIX socket (%s)", scanner_options));
1147 /* wait for result */
1148 memset(av_buffer, 0, sizeof(av_buffer));
1149 if ((!(bread = ip_recv(sock, av_buffer, sizeof(av_buffer), MALWARE_TIMEOUT)) > 0)) {
1151 return soph_errlog_defer(
1152 string_sprintf("unable to read from UNIX socket (%s)", scanner_options));
1158 if (av_buffer[0] == '1') {
1159 if (Ustrchr(av_buffer, '\n')) *Ustrchr(av_buffer, '\n') = '\0';
1160 malware_name_internal = string_copy(&av_buffer[2]);
1161 malware_name = malware_name_internal;
1163 else if (!strncmp(CS av_buffer, "-1", 2))
1164 return soph_errlog_defer("scanner reported error");
1165 else /* all ok, no virus */
1166 malware_name = NULL;
1170 case M_CLAMD: /* "clamd" scanner type ----------------------------------- */
1172 /* This code was originally contributed by David Saez */
1173 /* There are three scanning methods available to us:
1174 * (1) Use the SCAN command, pointing to a file in the filesystem
1175 * (2) Use the STREAM command, send the data on a separate port
1176 * (3) Use the zINSTREAM command, send the data inline
1177 * The zINSTREAM command was introduced with ClamAV 0.95, which marked
1178 * STREAM deprecated; see: http://wiki.clamav.net/bin/view/Main/UpgradeNotes095
1179 * In Exim, we use SCAN if using a Unix-domain socket or explicitly told that
1180 * the TCP-connected daemon is actually local; otherwise we use zINSTREAM unless
1181 * WITH_OLD_CLAMAV_STREAM is defined.
1182 * See Exim bug 926 for details. */
1184 uschar *p, *vname, *result_tag, *response_end;
1185 struct sockaddr_un server;
1189 uschar av_buffer[1024];
1190 uschar *hostname = "";
1192 uschar *clamav_fbuf;
1193 int clam_fd, result;
1195 BOOL use_scan_command = FALSE;
1196 clamd_address_container * clamd_address_vector[MAX_CLAMD_SERVERS];
1198 int num_servers = 0;
1199 #ifdef WITH_OLD_CLAMAV_STREAM
1200 uschar av_buffer2[1024];
1203 uint32_t send_size, send_final_zeroblock;
1206 if (*scanner_options == '/')
1207 /* Local file; so we def want to use_scan_command and don't want to try
1208 * passing IP/port combinations */
1209 use_scan_command = TRUE;
1211 const uschar *address = scanner_options;
1212 uschar address_buffer[MAX_CLAMD_ADDRESS_LENGTH + 20];
1214 /* Go through the rest of the list of host/port and construct an array
1215 * of servers to try. The first one is the bit we just passed from
1216 * scanner_options so process that first and then scan the remainder of
1217 * the address buffer */
1219 clamd_address_container *this_clamd;
1221 /* The 'local' option means use the SCAN command over the network
1222 * socket (ie common file storage in use) */
1223 if (strcmpic(address,US"local") == 0) {
1224 use_scan_command = TRUE;
1228 /* XXX: If unsuccessful we should free this memory */
1230 (clamd_address_container *)store_get(sizeof(clamd_address_container));
1232 /* extract host and port part */
1233 if( sscanf(CS address, "%" MAX_CLAMD_ADDRESS_LENGTH_S "s %u",
1234 this_clamd->tcp_addr, &(this_clamd->tcp_port)) != 2 ) {
1235 clmd_errlog(string_sprintf("invalid address '%s'", address));
1239 clamd_address_vector[num_servers] = this_clamd;
1241 if (num_servers >= MAX_CLAMD_SERVERS) {
1242 clmd_errlog("More than " MAX_CLAMD_SERVERS_S " clamd servers "
1243 "specified; only using the first " MAX_CLAMD_SERVERS_S );
1246 } while ((address = string_nextinlist(&av_scanner_work, &sep,
1248 sizeof(address_buffer))) != NULL);
1250 /* check if we have at least one server */
1252 return clmd_errlog_defer("no useable server addresses in malware configuration option.");
1255 /* See the discussion of response formats below to see why we really don't
1256 like colons in filenames when passing filenames to ClamAV. */
1257 if (use_scan_command && Ustrchr(eml_filename, ':'))
1258 return clmd_errlog_defer(
1259 string_sprintf("local/SCAN mode incompatible with" \
1260 " : in path to email filename [%s]", eml_filename));
1262 /* We have some network servers specified */
1265 /* Confirmed in ClamAV source (0.95.3) that the TCPAddr option of clamd
1266 * only supports AF_INET, but we should probably be looking to the
1267 * future and rewriting this to be protocol-independent anyway. */
1269 while ( num_servers > 0 ) {
1270 /* Randomly pick a server to start with */
1271 current_server = random_number( num_servers );
1273 debug_printf("trying server name %s, port %u\n",
1274 clamd_address_vector[current_server]->tcp_addr,
1275 clamd_address_vector[current_server]->tcp_port);
1277 /* Lookup the host. This is to ensure that we connect to the same IP
1278 * on both connections (as one host could resolve to multiple ips) */
1279 sock= m_tcpsocket(CS clamd_address_vector[current_server]->tcp_addr,
1280 clamd_address_vector[current_server]->tcp_port,
1283 /* Connection successfully established with a server */
1284 hostname = clamd_address_vector[current_server]->tcp_addr;
1288 clmd_errlog(errstr);
1290 /* Remove the server from the list. XXX We should free the memory */
1293 for( i = current_server; i < num_servers; i++ )
1294 clamd_address_vector[i] = clamd_address_vector[i+1];
1297 if ( num_servers == 0 )
1298 return clmd_errlog_defer("all servers failed");
1301 if((sock = m_unixsocket(scanner_options, &errstr)) < 0)
1302 return clmd_errlog_defer(errstr);
1305 /* have socket in variable "sock"; command to use is semi-independent of
1306 * the socket protocol. We use SCAN if is local (either Unix/local
1307 * domain socket, or explicitly told local) else we stream the data.
1308 * How we stream the data depends upon how we were built. */
1310 if (!use_scan_command) {
1312 #ifdef WITH_OLD_CLAMAV_STREAM
1313 /* "STREAM\n" command, get back a "PORT <N>\n" response, send data to
1314 * that port on a second connection; then in the scan-method-neutral
1315 * part, read the response back on the original connection. */
1317 DEBUG(D_acl) debug_printf("Malware scan: issuing %s old-style remote scan (PORT)\n",
1320 /* Pass the string to ClamAV (7 = "STREAM\n") */
1321 if (m_sock_send(sock, US"STREAM\n", 7, &errstr) < 0)
1322 return clmd_errlog_defer(errstr);
1324 memset(av_buffer2, 0, sizeof(av_buffer2));
1325 bread = ip_recv(sock, av_buffer2, sizeof(av_buffer2), MALWARE_TIMEOUT);
1330 return clmd_errlog_defer(
1331 string_sprintf("unable to read PORT from socket (%s)",
1335 if (bread == sizeof(av_buffer2)) {
1337 return clmd_errlog_defer("buffer too small");
1340 if (!(*av_buffer2)) {
1342 return clmd_errlog_defer("ClamAV returned null");
1345 av_buffer2[bread] = '\0';
1346 if( sscanf(CS av_buffer2, "PORT %u\n", &port) != 1 ) {
1348 return clmd_errlog_defer(
1349 string_sprintf("Expected port information from clamd, got '%s'",
1353 if ( (sockData = ip_socket(SOCK_STREAM, AF_INET)) < 0) {
1356 return clmd_errlog_defer(
1357 string_sprintf("unable to acquire socket (%s)", strerror(err)));
1360 if (ip_connect(sockData, AF_INET, (uschar*)inet_ntoa(in), port, 5) < 0) {
1362 (void)close(sockData); (void)close(sock);
1363 return clmd_errlog_defer(
1364 string_sprintf("connection to %s, port %u failed (%s)",
1365 inet_ntoa(in), port, strerror(err)));
1368 #define CLOSE_SOCKDATA (void)close(sockData)
1369 #else /* WITH_OLD_CLAMAV_STREAM not defined */
1370 /* New protocol: "zINSTREAM\n" followed by a sequence of <length><data>
1371 chunks, <n> a 4-byte number (network order), terminated by a zero-length
1374 DEBUG(D_acl) debug_printf("Malware scan: issuing %s new-style remote scan (zINSTREAM)\n",
1377 /* Pass the string to ClamAV (10 = "zINSTREAM\0") */
1378 if (send(sock, "zINSTREAM", 10, 0) < 0) {
1381 return clmd_errlog_defer(
1382 string_sprintf("unable to send zINSTREAM to socket (%s)",
1386 #define CLOSE_SOCKDATA /**/
1389 /* calc file size */
1390 clam_fd = open(CS eml_filename, O_RDONLY);
1391 if (clam_fd == -1) {
1393 CLOSE_SOCKDATA; (void)close(sock);
1394 return clmd_errlog_defer(
1395 string_sprintf("can't open spool file %s: %s",
1396 eml_filename, strerror(err)));
1398 fsize = lseek(clam_fd, 0, SEEK_END);
1401 CLOSE_SOCKDATA; (void)close(sock); (void)close(clam_fd);
1402 return clmd_errlog_defer(
1403 string_sprintf("can't seek spool file %s: %s",
1404 eml_filename, strerror(errno)));
1406 lseek(clam_fd, 0, SEEK_SET);
1408 clamav_fbuf = (uschar *) malloc (fsize);
1410 CLOSE_SOCKDATA; (void)close(sock); (void)close(clam_fd);
1411 return clmd_errlog_defer(
1412 string_sprintf("unable to allocate memory %u for file (%s)",
1413 fsize, eml_filename));
1416 result = read (clam_fd, clamav_fbuf, fsize);
1419 CLOSE_SOCKDATA; (void)close(sock); (void)close(clam_fd);
1421 return clmd_errlog_defer(
1422 string_sprintf("can't read spool file %s: %s",
1423 eml_filename, strerror(err)));
1425 (void)close(clam_fd);
1427 /* send file body to socket */
1428 #ifdef WITH_OLD_CLAMAV_STREAM
1429 if (send(sockData, clamav_fbuf, fsize, 0) < 0) {
1430 CLOSE_SOCKDATA; (void)close(sock);
1432 return clmd_errlog_defer(
1433 string_sprintf("unable to send file body to socket (%s:%u)",
1437 send_size = htonl(fsize);
1438 send_final_zeroblock = 0;
1439 if ((send(sock, &send_size, sizeof(send_size), 0) < 0) ||
1440 (send(sock, clamav_fbuf, fsize, 0) < 0) ||
1441 (send(sock, &send_final_zeroblock, sizeof(send_final_zeroblock), 0) < 0))
1445 return clmd_errlog_defer(
1446 string_sprintf("unable to send file body to socket (%s:%u)",
1454 #undef CLOSE_SOCKDATA
1456 } else { /* use scan command */
1457 /* Send a SCAN command pointing to a filename; then in the then in the
1458 * scan-method-neutral part, read the response back */
1460 /* ================================================================= */
1462 /* Prior to the reworking post-Exim-4.72, this scanned a directory,
1463 which dates to when ClamAV needed us to break apart the email into the
1464 MIME parts (eg, with the now deprecated demime condition coming first).
1465 Some time back, ClamAV gained the ability to deconstruct the emails, so
1466 doing this would actually have resulted in the mail attachments being
1467 scanned twice, in the broken out files and from the original .eml.
1468 Since ClamAV now handles emails (and has for quite some time) we can
1469 just use the email file itself. */
1470 /* Pass the string to ClamAV (7 = "SCAN \n" + \0) */
1471 file_name = string_sprintf("SCAN %s\n", eml_filename);
1473 DEBUG(D_acl) debug_printf("Malware scan: issuing %s local-path scan [%s]\n",
1474 scanner_name, scanner_options);
1476 if (send(sock, file_name, Ustrlen(file_name), 0) < 0) {
1479 return clmd_errlog_defer(
1480 string_sprintf("unable to write to socket (%s)", strerror(err)));
1483 /* Do not shut down the socket for writing; a user report noted that
1484 * clamd 0.70 does not react well to this. */
1486 /* Commands have been sent, no matter which scan method or connection
1487 * type we're using; now just read the result, independent of method. */
1489 /* Read the result */
1490 memset(av_buffer, 0, sizeof(av_buffer));
1491 bread = ip_recv(sock, av_buffer, sizeof(av_buffer), MALWARE_TIMEOUT);
1495 return clmd_errlog_defer(
1496 string_sprintf("unable to read from socket (%s)", strerror(errno)));
1498 if (bread == sizeof(av_buffer))
1499 return clmd_errlog_defer("buffer too small");
1500 /* We're now assured of a NULL at the end of av_buffer */
1502 /* Check the result. ClamAV returns one of two result formats.
1503 In the basic mode, the response is of the form:
1504 infected: -> "<filename>: <virusname> FOUND"
1505 not-infected: -> "<filename>: OK"
1506 error: -> "<filename>: <errcode> ERROR
1507 If the ExtendedDetectionInfo option has been turned on, then we get:
1508 "<filename>: <virusname>(<virushash>:<virussize>) FOUND"
1509 for the infected case. Compare:
1510 /tmp/eicar.com: Eicar-Test-Signature FOUND
1511 /tmp/eicar.com: Eicar-Test-Signature(44d88612fea8a8f36de82e1278abb02f:68) FOUND
1513 In the streaming case, clamd uses the filename "stream" which you should
1514 be able to verify with { ktrace clamdscan --stream /tmp/eicar.com }. (The
1515 client app will replace "stream" with the original filename before returning
1516 results to stdout, but the trace shows the data).
1518 We will assume that the pathname passed to clamd from Exim does not contain
1519 a colon. We will have whined loudly above if the eml_filename does (and we're
1520 passing a filename to clamd). */
1523 return clmd_errlog_defer("ClamAV returned null");
1525 /* strip newline at the end (won't be present for zINSTREAM)
1526 (also any trailing whitespace, which shouldn't exist, but we depend upon
1527 this below, so double-check) */
1528 p = av_buffer + Ustrlen(av_buffer) - 1;
1529 if (*p == '\n') *p = '\0';
1531 DEBUG(D_acl) debug_printf("Malware response: %s\n", av_buffer);
1533 while (isspace(*--p) && (p > av_buffer))
1538 /* colon in returned output? */
1539 if((p = Ustrchr(av_buffer,':')) == NULL)
1540 return clmd_errlog_defer(
1541 string_sprintf("ClamAV returned malformed result (missing colon): %s",
1544 /* strip filename */
1545 while (*p && isspace(*++p)) /**/;
1548 /* It would be bad to encounter a virus with "FOUND" in part of the name,
1549 but we should at least be resistant to it. */
1550 p = Ustrrchr(vname, ' ');
1551 result_tag = p ? p+1 : vname;
1553 if (Ustrcmp(result_tag, "FOUND") == 0) {
1554 /* p should still be the whitespace before the result_tag */
1555 while (isspace(*p)) --p;
1557 /* Strip off the extended information too, which will be in parens
1558 after the virus name, with no intervening whitespace. */
1560 /* "(hash:size)", so previous '(' will do; if not found, we have
1561 a curious virus name, but not an error. */
1562 p = Ustrrchr(vname, '(');
1566 malware_name_internal = string_copy(vname);
1567 malware_name = malware_name_internal;
1568 DEBUG(D_acl) debug_printf("Malware found, name \"%s\"\n", malware_name);
1570 } else if (Ustrcmp(result_tag, "ERROR") == 0)
1571 return clmd_errlog_defer(
1572 string_sprintf("ClamAV returned: %s", av_buffer));
1574 else if (Ustrcmp(result_tag, "OK") == 0) {
1575 /* Everything should be OK */
1576 malware_name = NULL;
1577 DEBUG(D_acl) debug_printf("Malware not found\n");
1580 return clmd_errlog_defer(
1581 string_sprintf("unparseable response from ClamAV: {%s}", av_buffer));
1586 case M_SOCK: /* "sock" scanner type ------------------------------------- */
1587 /* This code was derived by Martin Poole from the clamd code contributed
1588 by David Saez and the cmdline code
1592 uschar * commandline;
1593 uschar av_buffer[1024];
1594 uschar * linebuffer;
1595 uschar *sockline_scanner;
1596 uschar sockline_scanner_default[] = "%s\n";
1597 uschar *sockline_trigger;
1598 const pcre *sockline_trig_re;
1599 uschar *sockline_regex;
1600 const pcre *sockline_name_re;
1602 /* find scanner command line */
1603 if (!(sockline_scanner = string_nextinlist(&av_scanner_work, &sep,
1605 sockline_scanner = sockline_scanner_default;
1607 /* find scanner output trigger */
1608 if (!(sockline_trigger = string_nextinlist(&av_scanner_work, &sep,
1610 return sock_errlog_defer("missing trigger specification");
1612 /* precompile trigger regex */
1613 if (!(sockline_trig_re = m_pcre_compile(CS sockline_trigger, &errstr)))
1614 return sock_errlog_defer(errstr);
1616 /* find virus name regex */
1617 if (!(sockline_regex = string_nextinlist(&av_scanner_work, &sep,
1619 return sock_errlog_defer("missing virus name regex specification");
1621 /* precompile name regex */
1622 if (!(sockline_name_re = m_pcre_compile(CS sockline_regex, &errstr)))
1623 return sock_errlog_defer(errstr);
1625 /* prepare scanner call */
1626 commandline = string_sprintf("%s/scan/%s/%s.eml", spool_directory, message_id, message_id);
1627 commandline = string_sprintf( CS sockline_scanner, CS commandline);
1630 /* socket does not start with '/' -> network socket */
1631 sock = *scanner_options != '/'
1632 ? m_tcpsocket_fromdef(scanner_options, &errstr)
1633 : m_unixsocket(scanner_options, &errstr);
1635 return sock_errlog_defer(errstr);
1637 /* Pass the command string to the socket */
1638 if (m_sock_send(sock, commandline, Ustrlen(commandline), &errstr) < 0)
1639 return sock_errlog_defer(errstr);
1641 /* We're done sending, close socket for writing. */
1642 /* shutdown(sock, SHUT_WR); */
1644 /* Read the result */
1645 memset(av_buffer, 0, sizeof(av_buffer));
1646 bread = read(sock, av_buffer, sizeof(av_buffer));
1650 return sock_errlog_defer(
1651 string_sprintf("unable to read from socket (%s)", strerror(errno)));
1653 if (bread == sizeof(av_buffer))
1654 return sock_errlog_defer("buffer too small");
1655 linebuffer = string_copy(av_buffer);
1657 /* try trigger match */
1658 if (regex_match_and_setup(sockline_trig_re, linebuffer, 0, -1)) {
1659 if (!(malware_name = m_pcre_exec(sockline_name_re, av_buffer)))
1660 malware_name = US "unknown";
1662 else /* no virus found */
1663 malware_name = NULL;
1666 case M_MKSD: /* "mksd" scanner type ------------------------------------- */
1668 char *mksd_options_end;
1669 int mksd_maxproc = 1; /* default, if no option supplied */
1673 if (scanner_options) {
1674 mksd_maxproc = (int)strtol(CS scanner_options, &mksd_options_end, 10);
1675 if ( *scanner_options == '\0'
1676 || *mksd_options_end != '\0'
1678 || mksd_maxproc > 32
1680 return mksd_errlog_defer(
1681 string_sprintf("invalid option '%s'", scanner_options));
1684 if((sock = m_unixsocket(US "/var/run/mksd/socket", &errstr)) < 0)
1685 return mksd_errlog_defer(errstr);
1687 malware_name = NULL;
1689 DEBUG(D_acl) debug_printf("Malware scan: issuing %s scan\n", scanner_name);
1691 retval = mksd_scan_packed(sock, eml_filename);
1699 /* set "been here, done that" marker */
1703 /* match virus name against pattern (caseless ------->----------v) */
1704 if ( (malware_name != NULL) &&
1705 (regex_match_and_setup(re, malware_name, 0, -1)) ) {
1706 DEBUG(D_acl) debug_printf("Matched regex to malware [%s] [%s]\n", malware_regex, malware_name);
1714 /* simple wrapper for reading lines from sockets */
1716 recv_line(int sock, uschar *buffer, int size)
1720 memset(buffer,0,size);
1722 while(recv(sock,p,1,0) > -1) {
1723 if ((p-buffer) > (size-2)) break;
1724 if (*p == '\n') break;
1725 if (*p != '\r') p++;
1733 /* ============= private routines for the "mksd" scanner type ============== */
1735 #include <sys/uio.h>
1738 mksd_writev (int sock, struct iovec *iov, int iovcnt)
1744 i = writev (sock, iov, iovcnt);
1745 while ((i < 0) && (errno == EINTR));
1748 malware_errlog("unable to write to mksd UNIX socket (/var/run/mksd/socket)");
1753 if (i >= iov->iov_len) {
1760 iov->iov_base = CS iov->iov_base + i;
1767 mksd_read_lines (int sock, uschar *av_buffer, int av_buffer_size)
1773 if ((i = recv (sock, av_buffer+offset, av_buffer_size-offset, 0)) <= 0) {
1775 malware_errlog("unable to read from mksd UNIX socket (/var/run/mksd/socket)");
1780 /* offset == av_buffer_size -> buffer full */
1781 if (offset == av_buffer_size) {
1783 malware_errlog("malformed reply received from mksd");
1786 } while (av_buffer[offset-1] != '\n');
1788 av_buffer[offset] = '\0';
1793 mksd_parse_line (char *line)
1803 if ((p = strchr (line, '\n')) != NULL)
1805 return mksd_errlog_defer(string_sprintf("scanner failed: %s", line));
1808 if ((p = strchr (line, '\n')) != NULL) {
1810 if (((p-line) > 5) && (line[3] == ' '))
1811 if (((p = strchr (line+4, ' ')) != NULL) && ((p-line) > 4)) {
1813 malware_name_internal = string_copy(line+4);
1814 malware_name = malware_name_internal;
1818 return mksd_errlog_defer(
1819 string_sprintf("malformed reply received from mksd: %s", line));
1824 mksd_scan_packed(int sock, uschar *scan_filename)
1826 struct iovec iov[3];
1827 const char *cmd = "MSQ\n";
1828 uschar av_buffer[1024];
1830 iov[0].iov_base = (void *) cmd;
1832 iov[1].iov_base = CS scan_filename;
1833 iov[1].iov_len = Ustrlen(scan_filename);
1834 iov[2].iov_base = (void *) (cmd + 3);
1837 if (mksd_writev (sock, iov, 3) < 0)
1840 if (mksd_read_lines (sock, av_buffer, sizeof (av_buffer)) < 0)
1845 return mksd_parse_line (CS av_buffer);
1848 #endif /*WITH_CONTENT_SCAN*/