1 /*************************************************
2 * Exim - an Internet mail transport agent *
3 *************************************************/
5 /* Copyright (c) Tom Kistner <tom@duncanthrax.net> 2003-2014 */
8 /* Code for calling virus (malware) scanners. Called from acl.c. */
11 #ifdef WITH_CONTENT_SCAN
13 typedef enum {M_FPROTD, M_DRWEB, M_AVES, M_FSEC, M_KAVD, M_CMDL,
14 M_SOPHIE, M_CLAMD, M_SOCK, M_MKSD} scanner_t;
15 typedef enum {MC_NONE, MC_TCP, MC_UNIX, MC_STRM} contype_t;
20 const uschar * options_default;
24 { M_FPROTD, US"f-protd", US"localhost 10200-10204", MC_TCP },
25 { M_DRWEB, US"drweb", US"/usr/local/drweb/run/drwebd.sock", MC_STRM },
26 { M_AVES, US"aveserver", US"/var/run/aveserver", MC_UNIX },
27 { M_FSEC, US"fsecure", US"/var/run/.fsav", MC_UNIX },
28 { M_KAVD, US"kavdaemon", US"/var/run/AvpCtl", MC_UNIX },
29 { M_CMDL, US"cmdline", NULL, MC_NONE },
30 { M_SOPHIE, US"sophie", US"/var/run/sophie", MC_UNIX },
31 { M_CLAMD, US"clamd", US"/tmp/clamd", MC_NONE },
32 { M_SOCK, US"sock", US"/tmp/malware.sock", MC_STRM },
33 { M_MKSD, US"mksd", NULL, MC_NONE },
34 { -1, NULL, NULL, MC_NONE } /* end-marker */
37 /* The maximum number of clamd servers that are supported in the configuration */
38 #define MAX_CLAMD_SERVERS 32
39 #define MAX_CLAMD_SERVERS_S "32"
40 /* Maximum length of the hostname that can be specified in the clamd address list */
41 #define MAX_CLAMD_ADDRESS_LENGTH 64
42 #define MAX_CLAMD_ADDRESS_LENGTH_S "64"
44 typedef struct clamd_address_container {
45 uschar tcp_addr[MAX_CLAMD_ADDRESS_LENGTH+1];
46 unsigned int tcp_port;
47 } clamd_address_container;
49 /* declaration of private routines */
50 static int mksd_scan_packed(struct scan * scanent, int sock, uschar *scan_filename);
51 static int malware_internal(uschar **listptr, uschar *eml_filename, BOOL faking);
54 # define nelements(arr) (sizeof(arr) / sizeof(arr[0]))
58 #define MALWARE_TIMEOUT 120
61 #define DRWEBD_SCAN_CMD (1) /* scan file, buffer or diskfile */
62 #define DRWEBD_RETURN_VIRUSES (1<<0) /* ask daemon return to us viruses names from report */
63 #define DRWEBD_IS_MAIL (1<<19) /* say to daemon that format is "archive MAIL" */
65 #define DERR_READ_ERR (1<<0) /* read error */
66 #define DERR_NOMEMORY (1<<2) /* no memory */
67 #define DERR_TIMEOUT (1<<9) /* scan timeout has run out */
68 #define DERR_BAD_CALL (1<<15) /* wrong command */
70 /* Routine to check whether a system is big- or little-endian.
71 Ripped from http://www.faqs.org/faqs/graphics/fileformats-faq/part4/section-7.html
72 Needed for proper kavdaemon implementation. Sigh. */
73 #define BIG_MY_ENDIAN 0
74 #define LITTLE_MY_ENDIAN 1
75 static int test_byte_order(void);
79 short int word = 0x0001;
80 char *byte = (char *) &word;
81 return(byte[0] ? LITTLE_MY_ENDIAN : BIG_MY_ENDIAN);
84 BOOL malware_ok = FALSE;
86 /* Gross hacks for the -bmalware option; perhaps we should just create
87 the scan directory normally for that case, but look into rigging up the
88 needed header variables if not already set on the command-line? */
89 extern int spool_mbox_ok;
90 extern uschar spooled_message_id[17];
92 /*************************************************
93 * Scan an email for malware *
94 *************************************************/
96 /* This is the normal interface for scanning an email, which doesn't need a
97 filename; it's a wrapper around the malware_file function.
100 listptr the list of options to the "malware = ..." ACL condition
102 Returns: Exim message processing code (OK, FAIL, DEFER, ...)
103 where true means malware was found (condition applies)
106 malware(uschar **listptr)
108 uschar * scan_filename;
111 scan_filename = string_sprintf("%s/scan/%s/%s.eml",
112 spool_directory, message_id, message_id);
113 ret = malware_internal(listptr, scan_filename, FALSE);
114 if (ret == DEFER) av_failed = TRUE;
120 /*************************************************
121 * Scan a file for malware *
122 *************************************************/
124 /* This is a test wrapper for scanning an email, which is not used in
125 normal processing. Scan any file, using the Exim scanning interface.
126 This function tampers with various global variables so is unsafe to use
127 in any other context.
130 eml_filename a file holding the message to be scanned
132 Returns: Exim message processing code (OK, FAIL, DEFER, ...)
133 where true means malware was found (condition applies)
136 malware_in_file(uschar *eml_filename)
138 uschar *scan_options[2];
139 uschar message_id_buf[64];
142 scan_options[0] = US"*";
143 scan_options[1] = NULL;
145 /* spool_mbox() assumes various parameters exist, when creating
146 the relevant directory and the email within */
147 (void) string_format(message_id_buf, sizeof(message_id_buf),
148 "dummy-%d", vaguely_random_number(INT_MAX));
149 message_id = message_id_buf;
150 sender_address = US"malware-sender@example.net";
152 recipients_list = NULL;
153 receive_add_recipient(US"malware-victim@example.net", -1);
154 enable_dollar_recipients = TRUE;
156 ret = malware_internal(scan_options, eml_filename, TRUE);
158 Ustrncpy(spooled_message_id, message_id, sizeof(spooled_message_id));
160 /* don't set no_mbox_unspool; at present, there's no way for it to become
161 set, but if that changes, then it should apply to these tests too */
164 /* silence static analysis tools */
172 malware_errlog_defer(const uschar * str)
174 log_write(0, LOG_MAIN|LOG_PANIC, "malware acl condition: %s", str);
179 m_errlog_defer(struct scan * scanent, const uschar * str)
181 return malware_errlog_defer(string_sprintf("%s: %s", scanent->name, str));
184 m_errlog_defer_3(struct scan * scanent, const uschar * str,
187 (void) close(fd_to_close);
188 return m_errlog_defer(scanent, str);
191 /*************************************************/
193 /* Only used by the Clamav code, which is working from a list of servers and
194 uses the returned in_addr to get a second connection to the same system.
197 m_tcpsocket(const uschar * hostname, unsigned int port,
198 host_item * host, uschar ** errstr)
200 return ip_connectedsocket(SOCK_STREAM, hostname, port, port, 5, host, errstr);
204 m_tcpsocket_fromdef(const uschar * hostport, uschar ** errstr)
207 uschar hostname[256];
208 unsigned int portlow, porthigh;
210 /* extract host and port part */
211 scan = sscanf(CS hostport, "%255s %u-%u", hostname, &portlow, &porthigh);
214 *errstr = string_sprintf("invalid socket '%s'", hostport);
220 return ip_connectedsocket(SOCK_STREAM, hostname, portlow, porthigh,
225 m_unixsocket(const uschar * path, uschar ** errstr)
228 struct sockaddr_un server;
230 if ((sock = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
231 *errstr = US"can't open UNIX socket.";
235 server.sun_family = AF_UNIX;
236 Ustrncpy(server.sun_path, path, sizeof(server.sun_path)-1);
237 server.sun_path[sizeof(server.sun_path)-1] = '\0';
238 if (connect(sock, (struct sockaddr *) &server, sizeof(server)) < 0) {
241 *errstr = string_sprintf("unable to connect to UNIX socket (%s): %s",
242 path, strerror(err));
249 m_streamsocket(const uschar * spec, uschar ** errstr)
252 ? m_unixsocket(spec, errstr) : m_tcpsocket_fromdef(spec, errstr);
256 m_sock_send(int sock, uschar * buf, int cnt, uschar ** errstr)
258 if (send(sock, buf, cnt, 0) < 0) {
261 *errstr = string_sprintf("unable to send to socket (%s): %s",
269 m_pcre_compile(const uschar * re, uschar ** errstr)
271 const uschar * rerror;
275 cre = pcre_compile(CS re, PCRE_COPT, (const char **)&rerror, &roffset, NULL);
277 *errstr= string_sprintf("regular expression error in '%s': %s at offset %d",
278 re, rerror, roffset);
283 m_pcre_exec(const pcre * cre, uschar * text)
286 int i = pcre_exec(cre, NULL, CS text, Ustrlen(text), 0, 0,
287 ovector, nelements(ovector));
288 uschar * substr = NULL;
289 if (i >= 2) /* Got it */
290 pcre_get_substring(CS text, ovector, i, 1, (const char **) &substr);
295 m_pcre_nextinlist(uschar ** list, int * sep, char * listerr, uschar ** errstr)
297 const uschar * list_ele;
298 const pcre * cre = NULL;
300 if (!(list_ele = string_nextinlist(list, sep, NULL, 0)))
301 *errstr = US listerr;
303 cre = m_pcre_compile(CUS list_ele, errstr);
307 /*************************************************
308 * Scan content for malware *
309 *************************************************/
311 /* This is an internal interface for scanning an email; the normal interface
312 is via malware(), or there's malware_in_file() used for testing/debugging.
315 listptr the list of options to the "malware = ..." ACL condition
316 eml_filename the file holding the email to be scanned
317 faking whether or not we're faking this up for the -bmalware test
319 Returns: Exim message processing code (OK, FAIL, DEFER, ...)
320 where true means malware was found (condition applies)
323 malware_internal(uschar **listptr, uschar *eml_filename, BOOL faking)
326 uschar *list = *listptr;
327 uschar *av_scanner_work = av_scanner;
328 uschar *scanner_name;
329 uschar *malware_regex;
330 uschar malware_regex_default[] = ".+";
331 unsigned long mbox_size;
335 struct scan * scanent;
336 const uschar * scanner_options;
339 /* make sure the eml mbox file is spooled up */
340 if (!(mbox_file = spool_mbox(&mbox_size, faking ? eml_filename : NULL)))
341 return malware_errlog_defer(US"error while creating mbox spool file");
343 /* none of our current scanners need the mbox
344 file as a stream, so we can close it right away */
345 (void)fclose(mbox_file);
347 /* extract the malware regex to match against from the option list */
348 if (!(malware_regex = string_nextinlist(&list, &sep, NULL, 0)))
349 return FAIL; /* empty means "don't match anything" */
351 /* parse 1st option */
352 if ( (strcmpic(malware_regex,US"false") == 0) ||
353 (Ustrcmp(malware_regex,"0") == 0) )
354 return FAIL; /* explicitly no matching */
356 /* special cases (match anything except empty) */
357 if ( (strcmpic(malware_regex,US"true") == 0) ||
358 (Ustrcmp(malware_regex,"*") == 0) ||
359 (Ustrcmp(malware_regex,"1") == 0) )
360 malware_regex = malware_regex_default;
362 /* Reset sep that is set by previous string_nextinlist() call */
365 /* compile the regex, see if it works */
366 if (!(re = m_pcre_compile(malware_regex, &errstr)))
367 return malware_errlog_defer(errstr);
369 /* if av_scanner starts with a dollar, expand it first */
370 if (*av_scanner == '$') {
371 if (!(av_scanner_work = expand_string(av_scanner)))
372 return malware_errlog_defer(
373 string_sprintf("av_scanner starts with $, but expansion failed: %s",
374 expand_string_message));
376 debug_printf("Expanded av_scanner global: %s\n", av_scanner_work);
377 /* disable result caching in this case */
382 /* Do not scan twice (unless av_scanner is dynamic). */
385 /* find the scanner type from the av_scanner option */
386 if (!(scanner_name = string_nextinlist(&av_scanner_work, &sep, NULL, 0)))
387 return malware_errlog_defer(US"av_scanner configuration variable is empty");
389 for (scanent = m_scans; ; scanent++) {
391 return malware_errlog_defer(string_sprintf("unknown scanner type '%s'",
393 if (strcmpic(scanner_name, US scanent->name) != 0)
395 if (!(scanner_options = string_nextinlist(&av_scanner_work, &sep, NULL, 0)))
396 scanner_options = scanent->options_default;
397 if (scanent->conn == MC_NONE)
399 switch(scanent->conn)
401 case MC_TCP: sock = m_tcpsocket_fromdef(scanner_options, &errstr); break;
402 case MC_UNIX: sock = m_unixsocket(scanner_options, &errstr); break;
403 case MC_STRM: sock = m_streamsocket(scanner_options, &errstr); break;
404 default: /* compiler quietening */ break;
407 return m_errlog_defer(scanent, errstr);
410 DEBUG(D_lookup) debug_printf("Malware scan: %s\n", scanner_name);
412 switch (scanent->scancode) {
413 case M_FPROTD: /* "f-protd" scanner type -------------------------------- */
415 uschar *fp_scan_option;
416 unsigned int detected=0, par_count=0;
417 uschar * scanrequest;
418 uschar buf[32768], *strhelper, *strhelper2;
419 uschar * malware_name_internal = NULL;
421 DEBUG(D_acl) debug_printf("Malware scan: issuing %s GET\n", scanner_name);
422 scanrequest = string_sprintf("GET %s", eml_filename);
424 while ((fp_scan_option = string_nextinlist(&av_scanner_work, &sep,
426 scanrequest = string_sprintf("%s%s%s", scanrequest,
427 par_count ? "%20" : "?", fp_scan_option);
430 scanrequest = string_sprintf("%s HTTP/1.0\r\n\r\n", scanrequest);
432 /* send scan request */
433 if (m_sock_send(sock, scanrequest, Ustrlen(scanrequest)+1, &errstr) < 0)
434 return m_errlog_defer(scanent, errstr);
436 /* We get a lot of empty lines, so we need this hack to check for any data at all */
437 while( recv(sock, buf, 1, MSG_PEEK) > 0 ) {
438 if ( recv_line(sock, buf, sizeof(buf)) > 0) {
439 if ( Ustrstr(buf, US"<detected type=\"") != NULL )
441 else if ( detected && (strhelper = Ustrstr(buf, US"<name>")) ) {
442 if ((strhelper2 = Ustrstr(buf, US"</name>")) != NULL) {
444 malware_name_internal = string_copy(strhelper+6);
446 } else if ( Ustrstr(buf, US"<summary code=\"") )
447 malware_name = Ustrstr(buf, US"<summary code=\"11\">")
448 ? malware_name_internal : NULL;
454 case M_DRWEB: /* "drweb" scanner type ----------------------------------- */
455 /* v0.1 - added support for tcp sockets */
456 /* v0.0 - initial release -- support for unix sockets */
460 uschar * tmpbuf, *drweb_fbuf;
461 int drweb_rc, drweb_cmd, drweb_flags = 0x0000, drweb_fd,
462 drweb_vnum, drweb_slen, drweb_fin = 0x0000;
464 const pcre *drweb_re;
466 /* prepare variables */
467 drweb_cmd = htonl(DRWEBD_SCAN_CMD);
468 drweb_flags = htonl(DRWEBD_RETURN_VIRUSES | DRWEBD_IS_MAIL);
470 if (*scanner_options != '/') {
473 if ((drweb_fd = open(CS eml_filename, O_RDONLY)) == -1)
474 return m_errlog_defer_3(scanent,
475 string_sprintf("can't open spool file %s: %s",
476 eml_filename, strerror(errno)),
479 if ((fsize = lseek(drweb_fd, 0, SEEK_END)) == -1) {
481 (void)close(drweb_fd);
482 return m_errlog_defer_3(scanent,
483 string_sprintf("can't seek spool file %s: %s",
484 eml_filename, strerror(err)),
487 drweb_slen = htonl(fsize);
488 lseek(drweb_fd, 0, SEEK_SET);
490 DEBUG(D_acl) debug_printf("Malware scan: issuing %s remote scan [%s]\n",
491 scanner_name, scanner_options);
493 /* send scan request */
494 if ((send(sock, &drweb_cmd, sizeof(drweb_cmd), 0) < 0) ||
495 (send(sock, &drweb_flags, sizeof(drweb_flags), 0) < 0) ||
496 (send(sock, &drweb_fin, sizeof(drweb_fin), 0) < 0) ||
497 (send(sock, &drweb_slen, sizeof(drweb_slen), 0) < 0)) {
498 (void)close(drweb_fd);
499 return m_errlog_defer_3(scanent,
500 string_sprintf("unable to send commands to socket (%s)", scanner_options),
504 if (!(drweb_fbuf = (uschar *) malloc (fsize))) {
505 (void)close(drweb_fd);
506 return m_errlog_defer_3(scanent,
507 string_sprintf("unable to allocate memory %u for file (%s)",
508 fsize, eml_filename),
512 if ((result = read (drweb_fd, drweb_fbuf, fsize)) == -1) {
514 (void)close(drweb_fd);
516 return m_errlog_defer_3(scanent,
517 string_sprintf("can't read spool file %s: %s",
518 eml_filename, strerror(err)),
521 (void)close(drweb_fd);
523 /* send file body to socket */
524 if (send(sock, drweb_fbuf, fsize, 0) < 0) {
526 return m_errlog_defer_3(scanent,
527 string_sprintf("unable to send file body to socket (%s)", scanner_options),
530 (void)close(drweb_fd);
534 drweb_slen = htonl(Ustrlen(eml_filename));
536 DEBUG(D_acl) debug_printf("Malware scan: issuing %s local scan [%s]\n",
537 scanner_name, scanner_options);
539 /* send scan request */
540 if ((send(sock, &drweb_cmd, sizeof(drweb_cmd), 0) < 0) ||
541 (send(sock, &drweb_flags, sizeof(drweb_flags), 0) < 0) ||
542 (send(sock, &drweb_slen, sizeof(drweb_slen), 0) < 0) ||
543 (send(sock, eml_filename, Ustrlen(eml_filename), 0) < 0) ||
544 (send(sock, &drweb_fin, sizeof(drweb_fin), 0) < 0))
545 return m_errlog_defer_3(scanent,
546 string_sprintf("unable to send commands to socket (%s)", scanner_options),
550 /* wait for result */
551 if ((bread = recv(sock, &drweb_rc, sizeof(drweb_rc), 0) != sizeof(drweb_rc)))
552 return m_errlog_defer_3(scanent,
553 US"unable to read return code", sock);
554 drweb_rc = ntohl(drweb_rc);
556 if ((bread = recv(sock, &drweb_vnum, sizeof(drweb_vnum), 0) != sizeof(drweb_vnum)))
557 return m_errlog_defer_3(scanent,
558 US"unable to read the number of viruses", sock);
559 drweb_vnum = ntohl(drweb_vnum);
561 /* "virus(es) found" if virus number is > 0 */
565 /* setup default virus name */
566 malware_name = US"unknown";
568 /* set up match regex */
569 drweb_re = m_pcre_compile(US"infected\\swith\\s*(.+?)$", &errstr);
571 /* read and concatenate virus names into one string */
572 for (i=0;i<drweb_vnum;i++)
574 int size = 0, off = 0, ovector[10*3];
575 /* read the size of report */
576 if ((bread = recv(sock, &drweb_slen, sizeof(drweb_slen), 0) != sizeof(drweb_slen)))
577 return m_errlog_defer_3(scanent,
578 US"cannot read report size", sock);
579 drweb_slen = ntohl(drweb_slen);
580 tmpbuf = store_get(drweb_slen);
582 /* read report body */
583 if ((bread = recv(sock, tmpbuf, drweb_slen, 0)) != drweb_slen)
584 return m_errlog_defer_3(scanent,
585 US"cannot read report string", sock);
586 tmpbuf[drweb_slen] = '\0';
588 /* try matcher on the line, grab substring */
589 result = pcre_exec(drweb_re, NULL, CS tmpbuf, Ustrlen(tmpbuf), 0, 0,
590 ovector, nelements(ovector));
592 const char * pre_malware_nb;
594 pcre_get_substring(CS tmpbuf, ovector, result, 1, &pre_malware_nb);
596 if (i==0) /* the first name we just copy to malware_name */
597 malware_name = string_append(NULL, &size, &off,
600 else /* concatenate each new virus name to previous */
601 malware_name = string_append(malware_name, &size, &off,
602 2, "/", pre_malware_nb);
604 pcre_free_substring(pre_malware_nb);
609 const char *drweb_s = NULL;
611 if (drweb_rc & DERR_READ_ERR) drweb_s = "read error";
612 if (drweb_rc & DERR_NOMEMORY) drweb_s = "no memory";
613 if (drweb_rc & DERR_TIMEOUT) drweb_s = "timeout";
614 if (drweb_rc & DERR_BAD_CALL) drweb_s = "wrong command";
615 /* retcodes DERR_SYMLINK, DERR_NO_REGFILE, DERR_SKIPPED.
616 * DERR_TOO_BIG, DERR_TOO_COMPRESSED, DERR_SPAM,
617 * DERR_CRC_ERROR, DERR_READSOCKET, DERR_WRITE_ERR
618 * and others are ignored */
620 return m_errlog_defer_3(scanent,
621 string_sprintf("drweb daemon retcode 0x%x (%s)", drweb_rc, drweb_s),
630 case M_AVES: /* "aveserver" scanner type -------------------------------- */
635 /* read aveserver's greeting and see if it is ready (2xx greeting) */
636 recv_line(sock, buf, sizeof(buf));
638 if (buf[0] != '2') /* aveserver is having problems */
639 return m_errlog_defer_3(scanent,
640 string_sprintf("unavailable (Responded: %s).",
641 ((buf[0] != 0) ? buf : (uschar *)"nothing") ),
644 /* prepare our command */
645 (void)string_format(buf, sizeof(buf), "SCAN bPQRSTUW %s\r\n",
648 DEBUG(D_acl) debug_printf("Malware scan: issuing %s SCAN\n", scanner_name);
651 if (m_sock_send(sock, buf, Ustrlen(buf), &errstr) < 0)
652 return m_errlog_defer(scanent, errstr);
656 /* read response lines, find malware name and final response */
657 while (recv_line(sock, buf, sizeof(buf)) > 0) {
658 debug_printf("aveserver: %s\n", buf);
661 if (buf[0] == '5') { /* aveserver is having problems */
662 result = m_errlog_defer(scanent,
663 string_sprintf("unable to scan file %s (Responded: %s).",
666 } else if (Ustrncmp(buf,"322",3) == 0) {
667 uschar *p = Ustrchr(&buf[4],' ');
669 malware_name = string_copy(&buf[4]);
674 if (m_sock_send(sock, US"quit\r\n", 6, &errstr) < 0)
675 return m_errlog_defer(scanent, errstr);
677 /* read aveserver's greeting and see if it is ready (2xx greeting) */
678 recv_line(sock, buf, sizeof(buf));
680 if (buf[0] != '2') /* aveserver is having problems */
681 return m_errlog_defer_3(scanent,
682 string_sprintf("unable to quit dialogue (Responded: %s).",
683 ((buf[0] != 0) ? buf : (uschar *)"nothing") ),
686 if (result == DEFER) {
693 case M_FSEC: /* "fsecure" scanner type ---------------------------------- */
697 uschar av_buffer[1024];
699 static uschar *cmdopt[] = { US"CONFIGURE\tARCHIVE\t1\n",
700 US"CONFIGURE\tTIMEOUT\t0\n",
701 US"CONFIGURE\tMAXARCH\t5\n",
702 US"CONFIGURE\tMIME\t1\n" };
706 DEBUG(D_acl) debug_printf("Malware scan: issuing %s scan [%s]\n",
707 scanner_name, scanner_options);
710 memset(av_buffer, 0, sizeof(av_buffer));
711 for (i=0; i != nelements(cmdopt); i++) {
713 if (m_sock_send(sock, cmdopt[i], Ustrlen(cmdopt[i]), &errstr) < 0)
714 return m_errlog_defer(scanent, errstr);
716 bread = ip_recv(sock, av_buffer, sizeof(av_buffer), MALWARE_TIMEOUT);
717 if (bread >0) av_buffer[bread]='\0';
719 return m_errlog_defer_3(scanent,
720 string_sprintf("unable to read answer %d (%s)", i, strerror(errno)),
722 for (j=0;j<bread;j++)
723 if((av_buffer[j]=='\r')||(av_buffer[j]=='\n'))
727 /* pass the mailfile to fsecure */
728 file_name = string_sprintf("SCAN\t%s\n", eml_filename);
730 if (m_sock_send(sock, file_name, Ustrlen(file_name), &errstr) < 0)
731 return m_errlog_defer(scanent, errstr);
734 /* todo also SUSPICION\t */
735 fs_inf = m_pcre_compile(US"\\S{0,5}INFECTED\\t[^\\t]*\\t([^\\t]+)\\t\\S*$", &errstr);
737 /* read report, linewise */
740 memset(av_buffer, 0, sizeof(av_buffer));
742 if ((bread= ip_recv(sock, &av_buffer[i], 1, MALWARE_TIMEOUT)) < 0)
743 return m_errlog_defer_3(scanent,
744 string_sprintf("unable to read result (%s)", strerror(errno)),
746 } while (++i < sizeof(av_buffer)-1 && av_buffer[i-1] != '\n');
747 av_buffer[i-1] = '\0';
749 /* Really search for virus again? */
750 if (malware_name == NULL)
751 /* try matcher on the line, grab substring */
752 malware_name = m_pcre_exec(fs_inf, av_buffer);
754 while (Ustrstr(av_buffer, "OK\tScan ok.") == NULL);
758 case M_KAVD: /* "kavdaemon" scanner type -------------------------------- */
762 uschar * scanrequest;
764 unsigned long kav_reportlen, bread;
768 /* get current date and time, build scan request */
770 /* pdp note: before the eml_filename parameter, this scanned the
771 directory; not finding documentation, so we'll strip off the directory.
772 The side-effect is that the test framework scanning may end up in
773 scanning more than was requested, but for the normal interface, this is
776 strftime(CS tmpbuf, sizeof(tmpbuf), "%d %b %H:%M:%S", localtime(&t));
777 scanrequest = string_sprintf("<0>%s:%s", CS tmpbuf, eml_filename);
778 p = Ustrrchr(scanrequest, '/');
782 DEBUG(D_acl) debug_printf("Malware scan: issuing %s scan [%s]\n",
783 scanner_name, scanner_options);
785 /* send scan request */
786 if (m_sock_send(sock, scanrequest, Ustrlen(scanrequest)+1, &errstr) < 0)
787 return m_errlog_defer(scanent, errstr);
789 /* wait for result */
790 if ((bread = recv(sock, tmpbuf, 2, 0) != 2))
791 return m_errlog_defer_3(scanent,
792 US"unable to read 2 bytes from socket.", sock);
794 /* get errorcode from one nibble */
795 kav_rc = tmpbuf[ test_byte_order()==LITTLE_MY_ENDIAN ? 0 : 1 ] & 0x0F;
798 case 5: case 6: /* improper kavdaemon configuration */
799 return m_errlog_defer_3(scanent,
800 US"please reconfigure kavdaemon to NOT disinfect or remove infected files.",
803 return m_errlog_defer_3(scanent,
804 US"reported 'scanning not completed' (code 1).", sock);
806 return m_errlog_defer_3(scanent,
807 US"reported 'kavdaemon damaged' (code 7).", sock);
810 /* code 8 is not handled, since it is ambigous. It appears mostly on
811 bounces where part of a file has been cut off */
813 /* "virus found" return codes (2-4) */
814 if ((kav_rc > 1) && (kav_rc < 5)) {
817 /* setup default virus name */
818 malware_name = US"unknown";
820 report_flag = tmpbuf[ test_byte_order() == LITTLE_MY_ENDIAN ? 1 : 0 ];
822 /* read the report, if available */
823 if( report_flag == 1 ) {
824 /* read report size */
825 if ((bread = recv(sock, &kav_reportlen, 4, 0)) != 4)
826 return m_errlog_defer_3(scanent,
827 US"cannot read report size", sock);
829 /* it's possible that avp returns av_buffer[1] == 1 but the
830 reportsize is 0 (!?) */
831 if (kav_reportlen > 0) {
832 /* set up match regex, depends on retcode */
833 kav_re = m_pcre_compile( kav_rc == 3
834 ? US"suspicion:\\s*(.+?)\\s*$"
835 : US"infected:\\s*(.+?)\\s*$",
838 /* read report, linewise */
839 while (kav_reportlen > 0) {
841 while ( recv(sock, &tmpbuf[bread], 1, 0) == 1 ) {
843 if ( (tmpbuf[bread] == '\n') || (bread > 1021) ) break;
847 tmpbuf[bread] = '\0';
849 /* try matcher on the line, grab substring */
850 if ((malware_name = m_pcre_exec(kav_re, tmpbuf)))
856 else /* no virus found */
862 case M_CMDL: /* "cmdline" scanner type ---------------------------------- */
864 const uschar *cmdline_scanner = scanner_options;
865 const pcre *cmdline_trigger_re;
866 const pcre *cmdline_regex_re;
868 uschar * commandline;
869 void (*eximsigchld)(int);
870 void (*eximsigpipe)(int);
871 FILE *scanner_out = NULL;
872 FILE *scanner_record = NULL;
873 uschar linebuffer[32767];
877 if (!cmdline_scanner)
878 return m_errlog_defer(scanent, errstr);
880 /* find scanner output trigger */
881 cmdline_trigger_re = m_pcre_nextinlist(&av_scanner_work, &sep,
882 "missing trigger specification", &errstr);
883 if (!cmdline_trigger_re)
884 return m_errlog_defer(scanent, errstr);
886 /* find scanner name regex */
887 cmdline_regex_re = m_pcre_nextinlist(&av_scanner_work, &sep,
888 "missing virus name regex specification", &errstr);
889 if (!cmdline_regex_re)
890 return m_errlog_defer(scanent, errstr);
892 /* prepare scanner call; despite the naming, file_name holds a directory
893 name which is documented as the value given to %s. */
895 file_name = string_copy(eml_filename);
896 p = Ustrrchr(file_name, '/');
899 commandline = string_sprintf(CS cmdline_scanner, file_name);
901 /* redirect STDERR too */
902 commandline = string_sprintf("%s 2>&1", commandline);
904 DEBUG(D_acl) debug_printf("Malware scan: issuing %s scan [%s]\n", scanner_name, commandline);
906 /* store exims signal handlers */
907 eximsigchld = signal(SIGCHLD,SIG_DFL);
908 eximsigpipe = signal(SIGPIPE,SIG_DFL);
910 if (!(scanner_out = popen(CS commandline,"r"))) {
912 signal(SIGCHLD,eximsigchld); signal(SIGPIPE,eximsigpipe);
913 return m_errlog_defer(scanent,
914 string_sprintf("call (%s) failed: %s.", commandline, strerror(err)));
917 file_name = string_sprintf("%s/scan/%s/%s_scanner_output",
918 spool_directory, message_id, message_id);
919 scanner_record = modefopen(file_name, "wb", SPOOL_MODE);
921 if (scanner_record == NULL) {
923 (void) pclose(scanner_out);
924 signal(SIGCHLD,eximsigchld); signal(SIGPIPE,eximsigpipe);
925 return m_errlog_defer(scanent,
926 string_sprintf("opening scanner output file (%s) failed: %s.",
927 file_name, strerror(err)));
930 /* look for trigger while recording output */
931 while(fgets(CS linebuffer, sizeof(linebuffer), scanner_out)) {
932 if ( Ustrlen(linebuffer) > fwrite(linebuffer, 1, Ustrlen(linebuffer), scanner_record) ) {
934 (void) pclose(scanner_out);
935 signal(SIGCHLD,eximsigchld); signal(SIGPIPE,eximsigpipe);
936 return m_errlog_defer(scanent,
937 string_sprintf("short write on scanner output file (%s).", file_name));
939 /* try trigger match */
940 if (!trigger && regex_match_and_setup(cmdline_trigger_re, linebuffer, 0, -1))
944 (void)fclose(scanner_record);
945 sep = pclose(scanner_out);
946 signal(SIGCHLD,eximsigchld); signal(SIGPIPE,eximsigpipe);
948 return m_errlog_defer(scanent,
950 ? string_sprintf("running scanner failed: %s", strerror(sep))
951 : string_sprintf("scanner returned error code: %d", sep));
955 /* setup default virus name */
956 malware_name = US"unknown";
958 /* re-open the scanner output file, look for name match */
959 scanner_record = fopen(CS file_name, "rb");
960 while(fgets(CS linebuffer, sizeof(linebuffer), scanner_record)) {
962 if ((s = m_pcre_exec(cmdline_regex_re, linebuffer)))
965 (void)fclose(scanner_record);
967 else /* no virus found */
972 case M_SOPHIE: /* "sophie" scanner type --------------------------------- */
977 uschar av_buffer[1024];
979 /* pass the scan directory to sophie */
980 file_name = string_copy(eml_filename);
981 if ((p = Ustrrchr(file_name, '/')))
984 DEBUG(D_acl) debug_printf("Malware scan: issuing %s scan [%s]\n",
985 scanner_name, scanner_options);
987 if ( write(sock, file_name, Ustrlen(file_name)) < 0
988 || write(sock, "\n", 1) != 1
990 return m_errlog_defer_3(scanent,
991 string_sprintf("unable to write to UNIX socket (%s)", scanner_options),
994 /* wait for result */
995 memset(av_buffer, 0, sizeof(av_buffer));
996 if ((!(bread = ip_recv(sock, av_buffer, sizeof(av_buffer), MALWARE_TIMEOUT)) > 0))
997 return m_errlog_defer_3(scanent,
998 string_sprintf("unable to read from UNIX socket (%s)", scanner_options),
1002 if (av_buffer[0] == '1') {
1003 uschar * s = Ustrchr(av_buffer, '\n');
1006 malware_name = string_copy(&av_buffer[2]);
1008 else if (!strncmp(CS av_buffer, "-1", 2))
1009 return m_errlog_defer_3(scanent, US"scanner reported error", sock);
1010 else /* all ok, no virus */
1011 malware_name = NULL;
1016 case M_CLAMD: /* "clamd" scanner type ----------------------------------- */
1018 /* This code was originally contributed by David Saez */
1019 /* There are three scanning methods available to us:
1020 * (1) Use the SCAN command, pointing to a file in the filesystem
1021 * (2) Use the STREAM command, send the data on a separate port
1022 * (3) Use the zINSTREAM command, send the data inline
1023 * The zINSTREAM command was introduced with ClamAV 0.95, which marked
1024 * STREAM deprecated; see: http://wiki.clamav.net/bin/view/Main/UpgradeNotes095
1025 * In Exim, we use SCAN if using a Unix-domain socket or explicitly told that
1026 * the TCP-connected daemon is actually local; otherwise we use zINSTREAM unless
1027 * WITH_OLD_CLAMAV_STREAM is defined.
1028 * See Exim bug 926 for details. */
1030 uschar *p, *vname, *result_tag, *response_end;
1033 uschar av_buffer[1024];
1034 uschar *hostname = US"";
1036 uschar *clamav_fbuf;
1037 int clam_fd, result;
1039 BOOL use_scan_command = FALSE;
1040 clamd_address_container * clamd_address_vector[MAX_CLAMD_SERVERS];
1042 int num_servers = 0;
1043 #ifdef WITH_OLD_CLAMAV_STREAM
1045 uschar av_buffer2[1024];
1048 uint32_t send_size, send_final_zeroblock;
1051 if (*scanner_options == '/')
1052 /* Local file; so we def want to use_scan_command and don't want to try
1053 * passing IP/port combinations */
1054 use_scan_command = TRUE;
1056 const uschar *address = scanner_options;
1057 uschar address_buffer[MAX_CLAMD_ADDRESS_LENGTH + 20];
1059 /* Go through the rest of the list of host/port and construct an array
1060 * of servers to try. The first one is the bit we just passed from
1061 * scanner_options so process that first and then scan the remainder of
1062 * the address buffer */
1064 clamd_address_container *this_clamd;
1066 /* The 'local' option means use the SCAN command over the network
1067 * socket (ie common file storage in use) */
1068 if (strcmpic(address,US"local") == 0) {
1069 use_scan_command = TRUE;
1073 /* XXX: If unsuccessful we should free this memory */
1075 (clamd_address_container *)store_get(sizeof(clamd_address_container));
1077 /* extract host and port part */
1078 if( sscanf(CS address, "%" MAX_CLAMD_ADDRESS_LENGTH_S "s %u",
1079 this_clamd->tcp_addr, &(this_clamd->tcp_port)) != 2 ) {
1080 (void) m_errlog_defer(scanent,
1081 string_sprintf("invalid address '%s'", address));
1085 clamd_address_vector[num_servers] = this_clamd;
1087 if (num_servers >= MAX_CLAMD_SERVERS) {
1088 (void) m_errlog_defer(scanent,
1089 US"More than " MAX_CLAMD_SERVERS_S " clamd servers "
1090 "specified; only using the first " MAX_CLAMD_SERVERS_S );
1093 } while ((address = string_nextinlist(&av_scanner_work, &sep,
1095 sizeof(address_buffer))) != NULL);
1097 /* check if we have at least one server */
1099 return m_errlog_defer(scanent,
1100 US"no useable server addresses in malware configuration option.");
1103 /* See the discussion of response formats below to see why we really don't
1104 like colons in filenames when passing filenames to ClamAV. */
1105 if (use_scan_command && Ustrchr(eml_filename, ':'))
1106 return m_errlog_defer(scanent,
1107 string_sprintf("local/SCAN mode incompatible with" \
1108 " : in path to email filename [%s]", eml_filename));
1110 /* We have some network servers specified */
1113 /* Confirmed in ClamAV source (0.95.3) that the TCPAddr option of clamd
1114 * only supports AF_INET, but we should probably be looking to the
1115 * future and rewriting this to be protocol-independent anyway. */
1117 while ( num_servers > 0 ) {
1118 /* Randomly pick a server to start with */
1119 current_server = random_number( num_servers );
1121 debug_printf("trying server name %s, port %u\n",
1122 clamd_address_vector[current_server]->tcp_addr,
1123 clamd_address_vector[current_server]->tcp_port);
1125 /* Lookup the host. This is to ensure that we connect to the same IP
1126 * on both connections (as one host could resolve to multiple ips) */
1127 sock= m_tcpsocket(clamd_address_vector[current_server]->tcp_addr,
1128 clamd_address_vector[current_server]->tcp_port,
1129 &connhost, &errstr);
1131 /* Connection successfully established with a server */
1132 hostname = clamd_address_vector[current_server]->tcp_addr;
1136 (void) m_errlog_defer(scanent, errstr);
1138 /* Remove the server from the list. XXX We should free the memory */
1141 for( i = current_server; i < num_servers; i++ )
1142 clamd_address_vector[i] = clamd_address_vector[i+1];
1145 if ( num_servers == 0 )
1146 return m_errlog_defer(scanent, US"all servers failed");
1149 if ((sock = m_unixsocket(scanner_options, &errstr)) < 0)
1150 return m_errlog_defer(scanent, errstr);
1153 /* have socket in variable "sock"; command to use is semi-independent of
1154 * the socket protocol. We use SCAN if is local (either Unix/local
1155 * domain socket, or explicitly told local) else we stream the data.
1156 * How we stream the data depends upon how we were built. */
1158 if (!use_scan_command) {
1160 #ifdef WITH_OLD_CLAMAV_STREAM
1161 /* "STREAM\n" command, get back a "PORT <N>\n" response, send data to
1162 * that port on a second connection; then in the scan-method-neutral
1163 * part, read the response back on the original connection. */
1165 DEBUG(D_acl) debug_printf("Malware scan: issuing %s old-style remote scan (PORT)\n",
1168 /* Pass the string to ClamAV (7 = "STREAM\n") */
1169 if (m_sock_send(sock, US"STREAM\n", 7, &errstr) < 0)
1170 return m_errlog_defer(scanent, errstr);
1172 memset(av_buffer2, 0, sizeof(av_buffer2));
1173 bread = ip_recv(sock, av_buffer2, sizeof(av_buffer2), MALWARE_TIMEOUT);
1176 return m_errlog_defer_3(scanent,
1177 string_sprintf("unable to read PORT from socket (%s)",
1181 if (bread == sizeof(av_buffer2))
1182 return m_errlog_defer_3(scanent, "buffer too small", sock);
1185 return m_errlog_defer_3(scanent, "ClamAV returned null", sock);
1187 av_buffer2[bread] = '\0';
1188 if( sscanf(CS av_buffer2, "PORT %u\n", &port) != 1 )
1189 return m_errlog_defer_3(scanent,
1190 string_sprintf("Expected port information from clamd, got '%s'",
1194 sockData = m_tcpsocket(connhost.address, port, NULL, &errstr);
1196 return m_errlog_defer_3(scanent, errstr, sock);
1198 #define CLOSE_SOCKDATA (void)close(sockData)
1199 #else /* WITH_OLD_CLAMAV_STREAM not defined */
1200 /* New protocol: "zINSTREAM\n" followed by a sequence of <length><data>
1201 chunks, <n> a 4-byte number (network order), terminated by a zero-length
1204 DEBUG(D_acl) debug_printf("Malware scan: issuing %s new-style remote scan (zINSTREAM)\n",
1207 /* Pass the string to ClamAV (10 = "zINSTREAM\0") */
1208 if (send(sock, "zINSTREAM", 10, 0) < 0)
1209 return m_errlog_defer_3(scanent,
1210 string_sprintf("unable to send zINSTREAM to socket (%s)",
1214 #define CLOSE_SOCKDATA /**/
1217 /* calc file size */
1218 if ((clam_fd = open(CS eml_filename, O_RDONLY)) < 0) {
1221 return m_errlog_defer_3(scanent,
1222 string_sprintf("can't open spool file %s: %s",
1223 eml_filename, strerror(err)),
1226 if ((fsize = lseek(clam_fd, 0, SEEK_END)) < 0) {
1228 CLOSE_SOCKDATA; (void)close(clam_fd);
1229 return m_errlog_defer_3(scanent,
1230 string_sprintf("can't seek spool file %s: %s",
1231 eml_filename, strerror(err)),
1234 lseek(clam_fd, 0, SEEK_SET);
1236 if (!(clamav_fbuf = (uschar *) malloc (fsize))) {
1237 CLOSE_SOCKDATA; (void)close(clam_fd);
1238 return m_errlog_defer_3(scanent,
1239 string_sprintf("unable to allocate memory %u for file (%s)",
1240 fsize, eml_filename),
1244 if ((result = read(clam_fd, clamav_fbuf, fsize)) < 0) {
1246 free(clamav_fbuf); CLOSE_SOCKDATA; (void)close(clam_fd);
1247 return m_errlog_defer_3(scanent,
1248 string_sprintf("can't read spool file %s: %s",
1249 eml_filename, strerror(err)),
1252 (void)close(clam_fd);
1254 /* send file body to socket */
1255 #ifdef WITH_OLD_CLAMAV_STREAM
1256 if (send(sockData, clamav_fbuf, fsize, 0) < 0) {
1257 free(clamav_fbuf); CLOSE_SOCKDATA;
1258 return m_errlog_defer_3(scanent,
1259 string_sprintf("unable to send file body to socket (%s:%u)",
1264 send_size = htonl(fsize);
1265 send_final_zeroblock = 0;
1266 if ((send(sock, &send_size, sizeof(send_size), 0) < 0) ||
1267 (send(sock, clamav_fbuf, fsize, 0) < 0) ||
1268 (send(sock, &send_final_zeroblock, sizeof(send_final_zeroblock), 0) < 0))
1271 return m_errlog_defer_3(scanent,
1272 string_sprintf("unable to send file body to socket (%s)", hostname),
1280 #undef CLOSE_SOCKDATA
1282 } else { /* use scan command */
1283 /* Send a SCAN command pointing to a filename; then in the then in the
1284 * scan-method-neutral part, read the response back */
1286 /* ================================================================= */
1288 /* Prior to the reworking post-Exim-4.72, this scanned a directory,
1289 which dates to when ClamAV needed us to break apart the email into the
1290 MIME parts (eg, with the now deprecated demime condition coming first).
1291 Some time back, ClamAV gained the ability to deconstruct the emails, so
1292 doing this would actually have resulted in the mail attachments being
1293 scanned twice, in the broken out files and from the original .eml.
1294 Since ClamAV now handles emails (and has for quite some time) we can
1295 just use the email file itself. */
1296 /* Pass the string to ClamAV (7 = "SCAN \n" + \0) */
1297 file_name = string_sprintf("SCAN %s\n", eml_filename);
1299 DEBUG(D_acl) debug_printf("Malware scan: issuing %s local-path scan [%s]\n",
1300 scanner_name, scanner_options);
1302 if (send(sock, file_name, Ustrlen(file_name), 0) < 0)
1303 return m_errlog_defer_3(scanent,
1304 string_sprintf("unable to write to socket (%s)", strerror(errno)),
1307 /* Do not shut down the socket for writing; a user report noted that
1308 * clamd 0.70 does not react well to this. */
1310 /* Commands have been sent, no matter which scan method or connection
1311 * type we're using; now just read the result, independent of method. */
1313 /* Read the result */
1314 memset(av_buffer, 0, sizeof(av_buffer));
1315 bread = ip_recv(sock, av_buffer, sizeof(av_buffer), MALWARE_TIMEOUT);
1320 return m_errlog_defer(scanent,
1321 string_sprintf("unable to read from socket (%s)", strerror(errno)));
1323 if (bread == sizeof(av_buffer))
1324 return m_errlog_defer(scanent, US"buffer too small");
1325 /* We're now assured of a NULL at the end of av_buffer */
1327 /* Check the result. ClamAV returns one of two result formats.
1328 In the basic mode, the response is of the form:
1329 infected: -> "<filename>: <virusname> FOUND"
1330 not-infected: -> "<filename>: OK"
1331 error: -> "<filename>: <errcode> ERROR
1332 If the ExtendedDetectionInfo option has been turned on, then we get:
1333 "<filename>: <virusname>(<virushash>:<virussize>) FOUND"
1334 for the infected case. Compare:
1335 /tmp/eicar.com: Eicar-Test-Signature FOUND
1336 /tmp/eicar.com: Eicar-Test-Signature(44d88612fea8a8f36de82e1278abb02f:68) FOUND
1338 In the streaming case, clamd uses the filename "stream" which you should
1339 be able to verify with { ktrace clamdscan --stream /tmp/eicar.com }. (The
1340 client app will replace "stream" with the original filename before returning
1341 results to stdout, but the trace shows the data).
1343 We will assume that the pathname passed to clamd from Exim does not contain
1344 a colon. We will have whined loudly above if the eml_filename does (and we're
1345 passing a filename to clamd). */
1348 return m_errlog_defer(scanent, US"ClamAV returned null");
1350 /* strip newline at the end (won't be present for zINSTREAM)
1351 (also any trailing whitespace, which shouldn't exist, but we depend upon
1352 this below, so double-check) */
1353 p = av_buffer + Ustrlen(av_buffer) - 1;
1354 if (*p == '\n') *p = '\0';
1356 DEBUG(D_acl) debug_printf("Malware response: %s\n", av_buffer);
1358 while (isspace(*--p) && (p > av_buffer))
1363 /* colon in returned output? */
1364 if((p = Ustrchr(av_buffer,':')) == NULL)
1365 return m_errlog_defer(scanent,
1366 string_sprintf("ClamAV returned malformed result (missing colon): %s",
1369 /* strip filename */
1370 while (*p && isspace(*++p)) /**/;
1373 /* It would be bad to encounter a virus with "FOUND" in part of the name,
1374 but we should at least be resistant to it. */
1375 p = Ustrrchr(vname, ' ');
1376 result_tag = p ? p+1 : vname;
1378 if (Ustrcmp(result_tag, "FOUND") == 0) {
1379 /* p should still be the whitespace before the result_tag */
1380 while (isspace(*p)) --p;
1382 /* Strip off the extended information too, which will be in parens
1383 after the virus name, with no intervening whitespace. */
1385 /* "(hash:size)", so previous '(' will do; if not found, we have
1386 a curious virus name, but not an error. */
1387 p = Ustrrchr(vname, '(');
1391 malware_name = string_copy(vname);
1392 DEBUG(D_acl) debug_printf("Malware found, name \"%s\"\n", malware_name);
1394 } else if (Ustrcmp(result_tag, "ERROR") == 0)
1395 return m_errlog_defer(scanent,
1396 string_sprintf("ClamAV returned: %s", av_buffer));
1398 else if (Ustrcmp(result_tag, "OK") == 0) {
1399 /* Everything should be OK */
1400 malware_name = NULL;
1401 DEBUG(D_acl) debug_printf("Malware not found\n");
1404 return m_errlog_defer(scanent,
1405 string_sprintf("unparseable response from ClamAV: {%s}", av_buffer));
1410 case M_SOCK: /* "sock" scanner type ------------------------------------- */
1411 /* This code was derived by Martin Poole from the clamd code contributed
1412 by David Saez and the cmdline code
1416 uschar * commandline;
1417 uschar av_buffer[1024];
1418 uschar * linebuffer;
1419 uschar * sockline_scanner;
1420 uschar sockline_scanner_default[] = "%s\n";
1421 const pcre *sockline_trig_re;
1422 const pcre *sockline_name_re;
1424 /* find scanner command line */
1425 if ((sockline_scanner = string_nextinlist(&av_scanner_work, &sep,
1427 { /* check for no expansions apart from one %s */
1428 char * s = index(CS sockline_scanner, '%');
1430 if ((*s != 's' && *s != '%') || index(s+1, '%'))
1431 return m_errlog_defer_3(scanent,
1432 US"unsafe sock scanner call spec", sock);
1435 sockline_scanner = sockline_scanner_default;
1437 /* find scanner output trigger */
1438 sockline_trig_re = m_pcre_nextinlist(&av_scanner_work, &sep,
1439 "missing trigger specification", &errstr);
1440 if (!sockline_trig_re)
1441 return m_errlog_defer_3(scanent, errstr, sock);
1443 /* find virus name regex */
1444 sockline_name_re = m_pcre_nextinlist(&av_scanner_work, &sep,
1445 "missing virus name regex specification", &errstr);
1446 if (!sockline_name_re)
1447 return m_errlog_defer_3(scanent, errstr, sock);
1449 /* prepare scanner call - security depends on expansions check above */
1450 commandline = string_sprintf("%s/scan/%s/%s.eml", spool_directory, message_id, message_id);
1451 commandline = string_sprintf( CS sockline_scanner, CS commandline);
1454 /* Pass the command string to the socket */
1455 if (m_sock_send(sock, commandline, Ustrlen(commandline), &errstr) < 0)
1456 return m_errlog_defer(scanent, errstr);
1458 /* Read the result */
1459 memset(av_buffer, 0, sizeof(av_buffer));
1460 bread = read(sock, av_buffer, sizeof(av_buffer));
1463 return m_errlog_defer_3(scanent,
1464 string_sprintf("unable to read from socket (%s)", strerror(errno)),
1467 if (bread == sizeof(av_buffer))
1468 return m_errlog_defer_3(scanent, US"buffer too small", sock);
1469 linebuffer = string_copy(av_buffer);
1471 /* try trigger match */
1472 if (regex_match_and_setup(sockline_trig_re, linebuffer, 0, -1)) {
1473 if (!(malware_name = m_pcre_exec(sockline_name_re, av_buffer)))
1474 malware_name = US "unknown";
1476 else /* no virus found */
1477 malware_name = NULL;
1481 case M_MKSD: /* "mksd" scanner type ------------------------------------- */
1483 char *mksd_options_end;
1484 int mksd_maxproc = 1; /* default, if no option supplied */
1488 if (scanner_options) {
1489 mksd_maxproc = (int)strtol(CS scanner_options, &mksd_options_end, 10);
1490 if ( *scanner_options == '\0'
1491 || *mksd_options_end != '\0'
1493 || mksd_maxproc > 32
1495 return m_errlog_defer(scanent,
1496 string_sprintf("invalid option '%s'", scanner_options));
1499 if((sock = m_unixsocket(US "/var/run/mksd/socket", &errstr)) < 0)
1500 return m_errlog_defer(scanent, errstr);
1502 malware_name = NULL;
1504 DEBUG(D_acl) debug_printf("Malware scan: issuing %s scan\n", scanner_name);
1506 if ((retval = mksd_scan_packed(scanent, sock, eml_filename)) != OK) {
1515 (void) close (sock);
1516 malware_ok = TRUE; /* set "been here, done that" marker */
1519 /* match virus name against pattern (caseless ------->----------v) */
1520 if ( malware_name && (regex_match_and_setup(re, malware_name, 0, -1)) ) {
1521 DEBUG(D_acl) debug_printf("Matched regex to malware [%s] [%s]\n", malware_regex, malware_name);
1529 /* simple wrapper for reading lines from sockets */
1531 recv_line(int sock, uschar *buffer, int size)
1535 memset(buffer,0,size);
1537 while(recv(sock,p,1,0) > -1) {
1538 if ((p-buffer) > (size-2)) break;
1539 if (*p == '\n') break;
1540 if (*p != '\r') p++;
1548 /* ============= private routines for the "mksd" scanner type ============== */
1550 #include <sys/uio.h>
1553 mksd_writev (int sock, struct iovec *iov, int iovcnt)
1559 i = writev (sock, iov, iovcnt);
1560 while ((i < 0) && (errno == EINTR));
1562 (void) malware_errlog_defer(US"unable to write to mksd UNIX socket (/var/run/mksd/socket)");
1567 if (i >= iov->iov_len) {
1574 iov->iov_base = CS iov->iov_base + i;
1581 mksd_read_lines (int sock, uschar *av_buffer, int av_buffer_size)
1587 if ((i = recv (sock, av_buffer+offset, av_buffer_size-offset, 0)) <= 0) {
1588 (void) malware_errlog_defer(US"unable to read from mksd UNIX socket (/var/run/mksd/socket)");
1593 /* offset == av_buffer_size -> buffer full */
1594 if (offset == av_buffer_size) {
1595 (void) malware_errlog_defer(US"malformed reply received from mksd");
1598 } while (av_buffer[offset-1] != '\n');
1600 av_buffer[offset] = '\0';
1605 mksd_parse_line(struct scan * scanent, char *line)
1615 if ((p = strchr (line, '\n')) != NULL)
1617 return m_errlog_defer(scanent,
1618 string_sprintf("scanner failed: %s", line));
1621 if ((p = strchr (line, '\n')) != NULL) {
1623 if (((p-line) > 5) && (line[3] == ' '))
1624 if (((p = strchr (line+4, ' ')) != NULL) && ((p-line) > 4)) {
1626 malware_name = string_copy(US line+4);
1630 return m_errlog_defer(scanent,
1631 string_sprintf("malformed reply received: %s", line));
1636 mksd_scan_packed(struct scan * scanent, int sock, uschar *scan_filename)
1638 struct iovec iov[3];
1639 const char *cmd = "MSQ\n";
1640 uschar av_buffer[1024];
1642 iov[0].iov_base = (void *) cmd;
1644 iov[1].iov_base = CS scan_filename;
1645 iov[1].iov_len = Ustrlen(scan_filename);
1646 iov[2].iov_base = (void *) (cmd + 3);
1649 if (mksd_writev (sock, iov, 3) < 0)
1652 if (mksd_read_lines (sock, av_buffer, sizeof (av_buffer)) < 0)
1655 return mksd_parse_line (scanent, CS av_buffer);
1658 #endif /*WITH_CONTENT_SCAN*/