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", vaguely_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 */
134 /* silence static analysis tools */
141 /*************************************************
142 * Scan content for malware *
143 *************************************************/
145 /* This is an internal interface for scanning an email; the normal interface
146 is via malware(), or there's malware_in_file() used for testing/debugging.
149 listptr the list of options to the "malware = ..." ACL condition
150 eml_filename the file holding the email to be scanned
151 faking whether or not we're faking this up for the -bmalware test
153 Returns: Exim message processing code (OK, FAIL, DEFER, ...)
154 where true means malware was found (condition applies)
156 static int malware_internal(uschar **listptr, uschar *eml_filename, BOOL faking) {
158 uschar *list = *listptr;
159 uschar *av_scanner_work = av_scanner;
160 uschar *scanner_name;
161 uschar scanner_name_buffer[16];
162 uschar *malware_regex;
163 uschar malware_regex_buffer[64];
164 uschar malware_regex_default[] = ".+";
165 unsigned long mbox_size;
169 const uschar *rerror;
171 /* make sure the eml mbox file is spooled up */
172 mbox_file = spool_mbox(&mbox_size, faking ? eml_filename : NULL);
173 if (mbox_file == NULL) {
174 /* error while spooling */
175 log_write(0, LOG_MAIN|LOG_PANIC,
176 "malware acl condition: error while creating mbox spool file");
179 /* none of our current scanners need the mbox
180 file as a stream, so we can close it right away */
181 (void)fclose(mbox_file);
183 /* extract the malware regex to match against from the option list */
184 if ((malware_regex = string_nextinlist(&list, &sep,
185 malware_regex_buffer,
186 sizeof(malware_regex_buffer))) != NULL) {
188 /* parse 1st option */
189 if ( (strcmpic(malware_regex,US"false") == 0) ||
190 (Ustrcmp(malware_regex,"0") == 0) ) {
191 /* explicitly no matching */
195 /* special cases (match anything except empty) */
196 if ( (strcmpic(malware_regex,US"true") == 0) ||
197 (Ustrcmp(malware_regex,"*") == 0) ||
198 (Ustrcmp(malware_regex,"1") == 0) ) {
199 malware_regex = malware_regex_default;
203 /* empty means "don't match anything" */
207 /* Reset sep that is set by previous string_nextinlist() call */
210 /* compile the regex, see if it works */
211 re = pcre_compile(CS malware_regex, PCRE_COPT, (const char **)&rerror, &roffset, NULL);
213 log_write(0, LOG_MAIN|LOG_PANIC,
214 "malware acl condition: regular expression error in '%s': %s at offset %d", malware_regex, rerror, roffset);
218 /* if av_scanner starts with a dollar, expand it first */
219 if (*av_scanner == '$') {
220 av_scanner_work = expand_string(av_scanner);
221 if (av_scanner_work == NULL) {
222 log_write(0, LOG_MAIN|LOG_PANIC,
223 "malware acl condition: av_scanner starts with $, but expansion failed: %s", expand_string_message);
227 debug_printf("Expanded av_scanner global: %s\n", av_scanner_work);
228 /* disable result caching in this case */
234 /* Do not scan twice. */
235 if (malware_ok == 0) {
237 /* find the scanner type from the av_scanner option */
238 if ((scanner_name = string_nextinlist(&av_scanner_work, &sep,
240 sizeof(scanner_name_buffer))) == NULL) {
241 /* no scanner given */
242 log_write(0, LOG_MAIN|LOG_PANIC,
243 "malware acl condition: av_scanner configuration variable is empty");
247 /* "f-protd" scanner type ----------------------------------------------- */
248 if (strcmpic(scanner_name, US"f-protd") == 0) {
249 uschar *fp_options, *fp_scan_option;
250 uschar fp_scan_option_buffer[1024];
251 uschar fp_options_buffer[1024];
252 uschar fp_options_default[] = "localhost 10200-10204";
253 uschar hostname[256];
254 unsigned int port, portlow, porthigh, connect_ok=0, detected=0, par_count = 0;
258 uschar scanrequest[2048], buf[32768], *strhelper, *strhelper2;
260 if ((fp_options = string_nextinlist(&av_scanner_work, &sep,
261 fp_options_buffer, sizeof(fp_options_buffer))) == NULL) {
262 /* no options supplied, use default options */
263 fp_options = fp_options_default;
266 /* extract host and port part */
267 if ( sscanf(CS fp_options, "%s %u-%u", hostname, &portlow, &porthigh) != 3 ) {
268 if ( sscanf(CS fp_options, "%s %u", hostname, &portlow) != 2 ) {
269 log_write(0, LOG_MAIN|LOG_PANIC,
270 "malware acl condition: f-protd: invalid socket '%s'", fp_options);
276 /* Lookup the host */
277 if((he = gethostbyname(CS hostname)) == 0) {
278 log_write(0, LOG_MAIN|LOG_PANIC,
279 "malware acl condition: f-protd: failed to lookup host '%s'", hostname);
283 in = *(struct in_addr *) he->h_addr_list[0];
287 /* Open the f-protd TCP socket */
288 if ( (sock = ip_socket(SOCK_STREAM, AF_INET)) < 0) {
289 log_write(0, LOG_MAIN|LOG_PANIC,
290 "malware acl condition: f-protd: unable to acquire socket (%s)",
295 /* Try to connect to all portslow-high until connection is established */
296 for (port = portlow; !connect_ok && port < porthigh; port++) {
297 if (ip_connect(sock, AF_INET, (uschar*)inet_ntoa(in), port, 5) >= 0) {
303 log_write(0, LOG_MAIN|LOG_PANIC,
304 "malware acl condition: f-protd: connection to %s, port %u-%u failed (%s)",
305 inet_ntoa(in), portlow, porthigh, strerror(errno));
310 DEBUG(D_acl) debug_printf("Malware scan: issuing %s GET\n", scanner_name);
311 (void)string_format(scanrequest, 1024, CS"GET %s", eml_filename);
313 while ((fp_scan_option = string_nextinlist(&av_scanner_work, &sep,
314 fp_scan_option_buffer, sizeof(fp_scan_option_buffer))) != NULL) {
316 Ustrcat(scanrequest, "%20");
318 Ustrcat(scanrequest, "?");
320 Ustrcat(scanrequest, fp_scan_option);
323 Ustrcat(scanrequest, " HTTP/1.0\r\n\r\n");
325 /* send scan request */
326 if (send(sock, &scanrequest, Ustrlen(scanrequest)+1, 0) < 0) {
328 log_write(0, LOG_MAIN|LOG_PANIC,
329 "malware acl condition: f-protd: unable to send command to socket (%s)", scanrequest);
333 /* We get a lot of empty lines, so we need this hack to check for any data at all */
334 while( recv(sock, buf, 1, MSG_PEEK) > 0 ) {
335 if ( recv_line(sock, buf, 32768) > 0) {
336 if ( Ustrstr(buf, US"<detected type=\"") != NULL ) {
338 } else if ( detected && (strhelper = Ustrstr(buf, US"<name>")) ) {
339 if ((strhelper2 = Ustrstr(buf, US"</name>")) != NULL) {
341 Ustrcpy(malware_name_buffer, strhelper + 6);
343 } else if ( Ustrstr(buf, US"<summary code=\"") ) {
344 if ( Ustrstr(buf, US"<summary code=\"11\">") ) {
345 malware_name = malware_name_buffer;
354 /* "drweb" scanner type ----------------------------------------------- */
355 /* v0.1 - added support for tcp sockets */
356 /* v0.0 - initial release -- support for unix sockets */
357 else if (strcmpic(scanner_name,US"drweb") == 0) {
358 uschar *drweb_options;
359 uschar drweb_options_buffer[1024];
360 uschar drweb_options_default[] = "/usr/local/drweb/run/drwebd.sock";
361 struct sockaddr_un server;
362 int sock, result, ovector[30];
363 unsigned int port, fsize;
364 uschar tmpbuf[1024], *drweb_fbuf;
365 uschar drweb_match_string[128];
366 int drweb_rc, drweb_cmd, drweb_flags = 0x0000, drweb_fd,
367 drweb_vnum, drweb_slen, drweb_fin = 0x0000;
369 uschar hostname[256];
374 if ((drweb_options = string_nextinlist(&av_scanner_work, &sep,
375 drweb_options_buffer, sizeof(drweb_options_buffer))) == NULL) {
376 /* no options supplied, use default options */
377 drweb_options = drweb_options_default;
380 if (*drweb_options != '/') {
382 /* extract host and port part */
383 if( sscanf(CS drweb_options, "%s %u", hostname, &port) != 2 ) {
384 log_write(0, LOG_MAIN|LOG_PANIC,
385 "malware acl condition: drweb: invalid socket '%s'", drweb_options);
389 /* Lookup the host */
390 if((he = gethostbyname(CS hostname)) == 0) {
391 log_write(0, LOG_MAIN|LOG_PANIC,
392 "malware acl condition: drweb: failed to lookup host '%s'", hostname);
396 in = *(struct in_addr *) he->h_addr_list[0];
398 /* Open the drwebd TCP socket */
399 if ( (sock = ip_socket(SOCK_STREAM, AF_INET)) < 0) {
400 log_write(0, LOG_MAIN|LOG_PANIC,
401 "malware acl condition: drweb: unable to acquire socket (%s)",
406 if (ip_connect(sock, AF_INET, (uschar*)inet_ntoa(in), port, 5) < 0) {
408 log_write(0, LOG_MAIN|LOG_PANIC,
409 "malware acl condition: drweb: connection to %s, port %u failed (%s)",
410 inet_ntoa(in), port, strerror(errno));
414 /* prepare variables */
415 drweb_cmd = htonl(DRWEBD_SCAN_CMD);
416 drweb_flags = htonl(DRWEBD_RETURN_VIRUSES | DRWEBD_IS_MAIL);
419 drweb_fd = open(CS eml_filename, O_RDONLY);
420 if (drweb_fd == -1) {
422 log_write(0, LOG_MAIN|LOG_PANIC,
423 "malware acl condition: drweb: can't open spool file %s: %s",
424 eml_filename, strerror(errno));
427 fsize = lseek(drweb_fd, 0, SEEK_END);
430 (void)close(drweb_fd);
431 log_write(0, LOG_MAIN|LOG_PANIC,
432 "malware acl condition: drweb: can't seek spool file %s: %s",
433 eml_filename, strerror(errno));
436 drweb_slen = htonl(fsize);
437 lseek(drweb_fd, 0, SEEK_SET);
439 DEBUG(D_acl) debug_printf("Malware scan: issuing %s remote scan [%s %u]\n",
440 scanner_name, hostname, port);
442 /* send scan request */
443 if ((send(sock, &drweb_cmd, sizeof(drweb_cmd), 0) < 0) ||
444 (send(sock, &drweb_flags, sizeof(drweb_flags), 0) < 0) ||
445 (send(sock, &drweb_fin, sizeof(drweb_fin), 0) < 0) ||
446 (send(sock, &drweb_slen, sizeof(drweb_slen), 0) < 0)) {
448 (void)close(drweb_fd);
449 log_write(0, LOG_MAIN|LOG_PANIC,
450 "malware acl condition: drweb: unable to send commands to socket (%s)", drweb_options);
454 drweb_fbuf = (uschar *) malloc (fsize);
457 (void)close(drweb_fd);
458 log_write(0, LOG_MAIN|LOG_PANIC,
459 "malware acl condition: drweb: unable to allocate memory %u for file (%s)",
460 fsize, eml_filename);
464 result = read (drweb_fd, drweb_fbuf, fsize);
467 (void)close(drweb_fd);
469 log_write(0, LOG_MAIN|LOG_PANIC,
470 "malware acl condition: drweb: can't read spool file %s: %s",
471 eml_filename, strerror(errno));
474 (void)close(drweb_fd);
476 /* send file body to socket */
477 if (send(sock, drweb_fbuf, fsize, 0) < 0) {
480 log_write(0, LOG_MAIN|LOG_PANIC,
481 "malware acl condition: drweb: unable to send file body to socket (%s)", drweb_options);
484 (void)close(drweb_fd);
487 /* open the drwebd UNIX socket */
488 sock = socket(AF_UNIX, SOCK_STREAM, 0);
490 log_write(0, LOG_MAIN|LOG_PANIC,
491 "malware acl condition: drweb: can't open UNIX socket");
494 server.sun_family = AF_UNIX;
495 Ustrcpy(server.sun_path, drweb_options);
496 if (connect(sock, (struct sockaddr *) &server, sizeof(struct sockaddr_un)) < 0) {
498 log_write(0, LOG_MAIN|LOG_PANIC,
499 "malware acl condition: drweb: unable to connect to socket (%s). errno=%d", drweb_options, errno);
503 /* prepare variables */
504 drweb_cmd = htonl(DRWEBD_SCAN_CMD);
505 drweb_flags = htonl(DRWEBD_RETURN_VIRUSES | DRWEBD_IS_MAIL);
506 drweb_slen = htonl(Ustrlen(eml_filename));
508 DEBUG(D_acl) debug_printf("Malware scan: issuing %s local scan [%s]\n",
509 scanner_name, drweb_options);
511 /* send scan request */
512 if ((send(sock, &drweb_cmd, sizeof(drweb_cmd), 0) < 0) ||
513 (send(sock, &drweb_flags, sizeof(drweb_flags), 0) < 0) ||
514 (send(sock, &drweb_slen, sizeof(drweb_slen), 0) < 0) ||
515 (send(sock, eml_filename, Ustrlen(eml_filename), 0) < 0) ||
516 (send(sock, &drweb_fin, sizeof(drweb_fin), 0) < 0)) {
518 log_write(0, LOG_MAIN|LOG_PANIC,
519 "malware acl condition: drweb: unable to send commands to socket (%s)", drweb_options);
524 /* wait for result */
525 if ((bread = recv(sock, &drweb_rc, sizeof(drweb_rc), 0) != sizeof(drweb_rc))) {
527 log_write(0, LOG_MAIN|LOG_PANIC,
528 "malware acl condition: drweb: unable to read return code");
531 drweb_rc = ntohl(drweb_rc);
533 if ((bread = recv(sock, &drweb_vnum, sizeof(drweb_vnum), 0) != sizeof(drweb_vnum))) {
535 log_write(0, LOG_MAIN|LOG_PANIC,
536 "malware acl condition: drweb: unable to read the number of viruses");
539 drweb_vnum = ntohl(drweb_vnum);
541 /* "virus(es) found" if virus number is > 0 */
545 uschar pre_malware_nb[256];
547 malware_name = malware_name_buffer;
549 /* setup default virus name */
550 Ustrcpy(malware_name_buffer,"unknown");
552 /* read and concatenate virus names into one string */
553 for (i=0;i<drweb_vnum;i++)
555 /* read the size of report */
556 if ((bread = recv(sock, &drweb_slen, sizeof(drweb_slen), 0) != sizeof(drweb_slen))) {
558 log_write(0, LOG_MAIN|LOG_PANIC,
559 "malware acl condition: drweb: cannot read report size");
562 drweb_slen = ntohl(drweb_slen);
564 /* read report body */
565 if ((bread = recv(sock, tmpbuf, drweb_slen, 0)) != drweb_slen) {
567 log_write(0, LOG_MAIN|LOG_PANIC,
568 "malware acl condition: drweb: cannot read report string");
571 tmpbuf[drweb_slen] = '\0';
573 /* set up match regex, depends on retcode */
574 Ustrcpy(drweb_match_string, "infected\\swith\\s*(.+?)$");
576 drweb_re = pcre_compile( CS drweb_match_string,
578 (const char **)&rerror,
582 /* try matcher on the line, grab substring */
583 result = pcre_exec(drweb_re, NULL, CS tmpbuf, Ustrlen(tmpbuf), 0, 0, ovector, 30);
585 pcre_copy_substring(CS tmpbuf, ovector, result, 1, CS pre_malware_nb, 255);
587 /* the first name we just copy to malware_name */
589 Ustrcpy(CS malware_name_buffer, CS pre_malware_nb);
591 /* concatenate each new virus name to previous */
592 int slen = Ustrlen(malware_name_buffer);
593 if (slen < (slen+Ustrlen(pre_malware_nb))) {
594 Ustrcat(malware_name_buffer, "/");
595 Ustrcat(malware_name_buffer, pre_malware_nb);
601 const char *drweb_s = NULL;
603 if (drweb_rc & DERR_READ_ERR) drweb_s = "read error";
604 if (drweb_rc & DERR_NOMEMORY) drweb_s = "no memory";
605 if (drweb_rc & DERR_TIMEOUT) drweb_s = "timeout";
606 if (drweb_rc & DERR_BAD_CALL) drweb_s = "wrong command";
607 /* retcodes DERR_SYMLINK, DERR_NO_REGFILE, DERR_SKIPPED.
608 * DERR_TOO_BIG, DERR_TOO_COMPRESSED, DERR_SPAM,
609 * DERR_CRC_ERROR, DERR_READSOCKET, DERR_WRITE_ERR
610 * and others are ignored */
612 log_write(0, LOG_MAIN|LOG_PANIC,
613 "malware acl condition: drweb: drweb daemon retcode 0x%x (%s)", drweb_rc, drweb_s);
622 /* ----------------------------------------------------------------------- */
623 else if (strcmpic(scanner_name,US"aveserver") == 0) {
625 uschar kav_options_buffer[1024];
626 uschar kav_options_default[] = "/var/run/aveserver";
628 struct sockaddr_un server;
632 if ((kav_options = string_nextinlist(&av_scanner_work, &sep,
634 sizeof(kav_options_buffer))) == NULL) {
635 /* no options supplied, use default options */
636 kav_options = kav_options_default;
639 /* open the aveserver socket */
640 sock = socket(AF_UNIX, SOCK_STREAM, 0);
642 log_write(0, LOG_MAIN|LOG_PANIC,
643 "malware acl condition: can't open UNIX socket.");
646 server.sun_family = AF_UNIX;
647 Ustrcpy(server.sun_path, kav_options);
648 if (connect(sock, (struct sockaddr *) &server, sizeof(struct sockaddr_un)) < 0) {
650 log_write(0, LOG_MAIN|LOG_PANIC,
651 "malware acl condition: unable to connect to aveserver UNIX socket (%s). errno=%d", kav_options, errno);
655 /* read aveserver's greeting and see if it is ready (2xx greeting) */
656 recv_line(sock, buf, 32768);
659 /* aveserver is having problems */
661 log_write(0, LOG_MAIN|LOG_PANIC,
662 "malware acl condition: aveserver is unavailable (Responded: %s).", ((buf[0] != 0) ? buf : (uschar *)"nothing") );
666 /* prepare our command */
667 (void)string_format(buf, 32768, "SCAN bPQRSTUW %s\r\n", eml_filename);
669 DEBUG(D_acl) debug_printf("Malware scan: issuing %s SCAN\n", scanner_name);
672 if (send(sock, buf, Ustrlen(buf), 0) < 0) {
674 log_write(0, LOG_MAIN|LOG_PANIC,
675 "malware acl condition: unable to write to aveserver UNIX socket (%s)", kav_options);
681 /* read response lines, find malware name and final response */
682 while (recv_line(sock, buf, 32768) > 0) {
683 debug_printf("aveserver: %s\n", buf);
686 } else if (buf[0] == '5') {
687 /* aveserver is having problems */
688 log_write(0, LOG_MAIN|LOG_PANIC,
689 "malware acl condition: unable to scan file %s (Responded: %s).",
693 } else if (Ustrncmp(buf,"322",3) == 0) {
694 uschar *p = Ustrchr(&buf[4],' ');
696 Ustrcpy(malware_name_buffer,&buf[4]);
697 malware_name = malware_name_buffer;
701 /* prepare our command */
702 (void)string_format(buf, 32768, "quit\r\n");
705 if (send(sock, buf, Ustrlen(buf), 0) < 0) {
707 log_write(0, LOG_MAIN|LOG_PANIC,
708 "malware acl condition: unable to write to aveserver UNIX socket (%s)", kav_options);
712 /* read aveserver's greeting and see if it is ready (2xx greeting) */
713 recv_line(sock, buf, 32768);
716 /* aveserver is having problems */
718 log_write(0, LOG_MAIN|LOG_PANIC,
719 "malware acl condition: unable to quit aveserver dialogue (Responded: %s).", ((buf[0] != 0) ? buf : (uschar *)"nothing") );
725 if (result == DEFER) return DEFER;
727 /* "fsecure" scanner type ------------------------------------------------- */
728 else if (strcmpic(scanner_name,US"fsecure") == 0) {
729 uschar *fsecure_options;
730 uschar fsecure_options_buffer[1024];
731 uschar fsecure_options_default[] = "/var/run/.fsav";
732 struct sockaddr_un server;
733 int sock, i, j, bread = 0;
734 uschar file_name[1024];
735 uschar av_buffer[1024];
737 static uschar *cmdoptions[] = { US"CONFIGURE\tARCHIVE\t1\n",
738 US"CONFIGURE\tTIMEOUT\t0\n",
739 US"CONFIGURE\tMAXARCH\t5\n",
740 US"CONFIGURE\tMIME\t1\n" };
743 if ((fsecure_options = string_nextinlist(&av_scanner_work, &sep,
744 fsecure_options_buffer,
745 sizeof(fsecure_options_buffer))) == NULL) {
746 /* no options supplied, use default options */
747 fsecure_options = fsecure_options_default;
750 /* open the fsecure socket */
751 sock = socket(AF_UNIX, SOCK_STREAM, 0);
753 log_write(0, LOG_MAIN|LOG_PANIC,
754 "malware acl condition: unable to open fsecure socket %s (%s)",
755 fsecure_options, strerror(errno));
758 server.sun_family = AF_UNIX;
759 Ustrcpy(server.sun_path, fsecure_options);
760 if (connect(sock, (struct sockaddr *) &server, sizeof(struct sockaddr_un)) < 0) {
762 log_write(0, LOG_MAIN|LOG_PANIC,
763 "malware acl condition: unable to connect to fsecure socket %s (%s)",
764 fsecure_options, strerror(errno));
768 DEBUG(D_acl) debug_printf("Malware scan: issuing %s scan [%s]\n",
769 scanner_name, fsecure_options);
772 memset(av_buffer, 0, sizeof(av_buffer));
773 for (i=0; i != 4; i++) {
774 /* debug_printf("send option \"%s\"",cmdoptions[i]); */
775 if (write(sock, cmdoptions[i], Ustrlen(cmdoptions[i])) < 0) {
777 log_write(0, LOG_MAIN|LOG_PANIC,
778 "malware acl condition: unable to write fsecure option %d to %s (%s)",
779 i, fsecure_options, strerror(errno));
783 bread = ip_recv(sock, av_buffer, sizeof(av_buffer), MALWARE_TIMEOUT);
784 if (bread >0) av_buffer[bread]='\0';
787 log_write(0, LOG_MAIN|LOG_PANIC,
788 "malware acl condition: unable to read fsecure answer %d (%s)", i, strerror(errno));
791 for (j=0;j<bread;j++) if((av_buffer[j]=='\r')||(av_buffer[j]=='\n')) av_buffer[j] ='@';
792 /* debug_printf("read answer %d read=%d \"%s\"\n", i, bread, av_buffer ); */
793 /* while (Ustrstr(av_buffer, "OK\tServer configured.@") == NULL); */
796 /* pass the mailfile to fsecure */
797 (void)string_format(file_name,1024,"SCAN\t%s\n", eml_filename);
798 /* debug_printf("send scan %s",file_name); */
799 if (write(sock, file_name, Ustrlen(file_name)) < 0) {
801 log_write(0, LOG_MAIN|LOG_PANIC,
802 "malware acl condition: unable to write fsecure scan to %s (%s)",
803 fsecure_options, strerror(errno));
808 /* todo also SUSPICION\t */
809 fs_inf = pcre_compile("\\S{0,5}INFECTED\\t[^\\t]*\\t([^\\t]+)\\t\\S*$", PCRE_COPT, (const char **)&rerror, &roffset, NULL);
811 /* read report, linewise */
815 memset(av_buffer, 0, sizeof(av_buffer));
817 bread=ip_recv(sock, &av_buffer[i], 1, MALWARE_TIMEOUT);
820 log_write(0, LOG_MAIN|LOG_PANIC,
821 "malware acl condition: unable to read fsecure result (%s)", strerror(errno));
826 while ((i < sizeof(av_buffer)-1 ) && (av_buffer[i-1] != '\n'));
827 av_buffer[i-1] = '\0';
828 /* debug_printf("got line \"%s\"\n",av_buffer); */
830 /* Really search for virus again? */
831 if (malware_name == NULL) {
832 /* try matcher on the line, grab substring */
833 i = pcre_exec(fs_inf, NULL, CS av_buffer, Ustrlen(av_buffer), 0, 0, ovector, 30);
836 pcre_copy_substring(CS av_buffer, ovector, i, 1, CS malware_name_buffer, 255);
837 malware_name = malware_name_buffer;
841 while (Ustrstr(av_buffer, "OK\tScan ok.") == NULL);
844 /* ----------------------------------------------------------------------- */
846 /* "kavdaemon" scanner type ------------------------------------------------ */
847 else if (strcmpic(scanner_name,US"kavdaemon") == 0) {
849 uschar kav_options_buffer[1024];
850 uschar kav_options_default[] = "/var/run/AvpCtl";
851 struct sockaddr_un server;
855 uschar scanrequest[1024];
856 uschar kav_match_string[128];
858 unsigned long kav_reportlen, bread;
863 if ((kav_options = string_nextinlist(&av_scanner_work, &sep,
865 sizeof(kav_options_buffer))) == NULL) {
866 /* no options supplied, use default options */
867 kav_options = kav_options_default;
870 /* open the kavdaemon socket */
871 sock = socket(AF_UNIX, SOCK_STREAM, 0);
873 log_write(0, LOG_MAIN|LOG_PANIC,
874 "malware acl condition: can't open UNIX socket.");
877 server.sun_family = AF_UNIX;
878 Ustrcpy(server.sun_path, kav_options);
879 if (connect(sock, (struct sockaddr *) &server, sizeof(struct sockaddr_un)) < 0) {
881 log_write(0, LOG_MAIN|LOG_PANIC,
882 "malware acl condition: unable to connect to kavdaemon UNIX socket (%s). errno=%d", kav_options, errno);
886 /* get current date and time, build scan request */
888 /* pdp note: before the eml_filename parameter, this scanned the
889 directory; not finding documentation, so we'll strip off the directory.
890 The side-effect is that the test framework scanning may end up in
891 scanning more than was requested, but for the normal interface, this is
893 strftime(CS tmpbuf, sizeof(tmpbuf), "<0>%d %b %H:%M:%S:%%s", localtime(&t));
894 fits = string_format(scanrequest, 1024,CS tmpbuf, eml_filename);
897 log_write(0, LOG_MAIN|LOG_PANIC,
898 "malware filename does not fit in buffer [malware_internal() kavdaemon]");
900 p = Ustrrchr(scanrequest, '/');
904 DEBUG(D_acl) debug_printf("Malware scan: issuing %s scan [%s]\n",
905 scanner_name, kav_options);
907 /* send scan request */
908 if (send(sock, scanrequest, Ustrlen(scanrequest)+1, 0) < 0) {
910 log_write(0, LOG_MAIN|LOG_PANIC,
911 "malware acl condition: unable to write to kavdaemon UNIX socket (%s)", kav_options);
915 /* wait for result */
916 if ((bread = recv(sock, tmpbuf, 2, 0) != 2)) {
918 log_write(0, LOG_MAIN|LOG_PANIC,
919 "malware acl condition: unable to read 2 bytes from kavdaemon socket.");
923 /* get errorcode from one nibble */
924 if (test_byte_order() == LITTLE_MY_ENDIAN) {
925 kav_rc = tmpbuf[0] & 0x0F;
928 kav_rc = tmpbuf[1] & 0x0F;
931 /* improper kavdaemon configuration */
932 if ( (kav_rc == 5) || (kav_rc == 6) ) {
934 log_write(0, LOG_MAIN|LOG_PANIC,
935 "malware acl condition: please reconfigure kavdaemon to NOT disinfect or remove infected files.");
941 log_write(0, LOG_MAIN|LOG_PANIC,
942 "malware acl condition: kavdaemon reported 'scanning not completed' (code 1).");
948 log_write(0, LOG_MAIN|LOG_PANIC,
949 "malware acl condition: kavdaemon reported 'kavdaemon damaged' (code 7).");
953 /* code 8 is not handled, since it is ambigous. It appears mostly on
954 bounces where part of a file has been cut off */
956 /* "virus found" return codes (2-4) */
957 if ((kav_rc > 1) && (kav_rc < 5)) {
960 /* setup default virus name */
961 Ustrcpy(malware_name_buffer,"unknown");
962 malware_name = malware_name_buffer;
964 if (test_byte_order() == LITTLE_MY_ENDIAN) {
965 report_flag = tmpbuf[1];
968 report_flag = tmpbuf[0];
971 /* read the report, if available */
972 if( report_flag == 1 ) {
973 /* read report size */
974 if ((bread = recv(sock, &kav_reportlen, 4, 0)) != 4) {
976 log_write(0, LOG_MAIN|LOG_PANIC,
977 "malware acl condition: cannot read report size from kavdaemon");
981 /* it's possible that avp returns av_buffer[1] == 1 but the
982 reportsize is 0 (!?) */
983 if (kav_reportlen > 0) {
984 /* set up match regex, depends on retcode */
986 Ustrcpy(kav_match_string, "suspicion:\\s*(.+?)\\s*$");
988 Ustrcpy(kav_match_string, "infected:\\s*(.+?)\\s*$");
990 kav_re = pcre_compile( CS kav_match_string,
992 (const char **)&rerror,
996 /* read report, linewise */
997 while (kav_reportlen > 0) {
1002 while ( recv(sock, &tmpbuf[bread], 1, 0) == 1 ) {
1004 if ( (tmpbuf[bread] == '\n') || (bread > 1021) ) break;
1008 tmpbuf[bread] = '\0';
1010 /* try matcher on the line, grab substring */
1011 result = pcre_exec(kav_re, NULL, CS tmpbuf, Ustrlen(tmpbuf), 0, 0, ovector, 30);
1013 pcre_copy_substring(CS tmpbuf, ovector, result, 1, CS malware_name_buffer, 255);
1021 /* no virus found */
1022 malware_name = NULL;
1027 /* ----------------------------------------------------------------------- */
1030 /* "cmdline" scanner type ------------------------------------------------ */
1031 else if (strcmpic(scanner_name,US"cmdline") == 0) {
1032 uschar *cmdline_scanner;
1033 uschar cmdline_scanner_buffer[1024];
1034 uschar *cmdline_trigger;
1035 uschar cmdline_trigger_buffer[1024];
1036 const pcre *cmdline_trigger_re;
1037 uschar *cmdline_regex;
1038 uschar cmdline_regex_buffer[1024];
1039 const pcre *cmdline_regex_re;
1040 uschar file_name[1024];
1041 uschar commandline[1024];
1042 void (*eximsigchld)(int);
1043 void (*eximsigpipe)(int);
1044 FILE *scanner_out = NULL;
1045 FILE *scanner_record = NULL;
1046 uschar linebuffer[32767];
1053 /* find scanner command line */
1054 if ((cmdline_scanner = string_nextinlist(&av_scanner_work, &sep,
1055 cmdline_scanner_buffer,
1056 sizeof(cmdline_scanner_buffer))) == NULL) {
1057 /* no command line supplied */
1058 log_write(0, LOG_MAIN|LOG_PANIC,
1059 "malware acl condition: missing commandline specification for cmdline scanner type.");
1063 /* find scanner output trigger */
1064 if ((cmdline_trigger = string_nextinlist(&av_scanner_work, &sep,
1065 cmdline_trigger_buffer,
1066 sizeof(cmdline_trigger_buffer))) == NULL) {
1067 /* no trigger regex supplied */
1068 log_write(0, LOG_MAIN|LOG_PANIC,
1069 "malware acl condition: missing trigger specification for cmdline scanner type.");
1073 /* precompile trigger regex */
1074 cmdline_trigger_re = pcre_compile(CS cmdline_trigger, PCRE_COPT, (const char **)&rerror, &roffset, NULL);
1075 if (cmdline_trigger_re == NULL) {
1076 log_write(0, LOG_MAIN|LOG_PANIC,
1077 "malware acl condition: regular expression error in '%s': %s at offset %d", cmdline_trigger, rerror, roffset);
1081 /* find scanner name regex */
1082 if ((cmdline_regex = string_nextinlist(&av_scanner_work, &sep,
1083 cmdline_regex_buffer,
1084 sizeof(cmdline_regex_buffer))) == NULL) {
1085 /* no name regex supplied */
1086 log_write(0, LOG_MAIN|LOG_PANIC,
1087 "malware acl condition: missing virus name regex specification for cmdline scanner type.");
1091 /* precompile name regex */
1092 cmdline_regex_re = pcre_compile(CS cmdline_regex, PCRE_COPT, (const char **)&rerror, &roffset, NULL);
1093 if (cmdline_regex_re == NULL) {
1094 log_write(0, LOG_MAIN|LOG_PANIC,
1095 "malware acl condition: regular expression error in '%s': %s at offset %d", cmdline_regex, rerror, roffset);
1099 /* prepare scanner call; despite the naming, file_name holds a directory
1100 name which is documented as the value given to %s. */
1101 if (Ustrlen(eml_filename) > sizeof(file_name) - 1)
1103 log_write(0, LOG_MAIN|LOG_PANIC,
1104 "malware filename does not fit in buffer [malware_internal() cmdline]");
1107 Ustrcpy(file_name, eml_filename);
1108 p = Ustrrchr(file_name, '/');
1111 fits = string_format(commandline, sizeof(commandline), CS cmdline_scanner, file_name);
1114 log_write(0, LOG_MAIN|LOG_PANIC,
1115 "cmdline scanner command-line does not fit in buffer");
1119 /* redirect STDERR too */
1120 if (Ustrlen(commandline) + 5 > sizeof(commandline))
1122 log_write(0, LOG_MAIN|LOG_PANIC,
1123 "cmdline scanner command-line does not fit in buffer (STDERR redirect)");
1126 Ustrcat(commandline," 2>&1");
1128 DEBUG(D_acl) debug_printf("Malware scan: issuing %s scan [%s]\n", scanner_name, commandline);
1130 /* store exims signal handlers */
1131 eximsigchld = signal(SIGCHLD,SIG_DFL);
1132 eximsigpipe = signal(SIGPIPE,SIG_DFL);
1134 scanner_out = popen(CS commandline,"r");
1135 if (scanner_out == NULL) {
1136 log_write(0, LOG_MAIN|LOG_PANIC,
1137 "malware acl condition: calling cmdline scanner (%s) failed: %s.", commandline, strerror(errno));
1138 signal(SIGCHLD,eximsigchld);
1139 signal(SIGPIPE,eximsigpipe);
1143 (void)string_format(file_name,1024,"%s/scan/%s/%s_scanner_output", spool_directory, message_id, message_id);
1144 scanner_record = modefopen(file_name,"wb",SPOOL_MODE);
1146 if (scanner_record == NULL) {
1147 log_write(0, LOG_MAIN|LOG_PANIC,
1148 "malware acl condition: opening scanner output file (%s) failed: %s.", file_name, strerror(errno));
1149 pclose(scanner_out);
1150 signal(SIGCHLD,eximsigchld);
1151 signal(SIGPIPE,eximsigpipe);
1155 /* look for trigger while recording output */
1156 while(fgets(CS linebuffer,32767,scanner_out) != NULL) {
1157 if ( Ustrlen(linebuffer) > fwrite(linebuffer, 1, Ustrlen(linebuffer), scanner_record) ) {
1159 log_write(0, LOG_MAIN|LOG_PANIC,
1160 "malware acl condition: short write on scanner output file (%s).", file_name);
1161 pclose(scanner_out);
1162 signal(SIGCHLD,eximsigchld);
1163 signal(SIGPIPE,eximsigpipe);
1166 /* try trigger match */
1167 if (!trigger && regex_match_and_setup(cmdline_trigger_re, linebuffer, 0, -1))
1171 (void)fclose(scanner_record);
1172 pclose(scanner_out);
1173 signal(SIGCHLD,eximsigchld);
1174 signal(SIGPIPE,eximsigpipe);
1177 /* setup default virus name */
1178 Ustrcpy(malware_name_buffer,"unknown");
1179 malware_name = malware_name_buffer;
1181 /* re-open the scanner output file, look for name match */
1182 scanner_record = fopen(CS file_name,"rb");
1183 while(fgets(CS linebuffer,32767,scanner_record) != NULL) {
1185 result = pcre_exec(cmdline_regex_re, NULL, CS linebuffer, Ustrlen(linebuffer), 0, 0, ovector, 30);
1187 pcre_copy_substring(CS linebuffer, ovector, result, 1, CS malware_name_buffer, 255);
1190 (void)fclose(scanner_record);
1193 /* no virus found */
1194 malware_name = NULL;
1197 /* ----------------------------------------------------------------------- */
1200 /* "sophie" scanner type ------------------------------------------------- */
1201 else if (strcmpic(scanner_name,US"sophie") == 0) {
1202 uschar *sophie_options;
1203 uschar sophie_options_buffer[1024];
1204 uschar sophie_options_default[] = "/var/run/sophie";
1206 struct sockaddr_un server;
1209 uschar file_name[1024];
1210 uschar av_buffer[1024];
1212 if ((sophie_options = string_nextinlist(&av_scanner_work, &sep,
1213 sophie_options_buffer,
1214 sizeof(sophie_options_buffer))) == NULL) {
1215 /* no options supplied, use default options */
1216 sophie_options = sophie_options_default;
1219 /* open the sophie socket */
1220 sock = socket(AF_UNIX, SOCK_STREAM, 0);
1222 log_write(0, LOG_MAIN|LOG_PANIC,
1223 "malware acl condition: can't open UNIX socket.");
1226 server.sun_family = AF_UNIX;
1227 Ustrcpy(server.sun_path, sophie_options);
1228 if (connect(sock, (struct sockaddr *) &server, sizeof(struct sockaddr_un)) < 0) {
1230 log_write(0, LOG_MAIN|LOG_PANIC,
1231 "malware acl condition: unable to connect to sophie UNIX socket (%s). errno=%d", sophie_options, errno);
1235 /* pass the scan directory to sophie */
1236 len = Ustrlen(eml_filename) + 1;
1237 if (len > sizeof(file_name))
1240 log_write(0, LOG_MAIN|LOG_PANIC,
1241 "malware filename does not fit in buffer [malware_internal() sophie]");
1244 memcpy(file_name, eml_filename, len);
1245 p = Ustrrchr(file_name, '/');
1249 DEBUG(D_acl) debug_printf("Malware scan: issuing %s scan [%s]\n",
1250 scanner_name, sophie_options);
1252 if ( write(sock, file_name, Ustrlen(file_name)) < 0
1253 || write(sock, "\n", 1) != 1
1256 log_write(0, LOG_MAIN|LOG_PANIC,
1257 "malware acl condition: unable to write to sophie UNIX socket (%s)", sophie_options);
1261 /* wait for result */
1262 memset(av_buffer, 0, sizeof(av_buffer));
1263 if ((!(bread = ip_recv(sock, av_buffer, sizeof(av_buffer), MALWARE_TIMEOUT)) > 0)) {
1265 log_write(0, LOG_MAIN|LOG_PANIC,
1266 "malware acl condition: unable to read from sophie UNIX socket (%s)", sophie_options);
1273 if (av_buffer[0] == '1') {
1274 if (Ustrchr(av_buffer, '\n')) *Ustrchr(av_buffer, '\n') = '\0';
1275 Ustrcpy(malware_name_buffer,&av_buffer[2]);
1276 malware_name = malware_name_buffer;
1278 else if (!strncmp(CS av_buffer, "-1", 2)) {
1279 log_write(0, LOG_MAIN|LOG_PANIC,
1280 "malware acl condition: malware acl condition: sophie reported error");
1284 /* all ok, no virus */
1285 malware_name = NULL;
1288 /* ----------------------------------------------------------------------- */
1291 /* "clamd" scanner type ------------------------------------------------- */
1292 /* This code was originally contributed by David Saez */
1293 /* There are three scanning methods available to us:
1294 * (1) Use the SCAN command, pointing to a file in the filesystem
1295 * (2) Use the STREAM command, send the data on a separate port
1296 * (3) Use the zINSTREAM command, send the data inline
1297 * The zINSTREAM command was introduced with ClamAV 0.95, which marked
1298 * STREAM deprecated; see: http://wiki.clamav.net/bin/view/Main/UpgradeNotes095
1299 * In Exim, we use SCAN if using a Unix-domain socket or explicitly told that
1300 * the TCP-connected daemon is actually local; otherwise we use zINSTREAM unless
1301 * WITH_OLD_CLAMAV_STREAM is defined.
1302 * See Exim bug 926 for details. */
1303 else if (strcmpic(scanner_name,US"clamd") == 0) {
1304 uschar *clamd_options;
1305 uschar clamd_options_buffer[1024];
1306 uschar clamd_options_default[] = "/tmp/clamd";
1307 uschar *p, *vname, *result_tag, *response_end;
1308 struct sockaddr_un server;
1311 uschar file_name[1024];
1312 uschar av_buffer[1024];
1313 uschar hostname[256];
1316 uschar *clamd_options2;
1317 uschar clamd_options2_buffer[1024];
1318 uschar clamd_options2_default[] = "";
1319 uschar *clamav_fbuf;
1320 int clam_fd, result;
1322 BOOL use_scan_command, fits;
1323 #ifdef WITH_OLD_CLAMAV_STREAM
1324 uschar av_buffer2[1024];
1327 uint32_t send_size, send_final_zeroblock;
1330 if ((clamd_options = string_nextinlist(&av_scanner_work, &sep,
1331 clamd_options_buffer,
1332 sizeof(clamd_options_buffer))) == NULL) {
1333 /* no options supplied, use default options */
1334 clamd_options = clamd_options_default;
1336 if ((clamd_options2 = string_nextinlist(&av_scanner_work, &sep,
1337 clamd_options2_buffer,
1338 sizeof(clamd_options2_buffer))) == NULL) {
1339 clamd_options2 = clamd_options2_default;
1342 if ((*clamd_options == '/') || (strcmpic(clamd_options2,US"local") == 0))
1343 use_scan_command = TRUE;
1345 use_scan_command = FALSE;
1347 /* See the discussion of response formats below to see why we really don't
1348 like colons in filenames when passing filenames to ClamAV. */
1349 if (use_scan_command && Ustrchr(eml_filename, ':')) {
1350 log_write(0, LOG_MAIN|LOG_PANIC,
1351 "malware acl condition: clamd: local/SCAN mode incompatible with" \
1352 " : in path to email filename [%s]", eml_filename);
1356 /* socket does not start with '/' -> network socket */
1357 if (*clamd_options != '/') {
1359 /* Confirmed in ClamAV source (0.95.3) that the TCPAddr option of clamd
1360 * only supports AF_INET, but we should probably be looking to the
1361 * future and rewriting this to be protocol-independent anyway. */
1363 /* extract host and port part */
1364 if( sscanf(CS clamd_options, "%s %u", hostname, &port) != 2 ) {
1365 log_write(0, LOG_MAIN|LOG_PANIC,
1366 "malware acl condition: clamd: invalid socket '%s'", clamd_options);
1370 /* Lookup the host */
1371 if((he = gethostbyname(CS hostname)) == 0) {
1372 log_write(0, LOG_MAIN|LOG_PANIC,
1373 "malware acl condition: clamd: failed to lookup host '%s'", hostname);
1377 in = *(struct in_addr *) he->h_addr_list[0];
1379 /* Open the ClamAV Socket */
1380 if ( (sock = ip_socket(SOCK_STREAM, AF_INET)) < 0) {
1381 log_write(0, LOG_MAIN|LOG_PANIC,
1382 "malware acl condition: clamd: unable to acquire socket (%s)",
1387 if (ip_connect(sock, AF_INET, (uschar*)inet_ntoa(in), port, 5) < 0) {
1389 log_write(0, LOG_MAIN|LOG_PANIC,
1390 "malware acl condition: clamd: connection to %s, port %u failed (%s)",
1391 inet_ntoa(in), port, strerror(errno));
1396 /* open the local socket */
1397 if ((sock = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
1398 log_write(0, LOG_MAIN|LOG_PANIC,
1399 "malware acl condition: clamd: unable to acquire socket (%s)",
1404 server.sun_family = AF_UNIX;
1405 Ustrcpy(server.sun_path, clamd_options);
1407 if (connect(sock, (struct sockaddr *) &server, sizeof(struct sockaddr_un)) < 0) {
1409 log_write(0, LOG_MAIN|LOG_PANIC,
1410 "malware acl condition: clamd: unable to connect to UNIX socket %s (%s)",
1411 clamd_options, strerror(errno) );
1416 /* have socket in variable "sock"; command to use is semi-independent of
1417 * the socket protocol. We use SCAN if is local (either Unix/local
1418 * domain socket, or explicitly told local) else we stream the data.
1419 * How we stream the data depends upon how we were built. */
1421 if (!use_scan_command) {
1423 #ifdef WITH_OLD_CLAMAV_STREAM
1424 /* "STREAM\n" command, get back a "PORT <N>\n" response, send data to
1425 * that port on a second connection; then in the scan-method-neutral
1426 * part, read the response back on the original connection. */
1428 DEBUG(D_acl) debug_printf("Malware scan: issuing %s old-style remote scan (PORT)\n",
1431 /* Pass the string to ClamAV (7 = "STREAM\n") */
1432 if (send(sock, "STREAM\n", 7, 0) < 0) {
1433 log_write(0, LOG_MAIN|LOG_PANIC,"malware acl condition: clamd: unable to write to socket (%s)",
1438 memset(av_buffer2, 0, sizeof(av_buffer2));
1439 bread = ip_recv(sock, av_buffer2, sizeof(av_buffer2), MALWARE_TIMEOUT);
1442 log_write(0, LOG_MAIN|LOG_PANIC,
1443 "malware acl condition: clamd: unable to read PORT from socket (%s)",
1449 if (bread == sizeof(av_buffer)) {
1450 log_write(0, LOG_MAIN|LOG_PANIC,
1451 "malware acl condition: clamd: buffer too small");
1456 if (!(*av_buffer2)) {
1457 log_write(0, LOG_MAIN|LOG_PANIC,
1458 "malware acl condition: clamd: ClamAV returned null");
1463 av_buffer2[bread] = '\0';
1464 if( sscanf(CS av_buffer2, "PORT %u\n", &port) != 1 ) {
1465 log_write(0, LOG_MAIN|LOG_PANIC,
1466 "malware acl condition: clamd: Expected port information from clamd, got '%s'", av_buffer2);
1471 if ( (sockData = ip_socket(SOCK_STREAM, AF_INET)) < 0) {
1472 log_write(0, LOG_MAIN|LOG_PANIC,
1473 "malware acl condition: clamd: unable to acquire socket (%s)",
1479 if (ip_connect(sockData, AF_INET, (uschar*)inet_ntoa(in), port, 5) < 0) {
1480 log_write(0, LOG_MAIN|LOG_PANIC,
1481 "malware acl condition: clamd: connection to %s, port %u failed (%s)",
1482 inet_ntoa(in), port, strerror(errno));
1483 (void)close(sockData); (void)close(sock);
1487 #define CLOSE_SOCKDATA (void)close(sockData)
1488 #else /* WITH_OLD_CLAMAV_STREAM not defined */
1489 /* New protocol: "zINSTREAM\n" followed by a sequence of <length><data>
1490 chunks, <n> a 4-byte number (network order), terminated by a zero-length
1493 DEBUG(D_acl) debug_printf("Malware scan: issuing %s new-style remote scan (zINSTREAM)\n",
1496 /* Pass the string to ClamAV (10 = "zINSTREAM\0") */
1497 if (send(sock, "zINSTREAM", 10, 0) < 0) {
1498 log_write(0, LOG_MAIN|LOG_PANIC,
1499 "malware acl condition: clamd: unable to send zINSTREAM to socket (%s)",
1505 #define CLOSE_SOCKDATA /**/
1508 /* calc file size */
1509 clam_fd = open(CS eml_filename, O_RDONLY);
1510 if (clam_fd == -1) {
1511 log_write(0, LOG_MAIN|LOG_PANIC,
1512 "malware acl condition: clamd: can't open spool file %s: %s",
1513 eml_filename, strerror(errno));
1514 CLOSE_SOCKDATA; (void)close(sock);
1517 fsize = lseek(clam_fd, 0, SEEK_END);
1519 log_write(0, LOG_MAIN|LOG_PANIC,
1520 "malware acl condition: clamd: can't seek spool file %s: %s",
1521 eml_filename, strerror(errno));
1522 CLOSE_SOCKDATA; (void)close(sock);
1525 lseek(clam_fd, 0, SEEK_SET);
1527 clamav_fbuf = (uschar *) malloc (fsize);
1529 log_write(0, LOG_MAIN|LOG_PANIC,
1530 "malware acl condition: clamd: unable to allocate memory %u for file (%s)",
1531 fsize, eml_filename);
1532 CLOSE_SOCKDATA; (void)close(sock); (void)close(clam_fd);
1536 result = read (clam_fd, clamav_fbuf, fsize);
1538 log_write(0, LOG_MAIN|LOG_PANIC,
1539 "malware acl condition: clamd: can't read spool file %s: %s",
1540 eml_filename, strerror(errno));
1541 CLOSE_SOCKDATA; (void)close(sock); (void)close(clam_fd);
1545 (void)close(clam_fd);
1547 /* send file body to socket */
1548 #ifdef WITH_OLD_CLAMAV_STREAM
1549 if (send(sockData, clamav_fbuf, fsize, 0) < 0) {
1550 log_write(0, LOG_MAIN|LOG_PANIC,
1551 "malware acl condition: clamd: unable to send file body to socket (%s:%u)", hostname, port);
1552 CLOSE_SOCKDATA; (void)close(sock);
1557 send_size = htonl(fsize);
1558 send_final_zeroblock = 0;
1559 if ((send(sock, &send_size, sizeof(send_size), 0) < 0) ||
1560 (send(sock, clamav_fbuf, fsize, 0) < 0) ||
1561 (send(sock, &send_final_zeroblock, sizeof(send_final_zeroblock), 0) < 0))
1563 log_write(0, LOG_MAIN|LOG_PANIC,
1564 "malware acl condition: clamd: unable to send file body to socket (%s:%u)", hostname, port);
1574 #undef CLOSE_SOCKDATA
1576 } else { /* use scan command */
1577 /* Send a SCAN command pointing to a filename; then in the then in the
1578 * scan-method-neutral part, read the response back */
1580 /* ================================================================= */
1582 /* Prior to the reworking post-Exim-4.72, this scanned a directory,
1583 which dates to when ClamAV needed us to break apart the email into the
1584 MIME parts (eg, with the now deprecated demime condition coming first).
1585 Some time back, ClamAV gained the ability to deconstruct the emails, so
1586 doing this would actually have resulted in the mail attachments being
1587 scanned twice, in the broken out files and from the original .eml.
1588 Since ClamAV now handles emails (and has for quite some time) we can
1589 just use the email file itself. */
1590 /* Pass the string to ClamAV (7 = "SCAN \n" + \0) */
1591 fits = string_format(file_name, sizeof(file_name), "SCAN %s\n",
1595 log_write(0, LOG_MAIN|LOG_PANIC,
1596 "malware filename does not fit in buffer [malware_internal() clamd]");
1599 DEBUG(D_acl) debug_printf("Malware scan: issuing %s local-path scan [%s]\n",
1600 scanner_name, clamd_options);
1602 if (send(sock, file_name, Ustrlen(file_name), 0) < 0) {
1604 log_write(0, LOG_MAIN|LOG_PANIC,"malware acl condition: clamd: unable to write to socket (%s)",
1609 /* Do not shut down the socket for writing; a user report noted that
1610 * clamd 0.70 does not react well to this. */
1612 /* Commands have been sent, no matter which scan method or connection
1613 * type we're using; now just read the result, independent of method. */
1615 /* Read the result */
1616 memset(av_buffer, 0, sizeof(av_buffer));
1617 bread = ip_recv(sock, av_buffer, sizeof(av_buffer), MALWARE_TIMEOUT);
1621 log_write(0, LOG_MAIN|LOG_PANIC,
1622 "malware acl condition: clamd: unable to read from socket (%s)",
1627 if (bread == sizeof(av_buffer)) {
1628 log_write(0, LOG_MAIN|LOG_PANIC,
1629 "malware acl condition: clamd: buffer too small");
1633 /* Check the result. ClamAV returns one of two result formats.
1634 In the basic mode, the response is of the form:
1635 infected: -> "<filename>: <virusname> FOUND"
1636 not-infected: -> "<filename>: OK"
1637 error: -> "<filename>: <errcode> ERROR
1638 If the ExtendedDetectionInfo option has been turned on, then we get:
1639 "<filename>: <virusname>(<virushash>:<virussize>) FOUND"
1640 for the infected case. Compare:
1641 /tmp/eicar.com: Eicar-Test-Signature FOUND
1642 /tmp/eicar.com: Eicar-Test-Signature(44d88612fea8a8f36de82e1278abb02f:68) FOUND
1644 In the streaming case, clamd uses the filename "stream" which you should
1645 be able to verify with { ktrace clamdscan --stream /tmp/eicar.com }. (The
1646 client app will replace "stream" with the original filename before returning
1647 results to stdout, but the trace shows the data).
1649 We will assume that the pathname passed to clamd from Exim does not contain
1650 a colon. We will have whined loudly above if the eml_filename does (and we're
1651 passing a filename to clamd). */
1653 if (!(*av_buffer)) {
1654 log_write(0, LOG_MAIN|LOG_PANIC,
1655 "malware acl condition: clamd: ClamAV returned null");
1659 /* strip newline at the end (won't be present for zINSTREAM)
1660 (also any trailing whitespace, which shouldn't exist, but we depend upon
1661 this below, so double-check) */
1662 p = av_buffer + Ustrlen(av_buffer) - 1;
1663 if (*p == '\n') *p = '\0';
1665 DEBUG(D_acl) debug_printf("Malware response: %s\n", av_buffer);
1667 while (isspace(*--p) && (p > av_buffer))
1672 /* colon in returned output? */
1673 if((p = Ustrchr(av_buffer,':')) == NULL) {
1674 log_write(0, LOG_MAIN|LOG_PANIC,
1675 "malware acl condition: clamd: ClamAV returned malformed result (missing colon): %s",
1680 /* strip filename */
1681 while (*p && isspace(*++p)) /**/;
1684 /* It would be bad to encounter a virus with "FOUND" in part of the name,
1685 but we should at least be resistant to it. */
1686 p = Ustrrchr(vname, ' ');
1692 if (Ustrcmp(result_tag, "FOUND") == 0) {
1693 /* p should still be the whitespace before the result_tag */
1694 while (isspace(*p)) --p;
1696 /* Strip off the extended information too, which will be in parens
1697 after the virus name, with no intervening whitespace. */
1699 /* "(hash:size)", so previous '(' will do; if not found, we have
1700 a curious virus name, but not an error. */
1701 p = Ustrrchr(vname, '(');
1705 Ustrncpy(malware_name_buffer, vname, sizeof(malware_name_buffer)-1);
1706 malware_name = malware_name_buffer;
1707 DEBUG(D_acl) debug_printf("Malware found, name \"%s\"\n", malware_name);
1709 } else if (Ustrcmp(result_tag, "ERROR") == 0) {
1710 log_write(0, LOG_MAIN|LOG_PANIC,
1711 "malware acl condition: clamd: ClamAV returned: %s",
1715 } else if (Ustrcmp(result_tag, "OK") == 0) {
1716 /* Everything should be OK */
1717 malware_name = NULL;
1718 DEBUG(D_acl) debug_printf("Malware not found\n");
1721 log_write(0, LOG_MAIN|LOG_PANIC,
1722 "malware acl condition: clamd: unparseable response from ClamAV: {%s}",
1729 /* ----------------------------------------------------------------------- */
1732 /* "mksd" scanner type --------------------------------------------------- */
1733 else if (strcmpic(scanner_name,US"mksd") == 0) {
1734 uschar *mksd_options;
1735 char *mksd_options_end;
1736 uschar mksd_options_buffer[32];
1737 int mksd_maxproc = 1; /* default, if no option supplied */
1738 struct sockaddr_un server;
1742 if ((mksd_options = string_nextinlist(&av_scanner_work, &sep,
1743 mksd_options_buffer,
1744 sizeof(mksd_options_buffer))) != NULL) {
1745 mksd_maxproc = (int) strtol(CS mksd_options, &mksd_options_end, 10);
1746 if ((*mksd_options == '\0') || (*mksd_options_end != '\0') ||
1747 (mksd_maxproc < 1) || (mksd_maxproc > 32)) {
1748 log_write(0, LOG_MAIN|LOG_PANIC,
1749 "malware acl condition: mksd: invalid option '%s'", mksd_options);
1754 /* open the mksd socket */
1755 sock = socket(AF_UNIX, SOCK_STREAM, 0);
1757 log_write(0, LOG_MAIN|LOG_PANIC,
1758 "malware acl condition: can't open UNIX socket.");
1761 server.sun_family = AF_UNIX;
1762 Ustrcpy(server.sun_path, "/var/run/mksd/socket");
1763 if (connect(sock, (struct sockaddr *) &server, sizeof(struct sockaddr_un)) < 0) {
1765 log_write(0, LOG_MAIN|LOG_PANIC,
1766 "malware acl condition: unable to connect to mksd UNIX socket (/var/run/mksd/socket). errno=%d", errno);
1770 malware_name = NULL;
1772 DEBUG(D_acl) debug_printf("Malware scan: issuing %s scan\n", scanner_name);
1774 retval = mksd_scan_packed(sock, eml_filename);
1779 /* ----------------------------------------------------------------------- */
1781 /* "unknown" scanner type ------------------------------------------------- */
1783 log_write(0, LOG_MAIN|LOG_PANIC,
1784 "malware condition: unknown scanner type '%s'", scanner_name);
1787 /* ----------------------------------------------------------------------- */
1789 /* set "been here, done that" marker */
1793 /* match virus name against pattern (caseless ------->----------v) */
1794 if ( (malware_name != NULL) &&
1795 (regex_match_and_setup(re, malware_name, 0, -1)) ) {
1796 DEBUG(D_acl) debug_printf("Matched regex to malware [%s] [%s]\n", malware_regex, malware_name);
1805 /* simple wrapper for reading lines from sockets */
1806 int recv_line(int sock, uschar *buffer, int size) {
1809 memset(buffer,0,size);
1811 while(recv(sock,p,1,0) > -1) {
1812 if ((p-buffer) > (size-2)) break;
1813 if (*p == '\n') break;
1814 if (*p != '\r') p++;
1822 /* ============= private routines for the "mksd" scanner type ============== */
1824 #include <sys/uio.h>
1826 static int mksd_writev (int sock, struct iovec *iov, int iovcnt)
1832 i = writev (sock, iov, iovcnt);
1833 while ((i < 0) && (errno == EINTR));
1836 log_write(0, LOG_MAIN|LOG_PANIC,
1837 "malware acl condition: unable to write to mksd UNIX socket (/var/run/mksd/socket)");
1842 if (i >= iov->iov_len) {
1849 iov->iov_base = CS iov->iov_base + i;
1855 static int mksd_read_lines (int sock, uschar *av_buffer, int av_buffer_size)
1861 if ((i = recv (sock, av_buffer+offset, av_buffer_size-offset, 0)) <= 0) {
1863 log_write(0, LOG_MAIN|LOG_PANIC,
1864 "malware acl condition: unable to read from mksd UNIX socket (/var/run/mksd/socket)");
1869 /* offset == av_buffer_size -> buffer full */
1870 if (offset == av_buffer_size) {
1872 log_write(0, LOG_MAIN|LOG_PANIC,
1873 "malware acl condition: malformed reply received from mksd");
1876 } while (av_buffer[offset-1] != '\n');
1878 av_buffer[offset] = '\0';
1882 static int mksd_parse_line (char *line)
1893 if ((p = strchr (line, '\n')) != NULL)
1895 log_write(0, LOG_MAIN|LOG_PANIC,
1896 "malware acl condition: mksd scanner failed: %s", line);
1900 if ((p = strchr (line, '\n')) != NULL) {
1902 if (((p-line) > 5) && ((p-line) < sizeof (malware_name_buffer)) && (line[3] == ' '))
1903 if (((p = strchr (line+4, ' ')) != NULL) && ((p-line) > 4)) {
1905 Ustrcpy (malware_name_buffer, line+4);
1906 malware_name = malware_name_buffer;
1910 log_write(0, LOG_MAIN|LOG_PANIC,
1911 "malware acl condition: malformed reply received from mksd: %s", line);
1916 static int mksd_scan_packed(int sock, uschar *scan_filename)
1918 struct iovec iov[3];
1919 const char *cmd = "MSQ\n";
1920 uschar av_buffer[1024];
1922 iov[0].iov_base = (void *) cmd;
1924 iov[1].iov_base = CS scan_filename;
1925 iov[1].iov_len = Ustrlen(scan_filename);
1926 iov[2].iov_base = (void *) (cmd + 3);
1929 if (mksd_writev (sock, iov, 3) < 0)
1932 if (mksd_read_lines (sock, av_buffer, sizeof (av_buffer)) < 0)
1937 return mksd_parse_line (CS av_buffer);