1 /* $Cambridge: exim/src/src/malware.c,v 1.21 2010/06/07 00:12:42 pdp Exp $ */
3 /*************************************************
4 * Exim - an Internet mail transport agent *
5 *************************************************/
7 /* Copyright (c) Tom Kistner <tom@duncanthrax.net> 2003-???? */
10 /* Code for calling virus (malware) scanners. Called from acl.c. */
13 #ifdef WITH_CONTENT_SCAN
15 /* declaration of private routines */
16 static int mksd_scan_packed(int sock, uschar *scan_filename);
17 static int malware_internal(uschar **listptr, uschar *eml_filename, BOOL faking);
19 /* SHUT_WR seems to be undefined on Unixware? */
25 #define MALWARE_TIMEOUT 120
28 #define DRWEBD_SCAN_CMD (1) /* scan file, buffer or diskfile */
29 #define DRWEBD_RETURN_VIRUSES (1<<0) /* ask daemon return to us viruses names from report */
30 #define DRWEBD_IS_MAIL (1<<19) /* say to daemon that format is "archive MAIL" */
32 #define DERR_READ_ERR (1<<0) /* read error */
33 #define DERR_NOMEMORY (1<<2) /* no memory */
34 #define DERR_TIMEOUT (1<<9) /* scan timeout has run out */
35 #define DERR_BAD_CALL (1<<15) /* wrong command */
37 /* Routine to check whether a system is big- or litte-endian.
38 Ripped from http://www.faqs.org/faqs/graphics/fileformats-faq/part4/section-7.html
39 Needed for proper kavdaemon implementation. Sigh. */
40 #define BIG_MY_ENDIAN 0
41 #define LITTLE_MY_ENDIAN 1
42 int test_byte_order(void);
43 int test_byte_order() {
44 short int word = 0x0001;
45 char *byte = (char *) &word;
46 return(byte[0] ? LITTLE_MY_ENDIAN : BIG_MY_ENDIAN);
49 uschar malware_name_buffer[256];
52 /* Gross hacks for the -bmalware option; perhaps we should just create
53 the scan directory normally for that case, but look into rigging up the
54 needed header variables if not already set on the command-line? */
55 extern int spool_mbox_ok;
56 extern uschar spooled_message_id[17];
58 /*************************************************
59 * Scan an email for malware *
60 *************************************************/
62 /* This is the normal interface for scanning an email, which doesn't need a
63 filename; it's a wrapper around the malware_file function.
66 listptr the list of options to the "malware = ..." ACL condition
68 Returns: Exim message processing code (OK, FAIL, DEFER, ...)
69 where true means malware was found (condition applies)
71 int malware(uschar **listptr) {
72 uschar scan_filename[1024];
75 fits = string_format(scan_filename, sizeof(scan_filename),
76 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 return malware_internal(listptr, scan_filename, FALSE);
88 /*************************************************
89 * Scan a file for malware *
90 *************************************************/
92 /* This is a test wrapper for scanning an email, which is not used in
93 normal processing. Scan any file, using the Exim scanning interface.
94 This function tampers with various global variables so is unsafe to use
98 eml_filename a file holding the message to be scanned
100 Returns: Exim message processing code (OK, FAIL, DEFER, ...)
101 where true means malware was found (condition applies)
104 malware_in_file(uschar *eml_filename) {
105 uschar *scan_options[2];
106 uschar message_id_buf[64];
109 scan_options[0] = US"*";
110 scan_options[1] = NULL;
112 /* spool_mbox() assumes various parameters exist, when creating
113 the relevant directory and the email within */
114 (void) string_format(message_id_buf, sizeof(message_id_buf),
115 "dummy-%d", pseudo_random_number(INT_MAX));
116 message_id = message_id_buf;
117 sender_address = US"malware-sender@example.net";
119 recipients_list = NULL;
120 receive_add_recipient(US"malware-victim@example.net", -1);
121 enable_dollar_recipients = TRUE;
123 ret = malware_internal(scan_options, eml_filename, TRUE);
125 Ustrncpy(spooled_message_id, message_id, sizeof(spooled_message_id));
127 /* don't set no_mbox_unspool; at present, there's no way for it to become
128 set, but if that changes, then it should apply to these tests too */
135 /*************************************************
136 * Scan content for malware *
137 *************************************************/
139 /* This is an internal interface for scanning an email; the normal interface
140 is via malware(), or there's malware_in_file() used for testing/debugging.
143 listptr the list of options to the "malware = ..." ACL condition
144 eml_filename the file holding the email to be scanned
145 faking whether or not we're faking this up for the -bmalware test
147 Returns: Exim message processing code (OK, FAIL, DEFER, ...)
148 where true means malware was found (condition applies)
150 static int malware_internal(uschar **listptr, uschar *eml_filename, BOOL faking) {
152 uschar *list = *listptr;
153 uschar *av_scanner_work = av_scanner;
154 uschar *scanner_name;
155 uschar scanner_name_buffer[16];
156 uschar *malware_regex;
157 uschar malware_regex_buffer[64];
158 uschar malware_regex_default[] = ".+";
159 unsigned long mbox_size;
163 const uschar *rerror;
165 /* make sure the eml mbox file is spooled up */
166 mbox_file = spool_mbox(&mbox_size, faking ? eml_filename : NULL);
167 if (mbox_file == NULL) {
168 /* error while spooling */
169 log_write(0, LOG_MAIN|LOG_PANIC,
170 "malware acl condition: error while creating mbox spool file");
173 /* none of our current scanners need the mbox
174 file as a stream, so we can close it right away */
175 (void)fclose(mbox_file);
177 /* extract the malware regex to match against from the option list */
178 if ((malware_regex = string_nextinlist(&list, &sep,
179 malware_regex_buffer,
180 sizeof(malware_regex_buffer))) != NULL) {
182 /* parse 1st option */
183 if ( (strcmpic(malware_regex,US"false") == 0) ||
184 (Ustrcmp(malware_regex,"0") == 0) ) {
185 /* explicitly no matching */
189 /* special cases (match anything except empty) */
190 if ( (strcmpic(malware_regex,US"true") == 0) ||
191 (Ustrcmp(malware_regex,"*") == 0) ||
192 (Ustrcmp(malware_regex,"1") == 0) ) {
193 malware_regex = malware_regex_default;
197 /* empty means "don't match anything" */
201 /* Reset sep that is set by previous string_nextinlist() call */
204 /* compile the regex, see if it works */
205 re = pcre_compile(CS malware_regex, PCRE_COPT, (const char **)&rerror, &roffset, NULL);
207 log_write(0, LOG_MAIN|LOG_PANIC,
208 "malware acl condition: regular expression error in '%s': %s at offset %d", malware_regex, rerror, roffset);
212 /* if av_scanner starts with a dollar, expand it first */
213 if (*av_scanner == '$') {
214 av_scanner_work = expand_string(av_scanner);
215 if (av_scanner_work == NULL) {
216 log_write(0, LOG_MAIN|LOG_PANIC,
217 "malware acl condition: av_scanner starts with $, but expansion failed: %s", expand_string_message);
221 debug_printf("Expanded av_scanner global: %s\n", av_scanner_work);
222 /* disable result caching in this case */
228 /* Do not scan twice. */
229 if (malware_ok == 0) {
231 /* find the scanner type from the av_scanner option */
232 if ((scanner_name = string_nextinlist(&av_scanner_work, &sep,
234 sizeof(scanner_name_buffer))) == NULL) {
235 /* no scanner given */
236 log_write(0, LOG_MAIN|LOG_PANIC,
237 "malware acl condition: av_scanner configuration variable is empty");
241 /* "f-protd" scanner type ----------------------------------------------- */
242 if (strcmpic(scanner_name, US"f-protd") == 0) {
243 uschar *fp_options, *fp_scan_option;
244 uschar fp_scan_option_buffer[1024];
245 uschar fp_options_buffer[1024];
246 uschar fp_options_default[] = "localhost 10200-10204";
247 uschar hostname[256];
248 unsigned int port, portlow, porthigh, connect_ok=0, detected=0, par_count = 0;
252 uschar scanrequest[2048], buf[32768], *strhelper, *strhelper2;
254 if ((fp_options = string_nextinlist(&av_scanner_work, &sep,
255 fp_options_buffer, sizeof(fp_options_buffer))) == NULL) {
256 /* no options supplied, use default options */
257 fp_options = fp_options_default;
260 /* extract host and port part */
261 if ( sscanf(CS fp_options, "%s %u-%u", hostname, &portlow, &porthigh) != 3 ) {
262 if ( sscanf(CS fp_options, "%s %u", hostname, &portlow) != 2 ) {
263 log_write(0, LOG_MAIN|LOG_PANIC,
264 "malware acl condition: f-protd: invalid socket '%s'", fp_options);
270 /* Lookup the host */
271 if((he = gethostbyname(CS hostname)) == 0) {
272 log_write(0, LOG_MAIN|LOG_PANIC,
273 "malware acl condition: f-protd: failed to lookup host '%s'", hostname);
277 in = *(struct in_addr *) he->h_addr_list[0];
281 /* Open the f-protd TCP socket */
282 if ( (sock = ip_socket(SOCK_STREAM, AF_INET)) < 0) {
283 log_write(0, LOG_MAIN|LOG_PANIC,
284 "malware acl condition: f-protd: unable to acquire socket (%s)",
289 /* Try to connect to all portslow-high until connection is established */
290 for (port = portlow; !connect_ok && port < porthigh; port++) {
291 if (ip_connect(sock, AF_INET, (uschar*)inet_ntoa(in), port, 5) >= 0) {
297 log_write(0, LOG_MAIN|LOG_PANIC,
298 "malware acl condition: f-protd: connection to %s, port %u-%u failed (%s)",
299 inet_ntoa(in), portlow, porthigh, strerror(errno));
304 DEBUG(D_acl) debug_printf("Malware scan: issuing %s GET\n", scanner_name);
305 (void)string_format(scanrequest, 1024, CS"GET %s", eml_filename);
307 while ((fp_scan_option = string_nextinlist(&av_scanner_work, &sep,
308 fp_scan_option_buffer, sizeof(fp_scan_option_buffer))) != NULL) {
310 Ustrcat(scanrequest, "%20");
312 Ustrcat(scanrequest, "?");
314 Ustrcat(scanrequest, fp_scan_option);
317 Ustrcat(scanrequest, " HTTP/1.0\r\n\r\n");
319 /* send scan request */
320 if (send(sock, &scanrequest, Ustrlen(scanrequest)+1, 0) < 0) {
322 log_write(0, LOG_MAIN|LOG_PANIC,
323 "malware acl condition: f-protd: unable to send command to socket (%s)", scanrequest);
327 /* We get a lot of empty lines, so we need this hack to check for any data at all */
328 while( recv(sock, buf, 1, MSG_PEEK) > 0 ) {
329 if ( recv_line(sock, buf, 32768) > 0) {
330 if ( Ustrstr(buf, US"<detected type=\"") != NULL ) {
332 } else if ( detected && (strhelper = Ustrstr(buf, US"<name>")) ) {
333 if ((strhelper2 = Ustrstr(buf, US"</name>")) != NULL) {
335 Ustrcpy(malware_name_buffer, strhelper + 6);
337 } else if ( Ustrstr(buf, US"<summary code=\"") ) {
338 if ( Ustrstr(buf, US"<summary code=\"11\">") ) {
339 malware_name = malware_name_buffer;
348 /* "drweb" scanner type ----------------------------------------------- */
349 /* v0.1 - added support for tcp sockets */
350 /* v0.0 - initial release -- support for unix sockets */
351 else if (strcmpic(scanner_name,US"drweb") == 0) {
352 uschar *drweb_options;
353 uschar drweb_options_buffer[1024];
354 uschar drweb_options_default[] = "/usr/local/drweb/run/drwebd.sock";
355 struct sockaddr_un server;
356 int sock, result, ovector[30];
357 unsigned int port, fsize;
358 uschar tmpbuf[1024], *drweb_fbuf;
359 uschar drweb_match_string[128];
360 int drweb_rc, drweb_cmd, drweb_flags = 0x0000, drweb_fd,
361 drweb_vnum, drweb_slen, drweb_fin = 0x0000;
363 uschar hostname[256];
368 if ((drweb_options = string_nextinlist(&av_scanner_work, &sep,
369 drweb_options_buffer, sizeof(drweb_options_buffer))) == NULL) {
370 /* no options supplied, use default options */
371 drweb_options = drweb_options_default;
374 if (*drweb_options != '/') {
376 /* extract host and port part */
377 if( sscanf(CS drweb_options, "%s %u", hostname, &port) != 2 ) {
378 log_write(0, LOG_MAIN|LOG_PANIC,
379 "malware acl condition: drweb: invalid socket '%s'", drweb_options);
383 /* Lookup the host */
384 if((he = gethostbyname(CS hostname)) == 0) {
385 log_write(0, LOG_MAIN|LOG_PANIC,
386 "malware acl condition: drweb: failed to lookup host '%s'", hostname);
390 in = *(struct in_addr *) he->h_addr_list[0];
392 /* Open the drwebd TCP socket */
393 if ( (sock = ip_socket(SOCK_STREAM, AF_INET)) < 0) {
394 log_write(0, LOG_MAIN|LOG_PANIC,
395 "malware acl condition: drweb: unable to acquire socket (%s)",
400 if (ip_connect(sock, AF_INET, (uschar*)inet_ntoa(in), port, 5) < 0) {
402 log_write(0, LOG_MAIN|LOG_PANIC,
403 "malware acl condition: drweb: connection to %s, port %u failed (%s)",
404 inet_ntoa(in), port, strerror(errno));
408 /* prepare variables */
409 drweb_cmd = htonl(DRWEBD_SCAN_CMD);
410 drweb_flags = htonl(DRWEBD_RETURN_VIRUSES | DRWEBD_IS_MAIL);
413 drweb_fd = open(CS eml_filename, O_RDONLY);
414 if (drweb_fd == -1) {
416 log_write(0, LOG_MAIN|LOG_PANIC,
417 "malware acl condition: drweb: can't open spool file %s: %s",
418 eml_filename, strerror(errno));
421 fsize = lseek(drweb_fd, 0, SEEK_END);
424 (void)close(drweb_fd);
425 log_write(0, LOG_MAIN|LOG_PANIC,
426 "malware acl condition: drweb: can't seek spool file %s: %s",
427 eml_filename, strerror(errno));
430 drweb_slen = htonl(fsize);
431 lseek(drweb_fd, 0, SEEK_SET);
433 DEBUG(D_acl) debug_printf("Malware scan: issuing %s remote scan [%s %u]\n",
434 scanner_name, hostname, port);
436 /* send scan request */
437 if ((send(sock, &drweb_cmd, sizeof(drweb_cmd), 0) < 0) ||
438 (send(sock, &drweb_flags, sizeof(drweb_flags), 0) < 0) ||
439 (send(sock, &drweb_fin, sizeof(drweb_fin), 0) < 0) ||
440 (send(sock, &drweb_slen, sizeof(drweb_slen), 0) < 0)) {
442 (void)close(drweb_fd);
443 log_write(0, LOG_MAIN|LOG_PANIC,
444 "malware acl condition: drweb: unable to send commands to socket (%s)", drweb_options);
448 drweb_fbuf = (uschar *) malloc (fsize);
451 (void)close(drweb_fd);
452 log_write(0, LOG_MAIN|LOG_PANIC,
453 "malware acl condition: drweb: unable to allocate memory %u for file (%s)",
454 fsize, eml_filename);
458 result = read (drweb_fd, drweb_fbuf, fsize);
461 (void)close(drweb_fd);
463 log_write(0, LOG_MAIN|LOG_PANIC,
464 "malware acl condition: drweb: can't read spool file %s: %s",
465 eml_filename, strerror(errno));
468 (void)close(drweb_fd);
470 /* send file body to socket */
471 if (send(sock, drweb_fbuf, fsize, 0) < 0) {
474 log_write(0, LOG_MAIN|LOG_PANIC,
475 "malware acl condition: drweb: unable to send file body to socket (%s)", drweb_options);
478 (void)close(drweb_fd);
481 /* open the drwebd UNIX socket */
482 sock = socket(AF_UNIX, SOCK_STREAM, 0);
484 log_write(0, LOG_MAIN|LOG_PANIC,
485 "malware acl condition: drweb: can't open UNIX socket");
488 server.sun_family = AF_UNIX;
489 Ustrcpy(server.sun_path, drweb_options);
490 if (connect(sock, (struct sockaddr *) &server, sizeof(struct sockaddr_un)) < 0) {
492 log_write(0, LOG_MAIN|LOG_PANIC,
493 "malware acl condition: drweb: unable to connect to socket (%s). errno=%d", drweb_options, errno);
497 /* prepare variables */
498 drweb_cmd = htonl(DRWEBD_SCAN_CMD);
499 drweb_flags = htonl(DRWEBD_RETURN_VIRUSES | DRWEBD_IS_MAIL);
500 drweb_slen = htonl(Ustrlen(eml_filename));
502 DEBUG(D_acl) debug_printf("Malware scan: issuing %s local scan [%s]\n",
503 scanner_name, drweb_options);
505 /* send scan request */
506 if ((send(sock, &drweb_cmd, sizeof(drweb_cmd), 0) < 0) ||
507 (send(sock, &drweb_flags, sizeof(drweb_flags), 0) < 0) ||
508 (send(sock, &drweb_slen, sizeof(drweb_slen), 0) < 0) ||
509 (send(sock, eml_filename, Ustrlen(eml_filename), 0) < 0) ||
510 (send(sock, &drweb_fin, sizeof(drweb_fin), 0) < 0)) {
512 log_write(0, LOG_MAIN|LOG_PANIC,
513 "malware acl condition: drweb: unable to send commands to socket (%s)", drweb_options);
518 /* wait for result */
519 if ((bread = recv(sock, &drweb_rc, sizeof(drweb_rc), 0) != sizeof(drweb_rc))) {
521 log_write(0, LOG_MAIN|LOG_PANIC,
522 "malware acl condition: drweb: unable to read return code");
525 drweb_rc = ntohl(drweb_rc);
527 if ((bread = recv(sock, &drweb_vnum, sizeof(drweb_vnum), 0) != sizeof(drweb_vnum))) {
529 log_write(0, LOG_MAIN|LOG_PANIC,
530 "malware acl condition: drweb: unable to read the number of viruses");
533 drweb_vnum = ntohl(drweb_vnum);
535 /* "virus(es) found" if virus number is > 0 */
539 uschar pre_malware_nb[256];
541 malware_name = malware_name_buffer;
543 /* setup default virus name */
544 Ustrcpy(malware_name_buffer,"unknown");
546 /* read and concatenate virus names into one string */
547 for (i=0;i<drweb_vnum;i++)
549 /* read the size of report */
550 if ((bread = recv(sock, &drweb_slen, sizeof(drweb_slen), 0) != sizeof(drweb_slen))) {
552 log_write(0, LOG_MAIN|LOG_PANIC,
553 "malware acl condition: drweb: cannot read report size");
556 drweb_slen = ntohl(drweb_slen);
558 /* read report body */
559 if ((bread = recv(sock, tmpbuf, drweb_slen, 0)) != drweb_slen) {
561 log_write(0, LOG_MAIN|LOG_PANIC,
562 "malware acl condition: drweb: cannot read report string");
565 tmpbuf[drweb_slen] = '\0';
567 /* set up match regex, depends on retcode */
568 Ustrcpy(drweb_match_string, "infected\\swith\\s*(.+?)$");
570 drweb_re = pcre_compile( CS drweb_match_string,
572 (const char **)&rerror,
576 /* try matcher on the line, grab substring */
577 result = pcre_exec(drweb_re, NULL, CS tmpbuf, Ustrlen(tmpbuf), 0, 0, ovector, 30);
579 pcre_copy_substring(CS tmpbuf, ovector, result, 1, CS pre_malware_nb, 255);
581 /* the first name we just copy to malware_name */
583 Ustrcpy(CS malware_name_buffer, CS pre_malware_nb);
585 /* concatenate each new virus name to previous */
586 int slen = Ustrlen(malware_name_buffer);
587 if (slen < (slen+Ustrlen(pre_malware_nb))) {
588 Ustrcat(malware_name_buffer, "/");
589 Ustrcat(malware_name_buffer, pre_malware_nb);
595 char *drweb_s = NULL;
597 if (drweb_rc & DERR_READ_ERR) drweb_s = "read error";
598 if (drweb_rc & DERR_NOMEMORY) drweb_s = "no memory";
599 if (drweb_rc & DERR_TIMEOUT) drweb_s = "timeout";
600 if (drweb_rc & DERR_BAD_CALL) drweb_s = "wrong command";
601 /* retcodes DERR_SYMLINK, DERR_NO_REGFILE, DERR_SKIPPED.
602 * DERR_TOO_BIG, DERR_TOO_COMPRESSED, DERR_SPAM,
603 * DERR_CRC_ERROR, DERR_READSOCKET, DERR_WRITE_ERR
604 * and others are ignored */
606 log_write(0, LOG_MAIN|LOG_PANIC,
607 "malware acl condition: drweb: drweb daemon retcode 0x%x (%s)", drweb_rc, drweb_s);
616 /* ----------------------------------------------------------------------- */
617 else if (strcmpic(scanner_name,US"aveserver") == 0) {
619 uschar kav_options_buffer[1024];
620 uschar kav_options_default[] = "/var/run/aveserver";
622 struct sockaddr_un server;
626 if ((kav_options = string_nextinlist(&av_scanner_work, &sep,
628 sizeof(kav_options_buffer))) == NULL) {
629 /* no options supplied, use default options */
630 kav_options = kav_options_default;
633 /* open the aveserver socket */
634 sock = socket(AF_UNIX, SOCK_STREAM, 0);
636 log_write(0, LOG_MAIN|LOG_PANIC,
637 "malware acl condition: can't open UNIX socket.");
640 server.sun_family = AF_UNIX;
641 Ustrcpy(server.sun_path, kav_options);
642 if (connect(sock, (struct sockaddr *) &server, sizeof(struct sockaddr_un)) < 0) {
644 log_write(0, LOG_MAIN|LOG_PANIC,
645 "malware acl condition: unable to connect to aveserver UNIX socket (%s). errno=%d", kav_options, errno);
649 /* read aveserver's greeting and see if it is ready (2xx greeting) */
650 recv_line(sock, buf, 32768);
653 /* aveserver is having problems */
655 log_write(0, LOG_MAIN|LOG_PANIC,
656 "malware acl condition: aveserver is unavailable (Responded: %s).", ((buf[0] != 0) ? buf : (uschar *)"nothing") );
660 /* prepare our command */
661 (void)string_format(buf, 32768, "SCAN bPQRSTUW %s\r\n", eml_filename);
663 DEBUG(D_acl) debug_printf("Malware scan: issuing %s SCAN\n", scanner_name);
666 if (send(sock, buf, Ustrlen(buf), 0) < 0) {
668 log_write(0, LOG_MAIN|LOG_PANIC,
669 "malware acl condition: unable to write to aveserver UNIX socket (%s)", kav_options);
675 /* read response lines, find malware name and final response */
676 while (recv_line(sock, buf, 32768) > 0) {
677 debug_printf("aveserver: %s\n", buf);
680 } else if (buf[0] == '5') {
681 /* aveserver is having problems */
682 log_write(0, LOG_MAIN|LOG_PANIC,
683 "malware acl condition: unable to scan file %s (Responded: %s).",
687 } else if (Ustrncmp(buf,"322",3) == 0) {
688 uschar *p = Ustrchr(&buf[4],' ');
690 Ustrcpy(malware_name_buffer,&buf[4]);
691 malware_name = malware_name_buffer;
695 /* prepare our command */
696 (void)string_format(buf, 32768, "quit\r\n");
699 if (send(sock, buf, Ustrlen(buf), 0) < 0) {
701 log_write(0, LOG_MAIN|LOG_PANIC,
702 "malware acl condition: unable to write to aveserver UNIX socket (%s)", kav_options);
706 /* read aveserver's greeting and see if it is ready (2xx greeting) */
707 recv_line(sock, buf, 32768);
710 /* aveserver is having problems */
712 log_write(0, LOG_MAIN|LOG_PANIC,
713 "malware acl condition: unable to quit aveserver dialogue (Responded: %s).", ((buf[0] != 0) ? buf : (uschar *)"nothing") );
719 if (result == DEFER) return DEFER;
721 /* "fsecure" scanner type ------------------------------------------------- */
722 else if (strcmpic(scanner_name,US"fsecure") == 0) {
723 uschar *fsecure_options;
724 uschar fsecure_options_buffer[1024];
725 uschar fsecure_options_default[] = "/var/run/.fsav";
726 struct sockaddr_un server;
727 int sock, i, j, bread = 0;
728 uschar file_name[1024];
729 uschar av_buffer[1024];
731 static uschar *cmdoptions[] = { US"CONFIGURE\tARCHIVE\t1\n",
732 US"CONFIGURE\tTIMEOUT\t0\n",
733 US"CONFIGURE\tMAXARCH\t5\n",
734 US"CONFIGURE\tMIME\t1\n" };
737 if ((fsecure_options = string_nextinlist(&av_scanner_work, &sep,
738 fsecure_options_buffer,
739 sizeof(fsecure_options_buffer))) == NULL) {
740 /* no options supplied, use default options */
741 fsecure_options = fsecure_options_default;
744 /* open the fsecure socket */
745 sock = socket(AF_UNIX, SOCK_STREAM, 0);
747 log_write(0, LOG_MAIN|LOG_PANIC,
748 "malware acl condition: unable to open fsecure socket %s (%s)",
749 fsecure_options, strerror(errno));
752 server.sun_family = AF_UNIX;
753 Ustrcpy(server.sun_path, fsecure_options);
754 if (connect(sock, (struct sockaddr *) &server, sizeof(struct sockaddr_un)) < 0) {
756 log_write(0, LOG_MAIN|LOG_PANIC,
757 "malware acl condition: unable to connect to fsecure socket %s (%s)",
758 fsecure_options, strerror(errno));
762 DEBUG(D_acl) debug_printf("Malware scan: issuing %s scan [%s]\n",
763 scanner_name, fsecure_options);
766 memset(av_buffer, 0, sizeof(av_buffer));
767 for (i=0; i != 4; i++) {
768 /* debug_printf("send option \"%s\"",cmdoptions[i]); */
769 if (write(sock, cmdoptions[i], Ustrlen(cmdoptions[i])) < 0) {
771 log_write(0, LOG_MAIN|LOG_PANIC,
772 "malware acl condition: unable to write fsecure option %d to %s (%s)",
773 i, fsecure_options, strerror(errno));
777 bread = ip_recv(sock, av_buffer, sizeof(av_buffer), MALWARE_TIMEOUT);
778 if (bread >0) av_buffer[bread]='\0';
781 log_write(0, LOG_MAIN|LOG_PANIC,
782 "malware acl condition: unable to read fsecure answer %d (%s)", i, strerror(errno));
785 for (j=0;j<bread;j++) if((av_buffer[j]=='\r')||(av_buffer[j]=='\n')) av_buffer[j] ='@';
786 /* debug_printf("read answer %d read=%d \"%s\"\n", i, bread, av_buffer ); */
787 /* while (Ustrstr(av_buffer, "OK\tServer configured.@") == NULL); */
790 /* pass the mailfile to fsecure */
791 (void)string_format(file_name,1024,"SCAN\t%s\n", eml_filename);
792 /* debug_printf("send scan %s",file_name); */
793 if (write(sock, file_name, Ustrlen(file_name)) < 0) {
795 log_write(0, LOG_MAIN|LOG_PANIC,
796 "malware acl condition: unable to write fsecure scan to %s (%s)",
797 fsecure_options, strerror(errno));
802 /* todo also SUSPICION\t */
803 fs_inf = pcre_compile("\\S{0,5}INFECTED\\t[^\\t]*\\t([^\\t]+)\\t\\S*$", PCRE_COPT, (const char **)&rerror, &roffset, NULL);
805 /* read report, linewise */
809 memset(av_buffer, 0, sizeof(av_buffer));
811 bread=ip_recv(sock, &av_buffer[i], 1, MALWARE_TIMEOUT);
814 log_write(0, LOG_MAIN|LOG_PANIC,
815 "malware acl condition: unable to read fsecure result (%s)", strerror(errno));
820 while ((i < sizeof(av_buffer)-1 ) && (av_buffer[i-1] != '\n'));
821 av_buffer[i-1] = '\0';
822 /* debug_printf("got line \"%s\"\n",av_buffer); */
824 /* Really search for virus again? */
825 if (malware_name == NULL) {
826 /* try matcher on the line, grab substring */
827 i = pcre_exec(fs_inf, NULL, CS av_buffer, Ustrlen(av_buffer), 0, 0, ovector, 30);
830 pcre_copy_substring(CS av_buffer, ovector, i, 1, CS malware_name_buffer, 255);
831 malware_name = malware_name_buffer;
835 while (Ustrstr(av_buffer, "OK\tScan ok.") == NULL);
838 /* ----------------------------------------------------------------------- */
840 /* "kavdaemon" scanner type ------------------------------------------------ */
841 else if (strcmpic(scanner_name,US"kavdaemon") == 0) {
843 uschar kav_options_buffer[1024];
844 uschar kav_options_default[] = "/var/run/AvpCtl";
845 struct sockaddr_un server;
849 uschar scanrequest[1024];
850 uschar kav_match_string[128];
852 unsigned long kav_reportlen, bread;
857 if ((kav_options = string_nextinlist(&av_scanner_work, &sep,
859 sizeof(kav_options_buffer))) == NULL) {
860 /* no options supplied, use default options */
861 kav_options = kav_options_default;
864 /* open the kavdaemon socket */
865 sock = socket(AF_UNIX, SOCK_STREAM, 0);
867 log_write(0, LOG_MAIN|LOG_PANIC,
868 "malware acl condition: can't open UNIX socket.");
871 server.sun_family = AF_UNIX;
872 Ustrcpy(server.sun_path, kav_options);
873 if (connect(sock, (struct sockaddr *) &server, sizeof(struct sockaddr_un)) < 0) {
875 log_write(0, LOG_MAIN|LOG_PANIC,
876 "malware acl condition: unable to connect to kavdaemon UNIX socket (%s). errno=%d", kav_options, errno);
880 /* get current date and time, build scan request */
882 /* pdp note: before the eml_filename parameter, this scanned the
883 directory; not finding documentation, so we'll strip off the directory.
884 The side-effect is that the test framework scanning may end up in
885 scanning more than was requested, but for the normal interface, this is
887 strftime(CS tmpbuf, sizeof(tmpbuf), "<0>%d %b %H:%M:%S:%%s", localtime(&t));
888 fits = string_format(scanrequest, 1024,CS tmpbuf, eml_filename);
891 log_write(0, LOG_MAIN|LOG_PANIC,
892 "malware filename does not fit in buffer [malware_internal() kavdaemon]");
894 p = Ustrrchr(scanrequest, '/');
898 DEBUG(D_acl) debug_printf("Malware scan: issuing %s scan [%s]\n",
899 scanner_name, kav_options);
901 /* send scan request */
902 if (send(sock, scanrequest, Ustrlen(scanrequest)+1, 0) < 0) {
904 log_write(0, LOG_MAIN|LOG_PANIC,
905 "malware acl condition: unable to write to kavdaemon UNIX socket (%s)", kav_options);
909 /* wait for result */
910 if ((bread = recv(sock, tmpbuf, 2, 0) != 2)) {
912 log_write(0, LOG_MAIN|LOG_PANIC,
913 "malware acl condition: unable to read 2 bytes from kavdaemon socket.");
917 /* get errorcode from one nibble */
918 if (test_byte_order() == LITTLE_MY_ENDIAN) {
919 kav_rc = tmpbuf[0] & 0x0F;
922 kav_rc = tmpbuf[1] & 0x0F;
925 /* improper kavdaemon configuration */
926 if ( (kav_rc == 5) || (kav_rc == 6) ) {
928 log_write(0, LOG_MAIN|LOG_PANIC,
929 "malware acl condition: please reconfigure kavdaemon to NOT disinfect or remove infected files.");
935 log_write(0, LOG_MAIN|LOG_PANIC,
936 "malware acl condition: kavdaemon reported 'scanning not completed' (code 1).");
942 log_write(0, LOG_MAIN|LOG_PANIC,
943 "malware acl condition: kavdaemon reported 'kavdaemon damaged' (code 7).");
947 /* code 8 is not handled, since it is ambigous. It appears mostly on
948 bounces where part of a file has been cut off */
950 /* "virus found" return codes (2-4) */
951 if ((kav_rc > 1) && (kav_rc < 5)) {
954 /* setup default virus name */
955 Ustrcpy(malware_name_buffer,"unknown");
956 malware_name = malware_name_buffer;
958 if (test_byte_order() == LITTLE_MY_ENDIAN) {
959 report_flag = tmpbuf[1];
962 report_flag = tmpbuf[0];
965 /* read the report, if available */
966 if( report_flag == 1 ) {
967 /* read report size */
968 if ((bread = recv(sock, &kav_reportlen, 4, 0)) != 4) {
970 log_write(0, LOG_MAIN|LOG_PANIC,
971 "malware acl condition: cannot read report size from kavdaemon");
975 /* it's possible that avp returns av_buffer[1] == 1 but the
976 reportsize is 0 (!?) */
977 if (kav_reportlen > 0) {
978 /* set up match regex, depends on retcode */
980 Ustrcpy(kav_match_string, "suspicion:\\s*(.+?)\\s*$");
982 Ustrcpy(kav_match_string, "infected:\\s*(.+?)\\s*$");
984 kav_re = pcre_compile( CS kav_match_string,
986 (const char **)&rerror,
990 /* read report, linewise */
991 while (kav_reportlen > 0) {
996 while ( recv(sock, &tmpbuf[bread], 1, 0) == 1 ) {
998 if ( (tmpbuf[bread] == '\n') || (bread > 1021) ) break;
1002 tmpbuf[bread] = '\0';
1004 /* try matcher on the line, grab substring */
1005 result = pcre_exec(kav_re, NULL, CS tmpbuf, Ustrlen(tmpbuf), 0, 0, ovector, 30);
1007 pcre_copy_substring(CS tmpbuf, ovector, result, 1, CS malware_name_buffer, 255);
1015 /* no virus found */
1016 malware_name = NULL;
1021 /* ----------------------------------------------------------------------- */
1024 /* "cmdline" scanner type ------------------------------------------------ */
1025 else if (strcmpic(scanner_name,US"cmdline") == 0) {
1026 uschar *cmdline_scanner;
1027 uschar cmdline_scanner_buffer[1024];
1028 uschar *cmdline_trigger;
1029 uschar cmdline_trigger_buffer[1024];
1030 const pcre *cmdline_trigger_re;
1031 uschar *cmdline_regex;
1032 uschar cmdline_regex_buffer[1024];
1033 const pcre *cmdline_regex_re;
1034 uschar file_name[1024];
1035 uschar commandline[1024];
1036 void (*eximsigchld)(int);
1037 void (*eximsigpipe)(int);
1038 FILE *scanner_out = NULL;
1039 FILE *scanner_record = NULL;
1040 uschar linebuffer[32767];
1047 /* find scanner command line */
1048 if ((cmdline_scanner = string_nextinlist(&av_scanner_work, &sep,
1049 cmdline_scanner_buffer,
1050 sizeof(cmdline_scanner_buffer))) == NULL) {
1051 /* no command line supplied */
1052 log_write(0, LOG_MAIN|LOG_PANIC,
1053 "malware acl condition: missing commandline specification for cmdline scanner type.");
1057 /* find scanner output trigger */
1058 if ((cmdline_trigger = string_nextinlist(&av_scanner_work, &sep,
1059 cmdline_trigger_buffer,
1060 sizeof(cmdline_trigger_buffer))) == NULL) {
1061 /* no trigger regex supplied */
1062 log_write(0, LOG_MAIN|LOG_PANIC,
1063 "malware acl condition: missing trigger specification for cmdline scanner type.");
1067 /* precompile trigger regex */
1068 cmdline_trigger_re = pcre_compile(CS cmdline_trigger, PCRE_COPT, (const char **)&rerror, &roffset, NULL);
1069 if (cmdline_trigger_re == NULL) {
1070 log_write(0, LOG_MAIN|LOG_PANIC,
1071 "malware acl condition: regular expression error in '%s': %s at offset %d", cmdline_trigger_re, rerror, roffset);
1075 /* find scanner name regex */
1076 if ((cmdline_regex = string_nextinlist(&av_scanner_work, &sep,
1077 cmdline_regex_buffer,
1078 sizeof(cmdline_regex_buffer))) == NULL) {
1079 /* no name regex supplied */
1080 log_write(0, LOG_MAIN|LOG_PANIC,
1081 "malware acl condition: missing virus name regex specification for cmdline scanner type.");
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 log_write(0, LOG_MAIN|LOG_PANIC,
1089 "malware acl condition: regular expression error in '%s': %s at offset %d", cmdline_regex_re, rerror, roffset);
1093 /* prepare scanner call; despite the naming, file_name holds a directory
1094 name which is documented as the value given to %s. */
1095 if (Ustrlen(eml_filename) > sizeof(file_name) - 1)
1097 log_write(0, LOG_MAIN|LOG_PANIC,
1098 "malware filename does not fit in buffer [malware_internal() cmdline]");
1101 Ustrcpy(file_name, eml_filename);
1102 p = Ustrrchr(file_name, '/');
1105 fits = string_format(commandline, sizeof(commandline), CS cmdline_scanner, file_name);
1108 log_write(0, LOG_MAIN|LOG_PANIC,
1109 "cmdline scanner command-line does not fit in buffer");
1113 /* redirect STDERR too */
1114 if (Ustrlen(commandline) + 5 > sizeof(commandline))
1116 log_write(0, LOG_MAIN|LOG_PANIC,
1117 "cmdline scanner command-line does not fit in buffer (STDERR redirect)");
1120 Ustrcat(commandline," 2>&1");
1122 DEBUG(D_acl) debug_printf("Malware scan: issuing %s scan [%s]\n", scanner_name, commandline);
1124 /* store exims signal handlers */
1125 eximsigchld = signal(SIGCHLD,SIG_DFL);
1126 eximsigpipe = signal(SIGPIPE,SIG_DFL);
1128 scanner_out = popen(CS commandline,"r");
1129 if (scanner_out == NULL) {
1130 log_write(0, LOG_MAIN|LOG_PANIC,
1131 "malware acl condition: calling cmdline scanner (%s) failed: %s.", commandline, strerror(errno));
1132 signal(SIGCHLD,eximsigchld);
1133 signal(SIGPIPE,eximsigpipe);
1137 (void)string_format(file_name,1024,"%s/scan/%s/%s_scanner_output", spool_directory, message_id, message_id);
1138 scanner_record = modefopen(file_name,"wb",SPOOL_MODE);
1140 if (scanner_record == NULL) {
1141 log_write(0, LOG_MAIN|LOG_PANIC,
1142 "malware acl condition: opening scanner output file (%s) failed: %s.", file_name, strerror(errno));
1143 pclose(scanner_out);
1144 signal(SIGCHLD,eximsigchld);
1145 signal(SIGPIPE,eximsigpipe);
1149 /* look for trigger while recording output */
1150 while(fgets(CS linebuffer,32767,scanner_out) != NULL) {
1151 if ( Ustrlen(linebuffer) > fwrite(linebuffer, 1, Ustrlen(linebuffer), scanner_record) ) {
1153 log_write(0, LOG_MAIN|LOG_PANIC,
1154 "malware acl condition: short write on scanner output file (%s).", file_name);
1155 pclose(scanner_out);
1156 signal(SIGCHLD,eximsigchld);
1157 signal(SIGPIPE,eximsigpipe);
1160 /* try trigger match */
1161 if (!trigger && regex_match_and_setup(cmdline_trigger_re, linebuffer, 0, -1))
1165 (void)fclose(scanner_record);
1166 pclose(scanner_out);
1167 signal(SIGCHLD,eximsigchld);
1168 signal(SIGPIPE,eximsigpipe);
1171 /* setup default virus name */
1172 Ustrcpy(malware_name_buffer,"unknown");
1173 malware_name = malware_name_buffer;
1175 /* re-open the scanner output file, look for name match */
1176 scanner_record = fopen(CS file_name,"rb");
1177 while(fgets(CS linebuffer,32767,scanner_record) != NULL) {
1179 result = pcre_exec(cmdline_regex_re, NULL, CS linebuffer, Ustrlen(linebuffer), 0, 0, ovector, 30);
1181 pcre_copy_substring(CS linebuffer, ovector, result, 1, CS malware_name_buffer, 255);
1184 (void)fclose(scanner_record);
1187 /* no virus found */
1188 malware_name = NULL;
1191 /* ----------------------------------------------------------------------- */
1194 /* "sophie" scanner type ------------------------------------------------- */
1195 else if (strcmpic(scanner_name,US"sophie") == 0) {
1196 uschar *sophie_options;
1197 uschar sophie_options_buffer[1024];
1198 uschar sophie_options_default[] = "/var/run/sophie";
1200 struct sockaddr_un server;
1203 uschar file_name[1024];
1204 uschar av_buffer[1024];
1206 if ((sophie_options = string_nextinlist(&av_scanner_work, &sep,
1207 sophie_options_buffer,
1208 sizeof(sophie_options_buffer))) == NULL) {
1209 /* no options supplied, use default options */
1210 sophie_options = sophie_options_default;
1213 /* open the sophie socket */
1214 sock = socket(AF_UNIX, SOCK_STREAM, 0);
1216 log_write(0, LOG_MAIN|LOG_PANIC,
1217 "malware acl condition: can't open UNIX socket.");
1220 server.sun_family = AF_UNIX;
1221 Ustrcpy(server.sun_path, sophie_options);
1222 if (connect(sock, (struct sockaddr *) &server, sizeof(struct sockaddr_un)) < 0) {
1224 log_write(0, LOG_MAIN|LOG_PANIC,
1225 "malware acl condition: unable to connect to sophie UNIX socket (%s). errno=%d", sophie_options, errno);
1229 /* pass the scan directory to sophie */
1230 len = Ustrlen(eml_filename) + 1;
1231 if (len > sizeof(file_name))
1234 log_write(0, LOG_MAIN|LOG_PANIC,
1235 "malware filename does not fit in buffer [malware_internal() sophie]");
1238 memcpy(file_name, eml_filename, len);
1239 p = Ustrrchr(file_name, '/');
1243 DEBUG(D_acl) debug_printf("Malware scan: issuing %s scan [%s]\n",
1244 scanner_name, sophie_options);
1246 if (write(sock, file_name, Ustrlen(file_name)) < 0) {
1248 log_write(0, LOG_MAIN|LOG_PANIC,
1249 "malware acl condition: unable to write to sophie UNIX socket (%s)", sophie_options);
1253 (void)write(sock, "\n", 1);
1255 /* wait for result */
1256 memset(av_buffer, 0, sizeof(av_buffer));
1257 if ((!(bread = ip_recv(sock, av_buffer, sizeof(av_buffer), MALWARE_TIMEOUT)) > 0)) {
1259 log_write(0, LOG_MAIN|LOG_PANIC,
1260 "malware acl condition: unable to read from sophie UNIX socket (%s)", sophie_options);
1267 if (av_buffer[0] == '1') {
1268 if (Ustrchr(av_buffer, '\n')) *Ustrchr(av_buffer, '\n') = '\0';
1269 Ustrcpy(malware_name_buffer,&av_buffer[2]);
1270 malware_name = malware_name_buffer;
1272 else if (!strncmp(CS av_buffer, "-1", 2)) {
1273 log_write(0, LOG_MAIN|LOG_PANIC,
1274 "malware acl condition: malware acl condition: sophie reported error");
1278 /* all ok, no virus */
1279 malware_name = NULL;
1282 /* ----------------------------------------------------------------------- */
1285 /* "clamd" scanner type ------------------------------------------------- */
1286 /* This code was originally contributed by David Saez */
1287 /* There are three scanning methods available to us:
1288 * (1) Use the SCAN command, pointing to a file in the filesystem
1289 * (2) Use the STREAM command, send the data on a separate port
1290 * (3) Use the zINSTREAM command, send the data inline
1291 * The zINSTREAM command was introduced with ClamAV 0.95, which marked
1292 * STREAM deprecated; see: http://wiki.clamav.net/bin/view/Main/UpgradeNotes095
1293 * In Exim, we use SCAN if using a Unix-domain socket or explicitly told that
1294 * the TCP-connected daemon is actually local; otherwise we use zINSTREAM unless
1295 * WITH_OLD_CLAMAV_STREAM is defined.
1296 * See Exim bug 926 for details. */
1297 else if (strcmpic(scanner_name,US"clamd") == 0) {
1298 uschar *clamd_options;
1299 uschar clamd_options_buffer[1024];
1300 uschar clamd_options_default[] = "/tmp/clamd";
1301 uschar *p, *vname, *result_tag, *response_end;
1302 struct sockaddr_un server;
1305 uschar file_name[1024];
1306 uschar av_buffer[1024];
1307 uschar hostname[256];
1310 uschar *clamd_options2;
1311 uschar clamd_options2_buffer[1024];
1312 uschar clamd_options2_default[] = "";
1313 uschar *clamav_fbuf;
1314 int clam_fd, result;
1316 BOOL use_scan_command, fits;
1317 #ifdef WITH_OLD_CLAMAV_STREAM
1318 uschar av_buffer2[1024];
1321 uint32_t send_size, send_final_zeroblock;
1324 if ((clamd_options = string_nextinlist(&av_scanner_work, &sep,
1325 clamd_options_buffer,
1326 sizeof(clamd_options_buffer))) == NULL) {
1327 /* no options supplied, use default options */
1328 clamd_options = clamd_options_default;
1330 if ((clamd_options2 = string_nextinlist(&av_scanner_work, &sep,
1331 clamd_options2_buffer,
1332 sizeof(clamd_options2_buffer))) == NULL) {
1333 clamd_options2 = clamd_options2_default;
1336 if ((*clamd_options == '/') || (strcmpic(clamd_options2,US"local") == 0))
1337 use_scan_command = TRUE;
1339 use_scan_command = FALSE;
1341 /* See the discussion of response formats below to see why we really don't
1342 like colons in filenames when passing filenames to ClamAV. */
1343 if (use_scan_command && Ustrchr(eml_filename, ':')) {
1344 log_write(0, LOG_MAIN|LOG_PANIC,
1345 "malware acl condition: clamd: local/SCAN mode incompatible with" \
1346 " : in path to email filename [%s]", eml_filename);
1350 /* socket does not start with '/' -> network socket */
1351 if (*clamd_options != '/') {
1353 /* Confirmed in ClamAV source (0.95.3) that the TCPAddr option of clamd
1354 * only supports AF_INET, but we should probably be looking to the
1355 * future and rewriting this to be protocol-independent anyway. */
1357 /* extract host and port part */
1358 if( sscanf(CS clamd_options, "%s %u", hostname, &port) != 2 ) {
1359 log_write(0, LOG_MAIN|LOG_PANIC,
1360 "malware acl condition: clamd: invalid socket '%s'", clamd_options);
1364 /* Lookup the host */
1365 if((he = gethostbyname(CS hostname)) == 0) {
1366 log_write(0, LOG_MAIN|LOG_PANIC,
1367 "malware acl condition: clamd: failed to lookup host '%s'", hostname);
1371 in = *(struct in_addr *) he->h_addr_list[0];
1373 /* Open the ClamAV Socket */
1374 if ( (sock = ip_socket(SOCK_STREAM, AF_INET)) < 0) {
1375 log_write(0, LOG_MAIN|LOG_PANIC,
1376 "malware acl condition: clamd: unable to acquire socket (%s)",
1381 if (ip_connect(sock, AF_INET, (uschar*)inet_ntoa(in), port, 5) < 0) {
1383 log_write(0, LOG_MAIN|LOG_PANIC,
1384 "malware acl condition: clamd: connection to %s, port %u failed (%s)",
1385 inet_ntoa(in), port, strerror(errno));
1390 /* open the local socket */
1391 if ((sock = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
1392 log_write(0, LOG_MAIN|LOG_PANIC,
1393 "malware acl condition: clamd: unable to acquire socket (%s)",
1398 server.sun_family = AF_UNIX;
1399 Ustrcpy(server.sun_path, clamd_options);
1401 if (connect(sock, (struct sockaddr *) &server, sizeof(struct sockaddr_un)) < 0) {
1403 log_write(0, LOG_MAIN|LOG_PANIC,
1404 "malware acl condition: clamd: unable to connect to UNIX socket %s (%s)",
1405 clamd_options, strerror(errno) );
1410 /* have socket in variable "sock"; command to use is semi-independent of
1411 * the socket protocol. We use SCAN if is local (either Unix/local
1412 * domain socket, or explicitly told local) else we stream the data.
1413 * How we stream the data depends upon how we were built. */
1415 if (!use_scan_command) {
1417 #ifdef WITH_OLD_CLAMAV_STREAM
1418 /* "STREAM\n" command, get back a "PORT <N>\n" response, send data to
1419 * that port on a second connection; then in the scan-method-neutral
1420 * part, read the response back on the original connection. */
1422 DEBUG(D_acl) debug_printf("Malware scan: issuing %s old-style remote scan (PORT)\n",
1425 /* Pass the string to ClamAV (7 = "STREAM\n") */
1426 if (send(sock, "STREAM\n", 7, 0) < 0) {
1427 log_write(0, LOG_MAIN|LOG_PANIC,"malware acl condition: clamd: unable to write to socket (%s)",
1432 memset(av_buffer2, 0, sizeof(av_buffer2));
1433 bread = ip_recv(sock, av_buffer2, sizeof(av_buffer2), MALWARE_TIMEOUT);
1436 log_write(0, LOG_MAIN|LOG_PANIC,
1437 "malware acl condition: clamd: unable to read PORT from socket (%s)",
1443 if (bread == sizeof(av_buffer)) {
1444 log_write(0, LOG_MAIN|LOG_PANIC,
1445 "malware acl condition: clamd: buffer too small");
1450 if (!(*av_buffer2)) {
1451 log_write(0, LOG_MAIN|LOG_PANIC,
1452 "malware acl condition: clamd: ClamAV returned null");
1457 av_buffer2[bread] = '\0';
1458 if( sscanf(CS av_buffer2, "PORT %u\n", &port) != 1 ) {
1459 log_write(0, LOG_MAIN|LOG_PANIC,
1460 "malware acl condition: clamd: Expected port information from clamd, got '%s'", av_buffer2);
1465 if ( (sockData = ip_socket(SOCK_STREAM, AF_INET)) < 0) {
1466 log_write(0, LOG_MAIN|LOG_PANIC,
1467 "malware acl condition: clamd: unable to acquire socket (%s)",
1473 if (ip_connect(sockData, AF_INET, (uschar*)inet_ntoa(in), port, 5) < 0) {
1474 log_write(0, LOG_MAIN|LOG_PANIC,
1475 "malware acl condition: clamd: connection to %s, port %u failed (%s)",
1476 inet_ntoa(in), port, strerror(errno));
1477 (void)close(sockData); (void)close(sock);
1481 #define CLOSE_SOCKDATA (void)close(sockData)
1482 #else /* WITH_OLD_CLAMAV_STREAM not defined */
1483 /* New protocol: "zINSTREAM\n" followed by a sequence of <length><data>
1484 chunks, <n> a 4-byte number (network order), terminated by a zero-length
1487 DEBUG(D_acl) debug_printf("Malware scan: issuing %s new-style remote scan (zINSTREAM)\n",
1490 /* Pass the string to ClamAV (10 = "zINSTREAM\0") */
1491 if (send(sock, "zINSTREAM", 10, 0) < 0) {
1492 log_write(0, LOG_MAIN|LOG_PANIC,
1493 "malware acl condition: clamd: unable to send zINSTREAM to socket (%s)",
1499 #define CLOSE_SOCKDATA /**/
1502 /* calc file size */
1503 clam_fd = open(CS eml_filename, O_RDONLY);
1504 if (clam_fd == -1) {
1505 log_write(0, LOG_MAIN|LOG_PANIC,
1506 "malware acl condition: clamd: can't open spool file %s: %s",
1507 eml_filename, strerror(errno));
1508 CLOSE_SOCKDATA; (void)close(sock);
1511 fsize = lseek(clam_fd, 0, SEEK_END);
1513 log_write(0, LOG_MAIN|LOG_PANIC,
1514 "malware acl condition: clamd: can't seek spool file %s: %s",
1515 eml_filename, strerror(errno));
1516 CLOSE_SOCKDATA; (void)close(sock);
1519 lseek(clam_fd, 0, SEEK_SET);
1521 clamav_fbuf = (uschar *) malloc (fsize);
1523 log_write(0, LOG_MAIN|LOG_PANIC,
1524 "malware acl condition: clamd: unable to allocate memory %u for file (%s)",
1525 fsize, eml_filename);
1526 CLOSE_SOCKDATA; (void)close(sock); (void)close(clam_fd);
1530 result = read (clam_fd, clamav_fbuf, fsize);
1532 log_write(0, LOG_MAIN|LOG_PANIC,
1533 "malware acl condition: clamd: can't read spool file %s: %s",
1534 eml_filename, strerror(errno));
1535 CLOSE_SOCKDATA; (void)close(sock); (void)close(clam_fd);
1539 (void)close(clam_fd);
1541 /* send file body to socket */
1542 #ifdef WITH_OLD_CLAMAV_STREAM
1543 if (send(sockData, clamav_fbuf, fsize, 0) < 0) {
1544 log_write(0, LOG_MAIN|LOG_PANIC,
1545 "malware acl condition: clamd: unable to send file body to socket (%s:%u)", hostname, port);
1546 CLOSE_SOCKDATA; (void)close(sock);
1551 send_size = htonl(fsize);
1552 send_final_zeroblock = 0;
1553 if ((send(sock, &send_size, sizeof(send_size), 0) < 0) ||
1554 (send(sock, clamav_fbuf, fsize, 0) < 0) ||
1555 (send(sock, &send_final_zeroblock, sizeof(send_final_zeroblock), 0) < 0))
1557 log_write(0, LOG_MAIN|LOG_PANIC,
1558 "malware acl condition: clamd: unable to send file body to socket (%s:%u)", hostname, port);
1568 #undef CLOSE_SOCKDATA
1570 } else { /* use scan command */
1571 /* Send a SCAN command pointing to a filename; then in the then in the
1572 * scan-method-neutral part, read the response back */
1574 /* ================================================================= */
1576 /* Prior to the reworking post-Exim-4.72, this scanned a directory,
1577 which dates to when ClamAV needed us to break apart the email into the
1578 MIME parts (eg, with the now deprecated demime condition coming first).
1579 Some time back, ClamAV gained the ability to deconstruct the emails, so
1580 doing this would actually have resulted in the mail attachments being
1581 scanned twice, in the broken out files and from the original .eml.
1582 Since ClamAV now handles emails (and has for quite some time) we can
1583 just use the email file itself. */
1584 /* Pass the string to ClamAV (7 = "SCAN \n" + \0) */
1585 fits = string_format(file_name, sizeof(file_name), "SCAN %s\n",
1589 log_write(0, LOG_MAIN|LOG_PANIC,
1590 "malware filename does not fit in buffer [malware_internal() clamd]");
1593 DEBUG(D_acl) debug_printf("Malware scan: issuing %s local-path scan [%s]\n",
1594 scanner_name, clamd_options);
1596 if (send(sock, file_name, Ustrlen(file_name), 0) < 0) {
1598 log_write(0, LOG_MAIN|LOG_PANIC,"malware acl condition: clamd: unable to write to socket (%s)",
1603 /* Do not shut down the socket for writing; a user report noted that
1604 * clamd 0.70 does not react well to this. */
1606 /* Commands have been sent, no matter which scan method or connection
1607 * type we're using; now just read the result, independent of method. */
1609 /* Read the result */
1610 memset(av_buffer, 0, sizeof(av_buffer));
1611 bread = ip_recv(sock, av_buffer, sizeof(av_buffer), MALWARE_TIMEOUT);
1615 log_write(0, LOG_MAIN|LOG_PANIC,
1616 "malware acl condition: clamd: unable to read from socket (%s)",
1621 if (bread == sizeof(av_buffer)) {
1622 log_write(0, LOG_MAIN|LOG_PANIC,
1623 "malware acl condition: clamd: buffer too small");
1627 /* Check the result. ClamAV returns one of two result formats.
1628 In the basic mode, the response is of the form:
1629 infected: -> "<filename>: <virusname> FOUND"
1630 not-infected: -> "<filename>: OK"
1631 error: -> "<filename>: <errcode> ERROR
1632 If the ExtendedDetectionInfo option has been turned on, then we get:
1633 "<filename>: <virusname>(<virushash>:<virussize>) FOUND"
1634 for the infected case. Compare:
1635 /tmp/eicar.com: Eicar-Test-Signature FOUND
1636 /tmp/eicar.com: Eicar-Test-Signature(44d88612fea8a8f36de82e1278abb02f:68) FOUND
1638 In the streaming case, clamd uses the filename "stream" which you should
1639 be able to verify with { ktrace clamdscan --stream /tmp/eicar.com }. (The
1640 client app will replace "stream" with the original filename before returning
1641 results to stdout, but the trace shows the data).
1643 We will assume that the pathname passed to clamd from Exim does not contain
1644 a colon. We will have whined loudly above if the eml_filename does (and we're
1645 passing a filename to clamd). */
1647 if (!(*av_buffer)) {
1648 log_write(0, LOG_MAIN|LOG_PANIC,
1649 "malware acl condition: clamd: ClamAV returned null");
1653 /* strip newline at the end (won't be present for zINSTREAM)
1654 (also any trailing whitespace, which shouldn't exist, but we depend upon
1655 this below, so double-check) */
1656 p = av_buffer + Ustrlen(av_buffer) - 1;
1657 if (*p == '\n') *p = '\0';
1659 DEBUG(D_acl) debug_printf("Malware response: %s\n", av_buffer);
1661 while (isspace(*--p) && (p > av_buffer))
1666 /* colon in returned output? */
1667 if((p = Ustrchr(av_buffer,':')) == NULL) {
1668 log_write(0, LOG_MAIN|LOG_PANIC,
1669 "malware acl condition: clamd: ClamAV returned malformed result (missing colon): %s",
1674 /* strip filename */
1675 while (*p && isspace(*++p)) /**/;
1678 /* It would be bad to encounter a virus with "FOUND" in part of the name,
1679 but we should at least be resistant to it. */
1680 p = Ustrrchr(vname, ' ');
1686 if (Ustrcmp(result_tag, "FOUND") == 0) {
1687 /* p should still be the whitespace before the result_tag */
1688 while (isspace(*p)) --p;
1690 /* Strip off the extended information too, which will be in parens
1691 after the virus name, with no intervening whitespace. */
1693 /* "(hash:size)", so previous '(' will do; if not found, we have
1694 a curious virus name, but not an error. */
1695 p = Ustrrchr(vname, '(');
1699 Ustrncpy(malware_name_buffer, vname, sizeof(malware_name_buffer)-1);
1700 malware_name = malware_name_buffer;
1701 DEBUG(D_acl) debug_printf("Malware found, name \"%s\"\n", malware_name);
1703 } else if (Ustrcmp(result_tag, "ERROR") == 0) {
1704 log_write(0, LOG_MAIN|LOG_PANIC,
1705 "malware acl condition: clamd: ClamAV returned: %s",
1709 } else if (Ustrcmp(result_tag, "OK") == 0) {
1710 /* Everything should be OK */
1711 malware_name = NULL;
1712 DEBUG(D_acl) debug_printf("Malware not found\n");
1715 log_write(0, LOG_MAIN|LOG_PANIC,
1716 "malware acl condition: clamd: unparseable response from ClamAV: {%s}",
1723 /* ----------------------------------------------------------------------- */
1726 /* "mksd" scanner type --------------------------------------------------- */
1727 else if (strcmpic(scanner_name,US"mksd") == 0) {
1728 uschar *mksd_options;
1729 char *mksd_options_end;
1730 uschar mksd_options_buffer[32];
1731 int mksd_maxproc = 1; /* default, if no option supplied */
1732 struct sockaddr_un server;
1736 if ((mksd_options = string_nextinlist(&av_scanner_work, &sep,
1737 mksd_options_buffer,
1738 sizeof(mksd_options_buffer))) != NULL) {
1739 mksd_maxproc = (int) strtol(CS mksd_options, &mksd_options_end, 10);
1740 if ((*mksd_options == '\0') || (*mksd_options_end != '\0') ||
1741 (mksd_maxproc < 1) || (mksd_maxproc > 32)) {
1742 log_write(0, LOG_MAIN|LOG_PANIC,
1743 "malware acl condition: mksd: invalid option '%s'", mksd_options);
1748 /* open the mksd socket */
1749 sock = socket(AF_UNIX, SOCK_STREAM, 0);
1751 log_write(0, LOG_MAIN|LOG_PANIC,
1752 "malware acl condition: can't open UNIX socket.");
1755 server.sun_family = AF_UNIX;
1756 Ustrcpy(server.sun_path, "/var/run/mksd/socket");
1757 if (connect(sock, (struct sockaddr *) &server, sizeof(struct sockaddr_un)) < 0) {
1759 log_write(0, LOG_MAIN|LOG_PANIC,
1760 "malware acl condition: unable to connect to mksd UNIX socket (/var/run/mksd/socket). errno=%d", errno);
1764 malware_name = NULL;
1766 DEBUG(D_acl) debug_printf("Malware scan: issuing %s scan\n", scanner_name);
1768 retval = mksd_scan_packed(sock, eml_filename);
1773 /* ----------------------------------------------------------------------- */
1775 /* "unknown" scanner type ------------------------------------------------- */
1777 log_write(0, LOG_MAIN|LOG_PANIC,
1778 "malware condition: unknown scanner type '%s'", scanner_name);
1781 /* ----------------------------------------------------------------------- */
1783 /* set "been here, done that" marker */
1787 /* match virus name against pattern (caseless ------->----------v) */
1788 if ( (malware_name != NULL) &&
1789 (regex_match_and_setup(re, malware_name, 0, -1)) ) {
1790 DEBUG(D_acl) debug_printf("Matched regex to malware [%s] [%s]\n", malware_regex, malware_name);
1799 /* simple wrapper for reading lines from sockets */
1800 int recv_line(int sock, uschar *buffer, int size) {
1803 memset(buffer,0,size);
1805 while(recv(sock,p,1,0) > -1) {
1806 if ((p-buffer) > (size-2)) break;
1807 if (*p == '\n') break;
1808 if (*p != '\r') p++;
1816 /* ============= private routines for the "mksd" scanner type ============== */
1818 #include <sys/uio.h>
1820 static int mksd_writev (int sock, struct iovec *iov, int iovcnt)
1826 i = writev (sock, iov, iovcnt);
1827 while ((i < 0) && (errno == EINTR));
1830 log_write(0, LOG_MAIN|LOG_PANIC,
1831 "malware acl condition: unable to write to mksd UNIX socket (/var/run/mksd/socket)");
1836 if (i >= iov->iov_len) {
1843 iov->iov_base = CS iov->iov_base + i;
1849 static int mksd_read_lines (int sock, uschar *av_buffer, int av_buffer_size)
1855 if ((i = recv (sock, av_buffer+offset, av_buffer_size-offset, 0)) <= 0) {
1857 log_write(0, LOG_MAIN|LOG_PANIC,
1858 "malware acl condition: unable to read from mksd UNIX socket (/var/run/mksd/socket)");
1863 /* offset == av_buffer_size -> buffer full */
1864 if (offset == av_buffer_size) {
1866 log_write(0, LOG_MAIN|LOG_PANIC,
1867 "malware acl condition: malformed reply received from mksd");
1870 } while (av_buffer[offset-1] != '\n');
1872 av_buffer[offset] = '\0';
1876 static int mksd_parse_line (char *line)
1887 if ((p = strchr (line, '\n')) != NULL)
1889 log_write(0, LOG_MAIN|LOG_PANIC,
1890 "malware acl condition: mksd scanner failed: %s", line);
1894 if ((p = strchr (line, '\n')) != NULL) {
1896 if (((p-line) > 5) && ((p-line) < sizeof (malware_name_buffer)) && (line[3] == ' '))
1897 if (((p = strchr (line+4, ' ')) != NULL) && ((p-line) > 4)) {
1899 Ustrcpy (malware_name_buffer, line+4);
1900 malware_name = malware_name_buffer;
1904 log_write(0, LOG_MAIN|LOG_PANIC,
1905 "malware acl condition: malformed reply received from mksd: %s", line);
1910 static int mksd_scan_packed(int sock, uschar *scan_filename)
1912 struct iovec iov[3];
1913 char *cmd = "MSQ\n";
1914 uschar av_buffer[1024];
1916 iov[0].iov_base = cmd;
1918 iov[1].iov_base = CS scan_filename;
1919 iov[1].iov_len = Ustrlen(scan_filename);
1920 iov[2].iov_base = 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);