1 /* $Cambridge: exim/src/src/malware.c,v 1.19 2010/06/05 11:13:30 pdp Exp $ */
3 /*************************************************
4 * Exim - an Internet mail transport agent *
5 *************************************************/
7 /* Copyright (c) Tom Kistner <tom@duncanthrax.net> 2003-???? */
10 /* Code for calling virus (malware) scanners. Called from acl.c. */
13 #ifdef WITH_CONTENT_SCAN
15 /* declaration of private routines */
16 static int mksd_scan_packed(int sock, uschar *scan_filename);
17 static int malware_internal(uschar **listptr, uschar *eml_filename, BOOL faking);
19 /* SHUT_WR seems to be undefined on Unixware? */
25 #define MALWARE_TIMEOUT 120
28 #define DRWEBD_SCAN_CMD (1) /* scan file, buffer or diskfile */
29 #define DRWEBD_RETURN_VIRUSES (1<<0) /* ask daemon return to us viruses names from report */
30 #define DRWEBD_IS_MAIL (1<<19) /* say to daemon that format is "archive MAIL" */
32 #define DERR_READ_ERR (1<<0) /* read error */
33 #define DERR_NOMEMORY (1<<2) /* no memory */
34 #define DERR_TIMEOUT (1<<9) /* scan timeout has run out */
35 #define DERR_BAD_CALL (1<<15) /* wrong command */
37 /* Routine to check whether a system is big- or litte-endian.
38 Ripped from http://www.faqs.org/faqs/graphics/fileformats-faq/part4/section-7.html
39 Needed for proper kavdaemon implementation. Sigh. */
40 #define BIG_MY_ENDIAN 0
41 #define LITTLE_MY_ENDIAN 1
42 int test_byte_order(void);
43 int test_byte_order() {
44 short int word = 0x0001;
45 char *byte = (char *) &word;
46 return(byte[0] ? LITTLE_MY_ENDIAN : BIG_MY_ENDIAN);
49 uschar malware_name_buffer[256];
52 /* Gross hacks for the -bmalware option; perhaps we should just create
53 the scan directory normally for that case, but look into rigging up the
54 needed header variables if not already set on the command-line? */
55 extern int spool_mbox_ok;
56 extern uschar spooled_message_id[17];
58 /*************************************************
59 * Scan an email for malware *
60 *************************************************/
62 /* This is the normal interface for scanning an email, which doesn't need a
63 filename; it's a wrapper around the malware_file function.
66 listptr the list of options to the "malware = ..." ACL condition
68 Returns: Exim message processing code (OK, FAIL, DEFER, ...)
69 where true means malware was found (condition applies)
71 int malware(uschar **listptr) {
72 uschar scan_filename[1024];
75 fits = string_format(scan_filename, sizeof(scan_filename),
76 CS"%s/scan/%s/%s.eml", spool_directory, message_id, message_id);
79 log_write(0, LOG_MAIN|LOG_PANIC,
80 "malware filename does not fit in buffer [malware()]");
84 return malware_internal(listptr, scan_filename, FALSE);
88 /*************************************************
89 * Scan a file for malware *
90 *************************************************/
92 /* This is a test wrapper for scanning an email, which is not used in
93 normal processing. Scan any file, using the Exim scanning interface.
94 This function tampers with various global variables so is unsafe to use
98 eml_filename a file holding the message to be scanned
100 Returns: Exim message processing code (OK, FAIL, DEFER, ...)
101 where true means malware was found (condition applies)
103 int malware_in_file(uschar *eml_filename) {
104 uschar *scan_options[2];
105 uschar message_id_buf[64];
108 scan_options[0] = "*";
109 scan_options[1] = NULL;
111 /* spool_mbox() assumes various parameters exist, when creating
112 the relevant directory and the email within */
113 (void) string_format(message_id_buf, sizeof(message_id_buf),
114 US"dummy-%d", pseudo_random_number(INT_MAX));
115 message_id = message_id_buf;
116 sender_address = "malware-sender@example.net";
118 recipients_list = NULL;
119 receive_add_recipient("malware-victim@example.net", -1);
120 enable_dollar_recipients = TRUE;
122 ret = malware_internal(scan_options, eml_filename, TRUE);
124 strncpy(spooled_message_id, message_id, sizeof(spooled_message_id));
126 /* don't set no_mbox_unspool; at present, there's no way for it to become
127 set, but if that changes, then it should apply to these tests too */
134 /*************************************************
135 * Scan content for malware *
136 *************************************************/
138 /* This is an internal interface for scanning an email; the normal interface
139 is via malware(), or there's malware_in_file() used for testing/debugging.
142 listptr the list of options to the "malware = ..." ACL condition
143 eml_filename the file holding the email to be scanned
144 faking whether or not we're faking this up for the -bmalware test
146 Returns: Exim message processing code (OK, FAIL, DEFER, ...)
147 where true means malware was found (condition applies)
149 static int malware_internal(uschar **listptr, uschar *eml_filename, BOOL faking) {
151 uschar *list = *listptr;
152 uschar *av_scanner_work = av_scanner;
153 uschar *scanner_name;
154 uschar scanner_name_buffer[16];
155 uschar *malware_regex;
156 uschar malware_regex_buffer[64];
157 uschar malware_regex_default[] = ".+";
158 unsigned long mbox_size;
162 const uschar *rerror;
164 /* make sure the eml mbox file is spooled up */
165 mbox_file = spool_mbox(&mbox_size, faking ? eml_filename : NULL);
166 if (mbox_file == NULL) {
167 /* error while spooling */
168 log_write(0, LOG_MAIN|LOG_PANIC,
169 "malware acl condition: error while creating mbox spool file");
172 /* none of our current scanners need the mbox
173 file as a stream, so we can close it right away */
174 (void)fclose(mbox_file);
176 /* extract the malware regex to match against from the option list */
177 if ((malware_regex = string_nextinlist(&list, &sep,
178 malware_regex_buffer,
179 sizeof(malware_regex_buffer))) != NULL) {
181 /* parse 1st option */
182 if ( (strcmpic(malware_regex,US"false") == 0) ||
183 (Ustrcmp(malware_regex,"0") == 0) ) {
184 /* explicitly no matching */
188 /* special cases (match anything except empty) */
189 if ( (strcmpic(malware_regex,US"true") == 0) ||
190 (Ustrcmp(malware_regex,"*") == 0) ||
191 (Ustrcmp(malware_regex,"1") == 0) ) {
192 malware_regex = malware_regex_default;
196 /* empty means "don't match anything" */
200 /* Reset sep that is set by previous string_nextinlist() call */
203 /* compile the regex, see if it works */
204 re = pcre_compile(CS malware_regex, PCRE_COPT, (const char **)&rerror, &roffset, NULL);
206 log_write(0, LOG_MAIN|LOG_PANIC,
207 "malware acl condition: regular expression error in '%s': %s at offset %d", malware_regex, rerror, roffset);
211 /* if av_scanner starts with a dollar, expand it first */
212 if (*av_scanner == '$') {
213 av_scanner_work = expand_string(av_scanner);
214 if (av_scanner_work == NULL) {
215 log_write(0, LOG_MAIN|LOG_PANIC,
216 "malware acl condition: av_scanner starts with $, but expansion failed: %s", expand_string_message);
220 debug_printf("Expanded av_scanner global: %s\n", av_scanner_work);
221 /* disable result caching in this case */
227 /* Do not scan twice. */
228 if (malware_ok == 0) {
230 /* find the scanner type from the av_scanner option */
231 if ((scanner_name = string_nextinlist(&av_scanner_work, &sep,
233 sizeof(scanner_name_buffer))) == NULL) {
234 /* no scanner given */
235 log_write(0, LOG_MAIN|LOG_PANIC,
236 "malware acl condition: av_scanner configuration variable is empty");
240 /* "f-protd" scanner type ----------------------------------------------- */
241 if (strcmpic(scanner_name, US"f-protd") == 0) {
242 uschar *fp_options, *fp_scan_option;
243 uschar fp_scan_option_buffer[1024];
244 uschar fp_options_buffer[1024];
245 uschar fp_options_default[] = "localhost 10200-10204";
246 uschar hostname[256];
247 unsigned int port, portlow, porthigh, connect_ok=0, detected=0, par_count = 0;
251 uschar scanrequest[2048], buf[32768], *strhelper, *strhelper2;
253 if ((fp_options = string_nextinlist(&av_scanner_work, &sep,
254 fp_options_buffer, sizeof(fp_options_buffer))) == NULL) {
255 /* no options supplied, use default options */
256 fp_options = fp_options_default;
259 /* extract host and port part */
260 if ( sscanf(CS fp_options, "%s %u-%u", hostname, &portlow, &porthigh) != 3 ) {
261 if ( sscanf(CS fp_options, "%s %u", hostname, &portlow) != 2 ) {
262 log_write(0, LOG_MAIN|LOG_PANIC,
263 "malware acl condition: f-protd: invalid socket '%s'", fp_options);
269 /* Lookup the host */
270 if((he = gethostbyname(CS hostname)) == 0) {
271 log_write(0, LOG_MAIN|LOG_PANIC,
272 "malware acl condition: f-protd: failed to lookup host '%s'", hostname);
276 in = *(struct in_addr *) he->h_addr_list[0];
280 /* Open the f-protd TCP socket */
281 if ( (sock = ip_socket(SOCK_STREAM, AF_INET)) < 0) {
282 log_write(0, LOG_MAIN|LOG_PANIC,
283 "malware acl condition: f-protd: unable to acquire socket (%s)",
288 /* Try to connect to all portslow-high until connection is established */
289 for (port = portlow; !connect_ok && port < porthigh; port++) {
290 if (ip_connect(sock, AF_INET, (uschar*)inet_ntoa(in), port, 5) >= 0) {
296 log_write(0, LOG_MAIN|LOG_PANIC,
297 "malware acl condition: f-protd: connection to %s, port %u-%u failed (%s)",
298 inet_ntoa(in), portlow, porthigh, strerror(errno));
303 DEBUG(D_acl) debug_printf("Malware scan: issuing %s GET\n", scanner_name);
304 (void)string_format(scanrequest, 1024, CS"GET %s", eml_filename);
306 while ((fp_scan_option = string_nextinlist(&av_scanner_work, &sep,
307 fp_scan_option_buffer, sizeof(fp_scan_option_buffer))) != NULL) {
309 Ustrcat(scanrequest, "%20");
311 Ustrcat(scanrequest, "?");
313 Ustrcat(scanrequest, fp_scan_option);
316 Ustrcat(scanrequest, " HTTP/1.0\r\n\r\n");
318 /* send scan request */
319 if (send(sock, &scanrequest, Ustrlen(scanrequest)+1, 0) < 0) {
321 log_write(0, LOG_MAIN|LOG_PANIC,
322 "malware acl condition: f-protd: unable to send command to socket (%s)", scanrequest);
326 /* We get a lot of empty lines, so we need this hack to check for any data at all */
327 while( recv(sock, buf, 1, MSG_PEEK) > 0 ) {
328 if ( recv_line(sock, buf, 32768) > 0) {
329 if ( Ustrstr(buf, US"<detected type=\"") != NULL ) {
331 } else if ( detected && (strhelper = Ustrstr(buf, US"<name>")) ) {
332 if ((strhelper2 = Ustrstr(buf, US"</name>")) != NULL) {
334 Ustrcpy(malware_name_buffer, strhelper + 6);
336 } else if ( Ustrstr(buf, US"<summary code=\"") ) {
337 if ( Ustrstr(buf, US"<summary code=\"11\">") ) {
338 malware_name = malware_name_buffer;
347 /* "drweb" scanner type ----------------------------------------------- */
348 /* v0.1 - added support for tcp sockets */
349 /* v0.0 - initial release -- support for unix sockets */
350 else if (strcmpic(scanner_name,US"drweb") == 0) {
351 uschar *drweb_options;
352 uschar drweb_options_buffer[1024];
353 uschar drweb_options_default[] = "/usr/local/drweb/run/drwebd.sock";
354 struct sockaddr_un server;
355 int sock, result, ovector[30];
356 unsigned int port, fsize;
357 uschar tmpbuf[1024], *drweb_fbuf;
358 uschar drweb_match_string[128];
359 int drweb_rc, drweb_cmd, drweb_flags = 0x0000, drweb_fd,
360 drweb_vnum, drweb_slen, drweb_fin = 0x0000;
362 uschar hostname[256];
367 if ((drweb_options = string_nextinlist(&av_scanner_work, &sep,
368 drweb_options_buffer, sizeof(drweb_options_buffer))) == NULL) {
369 /* no options supplied, use default options */
370 drweb_options = drweb_options_default;
373 if (*drweb_options != '/') {
375 /* extract host and port part */
376 if( sscanf(CS drweb_options, "%s %u", hostname, &port) != 2 ) {
377 log_write(0, LOG_MAIN|LOG_PANIC,
378 "malware acl condition: drweb: invalid socket '%s'", drweb_options);
382 /* Lookup the host */
383 if((he = gethostbyname(CS hostname)) == 0) {
384 log_write(0, LOG_MAIN|LOG_PANIC,
385 "malware acl condition: drweb: failed to lookup host '%s'", hostname);
389 in = *(struct in_addr *) he->h_addr_list[0];
391 /* Open the drwebd TCP socket */
392 if ( (sock = ip_socket(SOCK_STREAM, AF_INET)) < 0) {
393 log_write(0, LOG_MAIN|LOG_PANIC,
394 "malware acl condition: drweb: unable to acquire socket (%s)",
399 if (ip_connect(sock, AF_INET, (uschar*)inet_ntoa(in), port, 5) < 0) {
401 log_write(0, LOG_MAIN|LOG_PANIC,
402 "malware acl condition: drweb: connection to %s, port %u failed (%s)",
403 inet_ntoa(in), port, strerror(errno));
407 /* prepare variables */
408 drweb_cmd = htonl(DRWEBD_SCAN_CMD);
409 drweb_flags = htonl(DRWEBD_RETURN_VIRUSES | DRWEBD_IS_MAIL);
412 drweb_fd = open(CS eml_filename, O_RDONLY);
413 if (drweb_fd == -1) {
415 log_write(0, LOG_MAIN|LOG_PANIC,
416 "malware acl condition: drweb: can't open spool file %s: %s",
417 eml_filename, strerror(errno));
420 fsize = lseek(drweb_fd, 0, SEEK_END);
423 (void)close(drweb_fd);
424 log_write(0, LOG_MAIN|LOG_PANIC,
425 "malware acl condition: drweb: can't seek spool file %s: %s",
426 eml_filename, strerror(errno));
429 drweb_slen = htonl(fsize);
430 lseek(drweb_fd, 0, SEEK_SET);
432 DEBUG(D_acl) debug_printf("Malware scan: issuing %s remote scan [%s %u]\n",
433 scanner_name, hostname, port);
435 /* send scan request */
436 if ((send(sock, &drweb_cmd, sizeof(drweb_cmd), 0) < 0) ||
437 (send(sock, &drweb_flags, sizeof(drweb_flags), 0) < 0) ||
438 (send(sock, &drweb_fin, sizeof(drweb_fin), 0) < 0) ||
439 (send(sock, &drweb_slen, sizeof(drweb_slen), 0) < 0)) {
441 (void)close(drweb_fd);
442 log_write(0, LOG_MAIN|LOG_PANIC,
443 "malware acl condition: drweb: unable to send commands to socket (%s)", drweb_options);
447 drweb_fbuf = (uschar *) malloc (fsize);
450 (void)close(drweb_fd);
451 log_write(0, LOG_MAIN|LOG_PANIC,
452 "malware acl condition: drweb: unable to allocate memory %u for file (%s)",
453 fsize, eml_filename);
457 result = read (drweb_fd, drweb_fbuf, fsize);
460 (void)close(drweb_fd);
462 log_write(0, LOG_MAIN|LOG_PANIC,
463 "malware acl condition: drweb: can't read spool file %s: %s",
464 eml_filename, strerror(errno));
467 (void)close(drweb_fd);
469 /* send file body to socket */
470 if (send(sock, drweb_fbuf, fsize, 0) < 0) {
473 log_write(0, LOG_MAIN|LOG_PANIC,
474 "malware acl condition: drweb: unable to send file body to socket (%s)", drweb_options);
477 (void)close(drweb_fd);
480 /* open the drwebd UNIX socket */
481 sock = socket(AF_UNIX, SOCK_STREAM, 0);
483 log_write(0, LOG_MAIN|LOG_PANIC,
484 "malware acl condition: drweb: can't open UNIX socket");
487 server.sun_family = AF_UNIX;
488 Ustrcpy(server.sun_path, drweb_options);
489 if (connect(sock, (struct sockaddr *) &server, sizeof(struct sockaddr_un)) < 0) {
491 log_write(0, LOG_MAIN|LOG_PANIC,
492 "malware acl condition: drweb: unable to connect to socket (%s). errno=%d", drweb_options, errno);
496 /* prepare variables */
497 drweb_cmd = htonl(DRWEBD_SCAN_CMD);
498 drweb_flags = htonl(DRWEBD_RETURN_VIRUSES | DRWEBD_IS_MAIL);
499 drweb_slen = htonl(Ustrlen(eml_filename));
501 DEBUG(D_acl) debug_printf("Malware scan: issuing %s local scan [%s]\n",
502 scanner_name, drweb_options);
504 /* send scan request */
505 if ((send(sock, &drweb_cmd, sizeof(drweb_cmd), 0) < 0) ||
506 (send(sock, &drweb_flags, sizeof(drweb_flags), 0) < 0) ||
507 (send(sock, &drweb_slen, sizeof(drweb_slen), 0) < 0) ||
508 (send(sock, eml_filename, Ustrlen(eml_filename), 0) < 0) ||
509 (send(sock, &drweb_fin, sizeof(drweb_fin), 0) < 0)) {
511 log_write(0, LOG_MAIN|LOG_PANIC,
512 "malware acl condition: drweb: unable to send commands to socket (%s)", drweb_options);
517 /* wait for result */
518 if ((bread = recv(sock, &drweb_rc, sizeof(drweb_rc), 0) != sizeof(drweb_rc))) {
520 log_write(0, LOG_MAIN|LOG_PANIC,
521 "malware acl condition: drweb: unable to read return code");
524 drweb_rc = ntohl(drweb_rc);
526 if ((bread = recv(sock, &drweb_vnum, sizeof(drweb_vnum), 0) != sizeof(drweb_vnum))) {
528 log_write(0, LOG_MAIN|LOG_PANIC,
529 "malware acl condition: drweb: unable to read the number of viruses");
532 drweb_vnum = ntohl(drweb_vnum);
534 /* "virus(es) found" if virus number is > 0 */
538 uschar pre_malware_nb[256];
540 malware_name = malware_name_buffer;
542 /* setup default virus name */
543 Ustrcpy(malware_name_buffer,"unknown");
545 /* read and concatenate virus names into one string */
546 for (i=0;i<drweb_vnum;i++)
548 /* read the size of report */
549 if ((bread = recv(sock, &drweb_slen, sizeof(drweb_slen), 0) != sizeof(drweb_slen))) {
551 log_write(0, LOG_MAIN|LOG_PANIC,
552 "malware acl condition: drweb: cannot read report size");
555 drweb_slen = ntohl(drweb_slen);
557 /* read report body */
558 if ((bread = recv(sock, tmpbuf, drweb_slen, 0)) != drweb_slen) {
560 log_write(0, LOG_MAIN|LOG_PANIC,
561 "malware acl condition: drweb: cannot read report string");
564 tmpbuf[drweb_slen] = '\0';
566 /* set up match regex, depends on retcode */
567 Ustrcpy(drweb_match_string, "infected\\swith\\s*(.+?)$");
569 drweb_re = pcre_compile( CS drweb_match_string,
571 (const char **)&rerror,
575 /* try matcher on the line, grab substring */
576 result = pcre_exec(drweb_re, NULL, CS tmpbuf, Ustrlen(tmpbuf), 0, 0, ovector, 30);
578 pcre_copy_substring(CS tmpbuf, ovector, result, 1, CS pre_malware_nb, 255);
580 /* the first name we just copy to malware_name */
582 Ustrcpy(CS malware_name_buffer, CS pre_malware_nb);
584 /* concatenate each new virus name to previous */
585 int slen = Ustrlen(malware_name_buffer);
586 if (slen < (slen+Ustrlen(pre_malware_nb))) {
587 Ustrcat(malware_name_buffer, "/");
588 Ustrcat(malware_name_buffer, pre_malware_nb);
594 char *drweb_s = NULL;
596 if (drweb_rc & DERR_READ_ERR) drweb_s = "read error";
597 if (drweb_rc & DERR_NOMEMORY) drweb_s = "no memory";
598 if (drweb_rc & DERR_TIMEOUT) drweb_s = "timeout";
599 if (drweb_rc & DERR_BAD_CALL) drweb_s = "wrong command";
600 /* retcodes DERR_SYMLINK, DERR_NO_REGFILE, DERR_SKIPPED.
601 * DERR_TOO_BIG, DERR_TOO_COMPRESSED, DERR_SPAM,
602 * DERR_CRC_ERROR, DERR_READSOCKET, DERR_WRITE_ERR
603 * and others are ignored */
605 log_write(0, LOG_MAIN|LOG_PANIC,
606 "malware acl condition: drweb: drweb daemon retcode 0x%x (%s)", drweb_rc, drweb_s);
615 /* ----------------------------------------------------------------------- */
616 else if (strcmpic(scanner_name,US"aveserver") == 0) {
618 uschar kav_options_buffer[1024];
619 uschar kav_options_default[] = "/var/run/aveserver";
621 struct sockaddr_un server;
625 if ((kav_options = string_nextinlist(&av_scanner_work, &sep,
627 sizeof(kav_options_buffer))) == NULL) {
628 /* no options supplied, use default options */
629 kav_options = kav_options_default;
632 /* open the aveserver socket */
633 sock = socket(AF_UNIX, SOCK_STREAM, 0);
635 log_write(0, LOG_MAIN|LOG_PANIC,
636 "malware acl condition: can't open UNIX socket.");
639 server.sun_family = AF_UNIX;
640 Ustrcpy(server.sun_path, kav_options);
641 if (connect(sock, (struct sockaddr *) &server, sizeof(struct sockaddr_un)) < 0) {
643 log_write(0, LOG_MAIN|LOG_PANIC,
644 "malware acl condition: unable to connect to aveserver UNIX socket (%s). errno=%d", kav_options, errno);
648 /* read aveserver's greeting and see if it is ready (2xx greeting) */
649 recv_line(sock, buf, 32768);
652 /* aveserver is having problems */
654 log_write(0, LOG_MAIN|LOG_PANIC,
655 "malware acl condition: aveserver is unavailable (Responded: %s).", ((buf[0] != 0) ? buf : (uschar *)"nothing") );
659 /* prepare our command */
660 (void)string_format(buf, 32768, "SCAN bPQRSTUW %s\r\n", eml_filename);
662 DEBUG(D_acl) debug_printf("Malware scan: issuing %s SCAN\n", scanner_name);
665 if (send(sock, buf, Ustrlen(buf), 0) < 0) {
667 log_write(0, LOG_MAIN|LOG_PANIC,
668 "malware acl condition: unable to write to aveserver UNIX socket (%s)", kav_options);
674 /* read response lines, find malware name and final response */
675 while (recv_line(sock, buf, 32768) > 0) {
676 debug_printf("aveserver: %s\n", buf);
679 } else if (buf[0] == '5') {
680 /* aveserver is having problems */
681 log_write(0, LOG_MAIN|LOG_PANIC,
682 "malware acl condition: unable to scan file %s (Responded: %s).",
686 } else if (Ustrncmp(buf,"322",3) == 0) {
687 uschar *p = Ustrchr(&buf[4],' ');
689 Ustrcpy(malware_name_buffer,&buf[4]);
690 malware_name = malware_name_buffer;
694 /* prepare our command */
695 (void)string_format(buf, 32768, "quit\r\n");
698 if (send(sock, buf, Ustrlen(buf), 0) < 0) {
700 log_write(0, LOG_MAIN|LOG_PANIC,
701 "malware acl condition: unable to write to aveserver UNIX socket (%s)", kav_options);
705 /* read aveserver's greeting and see if it is ready (2xx greeting) */
706 recv_line(sock, buf, 32768);
709 /* aveserver is having problems */
711 log_write(0, LOG_MAIN|LOG_PANIC,
712 "malware acl condition: unable to quit aveserver dialogue (Responded: %s).", ((buf[0] != 0) ? buf : (uschar *)"nothing") );
718 if (result == DEFER) return DEFER;
720 /* "fsecure" scanner type ------------------------------------------------- */
721 else if (strcmpic(scanner_name,US"fsecure") == 0) {
722 uschar *fsecure_options;
723 uschar fsecure_options_buffer[1024];
724 uschar fsecure_options_default[] = "/var/run/.fsav";
725 struct sockaddr_un server;
726 int sock, i, j, bread = 0;
727 uschar file_name[1024];
728 uschar av_buffer[1024];
730 static uschar *cmdoptions[] = { US"CONFIGURE\tARCHIVE\t1\n",
731 US"CONFIGURE\tTIMEOUT\t0\n",
732 US"CONFIGURE\tMAXARCH\t5\n",
733 US"CONFIGURE\tMIME\t1\n" };
736 if ((fsecure_options = string_nextinlist(&av_scanner_work, &sep,
737 fsecure_options_buffer,
738 sizeof(fsecure_options_buffer))) == NULL) {
739 /* no options supplied, use default options */
740 fsecure_options = fsecure_options_default;
743 /* open the fsecure socket */
744 sock = socket(AF_UNIX, SOCK_STREAM, 0);
746 log_write(0, LOG_MAIN|LOG_PANIC,
747 "malware acl condition: unable to open fsecure socket %s (%s)",
748 fsecure_options, strerror(errno));
751 server.sun_family = AF_UNIX;
752 Ustrcpy(server.sun_path, fsecure_options);
753 if (connect(sock, (struct sockaddr *) &server, sizeof(struct sockaddr_un)) < 0) {
755 log_write(0, LOG_MAIN|LOG_PANIC,
756 "malware acl condition: unable to connect to fsecure socket %s (%s)",
757 fsecure_options, strerror(errno));
761 DEBUG(D_acl) debug_printf("Malware scan: issuing %s scan [%s]\n",
762 scanner_name, fsecure_options);
765 memset(av_buffer, 0, sizeof(av_buffer));
766 for (i=0; i != 4; i++) {
767 /* debug_printf("send option \"%s\"",cmdoptions[i]); */
768 if (write(sock, cmdoptions[i], Ustrlen(cmdoptions[i])) < 0) {
770 log_write(0, LOG_MAIN|LOG_PANIC,
771 "malware acl condition: unable to write fsecure option %d to %s (%s)",
772 i, fsecure_options, strerror(errno));
776 bread = ip_recv(sock, av_buffer, sizeof(av_buffer), MALWARE_TIMEOUT);
777 if (bread >0) av_buffer[bread]='\0';
780 log_write(0, LOG_MAIN|LOG_PANIC,
781 "malware acl condition: unable to read fsecure answer %d (%s)", i, strerror(errno));
784 for (j=0;j<bread;j++) if((av_buffer[j]=='\r')||(av_buffer[j]=='\n')) av_buffer[j] ='@';
785 /* debug_printf("read answer %d read=%d \"%s\"\n", i, bread, av_buffer ); */
786 /* while (Ustrstr(av_buffer, "OK\tServer configured.@") == NULL); */
789 /* pass the mailfile to fsecure */
790 (void)string_format(file_name,1024,"SCAN\t%s\n", eml_filename);
791 /* debug_printf("send scan %s",file_name); */
792 if (write(sock, file_name, Ustrlen(file_name)) < 0) {
794 log_write(0, LOG_MAIN|LOG_PANIC,
795 "malware acl condition: unable to write fsecure scan to %s (%s)",
796 fsecure_options, strerror(errno));
801 /* todo also SUSPICION\t */
802 fs_inf = pcre_compile("\\S{0,5}INFECTED\\t[^\\t]*\\t([^\\t]+)\\t\\S*$", PCRE_COPT, (const char **)&rerror, &roffset, NULL);
804 /* read report, linewise */
808 memset(av_buffer, 0, sizeof(av_buffer));
810 bread=ip_recv(sock, &av_buffer[i], 1, MALWARE_TIMEOUT);
813 log_write(0, LOG_MAIN|LOG_PANIC,
814 "malware acl condition: unable to read fsecure result (%s)", strerror(errno));
819 while ((i < sizeof(av_buffer)-1 ) && (av_buffer[i-1] != '\n'));
820 av_buffer[i-1] = '\0';
821 /* debug_printf("got line \"%s\"\n",av_buffer); */
823 /* Really search for virus again? */
824 if (malware_name == NULL) {
825 /* try matcher on the line, grab substring */
826 i = pcre_exec(fs_inf, NULL, CS av_buffer, Ustrlen(av_buffer), 0, 0, ovector, 30);
829 pcre_copy_substring(CS av_buffer, ovector, i, 1, CS malware_name_buffer, 255);
830 malware_name = malware_name_buffer;
834 while (Ustrstr(av_buffer, "OK\tScan ok.") == NULL);
837 /* ----------------------------------------------------------------------- */
839 /* "kavdaemon" scanner type ------------------------------------------------ */
840 else if (strcmpic(scanner_name,US"kavdaemon") == 0) {
842 uschar kav_options_buffer[1024];
843 uschar kav_options_default[] = "/var/run/AvpCtl";
844 struct sockaddr_un server;
848 uschar scanrequest[1024];
849 uschar kav_match_string[128];
851 unsigned long kav_reportlen, bread;
856 if ((kav_options = string_nextinlist(&av_scanner_work, &sep,
858 sizeof(kav_options_buffer))) == NULL) {
859 /* no options supplied, use default options */
860 kav_options = kav_options_default;
863 /* open the kavdaemon socket */
864 sock = socket(AF_UNIX, SOCK_STREAM, 0);
866 log_write(0, LOG_MAIN|LOG_PANIC,
867 "malware acl condition: can't open UNIX socket.");
870 server.sun_family = AF_UNIX;
871 Ustrcpy(server.sun_path, kav_options);
872 if (connect(sock, (struct sockaddr *) &server, sizeof(struct sockaddr_un)) < 0) {
874 log_write(0, LOG_MAIN|LOG_PANIC,
875 "malware acl condition: unable to connect to kavdaemon UNIX socket (%s). errno=%d", kav_options, errno);
879 /* get current date and time, build scan request */
881 /* pdp note: before the eml_filename parameter, this scanned the
882 directory; not finding documentation, so we'll strip off the directory.
883 The side-effect is that the test framework scanning may end up in
884 scanning more than was requested, but for the normal interface, this is
886 strftime(CS tmpbuf, sizeof(tmpbuf), "<0>%d %b %H:%M:%S:%%s", localtime(&t));
887 fits = string_format(scanrequest, 1024,CS tmpbuf, eml_filename);
890 log_write(0, LOG_MAIN|LOG_PANIC,
891 "malware filename does not fit in buffer [malware_internal() kavdaemon]");
893 p = strrchr(scanrequest, '/');
897 DEBUG(D_acl) debug_printf("Malware scan: issuing %s scan [%s]\n",
898 scanner_name, kav_options);
900 /* send scan request */
901 if (send(sock, scanrequest, Ustrlen(scanrequest)+1, 0) < 0) {
903 log_write(0, LOG_MAIN|LOG_PANIC,
904 "malware acl condition: unable to write to kavdaemon UNIX socket (%s)", kav_options);
908 /* wait for result */
909 if ((bread = recv(sock, tmpbuf, 2, 0) != 2)) {
911 log_write(0, LOG_MAIN|LOG_PANIC,
912 "malware acl condition: unable to read 2 bytes from kavdaemon socket.");
916 /* get errorcode from one nibble */
917 if (test_byte_order() == LITTLE_MY_ENDIAN) {
918 kav_rc = tmpbuf[0] & 0x0F;
921 kav_rc = tmpbuf[1] & 0x0F;
924 /* improper kavdaemon configuration */
925 if ( (kav_rc == 5) || (kav_rc == 6) ) {
927 log_write(0, LOG_MAIN|LOG_PANIC,
928 "malware acl condition: please reconfigure kavdaemon to NOT disinfect or remove infected files.");
934 log_write(0, LOG_MAIN|LOG_PANIC,
935 "malware acl condition: kavdaemon reported 'scanning not completed' (code 1).");
941 log_write(0, LOG_MAIN|LOG_PANIC,
942 "malware acl condition: kavdaemon reported 'kavdaemon damaged' (code 7).");
946 /* code 8 is not handled, since it is ambigous. It appears mostly on
947 bounces where part of a file has been cut off */
949 /* "virus found" return codes (2-4) */
950 if ((kav_rc > 1) && (kav_rc < 5)) {
953 /* setup default virus name */
954 Ustrcpy(malware_name_buffer,"unknown");
955 malware_name = malware_name_buffer;
957 if (test_byte_order() == LITTLE_MY_ENDIAN) {
958 report_flag = tmpbuf[1];
961 report_flag = tmpbuf[0];
964 /* read the report, if available */
965 if( report_flag == 1 ) {
966 /* read report size */
967 if ((bread = recv(sock, &kav_reportlen, 4, 0)) != 4) {
969 log_write(0, LOG_MAIN|LOG_PANIC,
970 "malware acl condition: cannot read report size from kavdaemon");
974 /* it's possible that avp returns av_buffer[1] == 1 but the
975 reportsize is 0 (!?) */
976 if (kav_reportlen > 0) {
977 /* set up match regex, depends on retcode */
979 Ustrcpy(kav_match_string, "suspicion:\\s*(.+?)\\s*$");
981 Ustrcpy(kav_match_string, "infected:\\s*(.+?)\\s*$");
983 kav_re = pcre_compile( CS kav_match_string,
985 (const char **)&rerror,
989 /* read report, linewise */
990 while (kav_reportlen > 0) {
995 while ( recv(sock, &tmpbuf[bread], 1, 0) == 1 ) {
997 if ( (tmpbuf[bread] == '\n') || (bread > 1021) ) break;
1001 tmpbuf[bread] = '\0';
1003 /* try matcher on the line, grab substring */
1004 result = pcre_exec(kav_re, NULL, CS tmpbuf, Ustrlen(tmpbuf), 0, 0, ovector, 30);
1006 pcre_copy_substring(CS tmpbuf, ovector, result, 1, CS malware_name_buffer, 255);
1014 /* no virus found */
1015 malware_name = NULL;
1020 /* ----------------------------------------------------------------------- */
1023 /* "cmdline" scanner type ------------------------------------------------ */
1024 else if (strcmpic(scanner_name,US"cmdline") == 0) {
1025 uschar *cmdline_scanner;
1026 uschar cmdline_scanner_buffer[1024];
1027 uschar *cmdline_trigger;
1028 uschar cmdline_trigger_buffer[1024];
1029 const pcre *cmdline_trigger_re;
1030 uschar *cmdline_regex;
1031 uschar cmdline_regex_buffer[1024];
1032 const pcre *cmdline_regex_re;
1033 uschar file_name[1024];
1034 uschar commandline[1024];
1035 void (*eximsigchld)(int);
1036 void (*eximsigpipe)(int);
1037 FILE *scanner_out = NULL;
1038 FILE *scanner_record = NULL;
1039 uschar linebuffer[32767];
1046 /* find scanner command line */
1047 if ((cmdline_scanner = string_nextinlist(&av_scanner_work, &sep,
1048 cmdline_scanner_buffer,
1049 sizeof(cmdline_scanner_buffer))) == NULL) {
1050 /* no command line supplied */
1051 log_write(0, LOG_MAIN|LOG_PANIC,
1052 "malware acl condition: missing commandline specification for cmdline scanner type.");
1056 /* find scanner output trigger */
1057 if ((cmdline_trigger = string_nextinlist(&av_scanner_work, &sep,
1058 cmdline_trigger_buffer,
1059 sizeof(cmdline_trigger_buffer))) == NULL) {
1060 /* no trigger regex supplied */
1061 log_write(0, LOG_MAIN|LOG_PANIC,
1062 "malware acl condition: missing trigger specification for cmdline scanner type.");
1066 /* precompile trigger regex */
1067 cmdline_trigger_re = pcre_compile(CS cmdline_trigger, PCRE_COPT, (const char **)&rerror, &roffset, NULL);
1068 if (cmdline_trigger_re == NULL) {
1069 log_write(0, LOG_MAIN|LOG_PANIC,
1070 "malware acl condition: regular expression error in '%s': %s at offset %d", cmdline_trigger_re, rerror, roffset);
1074 /* find scanner name regex */
1075 if ((cmdline_regex = string_nextinlist(&av_scanner_work, &sep,
1076 cmdline_regex_buffer,
1077 sizeof(cmdline_regex_buffer))) == NULL) {
1078 /* no name regex supplied */
1079 log_write(0, LOG_MAIN|LOG_PANIC,
1080 "malware acl condition: missing virus name regex specification for cmdline scanner type.");
1084 /* precompile name regex */
1085 cmdline_regex_re = pcre_compile(CS cmdline_regex, PCRE_COPT, (const char **)&rerror, &roffset, NULL);
1086 if (cmdline_regex_re == NULL) {
1087 log_write(0, LOG_MAIN|LOG_PANIC,
1088 "malware acl condition: regular expression error in '%s': %s at offset %d", cmdline_regex_re, rerror, roffset);
1092 /* prepare scanner call; despite the naming, file_name holds a directory
1093 name which is documented as the value given to %s. */
1094 if (Ustrlen(eml_filename) > sizeof(file_name) - 1)
1096 log_write(0, LOG_MAIN|LOG_PANIC,
1097 "malware filename does not fit in buffer [malware_internal() cmdline]");
1100 p = strrchr(eml_filename, '/');
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;
1202 uschar file_name[1024];
1203 uschar av_buffer[1024];
1205 if ((sophie_options = string_nextinlist(&av_scanner_work, &sep,
1206 sophie_options_buffer,
1207 sizeof(sophie_options_buffer))) == NULL) {
1208 /* no options supplied, use default options */
1209 sophie_options = sophie_options_default;
1212 /* open the sophie socket */
1213 sock = socket(AF_UNIX, SOCK_STREAM, 0);
1215 log_write(0, LOG_MAIN|LOG_PANIC,
1216 "malware acl condition: can't open UNIX socket.");
1219 server.sun_family = AF_UNIX;
1220 Ustrcpy(server.sun_path, sophie_options);
1221 if (connect(sock, (struct sockaddr *) &server, sizeof(struct sockaddr_un)) < 0) {
1223 log_write(0, LOG_MAIN|LOG_PANIC,
1224 "malware acl condition: unable to connect to sophie UNIX socket (%s). errno=%d", sophie_options, errno);
1228 /* pass the scan directory to sophie */
1229 len = Ustrlen(eml_filename) + 1;
1230 if (len > sizeof(file_name))
1233 log_write(0, LOG_MAIN|LOG_PANIC,
1234 "malware filename does not fit in buffer [malware_internal() sophie]");
1237 memcpy(file_name, eml_filename, len);
1238 p = strrchr(file_name, '/');
1242 DEBUG(D_acl) debug_printf("Malware scan: issuing %s scan [%s]\n",
1243 scanner_name, sophie_options);
1245 if (write(sock, file_name, Ustrlen(file_name)) < 0) {
1247 log_write(0, LOG_MAIN|LOG_PANIC,
1248 "malware acl condition: unable to write to sophie UNIX socket (%s)", sophie_options);
1252 (void)write(sock, "\n", 1);
1254 /* wait for result */
1255 memset(av_buffer, 0, sizeof(av_buffer));
1256 if ((!(bread = ip_recv(sock, av_buffer, sizeof(av_buffer), MALWARE_TIMEOUT)) > 0)) {
1258 log_write(0, LOG_MAIN|LOG_PANIC,
1259 "malware acl condition: unable to read from sophie UNIX socket (%s)", sophie_options);
1266 if (av_buffer[0] == '1') {
1267 if (Ustrchr(av_buffer, '\n')) *Ustrchr(av_buffer, '\n') = '\0';
1268 Ustrcpy(malware_name_buffer,&av_buffer[2]);
1269 malware_name = malware_name_buffer;
1271 else if (!strncmp(CS av_buffer, "-1", 2)) {
1272 log_write(0, LOG_MAIN|LOG_PANIC,
1273 "malware acl condition: malware acl condition: sophie reported error");
1277 /* all ok, no virus */
1278 malware_name = NULL;
1281 /* ----------------------------------------------------------------------- */
1284 /* "clamd" scanner type ------------------------------------------------- */
1285 /* This code was originally contributed by David Saez */
1286 /* There are three scanning methods available to us:
1287 * (1) Use the SCAN command, pointing to a file in the filesystem
1288 * (2) Use the STREAM command, send the data on a separate port
1289 * (3) Use the zINSTREAM command, send the data inline
1290 * The zINSTREAM command was introduced with ClamAV 0.95, which marked
1291 * STREAM deprecated; see: http://wiki.clamav.net/bin/view/Main/UpgradeNotes095
1292 * In Exim, we use SCAN if using a Unix-domain socket or explicitly told that
1293 * the TCP-connected daemon is actually local; otherwise we use zINSTREAM unless
1294 * WITH_OLD_CLAMAV_STREAM is defined.
1295 * See Exim bug 926 for details. */
1296 else if (strcmpic(scanner_name,US"clamd") == 0) {
1297 uschar *clamd_options;
1298 uschar clamd_options_buffer[1024];
1299 uschar clamd_options_default[] = "/tmp/clamd";
1301 struct sockaddr_un server;
1304 uschar file_name[1024];
1305 uschar av_buffer[1024];
1306 uschar hostname[256];
1309 uschar *clamd_options2;
1310 uschar clamd_options2_buffer[1024];
1311 uschar clamd_options2_default[] = "";
1312 uschar *clamav_fbuf;
1313 uschar scanrequest[1024];
1314 int sockData, clam_fd, result;
1316 BOOL use_scan_command, fits;
1317 #ifdef WITH_OLD_CLAMAV_STREAM
1318 uschar av_buffer2[1024];
1320 uint32_t send_size, send_final_zeroblock;
1323 if ((clamd_options = string_nextinlist(&av_scanner_work, &sep,
1324 clamd_options_buffer,
1325 sizeof(clamd_options_buffer))) == NULL) {
1326 /* no options supplied, use default options */
1327 clamd_options = clamd_options_default;
1329 if ((clamd_options2 = string_nextinlist(&av_scanner_work, &sep,
1330 clamd_options2_buffer,
1331 sizeof(clamd_options2_buffer))) == NULL) {
1332 clamd_options2 = clamd_options2_default;
1335 if ((*clamd_options == '/') || (strcmpic(clamd_options2,US"local") == 0))
1336 use_scan_command = TRUE;
1338 use_scan_command = FALSE;
1340 /* socket does not start with '/' -> network socket */
1341 if (*clamd_options != '/') {
1343 /* Confirmed in ClamAV source (0.95.3) that the TCPAddr option of clamd
1344 * only supports AF_INET, but we should probably be looking to the
1345 * future and rewriting this to be protocol-independent anyway. */
1347 /* extract host and port part */
1348 if( sscanf(CS clamd_options, "%s %u", hostname, &port) != 2 ) {
1349 log_write(0, LOG_MAIN|LOG_PANIC,
1350 "malware acl condition: clamd: invalid socket '%s'", clamd_options);
1354 /* Lookup the host */
1355 if((he = gethostbyname(CS hostname)) == 0) {
1356 log_write(0, LOG_MAIN|LOG_PANIC,
1357 "malware acl condition: clamd: failed to lookup host '%s'", hostname);
1361 in = *(struct in_addr *) he->h_addr_list[0];
1363 /* Open the ClamAV Socket */
1364 if ( (sock = ip_socket(SOCK_STREAM, AF_INET)) < 0) {
1365 log_write(0, LOG_MAIN|LOG_PANIC,
1366 "malware acl condition: clamd: unable to acquire socket (%s)",
1371 if (ip_connect(sock, AF_INET, (uschar*)inet_ntoa(in), port, 5) < 0) {
1373 log_write(0, LOG_MAIN|LOG_PANIC,
1374 "malware acl condition: clamd: connection to %s, port %u failed (%s)",
1375 inet_ntoa(in), port, strerror(errno));
1380 /* open the local socket */
1381 if ((sock = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
1382 log_write(0, LOG_MAIN|LOG_PANIC,
1383 "malware acl condition: clamd: unable to acquire socket (%s)",
1388 server.sun_family = AF_UNIX;
1389 Ustrcpy(server.sun_path, clamd_options);
1391 if (connect(sock, (struct sockaddr *) &server, sizeof(struct sockaddr_un)) < 0) {
1393 log_write(0, LOG_MAIN|LOG_PANIC,
1394 "malware acl condition: clamd: unable to connect to UNIX socket %s (%s)",
1395 clamd_options, strerror(errno) );
1400 /* have socket in variable "sock"; command to use is semi-independent of
1401 * the socket protocol. We use SCAN if is local (either Unix/local
1402 * domain socket, or explicitly told local) else we stream the data.
1403 * How we stream the data depends upon how we were built. */
1405 if (!use_scan_command) {
1407 #ifdef WITH_OLD_CLAMAV_STREAM
1408 /* "STREAM\n" command, get back a "PORT <N>\n" response, send data to
1409 * that port on a second connection; then in the scan-method-neutral
1410 * part, read the response back on the original connection. */
1412 DEBUG(D_acl) debug_printf("Malware scan: issuing %s old-style remote scan (PORT)\n",
1415 /* Pass the string to ClamAV (7 = "STREAM\n") */
1416 if (send(sock, "STREAM\n", 7, 0) < 0) {
1417 log_write(0, LOG_MAIN|LOG_PANIC,"malware acl condition: clamd: unable to write to socket (%s)",
1422 memset(av_buffer2, 0, sizeof(av_buffer2));
1423 bread = ip_recv(sock, av_buffer2, sizeof(av_buffer2), MALWARE_TIMEOUT);
1426 log_write(0, LOG_MAIN|LOG_PANIC,
1427 "malware acl condition: clamd: unable to read PORT from socket (%s)",
1433 if (bread == sizeof(av_buffer)) {
1434 log_write(0, LOG_MAIN|LOG_PANIC,
1435 "malware acl condition: clamd: buffer too small");
1440 if (!(*av_buffer2)) {
1441 log_write(0, LOG_MAIN|LOG_PANIC,
1442 "malware acl condition: clamd: ClamAV returned null");
1447 av_buffer2[bread] = '\0';
1448 if( sscanf(CS av_buffer2, "PORT %u\n", &port) != 1 ) {
1449 log_write(0, LOG_MAIN|LOG_PANIC,
1450 "malware acl condition: clamd: Expected port information from clamd, got '%s'", av_buffer2);
1455 if ( (sockData = ip_socket(SOCK_STREAM, AF_INET)) < 0) {
1456 log_write(0, LOG_MAIN|LOG_PANIC,
1457 "malware acl condition: clamd: unable to acquire socket (%s)",
1463 if (ip_connect(sockData, AF_INET, (uschar*)inet_ntoa(in), port, 5) < 0) {
1464 log_write(0, LOG_MAIN|LOG_PANIC,
1465 "malware acl condition: clamd: connection to %s, port %u failed (%s)",
1466 inet_ntoa(in), port, strerror(errno));
1467 (void)close(sockData); (void)close(sock);
1471 #define CLOSE_SOCKDATA (void)close(sockData)
1472 #else /* WITH_OLD_CLAMAV_STREAM not defined */
1473 /* New protocol: "zINSTREAM\n" followed by a sequence of <length><data>
1474 chunks, <n> a 4-byte number (network order), terminated by a zero-length
1477 DEBUG(D_acl) debug_printf("Malware scan: issuing %s new-style remote scan (zINSTREAM)\n",
1480 /* Pass the string to ClamAV (10 = "zINSTREAM\0") */
1481 if (send(sock, "zINSTREAM", 10, 0) < 0) {
1482 log_write(0, LOG_MAIN|LOG_PANIC,
1483 "malware acl condition: clamd: unable to send zINSTREAM to socket (%s)",
1489 #define CLOSE_SOCKDATA /**/
1492 /* calc file size */
1493 clam_fd = open(CS eml_filename, O_RDONLY);
1494 if (clam_fd == -1) {
1495 log_write(0, LOG_MAIN|LOG_PANIC,
1496 "malware acl condition: clamd: can't open spool file %s: %s",
1497 eml_filename, strerror(errno));
1498 CLOSE_SOCKDATA; (void)close(sock);
1501 fsize = lseek(clam_fd, 0, SEEK_END);
1503 log_write(0, LOG_MAIN|LOG_PANIC,
1504 "malware acl condition: clamd: can't seek spool file %s: %s",
1505 eml_filename, strerror(errno));
1506 CLOSE_SOCKDATA; (void)close(sock);
1509 lseek(clam_fd, 0, SEEK_SET);
1511 clamav_fbuf = (uschar *) malloc (fsize);
1513 log_write(0, LOG_MAIN|LOG_PANIC,
1514 "malware acl condition: clamd: unable to allocate memory %u for file (%s)",
1515 fsize, eml_filename);
1516 CLOSE_SOCKDATA; (void)close(sock); (void)close(clam_fd);
1520 result = read (clam_fd, clamav_fbuf, fsize);
1522 log_write(0, LOG_MAIN|LOG_PANIC,
1523 "malware acl condition: clamd: can't read spool file %s: %s",
1524 eml_filename, strerror(errno));
1525 CLOSE_SOCKDATA; (void)close(sock); (void)close(clam_fd);
1529 (void)close(clam_fd);
1531 /* send file body to socket */
1532 #ifdef WITH_OLD_CLAMAV_STREAM
1533 if (send(sockData, clamav_fbuf, fsize, 0) < 0) {
1534 log_write(0, LOG_MAIN|LOG_PANIC,
1535 "malware acl condition: clamd: unable to send file body to socket (%s:%u)", hostname, port);
1536 CLOSE_SOCKDATA; (void)close(sock);
1541 send_size = htonl(fsize);
1542 send_final_zeroblock = 0;
1543 if ((send(sock, &send_size, sizeof(send_size), 0) < 0) ||
1544 (send(sock, clamav_fbuf, fsize, 0) < 0) ||
1545 (send(sock, &send_final_zeroblock, sizeof(send_final_zeroblock), 0) < 0))
1547 log_write(0, LOG_MAIN|LOG_PANIC,
1548 "malware acl condition: clamd: unable to send file body to socket (%s:%u)", hostname, port);
1558 #undef CLOSE_SOCKDATA
1560 } else { /* use scan command */
1561 /* Send a SCAN command pointing to a filename; then in the then in the
1562 * scan-method-neutral part, read the response back */
1564 /* ================================================================= */
1566 /* Prior to the reworking post-Exim-4.72, this scanned a directory,
1567 which dates to when ClamAV needed us to break apart the email into the
1568 MIME parts (eg, with the now deprecated demime condition coming first).
1569 Some time back, ClamAV gained the ability to deconstruct the emails, so
1570 doing this would actually have resulted in the mail attachments being
1571 scanned twice, in the broken out files and from the original .eml.
1572 Since ClamAV now handles emails (and has for quite some time) we can
1573 just use the email file itself. */
1574 /* Pass the string to ClamAV (7 = "SCAN \n" + \0) */
1575 fits = string_format(file_name, sizeof(file_name), "SCAN %s\n",
1579 log_write(0, LOG_MAIN|LOG_PANIC,
1580 "malware filename does not fit in buffer [malware_internal() clamd]");
1583 DEBUG(D_acl) debug_printf("Malware scan: issuing %s local-path scan [%s]\n",
1584 scanner_name, clamd_options);
1586 if (send(sock, file_name, Ustrlen(file_name), 0) < 0) {
1588 log_write(0, LOG_MAIN|LOG_PANIC,"malware acl condition: clamd: unable to write to socket (%s)",
1593 /* Do not shut down the socket for writing; a user report noted that
1594 * clamd 0.70 does not react well to this. */
1596 /* Commands have been sent, no matter which scan method or connection
1597 * type we're using; now just read the result, independent of method. */
1599 /* Read the result */
1600 memset(av_buffer, 0, sizeof(av_buffer));
1601 bread = ip_recv(sock, av_buffer, sizeof(av_buffer), MALWARE_TIMEOUT);
1605 log_write(0, LOG_MAIN|LOG_PANIC,
1606 "malware acl condition: clamd: unable to read from socket (%s)",
1611 if (bread == sizeof(av_buffer)) {
1612 log_write(0, LOG_MAIN|LOG_PANIC,
1613 "malware acl condition: clamd: buffer too small");
1617 /* Check the result. ClamAV Returns
1618 infected: -> "<filename>: <virusname> FOUND"
1619 not-infected: -> "<filename>: OK"
1620 error: -> "<filename>: <errcode> ERROR */
1622 if (!(*av_buffer)) {
1623 log_write(0, LOG_MAIN|LOG_PANIC,
1624 "malware acl condition: clamd: ClamAV returned null");
1628 /* strip newline at the end (won't be present for zINSTREAM) */
1629 p = av_buffer + Ustrlen(av_buffer) - 1;
1630 if( *p == '\n' ) *p = '\0';
1632 DEBUG(D_acl) debug_printf("Malware response: %s\n", av_buffer);
1634 /* colon in returned output? */
1635 if((p = Ustrrchr(av_buffer,':')) == NULL) {
1636 log_write(0, LOG_MAIN|LOG_PANIC,
1637 "malware acl condition: clamd: ClamAV returned malformed result: %s",
1642 /* strip filename */
1644 while (*p == ' ') ++p;
1646 if ((p = Ustrstr(vname, "FOUND"))!=NULL) {
1648 for (--p;p>vname && *p<=32;p--) *p=0;
1649 for (;*vname==32;vname++);
1650 Ustrcpy(malware_name_buffer,vname);
1651 malware_name = malware_name_buffer;
1652 DEBUG(D_acl) debug_printf("Malware found, name \"%s\"\n", malware_name);
1655 if (Ustrstr(vname, "ERROR")!=NULL) {
1656 /* ClamAV reports ERROR
1658 for (;*vname!='\n' && vname>av_buffer; vname--);
1659 if (*vname=='\n') vname++;
1661 log_write(0, LOG_MAIN|LOG_PANIC,
1662 "malware acl condition: clamd: ClamAV returned %s",vname);
1666 /* Everything should be OK */
1667 malware_name = NULL;
1668 DEBUG(D_acl) debug_printf("Malware not found\n");
1672 /* ----------------------------------------------------------------------- */
1675 /* "mksd" scanner type --------------------------------------------------- */
1676 else if (strcmpic(scanner_name,US"mksd") == 0) {
1677 uschar *mksd_options;
1678 char *mksd_options_end;
1679 uschar mksd_options_buffer[32];
1680 int mksd_maxproc = 1; /* default, if no option supplied */
1681 struct sockaddr_un server;
1685 if ((mksd_options = string_nextinlist(&av_scanner_work, &sep,
1686 mksd_options_buffer,
1687 sizeof(mksd_options_buffer))) != NULL) {
1688 mksd_maxproc = (int) strtol(CS mksd_options, &mksd_options_end, 10);
1689 if ((*mksd_options == '\0') || (*mksd_options_end != '\0') ||
1690 (mksd_maxproc < 1) || (mksd_maxproc > 32)) {
1691 log_write(0, LOG_MAIN|LOG_PANIC,
1692 "malware acl condition: mksd: invalid option '%s'", mksd_options);
1697 /* open the mksd socket */
1698 sock = socket(AF_UNIX, SOCK_STREAM, 0);
1700 log_write(0, LOG_MAIN|LOG_PANIC,
1701 "malware acl condition: can't open UNIX socket.");
1704 server.sun_family = AF_UNIX;
1705 Ustrcpy(server.sun_path, "/var/run/mksd/socket");
1706 if (connect(sock, (struct sockaddr *) &server, sizeof(struct sockaddr_un)) < 0) {
1708 log_write(0, LOG_MAIN|LOG_PANIC,
1709 "malware acl condition: unable to connect to mksd UNIX socket (/var/run/mksd/socket). errno=%d", errno);
1713 malware_name = NULL;
1715 DEBUG(D_acl) debug_printf("Malware scan: issuing %s scan\n", scanner_name);
1717 retval = mksd_scan_packed(sock, eml_filename);
1722 /* ----------------------------------------------------------------------- */
1724 /* "unknown" scanner type ------------------------------------------------- */
1726 log_write(0, LOG_MAIN|LOG_PANIC,
1727 "malware condition: unknown scanner type '%s'", scanner_name);
1730 /* ----------------------------------------------------------------------- */
1732 /* set "been here, done that" marker */
1736 /* match virus name against pattern (caseless ------->----------v) */
1737 if ( (malware_name != NULL) &&
1738 (regex_match_and_setup(re, malware_name, 0, -1)) ) {
1739 DEBUG(D_acl) debug_printf("Matched regex to malware [%s] [%s]\n", malware_regex, malware_name);
1748 /* simple wrapper for reading lines from sockets */
1749 int recv_line(int sock, uschar *buffer, int size) {
1752 memset(buffer,0,size);
1754 while(recv(sock,p,1,0) > -1) {
1755 if ((p-buffer) > (size-2)) break;
1756 if (*p == '\n') break;
1757 if (*p != '\r') p++;
1765 /* ============= private routines for the "mksd" scanner type ============== */
1767 #include <sys/uio.h>
1769 static int mksd_writev (int sock, struct iovec *iov, int iovcnt)
1775 i = writev (sock, iov, iovcnt);
1776 while ((i < 0) && (errno == EINTR));
1779 log_write(0, LOG_MAIN|LOG_PANIC,
1780 "malware acl condition: unable to write to mksd UNIX socket (/var/run/mksd/socket)");
1785 if (i >= iov->iov_len) {
1792 iov->iov_base = CS iov->iov_base + i;
1798 static int mksd_read_lines (int sock, uschar *av_buffer, int av_buffer_size)
1804 if ((i = recv (sock, av_buffer+offset, av_buffer_size-offset, 0)) <= 0) {
1806 log_write(0, LOG_MAIN|LOG_PANIC,
1807 "malware acl condition: unable to read from mksd UNIX socket (/var/run/mksd/socket)");
1812 /* offset == av_buffer_size -> buffer full */
1813 if (offset == av_buffer_size) {
1815 log_write(0, LOG_MAIN|LOG_PANIC,
1816 "malware acl condition: malformed reply received from mksd");
1819 } while (av_buffer[offset-1] != '\n');
1821 av_buffer[offset] = '\0';
1825 static int mksd_parse_line (char *line)
1836 if ((p = strchr (line, '\n')) != NULL)
1838 log_write(0, LOG_MAIN|LOG_PANIC,
1839 "malware acl condition: mksd scanner failed: %s", line);
1843 if ((p = strchr (line, '\n')) != NULL) {
1845 if (((p-line) > 5) && ((p-line) < sizeof (malware_name_buffer)) && (line[3] == ' '))
1846 if (((p = strchr (line+4, ' ')) != NULL) && ((p-line) > 4)) {
1848 Ustrcpy (malware_name_buffer, line+4);
1849 malware_name = malware_name_buffer;
1853 log_write(0, LOG_MAIN|LOG_PANIC,
1854 "malware acl condition: malformed reply received from mksd: %s", line);
1859 static int mksd_scan_packed(int sock, uschar *scan_filename)
1861 struct iovec iov[3];
1862 char *cmd = "MSQ\n";
1863 uschar av_buffer[1024];
1865 iov[0].iov_base = cmd;
1867 iov[1].iov_base = CS scan_filename;
1868 iov[1].iov_len = Ustrlen(scan_filename);
1869 iov[2].iov_base = cmd + 3;
1872 if (mksd_writev (sock, iov, 3) < 0)
1875 if (mksd_read_lines (sock, av_buffer, sizeof (av_buffer)) < 0)
1880 return mksd_parse_line (CS av_buffer);