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);
218 clmd_errlog(const uschar * str)
220 log_write(0, LOG_MAIN|LOG_PANIC, "malware acl condition: clamd: %s", str);
223 /*************************************************/
226 m_unixsocket(const uschar * path, uschar ** errstr)
229 struct sockaddr_un server;
231 if ((sock = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
232 *errstr = "can't open UNIX socket.";
236 server.sun_family = AF_UNIX;
237 Ustrcpy(server.sun_path, path);
238 if (connect(sock, (struct sockaddr *) &server, sizeof(server)) < 0) {
241 *errstr = string_sprintf("unable to connect to UNIX socket (%s): %s",
242 path, strerror(err));
249 m_sock_send(int sock, uschar * buf, int cnt, uschar ** errstr)
251 if (send(sock, buf, cnt, 0) < 0) {
254 *errstr = string_sprintf("unable to send to socket (%s): %s",
261 /*************************************************
262 * Scan content for malware *
263 *************************************************/
265 typedef enum {M_FPROTD, M_DRWEB, M_AVES, M_FSEC, M_KAVD, M_CMDL,
266 M_SOPHIE, M_CLAMD, M_MKSD} scanner_t;
271 const char * options_default;
274 { M_FPROTD, "f-protd", "localhost 10200-10204" },
275 { M_DRWEB, "drweb", "/usr/local/drweb/run/drwebd.sock" },
276 { M_AVES, "aveserver", "/var/run/aveserver" },
277 { M_FSEC, "fsecure", "/var/run/.fsav" },
278 { M_KAVD, "kavdaemon", "/var/run/AvpCtl" },
279 { M_CMDL, "cmdline", NULL },
280 { M_SOPHIE, "sophie", "/var/run/sophie" },
281 { M_CLAMD, "clamd", "/tmp/clamd" },
282 { M_MKSD, "mksd", NULL },
283 { -1, NULL, NULL } /* end-marker */
286 /* This is an internal interface for scanning an email; the normal interface
287 is via malware(), or there's malware_in_file() used for testing/debugging.
290 listptr the list of options to the "malware = ..." ACL condition
291 eml_filename the file holding the email to be scanned
292 faking whether or not we're faking this up for the -bmalware test
294 Returns: Exim message processing code (OK, FAIL, DEFER, ...)
295 where true means malware was found (condition applies)
298 malware_internal(uschar **listptr, uschar *eml_filename, BOOL faking)
301 uschar *list = *listptr;
302 uschar *av_scanner_work = av_scanner;
303 uschar *scanner_name;
304 uschar *malware_regex;
305 uschar malware_regex_default[] = ".+";
306 unsigned long mbox_size;
310 const uschar *rerror;
312 struct scan * scanent;
313 const uschar * scanner_options;
315 /* make sure the eml mbox file is spooled up */
316 mbox_file = spool_mbox(&mbox_size, faking ? eml_filename : NULL);
317 if (mbox_file == NULL) /* error while spooling */
318 return malware_errlog_defer("error while creating mbox spool file");
320 /* none of our current scanners need the mbox
321 file as a stream, so we can close it right away */
322 (void)fclose(mbox_file);
324 /* extract the malware regex to match against from the option list */
325 if (!(malware_regex = string_nextinlist(&list, &sep, NULL, 0))) {
327 /* parse 1st option */
328 if ( (strcmpic(malware_regex,US"false") == 0) ||
329 (Ustrcmp(malware_regex,"0") == 0) ) {
330 /* explicitly no matching */
334 /* special cases (match anything except empty) */
335 if ( (strcmpic(malware_regex,US"true") == 0) ||
336 (Ustrcmp(malware_regex,"*") == 0) ||
337 (Ustrcmp(malware_regex,"1") == 0) ) {
338 malware_regex = malware_regex_default;
341 else /* empty means "don't match anything" */
344 /* Reset sep that is set by previous string_nextinlist() call */
347 /* compile the regex, see if it works */
348 re = pcre_compile(CS malware_regex, PCRE_COPT, (const char **)&rerror, &roffset, NULL);
350 return malware_errlog_defer(
351 string_sprintf("regular expression error in '%s': %s at offset %d",
352 malware_regex, rerror, roffset));
354 /* if av_scanner starts with a dollar, expand it first */
355 if (*av_scanner == '$') {
356 av_scanner_work = expand_string(av_scanner);
357 if (!av_scanner_work)
358 return malware_errlog_defer(
359 string_sprintf("av_scanner starts with $, but expansion failed: %s",
360 expand_string_message));
362 debug_printf("Expanded av_scanner global: %s\n", av_scanner_work);
363 /* disable result caching in this case */
369 /* Do not scan twice. */
370 if (malware_ok == 0) {
372 /* find the scanner type from the av_scanner option */
373 if (!(scanner_name = string_nextinlist(&av_scanner_work, &sep, NULL, 0)))
374 return malware_errlog_defer("av_scanner configuration variable is empty");
376 for (scanent = m_scans; ; scanent++) {
378 return malware_errlog_defer(string_sprintf("unknown scanner type '%s'",
380 if (strcmpic(scanner_name, US scanent->name) == 0)
383 if (!(scanner_options = string_nextinlist(&av_scanner_work, &sep, NULL, 0)))
384 scanner_options = scanent->options_default;
386 switch (scanent->scancode) {
387 case M_FPROTD: /* "f-protd" scanner type -------------------------------- */
389 uschar *fp_scan_option;
390 uschar hostname[256];
391 unsigned int port, portlow, porthigh, connect_ok=0, detected=0, par_count=0;
395 uschar * scanrequest;
396 uschar buf[32768], *strhelper, *strhelper2;
398 /* extract host and port part */
399 if ( sscanf(CS scanner_options, "%255s %u-%u", hostname, &portlow, &porthigh) != 3 ) {
400 if ( sscanf(CS scanner_options, "%s %u", hostname, &portlow) != 2 )
401 return fprotd_errlog_defer(
402 string_sprintf("invalid socket '%s'", scanner_options));
406 /* Lookup the host */
407 if((he = gethostbyname(CS hostname)) == 0)
408 return fprotd_errlog_defer(
409 string_sprintf("failed to lookup host '%s'", hostname));
411 in = *(struct in_addr *) he->h_addr_list[0];
415 /* Open the f-protd TCP socket */
416 if ( (sock = ip_socket(SOCK_STREAM, AF_INET)) < 0)
417 return fprotd_errlog_defer(
418 string_sprintf("unable to acquire socket (%s)", strerror(errno)));
420 /* Try to connect to all portslow-high until connection is established */
421 for (port = portlow; !connect_ok && port < porthigh; port++)
422 if (ip_connect(sock, AF_INET, (uschar*)inet_ntoa(in), port, 5) >= 0)
428 return fprotd_errlog_defer(
429 string_sprintf("connection to %s, port %u-%u failed (%s)",
430 inet_ntoa(in), portlow, porthigh, strerror(err)));
433 DEBUG(D_acl) debug_printf("Malware scan: issuing %s GET\n", scanner_name);
434 scanrequest = string_sprintf("GET %s", eml_filename);
436 while ((fp_scan_option = string_nextinlist(&av_scanner_work, &sep,
438 scanrequest = string_sprintf("%s%s%s", scanrequest,
439 par_count ? "%20" : "?", fp_scan_option);
442 scanrequest = string_sprintf("%s HTTP/1.0\r\n\r\n", scanrequest);
444 /* send scan request */
445 if (m_sock_send(sock, scanrequest, Ustrlen(scanrequest)+1, &errstr) < 0)
446 return fprotd_errlog_defer(errstr);
448 /* We get a lot of empty lines, so we need this hack to check for any data at all */
449 while( recv(sock, buf, 1, MSG_PEEK) > 0 ) {
450 if ( recv_line(sock, buf, 32768) > 0) {
451 if ( Ustrstr(buf, US"<detected type=\"") != NULL )
453 else if ( detected && (strhelper = Ustrstr(buf, US"<name>")) ) {
454 if ((strhelper2 = Ustrstr(buf, US"</name>")) != NULL) {
456 malware_name_internal = string_copy(strhelper+6);
458 } else if ( Ustrstr(buf, US"<summary code=\"") )
459 malware_name = Ustrstr(buf, US"<summary code=\"11\">")
460 ? malware_name_internal : NULL;
467 case M_DRWEB: /* "drweb" scanner type ----------------------------------- */
468 /* v0.1 - added support for tcp sockets */
469 /* v0.0 - initial release -- support for unix sockets */
471 struct sockaddr_un server;
472 int sock, result, ovector[10*3];
473 unsigned int port, fsize;
474 uschar * tmpbuf, *drweb_fbuf;
475 int drweb_rc, drweb_cmd, drweb_flags = 0x0000, drweb_fd,
476 drweb_vnum, drweb_slen, drweb_fin = 0x0000;
478 uschar hostname[256];
483 if (*scanner_options != '/') {
485 /* extract host and port part */
486 if( sscanf(CS scanner_options, "%255s %u", hostname, &port) != 2 )
487 return drweb_errlog_defer(
488 string_sprintf("invalid socket '%s'", scanner_options));
490 /* Lookup the host */
491 if((he = gethostbyname(CS hostname)) == 0)
492 return drweb_errlog_defer(
493 string_sprintf("failed to lookup host '%s'", hostname));
495 in = *(struct in_addr *) he->h_addr_list[0];
497 /* Open the drwebd TCP socket */
498 if ( (sock = ip_socket(SOCK_STREAM, AF_INET)) < 0)
499 return drweb_errlog_defer(
500 string_sprintf("unable to acquire socket (%s)", strerror(errno)));
502 if (ip_connect(sock, AF_INET, (uschar*)inet_ntoa(in), port, 5) < 0) {
505 return drweb_errlog_defer(
506 string_sprintf("connection to %s, port %u failed (%s)",
507 inet_ntoa(in), port, strerror(err)));
510 /* prepare variables */
511 drweb_cmd = htonl(DRWEBD_SCAN_CMD);
512 drweb_flags = htonl(DRWEBD_RETURN_VIRUSES | DRWEBD_IS_MAIL);
515 drweb_fd = open(CS eml_filename, O_RDONLY);
516 if (drweb_fd == -1) {
519 return drweb_errlog_defer(
520 string_sprintf("can't open spool file %s: %s",
521 eml_filename, strerror(err)));
523 fsize = lseek(drweb_fd, 0, SEEK_END);
527 (void)close(drweb_fd);
528 return drweb_errlog_defer(
529 string_sprintf("can't seek spool file %s: %s",
530 eml_filename, strerror(err)));
532 drweb_slen = htonl(fsize);
533 lseek(drweb_fd, 0, SEEK_SET);
535 DEBUG(D_acl) debug_printf("Malware scan: issuing %s remote scan [%s %u]\n",
536 scanner_name, hostname, port);
538 /* send scan request */
539 if ((send(sock, &drweb_cmd, sizeof(drweb_cmd), 0) < 0) ||
540 (send(sock, &drweb_flags, sizeof(drweb_flags), 0) < 0) ||
541 (send(sock, &drweb_fin, sizeof(drweb_fin), 0) < 0) ||
542 (send(sock, &drweb_slen, sizeof(drweb_slen), 0) < 0)) {
544 (void)close(drweb_fd);
545 return drweb_errlog_defer(
546 string_sprintf("unable to send commands to socket (%s)", scanner_options));
549 drweb_fbuf = (uschar *) malloc (fsize);
552 (void)close(drweb_fd);
553 return drweb_errlog_defer(
554 string_sprintf("unable to allocate memory %u for file (%s)",
555 fsize, eml_filename));
558 result = read (drweb_fd, drweb_fbuf, fsize);
562 (void)close(drweb_fd);
564 return drweb_errlog_defer(
565 string_sprintf("can't read spool file %s: %s",
566 eml_filename, strerror(err)));
568 (void)close(drweb_fd);
570 /* send file body to socket */
571 if (send(sock, drweb_fbuf, fsize, 0) < 0) {
574 return drweb_errlog_defer(
575 string_sprintf("unable to send file body to socket (%s)", scanner_options));
577 (void)close(drweb_fd);
580 if((sock = m_unixsocket(scanner_options, &errstr)) < 0)
581 return drweb_errlog_defer(errstr);
583 /* prepare variables */
584 drweb_cmd = htonl(DRWEBD_SCAN_CMD);
585 drweb_flags = htonl(DRWEBD_RETURN_VIRUSES | DRWEBD_IS_MAIL);
586 drweb_slen = htonl(Ustrlen(eml_filename));
588 DEBUG(D_acl) debug_printf("Malware scan: issuing %s local scan [%s]\n",
589 scanner_name, scanner_options);
591 /* send scan request */
592 if ((send(sock, &drweb_cmd, sizeof(drweb_cmd), 0) < 0) ||
593 (send(sock, &drweb_flags, sizeof(drweb_flags), 0) < 0) ||
594 (send(sock, &drweb_slen, sizeof(drweb_slen), 0) < 0) ||
595 (send(sock, eml_filename, Ustrlen(eml_filename), 0) < 0) ||
596 (send(sock, &drweb_fin, sizeof(drweb_fin), 0) < 0)) {
598 return drweb_errlog_defer(
599 string_sprintf("unable to send commands to socket (%s)", scanner_options));
603 /* wait for result */
604 if ((bread = recv(sock, &drweb_rc, sizeof(drweb_rc), 0) != sizeof(drweb_rc))) {
606 return drweb_errlog_defer("unable to read return code");
608 drweb_rc = ntohl(drweb_rc);
610 if ((bread = recv(sock, &drweb_vnum, sizeof(drweb_vnum), 0) != sizeof(drweb_vnum))) {
612 return drweb_errlog_defer("unable to read the number of viruses");
614 drweb_vnum = ntohl(drweb_vnum);
616 /* "virus(es) found" if virus number is > 0 */
621 /* setup default virus name */
622 malware_name_internal = "unknown";
623 malware_name = malware_name_internal;
625 /* set up match regex */
626 drweb_re = pcre_compile( "infected\\swith\\s*(.+?)$", PCRE_COPT,
627 (const char **)&rerror, &roffset, NULL );
629 /* read and concatenate virus names into one string */
630 for (i=0;i<drweb_vnum;i++)
632 int size = 0, off = 0;
633 /* read the size of report */
634 if ((bread = recv(sock, &drweb_slen, sizeof(drweb_slen), 0) != sizeof(drweb_slen))) {
636 return drweb_errlog_defer("cannot read report size");
638 drweb_slen = ntohl(drweb_slen);
639 tmpbuf = store_get(drweb_slen);
641 /* read report body */
642 if ((bread = recv(sock, tmpbuf, drweb_slen, 0)) != drweb_slen) {
644 return drweb_errlog_defer("cannot read report string");
646 tmpbuf[drweb_slen] = '\0';
648 /* try matcher on the line, grab substring */
649 result = pcre_exec(drweb_re, NULL, CS tmpbuf, Ustrlen(tmpbuf), 0, 0,
650 ovector, nelements(ovector));
652 const char * pre_malware_nb;
654 pcre_get_substring(CS tmpbuf, ovector, result, 1, &pre_malware_nb);
656 /* the first name we just copy to malware_name */
658 malware_name_internal = string_append(NULL, &size, &off,
661 /* concatenate each new virus name to previous */
662 malware_name_internal = string_append(malware_name_internal,
663 &size, &off, 2, "/", pre_malware_nb);
665 pcre_free_substring(pre_malware_nb);
670 const char *drweb_s = NULL;
672 if (drweb_rc & DERR_READ_ERR) drweb_s = "read error";
673 if (drweb_rc & DERR_NOMEMORY) drweb_s = "no memory";
674 if (drweb_rc & DERR_TIMEOUT) drweb_s = "timeout";
675 if (drweb_rc & DERR_BAD_CALL) drweb_s = "wrong command";
676 /* retcodes DERR_SYMLINK, DERR_NO_REGFILE, DERR_SKIPPED.
677 * DERR_TOO_BIG, DERR_TOO_COMPRESSED, DERR_SPAM,
678 * DERR_CRC_ERROR, DERR_READSOCKET, DERR_WRITE_ERR
679 * and others are ignored */
682 return drweb_errlog_defer(
683 string_sprintf("drweb daemon retcode 0x%x (%s)", drweb_rc, drweb_s));
692 case M_AVES: /* "aveserver" scanner type -------------------------------- */
695 struct sockaddr_un server;
699 if((sock = m_unixsocket(scanner_options, &errstr)) < 0)
700 return aves_errlog_defer(errstr);
702 /* read aveserver's greeting and see if it is ready (2xx greeting) */
703 recv_line(sock, buf, sizeof(buf));
706 /* aveserver is having problems */
708 return aves_errlog_defer(
709 string_sprintf("unavailable (Responded: %s).", ((buf[0] != 0) ? buf : (uschar *)"nothing") ));
712 /* prepare our command */
713 (void)string_format(buf, sizeof(buf), "SCAN bPQRSTUW %s\r\n",
716 DEBUG(D_acl) debug_printf("Malware scan: issuing %s SCAN\n", scanner_name);
719 if (m_sock_send(sock, buf, Ustrlen(buf), &errstr) < 0)
720 return aves_errlog_defer(errstr);
724 /* read response lines, find malware name and final response */
725 while (recv_line(sock, buf, sizeof(buf)) > 0) {
726 debug_printf("aveserver: %s\n", buf);
730 /* aveserver is having problems */
731 log_write(0, LOG_MAIN|LOG_PANIC,
732 "malware acl condition: unable to scan file %s (Responded: %s).",
736 } else if (Ustrncmp(buf,"322",3) == 0) {
737 uschar *p = Ustrchr(&buf[4],' ');
739 malware_name_internal = string_copy(&buf[4]);
740 malware_name = malware_name_internal;
744 /* prepare our command */
745 (void)string_format(buf, sizeof(buf), "quit\r\n");
748 if (m_sock_send(sock, buf, Ustrlen(buf), &errstr) < 0)
749 return aves_errlog_defer(errstr);
751 /* read aveserver's greeting and see if it is ready (2xx greeting) */
752 recv_line(sock, buf, sizeof(buf));
755 /* aveserver is having problems */
757 return aves_errlog_defer(
758 string_sprintf("unable to quit dialogue (Responded: %s).",
759 ((buf[0] != 0) ? buf : (uschar *)"nothing") ));
764 if (result == DEFER) return DEFER;
768 case M_FSEC: /* "fsecure" scanner type ---------------------------------- */
770 struct sockaddr_un server;
771 int sock, i, j, bread = 0;
773 uschar av_buffer[1024];
775 static uschar *cmdopt[] = { US"CONFIGURE\tARCHIVE\t1\n",
776 US"CONFIGURE\tTIMEOUT\t0\n",
777 US"CONFIGURE\tMAXARCH\t5\n",
778 US"CONFIGURE\tMIME\t1\n" };
782 if((sock = m_unixsocket(scanner_options, &errstr)) < 0)
783 return fsec_errlog_defer(errstr);
785 DEBUG(D_acl) debug_printf("Malware scan: issuing %s scan [%s]\n",
786 scanner_name, scanner_options);
789 memset(av_buffer, 0, sizeof(av_buffer));
790 for (i=0; i != nelements(cmdopt); i++) {
791 /* debug_printf("send option \"%s\"",cmdopt[i]); */
793 if (m_sock_send(sock, cmdopt[i], Ustrlen(cmdopt[i]), &errstr) < 0)
794 return fsec_errlog_defer(errstr);
796 bread = ip_recv(sock, av_buffer, sizeof(av_buffer), MALWARE_TIMEOUT);
797 if (bread >0) av_buffer[bread]='\0';
801 return fsec_errlog_defer(
802 string_sprintf("unable to read answer %d (%s)", i, strerror(errno)));
804 for (j=0;j<bread;j++)
805 if((av_buffer[j]=='\r')||(av_buffer[j]=='\n'))
807 /* debug_printf("read answer %d read=%d \"%s\"\n", i, bread, av_buffer ); */
808 /* while (Ustrstr(av_buffer, "OK\tServer configured.@") == NULL); */
811 /* pass the mailfile to fsecure */
812 file_name = string_sprintf("SCAN\t%s\n", eml_filename);
814 /* debug_printf("send scan %s", file_name); */
815 if (m_sock_send(sock, file_name, Ustrlen(file_name), &errstr) < 0)
816 return fsec_errlog_defer(errstr);
819 /* todo also SUSPICION\t */
820 fs_inf = pcre_compile("\\S{0,5}INFECTED\\t[^\\t]*\\t([^\\t]+)\\t\\S*$", PCRE_COPT, (const char **)&rerror, &roffset, NULL);
822 /* read report, linewise */
826 memset(av_buffer, 0, sizeof(av_buffer));
828 bread=ip_recv(sock, &av_buffer[i], 1, MALWARE_TIMEOUT);
832 return fsec_errlog_defer(
833 string_sprintf("unable to read result (%s)", strerror(err)));
837 while ((i < sizeof(av_buffer)-1 ) && (av_buffer[i-1] != '\n'));
838 av_buffer[i-1] = '\0';
839 /* debug_printf("got line \"%s\"\n",av_buffer); */
841 /* Really search for virus again? */
842 if (malware_name == NULL) {
843 /* try matcher on the line, grab substring */
844 i = pcre_exec(fs_inf, NULL, CS av_buffer, Ustrlen(av_buffer), 0, 0,
845 ovector, nelements(ovector));
848 pcre_get_substring(CS av_buffer, ovector, i, 1,
849 (const char **) &malware_name_internal);
850 malware_name = malware_name_internal;
854 while (Ustrstr(av_buffer, "OK\tScan ok.") == NULL);
859 case M_KAVD: /* "kavdaemon" scanner type -------------------------------- */
861 struct sockaddr_un server;
865 uschar * scanrequest;
867 unsigned long kav_reportlen, bread;
871 if((sock = m_unixsocket(scanner_options, &errstr)) < 0)
872 return kavd_errlog_defer(errstr);
874 /* get current date and time, build scan request */
876 /* pdp note: before the eml_filename parameter, this scanned the
877 directory; not finding documentation, so we'll strip off the directory.
878 The side-effect is that the test framework scanning may end up in
879 scanning more than was requested, but for the normal interface, this is
881 strftime(CS tmpbuf, sizeof(tmpbuf), "%d %b %H:%M:%S", localtime(&t));
882 scanrequest = string_sprintf("<0>%s:%s", CS tmpbuf, eml_filename);
883 p = Ustrrchr(scanrequest, '/');
887 DEBUG(D_acl) debug_printf("Malware scan: issuing %s scan [%s]\n",
888 scanner_name, scanner_options);
890 /* send scan request */
891 if (m_sock_send(sock, scanrequest, Ustrlen(scanrequest)+1, &errstr) < 0)
892 return kavd_errlog_defer(errstr);
894 /* wait for result */
895 if ((bread = recv(sock, tmpbuf, 2, 0) != 2)) {
897 return kavd_errlog_defer("unable to read 2 bytes from socket.");
900 /* get errorcode from one nibble */
901 kav_rc = tmpbuf[ test_byte_order()==LITTLE_MY_ENDIAN ? 0 : 1 ] & 0x0F;
903 /* improper kavdaemon configuration */
904 if ( (kav_rc == 5) || (kav_rc == 6) ) {
906 return kavd_errlog_defer("please reconfigure kavdaemon to NOT disinfect or remove infected files.");
911 return kavd_errlog_defer("reported 'scanning not completed' (code 1).");
916 return kavd_errlog_defer("reported 'kavdaemon damaged' (code 7).");
919 /* code 8 is not handled, since it is ambigous. It appears mostly on
920 bounces where part of a file has been cut off */
922 /* "virus found" return codes (2-4) */
923 if ((kav_rc > 1) && (kav_rc < 5)) {
926 /* setup default virus name */
927 malware_name_internal = string_copy("unknown");
928 malware_name = malware_name_internal;
930 report_flag = tmpbuf[ test_byte_order() == LITTLE_MY_ENDIAN ? 1 : 0 ];
932 /* read the report, if available */
933 if( report_flag == 1 ) {
934 /* read report size */
935 if ((bread = recv(sock, &kav_reportlen, 4, 0)) != 4) {
937 return kavd_errlog_defer("cannot read report size");
940 /* it's possible that avp returns av_buffer[1] == 1 but the
941 reportsize is 0 (!?) */
942 if (kav_reportlen > 0) {
943 /* set up match regex, depends on retcode */
944 kav_re = pcre_compile( kav_rc == 3
945 ? "suspicion:\\s*(.+?)\\s*$"
946 : "infected:\\s*(.+?)\\s*$",
948 (const char **)&rerror,
952 /* read report, linewise */
953 while (kav_reportlen > 0) {
958 while ( recv(sock, &tmpbuf[bread], 1, 0) == 1 ) {
960 if ( (tmpbuf[bread] == '\n') || (bread > 1021) ) break;
964 tmpbuf[bread] = '\0';
966 /* try matcher on the line, grab substring */
967 result = pcre_exec(kav_re, NULL, CS tmpbuf, Ustrlen(tmpbuf), 0, 0,
968 ovector, nelements(ovector));
970 pcre_get_substring(CS tmpbuf, ovector, result, 1,
971 (const char **) &malware_name_internal);
978 else /* no virus found */
985 case M_CMDL: /* "cmdline" scanner type ---------------------------------- */
987 const uschar *cmdline_scanner = scanner_options;
988 uschar *cmdline_trigger;
989 const pcre *cmdline_trigger_re;
990 uschar *cmdline_regex;
991 const pcre *cmdline_regex_re;
993 uschar * commandline;
994 void (*eximsigchld)(int);
995 void (*eximsigpipe)(int);
996 FILE *scanner_out = NULL;
997 FILE *scanner_record = NULL;
998 uschar linebuffer[32767];
1005 if (!cmdline_scanner)
1006 return cmdl_errlog_defer("missing commandline specification");
1008 /* find scanner output trigger */
1009 if (!(cmdline_trigger = string_nextinlist(&av_scanner_work, &sep, NULL, 0)))
1010 return cmdl_errlog_defer("missing trigger specification");
1012 /* precompile trigger regex */
1013 cmdline_trigger_re = pcre_compile(CS cmdline_trigger, PCRE_COPT, (const char **)&rerror, &roffset, NULL);
1014 if (cmdline_trigger_re == NULL)
1015 return cmdl_errlog_defer(
1016 string_sprintf("regular expression error in '%s': %s at offset %d",
1017 cmdline_trigger, rerror, roffset));
1019 /* find scanner name regex */
1020 if (!(cmdline_regex = string_nextinlist(&av_scanner_work, &sep, NULL, 0)))
1021 return cmdl_errlog_defer("missing virus name regex specification");
1023 /* precompile name regex */
1024 cmdline_regex_re = pcre_compile(CS cmdline_regex, PCRE_COPT, (const char **)&rerror, &roffset, NULL);
1025 if (cmdline_regex_re == NULL)
1026 return cmdl_errlog_defer(
1027 string_sprintf("regular expression error in '%s': %s at offset %d",
1028 cmdline_regex, rerror, roffset));
1030 /* prepare scanner call; despite the naming, file_name holds a directory
1031 name which is documented as the value given to %s. */
1033 file_name = string_copy(eml_filename);
1034 p = Ustrrchr(file_name, '/');
1037 commandline = string_sprintf(CS cmdline_scanner, file_name);
1039 /* redirect STDERR too */
1040 commandline = string_sprintf("%s 2>&1", commandline);
1042 DEBUG(D_acl) debug_printf("Malware scan: issuing %s scan [%s]\n", scanner_name, commandline);
1044 /* store exims signal handlers */
1045 eximsigchld = signal(SIGCHLD,SIG_DFL);
1046 eximsigpipe = signal(SIGPIPE,SIG_DFL);
1048 scanner_out = popen(CS commandline,"r");
1049 if (scanner_out == NULL) {
1051 signal(SIGCHLD,eximsigchld);
1052 signal(SIGPIPE,eximsigpipe);
1053 return cmdl_errlog_defer(
1054 string_sprintf("call (%s) failed: %s.", commandline, strerror(err)));
1057 file_name = string_sprintf("%s/scan/%s/%s_scanner_output",
1058 spool_directory, message_id, message_id);
1059 scanner_record = modefopen(file_name, "wb", SPOOL_MODE);
1061 if (scanner_record == NULL) {
1063 pclose(scanner_out);
1064 signal(SIGCHLD,eximsigchld);
1065 signal(SIGPIPE,eximsigpipe);
1066 return cmdl_errlog_defer(
1067 string_sprintf("opening scanner output file (%s) failed: %s.",
1068 file_name, strerror(err)));
1071 /* look for trigger while recording output */
1072 while(fgets(CS linebuffer, sizeof(linebuffer), scanner_out) != NULL) {
1073 if ( Ustrlen(linebuffer) > fwrite(linebuffer, 1, Ustrlen(linebuffer), scanner_record) ) {
1075 pclose(scanner_out);
1076 signal(SIGCHLD,eximsigchld);
1077 signal(SIGPIPE,eximsigpipe);
1078 return cmdl_errlog_defer(
1079 string_sprintf("short write on scanner output file (%s).", file_name));
1081 /* try trigger match */
1082 if (!trigger && regex_match_and_setup(cmdline_trigger_re, linebuffer, 0, -1))
1086 (void)fclose(scanner_record);
1087 pclose(scanner_out);
1088 signal(SIGCHLD,eximsigchld);
1089 signal(SIGPIPE,eximsigpipe);
1092 /* setup default virus name */
1093 malware_name = US"unknown";
1095 /* re-open the scanner output file, look for name match */
1096 scanner_record = fopen(CS file_name, "rb");
1097 while(fgets(CS linebuffer,32767,scanner_record) != NULL) {
1099 result = pcre_exec(cmdline_regex_re, NULL,
1100 CS linebuffer, Ustrlen(linebuffer), 0, 0,
1101 ovector, nelements(ovector));
1103 pcre_get_substring(CS linebuffer, ovector, result, 1,
1104 (const char **) &malware_name_internal);
1106 (void)fclose(scanner_record);
1108 else /* no virus found */
1109 malware_name = NULL;
1113 case M_SOPHIE: /* "sophie" scanner type --------------------------------- */
1116 struct sockaddr_un server;
1120 uschar av_buffer[1024];
1122 if((sock = m_unixsocket(scanner_options, &errstr)) < 0)
1123 return soph_errlog_defer(errstr);
1125 /* pass the scan directory to sophie */
1126 file_name = string_copy(eml_filename);
1127 if ((p = Ustrrchr(file_name, '/')))
1130 DEBUG(D_acl) debug_printf("Malware scan: issuing %s scan [%s]\n",
1131 scanner_name, scanner_options);
1133 if ( write(sock, file_name, Ustrlen(file_name)) < 0
1134 || write(sock, "\n", 1) != 1
1137 return soph_errlog_defer(
1138 string_sprintf("unable to write to UNIX socket (%s)", scanner_options));
1141 /* wait for result */
1142 memset(av_buffer, 0, sizeof(av_buffer));
1143 if ((!(bread = ip_recv(sock, av_buffer, sizeof(av_buffer), MALWARE_TIMEOUT)) > 0)) {
1145 return soph_errlog_defer(
1146 string_sprintf("unable to read from UNIX socket (%s)", scanner_options));
1152 if (av_buffer[0] == '1') {
1153 if (Ustrchr(av_buffer, '\n')) *Ustrchr(av_buffer, '\n') = '\0';
1154 malware_name_internal = string_copy(&av_buffer[2]);
1155 malware_name = malware_name_internal;
1157 else if (!strncmp(CS av_buffer, "-1", 2))
1158 return soph_errlog_defer("scanner reported error");
1159 else /* all ok, no virus */
1160 malware_name = NULL;
1164 case M_CLAMD: /* "clamd" scanner type ----------------------------------- */
1166 /* This code was originally contributed by David Saez */
1167 /* There are three scanning methods available to us:
1168 * (1) Use the SCAN command, pointing to a file in the filesystem
1169 * (2) Use the STREAM command, send the data on a separate port
1170 * (3) Use the zINSTREAM command, send the data inline
1171 * The zINSTREAM command was introduced with ClamAV 0.95, which marked
1172 * STREAM deprecated; see: http://wiki.clamav.net/bin/view/Main/UpgradeNotes095
1173 * In Exim, we use SCAN if using a Unix-domain socket or explicitly told that
1174 * the TCP-connected daemon is actually local; otherwise we use zINSTREAM unless
1175 * WITH_OLD_CLAMAV_STREAM is defined.
1176 * See Exim bug 926 for details. */
1178 uschar *p, *vname, *result_tag, *response_end;
1179 struct sockaddr_un server;
1183 uschar av_buffer[1024];
1184 uschar *hostname = "";
1187 uschar *clamav_fbuf;
1188 int clam_fd, result;
1190 BOOL use_scan_command = FALSE;
1191 clamd_address_container * clamd_address_vector[MAX_CLAMD_SERVERS];
1193 int num_servers = 0;
1194 #ifdef WITH_OLD_CLAMAV_STREAM
1195 uschar av_buffer2[1024];
1198 uint32_t send_size, send_final_zeroblock;
1201 if (*scanner_options == '/')
1202 /* Local file; so we def want to use_scan_command and don't want to try
1203 * passing IP/port combinations */
1204 use_scan_command = TRUE;
1206 const uschar *address = scanner_options;
1207 uschar address_buffer[MAX_CLAMD_ADDRESS_LENGTH + 20];
1209 /* Go through the rest of the list of host/port and construct an array
1210 * of servers to try. The first one is the bit we just passed from
1211 * scanner_options so process that first and then scan the remainder of
1212 * the address buffer */
1214 clamd_address_container *this_clamd;
1216 /* The 'local' option means use the SCAN command over the network
1217 * socket (ie common file storage in use) */
1218 if (strcmpic(address,US"local") == 0) {
1219 use_scan_command = TRUE;
1223 /* XXX: If unsuccessful we should free this memory */
1225 (clamd_address_container *)store_get(sizeof(clamd_address_container));
1227 /* extract host and port part */
1228 if( sscanf(CS address, "%" MAX_CLAMD_ADDRESS_LENGTH_S "s %u",
1229 this_clamd->tcp_addr, &(this_clamd->tcp_port)) != 2 ) {
1230 clmd_errlog(string_sprintf("invalid address '%s'", address));
1234 clamd_address_vector[num_servers] = this_clamd;
1236 if (num_servers >= MAX_CLAMD_SERVERS) {
1237 clmd_errlog("More than " MAX_CLAMD_SERVERS_S " clamd servers "
1238 "specified; only using the first " MAX_CLAMD_SERVERS_S );
1241 } while ((address = string_nextinlist(&av_scanner_work, &sep,
1243 sizeof(address_buffer))) != NULL);
1245 /* check if we have at least one server */
1247 return clmd_errlog_defer("no useable server addresses in malware configuration option.");
1250 /* See the discussion of response formats below to see why we really don't
1251 like colons in filenames when passing filenames to ClamAV. */
1252 if (use_scan_command && Ustrchr(eml_filename, ':'))
1253 return clmd_errlog_defer(
1254 string_sprintf("local/SCAN mode incompatible with" \
1255 " : in path to email filename [%s]", eml_filename));
1257 /* We have some network servers specified */
1260 /* Confirmed in ClamAV source (0.95.3) that the TCPAddr option of clamd
1261 * only supports AF_INET, but we should probably be looking to the
1262 * future and rewriting this to be protocol-independent anyway. */
1264 while ( num_servers > 0 ) {
1265 /* Randomly pick a server to start with */
1266 current_server = random_number( num_servers );
1268 debug_printf("trying server name %s, port %u\n",
1269 clamd_address_vector[current_server]->tcp_addr,
1270 clamd_address_vector[current_server]->tcp_port);
1272 /* Lookup the host. This is to ensure that we connect to the same IP
1273 * on both connections (as one host could resolve to multiple ips) */
1274 if((he = gethostbyname(CS clamd_address_vector[current_server]->tcp_addr))
1276 clmd_errlog(string_sprintf("failed to lookup host '%s'",
1277 clamd_address_vector[current_server]->tcp_addr));
1278 goto try_next_server;
1281 in = *(struct in_addr *) he->h_addr_list[0];
1283 /* Open the ClamAV Socket */
1284 if ( (sock = ip_socket(SOCK_STREAM, AF_INET)) < 0) {
1285 clmd_errlog(string_sprintf("unable to acquire socket (%s)",
1287 goto try_next_server;
1290 if (ip_connect( sock,
1292 (uschar*)inet_ntoa(in),
1293 clamd_address_vector[current_server]->tcp_port,
1295 /* Connection successfully established with a server */
1296 hostname = clamd_address_vector[current_server]->tcp_addr;
1299 clmd_errlog(string_sprintf(
1300 "malware acl condition: clamd: connection to %s, port %u failed (%s)",
1301 clamd_address_vector[current_server]->tcp_addr,
1302 clamd_address_vector[current_server]->tcp_port,
1309 /* Remove the server from the list. XXX We should free the memory */
1312 for( i = current_server; i < num_servers; i++ )
1313 clamd_address_vector[i] = clamd_address_vector[i+1];
1316 if ( num_servers == 0 )
1317 return clmd_errlog_defer("all servers failed");
1320 if((sock = m_unixsocket(scanner_options, &errstr)) < 0)
1321 return clmd_errlog_defer(errstr);
1324 /* have socket in variable "sock"; command to use is semi-independent of
1325 * the socket protocol. We use SCAN if is local (either Unix/local
1326 * domain socket, or explicitly told local) else we stream the data.
1327 * How we stream the data depends upon how we were built. */
1329 if (!use_scan_command) {
1331 #ifdef WITH_OLD_CLAMAV_STREAM
1332 /* "STREAM\n" command, get back a "PORT <N>\n" response, send data to
1333 * that port on a second connection; then in the scan-method-neutral
1334 * part, read the response back on the original connection. */
1336 DEBUG(D_acl) debug_printf("Malware scan: issuing %s old-style remote scan (PORT)\n",
1339 /* Pass the string to ClamAV (7 = "STREAM\n") */
1340 if (m_sock_send(sock, US"STREAM\n", 7, &errstr) < 0)
1341 return clmd_errlog_defer(errstr);
1343 memset(av_buffer2, 0, sizeof(av_buffer2));
1344 bread = ip_recv(sock, av_buffer2, sizeof(av_buffer2), MALWARE_TIMEOUT);
1349 return clmd_errlog_defer(
1350 string_sprintf("unable to read PORT from socket (%s)",
1354 if (bread == sizeof(av_buffer2)) {
1356 return clmd_errlog_defer("buffer too small");
1359 if (!(*av_buffer2)) {
1361 return clmd_errlog_defer("ClamAV returned null");
1364 av_buffer2[bread] = '\0';
1365 if( sscanf(CS av_buffer2, "PORT %u\n", &port) != 1 ) {
1367 return clmd_errlog_defer(
1368 string_sprintf("Expected port information from clamd, got '%s'",
1372 if ( (sockData = ip_socket(SOCK_STREAM, AF_INET)) < 0) {
1375 return clmd_errlog_defer(
1376 string_sprintf("unable to acquire socket (%s)", strerror(err)));
1379 if (ip_connect(sockData, AF_INET, (uschar*)inet_ntoa(in), port, 5) < 0) {
1381 (void)close(sockData); (void)close(sock);
1382 return clmd_errlog_defer(
1383 string_sprintf("connection to %s, port %u failed (%s)",
1384 inet_ntoa(in), port, strerror(err)));
1387 #define CLOSE_SOCKDATA (void)close(sockData)
1388 #else /* WITH_OLD_CLAMAV_STREAM not defined */
1389 /* New protocol: "zINSTREAM\n" followed by a sequence of <length><data>
1390 chunks, <n> a 4-byte number (network order), terminated by a zero-length
1393 DEBUG(D_acl) debug_printf("Malware scan: issuing %s new-style remote scan (zINSTREAM)\n",
1396 /* Pass the string to ClamAV (10 = "zINSTREAM\0") */
1397 if (send(sock, "zINSTREAM", 10, 0) < 0) {
1400 return clmd_errlog_defer(
1401 string_sprintf("unable to send zINSTREAM to socket (%s)",
1405 #define CLOSE_SOCKDATA /**/
1408 /* calc file size */
1409 clam_fd = open(CS eml_filename, O_RDONLY);
1410 if (clam_fd == -1) {
1412 CLOSE_SOCKDATA; (void)close(sock);
1413 return clmd_errlog_defer(
1414 string_sprintf("can't open spool file %s: %s",
1415 eml_filename, strerror(err)));
1417 fsize = lseek(clam_fd, 0, SEEK_END);
1420 CLOSE_SOCKDATA; (void)close(sock); (void)close(clam_fd);
1421 return clmd_errlog_defer(
1422 string_sprintf("can't seek spool file %s: %s",
1423 eml_filename, strerror(errno)));
1425 lseek(clam_fd, 0, SEEK_SET);
1427 clamav_fbuf = (uschar *) malloc (fsize);
1429 CLOSE_SOCKDATA; (void)close(sock); (void)close(clam_fd);
1430 return clmd_errlog_defer(
1431 string_sprintf("unable to allocate memory %u for file (%s)",
1432 fsize, eml_filename));
1435 result = read (clam_fd, clamav_fbuf, fsize);
1438 CLOSE_SOCKDATA; (void)close(sock); (void)close(clam_fd);
1440 return clmd_errlog_defer(
1441 string_sprintf("can't read spool file %s: %s",
1442 eml_filename, strerror(err)));
1444 (void)close(clam_fd);
1446 /* send file body to socket */
1447 #ifdef WITH_OLD_CLAMAV_STREAM
1448 if (send(sockData, clamav_fbuf, fsize, 0) < 0) {
1449 CLOSE_SOCKDATA; (void)close(sock);
1451 return clmd_errlog_defer(
1452 string_sprintf("unable to send file body to socket (%s:%u)",
1456 send_size = htonl(fsize);
1457 send_final_zeroblock = 0;
1458 if ((send(sock, &send_size, sizeof(send_size), 0) < 0) ||
1459 (send(sock, clamav_fbuf, fsize, 0) < 0) ||
1460 (send(sock, &send_final_zeroblock, sizeof(send_final_zeroblock), 0) < 0))
1464 return clmd_errlog_defer(
1465 string_sprintf("unable to send file body to socket (%s:%u)",
1473 #undef CLOSE_SOCKDATA
1475 } else { /* use scan command */
1476 /* Send a SCAN command pointing to a filename; then in the then in the
1477 * scan-method-neutral part, read the response back */
1479 /* ================================================================= */
1481 /* Prior to the reworking post-Exim-4.72, this scanned a directory,
1482 which dates to when ClamAV needed us to break apart the email into the
1483 MIME parts (eg, with the now deprecated demime condition coming first).
1484 Some time back, ClamAV gained the ability to deconstruct the emails, so
1485 doing this would actually have resulted in the mail attachments being
1486 scanned twice, in the broken out files and from the original .eml.
1487 Since ClamAV now handles emails (and has for quite some time) we can
1488 just use the email file itself. */
1489 /* Pass the string to ClamAV (7 = "SCAN \n" + \0) */
1490 file_name = string_sprintf("SCAN %s\n", eml_filename);
1492 DEBUG(D_acl) debug_printf("Malware scan: issuing %s local-path scan [%s]\n",
1493 scanner_name, scanner_options);
1495 if (send(sock, file_name, Ustrlen(file_name), 0) < 0) {
1498 return clmd_errlog_defer(
1499 string_sprintf("unable to write to socket (%s)", strerror(err)));
1502 /* Do not shut down the socket for writing; a user report noted that
1503 * clamd 0.70 does not react well to this. */
1505 /* Commands have been sent, no matter which scan method or connection
1506 * type we're using; now just read the result, independent of method. */
1508 /* Read the result */
1509 memset(av_buffer, 0, sizeof(av_buffer));
1510 bread = ip_recv(sock, av_buffer, sizeof(av_buffer), MALWARE_TIMEOUT);
1514 return clmd_errlog_defer(
1515 string_sprintf("unable to read from socket (%s)", strerror(errno)));
1517 if (bread == sizeof(av_buffer))
1518 return clmd_errlog_defer("buffer too small");
1519 /* We're now assured of a NULL at the end of av_buffer */
1521 /* Check the result. ClamAV returns one of two result formats.
1522 In the basic mode, the response is of the form:
1523 infected: -> "<filename>: <virusname> FOUND"
1524 not-infected: -> "<filename>: OK"
1525 error: -> "<filename>: <errcode> ERROR
1526 If the ExtendedDetectionInfo option has been turned on, then we get:
1527 "<filename>: <virusname>(<virushash>:<virussize>) FOUND"
1528 for the infected case. Compare:
1529 /tmp/eicar.com: Eicar-Test-Signature FOUND
1530 /tmp/eicar.com: Eicar-Test-Signature(44d88612fea8a8f36de82e1278abb02f:68) FOUND
1532 In the streaming case, clamd uses the filename "stream" which you should
1533 be able to verify with { ktrace clamdscan --stream /tmp/eicar.com }. (The
1534 client app will replace "stream" with the original filename before returning
1535 results to stdout, but the trace shows the data).
1537 We will assume that the pathname passed to clamd from Exim does not contain
1538 a colon. We will have whined loudly above if the eml_filename does (and we're
1539 passing a filename to clamd). */
1542 return clmd_errlog_defer("ClamAV returned null");
1544 /* strip newline at the end (won't be present for zINSTREAM)
1545 (also any trailing whitespace, which shouldn't exist, but we depend upon
1546 this below, so double-check) */
1547 p = av_buffer + Ustrlen(av_buffer) - 1;
1548 if (*p == '\n') *p = '\0';
1550 DEBUG(D_acl) debug_printf("Malware response: %s\n", av_buffer);
1552 while (isspace(*--p) && (p > av_buffer))
1557 /* colon in returned output? */
1558 if((p = Ustrchr(av_buffer,':')) == NULL)
1559 return clmd_errlog_defer(
1560 string_sprintf("ClamAV returned malformed result (missing colon): %s",
1563 /* strip filename */
1564 while (*p && isspace(*++p)) /**/;
1567 /* It would be bad to encounter a virus with "FOUND" in part of the name,
1568 but we should at least be resistant to it. */
1569 p = Ustrrchr(vname, ' ');
1570 result_tag = p ? p+1 : vname;
1572 if (Ustrcmp(result_tag, "FOUND") == 0) {
1573 /* p should still be the whitespace before the result_tag */
1574 while (isspace(*p)) --p;
1576 /* Strip off the extended information too, which will be in parens
1577 after the virus name, with no intervening whitespace. */
1579 /* "(hash:size)", so previous '(' will do; if not found, we have
1580 a curious virus name, but not an error. */
1581 p = Ustrrchr(vname, '(');
1585 malware_name_internal = string_copy(vname);
1586 malware_name = malware_name_internal;
1587 DEBUG(D_acl) debug_printf("Malware found, name \"%s\"\n", malware_name);
1589 } else if (Ustrcmp(result_tag, "ERROR") == 0)
1590 return clmd_errlog_defer(
1591 string_sprintf("ClamAV returned: %s", av_buffer));
1593 else if (Ustrcmp(result_tag, "OK") == 0) {
1594 /* Everything should be OK */
1595 malware_name = NULL;
1596 DEBUG(D_acl) debug_printf("Malware not found\n");
1599 return clmd_errlog_defer(
1600 string_sprintf("unparseable response from ClamAV: {%s}", av_buffer));
1605 case M_MKSD: /* "mksd" scanner type ------------------------------------- */
1607 char *mksd_options_end;
1608 int mksd_maxproc = 1; /* default, if no option supplied */
1609 struct sockaddr_un server;
1613 if (scanner_options) {
1614 mksd_maxproc = (int)strtol(CS scanner_options, &mksd_options_end, 10);
1615 if ( *scanner_options == '\0'
1616 || *mksd_options_end != '\0'
1618 || mksd_maxproc > 32
1620 return mksd_errlog_defer(
1621 string_sprintf("invalid option '%s'", scanner_options));
1624 if((sock = m_unixsocket(US "/var/run/mksd/socket", &errstr)) < 0)
1625 return mksd_errlog_defer(errstr);
1627 malware_name = NULL;
1629 DEBUG(D_acl) debug_printf("Malware scan: issuing %s scan\n", scanner_name);
1631 retval = mksd_scan_packed(sock, eml_filename);
1639 /* set "been here, done that" marker */
1643 /* match virus name against pattern (caseless ------->----------v) */
1644 if ( (malware_name != NULL) &&
1645 (regex_match_and_setup(re, malware_name, 0, -1)) ) {
1646 DEBUG(D_acl) debug_printf("Matched regex to malware [%s] [%s]\n", malware_regex, malware_name);
1654 /* simple wrapper for reading lines from sockets */
1656 recv_line(int sock, uschar *buffer, int size)
1660 memset(buffer,0,size);
1662 while(recv(sock,p,1,0) > -1) {
1663 if ((p-buffer) > (size-2)) break;
1664 if (*p == '\n') break;
1665 if (*p != '\r') p++;
1673 /* ============= private routines for the "mksd" scanner type ============== */
1675 #include <sys/uio.h>
1678 mksd_writev (int sock, struct iovec *iov, int iovcnt)
1684 i = writev (sock, iov, iovcnt);
1685 while ((i < 0) && (errno == EINTR));
1688 malware_errlog("unable to write to mksd UNIX socket (/var/run/mksd/socket)");
1693 if (i >= iov->iov_len) {
1700 iov->iov_base = CS iov->iov_base + i;
1707 mksd_read_lines (int sock, uschar *av_buffer, int av_buffer_size)
1713 if ((i = recv (sock, av_buffer+offset, av_buffer_size-offset, 0)) <= 0) {
1715 malware_errlog("unable to read from mksd UNIX socket (/var/run/mksd/socket)");
1720 /* offset == av_buffer_size -> buffer full */
1721 if (offset == av_buffer_size) {
1723 malware_errlog("malformed reply received from mksd");
1726 } while (av_buffer[offset-1] != '\n');
1728 av_buffer[offset] = '\0';
1733 mksd_parse_line (char *line)
1743 if ((p = strchr (line, '\n')) != NULL)
1745 return mksd_errlog_defer(string_sprintf("scanner failed: %s", line));
1748 if ((p = strchr (line, '\n')) != NULL) {
1750 if (((p-line) > 5) && (line[3] == ' '))
1751 if (((p = strchr (line+4, ' ')) != NULL) && ((p-line) > 4)) {
1753 malware_name_internal = string_copy(line+4);
1754 malware_name = malware_name_internal;
1758 return mksd_errlog_defer(
1759 string_sprintf("malformed reply received from mksd: %s", line));
1764 mksd_scan_packed(int sock, uschar *scan_filename)
1766 struct iovec iov[3];
1767 const char *cmd = "MSQ\n";
1768 uschar av_buffer[1024];
1770 iov[0].iov_base = (void *) cmd;
1772 iov[1].iov_base = CS scan_filename;
1773 iov[1].iov_len = Ustrlen(scan_filename);
1774 iov[2].iov_base = (void *) (cmd + 3);
1777 if (mksd_writev (sock, iov, 3) < 0)
1780 if (mksd_read_lines (sock, av_buffer, sizeof (av_buffer)) < 0)
1785 return mksd_parse_line (CS av_buffer);
1788 #endif /*WITH_CONTENT_SCAN*/