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 MALWARE_TIMEOUT 120
38 #define DRWEBD_SCAN_CMD (1) /* scan file, buffer or diskfile */
39 #define DRWEBD_RETURN_VIRUSES (1<<0) /* ask daemon return to us viruses names from report */
40 #define DRWEBD_IS_MAIL (1<<19) /* say to daemon that format is "archive MAIL" */
42 #define DERR_READ_ERR (1<<0) /* read error */
43 #define DERR_NOMEMORY (1<<2) /* no memory */
44 #define DERR_TIMEOUT (1<<9) /* scan timeout has run out */
45 #define DERR_BAD_CALL (1<<15) /* wrong command */
47 /* Routine to check whether a system is big- or litte-endian.
48 Ripped from http://www.faqs.org/faqs/graphics/fileformats-faq/part4/section-7.html
49 Needed for proper kavdaemon implementation. Sigh. */
50 #define BIG_MY_ENDIAN 0
51 #define LITTLE_MY_ENDIAN 1
52 int test_byte_order(void);
56 short int word = 0x0001;
57 char *byte = (char *) &word;
58 return(byte[0] ? LITTLE_MY_ENDIAN : BIG_MY_ENDIAN);
61 static uschar * malware_name_internal = NULL;
64 /* Gross hacks for the -bmalware option; perhaps we should just create
65 the scan directory normally for that case, but look into rigging up the
66 needed header variables if not already set on the command-line? */
67 extern int spool_mbox_ok;
68 extern uschar spooled_message_id[17];
70 /*************************************************
71 * Scan an email for malware *
72 *************************************************/
74 /* This is the normal interface for scanning an email, which doesn't need a
75 filename; it's a wrapper around the malware_file function.
78 listptr the list of options to the "malware = ..." ACL condition
80 Returns: Exim message processing code (OK, FAIL, DEFER, ...)
81 where true means malware was found (condition applies)
84 malware(uschar **listptr)
86 uschar scan_filename[1024];
90 fits = string_format(scan_filename, sizeof(scan_filename),
91 CS"%s/scan/%s/%s.eml", spool_directory, message_id, message_id);
95 log_write(0, LOG_MAIN|LOG_PANIC,
96 "malware filename does not fit in buffer [malware()]");
100 ret = malware_internal(listptr, scan_filename, FALSE);
101 if (ret == DEFER) av_failed = TRUE;
107 /*************************************************
108 * Scan a file for malware *
109 *************************************************/
111 /* This is a test wrapper for scanning an email, which is not used in
112 normal processing. Scan any file, using the Exim scanning interface.
113 This function tampers with various global variables so is unsafe to use
114 in any other context.
117 eml_filename a file holding the message to be scanned
119 Returns: Exim message processing code (OK, FAIL, DEFER, ...)
120 where true means malware was found (condition applies)
123 malware_in_file(uschar *eml_filename)
125 uschar *scan_options[2];
126 uschar message_id_buf[64];
129 scan_options[0] = US"*";
130 scan_options[1] = NULL;
132 /* spool_mbox() assumes various parameters exist, when creating
133 the relevant directory and the email within */
134 (void) string_format(message_id_buf, sizeof(message_id_buf),
135 "dummy-%d", vaguely_random_number(INT_MAX));
136 message_id = message_id_buf;
137 sender_address = US"malware-sender@example.net";
139 recipients_list = NULL;
140 receive_add_recipient(US"malware-victim@example.net", -1);
141 enable_dollar_recipients = TRUE;
143 ret = malware_internal(scan_options, eml_filename, TRUE);
145 Ustrncpy(spooled_message_id, message_id, sizeof(spooled_message_id));
147 /* don't set no_mbox_unspool; at present, there's no way for it to become
148 set, but if that changes, then it should apply to these tests too */
151 /* silence static analysis tools */
159 malware_errlog(const uschar * str)
161 log_write(0, LOG_MAIN|LOG_PANIC, "malware acl condition: %s", str);
164 malware_errlog_defer(const uschar * str)
171 m_scanner_errlog_defer(const uschar * scanner, const uschar * str)
173 return malware_errlog_defer(string_sprintf("%s: %s", scanner, str));
177 fprotd_errlog_defer(const uschar * str)
179 return m_scanner_errlog_defer("f-protd", str);
182 drweb_errlog_defer(const uschar * str)
184 return m_scanner_errlog_defer("drweb", str);
187 aves_errlog_defer(const uschar * str)
189 return m_scanner_errlog_defer("aveserver", str);
192 fsec_errlog_defer(const uschar * str)
194 return m_scanner_errlog_defer("fsecure", str);
197 kavd_errlog_defer(const uschar * str)
199 return m_scanner_errlog_defer("kavdaemon", str);
202 cmdl_errlog_defer(const uschar * str)
204 return m_scanner_errlog_defer("commandline", str);
207 soph_errlog_defer(const uschar * str)
209 return m_scanner_errlog_defer("sophie", str);
212 clmd_errlog_defer(const uschar * str)
214 return m_scanner_errlog_defer("clamd", str);
217 mksd_errlog_defer(const uschar * str)
219 return m_scanner_errlog_defer("mksd", str);
223 clmd_errlog(const uschar * str)
225 log_write(0, LOG_MAIN|LOG_PANIC, "malware acl condition: clamd: %s", str);
228 /*************************************************
229 * Scan content for malware *
230 *************************************************/
232 /* This is an internal interface for scanning an email; the normal interface
233 is via malware(), or there's malware_in_file() used for testing/debugging.
236 listptr the list of options to the "malware = ..." ACL condition
237 eml_filename the file holding the email to be scanned
238 faking whether or not we're faking this up for the -bmalware test
240 Returns: Exim message processing code (OK, FAIL, DEFER, ...)
241 where true means malware was found (condition applies)
244 malware_internal(uschar **listptr, uschar *eml_filename, BOOL faking)
247 uschar *list = *listptr;
248 uschar *av_scanner_work = av_scanner;
249 uschar *scanner_name;
250 uschar scanner_name_buffer[16];
251 uschar *malware_regex;
252 uschar malware_regex_buffer[64];
253 uschar malware_regex_default[] = ".+";
254 unsigned long mbox_size;
258 const uschar *rerror;
260 /* make sure the eml mbox file is spooled up */
261 mbox_file = spool_mbox(&mbox_size, faking ? eml_filename : NULL);
262 if (mbox_file == NULL) /* error while spooling */
263 return malware_errlog_defer("error while creating mbox spool file");
265 /* none of our current scanners need the mbox
266 file as a stream, so we can close it right away */
267 (void)fclose(mbox_file);
269 /* extract the malware regex to match against from the option list */
270 if ((malware_regex = string_nextinlist(&list, &sep,
271 malware_regex_buffer,
272 sizeof(malware_regex_buffer))) != NULL) {
274 /* parse 1st option */
275 if ( (strcmpic(malware_regex,US"false") == 0) ||
276 (Ustrcmp(malware_regex,"0") == 0) ) {
277 /* explicitly no matching */
281 /* special cases (match anything except empty) */
282 if ( (strcmpic(malware_regex,US"true") == 0) ||
283 (Ustrcmp(malware_regex,"*") == 0) ||
284 (Ustrcmp(malware_regex,"1") == 0) ) {
285 malware_regex = malware_regex_default;
289 /* empty means "don't match anything" */
293 /* Reset sep that is set by previous string_nextinlist() call */
296 /* compile the regex, see if it works */
297 re = pcre_compile(CS malware_regex, PCRE_COPT, (const char **)&rerror, &roffset, NULL);
299 return malware_errlog_defer(
300 string_sprintf("regular expression error in '%s': %s at offset %d",
301 malware_regex, rerror, roffset));
303 /* if av_scanner starts with a dollar, expand it first */
304 if (*av_scanner == '$') {
305 av_scanner_work = expand_string(av_scanner);
306 if (!av_scanner_work)
307 return malware_errlog_defer(
308 string_sprintf("av_scanner starts with $, but expansion failed: %s",
309 expand_string_message));
311 debug_printf("Expanded av_scanner global: %s\n", av_scanner_work);
312 /* disable result caching in this case */
318 /* Do not scan twice. */
319 if (malware_ok == 0) {
321 /* find the scanner type from the av_scanner option */
322 if ((scanner_name = string_nextinlist(&av_scanner_work, &sep,
324 sizeof(scanner_name_buffer))) == NULL) {
325 /* no scanner given */
326 return malware_errlog_defer("av_scanner configuration variable is empty");
329 /* "f-protd" scanner type ----------------------------------------------- */
330 if (strcmpic(scanner_name, US"f-protd") == 0) {
331 uschar *fp_options, *fp_scan_option;
332 uschar fp_scan_option_buffer[1024];
333 uschar fp_options_buffer[1024];
334 uschar fp_options_default[] = "localhost 10200-10204";
335 uschar hostname[256];
336 unsigned int port, portlow, porthigh, connect_ok=0, detected=0, par_count = 0;
340 uschar scanrequest[2048], buf[32768], *strhelper, *strhelper2;
342 if ((fp_options = string_nextinlist(&av_scanner_work, &sep,
343 fp_options_buffer, sizeof(fp_options_buffer))) == NULL) {
344 /* no options supplied, use default options */
345 fp_options = fp_options_default;
348 /* extract host and port part */
349 if ( sscanf(CS fp_options, "%s %u-%u", hostname, &portlow, &porthigh) != 3 ) {
350 if ( sscanf(CS fp_options, "%s %u", hostname, &portlow) != 2 )
351 return fprotd_errlog_defer(
352 string_sprintf("invalid socket '%s'", fp_options));
356 /* Lookup the host */
357 if((he = gethostbyname(CS hostname)) == 0)
358 return fprotd_errlog_defer(
359 string_sprintf("failed to lookup host '%s'", hostname));
361 in = *(struct in_addr *) he->h_addr_list[0];
365 /* Open the f-protd TCP socket */
366 if ( (sock = ip_socket(SOCK_STREAM, AF_INET)) < 0)
367 return fprotd_errlog_defer(
368 string_sprintf("unable to acquire socket (%s)", strerror(errno)));
370 /* Try to connect to all portslow-high until connection is established */
371 for (port = portlow; !connect_ok && port < porthigh; port++)
372 if (ip_connect(sock, AF_INET, (uschar*)inet_ntoa(in), port, 5) >= 0)
378 return fprotd_errlog_defer(
379 string_sprintf("connection to %s, port %u-%u failed (%s)",
380 inet_ntoa(in), portlow, porthigh, strerror(err)));
383 DEBUG(D_acl) debug_printf("Malware scan: issuing %s GET\n", scanner_name);
384 (void)string_format(scanrequest, 1024, CS"GET %s", eml_filename);
386 while ((fp_scan_option = string_nextinlist(&av_scanner_work, &sep,
387 fp_scan_option_buffer, sizeof(fp_scan_option_buffer))) != NULL) {
389 Ustrcat(scanrequest, "%20");
391 Ustrcat(scanrequest, "?");
393 Ustrcat(scanrequest, fp_scan_option);
396 Ustrcat(scanrequest, " HTTP/1.0\r\n\r\n");
398 /* send scan request */
399 if (send(sock, &scanrequest, Ustrlen(scanrequest)+1, 0) < 0)
401 return fprotd_errlog_defer(
402 string_sprintf("unable to send command to socket (%s)", scanrequest));
404 /* We get a lot of empty lines, so we need this hack to check for any data at all */
405 while( recv(sock, buf, 1, MSG_PEEK) > 0 ) {
406 if ( recv_line(sock, buf, 32768) > 0) {
407 if ( Ustrstr(buf, US"<detected type=\"") != NULL )
409 else if ( detected && (strhelper = Ustrstr(buf, US"<name>")) ) {
410 if ((strhelper2 = Ustrstr(buf, US"</name>")) != NULL) {
412 malware_name_internal = string_copy(strhelper+6);
414 } else if ( Ustrstr(buf, US"<summary code=\"") )
415 malware_name = Ustrstr(buf, US"<summary code=\"11\">")
416 ? malware_name_internal : NULL;
421 /* "drweb" scanner type ----------------------------------------------- */
422 /* v0.1 - added support for tcp sockets */
423 /* v0.0 - initial release -- support for unix sockets */
424 else if (strcmpic(scanner_name,US"drweb") == 0) {
425 uschar *drweb_options;
426 uschar drweb_options_buffer[1024];
427 uschar drweb_options_default[] = "/usr/local/drweb/run/drwebd.sock";
428 struct sockaddr_un server;
429 int sock, result, ovector[30];
430 unsigned int port, fsize;
431 uschar tmpbuf[1024], *drweb_fbuf;
432 int drweb_rc, drweb_cmd, drweb_flags = 0x0000, drweb_fd,
433 drweb_vnum, drweb_slen, drweb_fin = 0x0000;
435 uschar hostname[256];
440 if ((drweb_options = string_nextinlist(&av_scanner_work, &sep,
441 drweb_options_buffer, sizeof(drweb_options_buffer))) == NULL) {
442 /* no options supplied, use default options */
443 drweb_options = drweb_options_default;
446 if (*drweb_options != '/') {
448 /* extract host and port part */
449 if( sscanf(CS drweb_options, "%s %u", hostname, &port) != 2 )
450 return drweb_errlog_defer(
451 string_sprintf("invalid socket '%s'", drweb_options));
453 /* Lookup the host */
454 if((he = gethostbyname(CS hostname)) == 0)
455 return drweb_errlog_defer(
456 string_sprintf("failed to lookup host '%s'", hostname));
458 in = *(struct in_addr *) he->h_addr_list[0];
460 /* Open the drwebd TCP socket */
461 if ( (sock = ip_socket(SOCK_STREAM, AF_INET)) < 0)
462 return drweb_errlog_defer(
463 string_sprintf("unable to acquire socket (%s)", strerror(errno)));
465 if (ip_connect(sock, AF_INET, (uschar*)inet_ntoa(in), port, 5) < 0) {
468 return drweb_errlog_defer(
469 string_sprintf("connection to %s, port %u failed (%s)",
470 inet_ntoa(in), port, strerror(err)));
473 /* prepare variables */
474 drweb_cmd = htonl(DRWEBD_SCAN_CMD);
475 drweb_flags = htonl(DRWEBD_RETURN_VIRUSES | DRWEBD_IS_MAIL);
478 drweb_fd = open(CS eml_filename, O_RDONLY);
479 if (drweb_fd == -1) {
482 return drweb_errlog_defer(
483 string_sprintf("can't open spool file %s: %s",
484 eml_filename, strerror(err)));
486 fsize = lseek(drweb_fd, 0, SEEK_END);
490 (void)close(drweb_fd);
491 return drweb_errlog_defer(
492 string_sprintf("can't seek spool file %s: %s",
493 eml_filename, strerror(err)));
495 drweb_slen = htonl(fsize);
496 lseek(drweb_fd, 0, SEEK_SET);
498 DEBUG(D_acl) debug_printf("Malware scan: issuing %s remote scan [%s %u]\n",
499 scanner_name, hostname, port);
501 /* send scan request */
502 if ((send(sock, &drweb_cmd, sizeof(drweb_cmd), 0) < 0) ||
503 (send(sock, &drweb_flags, sizeof(drweb_flags), 0) < 0) ||
504 (send(sock, &drweb_fin, sizeof(drweb_fin), 0) < 0) ||
505 (send(sock, &drweb_slen, sizeof(drweb_slen), 0) < 0)) {
507 (void)close(drweb_fd);
508 return drweb_errlog_defer(
509 string_sprintf("unable to send commands to socket (%s)", drweb_options));
512 drweb_fbuf = (uschar *) malloc (fsize);
515 (void)close(drweb_fd);
516 return drweb_errlog_defer(
517 string_sprintf("unable to allocate memory %u for file (%s)",
518 fsize, eml_filename));
521 result = read (drweb_fd, drweb_fbuf, fsize);
525 (void)close(drweb_fd);
527 return drweb_errlog_defer(
528 string_sprintf("can't read spool file %s: %s",
529 eml_filename, strerror(err)));
531 (void)close(drweb_fd);
533 /* send file body to socket */
534 if (send(sock, drweb_fbuf, fsize, 0) < 0) {
537 return drweb_errlog_defer(
538 string_sprintf("unable to send file body to socket (%s)", drweb_options));
540 (void)close(drweb_fd);
543 /* open the drwebd UNIX socket */
544 sock = socket(AF_UNIX, SOCK_STREAM, 0);
546 return drweb_errlog_defer("can't open UNIX socket");
547 server.sun_family = AF_UNIX;
548 Ustrcpy(server.sun_path, drweb_options);
549 if (connect(sock, (struct sockaddr *) &server, sizeof(struct sockaddr_un)) < 0) {
552 return drweb_errlog_defer(
553 string_sprintf("unable to connect to socket (%s). errno=%d", drweb_options, err));
556 /* prepare variables */
557 drweb_cmd = htonl(DRWEBD_SCAN_CMD);
558 drweb_flags = htonl(DRWEBD_RETURN_VIRUSES | DRWEBD_IS_MAIL);
559 drweb_slen = htonl(Ustrlen(eml_filename));
561 DEBUG(D_acl) debug_printf("Malware scan: issuing %s local scan [%s]\n",
562 scanner_name, drweb_options);
564 /* send scan request */
565 if ((send(sock, &drweb_cmd, sizeof(drweb_cmd), 0) < 0) ||
566 (send(sock, &drweb_flags, sizeof(drweb_flags), 0) < 0) ||
567 (send(sock, &drweb_slen, sizeof(drweb_slen), 0) < 0) ||
568 (send(sock, eml_filename, Ustrlen(eml_filename), 0) < 0) ||
569 (send(sock, &drweb_fin, sizeof(drweb_fin), 0) < 0)) {
571 return drweb_errlog_defer(
572 string_sprintf("unable to send commands to socket (%s)", drweb_options));
576 /* wait for result */
577 if ((bread = recv(sock, &drweb_rc, sizeof(drweb_rc), 0) != sizeof(drweb_rc))) {
579 return drweb_errlog_defer("unable to read return code");
581 drweb_rc = ntohl(drweb_rc);
583 if ((bread = recv(sock, &drweb_vnum, sizeof(drweb_vnum), 0) != sizeof(drweb_vnum))) {
585 return drweb_errlog_defer("unable to read the number of viruses");
587 drweb_vnum = ntohl(drweb_vnum);
589 /* "virus(es) found" if virus number is > 0 */
594 /* setup default virus name */
595 malware_name_internal = "unknown";
596 malware_name = malware_name_internal;
598 /* set up match regex */
599 drweb_re = pcre_compile( "infected\\swith\\s*(.+?)$", PCRE_COPT,
600 (const char **)&rerror, &roffset, NULL );
602 /* read and concatenate virus names into one string */
603 for (i=0;i<drweb_vnum;i++)
605 int size = 0, off = 0;
606 /* read the size of report */
607 if ((bread = recv(sock, &drweb_slen, sizeof(drweb_slen), 0) != sizeof(drweb_slen))) {
609 return drweb_errlog_defer("cannot read report size");
611 drweb_slen = ntohl(drweb_slen);
613 /* read report body */
614 if ((bread = recv(sock, tmpbuf, drweb_slen, 0)) != drweb_slen) {
616 return drweb_errlog_defer("cannot read report string");
618 tmpbuf[drweb_slen] = '\0';
620 /* try matcher on the line, grab substring */
621 result = pcre_exec(drweb_re, NULL, CS tmpbuf, Ustrlen(tmpbuf), 0, 0, ovector, 30);
623 const char * pre_malware_nb;
625 pcre_get_substring(CS tmpbuf, ovector, result, 1, &pre_malware_nb);
627 /* the first name we just copy to malware_name */
629 malware_name_internal = string_append(NULL, &size, &off,
632 /* concatenate each new virus name to previous */
633 malware_name_internal = string_append(malware_name_internal,
634 &size, &off, 2, "/", pre_malware_nb);
636 pcre_free_substring(pre_malware_nb);
641 const char *drweb_s = NULL;
643 if (drweb_rc & DERR_READ_ERR) drweb_s = "read error";
644 if (drweb_rc & DERR_NOMEMORY) drweb_s = "no memory";
645 if (drweb_rc & DERR_TIMEOUT) drweb_s = "timeout";
646 if (drweb_rc & DERR_BAD_CALL) drweb_s = "wrong command";
647 /* retcodes DERR_SYMLINK, DERR_NO_REGFILE, DERR_SKIPPED.
648 * DERR_TOO_BIG, DERR_TOO_COMPRESSED, DERR_SPAM,
649 * DERR_CRC_ERROR, DERR_READSOCKET, DERR_WRITE_ERR
650 * and others are ignored */
653 return drweb_errlog_defer(
654 string_sprintf("drweb daemon retcode 0x%x (%s)", drweb_rc, drweb_s));
661 /* ----------------------------------------------------------------------- */
662 else if (strcmpic(scanner_name,US"aveserver") == 0) {
664 uschar kav_options_buffer[1024];
665 uschar kav_options_default[] = "/var/run/aveserver";
667 struct sockaddr_un server;
671 if ((kav_options = string_nextinlist(&av_scanner_work, &sep,
673 sizeof(kav_options_buffer))) == NULL) {
674 /* no options supplied, use default options */
675 kav_options = kav_options_default;
678 /* open the aveserver socket */
679 sock = socket(AF_UNIX, SOCK_STREAM, 0);
681 return aves_errlog_defer("can't open UNIX socket.");
683 server.sun_family = AF_UNIX;
684 Ustrcpy(server.sun_path, kav_options);
685 if (connect(sock, (struct sockaddr *) &server, sizeof(struct sockaddr_un)) < 0) {
688 return aves_errlog_defer(
689 string_sprintf("unable to connect to UNIX socket (%s). errno=%d", kav_options, err));
692 /* read aveserver's greeting and see if it is ready (2xx greeting) */
693 recv_line(sock, buf, 32768);
696 /* aveserver is having problems */
698 return aves_errlog_defer(
699 string_sprintf("unavailable (Responded: %s).", ((buf[0] != 0) ? buf : (uschar *)"nothing") ));
702 /* prepare our command */
703 (void)string_format(buf, 32768, "SCAN bPQRSTUW %s\r\n", eml_filename);
705 DEBUG(D_acl) debug_printf("Malware scan: issuing %s SCAN\n", scanner_name);
708 if (send(sock, buf, Ustrlen(buf), 0) < 0) {
710 return aves_errlog_defer(
711 string_sprintf("unable to write to UNIX socket (%s)", kav_options));
716 /* read response lines, find malware name and final response */
717 while (recv_line(sock, buf, 32768) > 0) {
718 debug_printf("aveserver: %s\n", buf);
721 } else if (buf[0] == '5') {
722 /* aveserver is having problems */
723 log_write(0, LOG_MAIN|LOG_PANIC,
724 "malware acl condition: unable to scan file %s (Responded: %s).",
728 } else if (Ustrncmp(buf,"322",3) == 0) {
729 uschar *p = Ustrchr(&buf[4],' ');
731 malware_name_internal = string_copy(&buf[4]);
732 malware_name = malware_name_internal;
736 /* prepare our command */
737 (void)string_format(buf, 32768, "quit\r\n");
740 if (send(sock, buf, Ustrlen(buf), 0) < 0) {
742 return aves_errlog_defer(
743 string_sprintf("unable to write to UNIX socket (%s)", kav_options));
746 /* read aveserver's greeting and see if it is ready (2xx greeting) */
747 recv_line(sock, buf, 32768);
750 /* aveserver is having problems */
752 return aves_errlog_defer(
753 string_sprintf("unable to quit dialogue (Responded: %s).", ((buf[0] != 0) ? buf : (uschar *)"nothing") ));
758 if (result == DEFER) return DEFER;
760 /* "fsecure" scanner type ------------------------------------------------- */
761 else if (strcmpic(scanner_name,US"fsecure") == 0) {
762 uschar *fsecure_options;
763 uschar fsecure_options_buffer[1024];
764 uschar fsecure_options_default[] = "/var/run/.fsav";
765 struct sockaddr_un server;
766 int sock, i, j, bread = 0;
767 uschar file_name[1024];
768 uschar av_buffer[1024];
770 static uschar *cmdoptions[] = { US"CONFIGURE\tARCHIVE\t1\n",
771 US"CONFIGURE\tTIMEOUT\t0\n",
772 US"CONFIGURE\tMAXARCH\t5\n",
773 US"CONFIGURE\tMIME\t1\n" };
776 if ((fsecure_options = string_nextinlist(&av_scanner_work, &sep,
777 fsecure_options_buffer,
778 sizeof(fsecure_options_buffer))) == NULL) {
779 /* no options supplied, use default options */
780 fsecure_options = fsecure_options_default;
783 /* open the fsecure socket */
784 sock = socket(AF_UNIX, SOCK_STREAM, 0);
786 return fsec_errlog_defer(
787 string_sprintf("unable to open socket %s (%s)",
788 fsecure_options, strerror(errno)));
790 server.sun_family = AF_UNIX;
791 Ustrcpy(server.sun_path, fsecure_options);
792 if (connect(sock, (struct sockaddr *) &server, sizeof(struct sockaddr_un)) < 0) {
795 return fsec_errlog_defer(
796 string_sprintf("unable to connect to socket %s (%s)",
797 fsecure_options, strerror(err)));
800 DEBUG(D_acl) debug_printf("Malware scan: issuing %s scan [%s]\n",
801 scanner_name, fsecure_options);
804 memset(av_buffer, 0, sizeof(av_buffer));
805 for (i=0; i != 4; i++) {
806 /* debug_printf("send option \"%s\"",cmdoptions[i]); */
807 if (write(sock, cmdoptions[i], Ustrlen(cmdoptions[i])) < 0) {
810 return fsec_errlog_defer(
811 string_sprintf("unable to write option %d to %s (%s)",
812 i, fsecure_options, strerror(err)));
815 bread = ip_recv(sock, av_buffer, sizeof(av_buffer), MALWARE_TIMEOUT);
816 if (bread >0) av_buffer[bread]='\0';
820 return fsec_errlog_defer(
821 string_sprintf("unable to read answer %d (%s)", i, strerror(errno)));
823 for (j=0;j<bread;j++) if((av_buffer[j]=='\r')||(av_buffer[j]=='\n')) av_buffer[j] ='@';
824 /* debug_printf("read answer %d read=%d \"%s\"\n", i, bread, av_buffer ); */
825 /* while (Ustrstr(av_buffer, "OK\tServer configured.@") == NULL); */
828 /* pass the mailfile to fsecure */
829 (void)string_format(file_name,1024,"SCAN\t%s\n", eml_filename);
830 /* debug_printf("send scan %s",file_name); */
831 if (write(sock, file_name, Ustrlen(file_name)) < 0) {
834 return fsec_errlog_defer(
835 string_sprintf("unable to write scan to %s (%s)",
836 fsecure_options, strerror(err)));
840 /* todo also SUSPICION\t */
841 fs_inf = pcre_compile("\\S{0,5}INFECTED\\t[^\\t]*\\t([^\\t]+)\\t\\S*$", PCRE_COPT, (const char **)&rerror, &roffset, NULL);
843 /* read report, linewise */
847 memset(av_buffer, 0, sizeof(av_buffer));
849 bread=ip_recv(sock, &av_buffer[i], 1, MALWARE_TIMEOUT);
853 return fsec_errlog_defer(
854 string_sprintf("unable to read result (%s)", strerror(err)));
858 while ((i < sizeof(av_buffer)-1 ) && (av_buffer[i-1] != '\n'));
859 av_buffer[i-1] = '\0';
860 /* debug_printf("got line \"%s\"\n",av_buffer); */
862 /* Really search for virus again? */
863 if (malware_name == NULL) {
864 /* try matcher on the line, grab substring */
865 i = pcre_exec(fs_inf, NULL, CS av_buffer, Ustrlen(av_buffer), 0, 0, ovector, 30);
868 pcre_get_substring(CS av_buffer, ovector, i, 1,
869 (const char **) &malware_name_internal);
870 malware_name = malware_name_internal;
874 while (Ustrstr(av_buffer, "OK\tScan ok.") == NULL);
877 /* ----------------------------------------------------------------------- */
879 /* "kavdaemon" scanner type ------------------------------------------------ */
880 else if (strcmpic(scanner_name,US"kavdaemon") == 0) {
882 uschar kav_options_buffer[1024];
883 uschar kav_options_default[] = "/var/run/AvpCtl";
884 struct sockaddr_un server;
888 uschar scanrequest[1024];
890 unsigned long kav_reportlen, bread;
895 if ((kav_options = string_nextinlist(&av_scanner_work, &sep,
897 sizeof(kav_options_buffer))) == NULL) {
898 /* no options supplied, use default options */
899 kav_options = kav_options_default;
902 /* open the kavdaemon socket */
903 sock = socket(AF_UNIX, SOCK_STREAM, 0);
905 return kavd_errlog_defer("can't open UNIX socket.");
907 server.sun_family = AF_UNIX;
908 Ustrcpy(server.sun_path, kav_options);
909 if (connect(sock, (struct sockaddr *) &server, sizeof(struct sockaddr_un)) < 0) {
912 return kavd_errlog_defer(
913 string_sprintf("unable to connect to UNIX socket (%s). errno=%d", kav_options, err));
916 /* get current date and time, build scan request */
918 /* pdp note: before the eml_filename parameter, this scanned the
919 directory; not finding documentation, so we'll strip off the directory.
920 The side-effect is that the test framework scanning may end up in
921 scanning more than was requested, but for the normal interface, this is
923 strftime(CS tmpbuf, sizeof(tmpbuf), "<0>%d %b %H:%M:%S:%%s", localtime(&t));
924 fits = string_format(scanrequest, 1024,CS tmpbuf, eml_filename);
927 log_write(0, LOG_MAIN|LOG_PANIC,
928 "malware filename does not fit in buffer [malware_internal() kavdaemon]");
930 p = Ustrrchr(scanrequest, '/');
934 DEBUG(D_acl) debug_printf("Malware scan: issuing %s scan [%s]\n",
935 scanner_name, kav_options);
937 /* send scan request */
938 if (send(sock, scanrequest, Ustrlen(scanrequest)+1, 0) < 0) {
940 return kavd_errlog_defer(
941 string_sprintf("unable to write to UNIX socket (%s)", kav_options));
944 /* wait for result */
945 if ((bread = recv(sock, tmpbuf, 2, 0) != 2)) {
947 return kavd_errlog_defer("unable to read 2 bytes from socket.");
950 /* get errorcode from one nibble */
951 kav_rc = tmpbuf[ test_byte_order()==LITTLE_MY_ENDIAN ? 0 : 1 ] & 0x0F;
953 /* improper kavdaemon configuration */
954 if ( (kav_rc == 5) || (kav_rc == 6) ) {
956 return kavd_errlog_defer("please reconfigure kavdaemon to NOT disinfect or remove infected files.");
961 return kavd_errlog_defer("reported 'scanning not completed' (code 1).");
966 return kavd_errlog_defer("reported 'kavdaemon damaged' (code 7).");
969 /* code 8 is not handled, since it is ambigous. It appears mostly on
970 bounces where part of a file has been cut off */
972 /* "virus found" return codes (2-4) */
973 if ((kav_rc > 1) && (kav_rc < 5)) {
976 /* setup default virus name */
977 malware_name_internal = string_copy("unknown");
978 malware_name = malware_name_internal;
980 report_flag = tmpbuf[ test_byte_order() == LITTLE_MY_ENDIAN ? 1 : 0 ];
982 /* read the report, if available */
983 if( report_flag == 1 ) {
984 /* read report size */
985 if ((bread = recv(sock, &kav_reportlen, 4, 0)) != 4) {
987 return kavd_errlog_defer("cannot read report size");
990 /* it's possible that avp returns av_buffer[1] == 1 but the
991 reportsize is 0 (!?) */
992 if (kav_reportlen > 0) {
993 /* set up match regex, depends on retcode */
994 kav_re = pcre_compile( kav_rc == 3
995 ? "suspicion:\\s*(.+?)\\s*$"
996 : "infected:\\s*(.+?)\\s*$",
998 (const char **)&rerror,
1002 /* read report, linewise */
1003 while (kav_reportlen > 0) {
1008 while ( recv(sock, &tmpbuf[bread], 1, 0) == 1 ) {
1010 if ( (tmpbuf[bread] == '\n') || (bread > 1021) ) break;
1014 tmpbuf[bread] = '\0';
1016 /* try matcher on the line, grab substring */
1017 result = pcre_exec(kav_re, NULL, CS tmpbuf, Ustrlen(tmpbuf), 0, 0, ovector, 30);
1019 pcre_get_substring(CS tmpbuf, ovector, result, 1,
1020 (const char **) &malware_name_internal);
1028 /* no virus found */
1029 malware_name = NULL;
1034 /* ----------------------------------------------------------------------- */
1037 /* "cmdline" scanner type ------------------------------------------------ */
1038 else if (strcmpic(scanner_name,US"cmdline") == 0) {
1039 uschar *cmdline_scanner;
1040 uschar cmdline_scanner_buffer[1024];
1041 uschar *cmdline_trigger;
1042 uschar cmdline_trigger_buffer[1024];
1043 const pcre *cmdline_trigger_re;
1044 uschar *cmdline_regex;
1045 uschar cmdline_regex_buffer[1024];
1046 const pcre *cmdline_regex_re;
1047 uschar file_name[1024];
1048 uschar commandline[1024];
1049 void (*eximsigchld)(int);
1050 void (*eximsigpipe)(int);
1051 FILE *scanner_out = NULL;
1052 FILE *scanner_record = NULL;
1053 uschar linebuffer[32767];
1060 /* find scanner command line */
1061 if ((cmdline_scanner = string_nextinlist(&av_scanner_work, &sep,
1062 cmdline_scanner_buffer,
1063 sizeof(cmdline_scanner_buffer))) == NULL)
1064 return cmdl_errlog_defer("missing commandline specification");
1066 /* find scanner output trigger */
1067 if ((cmdline_trigger = string_nextinlist(&av_scanner_work, &sep,
1068 cmdline_trigger_buffer,
1069 sizeof(cmdline_trigger_buffer))) == NULL)
1070 return cmdl_errlog_defer("missing trigger specification");
1072 /* precompile trigger regex */
1073 cmdline_trigger_re = pcre_compile(CS cmdline_trigger, PCRE_COPT, (const char **)&rerror, &roffset, NULL);
1074 if (cmdline_trigger_re == NULL)
1075 return cmdl_errlog_defer(
1076 string_sprintf("regular expression error in '%s': %s at offset %d",
1077 cmdline_trigger, rerror, roffset));
1079 /* find scanner name regex */
1080 if ((cmdline_regex = string_nextinlist(&av_scanner_work, &sep,
1081 cmdline_regex_buffer,
1082 sizeof(cmdline_regex_buffer))) == NULL)
1083 return cmdl_errlog_defer("missing virus name regex specification");
1085 /* precompile name regex */
1086 cmdline_regex_re = pcre_compile(CS cmdline_regex, PCRE_COPT, (const char **)&rerror, &roffset, NULL);
1087 if (cmdline_regex_re == NULL)
1088 return cmdl_errlog_defer(
1089 string_sprintf("regular expression error in '%s': %s at offset %d",
1090 cmdline_regex, rerror, roffset));
1092 /* prepare scanner call; despite the naming, file_name holds a directory
1093 name which is documented as the value given to %s. */
1094 if (Ustrlen(eml_filename) > sizeof(file_name) - 1)
1095 return cmdl_errlog_defer("filename does not fit in buffer");
1097 Ustrcpy(file_name, eml_filename);
1098 p = Ustrrchr(file_name, '/');
1101 fits = string_format(commandline, sizeof(commandline), CS cmdline_scanner, file_name);
1103 return cmdl_errlog_defer("command-line does not fit in buffer");
1105 /* redirect STDERR too */
1106 if (Ustrlen(commandline) + 5 > sizeof(commandline))
1107 return cmdl_errlog_defer("command-line does not fit in buffer (STDERR redirect)");
1108 Ustrcat(commandline," 2>&1");
1110 DEBUG(D_acl) debug_printf("Malware scan: issuing %s scan [%s]\n", scanner_name, commandline);
1112 /* store exims signal handlers */
1113 eximsigchld = signal(SIGCHLD,SIG_DFL);
1114 eximsigpipe = signal(SIGPIPE,SIG_DFL);
1116 scanner_out = popen(CS commandline,"r");
1117 if (scanner_out == NULL) {
1119 signal(SIGCHLD,eximsigchld);
1120 signal(SIGPIPE,eximsigpipe);
1121 return cmdl_errlog_defer(
1122 string_sprintf("call (%s) failed: %s.", commandline, strerror(err)));
1125 (void)string_format(file_name,1024,"%s/scan/%s/%s_scanner_output", spool_directory, message_id, message_id);
1126 scanner_record = modefopen(file_name,"wb",SPOOL_MODE);
1128 if (scanner_record == NULL) {
1130 pclose(scanner_out);
1131 signal(SIGCHLD,eximsigchld);
1132 signal(SIGPIPE,eximsigpipe);
1133 return cmdl_errlog_defer(
1134 string_sprintf("opening scanner output file (%s) failed: %s.",
1135 file_name, strerror(err)));
1138 /* look for trigger while recording output */
1139 while(fgets(CS linebuffer,32767,scanner_out) != NULL) {
1140 if ( Ustrlen(linebuffer) > fwrite(linebuffer, 1, Ustrlen(linebuffer), scanner_record) ) {
1142 pclose(scanner_out);
1143 signal(SIGCHLD,eximsigchld);
1144 signal(SIGPIPE,eximsigpipe);
1145 return cmdl_errlog_defer(
1146 string_sprintf("short write on scanner output file (%s).", file_name));
1148 /* try trigger match */
1149 if (!trigger && regex_match_and_setup(cmdline_trigger_re, linebuffer, 0, -1))
1153 (void)fclose(scanner_record);
1154 pclose(scanner_out);
1155 signal(SIGCHLD,eximsigchld);
1156 signal(SIGPIPE,eximsigpipe);
1159 /* setup default virus name */
1160 malware_name = US"unknown";
1162 /* re-open the scanner output file, look for name match */
1163 scanner_record = fopen(CS file_name,"rb");
1164 while(fgets(CS linebuffer,32767,scanner_record) != NULL) {
1166 result = pcre_exec(cmdline_regex_re, NULL, CS linebuffer, Ustrlen(linebuffer), 0, 0, ovector, 30);
1168 pcre_get_substring(CS linebuffer, ovector, result, 1,
1169 (const char **) &malware_name_internal);
1171 (void)fclose(scanner_record);
1173 else /* no virus found */
1174 malware_name = NULL;
1176 /* ----------------------------------------------------------------------- */
1179 /* "sophie" scanner type ------------------------------------------------- */
1180 else if (strcmpic(scanner_name,US"sophie") == 0) {
1181 uschar *sophie_options;
1182 uschar sophie_options_buffer[1024];
1183 uschar sophie_options_default[] = "/var/run/sophie";
1185 struct sockaddr_un server;
1188 uschar file_name[1024];
1189 uschar av_buffer[1024];
1191 if ((sophie_options = string_nextinlist(&av_scanner_work, &sep,
1192 sophie_options_buffer,
1193 sizeof(sophie_options_buffer))) == NULL) {
1194 /* no options supplied, use default options */
1195 sophie_options = sophie_options_default;
1198 /* open the sophie socket */
1199 sock = socket(AF_UNIX, SOCK_STREAM, 0);
1201 return soph_errlog_defer("can't open UNIX socket.");
1203 server.sun_family = AF_UNIX;
1204 Ustrcpy(server.sun_path, sophie_options);
1205 if (connect(sock, (struct sockaddr *) &server, sizeof(struct sockaddr_un)) < 0) {
1208 return soph_errlog_defer(
1209 string_sprintf("unable to connect to UNIX socket (%s). errno=%d",
1210 sophie_options, err));
1213 /* pass the scan directory to sophie */
1214 len = Ustrlen(eml_filename) + 1;
1215 if (len > sizeof(file_name))
1218 return soph_errlog_defer("malware filename does not fit in buffer");
1220 memcpy(file_name, eml_filename, len);
1221 p = Ustrrchr(file_name, '/');
1225 DEBUG(D_acl) debug_printf("Malware scan: issuing %s scan [%s]\n",
1226 scanner_name, sophie_options);
1228 if ( write(sock, file_name, Ustrlen(file_name)) < 0
1229 || write(sock, "\n", 1) != 1
1232 return soph_errlog_defer(
1233 string_sprintf("unable to write to UNIX socket (%s)", sophie_options));
1236 /* wait for result */
1237 memset(av_buffer, 0, sizeof(av_buffer));
1238 if ((!(bread = ip_recv(sock, av_buffer, sizeof(av_buffer), MALWARE_TIMEOUT)) > 0)) {
1240 return soph_errlog_defer(
1241 string_sprintf("unable to read from UNIX socket (%s)", sophie_options));
1247 if (av_buffer[0] == '1') {
1248 if (Ustrchr(av_buffer, '\n')) *Ustrchr(av_buffer, '\n') = '\0';
1249 malware_name_internal = string_copy(&av_buffer[2]);
1250 malware_name = malware_name_internal;
1252 else if (!strncmp(CS av_buffer, "-1", 2))
1253 return soph_errlog_defer("scanner reported error");
1254 else /* all ok, no virus */
1255 malware_name = NULL;
1257 /* ----------------------------------------------------------------------- */
1260 /* "clamd" scanner type ------------------------------------------------- */
1261 /* This code was originally contributed by David Saez */
1262 /* There are three scanning methods available to us:
1263 * (1) Use the SCAN command, pointing to a file in the filesystem
1264 * (2) Use the STREAM command, send the data on a separate port
1265 * (3) Use the zINSTREAM command, send the data inline
1266 * The zINSTREAM command was introduced with ClamAV 0.95, which marked
1267 * STREAM deprecated; see: http://wiki.clamav.net/bin/view/Main/UpgradeNotes095
1268 * In Exim, we use SCAN if using a Unix-domain socket or explicitly told that
1269 * the TCP-connected daemon is actually local; otherwise we use zINSTREAM unless
1270 * WITH_OLD_CLAMAV_STREAM is defined.
1271 * See Exim bug 926 for details. */
1272 else if (strcmpic(scanner_name,US"clamd") == 0) {
1273 uschar *clamd_options = NULL;
1274 uschar clamd_options_buffer[1024];
1275 uschar clamd_options_default[] = "/tmp/clamd";
1276 uschar *p, *vname, *result_tag, *response_end;
1277 struct sockaddr_un server;
1280 uschar file_name[1024];
1281 uschar av_buffer[1024];
1282 uschar *hostname = "";
1285 uschar *clamav_fbuf;
1286 int clam_fd, result;
1288 BOOL use_scan_command = FALSE, fits;
1289 clamd_address_container * clamd_address_vector[MAX_CLAMD_SERVERS];
1291 int num_servers = 0;
1292 #ifdef WITH_OLD_CLAMAV_STREAM
1293 uschar av_buffer2[1024];
1296 uint32_t send_size, send_final_zeroblock;
1299 if ((clamd_options = string_nextinlist(&av_scanner_work, &sep,
1300 clamd_options_buffer,
1301 sizeof(clamd_options_buffer))) == NULL) {
1302 /* no options supplied, use default options */
1303 clamd_options = clamd_options_default;
1306 if (*clamd_options == '/')
1307 /* Local file; so we def want to use_scan_command and don't want to try
1308 * passing IP/port combinations */
1309 use_scan_command = TRUE;
1311 uschar *address = clamd_options;
1312 uschar address_buffer[MAX_CLAMD_ADDRESS_LENGTH + 20];
1314 /* Go through the rest of the list of host/port and construct an array
1315 * of servers to try. The first one is the bit we just passed from
1316 * clamd_options so process that first and then scan the remainder of
1317 * the address buffer */
1319 clamd_address_container *this_clamd;
1321 /* The 'local' option means use the SCAN command over the network
1322 * socket (ie common file storage in use) */
1323 if (strcmpic(address,US"local") == 0) {
1324 use_scan_command = TRUE;
1328 /* XXX: If unsuccessful we should free this memory */
1330 (clamd_address_container *)store_get(sizeof(clamd_address_container));
1332 /* extract host and port part */
1333 if( sscanf(CS address, "%" MAX_CLAMD_ADDRESS_LENGTH_S "s %u", this_clamd->tcp_addr,
1334 &(this_clamd->tcp_port)) != 2 ) {
1335 clmd_errlog(string_sprintf("invalid address '%s'", address));
1339 clamd_address_vector[num_servers] = this_clamd;
1341 if (num_servers >= MAX_CLAMD_SERVERS) {
1342 clmd_errlog("More than " MAX_CLAMD_SERVERS_S " clamd servers "
1343 "specified; only using the first " MAX_CLAMD_SERVERS_S );
1346 } while ((address = string_nextinlist(&av_scanner_work, &sep,
1348 sizeof(address_buffer))) != NULL);
1350 /* check if we have at least one server */
1352 return clmd_errlog_defer("no useable server addresses in malware configuration option.");
1355 /* See the discussion of response formats below to see why we really don't
1356 like colons in filenames when passing filenames to ClamAV. */
1357 if (use_scan_command && Ustrchr(eml_filename, ':'))
1358 return clmd_errlog_defer(
1359 string_sprintf("local/SCAN mode incompatible with" \
1360 " : in path to email filename [%s]", eml_filename));
1362 /* We have some network servers specified */
1365 /* Confirmed in ClamAV source (0.95.3) that the TCPAddr option of clamd
1366 * only supports AF_INET, but we should probably be looking to the
1367 * future and rewriting this to be protocol-independent anyway. */
1369 while ( num_servers > 0 ) {
1370 /* Randomly pick a server to start with */
1371 current_server = random_number( num_servers );
1373 debug_printf("trying server name %s, port %u\n",
1374 clamd_address_vector[current_server]->tcp_addr,
1375 clamd_address_vector[current_server]->tcp_port);
1377 /* Lookup the host. This is to ensure that we connect to the same IP
1378 * on both connections (as one host could resolve to multiple ips) */
1379 if((he = gethostbyname(CS clamd_address_vector[current_server]->tcp_addr))
1381 clmd_errlog(string_sprintf("failed to lookup host '%s'",
1382 clamd_address_vector[current_server]->tcp_addr));
1383 goto try_next_server;
1386 in = *(struct in_addr *) he->h_addr_list[0];
1388 /* Open the ClamAV Socket */
1389 if ( (sock = ip_socket(SOCK_STREAM, AF_INET)) < 0) {
1390 clmd_errlog(string_sprintf("unable to acquire socket (%s)",
1392 goto try_next_server;
1395 if (ip_connect( sock,
1397 (uschar*)inet_ntoa(in),
1398 clamd_address_vector[current_server]->tcp_port,
1400 /* Connection successfully established with a server */
1401 hostname = clamd_address_vector[current_server]->tcp_addr;
1404 clmd_errlog(string_sprintf(
1405 "malware acl condition: clamd: connection to %s, port %u failed (%s)",
1406 clamd_address_vector[current_server]->tcp_addr,
1407 clamd_address_vector[current_server]->tcp_port,
1414 /* Remove the server from the list. XXX We should free the memory */
1417 for( i = current_server; i < num_servers; i++ )
1418 clamd_address_vector[i] = clamd_address_vector[i+1];
1421 if ( num_servers == 0 )
1422 return clmd_errlog_defer("all servers failed");
1425 /* open the local socket */
1426 if ((sock = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
1427 return clmd_errlog_defer(
1428 string_sprintf("unable to acquire socket (%s)", strerror(errno)));
1430 server.sun_family = AF_UNIX;
1431 Ustrcpy(server.sun_path, clamd_options);
1433 if (connect(sock, (struct sockaddr *) &server, sizeof(struct sockaddr_un)) < 0) {
1436 return clmd_errlog_defer(
1437 string_sprintf("unable to connect to UNIX socket %s (%s)",
1438 clamd_options, strerror(err) ));
1442 /* have socket in variable "sock"; command to use is semi-independent of
1443 * the socket protocol. We use SCAN if is local (either Unix/local
1444 * domain socket, or explicitly told local) else we stream the data.
1445 * How we stream the data depends upon how we were built. */
1447 if (!use_scan_command) {
1449 #ifdef WITH_OLD_CLAMAV_STREAM
1450 /* "STREAM\n" command, get back a "PORT <N>\n" response, send data to
1451 * that port on a second connection; then in the scan-method-neutral
1452 * part, read the response back on the original connection. */
1454 DEBUG(D_acl) debug_printf("Malware scan: issuing %s old-style remote scan (PORT)\n",
1457 /* Pass the string to ClamAV (7 = "STREAM\n") */
1458 if (send(sock, "STREAM\n", 7, 0) < 0) {
1461 return clmd_errlog_defer(
1462 string_sprintf("unable to write to socket (%s)", strerror(err)));
1464 memset(av_buffer2, 0, sizeof(av_buffer2));
1465 bread = ip_recv(sock, av_buffer2, sizeof(av_buffer2), MALWARE_TIMEOUT);
1470 return clmd_errlog_defer(
1471 string_sprintf("unable to read PORT from socket (%s)",
1475 if (bread == sizeof(av_buffer)) {
1477 return clmd_errlog_defer("buffer too small");
1480 if (!(*av_buffer2)) {
1482 return clmd_errlog_defer("ClamAV returned null");
1485 av_buffer2[bread] = '\0';
1486 if( sscanf(CS av_buffer2, "PORT %u\n", &port) != 1 ) {
1488 return clmd_errlog_defer(
1489 string_sprintf("Expected port information from clamd, got '%s'",
1493 if ( (sockData = ip_socket(SOCK_STREAM, AF_INET)) < 0) {
1496 return clmd_errlog_defer(
1497 string_sprintf("unable to acquire socket (%s)", strerror(err)));
1500 if (ip_connect(sockData, AF_INET, (uschar*)inet_ntoa(in), port, 5) < 0) {
1502 (void)close(sockData); (void)close(sock);
1503 return clmd_errlog_defer(
1504 string_sprintf("connection to %s, port %u failed (%s)",
1505 inet_ntoa(in), port, strerror(err)));
1508 #define CLOSE_SOCKDATA (void)close(sockData)
1509 #else /* WITH_OLD_CLAMAV_STREAM not defined */
1510 /* New protocol: "zINSTREAM\n" followed by a sequence of <length><data>
1511 chunks, <n> a 4-byte number (network order), terminated by a zero-length
1514 DEBUG(D_acl) debug_printf("Malware scan: issuing %s new-style remote scan (zINSTREAM)\n",
1517 /* Pass the string to ClamAV (10 = "zINSTREAM\0") */
1518 if (send(sock, "zINSTREAM", 10, 0) < 0) {
1521 return clmd_errlog_defer(
1522 string_sprintf("unable to send zINSTREAM to socket (%s)",
1526 #define CLOSE_SOCKDATA /**/
1529 /* calc file size */
1530 clam_fd = open(CS eml_filename, O_RDONLY);
1531 if (clam_fd == -1) {
1533 CLOSE_SOCKDATA; (void)close(sock);
1534 return clmd_errlog_defer(
1535 string_sprintf("can't open spool file %s: %s",
1536 eml_filename, strerror(err)));
1538 fsize = lseek(clam_fd, 0, SEEK_END);
1541 CLOSE_SOCKDATA; (void)close(sock); (void)close(clam_fd);
1542 return clmd_errlog_defer(
1543 string_sprintf("can't seek spool file %s: %s",
1544 eml_filename, strerror(errno)));
1546 lseek(clam_fd, 0, SEEK_SET);
1548 clamav_fbuf = (uschar *) malloc (fsize);
1550 CLOSE_SOCKDATA; (void)close(sock); (void)close(clam_fd);
1551 return clmd_errlog_defer(
1552 string_sprintf("unable to allocate memory %u for file (%s)",
1553 fsize, eml_filename));
1556 result = read (clam_fd, clamav_fbuf, fsize);
1559 CLOSE_SOCKDATA; (void)close(sock); (void)close(clam_fd);
1561 return clmd_errlog_defer(
1562 string_sprintf("can't read spool file %s: %s",
1563 eml_filename, strerror(err)));
1565 (void)close(clam_fd);
1567 /* send file body to socket */
1568 #ifdef WITH_OLD_CLAMAV_STREAM
1569 if (send(sockData, clamav_fbuf, fsize, 0) < 0) {
1570 CLOSE_SOCKDATA; (void)close(sock);
1572 return clmd_errlog_defer(
1573 string_sprintf("unable to send file body to socket (%s:%u)",
1577 send_size = htonl(fsize);
1578 send_final_zeroblock = 0;
1579 if ((send(sock, &send_size, sizeof(send_size), 0) < 0) ||
1580 (send(sock, clamav_fbuf, fsize, 0) < 0) ||
1581 (send(sock, &send_final_zeroblock, sizeof(send_final_zeroblock), 0) < 0))
1585 return clmd_errlog_defer(
1586 string_sprintf("unable to send file body to socket (%s:%u)",
1594 #undef CLOSE_SOCKDATA
1596 } else { /* use scan command */
1597 /* Send a SCAN command pointing to a filename; then in the then in the
1598 * scan-method-neutral part, read the response back */
1600 /* ================================================================= */
1602 /* Prior to the reworking post-Exim-4.72, this scanned a directory,
1603 which dates to when ClamAV needed us to break apart the email into the
1604 MIME parts (eg, with the now deprecated demime condition coming first).
1605 Some time back, ClamAV gained the ability to deconstruct the emails, so
1606 doing this would actually have resulted in the mail attachments being
1607 scanned twice, in the broken out files and from the original .eml.
1608 Since ClamAV now handles emails (and has for quite some time) we can
1609 just use the email file itself. */
1610 /* Pass the string to ClamAV (7 = "SCAN \n" + \0) */
1611 fits = string_format(file_name, sizeof(file_name), "SCAN %s\n",
1615 clmd_errlog("filename does not fit in buffer");
1618 DEBUG(D_acl) debug_printf("Malware scan: issuing %s local-path scan [%s]\n",
1619 scanner_name, clamd_options);
1621 if (send(sock, file_name, Ustrlen(file_name), 0) < 0) {
1624 return clmd_errlog_defer(
1625 string_sprintf("unable to write to socket (%s)", strerror(err)));
1628 /* Do not shut down the socket for writing; a user report noted that
1629 * clamd 0.70 does not react well to this. */
1631 /* Commands have been sent, no matter which scan method or connection
1632 * type we're using; now just read the result, independent of method. */
1634 /* Read the result */
1635 memset(av_buffer, 0, sizeof(av_buffer));
1636 bread = ip_recv(sock, av_buffer, sizeof(av_buffer), MALWARE_TIMEOUT);
1640 return clmd_errlog_defer(
1641 string_sprintf("unable to read from socket (%s)", strerror(errno)));
1643 if (bread == sizeof(av_buffer))
1644 return clmd_errlog_defer("buffer too small");
1645 /* We're now assured of a NULL at the end of av_buffer */
1647 /* Check the result. ClamAV returns one of two result formats.
1648 In the basic mode, the response is of the form:
1649 infected: -> "<filename>: <virusname> FOUND"
1650 not-infected: -> "<filename>: OK"
1651 error: -> "<filename>: <errcode> ERROR
1652 If the ExtendedDetectionInfo option has been turned on, then we get:
1653 "<filename>: <virusname>(<virushash>:<virussize>) FOUND"
1654 for the infected case. Compare:
1655 /tmp/eicar.com: Eicar-Test-Signature FOUND
1656 /tmp/eicar.com: Eicar-Test-Signature(44d88612fea8a8f36de82e1278abb02f:68) FOUND
1658 In the streaming case, clamd uses the filename "stream" which you should
1659 be able to verify with { ktrace clamdscan --stream /tmp/eicar.com }. (The
1660 client app will replace "stream" with the original filename before returning
1661 results to stdout, but the trace shows the data).
1663 We will assume that the pathname passed to clamd from Exim does not contain
1664 a colon. We will have whined loudly above if the eml_filename does (and we're
1665 passing a filename to clamd). */
1668 return clmd_errlog_defer("ClamAV returned null");
1670 /* strip newline at the end (won't be present for zINSTREAM)
1671 (also any trailing whitespace, which shouldn't exist, but we depend upon
1672 this below, so double-check) */
1673 p = av_buffer + Ustrlen(av_buffer) - 1;
1674 if (*p == '\n') *p = '\0';
1676 DEBUG(D_acl) debug_printf("Malware response: %s\n", av_buffer);
1678 while (isspace(*--p) && (p > av_buffer))
1683 /* colon in returned output? */
1684 if((p = Ustrchr(av_buffer,':')) == NULL)
1685 return clmd_errlog_defer(
1686 string_sprintf("ClamAV returned malformed result (missing colon): %s",
1689 /* strip filename */
1690 while (*p && isspace(*++p)) /**/;
1693 /* It would be bad to encounter a virus with "FOUND" in part of the name,
1694 but we should at least be resistant to it. */
1695 p = Ustrrchr(vname, ' ');
1696 result_tag = p ? p+1 : vname;
1698 if (Ustrcmp(result_tag, "FOUND") == 0) {
1699 /* p should still be the whitespace before the result_tag */
1700 while (isspace(*p)) --p;
1702 /* Strip off the extended information too, which will be in parens
1703 after the virus name, with no intervening whitespace. */
1705 /* "(hash:size)", so previous '(' will do; if not found, we have
1706 a curious virus name, but not an error. */
1707 p = Ustrrchr(vname, '(');
1711 malware_name_internal = string_copy(vname);
1712 malware_name = malware_name_internal;
1713 DEBUG(D_acl) debug_printf("Malware found, name \"%s\"\n", malware_name);
1715 } else if (Ustrcmp(result_tag, "ERROR") == 0)
1716 return clmd_errlog_defer(
1717 string_sprintf("ClamAV returned: %s", av_buffer));
1719 else if (Ustrcmp(result_tag, "OK") == 0) {
1720 /* Everything should be OK */
1721 malware_name = NULL;
1722 DEBUG(D_acl) debug_printf("Malware not found\n");
1725 return clmd_errlog_defer(
1726 string_sprintf("unparseable response from ClamAV: {%s}", av_buffer));
1730 /* ----------------------------------------------------------------------- */
1733 /* "mksd" scanner type --------------------------------------------------- */
1734 else if (strcmpic(scanner_name,US"mksd") == 0) {
1735 uschar *mksd_options;
1736 char *mksd_options_end;
1737 uschar mksd_options_buffer[32];
1738 int mksd_maxproc = 1; /* default, if no option supplied */
1739 struct sockaddr_un server;
1743 if ((mksd_options = string_nextinlist(&av_scanner_work, &sep,
1744 mksd_options_buffer,
1745 sizeof(mksd_options_buffer))) != NULL) {
1746 mksd_maxproc = (int) strtol(CS mksd_options, &mksd_options_end, 10);
1747 if ((*mksd_options == '\0') || (*mksd_options_end != '\0') ||
1748 (mksd_maxproc < 1) || (mksd_maxproc > 32))
1749 return mksd_errlog_defer(
1750 string_sprintf("invalid option '%s'", mksd_options));
1753 /* open the mksd socket */
1754 sock = socket(AF_UNIX, SOCK_STREAM, 0);
1756 return mksd_errlog_defer("can't open UNIX socket.");
1758 server.sun_family = AF_UNIX;
1759 Ustrcpy(server.sun_path, "/var/run/mksd/socket");
1760 if (connect(sock, (struct sockaddr *) &server, sizeof(struct sockaddr_un)) < 0) {
1763 return mksd_errlog_defer(
1764 string_sprintf("unable to connect to mksd UNIX socket (/var/run/mksd/socket). errno=%d", err));
1767 malware_name = NULL;
1769 DEBUG(D_acl) debug_printf("Malware scan: issuing %s scan\n", scanner_name);
1771 retval = mksd_scan_packed(sock, eml_filename);
1776 /* ----------------------------------------------------------------------- */
1778 /* "unknown" scanner type ------------------------------------------------- */
1780 return malware_errlog_defer(string_sprintf("unknown scanner type '%s'",
1782 /* ----------------------------------------------------------------------- */
1784 /* set "been here, done that" marker */
1788 /* match virus name against pattern (caseless ------->----------v) */
1789 if ( (malware_name != NULL) &&
1790 (regex_match_and_setup(re, malware_name, 0, -1)) ) {
1791 DEBUG(D_acl) debug_printf("Matched regex to malware [%s] [%s]\n", malware_regex, malware_name);
1800 /* simple wrapper for reading lines from sockets */
1802 recv_line(int sock, uschar *buffer, int size)
1806 memset(buffer,0,size);
1808 while(recv(sock,p,1,0) > -1) {
1809 if ((p-buffer) > (size-2)) break;
1810 if (*p == '\n') break;
1811 if (*p != '\r') p++;
1819 /* ============= private routines for the "mksd" scanner type ============== */
1821 #include <sys/uio.h>
1824 mksd_writev (int sock, struct iovec *iov, int iovcnt)
1830 i = writev (sock, iov, iovcnt);
1831 while ((i < 0) && (errno == EINTR));
1834 malware_errlog("unable to write to mksd UNIX socket (/var/run/mksd/socket)");
1839 if (i >= iov->iov_len) {
1846 iov->iov_base = CS iov->iov_base + i;
1853 mksd_read_lines (int sock, uschar *av_buffer, int av_buffer_size)
1859 if ((i = recv (sock, av_buffer+offset, av_buffer_size-offset, 0)) <= 0) {
1861 malware_errlog("unable to read from mksd UNIX socket (/var/run/mksd/socket)");
1866 /* offset == av_buffer_size -> buffer full */
1867 if (offset == av_buffer_size) {
1869 malware_errlog("malformed reply received from mksd");
1872 } while (av_buffer[offset-1] != '\n');
1874 av_buffer[offset] = '\0';
1879 mksd_parse_line (char *line)
1889 if ((p = strchr (line, '\n')) != NULL)
1891 return mksd_errlog_defer(string_sprintf("scanner failed: %s", line));
1894 if ((p = strchr (line, '\n')) != NULL) {
1896 if (((p-line) > 5) && (line[3] == ' '))
1897 if (((p = strchr (line+4, ' ')) != NULL) && ((p-line) > 4)) {
1899 malware_name_internal = string_copy(line+4);
1900 malware_name = malware_name_internal;
1904 return mksd_errlog_defer(
1905 string_sprintf("malformed reply received from mksd: %s", line));
1910 mksd_scan_packed(int sock, uschar *scan_filename)
1912 struct iovec iov[3];
1913 const char *cmd = "MSQ\n";
1914 uschar av_buffer[1024];
1916 iov[0].iov_base = (void *) cmd;
1918 iov[1].iov_base = CS scan_filename;
1919 iov[1].iov_len = Ustrlen(scan_filename);
1920 iov[2].iov_base = (void *) (cmd + 3);
1923 if (mksd_writev (sock, iov, 3) < 0)
1926 if (mksd_read_lines (sock, av_buffer, sizeof (av_buffer)) < 0)
1931 return mksd_parse_line (CS av_buffer);
1934 #endif /*WITH_CONTENT_SCAN*/