1 /*************************************************
2 * Exim - an Internet mail transport agent *
3 *************************************************/
5 /* Copyright (c) Tom Kistner <tom@duncanthrax.net> 2003-2013 */
8 /* Code for calling virus (malware) scanners. Called from acl.c. */
11 #ifdef WITH_CONTENT_SCAN
13 /* The maximum number of clamd servers that are supported in the configuration */
14 #define MAX_CLAMD_SERVERS 32
15 #define MAX_CLAMD_SERVERS_S "32"
16 /* Maximum length of the hostname that can be specified in the clamd address list */
17 #define MAX_CLAMD_ADDRESS_LENGTH 64
18 #define MAX_CLAMD_ADDRESS_LENGTH_S "64"
20 typedef struct clamd_address_container {
21 uschar tcp_addr[MAX_CLAMD_ADDRESS_LENGTH];
22 unsigned int tcp_port;
23 } clamd_address_container;
25 /* declaration of private routines */
26 static int mksd_scan_packed(int sock, uschar *scan_filename);
27 static int malware_internal(uschar **listptr, uschar *eml_filename, BOOL faking);
29 /* SHUT_WR seems to be undefined on Unixware? */
35 #define MALWARE_TIMEOUT 120
38 #define DRWEBD_SCAN_CMD (1) /* scan file, buffer or diskfile */
39 #define DRWEBD_RETURN_VIRUSES (1<<0) /* ask daemon return to us viruses names from report */
40 #define DRWEBD_IS_MAIL (1<<19) /* say to daemon that format is "archive MAIL" */
42 #define DERR_READ_ERR (1<<0) /* read error */
43 #define DERR_NOMEMORY (1<<2) /* no memory */
44 #define DERR_TIMEOUT (1<<9) /* scan timeout has run out */
45 #define DERR_BAD_CALL (1<<15) /* wrong command */
47 /* Routine to check whether a system is big- or litte-endian.
48 Ripped from http://www.faqs.org/faqs/graphics/fileformats-faq/part4/section-7.html
49 Needed for proper kavdaemon implementation. Sigh. */
50 #define BIG_MY_ENDIAN 0
51 #define LITTLE_MY_ENDIAN 1
52 int test_byte_order(void);
53 int test_byte_order() {
54 short int word = 0x0001;
55 char *byte = (char *) &word;
56 return(byte[0] ? LITTLE_MY_ENDIAN : BIG_MY_ENDIAN);
59 uschar malware_name_buffer[256];
62 /* Gross hacks for the -bmalware option; perhaps we should just create
63 the scan directory normally for that case, but look into rigging up the
64 needed header variables if not already set on the command-line? */
65 extern int spool_mbox_ok;
66 extern uschar spooled_message_id[17];
68 /*************************************************
69 * Scan an email for malware *
70 *************************************************/
72 /* This is the normal interface for scanning an email, which doesn't need a
73 filename; it's a wrapper around the malware_file function.
76 listptr the list of options to the "malware = ..." ACL condition
78 Returns: Exim message processing code (OK, FAIL, DEFER, ...)
79 where true means malware was found (condition applies)
81 int malware(uschar **listptr) {
82 uschar scan_filename[1024];
86 fits = string_format(scan_filename, sizeof(scan_filename),
87 CS"%s/scan/%s/%s.eml", spool_directory, message_id, message_id);
91 log_write(0, LOG_MAIN|LOG_PANIC,
92 "malware filename does not fit in buffer [malware()]");
96 ret = malware_internal(listptr, scan_filename, FALSE);
97 if (ret == DEFER) av_failed = TRUE;
103 /*************************************************
104 * Scan a file for malware *
105 *************************************************/
107 /* This is a test wrapper for scanning an email, which is not used in
108 normal processing. Scan any file, using the Exim scanning interface.
109 This function tampers with various global variables so is unsafe to use
110 in any other context.
113 eml_filename a file holding the message to be scanned
115 Returns: Exim message processing code (OK, FAIL, DEFER, ...)
116 where true means malware was found (condition applies)
119 malware_in_file(uschar *eml_filename) {
120 uschar *scan_options[2];
121 uschar message_id_buf[64];
124 scan_options[0] = US"*";
125 scan_options[1] = NULL;
127 /* spool_mbox() assumes various parameters exist, when creating
128 the relevant directory and the email within */
129 (void) string_format(message_id_buf, sizeof(message_id_buf),
130 "dummy-%d", vaguely_random_number(INT_MAX));
131 message_id = message_id_buf;
132 sender_address = US"malware-sender@example.net";
134 recipients_list = NULL;
135 receive_add_recipient(US"malware-victim@example.net", -1);
136 enable_dollar_recipients = TRUE;
138 ret = malware_internal(scan_options, eml_filename, TRUE);
140 Ustrncpy(spooled_message_id, message_id, sizeof(spooled_message_id));
142 /* don't set no_mbox_unspool; at present, there's no way for it to become
143 set, but if that changes, then it should apply to these tests too */
146 /* silence static analysis tools */
153 /*************************************************
154 * Scan content for malware *
155 *************************************************/
157 /* This is an internal interface for scanning an email; the normal interface
158 is via malware(), or there's malware_in_file() used for testing/debugging.
161 listptr the list of options to the "malware = ..." ACL condition
162 eml_filename the file holding the email to be scanned
163 faking whether or not we're faking this up for the -bmalware test
165 Returns: Exim message processing code (OK, FAIL, DEFER, ...)
166 where true means malware was found (condition applies)
168 static int malware_internal(uschar **listptr, uschar *eml_filename, BOOL faking) {
170 uschar *list = *listptr;
171 uschar *av_scanner_work = av_scanner;
172 uschar *scanner_name;
173 uschar scanner_name_buffer[16];
174 uschar *malware_regex;
175 uschar malware_regex_buffer[64];
176 uschar malware_regex_default[] = ".+";
177 unsigned long mbox_size;
181 const uschar *rerror;
183 /* make sure the eml mbox file is spooled up */
184 mbox_file = spool_mbox(&mbox_size, faking ? eml_filename : NULL);
185 if (mbox_file == NULL) {
186 /* error while spooling */
187 log_write(0, LOG_MAIN|LOG_PANIC,
188 "malware acl condition: error while creating mbox spool file");
191 /* none of our current scanners need the mbox
192 file as a stream, so we can close it right away */
193 (void)fclose(mbox_file);
195 /* extract the malware regex to match against from the option list */
196 if ((malware_regex = string_nextinlist(&list, &sep,
197 malware_regex_buffer,
198 sizeof(malware_regex_buffer))) != NULL) {
200 /* parse 1st option */
201 if ( (strcmpic(malware_regex,US"false") == 0) ||
202 (Ustrcmp(malware_regex,"0") == 0) ) {
203 /* explicitly no matching */
207 /* special cases (match anything except empty) */
208 if ( (strcmpic(malware_regex,US"true") == 0) ||
209 (Ustrcmp(malware_regex,"*") == 0) ||
210 (Ustrcmp(malware_regex,"1") == 0) ) {
211 malware_regex = malware_regex_default;
215 /* empty means "don't match anything" */
219 /* Reset sep that is set by previous string_nextinlist() call */
222 /* compile the regex, see if it works */
223 re = pcre_compile(CS malware_regex, PCRE_COPT, (const char **)&rerror, &roffset, NULL);
225 log_write(0, LOG_MAIN|LOG_PANIC,
226 "malware acl condition: regular expression error in '%s': %s at offset %d", malware_regex, rerror, roffset);
230 /* if av_scanner starts with a dollar, expand it first */
231 if (*av_scanner == '$') {
232 av_scanner_work = expand_string(av_scanner);
233 if (av_scanner_work == NULL) {
234 log_write(0, LOG_MAIN|LOG_PANIC,
235 "malware acl condition: av_scanner starts with $, but expansion failed: %s", expand_string_message);
239 debug_printf("Expanded av_scanner global: %s\n", av_scanner_work);
240 /* disable result caching in this case */
246 /* Do not scan twice. */
247 if (malware_ok == 0) {
249 /* find the scanner type from the av_scanner option */
250 if ((scanner_name = string_nextinlist(&av_scanner_work, &sep,
252 sizeof(scanner_name_buffer))) == NULL) {
253 /* no scanner given */
254 log_write(0, LOG_MAIN|LOG_PANIC,
255 "malware acl condition: av_scanner configuration variable is empty");
259 /* "f-protd" scanner type ----------------------------------------------- */
260 if (strcmpic(scanner_name, US"f-protd") == 0) {
261 uschar *fp_options, *fp_scan_option;
262 uschar fp_scan_option_buffer[1024];
263 uschar fp_options_buffer[1024];
264 uschar fp_options_default[] = "localhost 10200-10204";
265 uschar hostname[256];
266 unsigned int port, portlow, porthigh, connect_ok=0, detected=0, par_count = 0;
270 uschar scanrequest[2048], buf[32768], *strhelper, *strhelper2;
272 if ((fp_options = string_nextinlist(&av_scanner_work, &sep,
273 fp_options_buffer, sizeof(fp_options_buffer))) == NULL) {
274 /* no options supplied, use default options */
275 fp_options = fp_options_default;
278 /* extract host and port part */
279 if ( sscanf(CS fp_options, "%s %u-%u", hostname, &portlow, &porthigh) != 3 ) {
280 if ( sscanf(CS fp_options, "%s %u", hostname, &portlow) != 2 ) {
281 log_write(0, LOG_MAIN|LOG_PANIC,
282 "malware acl condition: f-protd: invalid socket '%s'", fp_options);
288 /* Lookup the host */
289 if((he = gethostbyname(CS hostname)) == 0) {
290 log_write(0, LOG_MAIN|LOG_PANIC,
291 "malware acl condition: f-protd: failed to lookup host '%s'", hostname);
295 in = *(struct in_addr *) he->h_addr_list[0];
299 /* Open the f-protd TCP socket */
300 if ( (sock = ip_socket(SOCK_STREAM, AF_INET)) < 0) {
301 log_write(0, LOG_MAIN|LOG_PANIC,
302 "malware acl condition: f-protd: unable to acquire socket (%s)",
307 /* Try to connect to all portslow-high until connection is established */
308 for (port = portlow; !connect_ok && port < porthigh; port++) {
309 if (ip_connect(sock, AF_INET, (uschar*)inet_ntoa(in), port, 5) >= 0) {
315 log_write(0, LOG_MAIN|LOG_PANIC,
316 "malware acl condition: f-protd: connection to %s, port %u-%u failed (%s)",
317 inet_ntoa(in), portlow, porthigh, strerror(errno));
322 DEBUG(D_acl) debug_printf("Malware scan: issuing %s GET\n", scanner_name);
323 (void)string_format(scanrequest, 1024, CS"GET %s", eml_filename);
325 while ((fp_scan_option = string_nextinlist(&av_scanner_work, &sep,
326 fp_scan_option_buffer, sizeof(fp_scan_option_buffer))) != NULL) {
328 Ustrcat(scanrequest, "%20");
330 Ustrcat(scanrequest, "?");
332 Ustrcat(scanrequest, fp_scan_option);
335 Ustrcat(scanrequest, " HTTP/1.0\r\n\r\n");
337 /* send scan request */
338 if (send(sock, &scanrequest, Ustrlen(scanrequest)+1, 0) < 0) {
340 log_write(0, LOG_MAIN|LOG_PANIC,
341 "malware acl condition: f-protd: unable to send command to socket (%s)", scanrequest);
345 /* We get a lot of empty lines, so we need this hack to check for any data at all */
346 while( recv(sock, buf, 1, MSG_PEEK) > 0 ) {
347 if ( recv_line(sock, buf, 32768) > 0) {
348 if ( Ustrstr(buf, US"<detected type=\"") != NULL ) {
350 } else if ( detected && (strhelper = Ustrstr(buf, US"<name>")) ) {
351 if ((strhelper2 = Ustrstr(buf, US"</name>")) != NULL) {
353 Ustrcpy(malware_name_buffer, strhelper + 6);
355 } else if ( Ustrstr(buf, US"<summary code=\"") ) {
356 if ( Ustrstr(buf, US"<summary code=\"11\">") ) {
357 malware_name = malware_name_buffer;
366 /* "drweb" scanner type ----------------------------------------------- */
367 /* v0.1 - added support for tcp sockets */
368 /* v0.0 - initial release -- support for unix sockets */
369 else if (strcmpic(scanner_name,US"drweb") == 0) {
370 uschar *drweb_options;
371 uschar drweb_options_buffer[1024];
372 uschar drweb_options_default[] = "/usr/local/drweb/run/drwebd.sock";
373 struct sockaddr_un server;
374 int sock, result, ovector[30];
375 unsigned int port, fsize;
376 uschar tmpbuf[1024], *drweb_fbuf;
377 uschar drweb_match_string[128];
378 int drweb_rc, drweb_cmd, drweb_flags = 0x0000, drweb_fd,
379 drweb_vnum, drweb_slen, drweb_fin = 0x0000;
381 uschar hostname[256];
386 if ((drweb_options = string_nextinlist(&av_scanner_work, &sep,
387 drweb_options_buffer, sizeof(drweb_options_buffer))) == NULL) {
388 /* no options supplied, use default options */
389 drweb_options = drweb_options_default;
392 if (*drweb_options != '/') {
394 /* extract host and port part */
395 if( sscanf(CS drweb_options, "%s %u", hostname, &port) != 2 ) {
396 log_write(0, LOG_MAIN|LOG_PANIC,
397 "malware acl condition: drweb: invalid socket '%s'", drweb_options);
401 /* Lookup the host */
402 if((he = gethostbyname(CS hostname)) == 0) {
403 log_write(0, LOG_MAIN|LOG_PANIC,
404 "malware acl condition: drweb: failed to lookup host '%s'", hostname);
408 in = *(struct in_addr *) he->h_addr_list[0];
410 /* Open the drwebd TCP socket */
411 if ( (sock = ip_socket(SOCK_STREAM, AF_INET)) < 0) {
412 log_write(0, LOG_MAIN|LOG_PANIC,
413 "malware acl condition: drweb: unable to acquire socket (%s)",
418 if (ip_connect(sock, AF_INET, (uschar*)inet_ntoa(in), port, 5) < 0) {
420 log_write(0, LOG_MAIN|LOG_PANIC,
421 "malware acl condition: drweb: connection to %s, port %u failed (%s)",
422 inet_ntoa(in), port, strerror(errno));
426 /* prepare variables */
427 drweb_cmd = htonl(DRWEBD_SCAN_CMD);
428 drweb_flags = htonl(DRWEBD_RETURN_VIRUSES | DRWEBD_IS_MAIL);
431 drweb_fd = open(CS eml_filename, O_RDONLY);
432 if (drweb_fd == -1) {
434 log_write(0, LOG_MAIN|LOG_PANIC,
435 "malware acl condition: drweb: can't open spool file %s: %s",
436 eml_filename, strerror(errno));
439 fsize = lseek(drweb_fd, 0, SEEK_END);
442 (void)close(drweb_fd);
443 log_write(0, LOG_MAIN|LOG_PANIC,
444 "malware acl condition: drweb: can't seek spool file %s: %s",
445 eml_filename, strerror(errno));
448 drweb_slen = htonl(fsize);
449 lseek(drweb_fd, 0, SEEK_SET);
451 DEBUG(D_acl) debug_printf("Malware scan: issuing %s remote scan [%s %u]\n",
452 scanner_name, hostname, port);
454 /* send scan request */
455 if ((send(sock, &drweb_cmd, sizeof(drweb_cmd), 0) < 0) ||
456 (send(sock, &drweb_flags, sizeof(drweb_flags), 0) < 0) ||
457 (send(sock, &drweb_fin, sizeof(drweb_fin), 0) < 0) ||
458 (send(sock, &drweb_slen, sizeof(drweb_slen), 0) < 0)) {
460 (void)close(drweb_fd);
461 log_write(0, LOG_MAIN|LOG_PANIC,
462 "malware acl condition: drweb: unable to send commands to socket (%s)", drweb_options);
466 drweb_fbuf = (uschar *) malloc (fsize);
469 (void)close(drweb_fd);
470 log_write(0, LOG_MAIN|LOG_PANIC,
471 "malware acl condition: drweb: unable to allocate memory %u for file (%s)",
472 fsize, eml_filename);
476 result = read (drweb_fd, drweb_fbuf, fsize);
479 (void)close(drweb_fd);
481 log_write(0, LOG_MAIN|LOG_PANIC,
482 "malware acl condition: drweb: can't read spool file %s: %s",
483 eml_filename, strerror(errno));
486 (void)close(drweb_fd);
488 /* send file body to socket */
489 if (send(sock, drweb_fbuf, fsize, 0) < 0) {
492 log_write(0, LOG_MAIN|LOG_PANIC,
493 "malware acl condition: drweb: unable to send file body to socket (%s)", drweb_options);
496 (void)close(drweb_fd);
499 /* open the drwebd UNIX socket */
500 sock = socket(AF_UNIX, SOCK_STREAM, 0);
502 log_write(0, LOG_MAIN|LOG_PANIC,
503 "malware acl condition: drweb: can't open UNIX socket");
506 server.sun_family = AF_UNIX;
507 Ustrcpy(server.sun_path, drweb_options);
508 if (connect(sock, (struct sockaddr *) &server, sizeof(struct sockaddr_un)) < 0) {
510 log_write(0, LOG_MAIN|LOG_PANIC,
511 "malware acl condition: drweb: unable to connect to socket (%s). errno=%d", drweb_options, errno);
515 /* prepare variables */
516 drweb_cmd = htonl(DRWEBD_SCAN_CMD);
517 drweb_flags = htonl(DRWEBD_RETURN_VIRUSES | DRWEBD_IS_MAIL);
518 drweb_slen = htonl(Ustrlen(eml_filename));
520 DEBUG(D_acl) debug_printf("Malware scan: issuing %s local scan [%s]\n",
521 scanner_name, drweb_options);
523 /* send scan request */
524 if ((send(sock, &drweb_cmd, sizeof(drweb_cmd), 0) < 0) ||
525 (send(sock, &drweb_flags, sizeof(drweb_flags), 0) < 0) ||
526 (send(sock, &drweb_slen, sizeof(drweb_slen), 0) < 0) ||
527 (send(sock, eml_filename, Ustrlen(eml_filename), 0) < 0) ||
528 (send(sock, &drweb_fin, sizeof(drweb_fin), 0) < 0)) {
530 log_write(0, LOG_MAIN|LOG_PANIC,
531 "malware acl condition: drweb: unable to send commands to socket (%s)", drweb_options);
536 /* wait for result */
537 if ((bread = recv(sock, &drweb_rc, sizeof(drweb_rc), 0) != sizeof(drweb_rc))) {
539 log_write(0, LOG_MAIN|LOG_PANIC,
540 "malware acl condition: drweb: unable to read return code");
543 drweb_rc = ntohl(drweb_rc);
545 if ((bread = recv(sock, &drweb_vnum, sizeof(drweb_vnum), 0) != sizeof(drweb_vnum))) {
547 log_write(0, LOG_MAIN|LOG_PANIC,
548 "malware acl condition: drweb: unable to read the number of viruses");
551 drweb_vnum = ntohl(drweb_vnum);
553 /* "virus(es) found" if virus number is > 0 */
557 uschar pre_malware_nb[256];
559 malware_name = malware_name_buffer;
561 /* setup default virus name */
562 Ustrcpy(malware_name_buffer,"unknown");
564 /* read and concatenate virus names into one string */
565 for (i=0;i<drweb_vnum;i++)
567 /* read the size of report */
568 if ((bread = recv(sock, &drweb_slen, sizeof(drweb_slen), 0) != sizeof(drweb_slen))) {
570 log_write(0, LOG_MAIN|LOG_PANIC,
571 "malware acl condition: drweb: cannot read report size");
574 drweb_slen = ntohl(drweb_slen);
576 /* read report body */
577 if ((bread = recv(sock, tmpbuf, drweb_slen, 0)) != drweb_slen) {
579 log_write(0, LOG_MAIN|LOG_PANIC,
580 "malware acl condition: drweb: cannot read report string");
583 tmpbuf[drweb_slen] = '\0';
585 /* set up match regex, depends on retcode */
586 Ustrcpy(drweb_match_string, "infected\\swith\\s*(.+?)$");
588 drweb_re = pcre_compile( CS drweb_match_string,
590 (const char **)&rerror,
594 /* try matcher on the line, grab substring */
595 result = pcre_exec(drweb_re, NULL, CS tmpbuf, Ustrlen(tmpbuf), 0, 0, ovector, 30);
597 pcre_copy_substring(CS tmpbuf, ovector, result, 1, CS pre_malware_nb, 255);
599 /* the first name we just copy to malware_name */
601 Ustrcpy(CS malware_name_buffer, CS pre_malware_nb);
603 /* concatenate each new virus name to previous */
604 int slen = Ustrlen(malware_name_buffer);
605 if (slen < (slen+Ustrlen(pre_malware_nb))) {
606 Ustrcat(malware_name_buffer, "/");
607 Ustrcat(malware_name_buffer, pre_malware_nb);
613 const char *drweb_s = NULL;
615 if (drweb_rc & DERR_READ_ERR) drweb_s = "read error";
616 if (drweb_rc & DERR_NOMEMORY) drweb_s = "no memory";
617 if (drweb_rc & DERR_TIMEOUT) drweb_s = "timeout";
618 if (drweb_rc & DERR_BAD_CALL) drweb_s = "wrong command";
619 /* retcodes DERR_SYMLINK, DERR_NO_REGFILE, DERR_SKIPPED.
620 * DERR_TOO_BIG, DERR_TOO_COMPRESSED, DERR_SPAM,
621 * DERR_CRC_ERROR, DERR_READSOCKET, DERR_WRITE_ERR
622 * and others are ignored */
624 log_write(0, LOG_MAIN|LOG_PANIC,
625 "malware acl condition: drweb: drweb daemon retcode 0x%x (%s)", drweb_rc, drweb_s);
634 /* ----------------------------------------------------------------------- */
635 else if (strcmpic(scanner_name,US"aveserver") == 0) {
637 uschar kav_options_buffer[1024];
638 uschar kav_options_default[] = "/var/run/aveserver";
640 struct sockaddr_un server;
644 if ((kav_options = string_nextinlist(&av_scanner_work, &sep,
646 sizeof(kav_options_buffer))) == NULL) {
647 /* no options supplied, use default options */
648 kav_options = kav_options_default;
651 /* open the aveserver socket */
652 sock = socket(AF_UNIX, SOCK_STREAM, 0);
654 log_write(0, LOG_MAIN|LOG_PANIC,
655 "malware acl condition: can't open UNIX socket.");
658 server.sun_family = AF_UNIX;
659 Ustrcpy(server.sun_path, kav_options);
660 if (connect(sock, (struct sockaddr *) &server, sizeof(struct sockaddr_un)) < 0) {
662 log_write(0, LOG_MAIN|LOG_PANIC,
663 "malware acl condition: unable to connect to aveserver UNIX socket (%s). errno=%d", kav_options, errno);
667 /* read aveserver's greeting and see if it is ready (2xx greeting) */
668 recv_line(sock, buf, 32768);
671 /* aveserver is having problems */
673 log_write(0, LOG_MAIN|LOG_PANIC,
674 "malware acl condition: aveserver is unavailable (Responded: %s).", ((buf[0] != 0) ? buf : (uschar *)"nothing") );
678 /* prepare our command */
679 (void)string_format(buf, 32768, "SCAN bPQRSTUW %s\r\n", eml_filename);
681 DEBUG(D_acl) debug_printf("Malware scan: issuing %s SCAN\n", scanner_name);
684 if (send(sock, buf, Ustrlen(buf), 0) < 0) {
686 log_write(0, LOG_MAIN|LOG_PANIC,
687 "malware acl condition: unable to write to aveserver UNIX socket (%s)", kav_options);
693 /* read response lines, find malware name and final response */
694 while (recv_line(sock, buf, 32768) > 0) {
695 debug_printf("aveserver: %s\n", buf);
698 } else if (buf[0] == '5') {
699 /* aveserver is having problems */
700 log_write(0, LOG_MAIN|LOG_PANIC,
701 "malware acl condition: unable to scan file %s (Responded: %s).",
705 } else if (Ustrncmp(buf,"322",3) == 0) {
706 uschar *p = Ustrchr(&buf[4],' ');
708 Ustrcpy(malware_name_buffer,&buf[4]);
709 malware_name = malware_name_buffer;
713 /* prepare our command */
714 (void)string_format(buf, 32768, "quit\r\n");
717 if (send(sock, buf, Ustrlen(buf), 0) < 0) {
719 log_write(0, LOG_MAIN|LOG_PANIC,
720 "malware acl condition: unable to write to aveserver UNIX socket (%s)", kav_options);
724 /* read aveserver's greeting and see if it is ready (2xx greeting) */
725 recv_line(sock, buf, 32768);
728 /* aveserver is having problems */
730 log_write(0, LOG_MAIN|LOG_PANIC,
731 "malware acl condition: unable to quit aveserver dialogue (Responded: %s).", ((buf[0] != 0) ? buf : (uschar *)"nothing") );
737 if (result == DEFER) return DEFER;
739 /* "fsecure" scanner type ------------------------------------------------- */
740 else if (strcmpic(scanner_name,US"fsecure") == 0) {
741 uschar *fsecure_options;
742 uschar fsecure_options_buffer[1024];
743 uschar fsecure_options_default[] = "/var/run/.fsav";
744 struct sockaddr_un server;
745 int sock, i, j, bread = 0;
746 uschar file_name[1024];
747 uschar av_buffer[1024];
749 static uschar *cmdoptions[] = { US"CONFIGURE\tARCHIVE\t1\n",
750 US"CONFIGURE\tTIMEOUT\t0\n",
751 US"CONFIGURE\tMAXARCH\t5\n",
752 US"CONFIGURE\tMIME\t1\n" };
755 if ((fsecure_options = string_nextinlist(&av_scanner_work, &sep,
756 fsecure_options_buffer,
757 sizeof(fsecure_options_buffer))) == NULL) {
758 /* no options supplied, use default options */
759 fsecure_options = fsecure_options_default;
762 /* open the fsecure socket */
763 sock = socket(AF_UNIX, SOCK_STREAM, 0);
765 log_write(0, LOG_MAIN|LOG_PANIC,
766 "malware acl condition: unable to open fsecure socket %s (%s)",
767 fsecure_options, strerror(errno));
770 server.sun_family = AF_UNIX;
771 Ustrcpy(server.sun_path, fsecure_options);
772 if (connect(sock, (struct sockaddr *) &server, sizeof(struct sockaddr_un)) < 0) {
774 log_write(0, LOG_MAIN|LOG_PANIC,
775 "malware acl condition: unable to connect to fsecure socket %s (%s)",
776 fsecure_options, strerror(errno));
780 DEBUG(D_acl) debug_printf("Malware scan: issuing %s scan [%s]\n",
781 scanner_name, fsecure_options);
784 memset(av_buffer, 0, sizeof(av_buffer));
785 for (i=0; i != 4; i++) {
786 /* debug_printf("send option \"%s\"",cmdoptions[i]); */
787 if (write(sock, cmdoptions[i], Ustrlen(cmdoptions[i])) < 0) {
789 log_write(0, LOG_MAIN|LOG_PANIC,
790 "malware acl condition: unable to write fsecure option %d to %s (%s)",
791 i, fsecure_options, strerror(errno));
795 bread = ip_recv(sock, av_buffer, sizeof(av_buffer), MALWARE_TIMEOUT);
796 if (bread >0) av_buffer[bread]='\0';
799 log_write(0, LOG_MAIN|LOG_PANIC,
800 "malware acl condition: unable to read fsecure answer %d (%s)", i, strerror(errno));
803 for (j=0;j<bread;j++) if((av_buffer[j]=='\r')||(av_buffer[j]=='\n')) av_buffer[j] ='@';
804 /* debug_printf("read answer %d read=%d \"%s\"\n", i, bread, av_buffer ); */
805 /* while (Ustrstr(av_buffer, "OK\tServer configured.@") == NULL); */
808 /* pass the mailfile to fsecure */
809 (void)string_format(file_name,1024,"SCAN\t%s\n", eml_filename);
810 /* debug_printf("send scan %s",file_name); */
811 if (write(sock, file_name, Ustrlen(file_name)) < 0) {
813 log_write(0, LOG_MAIN|LOG_PANIC,
814 "malware acl condition: unable to write fsecure scan to %s (%s)",
815 fsecure_options, strerror(errno));
820 /* todo also SUSPICION\t */
821 fs_inf = pcre_compile("\\S{0,5}INFECTED\\t[^\\t]*\\t([^\\t]+)\\t\\S*$", PCRE_COPT, (const char **)&rerror, &roffset, NULL);
823 /* read report, linewise */
827 memset(av_buffer, 0, sizeof(av_buffer));
829 bread=ip_recv(sock, &av_buffer[i], 1, MALWARE_TIMEOUT);
832 log_write(0, LOG_MAIN|LOG_PANIC,
833 "malware acl condition: unable to read fsecure result (%s)", strerror(errno));
838 while ((i < sizeof(av_buffer)-1 ) && (av_buffer[i-1] != '\n'));
839 av_buffer[i-1] = '\0';
840 /* debug_printf("got line \"%s\"\n",av_buffer); */
842 /* Really search for virus again? */
843 if (malware_name == NULL) {
844 /* try matcher on the line, grab substring */
845 i = pcre_exec(fs_inf, NULL, CS av_buffer, Ustrlen(av_buffer), 0, 0, ovector, 30);
848 pcre_copy_substring(CS av_buffer, ovector, i, 1, CS malware_name_buffer, 255);
849 malware_name = malware_name_buffer;
853 while (Ustrstr(av_buffer, "OK\tScan ok.") == NULL);
856 /* ----------------------------------------------------------------------- */
858 /* "kavdaemon" scanner type ------------------------------------------------ */
859 else if (strcmpic(scanner_name,US"kavdaemon") == 0) {
861 uschar kav_options_buffer[1024];
862 uschar kav_options_default[] = "/var/run/AvpCtl";
863 struct sockaddr_un server;
867 uschar scanrequest[1024];
868 uschar kav_match_string[128];
870 unsigned long kav_reportlen, bread;
875 if ((kav_options = string_nextinlist(&av_scanner_work, &sep,
877 sizeof(kav_options_buffer))) == NULL) {
878 /* no options supplied, use default options */
879 kav_options = kav_options_default;
882 /* open the kavdaemon socket */
883 sock = socket(AF_UNIX, SOCK_STREAM, 0);
885 log_write(0, LOG_MAIN|LOG_PANIC,
886 "malware acl condition: can't open UNIX socket.");
889 server.sun_family = AF_UNIX;
890 Ustrcpy(server.sun_path, kav_options);
891 if (connect(sock, (struct sockaddr *) &server, sizeof(struct sockaddr_un)) < 0) {
893 log_write(0, LOG_MAIN|LOG_PANIC,
894 "malware acl condition: unable to connect to kavdaemon UNIX socket (%s). errno=%d", kav_options, errno);
898 /* get current date and time, build scan request */
900 /* pdp note: before the eml_filename parameter, this scanned the
901 directory; not finding documentation, so we'll strip off the directory.
902 The side-effect is that the test framework scanning may end up in
903 scanning more than was requested, but for the normal interface, this is
905 strftime(CS tmpbuf, sizeof(tmpbuf), "<0>%d %b %H:%M:%S:%%s", localtime(&t));
906 fits = string_format(scanrequest, 1024,CS tmpbuf, eml_filename);
909 log_write(0, LOG_MAIN|LOG_PANIC,
910 "malware filename does not fit in buffer [malware_internal() kavdaemon]");
912 p = Ustrrchr(scanrequest, '/');
916 DEBUG(D_acl) debug_printf("Malware scan: issuing %s scan [%s]\n",
917 scanner_name, kav_options);
919 /* send scan request */
920 if (send(sock, scanrequest, Ustrlen(scanrequest)+1, 0) < 0) {
922 log_write(0, LOG_MAIN|LOG_PANIC,
923 "malware acl condition: unable to write to kavdaemon UNIX socket (%s)", kav_options);
927 /* wait for result */
928 if ((bread = recv(sock, tmpbuf, 2, 0) != 2)) {
930 log_write(0, LOG_MAIN|LOG_PANIC,
931 "malware acl condition: unable to read 2 bytes from kavdaemon socket.");
935 /* get errorcode from one nibble */
936 if (test_byte_order() == LITTLE_MY_ENDIAN) {
937 kav_rc = tmpbuf[0] & 0x0F;
940 kav_rc = tmpbuf[1] & 0x0F;
943 /* improper kavdaemon configuration */
944 if ( (kav_rc == 5) || (kav_rc == 6) ) {
946 log_write(0, LOG_MAIN|LOG_PANIC,
947 "malware acl condition: please reconfigure kavdaemon to NOT disinfect or remove infected files.");
953 log_write(0, LOG_MAIN|LOG_PANIC,
954 "malware acl condition: kavdaemon reported 'scanning not completed' (code 1).");
960 log_write(0, LOG_MAIN|LOG_PANIC,
961 "malware acl condition: kavdaemon reported 'kavdaemon damaged' (code 7).");
965 /* code 8 is not handled, since it is ambigous. It appears mostly on
966 bounces where part of a file has been cut off */
968 /* "virus found" return codes (2-4) */
969 if ((kav_rc > 1) && (kav_rc < 5)) {
972 /* setup default virus name */
973 Ustrcpy(malware_name_buffer,"unknown");
974 malware_name = malware_name_buffer;
976 if (test_byte_order() == LITTLE_MY_ENDIAN) {
977 report_flag = tmpbuf[1];
980 report_flag = tmpbuf[0];
983 /* read the report, if available */
984 if( report_flag == 1 ) {
985 /* read report size */
986 if ((bread = recv(sock, &kav_reportlen, 4, 0)) != 4) {
988 log_write(0, LOG_MAIN|LOG_PANIC,
989 "malware acl condition: cannot read report size from kavdaemon");
993 /* it's possible that avp returns av_buffer[1] == 1 but the
994 reportsize is 0 (!?) */
995 if (kav_reportlen > 0) {
996 /* set up match regex, depends on retcode */
998 Ustrcpy(kav_match_string, "suspicion:\\s*(.+?)\\s*$");
1000 Ustrcpy(kav_match_string, "infected:\\s*(.+?)\\s*$");
1002 kav_re = pcre_compile( CS kav_match_string,
1004 (const char **)&rerror,
1008 /* read report, linewise */
1009 while (kav_reportlen > 0) {
1014 while ( recv(sock, &tmpbuf[bread], 1, 0) == 1 ) {
1016 if ( (tmpbuf[bread] == '\n') || (bread > 1021) ) break;
1020 tmpbuf[bread] = '\0';
1022 /* try matcher on the line, grab substring */
1023 result = pcre_exec(kav_re, NULL, CS tmpbuf, Ustrlen(tmpbuf), 0, 0, ovector, 30);
1025 pcre_copy_substring(CS tmpbuf, ovector, result, 1, CS malware_name_buffer, 255);
1033 /* no virus found */
1034 malware_name = NULL;
1039 /* ----------------------------------------------------------------------- */
1042 /* "cmdline" scanner type ------------------------------------------------ */
1043 else if (strcmpic(scanner_name,US"cmdline") == 0) {
1044 uschar *cmdline_scanner;
1045 uschar cmdline_scanner_buffer[1024];
1046 uschar *cmdline_trigger;
1047 uschar cmdline_trigger_buffer[1024];
1048 const pcre *cmdline_trigger_re;
1049 uschar *cmdline_regex;
1050 uschar cmdline_regex_buffer[1024];
1051 const pcre *cmdline_regex_re;
1052 uschar file_name[1024];
1053 uschar commandline[1024];
1054 void (*eximsigchld)(int);
1055 void (*eximsigpipe)(int);
1056 FILE *scanner_out = NULL;
1057 FILE *scanner_record = NULL;
1058 uschar linebuffer[32767];
1065 /* find scanner command line */
1066 if ((cmdline_scanner = string_nextinlist(&av_scanner_work, &sep,
1067 cmdline_scanner_buffer,
1068 sizeof(cmdline_scanner_buffer))) == NULL) {
1069 /* no command line supplied */
1070 log_write(0, LOG_MAIN|LOG_PANIC,
1071 "malware acl condition: missing commandline specification for cmdline scanner type.");
1075 /* find scanner output trigger */
1076 if ((cmdline_trigger = string_nextinlist(&av_scanner_work, &sep,
1077 cmdline_trigger_buffer,
1078 sizeof(cmdline_trigger_buffer))) == NULL) {
1079 /* no trigger regex supplied */
1080 log_write(0, LOG_MAIN|LOG_PANIC,
1081 "malware acl condition: missing trigger specification for cmdline scanner type.");
1085 /* precompile trigger regex */
1086 cmdline_trigger_re = pcre_compile(CS cmdline_trigger, PCRE_COPT, (const char **)&rerror, &roffset, NULL);
1087 if (cmdline_trigger_re == NULL) {
1088 log_write(0, LOG_MAIN|LOG_PANIC,
1089 "malware acl condition: regular expression error in '%s': %s at offset %d", cmdline_trigger, rerror, roffset);
1093 /* find scanner name regex */
1094 if ((cmdline_regex = string_nextinlist(&av_scanner_work, &sep,
1095 cmdline_regex_buffer,
1096 sizeof(cmdline_regex_buffer))) == NULL) {
1097 /* no name regex supplied */
1098 log_write(0, LOG_MAIN|LOG_PANIC,
1099 "malware acl condition: missing virus name regex specification for cmdline scanner type.");
1103 /* precompile name regex */
1104 cmdline_regex_re = pcre_compile(CS cmdline_regex, PCRE_COPT, (const char **)&rerror, &roffset, NULL);
1105 if (cmdline_regex_re == NULL) {
1106 log_write(0, LOG_MAIN|LOG_PANIC,
1107 "malware acl condition: regular expression error in '%s': %s at offset %d", cmdline_regex, rerror, roffset);
1111 /* prepare scanner call; despite the naming, file_name holds a directory
1112 name which is documented as the value given to %s. */
1113 if (Ustrlen(eml_filename) > sizeof(file_name) - 1)
1115 log_write(0, LOG_MAIN|LOG_PANIC,
1116 "malware filename does not fit in buffer [malware_internal() cmdline]");
1119 Ustrcpy(file_name, eml_filename);
1120 p = Ustrrchr(file_name, '/');
1123 fits = string_format(commandline, sizeof(commandline), CS cmdline_scanner, file_name);
1126 log_write(0, LOG_MAIN|LOG_PANIC,
1127 "cmdline scanner command-line does not fit in buffer");
1131 /* redirect STDERR too */
1132 if (Ustrlen(commandline) + 5 > sizeof(commandline))
1134 log_write(0, LOG_MAIN|LOG_PANIC,
1135 "cmdline scanner command-line does not fit in buffer (STDERR redirect)");
1138 Ustrcat(commandline," 2>&1");
1140 DEBUG(D_acl) debug_printf("Malware scan: issuing %s scan [%s]\n", scanner_name, commandline);
1142 /* store exims signal handlers */
1143 eximsigchld = signal(SIGCHLD,SIG_DFL);
1144 eximsigpipe = signal(SIGPIPE,SIG_DFL);
1146 scanner_out = popen(CS commandline,"r");
1147 if (scanner_out == NULL) {
1148 log_write(0, LOG_MAIN|LOG_PANIC,
1149 "malware acl condition: calling cmdline scanner (%s) failed: %s.", commandline, strerror(errno));
1150 signal(SIGCHLD,eximsigchld);
1151 signal(SIGPIPE,eximsigpipe);
1155 (void)string_format(file_name,1024,"%s/scan/%s/%s_scanner_output", spool_directory, message_id, message_id);
1156 scanner_record = modefopen(file_name,"wb",SPOOL_MODE);
1158 if (scanner_record == NULL) {
1159 log_write(0, LOG_MAIN|LOG_PANIC,
1160 "malware acl condition: opening scanner output file (%s) failed: %s.", file_name, strerror(errno));
1161 pclose(scanner_out);
1162 signal(SIGCHLD,eximsigchld);
1163 signal(SIGPIPE,eximsigpipe);
1167 /* look for trigger while recording output */
1168 while(fgets(CS linebuffer,32767,scanner_out) != NULL) {
1169 if ( Ustrlen(linebuffer) > fwrite(linebuffer, 1, Ustrlen(linebuffer), scanner_record) ) {
1171 log_write(0, LOG_MAIN|LOG_PANIC,
1172 "malware acl condition: short write on scanner output file (%s).", file_name);
1173 pclose(scanner_out);
1174 signal(SIGCHLD,eximsigchld);
1175 signal(SIGPIPE,eximsigpipe);
1178 /* try trigger match */
1179 if (!trigger && regex_match_and_setup(cmdline_trigger_re, linebuffer, 0, -1))
1183 (void)fclose(scanner_record);
1184 pclose(scanner_out);
1185 signal(SIGCHLD,eximsigchld);
1186 signal(SIGPIPE,eximsigpipe);
1189 /* setup default virus name */
1190 Ustrcpy(malware_name_buffer,"unknown");
1191 malware_name = malware_name_buffer;
1193 /* re-open the scanner output file, look for name match */
1194 scanner_record = fopen(CS file_name,"rb");
1195 while(fgets(CS linebuffer,32767,scanner_record) != NULL) {
1197 result = pcre_exec(cmdline_regex_re, NULL, CS linebuffer, Ustrlen(linebuffer), 0, 0, ovector, 30);
1199 pcre_copy_substring(CS linebuffer, ovector, result, 1, CS malware_name_buffer, 255);
1202 (void)fclose(scanner_record);
1205 /* no virus found */
1206 malware_name = NULL;
1209 /* ----------------------------------------------------------------------- */
1212 /* "sophie" scanner type ------------------------------------------------- */
1213 else if (strcmpic(scanner_name,US"sophie") == 0) {
1214 uschar *sophie_options;
1215 uschar sophie_options_buffer[1024];
1216 uschar sophie_options_default[] = "/var/run/sophie";
1218 struct sockaddr_un server;
1221 uschar file_name[1024];
1222 uschar av_buffer[1024];
1224 if ((sophie_options = string_nextinlist(&av_scanner_work, &sep,
1225 sophie_options_buffer,
1226 sizeof(sophie_options_buffer))) == NULL) {
1227 /* no options supplied, use default options */
1228 sophie_options = sophie_options_default;
1231 /* open the sophie socket */
1232 sock = socket(AF_UNIX, SOCK_STREAM, 0);
1234 log_write(0, LOG_MAIN|LOG_PANIC,
1235 "malware acl condition: can't open UNIX socket.");
1238 server.sun_family = AF_UNIX;
1239 Ustrcpy(server.sun_path, sophie_options);
1240 if (connect(sock, (struct sockaddr *) &server, sizeof(struct sockaddr_un)) < 0) {
1242 log_write(0, LOG_MAIN|LOG_PANIC,
1243 "malware acl condition: unable to connect to sophie UNIX socket (%s). errno=%d", sophie_options, errno);
1247 /* pass the scan directory to sophie */
1248 len = Ustrlen(eml_filename) + 1;
1249 if (len > sizeof(file_name))
1252 log_write(0, LOG_MAIN|LOG_PANIC,
1253 "malware filename does not fit in buffer [malware_internal() sophie]");
1256 memcpy(file_name, eml_filename, len);
1257 p = Ustrrchr(file_name, '/');
1261 DEBUG(D_acl) debug_printf("Malware scan: issuing %s scan [%s]\n",
1262 scanner_name, sophie_options);
1264 if ( write(sock, file_name, Ustrlen(file_name)) < 0
1265 || write(sock, "\n", 1) != 1
1268 log_write(0, LOG_MAIN|LOG_PANIC,
1269 "malware acl condition: unable to write to sophie UNIX socket (%s)", sophie_options);
1273 /* wait for result */
1274 memset(av_buffer, 0, sizeof(av_buffer));
1275 if ((!(bread = ip_recv(sock, av_buffer, sizeof(av_buffer), MALWARE_TIMEOUT)) > 0)) {
1277 log_write(0, LOG_MAIN|LOG_PANIC,
1278 "malware acl condition: unable to read from sophie UNIX socket (%s)", sophie_options);
1285 if (av_buffer[0] == '1') {
1286 if (Ustrchr(av_buffer, '\n')) *Ustrchr(av_buffer, '\n') = '\0';
1287 Ustrcpy(malware_name_buffer,&av_buffer[2]);
1288 malware_name = malware_name_buffer;
1290 else if (!strncmp(CS av_buffer, "-1", 2)) {
1291 log_write(0, LOG_MAIN|LOG_PANIC,
1292 "malware acl condition: malware acl condition: sophie reported error");
1296 /* all ok, no virus */
1297 malware_name = NULL;
1300 /* ----------------------------------------------------------------------- */
1303 /* "clamd" scanner type ------------------------------------------------- */
1304 /* This code was originally contributed by David Saez */
1305 /* There are three scanning methods available to us:
1306 * (1) Use the SCAN command, pointing to a file in the filesystem
1307 * (2) Use the STREAM command, send the data on a separate port
1308 * (3) Use the zINSTREAM command, send the data inline
1309 * The zINSTREAM command was introduced with ClamAV 0.95, which marked
1310 * STREAM deprecated; see: http://wiki.clamav.net/bin/view/Main/UpgradeNotes095
1311 * In Exim, we use SCAN if using a Unix-domain socket or explicitly told that
1312 * the TCP-connected daemon is actually local; otherwise we use zINSTREAM unless
1313 * WITH_OLD_CLAMAV_STREAM is defined.
1314 * See Exim bug 926 for details. */
1315 else if (strcmpic(scanner_name,US"clamd") == 0) {
1316 uschar *clamd_options = NULL;
1317 uschar clamd_options_buffer[1024];
1318 uschar clamd_options_default[] = "/tmp/clamd";
1319 uschar *p, *vname, *result_tag, *response_end;
1320 struct sockaddr_un server;
1323 uschar file_name[1024];
1324 uschar av_buffer[1024];
1325 uschar *hostname = "";
1328 uschar *clamav_fbuf;
1329 int clam_fd, result;
1331 BOOL use_scan_command = FALSE, fits;
1332 clamd_address_container * clamd_address_vector[MAX_CLAMD_SERVERS];
1334 int num_servers = 0;
1335 #ifdef WITH_OLD_CLAMAV_STREAM
1336 uschar av_buffer2[1024];
1339 uint32_t send_size, send_final_zeroblock;
1342 if ((clamd_options = string_nextinlist(&av_scanner_work, &sep,
1343 clamd_options_buffer,
1344 sizeof(clamd_options_buffer))) == NULL) {
1345 /* no options supplied, use default options */
1346 clamd_options = clamd_options_default;
1349 if (*clamd_options == '/')
1350 /* Local file; so we def want to use_scan_command and don't want to try
1351 * passing IP/port combinations */
1352 use_scan_command = TRUE;
1354 uschar *address = clamd_options;
1355 uschar address_buffer[MAX_CLAMD_ADDRESS_LENGTH + 20];
1357 /* Go through the rest of the list of host/port and construct an array
1358 * of servers to try. The first one is the bit we just passed from
1359 * clamd_options so process that first and then scan the remainder of
1360 * the address buffer */
1362 clamd_address_container *this_clamd;
1364 /* The 'local' option means use the SCAN command over the network
1365 * socket (ie common file storage in use) */
1366 if (strcmpic(address,US"local") == 0) {
1367 use_scan_command = TRUE;
1371 /* XXX: If unsuccessful we should free this memory */
1373 (clamd_address_container *)store_get(sizeof(clamd_address_container));
1375 /* extract host and port part */
1376 if( sscanf(CS address, "%" MAX_CLAMD_ADDRESS_LENGTH_S "s %u", this_clamd->tcp_addr,
1377 &(this_clamd->tcp_port)) != 2 ) {
1378 log_write(0, LOG_MAIN|LOG_PANIC,
1379 "malware acl condition: clamd: invalid address '%s'", address);
1383 clamd_address_vector[num_servers] = this_clamd;
1385 if (num_servers >= MAX_CLAMD_SERVERS) {
1386 log_write(0, LOG_MAIN|LOG_PANIC,
1387 "More than " MAX_CLAMD_SERVERS_S " clamd servers specified; "
1388 "only using the first " MAX_CLAMD_SERVERS_S );
1391 } while ((address = string_nextinlist(&av_scanner_work, &sep,
1393 sizeof(address_buffer))) != NULL);
1395 /* check if we have at least one server */
1397 log_write(0, LOG_MAIN|LOG_PANIC,
1398 "malware acl condition: clamd: no useable clamd server addresses in malware configuration option.");
1403 /* See the discussion of response formats below to see why we really don't
1404 like colons in filenames when passing filenames to ClamAV. */
1405 if (use_scan_command && Ustrchr(eml_filename, ':')) {
1406 log_write(0, LOG_MAIN|LOG_PANIC,
1407 "malware acl condition: clamd: local/SCAN mode incompatible with" \
1408 " : in path to email filename [%s]", eml_filename);
1412 /* We have some network servers specified */
1415 /* Confirmed in ClamAV source (0.95.3) that the TCPAddr option of clamd
1416 * only supports AF_INET, but we should probably be looking to the
1417 * future and rewriting this to be protocol-independent anyway. */
1419 while ( num_servers > 0 ) {
1420 /* Randomly pick a server to start with */
1421 current_server = random_number( num_servers );
1423 debug_printf("trying server name %s, port %u\n",
1424 clamd_address_vector[current_server]->tcp_addr,
1425 clamd_address_vector[current_server]->tcp_port);
1427 /* Lookup the host. This is to ensure that we connect to the same IP
1428 * on both connections (as one host could resolve to multiple ips) */
1429 if((he = gethostbyname(CS clamd_address_vector[current_server]->tcp_addr))
1431 log_write(0, LOG_MAIN|LOG_PANIC,
1432 "malware acl condition: clamd: failed to lookup host '%s'",
1433 clamd_address_vector[current_server]->tcp_addr
1435 goto try_next_server;
1438 in = *(struct in_addr *) he->h_addr_list[0];
1440 /* Open the ClamAV Socket */
1441 if ( (sock = ip_socket(SOCK_STREAM, AF_INET)) < 0) {
1442 log_write(0, LOG_MAIN|LOG_PANIC,
1443 "malware acl condition: clamd: unable to acquire socket (%s)",
1445 goto try_next_server;
1448 if (ip_connect( sock,
1450 (uschar*)inet_ntoa(in),
1451 clamd_address_vector[current_server]->tcp_port,
1453 /* Connection successfully established with a server */
1454 hostname = clamd_address_vector[current_server]->tcp_addr;
1457 log_write(0, LOG_MAIN|LOG_PANIC,
1458 "malware acl condition: clamd: connection to %s, port %u failed (%s)",
1459 clamd_address_vector[current_server]->tcp_addr,
1460 clamd_address_vector[current_server]->tcp_port,
1467 /* Remove the server from the list. XXX We should free the memory */
1470 for( i = current_server; i < num_servers; i++ )
1471 clamd_address_vector[i] = clamd_address_vector[i+1];
1474 if ( num_servers == 0 ) {
1475 log_write(0, LOG_MAIN|LOG_PANIC, "malware acl condition: all clamd servers failed");
1479 /* open the local socket */
1480 if ((sock = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
1481 log_write(0, LOG_MAIN|LOG_PANIC,
1482 "malware acl condition: clamd: unable to acquire socket (%s)",
1487 server.sun_family = AF_UNIX;
1488 Ustrcpy(server.sun_path, clamd_options);
1490 if (connect(sock, (struct sockaddr *) &server, sizeof(struct sockaddr_un)) < 0) {
1492 log_write(0, LOG_MAIN|LOG_PANIC,
1493 "malware acl condition: clamd: unable to connect to UNIX socket %s (%s)",
1494 clamd_options, strerror(errno) );
1499 /* have socket in variable "sock"; command to use is semi-independent of
1500 * the socket protocol. We use SCAN if is local (either Unix/local
1501 * domain socket, or explicitly told local) else we stream the data.
1502 * How we stream the data depends upon how we were built. */
1504 if (!use_scan_command) {
1506 #ifdef WITH_OLD_CLAMAV_STREAM
1507 /* "STREAM\n" command, get back a "PORT <N>\n" response, send data to
1508 * that port on a second connection; then in the scan-method-neutral
1509 * part, read the response back on the original connection. */
1511 DEBUG(D_acl) debug_printf("Malware scan: issuing %s old-style remote scan (PORT)\n",
1514 /* Pass the string to ClamAV (7 = "STREAM\n") */
1515 if (send(sock, "STREAM\n", 7, 0) < 0) {
1516 log_write(0, LOG_MAIN|LOG_PANIC,"malware acl condition: clamd: unable to write to socket (%s)",
1521 memset(av_buffer2, 0, sizeof(av_buffer2));
1522 bread = ip_recv(sock, av_buffer2, sizeof(av_buffer2), MALWARE_TIMEOUT);
1525 log_write(0, LOG_MAIN|LOG_PANIC,
1526 "malware acl condition: clamd: unable to read PORT from socket (%s)",
1532 if (bread == sizeof(av_buffer)) {
1533 log_write(0, LOG_MAIN|LOG_PANIC,
1534 "malware acl condition: clamd: buffer too small");
1539 if (!(*av_buffer2)) {
1540 log_write(0, LOG_MAIN|LOG_PANIC,
1541 "malware acl condition: clamd: ClamAV returned null");
1546 av_buffer2[bread] = '\0';
1547 if( sscanf(CS av_buffer2, "PORT %u\n", &port) != 1 ) {
1548 log_write(0, LOG_MAIN|LOG_PANIC,
1549 "malware acl condition: clamd: Expected port information from clamd, got '%s'", av_buffer2);
1554 if ( (sockData = ip_socket(SOCK_STREAM, AF_INET)) < 0) {
1555 log_write(0, LOG_MAIN|LOG_PANIC,
1556 "malware acl condition: clamd: unable to acquire socket (%s)",
1562 if (ip_connect(sockData, AF_INET, (uschar*)inet_ntoa(in), port, 5) < 0) {
1563 log_write(0, LOG_MAIN|LOG_PANIC,
1564 "malware acl condition: clamd: connection to %s, port %u failed (%s)",
1565 inet_ntoa(in), port, strerror(errno));
1566 (void)close(sockData); (void)close(sock);
1570 #define CLOSE_SOCKDATA (void)close(sockData)
1571 #else /* WITH_OLD_CLAMAV_STREAM not defined */
1572 /* New protocol: "zINSTREAM\n" followed by a sequence of <length><data>
1573 chunks, <n> a 4-byte number (network order), terminated by a zero-length
1576 DEBUG(D_acl) debug_printf("Malware scan: issuing %s new-style remote scan (zINSTREAM)\n",
1579 /* Pass the string to ClamAV (10 = "zINSTREAM\0") */
1580 if (send(sock, "zINSTREAM", 10, 0) < 0) {
1581 log_write(0, LOG_MAIN|LOG_PANIC,
1582 "malware acl condition: clamd: unable to send zINSTREAM to socket (%s)",
1588 #define CLOSE_SOCKDATA /**/
1591 /* calc file size */
1592 clam_fd = open(CS eml_filename, O_RDONLY);
1593 if (clam_fd == -1) {
1594 log_write(0, LOG_MAIN|LOG_PANIC,
1595 "malware acl condition: clamd: can't open spool file %s: %s",
1596 eml_filename, strerror(errno));
1597 CLOSE_SOCKDATA; (void)close(sock);
1600 fsize = lseek(clam_fd, 0, SEEK_END);
1602 log_write(0, LOG_MAIN|LOG_PANIC,
1603 "malware acl condition: clamd: can't seek spool file %s: %s",
1604 eml_filename, strerror(errno));
1605 CLOSE_SOCKDATA; (void)close(sock);
1608 lseek(clam_fd, 0, SEEK_SET);
1610 clamav_fbuf = (uschar *) malloc (fsize);
1612 log_write(0, LOG_MAIN|LOG_PANIC,
1613 "malware acl condition: clamd: unable to allocate memory %u for file (%s)",
1614 fsize, eml_filename);
1615 CLOSE_SOCKDATA; (void)close(sock); (void)close(clam_fd);
1619 result = read (clam_fd, clamav_fbuf, fsize);
1621 log_write(0, LOG_MAIN|LOG_PANIC,
1622 "malware acl condition: clamd: can't read spool file %s: %s",
1623 eml_filename, strerror(errno));
1624 CLOSE_SOCKDATA; (void)close(sock); (void)close(clam_fd);
1628 (void)close(clam_fd);
1630 /* send file body to socket */
1631 #ifdef WITH_OLD_CLAMAV_STREAM
1632 if (send(sockData, clamav_fbuf, fsize, 0) < 0) {
1633 log_write(0, LOG_MAIN|LOG_PANIC,
1634 "malware acl condition: clamd: unable to send file body to socket (%s:%u)", hostname, port);
1635 CLOSE_SOCKDATA; (void)close(sock);
1640 send_size = htonl(fsize);
1641 send_final_zeroblock = 0;
1642 if ((send(sock, &send_size, sizeof(send_size), 0) < 0) ||
1643 (send(sock, clamav_fbuf, fsize, 0) < 0) ||
1644 (send(sock, &send_final_zeroblock, sizeof(send_final_zeroblock), 0) < 0))
1646 log_write(0, LOG_MAIN|LOG_PANIC,
1647 "malware acl condition: clamd: unable to send file body to socket (%s:%u)", hostname, port);
1657 #undef CLOSE_SOCKDATA
1659 } else { /* use scan command */
1660 /* Send a SCAN command pointing to a filename; then in the then in the
1661 * scan-method-neutral part, read the response back */
1663 /* ================================================================= */
1665 /* Prior to the reworking post-Exim-4.72, this scanned a directory,
1666 which dates to when ClamAV needed us to break apart the email into the
1667 MIME parts (eg, with the now deprecated demime condition coming first).
1668 Some time back, ClamAV gained the ability to deconstruct the emails, so
1669 doing this would actually have resulted in the mail attachments being
1670 scanned twice, in the broken out files and from the original .eml.
1671 Since ClamAV now handles emails (and has for quite some time) we can
1672 just use the email file itself. */
1673 /* Pass the string to ClamAV (7 = "SCAN \n" + \0) */
1674 fits = string_format(file_name, sizeof(file_name), "SCAN %s\n",
1678 log_write(0, LOG_MAIN|LOG_PANIC,
1679 "malware filename does not fit in buffer [malware_internal() clamd]");
1682 DEBUG(D_acl) debug_printf("Malware scan: issuing %s local-path scan [%s]\n",
1683 scanner_name, clamd_options);
1685 if (send(sock, file_name, Ustrlen(file_name), 0) < 0) {
1687 log_write(0, LOG_MAIN|LOG_PANIC,"malware acl condition: clamd: unable to write to socket (%s)",
1692 /* Do not shut down the socket for writing; a user report noted that
1693 * clamd 0.70 does not react well to this. */
1695 /* Commands have been sent, no matter which scan method or connection
1696 * type we're using; now just read the result, independent of method. */
1698 /* Read the result */
1699 memset(av_buffer, 0, sizeof(av_buffer));
1700 bread = ip_recv(sock, av_buffer, sizeof(av_buffer), MALWARE_TIMEOUT);
1704 log_write(0, LOG_MAIN|LOG_PANIC,
1705 "malware acl condition: clamd: unable to read from socket (%s)",
1710 if (bread == sizeof(av_buffer)) {
1711 log_write(0, LOG_MAIN|LOG_PANIC,
1712 "malware acl condition: clamd: buffer too small");
1716 /* Check the result. ClamAV returns one of two result formats.
1717 In the basic mode, the response is of the form:
1718 infected: -> "<filename>: <virusname> FOUND"
1719 not-infected: -> "<filename>: OK"
1720 error: -> "<filename>: <errcode> ERROR
1721 If the ExtendedDetectionInfo option has been turned on, then we get:
1722 "<filename>: <virusname>(<virushash>:<virussize>) FOUND"
1723 for the infected case. Compare:
1724 /tmp/eicar.com: Eicar-Test-Signature FOUND
1725 /tmp/eicar.com: Eicar-Test-Signature(44d88612fea8a8f36de82e1278abb02f:68) FOUND
1727 In the streaming case, clamd uses the filename "stream" which you should
1728 be able to verify with { ktrace clamdscan --stream /tmp/eicar.com }. (The
1729 client app will replace "stream" with the original filename before returning
1730 results to stdout, but the trace shows the data).
1732 We will assume that the pathname passed to clamd from Exim does not contain
1733 a colon. We will have whined loudly above if the eml_filename does (and we're
1734 passing a filename to clamd). */
1736 if (!(*av_buffer)) {
1737 log_write(0, LOG_MAIN|LOG_PANIC,
1738 "malware acl condition: clamd: ClamAV returned null");
1742 /* strip newline at the end (won't be present for zINSTREAM)
1743 (also any trailing whitespace, which shouldn't exist, but we depend upon
1744 this below, so double-check) */
1745 p = av_buffer + Ustrlen(av_buffer) - 1;
1746 if (*p == '\n') *p = '\0';
1748 DEBUG(D_acl) debug_printf("Malware response: %s\n", av_buffer);
1750 while (isspace(*--p) && (p > av_buffer))
1755 /* colon in returned output? */
1756 if((p = Ustrchr(av_buffer,':')) == NULL) {
1757 log_write(0, LOG_MAIN|LOG_PANIC,
1758 "malware acl condition: clamd: ClamAV returned malformed result (missing colon): %s",
1763 /* strip filename */
1764 while (*p && isspace(*++p)) /**/;
1767 /* It would be bad to encounter a virus with "FOUND" in part of the name,
1768 but we should at least be resistant to it. */
1769 p = Ustrrchr(vname, ' ');
1775 if (Ustrcmp(result_tag, "FOUND") == 0) {
1776 /* p should still be the whitespace before the result_tag */
1777 while (isspace(*p)) --p;
1779 /* Strip off the extended information too, which will be in parens
1780 after the virus name, with no intervening whitespace. */
1782 /* "(hash:size)", so previous '(' will do; if not found, we have
1783 a curious virus name, but not an error. */
1784 p = Ustrrchr(vname, '(');
1788 Ustrncpy(malware_name_buffer, vname, sizeof(malware_name_buffer)-1);
1789 malware_name = malware_name_buffer;
1790 DEBUG(D_acl) debug_printf("Malware found, name \"%s\"\n", malware_name);
1792 } else if (Ustrcmp(result_tag, "ERROR") == 0) {
1793 log_write(0, LOG_MAIN|LOG_PANIC,
1794 "malware acl condition: clamd: ClamAV returned: %s",
1798 } else if (Ustrcmp(result_tag, "OK") == 0) {
1799 /* Everything should be OK */
1800 malware_name = NULL;
1801 DEBUG(D_acl) debug_printf("Malware not found\n");
1804 log_write(0, LOG_MAIN|LOG_PANIC,
1805 "malware acl condition: clamd: unparseable response from ClamAV: {%s}",
1812 /* ----------------------------------------------------------------------- */
1815 /* "mksd" scanner type --------------------------------------------------- */
1816 else if (strcmpic(scanner_name,US"mksd") == 0) {
1817 uschar *mksd_options;
1818 char *mksd_options_end;
1819 uschar mksd_options_buffer[32];
1820 int mksd_maxproc = 1; /* default, if no option supplied */
1821 struct sockaddr_un server;
1825 if ((mksd_options = string_nextinlist(&av_scanner_work, &sep,
1826 mksd_options_buffer,
1827 sizeof(mksd_options_buffer))) != NULL) {
1828 mksd_maxproc = (int) strtol(CS mksd_options, &mksd_options_end, 10);
1829 if ((*mksd_options == '\0') || (*mksd_options_end != '\0') ||
1830 (mksd_maxproc < 1) || (mksd_maxproc > 32)) {
1831 log_write(0, LOG_MAIN|LOG_PANIC,
1832 "malware acl condition: mksd: invalid option '%s'", mksd_options);
1837 /* open the mksd socket */
1838 sock = socket(AF_UNIX, SOCK_STREAM, 0);
1840 log_write(0, LOG_MAIN|LOG_PANIC,
1841 "malware acl condition: can't open UNIX socket.");
1844 server.sun_family = AF_UNIX;
1845 Ustrcpy(server.sun_path, "/var/run/mksd/socket");
1846 if (connect(sock, (struct sockaddr *) &server, sizeof(struct sockaddr_un)) < 0) {
1848 log_write(0, LOG_MAIN|LOG_PANIC,
1849 "malware acl condition: unable to connect to mksd UNIX socket (/var/run/mksd/socket). errno=%d", errno);
1853 malware_name = NULL;
1855 DEBUG(D_acl) debug_printf("Malware scan: issuing %s scan\n", scanner_name);
1857 retval = mksd_scan_packed(sock, eml_filename);
1862 /* ----------------------------------------------------------------------- */
1864 /* "unknown" scanner type ------------------------------------------------- */
1866 log_write(0, LOG_MAIN|LOG_PANIC,
1867 "malware condition: unknown scanner type '%s'", scanner_name);
1870 /* ----------------------------------------------------------------------- */
1872 /* set "been here, done that" marker */
1876 /* match virus name against pattern (caseless ------->----------v) */
1877 if ( (malware_name != NULL) &&
1878 (regex_match_and_setup(re, malware_name, 0, -1)) ) {
1879 DEBUG(D_acl) debug_printf("Matched regex to malware [%s] [%s]\n", malware_regex, malware_name);
1888 /* simple wrapper for reading lines from sockets */
1889 int recv_line(int sock, uschar *buffer, int size) {
1892 memset(buffer,0,size);
1894 while(recv(sock,p,1,0) > -1) {
1895 if ((p-buffer) > (size-2)) break;
1896 if (*p == '\n') break;
1897 if (*p != '\r') p++;
1905 /* ============= private routines for the "mksd" scanner type ============== */
1907 #include <sys/uio.h>
1909 static int mksd_writev (int sock, struct iovec *iov, int iovcnt)
1915 i = writev (sock, iov, iovcnt);
1916 while ((i < 0) && (errno == EINTR));
1919 log_write(0, LOG_MAIN|LOG_PANIC,
1920 "malware acl condition: unable to write to mksd UNIX socket (/var/run/mksd/socket)");
1925 if (i >= iov->iov_len) {
1932 iov->iov_base = CS iov->iov_base + i;
1938 static int mksd_read_lines (int sock, uschar *av_buffer, int av_buffer_size)
1944 if ((i = recv (sock, av_buffer+offset, av_buffer_size-offset, 0)) <= 0) {
1946 log_write(0, LOG_MAIN|LOG_PANIC,
1947 "malware acl condition: unable to read from mksd UNIX socket (/var/run/mksd/socket)");
1952 /* offset == av_buffer_size -> buffer full */
1953 if (offset == av_buffer_size) {
1955 log_write(0, LOG_MAIN|LOG_PANIC,
1956 "malware acl condition: malformed reply received from mksd");
1959 } while (av_buffer[offset-1] != '\n');
1961 av_buffer[offset] = '\0';
1965 static int mksd_parse_line (char *line)
1976 if ((p = strchr (line, '\n')) != NULL)
1978 log_write(0, LOG_MAIN|LOG_PANIC,
1979 "malware acl condition: mksd scanner failed: %s", line);
1983 if ((p = strchr (line, '\n')) != NULL) {
1985 if (((p-line) > 5) && ((p-line) < sizeof (malware_name_buffer)) && (line[3] == ' '))
1986 if (((p = strchr (line+4, ' ')) != NULL) && ((p-line) > 4)) {
1988 Ustrcpy (malware_name_buffer, line+4);
1989 malware_name = malware_name_buffer;
1993 log_write(0, LOG_MAIN|LOG_PANIC,
1994 "malware acl condition: malformed reply received from mksd: %s", line);
1999 static int mksd_scan_packed(int sock, uschar *scan_filename)
2001 struct iovec iov[3];
2002 const char *cmd = "MSQ\n";
2003 uschar av_buffer[1024];
2005 iov[0].iov_base = (void *) cmd;
2007 iov[1].iov_base = CS scan_filename;
2008 iov[1].iov_len = Ustrlen(scan_filename);
2009 iov[2].iov_base = (void *) (cmd + 3);
2012 if (mksd_writev (sock, iov, 3) < 0)
2015 if (mksd_read_lines (sock, av_buffer, sizeof (av_buffer)) < 0)
2020 return mksd_parse_line (CS av_buffer);