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];
73 fits = string_format(scan_filename, sizeof(scan_filename),
74 CS"%s/scan/%s/%s.eml", spool_directory, message_id, message_id);
77 log_write(0, LOG_MAIN|LOG_PANIC,
78 "malware filename does not fit in buffer [malware()]");
82 return malware_internal(listptr, scan_filename, FALSE);
86 /*************************************************
87 * Scan a file for malware *
88 *************************************************/
90 /* This is a test wrapper for scanning an email, which is not used in
91 normal processing. Scan any file, using the Exim scanning interface.
92 This function tampers with various global variables so is unsafe to use
96 eml_filename a file holding the message to be scanned
98 Returns: Exim message processing code (OK, FAIL, DEFER, ...)
99 where true means malware was found (condition applies)
102 malware_in_file(uschar *eml_filename) {
103 uschar *scan_options[2];
104 uschar message_id_buf[64];
107 scan_options[0] = US"*";
108 scan_options[1] = NULL;
110 /* spool_mbox() assumes various parameters exist, when creating
111 the relevant directory and the email within */
112 (void) string_format(message_id_buf, sizeof(message_id_buf),
113 "dummy-%d", pseudo_random_number(INT_MAX));
114 message_id = message_id_buf;
115 sender_address = US"malware-sender@example.net";
117 recipients_list = NULL;
118 receive_add_recipient(US"malware-victim@example.net", -1);
119 enable_dollar_recipients = TRUE;
121 ret = malware_internal(scan_options, eml_filename, TRUE);
123 Ustrncpy(spooled_message_id, message_id, sizeof(spooled_message_id));
125 /* don't set no_mbox_unspool; at present, there's no way for it to become
126 set, but if that changes, then it should apply to these tests too */
133 /*************************************************
134 * Scan content for malware *
135 *************************************************/
137 /* This is an internal interface for scanning an email; the normal interface
138 is via malware(), or there's malware_in_file() used for testing/debugging.
141 listptr the list of options to the "malware = ..." ACL condition
142 eml_filename the file holding the email to be scanned
143 faking whether or not we're faking this up for the -bmalware test
145 Returns: Exim message processing code (OK, FAIL, DEFER, ...)
146 where true means malware was found (condition applies)
148 static int malware_internal(uschar **listptr, uschar *eml_filename, BOOL faking) {
150 uschar *list = *listptr;
151 uschar *av_scanner_work = av_scanner;
152 uschar *scanner_name;
153 uschar scanner_name_buffer[16];
154 uschar *malware_regex;
155 uschar malware_regex_buffer[64];
156 uschar malware_regex_default[] = ".+";
157 unsigned long mbox_size;
161 const uschar *rerror;
163 /* make sure the eml mbox file is spooled up */
164 mbox_file = spool_mbox(&mbox_size, faking ? eml_filename : NULL);
165 if (mbox_file == NULL) {
166 /* error while spooling */
167 log_write(0, LOG_MAIN|LOG_PANIC,
168 "malware acl condition: error while creating mbox spool file");
171 /* none of our current scanners need the mbox
172 file as a stream, so we can close it right away */
173 (void)fclose(mbox_file);
175 /* extract the malware regex to match against from the option list */
176 if ((malware_regex = string_nextinlist(&list, &sep,
177 malware_regex_buffer,
178 sizeof(malware_regex_buffer))) != NULL) {
180 /* parse 1st option */
181 if ( (strcmpic(malware_regex,US"false") == 0) ||
182 (Ustrcmp(malware_regex,"0") == 0) ) {
183 /* explicitly no matching */
187 /* special cases (match anything except empty) */
188 if ( (strcmpic(malware_regex,US"true") == 0) ||
189 (Ustrcmp(malware_regex,"*") == 0) ||
190 (Ustrcmp(malware_regex,"1") == 0) ) {
191 malware_regex = malware_regex_default;
195 /* empty means "don't match anything" */
199 /* Reset sep that is set by previous string_nextinlist() call */
202 /* compile the regex, see if it works */
203 re = pcre_compile(CS malware_regex, PCRE_COPT, (const char **)&rerror, &roffset, NULL);
205 log_write(0, LOG_MAIN|LOG_PANIC,
206 "malware acl condition: regular expression error in '%s': %s at offset %d", malware_regex, rerror, roffset);
210 /* if av_scanner starts with a dollar, expand it first */
211 if (*av_scanner == '$') {
212 av_scanner_work = expand_string(av_scanner);
213 if (av_scanner_work == NULL) {
214 log_write(0, LOG_MAIN|LOG_PANIC,
215 "malware acl condition: av_scanner starts with $, but expansion failed: %s", expand_string_message);
219 debug_printf("Expanded av_scanner global: %s\n", av_scanner_work);
220 /* disable result caching in this case */
226 /* Do not scan twice. */
227 if (malware_ok == 0) {
229 /* find the scanner type from the av_scanner option */
230 if ((scanner_name = string_nextinlist(&av_scanner_work, &sep,
232 sizeof(scanner_name_buffer))) == NULL) {
233 /* no scanner given */
234 log_write(0, LOG_MAIN|LOG_PANIC,
235 "malware acl condition: av_scanner configuration variable is empty");
239 /* "f-protd" scanner type ----------------------------------------------- */
240 if (strcmpic(scanner_name, US"f-protd") == 0) {
241 uschar *fp_options, *fp_scan_option;
242 uschar fp_scan_option_buffer[1024];
243 uschar fp_options_buffer[1024];
244 uschar fp_options_default[] = "localhost 10200-10204";
245 uschar hostname[256];
246 unsigned int port, portlow, porthigh, connect_ok=0, detected=0, par_count = 0;
250 uschar scanrequest[2048], buf[32768], *strhelper, *strhelper2;
252 if ((fp_options = string_nextinlist(&av_scanner_work, &sep,
253 fp_options_buffer, sizeof(fp_options_buffer))) == NULL) {
254 /* no options supplied, use default options */
255 fp_options = fp_options_default;
258 /* extract host and port part */
259 if ( sscanf(CS fp_options, "%s %u-%u", hostname, &portlow, &porthigh) != 3 ) {
260 if ( sscanf(CS fp_options, "%s %u", hostname, &portlow) != 2 ) {
261 log_write(0, LOG_MAIN|LOG_PANIC,
262 "malware acl condition: f-protd: invalid socket '%s'", fp_options);
268 /* Lookup the host */
269 if((he = gethostbyname(CS hostname)) == 0) {
270 log_write(0, LOG_MAIN|LOG_PANIC,
271 "malware acl condition: f-protd: failed to lookup host '%s'", hostname);
275 in = *(struct in_addr *) he->h_addr_list[0];
279 /* Open the f-protd TCP socket */
280 if ( (sock = ip_socket(SOCK_STREAM, AF_INET)) < 0) {
281 log_write(0, LOG_MAIN|LOG_PANIC,
282 "malware acl condition: f-protd: unable to acquire socket (%s)",
287 /* Try to connect to all portslow-high until connection is established */
288 for (port = portlow; !connect_ok && port < porthigh; port++) {
289 if (ip_connect(sock, AF_INET, (uschar*)inet_ntoa(in), port, 5) >= 0) {
295 log_write(0, LOG_MAIN|LOG_PANIC,
296 "malware acl condition: f-protd: connection to %s, port %u-%u failed (%s)",
297 inet_ntoa(in), portlow, porthigh, strerror(errno));
302 DEBUG(D_acl) debug_printf("Malware scan: issuing %s GET\n", scanner_name);
303 (void)string_format(scanrequest, 1024, CS"GET %s", eml_filename);
305 while ((fp_scan_option = string_nextinlist(&av_scanner_work, &sep,
306 fp_scan_option_buffer, sizeof(fp_scan_option_buffer))) != NULL) {
308 Ustrcat(scanrequest, "%20");
310 Ustrcat(scanrequest, "?");
312 Ustrcat(scanrequest, fp_scan_option);
315 Ustrcat(scanrequest, " HTTP/1.0\r\n\r\n");
317 /* send scan request */
318 if (send(sock, &scanrequest, Ustrlen(scanrequest)+1, 0) < 0) {
320 log_write(0, LOG_MAIN|LOG_PANIC,
321 "malware acl condition: f-protd: unable to send command to socket (%s)", scanrequest);
325 /* We get a lot of empty lines, so we need this hack to check for any data at all */
326 while( recv(sock, buf, 1, MSG_PEEK) > 0 ) {
327 if ( recv_line(sock, buf, 32768) > 0) {
328 if ( Ustrstr(buf, US"<detected type=\"") != NULL ) {
330 } else if ( detected && (strhelper = Ustrstr(buf, US"<name>")) ) {
331 if ((strhelper2 = Ustrstr(buf, US"</name>")) != NULL) {
333 Ustrcpy(malware_name_buffer, strhelper + 6);
335 } else if ( Ustrstr(buf, US"<summary code=\"") ) {
336 if ( Ustrstr(buf, US"<summary code=\"11\">") ) {
337 malware_name = malware_name_buffer;
346 /* "drweb" scanner type ----------------------------------------------- */
347 /* v0.1 - added support for tcp sockets */
348 /* v0.0 - initial release -- support for unix sockets */
349 else if (strcmpic(scanner_name,US"drweb") == 0) {
350 uschar *drweb_options;
351 uschar drweb_options_buffer[1024];
352 uschar drweb_options_default[] = "/usr/local/drweb/run/drwebd.sock";
353 struct sockaddr_un server;
354 int sock, result, ovector[30];
355 unsigned int port, fsize;
356 uschar tmpbuf[1024], *drweb_fbuf;
357 uschar drweb_match_string[128];
358 int drweb_rc, drweb_cmd, drweb_flags = 0x0000, drweb_fd,
359 drweb_vnum, drweb_slen, drweb_fin = 0x0000;
361 uschar hostname[256];
366 if ((drweb_options = string_nextinlist(&av_scanner_work, &sep,
367 drweb_options_buffer, sizeof(drweb_options_buffer))) == NULL) {
368 /* no options supplied, use default options */
369 drweb_options = drweb_options_default;
372 if (*drweb_options != '/') {
374 /* extract host and port part */
375 if( sscanf(CS drweb_options, "%s %u", hostname, &port) != 2 ) {
376 log_write(0, LOG_MAIN|LOG_PANIC,
377 "malware acl condition: drweb: invalid socket '%s'", drweb_options);
381 /* Lookup the host */
382 if((he = gethostbyname(CS hostname)) == 0) {
383 log_write(0, LOG_MAIN|LOG_PANIC,
384 "malware acl condition: drweb: failed to lookup host '%s'", hostname);
388 in = *(struct in_addr *) he->h_addr_list[0];
390 /* Open the drwebd TCP socket */
391 if ( (sock = ip_socket(SOCK_STREAM, AF_INET)) < 0) {
392 log_write(0, LOG_MAIN|LOG_PANIC,
393 "malware acl condition: drweb: unable to acquire socket (%s)",
398 if (ip_connect(sock, AF_INET, (uschar*)inet_ntoa(in), port, 5) < 0) {
400 log_write(0, LOG_MAIN|LOG_PANIC,
401 "malware acl condition: drweb: connection to %s, port %u failed (%s)",
402 inet_ntoa(in), port, strerror(errno));
406 /* prepare variables */
407 drweb_cmd = htonl(DRWEBD_SCAN_CMD);
408 drweb_flags = htonl(DRWEBD_RETURN_VIRUSES | DRWEBD_IS_MAIL);
411 drweb_fd = open(CS eml_filename, O_RDONLY);
412 if (drweb_fd == -1) {
414 log_write(0, LOG_MAIN|LOG_PANIC,
415 "malware acl condition: drweb: can't open spool file %s: %s",
416 eml_filename, strerror(errno));
419 fsize = lseek(drweb_fd, 0, SEEK_END);
422 (void)close(drweb_fd);
423 log_write(0, LOG_MAIN|LOG_PANIC,
424 "malware acl condition: drweb: can't seek spool file %s: %s",
425 eml_filename, strerror(errno));
428 drweb_slen = htonl(fsize);
429 lseek(drweb_fd, 0, SEEK_SET);
431 DEBUG(D_acl) debug_printf("Malware scan: issuing %s remote scan [%s %u]\n",
432 scanner_name, hostname, port);
434 /* send scan request */
435 if ((send(sock, &drweb_cmd, sizeof(drweb_cmd), 0) < 0) ||
436 (send(sock, &drweb_flags, sizeof(drweb_flags), 0) < 0) ||
437 (send(sock, &drweb_fin, sizeof(drweb_fin), 0) < 0) ||
438 (send(sock, &drweb_slen, sizeof(drweb_slen), 0) < 0)) {
440 (void)close(drweb_fd);
441 log_write(0, LOG_MAIN|LOG_PANIC,
442 "malware acl condition: drweb: unable to send commands to socket (%s)", drweb_options);
446 drweb_fbuf = (uschar *) malloc (fsize);
449 (void)close(drweb_fd);
450 log_write(0, LOG_MAIN|LOG_PANIC,
451 "malware acl condition: drweb: unable to allocate memory %u for file (%s)",
452 fsize, eml_filename);
456 result = read (drweb_fd, drweb_fbuf, fsize);
459 (void)close(drweb_fd);
461 log_write(0, LOG_MAIN|LOG_PANIC,
462 "malware acl condition: drweb: can't read spool file %s: %s",
463 eml_filename, strerror(errno));
466 (void)close(drweb_fd);
468 /* send file body to socket */
469 if (send(sock, drweb_fbuf, fsize, 0) < 0) {
472 log_write(0, LOG_MAIN|LOG_PANIC,
473 "malware acl condition: drweb: unable to send file body to socket (%s)", drweb_options);
476 (void)close(drweb_fd);
479 /* open the drwebd UNIX socket */
480 sock = socket(AF_UNIX, SOCK_STREAM, 0);
482 log_write(0, LOG_MAIN|LOG_PANIC,
483 "malware acl condition: drweb: can't open UNIX socket");
486 server.sun_family = AF_UNIX;
487 Ustrcpy(server.sun_path, drweb_options);
488 if (connect(sock, (struct sockaddr *) &server, sizeof(struct sockaddr_un)) < 0) {
490 log_write(0, LOG_MAIN|LOG_PANIC,
491 "malware acl condition: drweb: unable to connect to socket (%s). errno=%d", drweb_options, errno);
495 /* prepare variables */
496 drweb_cmd = htonl(DRWEBD_SCAN_CMD);
497 drweb_flags = htonl(DRWEBD_RETURN_VIRUSES | DRWEBD_IS_MAIL);
498 drweb_slen = htonl(Ustrlen(eml_filename));
500 DEBUG(D_acl) debug_printf("Malware scan: issuing %s local scan [%s]\n",
501 scanner_name, drweb_options);
503 /* send scan request */
504 if ((send(sock, &drweb_cmd, sizeof(drweb_cmd), 0) < 0) ||
505 (send(sock, &drweb_flags, sizeof(drweb_flags), 0) < 0) ||
506 (send(sock, &drweb_slen, sizeof(drweb_slen), 0) < 0) ||
507 (send(sock, eml_filename, Ustrlen(eml_filename), 0) < 0) ||
508 (send(sock, &drweb_fin, sizeof(drweb_fin), 0) < 0)) {
510 log_write(0, LOG_MAIN|LOG_PANIC,
511 "malware acl condition: drweb: unable to send commands to socket (%s)", drweb_options);
516 /* wait for result */
517 if ((bread = recv(sock, &drweb_rc, sizeof(drweb_rc), 0) != sizeof(drweb_rc))) {
519 log_write(0, LOG_MAIN|LOG_PANIC,
520 "malware acl condition: drweb: unable to read return code");
523 drweb_rc = ntohl(drweb_rc);
525 if ((bread = recv(sock, &drweb_vnum, sizeof(drweb_vnum), 0) != sizeof(drweb_vnum))) {
527 log_write(0, LOG_MAIN|LOG_PANIC,
528 "malware acl condition: drweb: unable to read the number of viruses");
531 drweb_vnum = ntohl(drweb_vnum);
533 /* "virus(es) found" if virus number is > 0 */
537 uschar pre_malware_nb[256];
539 malware_name = malware_name_buffer;
541 /* setup default virus name */
542 Ustrcpy(malware_name_buffer,"unknown");
544 /* read and concatenate virus names into one string */
545 for (i=0;i<drweb_vnum;i++)
547 /* read the size of report */
548 if ((bread = recv(sock, &drweb_slen, sizeof(drweb_slen), 0) != sizeof(drweb_slen))) {
550 log_write(0, LOG_MAIN|LOG_PANIC,
551 "malware acl condition: drweb: cannot read report size");
554 drweb_slen = ntohl(drweb_slen);
556 /* read report body */
557 if ((bread = recv(sock, tmpbuf, drweb_slen, 0)) != drweb_slen) {
559 log_write(0, LOG_MAIN|LOG_PANIC,
560 "malware acl condition: drweb: cannot read report string");
563 tmpbuf[drweb_slen] = '\0';
565 /* set up match regex, depends on retcode */
566 Ustrcpy(drweb_match_string, "infected\\swith\\s*(.+?)$");
568 drweb_re = pcre_compile( CS drweb_match_string,
570 (const char **)&rerror,
574 /* try matcher on the line, grab substring */
575 result = pcre_exec(drweb_re, NULL, CS tmpbuf, Ustrlen(tmpbuf), 0, 0, ovector, 30);
577 pcre_copy_substring(CS tmpbuf, ovector, result, 1, CS pre_malware_nb, 255);
579 /* the first name we just copy to malware_name */
581 Ustrcpy(CS malware_name_buffer, CS pre_malware_nb);
583 /* concatenate each new virus name to previous */
584 int slen = Ustrlen(malware_name_buffer);
585 if (slen < (slen+Ustrlen(pre_malware_nb))) {
586 Ustrcat(malware_name_buffer, "/");
587 Ustrcat(malware_name_buffer, pre_malware_nb);
593 const char *drweb_s = NULL;
595 if (drweb_rc & DERR_READ_ERR) drweb_s = "read error";
596 if (drweb_rc & DERR_NOMEMORY) drweb_s = "no memory";
597 if (drweb_rc & DERR_TIMEOUT) drweb_s = "timeout";
598 if (drweb_rc & DERR_BAD_CALL) drweb_s = "wrong command";
599 /* retcodes DERR_SYMLINK, DERR_NO_REGFILE, DERR_SKIPPED.
600 * DERR_TOO_BIG, DERR_TOO_COMPRESSED, DERR_SPAM,
601 * DERR_CRC_ERROR, DERR_READSOCKET, DERR_WRITE_ERR
602 * and others are ignored */
604 log_write(0, LOG_MAIN|LOG_PANIC,
605 "malware acl condition: drweb: drweb daemon retcode 0x%x (%s)", drweb_rc, drweb_s);
614 /* ----------------------------------------------------------------------- */
615 else if (strcmpic(scanner_name,US"aveserver") == 0) {
617 uschar kav_options_buffer[1024];
618 uschar kav_options_default[] = "/var/run/aveserver";
620 struct sockaddr_un server;
624 if ((kav_options = string_nextinlist(&av_scanner_work, &sep,
626 sizeof(kav_options_buffer))) == NULL) {
627 /* no options supplied, use default options */
628 kav_options = kav_options_default;
631 /* open the aveserver socket */
632 sock = socket(AF_UNIX, SOCK_STREAM, 0);
634 log_write(0, LOG_MAIN|LOG_PANIC,
635 "malware acl condition: can't open UNIX socket.");
638 server.sun_family = AF_UNIX;
639 Ustrcpy(server.sun_path, kav_options);
640 if (connect(sock, (struct sockaddr *) &server, sizeof(struct sockaddr_un)) < 0) {
642 log_write(0, LOG_MAIN|LOG_PANIC,
643 "malware acl condition: unable to connect to aveserver UNIX socket (%s). errno=%d", kav_options, errno);
647 /* read aveserver's greeting and see if it is ready (2xx greeting) */
648 recv_line(sock, buf, 32768);
651 /* aveserver is having problems */
653 log_write(0, LOG_MAIN|LOG_PANIC,
654 "malware acl condition: aveserver is unavailable (Responded: %s).", ((buf[0] != 0) ? buf : (uschar *)"nothing") );
658 /* prepare our command */
659 (void)string_format(buf, 32768, "SCAN bPQRSTUW %s\r\n", eml_filename);
661 DEBUG(D_acl) debug_printf("Malware scan: issuing %s SCAN\n", scanner_name);
664 if (send(sock, buf, Ustrlen(buf), 0) < 0) {
666 log_write(0, LOG_MAIN|LOG_PANIC,
667 "malware acl condition: unable to write to aveserver UNIX socket (%s)", kav_options);
673 /* read response lines, find malware name and final response */
674 while (recv_line(sock, buf, 32768) > 0) {
675 debug_printf("aveserver: %s\n", buf);
678 } else if (buf[0] == '5') {
679 /* aveserver is having problems */
680 log_write(0, LOG_MAIN|LOG_PANIC,
681 "malware acl condition: unable to scan file %s (Responded: %s).",
685 } else if (Ustrncmp(buf,"322",3) == 0) {
686 uschar *p = Ustrchr(&buf[4],' ');
688 Ustrcpy(malware_name_buffer,&buf[4]);
689 malware_name = malware_name_buffer;
693 /* prepare our command */
694 (void)string_format(buf, 32768, "quit\r\n");
697 if (send(sock, buf, Ustrlen(buf), 0) < 0) {
699 log_write(0, LOG_MAIN|LOG_PANIC,
700 "malware acl condition: unable to write to aveserver UNIX socket (%s)", kav_options);
704 /* read aveserver's greeting and see if it is ready (2xx greeting) */
705 recv_line(sock, buf, 32768);
708 /* aveserver is having problems */
710 log_write(0, LOG_MAIN|LOG_PANIC,
711 "malware acl condition: unable to quit aveserver dialogue (Responded: %s).", ((buf[0] != 0) ? buf : (uschar *)"nothing") );
717 if (result == DEFER) return DEFER;
719 /* "fsecure" scanner type ------------------------------------------------- */
720 else if (strcmpic(scanner_name,US"fsecure") == 0) {
721 uschar *fsecure_options;
722 uschar fsecure_options_buffer[1024];
723 uschar fsecure_options_default[] = "/var/run/.fsav";
724 struct sockaddr_un server;
725 int sock, i, j, bread = 0;
726 uschar file_name[1024];
727 uschar av_buffer[1024];
729 static uschar *cmdoptions[] = { US"CONFIGURE\tARCHIVE\t1\n",
730 US"CONFIGURE\tTIMEOUT\t0\n",
731 US"CONFIGURE\tMAXARCH\t5\n",
732 US"CONFIGURE\tMIME\t1\n" };
735 if ((fsecure_options = string_nextinlist(&av_scanner_work, &sep,
736 fsecure_options_buffer,
737 sizeof(fsecure_options_buffer))) == NULL) {
738 /* no options supplied, use default options */
739 fsecure_options = fsecure_options_default;
742 /* open the fsecure socket */
743 sock = socket(AF_UNIX, SOCK_STREAM, 0);
745 log_write(0, LOG_MAIN|LOG_PANIC,
746 "malware acl condition: unable to open fsecure socket %s (%s)",
747 fsecure_options, strerror(errno));
750 server.sun_family = AF_UNIX;
751 Ustrcpy(server.sun_path, fsecure_options);
752 if (connect(sock, (struct sockaddr *) &server, sizeof(struct sockaddr_un)) < 0) {
754 log_write(0, LOG_MAIN|LOG_PANIC,
755 "malware acl condition: unable to connect to fsecure socket %s (%s)",
756 fsecure_options, strerror(errno));
760 DEBUG(D_acl) debug_printf("Malware scan: issuing %s scan [%s]\n",
761 scanner_name, fsecure_options);
764 memset(av_buffer, 0, sizeof(av_buffer));
765 for (i=0; i != 4; i++) {
766 /* debug_printf("send option \"%s\"",cmdoptions[i]); */
767 if (write(sock, cmdoptions[i], Ustrlen(cmdoptions[i])) < 0) {
769 log_write(0, LOG_MAIN|LOG_PANIC,
770 "malware acl condition: unable to write fsecure option %d to %s (%s)",
771 i, fsecure_options, strerror(errno));
775 bread = ip_recv(sock, av_buffer, sizeof(av_buffer), MALWARE_TIMEOUT);
776 if (bread >0) av_buffer[bread]='\0';
779 log_write(0, LOG_MAIN|LOG_PANIC,
780 "malware acl condition: unable to read fsecure answer %d (%s)", i, strerror(errno));
783 for (j=0;j<bread;j++) if((av_buffer[j]=='\r')||(av_buffer[j]=='\n')) av_buffer[j] ='@';
784 /* debug_printf("read answer %d read=%d \"%s\"\n", i, bread, av_buffer ); */
785 /* while (Ustrstr(av_buffer, "OK\tServer configured.@") == NULL); */
788 /* pass the mailfile to fsecure */
789 (void)string_format(file_name,1024,"SCAN\t%s\n", eml_filename);
790 /* debug_printf("send scan %s",file_name); */
791 if (write(sock, file_name, Ustrlen(file_name)) < 0) {
793 log_write(0, LOG_MAIN|LOG_PANIC,
794 "malware acl condition: unable to write fsecure scan to %s (%s)",
795 fsecure_options, strerror(errno));
800 /* todo also SUSPICION\t */
801 fs_inf = pcre_compile("\\S{0,5}INFECTED\\t[^\\t]*\\t([^\\t]+)\\t\\S*$", PCRE_COPT, (const char **)&rerror, &roffset, NULL);
803 /* read report, linewise */
807 memset(av_buffer, 0, sizeof(av_buffer));
809 bread=ip_recv(sock, &av_buffer[i], 1, MALWARE_TIMEOUT);
812 log_write(0, LOG_MAIN|LOG_PANIC,
813 "malware acl condition: unable to read fsecure result (%s)", strerror(errno));
818 while ((i < sizeof(av_buffer)-1 ) && (av_buffer[i-1] != '\n'));
819 av_buffer[i-1] = '\0';
820 /* debug_printf("got line \"%s\"\n",av_buffer); */
822 /* Really search for virus again? */
823 if (malware_name == NULL) {
824 /* try matcher on the line, grab substring */
825 i = pcre_exec(fs_inf, NULL, CS av_buffer, Ustrlen(av_buffer), 0, 0, ovector, 30);
828 pcre_copy_substring(CS av_buffer, ovector, i, 1, CS malware_name_buffer, 255);
829 malware_name = malware_name_buffer;
833 while (Ustrstr(av_buffer, "OK\tScan ok.") == NULL);
836 /* ----------------------------------------------------------------------- */
838 /* "kavdaemon" scanner type ------------------------------------------------ */
839 else if (strcmpic(scanner_name,US"kavdaemon") == 0) {
841 uschar kav_options_buffer[1024];
842 uschar kav_options_default[] = "/var/run/AvpCtl";
843 struct sockaddr_un server;
847 uschar scanrequest[1024];
848 uschar kav_match_string[128];
850 unsigned long kav_reportlen, bread;
855 if ((kav_options = string_nextinlist(&av_scanner_work, &sep,
857 sizeof(kav_options_buffer))) == NULL) {
858 /* no options supplied, use default options */
859 kav_options = kav_options_default;
862 /* open the kavdaemon socket */
863 sock = socket(AF_UNIX, SOCK_STREAM, 0);
865 log_write(0, LOG_MAIN|LOG_PANIC,
866 "malware acl condition: can't open UNIX socket.");
869 server.sun_family = AF_UNIX;
870 Ustrcpy(server.sun_path, kav_options);
871 if (connect(sock, (struct sockaddr *) &server, sizeof(struct sockaddr_un)) < 0) {
873 log_write(0, LOG_MAIN|LOG_PANIC,
874 "malware acl condition: unable to connect to kavdaemon UNIX socket (%s). errno=%d", kav_options, errno);
878 /* get current date and time, build scan request */
880 /* pdp note: before the eml_filename parameter, this scanned the
881 directory; not finding documentation, so we'll strip off the directory.
882 The side-effect is that the test framework scanning may end up in
883 scanning more than was requested, but for the normal interface, this is
885 strftime(CS tmpbuf, sizeof(tmpbuf), "<0>%d %b %H:%M:%S:%%s", localtime(&t));
886 fits = string_format(scanrequest, 1024,CS tmpbuf, eml_filename);
889 log_write(0, LOG_MAIN|LOG_PANIC,
890 "malware filename does not fit in buffer [malware_internal() kavdaemon]");
892 p = Ustrrchr(scanrequest, '/');
896 DEBUG(D_acl) debug_printf("Malware scan: issuing %s scan [%s]\n",
897 scanner_name, kav_options);
899 /* send scan request */
900 if (send(sock, scanrequest, Ustrlen(scanrequest)+1, 0) < 0) {
902 log_write(0, LOG_MAIN|LOG_PANIC,
903 "malware acl condition: unable to write to kavdaemon UNIX socket (%s)", kav_options);
907 /* wait for result */
908 if ((bread = recv(sock, tmpbuf, 2, 0) != 2)) {
910 log_write(0, LOG_MAIN|LOG_PANIC,
911 "malware acl condition: unable to read 2 bytes from kavdaemon socket.");
915 /* get errorcode from one nibble */
916 if (test_byte_order() == LITTLE_MY_ENDIAN) {
917 kav_rc = tmpbuf[0] & 0x0F;
920 kav_rc = tmpbuf[1] & 0x0F;
923 /* improper kavdaemon configuration */
924 if ( (kav_rc == 5) || (kav_rc == 6) ) {
926 log_write(0, LOG_MAIN|LOG_PANIC,
927 "malware acl condition: please reconfigure kavdaemon to NOT disinfect or remove infected files.");
933 log_write(0, LOG_MAIN|LOG_PANIC,
934 "malware acl condition: kavdaemon reported 'scanning not completed' (code 1).");
940 log_write(0, LOG_MAIN|LOG_PANIC,
941 "malware acl condition: kavdaemon reported 'kavdaemon damaged' (code 7).");
945 /* code 8 is not handled, since it is ambigous. It appears mostly on
946 bounces where part of a file has been cut off */
948 /* "virus found" return codes (2-4) */
949 if ((kav_rc > 1) && (kav_rc < 5)) {
952 /* setup default virus name */
953 Ustrcpy(malware_name_buffer,"unknown");
954 malware_name = malware_name_buffer;
956 if (test_byte_order() == LITTLE_MY_ENDIAN) {
957 report_flag = tmpbuf[1];
960 report_flag = tmpbuf[0];
963 /* read the report, if available */
964 if( report_flag == 1 ) {
965 /* read report size */
966 if ((bread = recv(sock, &kav_reportlen, 4, 0)) != 4) {
968 log_write(0, LOG_MAIN|LOG_PANIC,
969 "malware acl condition: cannot read report size from kavdaemon");
973 /* it's possible that avp returns av_buffer[1] == 1 but the
974 reportsize is 0 (!?) */
975 if (kav_reportlen > 0) {
976 /* set up match regex, depends on retcode */
978 Ustrcpy(kav_match_string, "suspicion:\\s*(.+?)\\s*$");
980 Ustrcpy(kav_match_string, "infected:\\s*(.+?)\\s*$");
982 kav_re = pcre_compile( CS kav_match_string,
984 (const char **)&rerror,
988 /* read report, linewise */
989 while (kav_reportlen > 0) {
994 while ( recv(sock, &tmpbuf[bread], 1, 0) == 1 ) {
996 if ( (tmpbuf[bread] == '\n') || (bread > 1021) ) break;
1000 tmpbuf[bread] = '\0';
1002 /* try matcher on the line, grab substring */
1003 result = pcre_exec(kav_re, NULL, CS tmpbuf, Ustrlen(tmpbuf), 0, 0, ovector, 30);
1005 pcre_copy_substring(CS tmpbuf, ovector, result, 1, CS malware_name_buffer, 255);
1013 /* no virus found */
1014 malware_name = NULL;
1019 /* ----------------------------------------------------------------------- */
1022 /* "cmdline" scanner type ------------------------------------------------ */
1023 else if (strcmpic(scanner_name,US"cmdline") == 0) {
1024 uschar *cmdline_scanner;
1025 uschar cmdline_scanner_buffer[1024];
1026 uschar *cmdline_trigger;
1027 uschar cmdline_trigger_buffer[1024];
1028 const pcre *cmdline_trigger_re;
1029 uschar *cmdline_regex;
1030 uschar cmdline_regex_buffer[1024];
1031 const pcre *cmdline_regex_re;
1032 uschar file_name[1024];
1033 uschar commandline[1024];
1034 void (*eximsigchld)(int);
1035 void (*eximsigpipe)(int);
1036 FILE *scanner_out = NULL;
1037 FILE *scanner_record = NULL;
1038 uschar linebuffer[32767];
1045 /* find scanner command line */
1046 if ((cmdline_scanner = string_nextinlist(&av_scanner_work, &sep,
1047 cmdline_scanner_buffer,
1048 sizeof(cmdline_scanner_buffer))) == NULL) {
1049 /* no command line supplied */
1050 log_write(0, LOG_MAIN|LOG_PANIC,
1051 "malware acl condition: missing commandline specification for cmdline scanner type.");
1055 /* find scanner output trigger */
1056 if ((cmdline_trigger = string_nextinlist(&av_scanner_work, &sep,
1057 cmdline_trigger_buffer,
1058 sizeof(cmdline_trigger_buffer))) == NULL) {
1059 /* no trigger regex supplied */
1060 log_write(0, LOG_MAIN|LOG_PANIC,
1061 "malware acl condition: missing trigger specification for cmdline scanner type.");
1065 /* precompile trigger regex */
1066 cmdline_trigger_re = pcre_compile(CS cmdline_trigger, PCRE_COPT, (const char **)&rerror, &roffset, NULL);
1067 if (cmdline_trigger_re == NULL) {
1068 log_write(0, LOG_MAIN|LOG_PANIC,
1069 "malware acl condition: regular expression error in '%s': %s at offset %d", cmdline_trigger_re, rerror, roffset);
1073 /* find scanner name regex */
1074 if ((cmdline_regex = string_nextinlist(&av_scanner_work, &sep,
1075 cmdline_regex_buffer,
1076 sizeof(cmdline_regex_buffer))) == NULL) {
1077 /* no name regex supplied */
1078 log_write(0, LOG_MAIN|LOG_PANIC,
1079 "malware acl condition: missing virus name regex specification for cmdline scanner type.");
1083 /* precompile name regex */
1084 cmdline_regex_re = pcre_compile(CS cmdline_regex, PCRE_COPT, (const char **)&rerror, &roffset, NULL);
1085 if (cmdline_regex_re == NULL) {
1086 log_write(0, LOG_MAIN|LOG_PANIC,
1087 "malware acl condition: regular expression error in '%s': %s at offset %d", cmdline_regex_re, rerror, roffset);
1091 /* prepare scanner call; despite the naming, file_name holds a directory
1092 name which is documented as the value given to %s. */
1093 if (Ustrlen(eml_filename) > sizeof(file_name) - 1)
1095 log_write(0, LOG_MAIN|LOG_PANIC,
1096 "malware filename does not fit in buffer [malware_internal() cmdline]");
1099 Ustrcpy(file_name, eml_filename);
1100 p = Ustrrchr(file_name, '/');
1103 fits = string_format(commandline, sizeof(commandline), CS cmdline_scanner, file_name);
1106 log_write(0, LOG_MAIN|LOG_PANIC,
1107 "cmdline scanner command-line does not fit in buffer");
1111 /* redirect STDERR too */
1112 if (Ustrlen(commandline) + 5 > sizeof(commandline))
1114 log_write(0, LOG_MAIN|LOG_PANIC,
1115 "cmdline scanner command-line does not fit in buffer (STDERR redirect)");
1118 Ustrcat(commandline," 2>&1");
1120 DEBUG(D_acl) debug_printf("Malware scan: issuing %s scan [%s]\n", scanner_name, commandline);
1122 /* store exims signal handlers */
1123 eximsigchld = signal(SIGCHLD,SIG_DFL);
1124 eximsigpipe = signal(SIGPIPE,SIG_DFL);
1126 scanner_out = popen(CS commandline,"r");
1127 if (scanner_out == NULL) {
1128 log_write(0, LOG_MAIN|LOG_PANIC,
1129 "malware acl condition: calling cmdline scanner (%s) failed: %s.", commandline, strerror(errno));
1130 signal(SIGCHLD,eximsigchld);
1131 signal(SIGPIPE,eximsigpipe);
1135 (void)string_format(file_name,1024,"%s/scan/%s/%s_scanner_output", spool_directory, message_id, message_id);
1136 scanner_record = modefopen(file_name,"wb",SPOOL_MODE);
1138 if (scanner_record == NULL) {
1139 log_write(0, LOG_MAIN|LOG_PANIC,
1140 "malware acl condition: opening scanner output file (%s) failed: %s.", file_name, strerror(errno));
1141 pclose(scanner_out);
1142 signal(SIGCHLD,eximsigchld);
1143 signal(SIGPIPE,eximsigpipe);
1147 /* look for trigger while recording output */
1148 while(fgets(CS linebuffer,32767,scanner_out) != NULL) {
1149 if ( Ustrlen(linebuffer) > fwrite(linebuffer, 1, Ustrlen(linebuffer), scanner_record) ) {
1151 log_write(0, LOG_MAIN|LOG_PANIC,
1152 "malware acl condition: short write on scanner output file (%s).", file_name);
1153 pclose(scanner_out);
1154 signal(SIGCHLD,eximsigchld);
1155 signal(SIGPIPE,eximsigpipe);
1158 /* try trigger match */
1159 if (!trigger && regex_match_and_setup(cmdline_trigger_re, linebuffer, 0, -1))
1163 (void)fclose(scanner_record);
1164 pclose(scanner_out);
1165 signal(SIGCHLD,eximsigchld);
1166 signal(SIGPIPE,eximsigpipe);
1169 /* setup default virus name */
1170 Ustrcpy(malware_name_buffer,"unknown");
1171 malware_name = malware_name_buffer;
1173 /* re-open the scanner output file, look for name match */
1174 scanner_record = fopen(CS file_name,"rb");
1175 while(fgets(CS linebuffer,32767,scanner_record) != NULL) {
1177 result = pcre_exec(cmdline_regex_re, NULL, CS linebuffer, Ustrlen(linebuffer), 0, 0, ovector, 30);
1179 pcre_copy_substring(CS linebuffer, ovector, result, 1, CS malware_name_buffer, 255);
1182 (void)fclose(scanner_record);
1185 /* no virus found */
1186 malware_name = NULL;
1189 /* ----------------------------------------------------------------------- */
1192 /* "sophie" scanner type ------------------------------------------------- */
1193 else if (strcmpic(scanner_name,US"sophie") == 0) {
1194 uschar *sophie_options;
1195 uschar sophie_options_buffer[1024];
1196 uschar sophie_options_default[] = "/var/run/sophie";
1198 struct sockaddr_un server;
1201 uschar file_name[1024];
1202 uschar av_buffer[1024];
1204 if ((sophie_options = string_nextinlist(&av_scanner_work, &sep,
1205 sophie_options_buffer,
1206 sizeof(sophie_options_buffer))) == NULL) {
1207 /* no options supplied, use default options */
1208 sophie_options = sophie_options_default;
1211 /* open the sophie socket */
1212 sock = socket(AF_UNIX, SOCK_STREAM, 0);
1214 log_write(0, LOG_MAIN|LOG_PANIC,
1215 "malware acl condition: can't open UNIX socket.");
1218 server.sun_family = AF_UNIX;
1219 Ustrcpy(server.sun_path, sophie_options);
1220 if (connect(sock, (struct sockaddr *) &server, sizeof(struct sockaddr_un)) < 0) {
1222 log_write(0, LOG_MAIN|LOG_PANIC,
1223 "malware acl condition: unable to connect to sophie UNIX socket (%s). errno=%d", sophie_options, errno);
1227 /* pass the scan directory to sophie */
1228 len = Ustrlen(eml_filename) + 1;
1229 if (len > sizeof(file_name))
1232 log_write(0, LOG_MAIN|LOG_PANIC,
1233 "malware filename does not fit in buffer [malware_internal() sophie]");
1236 memcpy(file_name, eml_filename, len);
1237 p = Ustrrchr(file_name, '/');
1241 DEBUG(D_acl) debug_printf("Malware scan: issuing %s scan [%s]\n",
1242 scanner_name, sophie_options);
1244 if (write(sock, file_name, Ustrlen(file_name)) < 0) {
1246 log_write(0, LOG_MAIN|LOG_PANIC,
1247 "malware acl condition: unable to write to sophie UNIX socket (%s)", sophie_options);
1251 (void)write(sock, "\n", 1);
1253 /* wait for result */
1254 memset(av_buffer, 0, sizeof(av_buffer));
1255 if ((!(bread = ip_recv(sock, av_buffer, sizeof(av_buffer), MALWARE_TIMEOUT)) > 0)) {
1257 log_write(0, LOG_MAIN|LOG_PANIC,
1258 "malware acl condition: unable to read from sophie UNIX socket (%s)", sophie_options);
1265 if (av_buffer[0] == '1') {
1266 if (Ustrchr(av_buffer, '\n')) *Ustrchr(av_buffer, '\n') = '\0';
1267 Ustrcpy(malware_name_buffer,&av_buffer[2]);
1268 malware_name = malware_name_buffer;
1270 else if (!strncmp(CS av_buffer, "-1", 2)) {
1271 log_write(0, LOG_MAIN|LOG_PANIC,
1272 "malware acl condition: malware acl condition: sophie reported error");
1276 /* all ok, no virus */
1277 malware_name = NULL;
1280 /* ----------------------------------------------------------------------- */
1283 /* "clamd" scanner type ------------------------------------------------- */
1284 /* This code was originally contributed by David Saez */
1285 /* There are three scanning methods available to us:
1286 * (1) Use the SCAN command, pointing to a file in the filesystem
1287 * (2) Use the STREAM command, send the data on a separate port
1288 * (3) Use the zINSTREAM command, send the data inline
1289 * The zINSTREAM command was introduced with ClamAV 0.95, which marked
1290 * STREAM deprecated; see: http://wiki.clamav.net/bin/view/Main/UpgradeNotes095
1291 * In Exim, we use SCAN if using a Unix-domain socket or explicitly told that
1292 * the TCP-connected daemon is actually local; otherwise we use zINSTREAM unless
1293 * WITH_OLD_CLAMAV_STREAM is defined.
1294 * See Exim bug 926 for details. */
1295 else if (strcmpic(scanner_name,US"clamd") == 0) {
1296 uschar *clamd_options;
1297 uschar clamd_options_buffer[1024];
1298 uschar clamd_options_default[] = "/tmp/clamd";
1299 uschar *p, *vname, *result_tag, *response_end;
1300 struct sockaddr_un server;
1303 uschar file_name[1024];
1304 uschar av_buffer[1024];
1305 uschar hostname[256];
1308 uschar *clamd_options2;
1309 uschar clamd_options2_buffer[1024];
1310 uschar clamd_options2_default[] = "";
1311 uschar *clamav_fbuf;
1312 int clam_fd, result;
1314 BOOL use_scan_command, fits;
1315 #ifdef WITH_OLD_CLAMAV_STREAM
1316 uschar av_buffer2[1024];
1319 uint32_t send_size, send_final_zeroblock;
1322 if ((clamd_options = string_nextinlist(&av_scanner_work, &sep,
1323 clamd_options_buffer,
1324 sizeof(clamd_options_buffer))) == NULL) {
1325 /* no options supplied, use default options */
1326 clamd_options = clamd_options_default;
1328 if ((clamd_options2 = string_nextinlist(&av_scanner_work, &sep,
1329 clamd_options2_buffer,
1330 sizeof(clamd_options2_buffer))) == NULL) {
1331 clamd_options2 = clamd_options2_default;
1334 if ((*clamd_options == '/') || (strcmpic(clamd_options2,US"local") == 0))
1335 use_scan_command = TRUE;
1337 use_scan_command = FALSE;
1339 /* See the discussion of response formats below to see why we really don't
1340 like colons in filenames when passing filenames to ClamAV. */
1341 if (use_scan_command && Ustrchr(eml_filename, ':')) {
1342 log_write(0, LOG_MAIN|LOG_PANIC,
1343 "malware acl condition: clamd: local/SCAN mode incompatible with" \
1344 " : in path to email filename [%s]", eml_filename);
1348 /* socket does not start with '/' -> network socket */
1349 if (*clamd_options != '/') {
1351 /* Confirmed in ClamAV source (0.95.3) that the TCPAddr option of clamd
1352 * only supports AF_INET, but we should probably be looking to the
1353 * future and rewriting this to be protocol-independent anyway. */
1355 /* extract host and port part */
1356 if( sscanf(CS clamd_options, "%s %u", hostname, &port) != 2 ) {
1357 log_write(0, LOG_MAIN|LOG_PANIC,
1358 "malware acl condition: clamd: invalid socket '%s'", clamd_options);
1362 /* Lookup the host */
1363 if((he = gethostbyname(CS hostname)) == 0) {
1364 log_write(0, LOG_MAIN|LOG_PANIC,
1365 "malware acl condition: clamd: failed to lookup host '%s'", hostname);
1369 in = *(struct in_addr *) he->h_addr_list[0];
1371 /* Open the ClamAV Socket */
1372 if ( (sock = ip_socket(SOCK_STREAM, AF_INET)) < 0) {
1373 log_write(0, LOG_MAIN|LOG_PANIC,
1374 "malware acl condition: clamd: unable to acquire socket (%s)",
1379 if (ip_connect(sock, AF_INET, (uschar*)inet_ntoa(in), port, 5) < 0) {
1381 log_write(0, LOG_MAIN|LOG_PANIC,
1382 "malware acl condition: clamd: connection to %s, port %u failed (%s)",
1383 inet_ntoa(in), port, strerror(errno));
1388 /* open the local socket */
1389 if ((sock = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
1390 log_write(0, LOG_MAIN|LOG_PANIC,
1391 "malware acl condition: clamd: unable to acquire socket (%s)",
1396 server.sun_family = AF_UNIX;
1397 Ustrcpy(server.sun_path, clamd_options);
1399 if (connect(sock, (struct sockaddr *) &server, sizeof(struct sockaddr_un)) < 0) {
1401 log_write(0, LOG_MAIN|LOG_PANIC,
1402 "malware acl condition: clamd: unable to connect to UNIX socket %s (%s)",
1403 clamd_options, strerror(errno) );
1408 /* have socket in variable "sock"; command to use is semi-independent of
1409 * the socket protocol. We use SCAN if is local (either Unix/local
1410 * domain socket, or explicitly told local) else we stream the data.
1411 * How we stream the data depends upon how we were built. */
1413 if (!use_scan_command) {
1415 #ifdef WITH_OLD_CLAMAV_STREAM
1416 /* "STREAM\n" command, get back a "PORT <N>\n" response, send data to
1417 * that port on a second connection; then in the scan-method-neutral
1418 * part, read the response back on the original connection. */
1420 DEBUG(D_acl) debug_printf("Malware scan: issuing %s old-style remote scan (PORT)\n",
1423 /* Pass the string to ClamAV (7 = "STREAM\n") */
1424 if (send(sock, "STREAM\n", 7, 0) < 0) {
1425 log_write(0, LOG_MAIN|LOG_PANIC,"malware acl condition: clamd: unable to write to socket (%s)",
1430 memset(av_buffer2, 0, sizeof(av_buffer2));
1431 bread = ip_recv(sock, av_buffer2, sizeof(av_buffer2), MALWARE_TIMEOUT);
1434 log_write(0, LOG_MAIN|LOG_PANIC,
1435 "malware acl condition: clamd: unable to read PORT from socket (%s)",
1441 if (bread == sizeof(av_buffer)) {
1442 log_write(0, LOG_MAIN|LOG_PANIC,
1443 "malware acl condition: clamd: buffer too small");
1448 if (!(*av_buffer2)) {
1449 log_write(0, LOG_MAIN|LOG_PANIC,
1450 "malware acl condition: clamd: ClamAV returned null");
1455 av_buffer2[bread] = '\0';
1456 if( sscanf(CS av_buffer2, "PORT %u\n", &port) != 1 ) {
1457 log_write(0, LOG_MAIN|LOG_PANIC,
1458 "malware acl condition: clamd: Expected port information from clamd, got '%s'", av_buffer2);
1463 if ( (sockData = ip_socket(SOCK_STREAM, AF_INET)) < 0) {
1464 log_write(0, LOG_MAIN|LOG_PANIC,
1465 "malware acl condition: clamd: unable to acquire socket (%s)",
1471 if (ip_connect(sockData, AF_INET, (uschar*)inet_ntoa(in), port, 5) < 0) {
1472 log_write(0, LOG_MAIN|LOG_PANIC,
1473 "malware acl condition: clamd: connection to %s, port %u failed (%s)",
1474 inet_ntoa(in), port, strerror(errno));
1475 (void)close(sockData); (void)close(sock);
1479 #define CLOSE_SOCKDATA (void)close(sockData)
1480 #else /* WITH_OLD_CLAMAV_STREAM not defined */
1481 /* New protocol: "zINSTREAM\n" followed by a sequence of <length><data>
1482 chunks, <n> a 4-byte number (network order), terminated by a zero-length
1485 DEBUG(D_acl) debug_printf("Malware scan: issuing %s new-style remote scan (zINSTREAM)\n",
1488 /* Pass the string to ClamAV (10 = "zINSTREAM\0") */
1489 if (send(sock, "zINSTREAM", 10, 0) < 0) {
1490 log_write(0, LOG_MAIN|LOG_PANIC,
1491 "malware acl condition: clamd: unable to send zINSTREAM to socket (%s)",
1497 #define CLOSE_SOCKDATA /**/
1500 /* calc file size */
1501 clam_fd = open(CS eml_filename, O_RDONLY);
1502 if (clam_fd == -1) {
1503 log_write(0, LOG_MAIN|LOG_PANIC,
1504 "malware acl condition: clamd: can't open spool file %s: %s",
1505 eml_filename, strerror(errno));
1506 CLOSE_SOCKDATA; (void)close(sock);
1509 fsize = lseek(clam_fd, 0, SEEK_END);
1511 log_write(0, LOG_MAIN|LOG_PANIC,
1512 "malware acl condition: clamd: can't seek spool file %s: %s",
1513 eml_filename, strerror(errno));
1514 CLOSE_SOCKDATA; (void)close(sock);
1517 lseek(clam_fd, 0, SEEK_SET);
1519 clamav_fbuf = (uschar *) malloc (fsize);
1521 log_write(0, LOG_MAIN|LOG_PANIC,
1522 "malware acl condition: clamd: unable to allocate memory %u for file (%s)",
1523 fsize, eml_filename);
1524 CLOSE_SOCKDATA; (void)close(sock); (void)close(clam_fd);
1528 result = read (clam_fd, clamav_fbuf, fsize);
1530 log_write(0, LOG_MAIN|LOG_PANIC,
1531 "malware acl condition: clamd: can't read spool file %s: %s",
1532 eml_filename, strerror(errno));
1533 CLOSE_SOCKDATA; (void)close(sock); (void)close(clam_fd);
1537 (void)close(clam_fd);
1539 /* send file body to socket */
1540 #ifdef WITH_OLD_CLAMAV_STREAM
1541 if (send(sockData, clamav_fbuf, fsize, 0) < 0) {
1542 log_write(0, LOG_MAIN|LOG_PANIC,
1543 "malware acl condition: clamd: unable to send file body to socket (%s:%u)", hostname, port);
1544 CLOSE_SOCKDATA; (void)close(sock);
1549 send_size = htonl(fsize);
1550 send_final_zeroblock = 0;
1551 if ((send(sock, &send_size, sizeof(send_size), 0) < 0) ||
1552 (send(sock, clamav_fbuf, fsize, 0) < 0) ||
1553 (send(sock, &send_final_zeroblock, sizeof(send_final_zeroblock), 0) < 0))
1555 log_write(0, LOG_MAIN|LOG_PANIC,
1556 "malware acl condition: clamd: unable to send file body to socket (%s:%u)", hostname, port);
1566 #undef CLOSE_SOCKDATA
1568 } else { /* use scan command */
1569 /* Send a SCAN command pointing to a filename; then in the then in the
1570 * scan-method-neutral part, read the response back */
1572 /* ================================================================= */
1574 /* Prior to the reworking post-Exim-4.72, this scanned a directory,
1575 which dates to when ClamAV needed us to break apart the email into the
1576 MIME parts (eg, with the now deprecated demime condition coming first).
1577 Some time back, ClamAV gained the ability to deconstruct the emails, so
1578 doing this would actually have resulted in the mail attachments being
1579 scanned twice, in the broken out files and from the original .eml.
1580 Since ClamAV now handles emails (and has for quite some time) we can
1581 just use the email file itself. */
1582 /* Pass the string to ClamAV (7 = "SCAN \n" + \0) */
1583 fits = string_format(file_name, sizeof(file_name), "SCAN %s\n",
1587 log_write(0, LOG_MAIN|LOG_PANIC,
1588 "malware filename does not fit in buffer [malware_internal() clamd]");
1591 DEBUG(D_acl) debug_printf("Malware scan: issuing %s local-path scan [%s]\n",
1592 scanner_name, clamd_options);
1594 if (send(sock, file_name, Ustrlen(file_name), 0) < 0) {
1596 log_write(0, LOG_MAIN|LOG_PANIC,"malware acl condition: clamd: unable to write to socket (%s)",
1601 /* Do not shut down the socket for writing; a user report noted that
1602 * clamd 0.70 does not react well to this. */
1604 /* Commands have been sent, no matter which scan method or connection
1605 * type we're using; now just read the result, independent of method. */
1607 /* Read the result */
1608 memset(av_buffer, 0, sizeof(av_buffer));
1609 bread = ip_recv(sock, av_buffer, sizeof(av_buffer), MALWARE_TIMEOUT);
1613 log_write(0, LOG_MAIN|LOG_PANIC,
1614 "malware acl condition: clamd: unable to read from socket (%s)",
1619 if (bread == sizeof(av_buffer)) {
1620 log_write(0, LOG_MAIN|LOG_PANIC,
1621 "malware acl condition: clamd: buffer too small");
1625 /* Check the result. ClamAV returns one of two result formats.
1626 In the basic mode, the response is of the form:
1627 infected: -> "<filename>: <virusname> FOUND"
1628 not-infected: -> "<filename>: OK"
1629 error: -> "<filename>: <errcode> ERROR
1630 If the ExtendedDetectionInfo option has been turned on, then we get:
1631 "<filename>: <virusname>(<virushash>:<virussize>) FOUND"
1632 for the infected case. Compare:
1633 /tmp/eicar.com: Eicar-Test-Signature FOUND
1634 /tmp/eicar.com: Eicar-Test-Signature(44d88612fea8a8f36de82e1278abb02f:68) FOUND
1636 In the streaming case, clamd uses the filename "stream" which you should
1637 be able to verify with { ktrace clamdscan --stream /tmp/eicar.com }. (The
1638 client app will replace "stream" with the original filename before returning
1639 results to stdout, but the trace shows the data).
1641 We will assume that the pathname passed to clamd from Exim does not contain
1642 a colon. We will have whined loudly above if the eml_filename does (and we're
1643 passing a filename to clamd). */
1645 if (!(*av_buffer)) {
1646 log_write(0, LOG_MAIN|LOG_PANIC,
1647 "malware acl condition: clamd: ClamAV returned null");
1651 /* strip newline at the end (won't be present for zINSTREAM)
1652 (also any trailing whitespace, which shouldn't exist, but we depend upon
1653 this below, so double-check) */
1654 p = av_buffer + Ustrlen(av_buffer) - 1;
1655 if (*p == '\n') *p = '\0';
1657 DEBUG(D_acl) debug_printf("Malware response: %s\n", av_buffer);
1659 while (isspace(*--p) && (p > av_buffer))
1664 /* colon in returned output? */
1665 if((p = Ustrchr(av_buffer,':')) == NULL) {
1666 log_write(0, LOG_MAIN|LOG_PANIC,
1667 "malware acl condition: clamd: ClamAV returned malformed result (missing colon): %s",
1672 /* strip filename */
1673 while (*p && isspace(*++p)) /**/;
1676 /* It would be bad to encounter a virus with "FOUND" in part of the name,
1677 but we should at least be resistant to it. */
1678 p = Ustrrchr(vname, ' ');
1684 if (Ustrcmp(result_tag, "FOUND") == 0) {
1685 /* p should still be the whitespace before the result_tag */
1686 while (isspace(*p)) --p;
1688 /* Strip off the extended information too, which will be in parens
1689 after the virus name, with no intervening whitespace. */
1691 /* "(hash:size)", so previous '(' will do; if not found, we have
1692 a curious virus name, but not an error. */
1693 p = Ustrrchr(vname, '(');
1697 Ustrncpy(malware_name_buffer, vname, sizeof(malware_name_buffer)-1);
1698 malware_name = malware_name_buffer;
1699 DEBUG(D_acl) debug_printf("Malware found, name \"%s\"\n", malware_name);
1701 } else if (Ustrcmp(result_tag, "ERROR") == 0) {
1702 log_write(0, LOG_MAIN|LOG_PANIC,
1703 "malware acl condition: clamd: ClamAV returned: %s",
1707 } else if (Ustrcmp(result_tag, "OK") == 0) {
1708 /* Everything should be OK */
1709 malware_name = NULL;
1710 DEBUG(D_acl) debug_printf("Malware not found\n");
1713 log_write(0, LOG_MAIN|LOG_PANIC,
1714 "malware acl condition: clamd: unparseable response from ClamAV: {%s}",
1721 /* ----------------------------------------------------------------------- */
1724 /* "mksd" scanner type --------------------------------------------------- */
1725 else if (strcmpic(scanner_name,US"mksd") == 0) {
1726 uschar *mksd_options;
1727 char *mksd_options_end;
1728 uschar mksd_options_buffer[32];
1729 int mksd_maxproc = 1; /* default, if no option supplied */
1730 struct sockaddr_un server;
1734 if ((mksd_options = string_nextinlist(&av_scanner_work, &sep,
1735 mksd_options_buffer,
1736 sizeof(mksd_options_buffer))) != NULL) {
1737 mksd_maxproc = (int) strtol(CS mksd_options, &mksd_options_end, 10);
1738 if ((*mksd_options == '\0') || (*mksd_options_end != '\0') ||
1739 (mksd_maxproc < 1) || (mksd_maxproc > 32)) {
1740 log_write(0, LOG_MAIN|LOG_PANIC,
1741 "malware acl condition: mksd: invalid option '%s'", mksd_options);
1746 /* open the mksd socket */
1747 sock = socket(AF_UNIX, SOCK_STREAM, 0);
1749 log_write(0, LOG_MAIN|LOG_PANIC,
1750 "malware acl condition: can't open UNIX socket.");
1753 server.sun_family = AF_UNIX;
1754 Ustrcpy(server.sun_path, "/var/run/mksd/socket");
1755 if (connect(sock, (struct sockaddr *) &server, sizeof(struct sockaddr_un)) < 0) {
1757 log_write(0, LOG_MAIN|LOG_PANIC,
1758 "malware acl condition: unable to connect to mksd UNIX socket (/var/run/mksd/socket). errno=%d", errno);
1762 malware_name = NULL;
1764 DEBUG(D_acl) debug_printf("Malware scan: issuing %s scan\n", scanner_name);
1766 retval = mksd_scan_packed(sock, eml_filename);
1771 /* ----------------------------------------------------------------------- */
1773 /* "unknown" scanner type ------------------------------------------------- */
1775 log_write(0, LOG_MAIN|LOG_PANIC,
1776 "malware condition: unknown scanner type '%s'", scanner_name);
1779 /* ----------------------------------------------------------------------- */
1781 /* set "been here, done that" marker */
1785 /* match virus name against pattern (caseless ------->----------v) */
1786 if ( (malware_name != NULL) &&
1787 (regex_match_and_setup(re, malware_name, 0, -1)) ) {
1788 DEBUG(D_acl) debug_printf("Matched regex to malware [%s] [%s]\n", malware_regex, malware_name);
1797 /* simple wrapper for reading lines from sockets */
1798 int recv_line(int sock, uschar *buffer, int size) {
1801 memset(buffer,0,size);
1803 while(recv(sock,p,1,0) > -1) {
1804 if ((p-buffer) > (size-2)) break;
1805 if (*p == '\n') break;
1806 if (*p != '\r') p++;
1814 /* ============= private routines for the "mksd" scanner type ============== */
1816 #include <sys/uio.h>
1818 static int mksd_writev (int sock, struct iovec *iov, int iovcnt)
1824 i = writev (sock, iov, iovcnt);
1825 while ((i < 0) && (errno == EINTR));
1828 log_write(0, LOG_MAIN|LOG_PANIC,
1829 "malware acl condition: unable to write to mksd UNIX socket (/var/run/mksd/socket)");
1834 if (i >= iov->iov_len) {
1841 iov->iov_base = CS iov->iov_base + i;
1847 static int mksd_read_lines (int sock, uschar *av_buffer, int av_buffer_size)
1853 if ((i = recv (sock, av_buffer+offset, av_buffer_size-offset, 0)) <= 0) {
1855 log_write(0, LOG_MAIN|LOG_PANIC,
1856 "malware acl condition: unable to read from mksd UNIX socket (/var/run/mksd/socket)");
1861 /* offset == av_buffer_size -> buffer full */
1862 if (offset == av_buffer_size) {
1864 log_write(0, LOG_MAIN|LOG_PANIC,
1865 "malware acl condition: malformed reply received from mksd");
1868 } while (av_buffer[offset-1] != '\n');
1870 av_buffer[offset] = '\0';
1874 static int mksd_parse_line (char *line)
1885 if ((p = strchr (line, '\n')) != NULL)
1887 log_write(0, LOG_MAIN|LOG_PANIC,
1888 "malware acl condition: mksd scanner failed: %s", line);
1892 if ((p = strchr (line, '\n')) != NULL) {
1894 if (((p-line) > 5) && ((p-line) < sizeof (malware_name_buffer)) && (line[3] == ' '))
1895 if (((p = strchr (line+4, ' ')) != NULL) && ((p-line) > 4)) {
1897 Ustrcpy (malware_name_buffer, line+4);
1898 malware_name = malware_name_buffer;
1902 log_write(0, LOG_MAIN|LOG_PANIC,
1903 "malware acl condition: malformed reply received from mksd: %s", line);
1908 static int mksd_scan_packed(int sock, uschar *scan_filename)
1910 struct iovec iov[3];
1911 const char *cmd = "MSQ\n";
1912 uschar av_buffer[1024];
1914 iov[0].iov_base = (void *) cmd;
1916 iov[1].iov_base = CS scan_filename;
1917 iov[1].iov_len = Ustrlen(scan_filename);
1918 iov[2].iov_base = (void *) (cmd + 3);
1921 if (mksd_writev (sock, iov, 3) < 0)
1924 if (mksd_read_lines (sock, av_buffer, sizeof (av_buffer)) < 0)
1929 return mksd_parse_line (CS av_buffer);