1 /*************************************************
2 * Exim - an Internet mail transport agent *
3 *************************************************/
5 /* Copyright (c) Tom Kistner <tom@duncanthrax.net> 2003-???? */
8 /* Code for calling virus (malware) scanners. Called from acl.c. */
11 #ifdef WITH_CONTENT_SCAN
13 /* declaration of private routines */
14 static int mksd_scan_packed(int sock, uschar *scan_filename);
15 static int malware_internal(uschar **listptr, uschar *eml_filename, BOOL faking);
17 /* SHUT_WR seems to be undefined on Unixware? */
23 #define MALWARE_TIMEOUT 120
26 #define DRWEBD_SCAN_CMD (1) /* scan file, buffer or diskfile */
27 #define DRWEBD_RETURN_VIRUSES (1<<0) /* ask daemon return to us viruses names from report */
28 #define DRWEBD_IS_MAIL (1<<19) /* say to daemon that format is "archive MAIL" */
30 #define DERR_READ_ERR (1<<0) /* read error */
31 #define DERR_NOMEMORY (1<<2) /* no memory */
32 #define DERR_TIMEOUT (1<<9) /* scan timeout has run out */
33 #define DERR_BAD_CALL (1<<15) /* wrong command */
35 /* Routine to check whether a system is big- or litte-endian.
36 Ripped from http://www.faqs.org/faqs/graphics/fileformats-faq/part4/section-7.html
37 Needed for proper kavdaemon implementation. Sigh. */
38 #define BIG_MY_ENDIAN 0
39 #define LITTLE_MY_ENDIAN 1
40 int test_byte_order(void);
41 int test_byte_order() {
42 short int word = 0x0001;
43 char *byte = (char *) &word;
44 return(byte[0] ? LITTLE_MY_ENDIAN : BIG_MY_ENDIAN);
47 uschar malware_name_buffer[256];
50 /* Gross hacks for the -bmalware option; perhaps we should just create
51 the scan directory normally for that case, but look into rigging up the
52 needed header variables if not already set on the command-line? */
53 extern int spool_mbox_ok;
54 extern uschar spooled_message_id[17];
56 /*************************************************
57 * Scan an email for malware *
58 *************************************************/
60 /* This is the normal interface for scanning an email, which doesn't need a
61 filename; it's a wrapper around the malware_file function.
64 listptr the list of options to the "malware = ..." ACL condition
66 Returns: Exim message processing code (OK, FAIL, DEFER, ...)
67 where true means malware was found (condition applies)
69 int malware(uschar **listptr) {
70 uschar scan_filename[1024];
74 fits = string_format(scan_filename, sizeof(scan_filename),
75 CS"%s/scan/%s/%s.eml", spool_directory, message_id, message_id);
79 log_write(0, LOG_MAIN|LOG_PANIC,
80 "malware filename does not fit in buffer [malware()]");
84 ret = malware_internal(listptr, scan_filename, FALSE);
85 if (ret == DEFER) av_failed = TRUE;
91 /*************************************************
92 * Scan a file for malware *
93 *************************************************/
95 /* This is a test wrapper for scanning an email, which is not used in
96 normal processing. Scan any file, using the Exim scanning interface.
97 This function tampers with various global variables so is unsafe to use
101 eml_filename a file holding the message to be scanned
103 Returns: Exim message processing code (OK, FAIL, DEFER, ...)
104 where true means malware was found (condition applies)
107 malware_in_file(uschar *eml_filename) {
108 uschar *scan_options[2];
109 uschar message_id_buf[64];
112 scan_options[0] = US"*";
113 scan_options[1] = NULL;
115 /* spool_mbox() assumes various parameters exist, when creating
116 the relevant directory and the email within */
117 (void) string_format(message_id_buf, sizeof(message_id_buf),
118 "dummy-%d", pseudo_random_number(INT_MAX));
119 message_id = message_id_buf;
120 sender_address = US"malware-sender@example.net";
122 recipients_list = NULL;
123 receive_add_recipient(US"malware-victim@example.net", -1);
124 enable_dollar_recipients = TRUE;
126 ret = malware_internal(scan_options, eml_filename, TRUE);
128 Ustrncpy(spooled_message_id, message_id, sizeof(spooled_message_id));
130 /* don't set no_mbox_unspool; at present, there's no way for it to become
131 set, but if that changes, then it should apply to these tests too */
138 /*************************************************
139 * Scan content for malware *
140 *************************************************/
142 /* This is an internal interface for scanning an email; the normal interface
143 is via malware(), or there's malware_in_file() used for testing/debugging.
146 listptr the list of options to the "malware = ..." ACL condition
147 eml_filename the file holding the email to be scanned
148 faking whether or not we're faking this up for the -bmalware test
150 Returns: Exim message processing code (OK, FAIL, DEFER, ...)
151 where true means malware was found (condition applies)
153 static int malware_internal(uschar **listptr, uschar *eml_filename, BOOL faking) {
155 uschar *list = *listptr;
156 uschar *av_scanner_work = av_scanner;
157 uschar *scanner_name;
158 uschar scanner_name_buffer[16];
159 uschar *malware_regex;
160 uschar malware_regex_buffer[64];
161 uschar malware_regex_default[] = ".+";
162 unsigned long mbox_size;
166 const uschar *rerror;
168 /* make sure the eml mbox file is spooled up */
169 mbox_file = spool_mbox(&mbox_size, faking ? eml_filename : NULL);
170 if (mbox_file == NULL) {
171 /* error while spooling */
172 log_write(0, LOG_MAIN|LOG_PANIC,
173 "malware acl condition: error while creating mbox spool file");
176 /* none of our current scanners need the mbox
177 file as a stream, so we can close it right away */
178 (void)fclose(mbox_file);
180 /* extract the malware regex to match against from the option list */
181 if ((malware_regex = string_nextinlist(&list, &sep,
182 malware_regex_buffer,
183 sizeof(malware_regex_buffer))) != NULL) {
185 /* parse 1st option */
186 if ( (strcmpic(malware_regex,US"false") == 0) ||
187 (Ustrcmp(malware_regex,"0") == 0) ) {
188 /* explicitly no matching */
192 /* special cases (match anything except empty) */
193 if ( (strcmpic(malware_regex,US"true") == 0) ||
194 (Ustrcmp(malware_regex,"*") == 0) ||
195 (Ustrcmp(malware_regex,"1") == 0) ) {
196 malware_regex = malware_regex_default;
200 /* empty means "don't match anything" */
204 /* Reset sep that is set by previous string_nextinlist() call */
207 /* compile the regex, see if it works */
208 re = pcre_compile(CS malware_regex, PCRE_COPT, (const char **)&rerror, &roffset, NULL);
210 log_write(0, LOG_MAIN|LOG_PANIC,
211 "malware acl condition: regular expression error in '%s': %s at offset %d", malware_regex, rerror, roffset);
215 /* if av_scanner starts with a dollar, expand it first */
216 if (*av_scanner == '$') {
217 av_scanner_work = expand_string(av_scanner);
218 if (av_scanner_work == NULL) {
219 log_write(0, LOG_MAIN|LOG_PANIC,
220 "malware acl condition: av_scanner starts with $, but expansion failed: %s", expand_string_message);
224 debug_printf("Expanded av_scanner global: %s\n", av_scanner_work);
225 /* disable result caching in this case */
231 /* Do not scan twice. */
232 if (malware_ok == 0) {
234 /* find the scanner type from the av_scanner option */
235 if ((scanner_name = string_nextinlist(&av_scanner_work, &sep,
237 sizeof(scanner_name_buffer))) == NULL) {
238 /* no scanner given */
239 log_write(0, LOG_MAIN|LOG_PANIC,
240 "malware acl condition: av_scanner configuration variable is empty");
244 /* "f-protd" scanner type ----------------------------------------------- */
245 if (strcmpic(scanner_name, US"f-protd") == 0) {
246 uschar *fp_options, *fp_scan_option;
247 uschar fp_scan_option_buffer[1024];
248 uschar fp_options_buffer[1024];
249 uschar fp_options_default[] = "localhost 10200-10204";
250 uschar hostname[256];
251 unsigned int port, portlow, porthigh, connect_ok=0, detected=0, par_count = 0;
255 uschar scanrequest[2048], buf[32768], *strhelper, *strhelper2;
257 if ((fp_options = string_nextinlist(&av_scanner_work, &sep,
258 fp_options_buffer, sizeof(fp_options_buffer))) == NULL) {
259 /* no options supplied, use default options */
260 fp_options = fp_options_default;
263 /* extract host and port part */
264 if ( sscanf(CS fp_options, "%s %u-%u", hostname, &portlow, &porthigh) != 3 ) {
265 if ( sscanf(CS fp_options, "%s %u", hostname, &portlow) != 2 ) {
266 log_write(0, LOG_MAIN|LOG_PANIC,
267 "malware acl condition: f-protd: invalid socket '%s'", fp_options);
273 /* Lookup the host */
274 if((he = gethostbyname(CS hostname)) == 0) {
275 log_write(0, LOG_MAIN|LOG_PANIC,
276 "malware acl condition: f-protd: failed to lookup host '%s'", hostname);
280 in = *(struct in_addr *) he->h_addr_list[0];
284 /* Open the f-protd TCP socket */
285 if ( (sock = ip_socket(SOCK_STREAM, AF_INET)) < 0) {
286 log_write(0, LOG_MAIN|LOG_PANIC,
287 "malware acl condition: f-protd: unable to acquire socket (%s)",
292 /* Try to connect to all portslow-high until connection is established */
293 for (port = portlow; !connect_ok && port < porthigh; port++) {
294 if (ip_connect(sock, AF_INET, (uschar*)inet_ntoa(in), port, 5) >= 0) {
300 log_write(0, LOG_MAIN|LOG_PANIC,
301 "malware acl condition: f-protd: connection to %s, port %u-%u failed (%s)",
302 inet_ntoa(in), portlow, porthigh, strerror(errno));
307 DEBUG(D_acl) debug_printf("Malware scan: issuing %s GET\n", scanner_name);
308 (void)string_format(scanrequest, 1024, CS"GET %s", eml_filename);
310 while ((fp_scan_option = string_nextinlist(&av_scanner_work, &sep,
311 fp_scan_option_buffer, sizeof(fp_scan_option_buffer))) != NULL) {
313 Ustrcat(scanrequest, "%20");
315 Ustrcat(scanrequest, "?");
317 Ustrcat(scanrequest, fp_scan_option);
320 Ustrcat(scanrequest, " HTTP/1.0\r\n\r\n");
322 /* send scan request */
323 if (send(sock, &scanrequest, Ustrlen(scanrequest)+1, 0) < 0) {
325 log_write(0, LOG_MAIN|LOG_PANIC,
326 "malware acl condition: f-protd: unable to send command to socket (%s)", scanrequest);
330 /* We get a lot of empty lines, so we need this hack to check for any data at all */
331 while( recv(sock, buf, 1, MSG_PEEK) > 0 ) {
332 if ( recv_line(sock, buf, 32768) > 0) {
333 if ( Ustrstr(buf, US"<detected type=\"") != NULL ) {
335 } else if ( detected && (strhelper = Ustrstr(buf, US"<name>")) ) {
336 if ((strhelper2 = Ustrstr(buf, US"</name>")) != NULL) {
338 Ustrcpy(malware_name_buffer, strhelper + 6);
340 } else if ( Ustrstr(buf, US"<summary code=\"") ) {
341 if ( Ustrstr(buf, US"<summary code=\"11\">") ) {
342 malware_name = malware_name_buffer;
351 /* "drweb" scanner type ----------------------------------------------- */
352 /* v0.1 - added support for tcp sockets */
353 /* v0.0 - initial release -- support for unix sockets */
354 else if (strcmpic(scanner_name,US"drweb") == 0) {
355 uschar *drweb_options;
356 uschar drweb_options_buffer[1024];
357 uschar drweb_options_default[] = "/usr/local/drweb/run/drwebd.sock";
358 struct sockaddr_un server;
359 int sock, result, ovector[30];
360 unsigned int port, fsize;
361 uschar tmpbuf[1024], *drweb_fbuf;
362 uschar drweb_match_string[128];
363 int drweb_rc, drweb_cmd, drweb_flags = 0x0000, drweb_fd,
364 drweb_vnum, drweb_slen, drweb_fin = 0x0000;
366 uschar hostname[256];
371 if ((drweb_options = string_nextinlist(&av_scanner_work, &sep,
372 drweb_options_buffer, sizeof(drweb_options_buffer))) == NULL) {
373 /* no options supplied, use default options */
374 drweb_options = drweb_options_default;
377 if (*drweb_options != '/') {
379 /* extract host and port part */
380 if( sscanf(CS drweb_options, "%s %u", hostname, &port) != 2 ) {
381 log_write(0, LOG_MAIN|LOG_PANIC,
382 "malware acl condition: drweb: invalid socket '%s'", drweb_options);
386 /* Lookup the host */
387 if((he = gethostbyname(CS hostname)) == 0) {
388 log_write(0, LOG_MAIN|LOG_PANIC,
389 "malware acl condition: drweb: failed to lookup host '%s'", hostname);
393 in = *(struct in_addr *) he->h_addr_list[0];
395 /* Open the drwebd TCP socket */
396 if ( (sock = ip_socket(SOCK_STREAM, AF_INET)) < 0) {
397 log_write(0, LOG_MAIN|LOG_PANIC,
398 "malware acl condition: drweb: unable to acquire socket (%s)",
403 if (ip_connect(sock, AF_INET, (uschar*)inet_ntoa(in), port, 5) < 0) {
405 log_write(0, LOG_MAIN|LOG_PANIC,
406 "malware acl condition: drweb: connection to %s, port %u failed (%s)",
407 inet_ntoa(in), port, strerror(errno));
411 /* prepare variables */
412 drweb_cmd = htonl(DRWEBD_SCAN_CMD);
413 drweb_flags = htonl(DRWEBD_RETURN_VIRUSES | DRWEBD_IS_MAIL);
416 drweb_fd = open(CS eml_filename, O_RDONLY);
417 if (drweb_fd == -1) {
419 log_write(0, LOG_MAIN|LOG_PANIC,
420 "malware acl condition: drweb: can't open spool file %s: %s",
421 eml_filename, strerror(errno));
424 fsize = lseek(drweb_fd, 0, SEEK_END);
427 (void)close(drweb_fd);
428 log_write(0, LOG_MAIN|LOG_PANIC,
429 "malware acl condition: drweb: can't seek spool file %s: %s",
430 eml_filename, strerror(errno));
433 drweb_slen = htonl(fsize);
434 lseek(drweb_fd, 0, SEEK_SET);
436 DEBUG(D_acl) debug_printf("Malware scan: issuing %s remote scan [%s %u]\n",
437 scanner_name, hostname, port);
439 /* send scan request */
440 if ((send(sock, &drweb_cmd, sizeof(drweb_cmd), 0) < 0) ||
441 (send(sock, &drweb_flags, sizeof(drweb_flags), 0) < 0) ||
442 (send(sock, &drweb_fin, sizeof(drweb_fin), 0) < 0) ||
443 (send(sock, &drweb_slen, sizeof(drweb_slen), 0) < 0)) {
445 (void)close(drweb_fd);
446 log_write(0, LOG_MAIN|LOG_PANIC,
447 "malware acl condition: drweb: unable to send commands to socket (%s)", drweb_options);
451 drweb_fbuf = (uschar *) malloc (fsize);
454 (void)close(drweb_fd);
455 log_write(0, LOG_MAIN|LOG_PANIC,
456 "malware acl condition: drweb: unable to allocate memory %u for file (%s)",
457 fsize, eml_filename);
461 result = read (drweb_fd, drweb_fbuf, fsize);
464 (void)close(drweb_fd);
466 log_write(0, LOG_MAIN|LOG_PANIC,
467 "malware acl condition: drweb: can't read spool file %s: %s",
468 eml_filename, strerror(errno));
471 (void)close(drweb_fd);
473 /* send file body to socket */
474 if (send(sock, drweb_fbuf, fsize, 0) < 0) {
477 log_write(0, LOG_MAIN|LOG_PANIC,
478 "malware acl condition: drweb: unable to send file body to socket (%s)", drweb_options);
481 (void)close(drweb_fd);
484 /* open the drwebd UNIX socket */
485 sock = socket(AF_UNIX, SOCK_STREAM, 0);
487 log_write(0, LOG_MAIN|LOG_PANIC,
488 "malware acl condition: drweb: can't open UNIX socket");
491 server.sun_family = AF_UNIX;
492 Ustrcpy(server.sun_path, drweb_options);
493 if (connect(sock, (struct sockaddr *) &server, sizeof(struct sockaddr_un)) < 0) {
495 log_write(0, LOG_MAIN|LOG_PANIC,
496 "malware acl condition: drweb: unable to connect to socket (%s). errno=%d", drweb_options, errno);
500 /* prepare variables */
501 drweb_cmd = htonl(DRWEBD_SCAN_CMD);
502 drweb_flags = htonl(DRWEBD_RETURN_VIRUSES | DRWEBD_IS_MAIL);
503 drweb_slen = htonl(Ustrlen(eml_filename));
505 DEBUG(D_acl) debug_printf("Malware scan: issuing %s local scan [%s]\n",
506 scanner_name, drweb_options);
508 /* send scan request */
509 if ((send(sock, &drweb_cmd, sizeof(drweb_cmd), 0) < 0) ||
510 (send(sock, &drweb_flags, sizeof(drweb_flags), 0) < 0) ||
511 (send(sock, &drweb_slen, sizeof(drweb_slen), 0) < 0) ||
512 (send(sock, eml_filename, Ustrlen(eml_filename), 0) < 0) ||
513 (send(sock, &drweb_fin, sizeof(drweb_fin), 0) < 0)) {
515 log_write(0, LOG_MAIN|LOG_PANIC,
516 "malware acl condition: drweb: unable to send commands to socket (%s)", drweb_options);
521 /* wait for result */
522 if ((bread = recv(sock, &drweb_rc, sizeof(drweb_rc), 0) != sizeof(drweb_rc))) {
524 log_write(0, LOG_MAIN|LOG_PANIC,
525 "malware acl condition: drweb: unable to read return code");
528 drweb_rc = ntohl(drweb_rc);
530 if ((bread = recv(sock, &drweb_vnum, sizeof(drweb_vnum), 0) != sizeof(drweb_vnum))) {
532 log_write(0, LOG_MAIN|LOG_PANIC,
533 "malware acl condition: drweb: unable to read the number of viruses");
536 drweb_vnum = ntohl(drweb_vnum);
538 /* "virus(es) found" if virus number is > 0 */
542 uschar pre_malware_nb[256];
544 malware_name = malware_name_buffer;
546 /* setup default virus name */
547 Ustrcpy(malware_name_buffer,"unknown");
549 /* read and concatenate virus names into one string */
550 for (i=0;i<drweb_vnum;i++)
552 /* read the size of report */
553 if ((bread = recv(sock, &drweb_slen, sizeof(drweb_slen), 0) != sizeof(drweb_slen))) {
555 log_write(0, LOG_MAIN|LOG_PANIC,
556 "malware acl condition: drweb: cannot read report size");
559 drweb_slen = ntohl(drweb_slen);
561 /* read report body */
562 if ((bread = recv(sock, tmpbuf, drweb_slen, 0)) != drweb_slen) {
564 log_write(0, LOG_MAIN|LOG_PANIC,
565 "malware acl condition: drweb: cannot read report string");
568 tmpbuf[drweb_slen] = '\0';
570 /* set up match regex, depends on retcode */
571 Ustrcpy(drweb_match_string, "infected\\swith\\s*(.+?)$");
573 drweb_re = pcre_compile( CS drweb_match_string,
575 (const char **)&rerror,
579 /* try matcher on the line, grab substring */
580 result = pcre_exec(drweb_re, NULL, CS tmpbuf, Ustrlen(tmpbuf), 0, 0, ovector, 30);
582 pcre_copy_substring(CS tmpbuf, ovector, result, 1, CS pre_malware_nb, 255);
584 /* the first name we just copy to malware_name */
586 Ustrcpy(CS malware_name_buffer, CS pre_malware_nb);
588 /* concatenate each new virus name to previous */
589 int slen = Ustrlen(malware_name_buffer);
590 if (slen < (slen+Ustrlen(pre_malware_nb))) {
591 Ustrcat(malware_name_buffer, "/");
592 Ustrcat(malware_name_buffer, pre_malware_nb);
598 const char *drweb_s = NULL;
600 if (drweb_rc & DERR_READ_ERR) drweb_s = "read error";
601 if (drweb_rc & DERR_NOMEMORY) drweb_s = "no memory";
602 if (drweb_rc & DERR_TIMEOUT) drweb_s = "timeout";
603 if (drweb_rc & DERR_BAD_CALL) drweb_s = "wrong command";
604 /* retcodes DERR_SYMLINK, DERR_NO_REGFILE, DERR_SKIPPED.
605 * DERR_TOO_BIG, DERR_TOO_COMPRESSED, DERR_SPAM,
606 * DERR_CRC_ERROR, DERR_READSOCKET, DERR_WRITE_ERR
607 * and others are ignored */
609 log_write(0, LOG_MAIN|LOG_PANIC,
610 "malware acl condition: drweb: drweb daemon retcode 0x%x (%s)", drweb_rc, drweb_s);
619 /* ----------------------------------------------------------------------- */
620 else if (strcmpic(scanner_name,US"aveserver") == 0) {
622 uschar kav_options_buffer[1024];
623 uschar kav_options_default[] = "/var/run/aveserver";
625 struct sockaddr_un server;
629 if ((kav_options = string_nextinlist(&av_scanner_work, &sep,
631 sizeof(kav_options_buffer))) == NULL) {
632 /* no options supplied, use default options */
633 kav_options = kav_options_default;
636 /* open the aveserver socket */
637 sock = socket(AF_UNIX, SOCK_STREAM, 0);
639 log_write(0, LOG_MAIN|LOG_PANIC,
640 "malware acl condition: can't open UNIX socket.");
643 server.sun_family = AF_UNIX;
644 Ustrcpy(server.sun_path, kav_options);
645 if (connect(sock, (struct sockaddr *) &server, sizeof(struct sockaddr_un)) < 0) {
647 log_write(0, LOG_MAIN|LOG_PANIC,
648 "malware acl condition: unable to connect to aveserver UNIX socket (%s). errno=%d", kav_options, errno);
652 /* read aveserver's greeting and see if it is ready (2xx greeting) */
653 recv_line(sock, buf, 32768);
656 /* aveserver is having problems */
658 log_write(0, LOG_MAIN|LOG_PANIC,
659 "malware acl condition: aveserver is unavailable (Responded: %s).", ((buf[0] != 0) ? buf : (uschar *)"nothing") );
663 /* prepare our command */
664 (void)string_format(buf, 32768, "SCAN bPQRSTUW %s\r\n", eml_filename);
666 DEBUG(D_acl) debug_printf("Malware scan: issuing %s SCAN\n", scanner_name);
669 if (send(sock, buf, Ustrlen(buf), 0) < 0) {
671 log_write(0, LOG_MAIN|LOG_PANIC,
672 "malware acl condition: unable to write to aveserver UNIX socket (%s)", kav_options);
678 /* read response lines, find malware name and final response */
679 while (recv_line(sock, buf, 32768) > 0) {
680 debug_printf("aveserver: %s\n", buf);
683 } else if (buf[0] == '5') {
684 /* aveserver is having problems */
685 log_write(0, LOG_MAIN|LOG_PANIC,
686 "malware acl condition: unable to scan file %s (Responded: %s).",
690 } else if (Ustrncmp(buf,"322",3) == 0) {
691 uschar *p = Ustrchr(&buf[4],' ');
693 Ustrcpy(malware_name_buffer,&buf[4]);
694 malware_name = malware_name_buffer;
698 /* prepare our command */
699 (void)string_format(buf, 32768, "quit\r\n");
702 if (send(sock, buf, Ustrlen(buf), 0) < 0) {
704 log_write(0, LOG_MAIN|LOG_PANIC,
705 "malware acl condition: unable to write to aveserver UNIX socket (%s)", kav_options);
709 /* read aveserver's greeting and see if it is ready (2xx greeting) */
710 recv_line(sock, buf, 32768);
713 /* aveserver is having problems */
715 log_write(0, LOG_MAIN|LOG_PANIC,
716 "malware acl condition: unable to quit aveserver dialogue (Responded: %s).", ((buf[0] != 0) ? buf : (uschar *)"nothing") );
722 if (result == DEFER) return DEFER;
724 /* "fsecure" scanner type ------------------------------------------------- */
725 else if (strcmpic(scanner_name,US"fsecure") == 0) {
726 uschar *fsecure_options;
727 uschar fsecure_options_buffer[1024];
728 uschar fsecure_options_default[] = "/var/run/.fsav";
729 struct sockaddr_un server;
730 int sock, i, j, bread = 0;
731 uschar file_name[1024];
732 uschar av_buffer[1024];
734 static uschar *cmdoptions[] = { US"CONFIGURE\tARCHIVE\t1\n",
735 US"CONFIGURE\tTIMEOUT\t0\n",
736 US"CONFIGURE\tMAXARCH\t5\n",
737 US"CONFIGURE\tMIME\t1\n" };
740 if ((fsecure_options = string_nextinlist(&av_scanner_work, &sep,
741 fsecure_options_buffer,
742 sizeof(fsecure_options_buffer))) == NULL) {
743 /* no options supplied, use default options */
744 fsecure_options = fsecure_options_default;
747 /* open the fsecure socket */
748 sock = socket(AF_UNIX, SOCK_STREAM, 0);
750 log_write(0, LOG_MAIN|LOG_PANIC,
751 "malware acl condition: unable to open fsecure socket %s (%s)",
752 fsecure_options, strerror(errno));
755 server.sun_family = AF_UNIX;
756 Ustrcpy(server.sun_path, fsecure_options);
757 if (connect(sock, (struct sockaddr *) &server, sizeof(struct sockaddr_un)) < 0) {
759 log_write(0, LOG_MAIN|LOG_PANIC,
760 "malware acl condition: unable to connect to fsecure socket %s (%s)",
761 fsecure_options, strerror(errno));
765 DEBUG(D_acl) debug_printf("Malware scan: issuing %s scan [%s]\n",
766 scanner_name, fsecure_options);
769 memset(av_buffer, 0, sizeof(av_buffer));
770 for (i=0; i != 4; i++) {
771 /* debug_printf("send option \"%s\"",cmdoptions[i]); */
772 if (write(sock, cmdoptions[i], Ustrlen(cmdoptions[i])) < 0) {
774 log_write(0, LOG_MAIN|LOG_PANIC,
775 "malware acl condition: unable to write fsecure option %d to %s (%s)",
776 i, fsecure_options, strerror(errno));
780 bread = ip_recv(sock, av_buffer, sizeof(av_buffer), MALWARE_TIMEOUT);
781 if (bread >0) av_buffer[bread]='\0';
784 log_write(0, LOG_MAIN|LOG_PANIC,
785 "malware acl condition: unable to read fsecure answer %d (%s)", i, strerror(errno));
788 for (j=0;j<bread;j++) if((av_buffer[j]=='\r')||(av_buffer[j]=='\n')) av_buffer[j] ='@';
789 /* debug_printf("read answer %d read=%d \"%s\"\n", i, bread, av_buffer ); */
790 /* while (Ustrstr(av_buffer, "OK\tServer configured.@") == NULL); */
793 /* pass the mailfile to fsecure */
794 (void)string_format(file_name,1024,"SCAN\t%s\n", eml_filename);
795 /* debug_printf("send scan %s",file_name); */
796 if (write(sock, file_name, Ustrlen(file_name)) < 0) {
798 log_write(0, LOG_MAIN|LOG_PANIC,
799 "malware acl condition: unable to write fsecure scan to %s (%s)",
800 fsecure_options, strerror(errno));
805 /* todo also SUSPICION\t */
806 fs_inf = pcre_compile("\\S{0,5}INFECTED\\t[^\\t]*\\t([^\\t]+)\\t\\S*$", PCRE_COPT, (const char **)&rerror, &roffset, NULL);
808 /* read report, linewise */
812 memset(av_buffer, 0, sizeof(av_buffer));
814 bread=ip_recv(sock, &av_buffer[i], 1, MALWARE_TIMEOUT);
817 log_write(0, LOG_MAIN|LOG_PANIC,
818 "malware acl condition: unable to read fsecure result (%s)", strerror(errno));
823 while ((i < sizeof(av_buffer)-1 ) && (av_buffer[i-1] != '\n'));
824 av_buffer[i-1] = '\0';
825 /* debug_printf("got line \"%s\"\n",av_buffer); */
827 /* Really search for virus again? */
828 if (malware_name == NULL) {
829 /* try matcher on the line, grab substring */
830 i = pcre_exec(fs_inf, NULL, CS av_buffer, Ustrlen(av_buffer), 0, 0, ovector, 30);
833 pcre_copy_substring(CS av_buffer, ovector, i, 1, CS malware_name_buffer, 255);
834 malware_name = malware_name_buffer;
838 while (Ustrstr(av_buffer, "OK\tScan ok.") == NULL);
841 /* ----------------------------------------------------------------------- */
843 /* "kavdaemon" scanner type ------------------------------------------------ */
844 else if (strcmpic(scanner_name,US"kavdaemon") == 0) {
846 uschar kav_options_buffer[1024];
847 uschar kav_options_default[] = "/var/run/AvpCtl";
848 struct sockaddr_un server;
852 uschar scanrequest[1024];
853 uschar kav_match_string[128];
855 unsigned long kav_reportlen, bread;
860 if ((kav_options = string_nextinlist(&av_scanner_work, &sep,
862 sizeof(kav_options_buffer))) == NULL) {
863 /* no options supplied, use default options */
864 kav_options = kav_options_default;
867 /* open the kavdaemon socket */
868 sock = socket(AF_UNIX, SOCK_STREAM, 0);
870 log_write(0, LOG_MAIN|LOG_PANIC,
871 "malware acl condition: can't open UNIX socket.");
874 server.sun_family = AF_UNIX;
875 Ustrcpy(server.sun_path, kav_options);
876 if (connect(sock, (struct sockaddr *) &server, sizeof(struct sockaddr_un)) < 0) {
878 log_write(0, LOG_MAIN|LOG_PANIC,
879 "malware acl condition: unable to connect to kavdaemon UNIX socket (%s). errno=%d", kav_options, errno);
883 /* get current date and time, build scan request */
885 /* pdp note: before the eml_filename parameter, this scanned the
886 directory; not finding documentation, so we'll strip off the directory.
887 The side-effect is that the test framework scanning may end up in
888 scanning more than was requested, but for the normal interface, this is
890 strftime(CS tmpbuf, sizeof(tmpbuf), "<0>%d %b %H:%M:%S:%%s", localtime(&t));
891 fits = string_format(scanrequest, 1024,CS tmpbuf, eml_filename);
894 log_write(0, LOG_MAIN|LOG_PANIC,
895 "malware filename does not fit in buffer [malware_internal() kavdaemon]");
897 p = Ustrrchr(scanrequest, '/');
901 DEBUG(D_acl) debug_printf("Malware scan: issuing %s scan [%s]\n",
902 scanner_name, kav_options);
904 /* send scan request */
905 if (send(sock, scanrequest, Ustrlen(scanrequest)+1, 0) < 0) {
907 log_write(0, LOG_MAIN|LOG_PANIC,
908 "malware acl condition: unable to write to kavdaemon UNIX socket (%s)", kav_options);
912 /* wait for result */
913 if ((bread = recv(sock, tmpbuf, 2, 0) != 2)) {
915 log_write(0, LOG_MAIN|LOG_PANIC,
916 "malware acl condition: unable to read 2 bytes from kavdaemon socket.");
920 /* get errorcode from one nibble */
921 if (test_byte_order() == LITTLE_MY_ENDIAN) {
922 kav_rc = tmpbuf[0] & 0x0F;
925 kav_rc = tmpbuf[1] & 0x0F;
928 /* improper kavdaemon configuration */
929 if ( (kav_rc == 5) || (kav_rc == 6) ) {
931 log_write(0, LOG_MAIN|LOG_PANIC,
932 "malware acl condition: please reconfigure kavdaemon to NOT disinfect or remove infected files.");
938 log_write(0, LOG_MAIN|LOG_PANIC,
939 "malware acl condition: kavdaemon reported 'scanning not completed' (code 1).");
945 log_write(0, LOG_MAIN|LOG_PANIC,
946 "malware acl condition: kavdaemon reported 'kavdaemon damaged' (code 7).");
950 /* code 8 is not handled, since it is ambigous. It appears mostly on
951 bounces where part of a file has been cut off */
953 /* "virus found" return codes (2-4) */
954 if ((kav_rc > 1) && (kav_rc < 5)) {
957 /* setup default virus name */
958 Ustrcpy(malware_name_buffer,"unknown");
959 malware_name = malware_name_buffer;
961 if (test_byte_order() == LITTLE_MY_ENDIAN) {
962 report_flag = tmpbuf[1];
965 report_flag = tmpbuf[0];
968 /* read the report, if available */
969 if( report_flag == 1 ) {
970 /* read report size */
971 if ((bread = recv(sock, &kav_reportlen, 4, 0)) != 4) {
973 log_write(0, LOG_MAIN|LOG_PANIC,
974 "malware acl condition: cannot read report size from kavdaemon");
978 /* it's possible that avp returns av_buffer[1] == 1 but the
979 reportsize is 0 (!?) */
980 if (kav_reportlen > 0) {
981 /* set up match regex, depends on retcode */
983 Ustrcpy(kav_match_string, "suspicion:\\s*(.+?)\\s*$");
985 Ustrcpy(kav_match_string, "infected:\\s*(.+?)\\s*$");
987 kav_re = pcre_compile( CS kav_match_string,
989 (const char **)&rerror,
993 /* read report, linewise */
994 while (kav_reportlen > 0) {
999 while ( recv(sock, &tmpbuf[bread], 1, 0) == 1 ) {
1001 if ( (tmpbuf[bread] == '\n') || (bread > 1021) ) break;
1005 tmpbuf[bread] = '\0';
1007 /* try matcher on the line, grab substring */
1008 result = pcre_exec(kav_re, NULL, CS tmpbuf, Ustrlen(tmpbuf), 0, 0, ovector, 30);
1010 pcre_copy_substring(CS tmpbuf, ovector, result, 1, CS malware_name_buffer, 255);
1018 /* no virus found */
1019 malware_name = NULL;
1024 /* ----------------------------------------------------------------------- */
1027 /* "cmdline" scanner type ------------------------------------------------ */
1028 else if (strcmpic(scanner_name,US"cmdline") == 0) {
1029 uschar *cmdline_scanner;
1030 uschar cmdline_scanner_buffer[1024];
1031 uschar *cmdline_trigger;
1032 uschar cmdline_trigger_buffer[1024];
1033 const pcre *cmdline_trigger_re;
1034 uschar *cmdline_regex;
1035 uschar cmdline_regex_buffer[1024];
1036 const pcre *cmdline_regex_re;
1037 uschar file_name[1024];
1038 uschar commandline[1024];
1039 void (*eximsigchld)(int);
1040 void (*eximsigpipe)(int);
1041 FILE *scanner_out = NULL;
1042 FILE *scanner_record = NULL;
1043 uschar linebuffer[32767];
1050 /* find scanner command line */
1051 if ((cmdline_scanner = string_nextinlist(&av_scanner_work, &sep,
1052 cmdline_scanner_buffer,
1053 sizeof(cmdline_scanner_buffer))) == NULL) {
1054 /* no command line supplied */
1055 log_write(0, LOG_MAIN|LOG_PANIC,
1056 "malware acl condition: missing commandline specification for cmdline scanner type.");
1060 /* find scanner output trigger */
1061 if ((cmdline_trigger = string_nextinlist(&av_scanner_work, &sep,
1062 cmdline_trigger_buffer,
1063 sizeof(cmdline_trigger_buffer))) == NULL) {
1064 /* no trigger regex supplied */
1065 log_write(0, LOG_MAIN|LOG_PANIC,
1066 "malware acl condition: missing trigger specification for cmdline scanner type.");
1070 /* precompile trigger regex */
1071 cmdline_trigger_re = pcre_compile(CS cmdline_trigger, PCRE_COPT, (const char **)&rerror, &roffset, NULL);
1072 if (cmdline_trigger_re == NULL) {
1073 log_write(0, LOG_MAIN|LOG_PANIC,
1074 "malware acl condition: regular expression error in '%s': %s at offset %d", cmdline_trigger_re, rerror, roffset);
1078 /* find scanner name regex */
1079 if ((cmdline_regex = string_nextinlist(&av_scanner_work, &sep,
1080 cmdline_regex_buffer,
1081 sizeof(cmdline_regex_buffer))) == NULL) {
1082 /* no name regex supplied */
1083 log_write(0, LOG_MAIN|LOG_PANIC,
1084 "malware acl condition: missing virus name regex specification for cmdline scanner type.");
1088 /* precompile name regex */
1089 cmdline_regex_re = pcre_compile(CS cmdline_regex, PCRE_COPT, (const char **)&rerror, &roffset, NULL);
1090 if (cmdline_regex_re == NULL) {
1091 log_write(0, LOG_MAIN|LOG_PANIC,
1092 "malware acl condition: regular expression error in '%s': %s at offset %d", cmdline_regex_re, rerror, roffset);
1096 /* prepare scanner call; despite the naming, file_name holds a directory
1097 name which is documented as the value given to %s. */
1098 if (Ustrlen(eml_filename) > sizeof(file_name) - 1)
1100 log_write(0, LOG_MAIN|LOG_PANIC,
1101 "malware filename does not fit in buffer [malware_internal() cmdline]");
1104 Ustrcpy(file_name, eml_filename);
1105 p = Ustrrchr(file_name, '/');
1108 fits = string_format(commandline, sizeof(commandline), CS cmdline_scanner, file_name);
1111 log_write(0, LOG_MAIN|LOG_PANIC,
1112 "cmdline scanner command-line does not fit in buffer");
1116 /* redirect STDERR too */
1117 if (Ustrlen(commandline) + 5 > sizeof(commandline))
1119 log_write(0, LOG_MAIN|LOG_PANIC,
1120 "cmdline scanner command-line does not fit in buffer (STDERR redirect)");
1123 Ustrcat(commandline," 2>&1");
1125 DEBUG(D_acl) debug_printf("Malware scan: issuing %s scan [%s]\n", scanner_name, commandline);
1127 /* store exims signal handlers */
1128 eximsigchld = signal(SIGCHLD,SIG_DFL);
1129 eximsigpipe = signal(SIGPIPE,SIG_DFL);
1131 scanner_out = popen(CS commandline,"r");
1132 if (scanner_out == NULL) {
1133 log_write(0, LOG_MAIN|LOG_PANIC,
1134 "malware acl condition: calling cmdline scanner (%s) failed: %s.", commandline, strerror(errno));
1135 signal(SIGCHLD,eximsigchld);
1136 signal(SIGPIPE,eximsigpipe);
1140 (void)string_format(file_name,1024,"%s/scan/%s/%s_scanner_output", spool_directory, message_id, message_id);
1141 scanner_record = modefopen(file_name,"wb",SPOOL_MODE);
1143 if (scanner_record == NULL) {
1144 log_write(0, LOG_MAIN|LOG_PANIC,
1145 "malware acl condition: opening scanner output file (%s) failed: %s.", file_name, strerror(errno));
1146 pclose(scanner_out);
1147 signal(SIGCHLD,eximsigchld);
1148 signal(SIGPIPE,eximsigpipe);
1152 /* look for trigger while recording output */
1153 while(fgets(CS linebuffer,32767,scanner_out) != NULL) {
1154 if ( Ustrlen(linebuffer) > fwrite(linebuffer, 1, Ustrlen(linebuffer), scanner_record) ) {
1156 log_write(0, LOG_MAIN|LOG_PANIC,
1157 "malware acl condition: short write on scanner output file (%s).", file_name);
1158 pclose(scanner_out);
1159 signal(SIGCHLD,eximsigchld);
1160 signal(SIGPIPE,eximsigpipe);
1163 /* try trigger match */
1164 if (!trigger && regex_match_and_setup(cmdline_trigger_re, linebuffer, 0, -1))
1168 (void)fclose(scanner_record);
1169 pclose(scanner_out);
1170 signal(SIGCHLD,eximsigchld);
1171 signal(SIGPIPE,eximsigpipe);
1174 /* setup default virus name */
1175 Ustrcpy(malware_name_buffer,"unknown");
1176 malware_name = malware_name_buffer;
1178 /* re-open the scanner output file, look for name match */
1179 scanner_record = fopen(CS file_name,"rb");
1180 while(fgets(CS linebuffer,32767,scanner_record) != NULL) {
1182 result = pcre_exec(cmdline_regex_re, NULL, CS linebuffer, Ustrlen(linebuffer), 0, 0, ovector, 30);
1184 pcre_copy_substring(CS linebuffer, ovector, result, 1, CS malware_name_buffer, 255);
1187 (void)fclose(scanner_record);
1190 /* no virus found */
1191 malware_name = NULL;
1194 /* ----------------------------------------------------------------------- */
1197 /* "sophie" scanner type ------------------------------------------------- */
1198 else if (strcmpic(scanner_name,US"sophie") == 0) {
1199 uschar *sophie_options;
1200 uschar sophie_options_buffer[1024];
1201 uschar sophie_options_default[] = "/var/run/sophie";
1203 struct sockaddr_un server;
1206 uschar file_name[1024];
1207 uschar av_buffer[1024];
1209 if ((sophie_options = string_nextinlist(&av_scanner_work, &sep,
1210 sophie_options_buffer,
1211 sizeof(sophie_options_buffer))) == NULL) {
1212 /* no options supplied, use default options */
1213 sophie_options = sophie_options_default;
1216 /* open the sophie socket */
1217 sock = socket(AF_UNIX, SOCK_STREAM, 0);
1219 log_write(0, LOG_MAIN|LOG_PANIC,
1220 "malware acl condition: can't open UNIX socket.");
1223 server.sun_family = AF_UNIX;
1224 Ustrcpy(server.sun_path, sophie_options);
1225 if (connect(sock, (struct sockaddr *) &server, sizeof(struct sockaddr_un)) < 0) {
1227 log_write(0, LOG_MAIN|LOG_PANIC,
1228 "malware acl condition: unable to connect to sophie UNIX socket (%s). errno=%d", sophie_options, errno);
1232 /* pass the scan directory to sophie */
1233 len = Ustrlen(eml_filename) + 1;
1234 if (len > sizeof(file_name))
1237 log_write(0, LOG_MAIN|LOG_PANIC,
1238 "malware filename does not fit in buffer [malware_internal() sophie]");
1241 memcpy(file_name, eml_filename, len);
1242 p = Ustrrchr(file_name, '/');
1246 DEBUG(D_acl) debug_printf("Malware scan: issuing %s scan [%s]\n",
1247 scanner_name, sophie_options);
1249 if (write(sock, file_name, Ustrlen(file_name)) < 0) {
1251 log_write(0, LOG_MAIN|LOG_PANIC,
1252 "malware acl condition: unable to write to sophie UNIX socket (%s)", sophie_options);
1256 (void)write(sock, "\n", 1);
1258 /* wait for result */
1259 memset(av_buffer, 0, sizeof(av_buffer));
1260 if ((!(bread = ip_recv(sock, av_buffer, sizeof(av_buffer), MALWARE_TIMEOUT)) > 0)) {
1262 log_write(0, LOG_MAIN|LOG_PANIC,
1263 "malware acl condition: unable to read from sophie UNIX socket (%s)", sophie_options);
1270 if (av_buffer[0] == '1') {
1271 if (Ustrchr(av_buffer, '\n')) *Ustrchr(av_buffer, '\n') = '\0';
1272 Ustrcpy(malware_name_buffer,&av_buffer[2]);
1273 malware_name = malware_name_buffer;
1275 else if (!strncmp(CS av_buffer, "-1", 2)) {
1276 log_write(0, LOG_MAIN|LOG_PANIC,
1277 "malware acl condition: malware acl condition: sophie reported error");
1281 /* all ok, no virus */
1282 malware_name = NULL;
1285 /* ----------------------------------------------------------------------- */
1288 /* "clamd" scanner type ------------------------------------------------- */
1289 /* This code was originally contributed by David Saez */
1290 /* There are three scanning methods available to us:
1291 * (1) Use the SCAN command, pointing to a file in the filesystem
1292 * (2) Use the STREAM command, send the data on a separate port
1293 * (3) Use the zINSTREAM command, send the data inline
1294 * The zINSTREAM command was introduced with ClamAV 0.95, which marked
1295 * STREAM deprecated; see: http://wiki.clamav.net/bin/view/Main/UpgradeNotes095
1296 * In Exim, we use SCAN if using a Unix-domain socket or explicitly told that
1297 * the TCP-connected daemon is actually local; otherwise we use zINSTREAM unless
1298 * WITH_OLD_CLAMAV_STREAM is defined.
1299 * See Exim bug 926 for details. */
1300 else if (strcmpic(scanner_name,US"clamd") == 0) {
1301 uschar *clamd_options;
1302 uschar clamd_options_buffer[1024];
1303 uschar clamd_options_default[] = "/tmp/clamd";
1304 uschar *p, *vname, *result_tag, *response_end;
1305 struct sockaddr_un server;
1308 uschar file_name[1024];
1309 uschar av_buffer[1024];
1310 uschar hostname[256];
1313 uschar *clamd_options2;
1314 uschar clamd_options2_buffer[1024];
1315 uschar clamd_options2_default[] = "";
1316 uschar *clamav_fbuf;
1317 int clam_fd, result;
1319 BOOL use_scan_command, fits;
1320 #ifdef WITH_OLD_CLAMAV_STREAM
1321 uschar av_buffer2[1024];
1324 uint32_t send_size, send_final_zeroblock;
1327 if ((clamd_options = string_nextinlist(&av_scanner_work, &sep,
1328 clamd_options_buffer,
1329 sizeof(clamd_options_buffer))) == NULL) {
1330 /* no options supplied, use default options */
1331 clamd_options = clamd_options_default;
1333 if ((clamd_options2 = string_nextinlist(&av_scanner_work, &sep,
1334 clamd_options2_buffer,
1335 sizeof(clamd_options2_buffer))) == NULL) {
1336 clamd_options2 = clamd_options2_default;
1339 if ((*clamd_options == '/') || (strcmpic(clamd_options2,US"local") == 0))
1340 use_scan_command = TRUE;
1342 use_scan_command = FALSE;
1344 /* See the discussion of response formats below to see why we really don't
1345 like colons in filenames when passing filenames to ClamAV. */
1346 if (use_scan_command && Ustrchr(eml_filename, ':')) {
1347 log_write(0, LOG_MAIN|LOG_PANIC,
1348 "malware acl condition: clamd: local/SCAN mode incompatible with" \
1349 " : in path to email filename [%s]", eml_filename);
1353 /* socket does not start with '/' -> network socket */
1354 if (*clamd_options != '/') {
1356 /* Confirmed in ClamAV source (0.95.3) that the TCPAddr option of clamd
1357 * only supports AF_INET, but we should probably be looking to the
1358 * future and rewriting this to be protocol-independent anyway. */
1360 /* extract host and port part */
1361 if( sscanf(CS clamd_options, "%s %u", hostname, &port) != 2 ) {
1362 log_write(0, LOG_MAIN|LOG_PANIC,
1363 "malware acl condition: clamd: invalid socket '%s'", clamd_options);
1367 /* Lookup the host */
1368 if((he = gethostbyname(CS hostname)) == 0) {
1369 log_write(0, LOG_MAIN|LOG_PANIC,
1370 "malware acl condition: clamd: failed to lookup host '%s'", hostname);
1374 in = *(struct in_addr *) he->h_addr_list[0];
1376 /* Open the ClamAV Socket */
1377 if ( (sock = ip_socket(SOCK_STREAM, AF_INET)) < 0) {
1378 log_write(0, LOG_MAIN|LOG_PANIC,
1379 "malware acl condition: clamd: unable to acquire socket (%s)",
1384 if (ip_connect(sock, AF_INET, (uschar*)inet_ntoa(in), port, 5) < 0) {
1386 log_write(0, LOG_MAIN|LOG_PANIC,
1387 "malware acl condition: clamd: connection to %s, port %u failed (%s)",
1388 inet_ntoa(in), port, strerror(errno));
1393 /* open the local socket */
1394 if ((sock = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
1395 log_write(0, LOG_MAIN|LOG_PANIC,
1396 "malware acl condition: clamd: unable to acquire socket (%s)",
1401 server.sun_family = AF_UNIX;
1402 Ustrcpy(server.sun_path, clamd_options);
1404 if (connect(sock, (struct sockaddr *) &server, sizeof(struct sockaddr_un)) < 0) {
1406 log_write(0, LOG_MAIN|LOG_PANIC,
1407 "malware acl condition: clamd: unable to connect to UNIX socket %s (%s)",
1408 clamd_options, strerror(errno) );
1413 /* have socket in variable "sock"; command to use is semi-independent of
1414 * the socket protocol. We use SCAN if is local (either Unix/local
1415 * domain socket, or explicitly told local) else we stream the data.
1416 * How we stream the data depends upon how we were built. */
1418 if (!use_scan_command) {
1420 #ifdef WITH_OLD_CLAMAV_STREAM
1421 /* "STREAM\n" command, get back a "PORT <N>\n" response, send data to
1422 * that port on a second connection; then in the scan-method-neutral
1423 * part, read the response back on the original connection. */
1425 DEBUG(D_acl) debug_printf("Malware scan: issuing %s old-style remote scan (PORT)\n",
1428 /* Pass the string to ClamAV (7 = "STREAM\n") */
1429 if (send(sock, "STREAM\n", 7, 0) < 0) {
1430 log_write(0, LOG_MAIN|LOG_PANIC,"malware acl condition: clamd: unable to write to socket (%s)",
1435 memset(av_buffer2, 0, sizeof(av_buffer2));
1436 bread = ip_recv(sock, av_buffer2, sizeof(av_buffer2), MALWARE_TIMEOUT);
1439 log_write(0, LOG_MAIN|LOG_PANIC,
1440 "malware acl condition: clamd: unable to read PORT from socket (%s)",
1446 if (bread == sizeof(av_buffer)) {
1447 log_write(0, LOG_MAIN|LOG_PANIC,
1448 "malware acl condition: clamd: buffer too small");
1453 if (!(*av_buffer2)) {
1454 log_write(0, LOG_MAIN|LOG_PANIC,
1455 "malware acl condition: clamd: ClamAV returned null");
1460 av_buffer2[bread] = '\0';
1461 if( sscanf(CS av_buffer2, "PORT %u\n", &port) != 1 ) {
1462 log_write(0, LOG_MAIN|LOG_PANIC,
1463 "malware acl condition: clamd: Expected port information from clamd, got '%s'", av_buffer2);
1468 if ( (sockData = ip_socket(SOCK_STREAM, AF_INET)) < 0) {
1469 log_write(0, LOG_MAIN|LOG_PANIC,
1470 "malware acl condition: clamd: unable to acquire socket (%s)",
1476 if (ip_connect(sockData, AF_INET, (uschar*)inet_ntoa(in), port, 5) < 0) {
1477 log_write(0, LOG_MAIN|LOG_PANIC,
1478 "malware acl condition: clamd: connection to %s, port %u failed (%s)",
1479 inet_ntoa(in), port, strerror(errno));
1480 (void)close(sockData); (void)close(sock);
1484 #define CLOSE_SOCKDATA (void)close(sockData)
1485 #else /* WITH_OLD_CLAMAV_STREAM not defined */
1486 /* New protocol: "zINSTREAM\n" followed by a sequence of <length><data>
1487 chunks, <n> a 4-byte number (network order), terminated by a zero-length
1490 DEBUG(D_acl) debug_printf("Malware scan: issuing %s new-style remote scan (zINSTREAM)\n",
1493 /* Pass the string to ClamAV (10 = "zINSTREAM\0") */
1494 if (send(sock, "zINSTREAM", 10, 0) < 0) {
1495 log_write(0, LOG_MAIN|LOG_PANIC,
1496 "malware acl condition: clamd: unable to send zINSTREAM to socket (%s)",
1502 #define CLOSE_SOCKDATA /**/
1505 /* calc file size */
1506 clam_fd = open(CS eml_filename, O_RDONLY);
1507 if (clam_fd == -1) {
1508 log_write(0, LOG_MAIN|LOG_PANIC,
1509 "malware acl condition: clamd: can't open spool file %s: %s",
1510 eml_filename, strerror(errno));
1511 CLOSE_SOCKDATA; (void)close(sock);
1514 fsize = lseek(clam_fd, 0, SEEK_END);
1516 log_write(0, LOG_MAIN|LOG_PANIC,
1517 "malware acl condition: clamd: can't seek spool file %s: %s",
1518 eml_filename, strerror(errno));
1519 CLOSE_SOCKDATA; (void)close(sock);
1522 lseek(clam_fd, 0, SEEK_SET);
1524 clamav_fbuf = (uschar *) malloc (fsize);
1526 log_write(0, LOG_MAIN|LOG_PANIC,
1527 "malware acl condition: clamd: unable to allocate memory %u for file (%s)",
1528 fsize, eml_filename);
1529 CLOSE_SOCKDATA; (void)close(sock); (void)close(clam_fd);
1533 result = read (clam_fd, clamav_fbuf, fsize);
1535 log_write(0, LOG_MAIN|LOG_PANIC,
1536 "malware acl condition: clamd: can't read spool file %s: %s",
1537 eml_filename, strerror(errno));
1538 CLOSE_SOCKDATA; (void)close(sock); (void)close(clam_fd);
1542 (void)close(clam_fd);
1544 /* send file body to socket */
1545 #ifdef WITH_OLD_CLAMAV_STREAM
1546 if (send(sockData, clamav_fbuf, fsize, 0) < 0) {
1547 log_write(0, LOG_MAIN|LOG_PANIC,
1548 "malware acl condition: clamd: unable to send file body to socket (%s:%u)", hostname, port);
1549 CLOSE_SOCKDATA; (void)close(sock);
1554 send_size = htonl(fsize);
1555 send_final_zeroblock = 0;
1556 if ((send(sock, &send_size, sizeof(send_size), 0) < 0) ||
1557 (send(sock, clamav_fbuf, fsize, 0) < 0) ||
1558 (send(sock, &send_final_zeroblock, sizeof(send_final_zeroblock), 0) < 0))
1560 log_write(0, LOG_MAIN|LOG_PANIC,
1561 "malware acl condition: clamd: unable to send file body to socket (%s:%u)", hostname, port);
1571 #undef CLOSE_SOCKDATA
1573 } else { /* use scan command */
1574 /* Send a SCAN command pointing to a filename; then in the then in the
1575 * scan-method-neutral part, read the response back */
1577 /* ================================================================= */
1579 /* Prior to the reworking post-Exim-4.72, this scanned a directory,
1580 which dates to when ClamAV needed us to break apart the email into the
1581 MIME parts (eg, with the now deprecated demime condition coming first).
1582 Some time back, ClamAV gained the ability to deconstruct the emails, so
1583 doing this would actually have resulted in the mail attachments being
1584 scanned twice, in the broken out files and from the original .eml.
1585 Since ClamAV now handles emails (and has for quite some time) we can
1586 just use the email file itself. */
1587 /* Pass the string to ClamAV (7 = "SCAN \n" + \0) */
1588 fits = string_format(file_name, sizeof(file_name), "SCAN %s\n",
1592 log_write(0, LOG_MAIN|LOG_PANIC,
1593 "malware filename does not fit in buffer [malware_internal() clamd]");
1596 DEBUG(D_acl) debug_printf("Malware scan: issuing %s local-path scan [%s]\n",
1597 scanner_name, clamd_options);
1599 if (send(sock, file_name, Ustrlen(file_name), 0) < 0) {
1601 log_write(0, LOG_MAIN|LOG_PANIC,"malware acl condition: clamd: unable to write to socket (%s)",
1606 /* Do not shut down the socket for writing; a user report noted that
1607 * clamd 0.70 does not react well to this. */
1609 /* Commands have been sent, no matter which scan method or connection
1610 * type we're using; now just read the result, independent of method. */
1612 /* Read the result */
1613 memset(av_buffer, 0, sizeof(av_buffer));
1614 bread = ip_recv(sock, av_buffer, sizeof(av_buffer), MALWARE_TIMEOUT);
1618 log_write(0, LOG_MAIN|LOG_PANIC,
1619 "malware acl condition: clamd: unable to read from socket (%s)",
1624 if (bread == sizeof(av_buffer)) {
1625 log_write(0, LOG_MAIN|LOG_PANIC,
1626 "malware acl condition: clamd: buffer too small");
1630 /* Check the result. ClamAV returns one of two result formats.
1631 In the basic mode, the response is of the form:
1632 infected: -> "<filename>: <virusname> FOUND"
1633 not-infected: -> "<filename>: OK"
1634 error: -> "<filename>: <errcode> ERROR
1635 If the ExtendedDetectionInfo option has been turned on, then we get:
1636 "<filename>: <virusname>(<virushash>:<virussize>) FOUND"
1637 for the infected case. Compare:
1638 /tmp/eicar.com: Eicar-Test-Signature FOUND
1639 /tmp/eicar.com: Eicar-Test-Signature(44d88612fea8a8f36de82e1278abb02f:68) FOUND
1641 In the streaming case, clamd uses the filename "stream" which you should
1642 be able to verify with { ktrace clamdscan --stream /tmp/eicar.com }. (The
1643 client app will replace "stream" with the original filename before returning
1644 results to stdout, but the trace shows the data).
1646 We will assume that the pathname passed to clamd from Exim does not contain
1647 a colon. We will have whined loudly above if the eml_filename does (and we're
1648 passing a filename to clamd). */
1650 if (!(*av_buffer)) {
1651 log_write(0, LOG_MAIN|LOG_PANIC,
1652 "malware acl condition: clamd: ClamAV returned null");
1656 /* strip newline at the end (won't be present for zINSTREAM)
1657 (also any trailing whitespace, which shouldn't exist, but we depend upon
1658 this below, so double-check) */
1659 p = av_buffer + Ustrlen(av_buffer) - 1;
1660 if (*p == '\n') *p = '\0';
1662 DEBUG(D_acl) debug_printf("Malware response: %s\n", av_buffer);
1664 while (isspace(*--p) && (p > av_buffer))
1669 /* colon in returned output? */
1670 if((p = Ustrchr(av_buffer,':')) == NULL) {
1671 log_write(0, LOG_MAIN|LOG_PANIC,
1672 "malware acl condition: clamd: ClamAV returned malformed result (missing colon): %s",
1677 /* strip filename */
1678 while (*p && isspace(*++p)) /**/;
1681 /* It would be bad to encounter a virus with "FOUND" in part of the name,
1682 but we should at least be resistant to it. */
1683 p = Ustrrchr(vname, ' ');
1689 if (Ustrcmp(result_tag, "FOUND") == 0) {
1690 /* p should still be the whitespace before the result_tag */
1691 while (isspace(*p)) --p;
1693 /* Strip off the extended information too, which will be in parens
1694 after the virus name, with no intervening whitespace. */
1696 /* "(hash:size)", so previous '(' will do; if not found, we have
1697 a curious virus name, but not an error. */
1698 p = Ustrrchr(vname, '(');
1702 Ustrncpy(malware_name_buffer, vname, sizeof(malware_name_buffer)-1);
1703 malware_name = malware_name_buffer;
1704 DEBUG(D_acl) debug_printf("Malware found, name \"%s\"\n", malware_name);
1706 } else if (Ustrcmp(result_tag, "ERROR") == 0) {
1707 log_write(0, LOG_MAIN|LOG_PANIC,
1708 "malware acl condition: clamd: ClamAV returned: %s",
1712 } else if (Ustrcmp(result_tag, "OK") == 0) {
1713 /* Everything should be OK */
1714 malware_name = NULL;
1715 DEBUG(D_acl) debug_printf("Malware not found\n");
1718 log_write(0, LOG_MAIN|LOG_PANIC,
1719 "malware acl condition: clamd: unparseable response from ClamAV: {%s}",
1726 /* ----------------------------------------------------------------------- */
1729 /* "mksd" scanner type --------------------------------------------------- */
1730 else if (strcmpic(scanner_name,US"mksd") == 0) {
1731 uschar *mksd_options;
1732 char *mksd_options_end;
1733 uschar mksd_options_buffer[32];
1734 int mksd_maxproc = 1; /* default, if no option supplied */
1735 struct sockaddr_un server;
1739 if ((mksd_options = string_nextinlist(&av_scanner_work, &sep,
1740 mksd_options_buffer,
1741 sizeof(mksd_options_buffer))) != NULL) {
1742 mksd_maxproc = (int) strtol(CS mksd_options, &mksd_options_end, 10);
1743 if ((*mksd_options == '\0') || (*mksd_options_end != '\0') ||
1744 (mksd_maxproc < 1) || (mksd_maxproc > 32)) {
1745 log_write(0, LOG_MAIN|LOG_PANIC,
1746 "malware acl condition: mksd: invalid option '%s'", mksd_options);
1751 /* open the mksd socket */
1752 sock = socket(AF_UNIX, SOCK_STREAM, 0);
1754 log_write(0, LOG_MAIN|LOG_PANIC,
1755 "malware acl condition: 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) {
1762 log_write(0, LOG_MAIN|LOG_PANIC,
1763 "malware acl condition: unable to connect to mksd UNIX socket (/var/run/mksd/socket). errno=%d", errno);
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 log_write(0, LOG_MAIN|LOG_PANIC,
1781 "malware condition: unknown scanner type '%s'", scanner_name);
1784 /* ----------------------------------------------------------------------- */
1786 /* set "been here, done that" marker */
1790 /* match virus name against pattern (caseless ------->----------v) */
1791 if ( (malware_name != NULL) &&
1792 (regex_match_and_setup(re, malware_name, 0, -1)) ) {
1793 DEBUG(D_acl) debug_printf("Matched regex to malware [%s] [%s]\n", malware_regex, malware_name);
1802 /* simple wrapper for reading lines from sockets */
1803 int 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>
1823 static int mksd_writev (int sock, struct iovec *iov, int iovcnt)
1829 i = writev (sock, iov, iovcnt);
1830 while ((i < 0) && (errno == EINTR));
1833 log_write(0, LOG_MAIN|LOG_PANIC,
1834 "malware acl condition: 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;
1852 static int mksd_read_lines (int sock, uschar *av_buffer, int av_buffer_size)
1858 if ((i = recv (sock, av_buffer+offset, av_buffer_size-offset, 0)) <= 0) {
1860 log_write(0, LOG_MAIN|LOG_PANIC,
1861 "malware acl condition: 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 log_write(0, LOG_MAIN|LOG_PANIC,
1870 "malware acl condition: malformed reply received from mksd");
1873 } while (av_buffer[offset-1] != '\n');
1875 av_buffer[offset] = '\0';
1879 static int mksd_parse_line (char *line)
1890 if ((p = strchr (line, '\n')) != NULL)
1892 log_write(0, LOG_MAIN|LOG_PANIC,
1893 "malware acl condition: mksd scanner failed: %s", line);
1897 if ((p = strchr (line, '\n')) != NULL) {
1899 if (((p-line) > 5) && ((p-line) < sizeof (malware_name_buffer)) && (line[3] == ' '))
1900 if (((p = strchr (line+4, ' ')) != NULL) && ((p-line) > 4)) {
1902 Ustrcpy (malware_name_buffer, line+4);
1903 malware_name = malware_name_buffer;
1907 log_write(0, LOG_MAIN|LOG_PANIC,
1908 "malware acl condition: malformed reply received from mksd: %s", line);
1913 static int mksd_scan_packed(int sock, uschar *scan_filename)
1915 struct iovec iov[3];
1916 const char *cmd = "MSQ\n";
1917 uschar av_buffer[1024];
1919 iov[0].iov_base = (void *) cmd;
1921 iov[1].iov_base = CS scan_filename;
1922 iov[1].iov_len = Ustrlen(scan_filename);
1923 iov[2].iov_base = (void *) (cmd + 3);
1926 if (mksd_writev (sock, iov, 3) < 0)
1929 if (mksd_read_lines (sock, av_buffer, sizeof (av_buffer)) < 0)
1934 return mksd_parse_line (CS av_buffer);